1<?php
2error_reporting(1803);
3if (function_exists('mb_internal_encoding')) {
4    mb_internal_encoding('ASCII');
5}
6if (!class_exists('PHP_Archive')) {/**
7 * PHP_Archive Class (implements .phar)
8 *
9 * @package PHP_Archive
10 * @category PHP
11 */
12/**
13 * PHP_Archive Class (implements .phar)
14 *
15 * PHAR files a singular archive from which an entire application can run.
16 * To use it, simply package it using {@see PHP_Archive_Creator} and use phar://
17 * URIs to your includes. i.e. require_once 'phar://config.php' will include config.php
18 * from the root of the PHAR file.
19 *
20 * Gz code borrowed from the excellent File_Archive package by Vincent Lascaux.
21 *
22 * @copyright Copyright David Shafik and Synaptic Media 2004. All rights reserved.
23 * @author Davey Shafik <davey@synapticmedia.net>
24 * @author Greg Beaver <cellog@php.net>
25 * @link http://www.synapticmedia.net Synaptic Media
26 * @version Id: Archive.php,v 1.52 2007/09/01 20:28:14 cellog Exp $
27 * @package PHP_Archive
28 * @category PHP
29 */
30 
31class PHP_Archive
32{
33    const GZ = 0x00001000;
34    const BZ2 = 0x00002000;
35    const SIG = 0x00010000;
36    const SHA1 = 0x0002;
37    const MD5 = 0x0001;
38    /**
39     * Whether this archive is compressed with zlib
40     *
41     * @var bool
42     */
43    private $_compressed;
44    /**
45     * @var string Real path to the .phar archive
46     */
47    private $_archiveName = null;
48    /**
49     * Current file name in the phar
50     * @var string
51     */
52    protected $currentFilename = null;
53    /**
54     * Length of current file in the phar
55     * @var string
56     */
57    protected $internalFileLength = null;
58    /**
59     * Current file statistics (size, creation date, etc.)
60     * @var string
61     */
62    protected $currentStat = null;
63    /**
64     * @var resource|null Pointer to open .phar
65     */
66    protected $fp = null;
67    /**
68     * @var int Current Position of the pointer
69     */
70    protected $position = 0;
71
72    /**
73     * Map actual realpath of phars to meta-data about the phar
74     *
75     * Data is indexed by the alias that is used by internal files.  In other
76     * words, if a file is included via:
77     * <code>
78     * require_once 'phar://PEAR.phar/PEAR/Installer.php';
79     * </code>
80     * then the alias is "PEAR.phar"
81     * 
82     * Information stored is a boolean indicating whether this .phar is compressed
83     * with zlib, another for bzip2, phar-specific meta-data, and
84     * the precise offset of internal files
85     * within the .phar, used with the {@link $_manifest} to load actual file contents
86     * @var array
87     */
88    private static $_pharMapping = array();
89    /**
90     * Map real file paths to alias used
91     *
92     * @var array
93     */
94    private static $_pharFiles = array();
95    /**
96     * File listing for the .phar
97     * 
98     * The manifest is indexed per phar.
99     * 
100     * Files within the .phar are indexed by their relative path within the
101     * .phar.  Each file has this information in its internal array
102     *
103     * - 0 = uncompressed file size
104     * - 1 = timestamp of when file was added to phar
105     * - 2 = offset of file within phar relative to internal file's start
106     * - 3 = compressed file size (actual size in the phar)
107     * @var array
108     */
109    private static $_manifest = array();
110    /**
111     * Absolute offset of internal files within the .phar, indexed by absolute
112     * path to the .phar
113     *
114     * @var array
115     */
116    private static $_fileStart = array();
117    /**
118     * file name of the phar
119     *
120     * @var string
121     */
122    private $_basename;
123
124
125    /**
126     * Default MIME types used for the web front controller
127     *
128     * @var array
129     */
130    public static $defaultmimes = array(
131            'aif' => 'audio/x-aiff',
132            'aiff' => 'audio/x-aiff',
133            'arc' => 'application/octet-stream',
134            'arj' => 'application/octet-stream',
135            'art' => 'image/x-jg',
136            'asf' => 'video/x-ms-asf',
137            'asx' => 'video/x-ms-asf',
138            'avi' => 'video/avi',
139            'bin' => 'application/octet-stream',
140            'bm' => 'image/bmp',
141            'bmp' => 'image/bmp',
142            'bz2' => 'application/x-bzip2',
143            'css' => 'text/css',
144            'doc' => 'application/msword',
145            'dot' => 'application/msword',
146            'dv' => 'video/x-dv',
147            'dvi' => 'application/x-dvi',
148            'eps' => 'application/postscript',
149            'exe' => 'application/octet-stream',
150            'gif' => 'image/gif',
151            'gz' => 'application/x-gzip',
152            'gzip' => 'application/x-gzip',
153            'htm' => 'text/html',
154            'html' => 'text/html',
155            'ico' => 'image/x-icon',
156            'jpe' => 'image/jpeg',
157            'jpg' => 'image/jpeg',
158            'jpeg' => 'image/jpeg',
159            'js' => 'application/x-javascript',
160            'log' => 'text/plain',
161            'mid' => 'audio/x-midi',
162            'mov' => 'video/quicktime',
163            'mp2' => 'audio/mpeg',
164            'mp3' => 'audio/mpeg3',
165            'mpg' => 'audio/mpeg',
166            'pdf' => 'aplication/pdf',
167            'png' => 'image/png',
168            'rtf' => 'application/rtf',
169            'tif' => 'image/tiff',
170            'tiff' => 'image/tiff',
171            'txt' => 'text/plain',
172            'xml' => 'text/xml',
173        );
174
175    public static $defaultphp = array(
176        'php' => true
177        );
178
179    public static $defaultphps = array(
180        'phps' => true
181        );
182
183    public static $deny = array('/.+\.inc$/');
184
185    public static function viewSource($archive, $file)
186    {
187        // security, idea borrowed from PHK
188        if (!file_exists($archive . '.introspect')) {
189            header("HTTP/1.0 404 Not Found");
190            return false;
191        }
192        if (self::_fileExists($archive, $_GET['viewsource'])) {
193            $source = highlight_file('phar://install-pear-nozlib.phar/' .
194                $_GET['viewsource'], true);
195            header('Content-Type: text/html');
196            header('Content-Length: ' . strlen($source));
197            echo '<html><head><title>Source of ',
198                htmlspecialchars($_GET['viewsource']), '</title></head>';
199            echo '<body><h1>Source of ',
200                htmlspecialchars($_GET['viewsource']), '</h1>';
201            if (isset($_GET['introspect'])) {
202                echo '<a href="', htmlspecialchars($_SERVER['PHP_SELF']),
203                    '?introspect=', urlencode(htmlspecialchars($_GET['introspect'])),
204                    '">Return to ', htmlspecialchars($_GET['introspect']), '</a><br />';
205            }
206            echo $source;
207            return false;
208        } else {
209            header("HTTP/1.0 404 Not Found");
210            return false;
211        }
212        
213    }
214
215    public static function introspect($archive, $dir)
216    {
217        // security, idea borrowed from PHK
218        if (!file_exists($archive . '.introspect')) {
219            header("HTTP/1.0 404 Not Found");
220            return false;
221        }
222        if (!$dir) {
223            $dir = '/';
224        }
225        $dir = self::processFile($dir);
226        if ($dir[0] != '/') {
227            $dir = '/' . $dir;
228        }
229        try {
230            $self = htmlspecialchars($_SERVER['PHP_SELF']);
231            $iterate = new DirectoryIterator('phar://install-pear-nozlib.phar' . $dir);
232            echo '<html><head><title>Introspect ', htmlspecialchars($dir),
233                '</title></head><body><h1>Introspect ', htmlspecialchars($dir),
234                '</h1><ul>';
235            if ($dir != '/') {
236                echo '<li><a href="', $self, '?introspect=',
237                    htmlspecialchars(dirname($dir)), '">..</a></li>';
238            }
239            foreach ($iterate as $entry) {
240                if ($entry->isDot()) continue;
241                $name = self::processFile($entry->getPathname());
242                $name = str_replace('phar://install-pear-nozlib.phar/', '', $name);
243                if ($entry->isDir()) {
244                    echo '<li><a href="', $self, '?introspect=',
245                        urlencode(htmlspecialchars($name)),
246                        '">',
247                        htmlspecialchars($entry->getFilename()), '/</a> [directory]</li>';
248                } else {
249                    echo '<li><a href="', $self, '?introspect=',
250                        urlencode(htmlspecialchars($dir)), '&viewsource=',
251                        urlencode(htmlspecialchars($name)),
252                        '">',
253                        htmlspecialchars($entry->getFilename()), '</a></li>';
254                }
255            }
256            return false;
257        } catch (Exception $e) {
258            echo '<html><head><title>Directory not found: ',
259                htmlspecialchars($dir), '</title></head>',
260                '<body><h1>Directory not found: ', htmlspecialchars($dir), '</h1>',
261                '<p>Try <a href="', htmlspecialchars($_SERVER['PHP_SELF']), '?introspect=/">',
262                'This link</a></p></body></html>';
263            return false;
264        }
265    }
266
267    public static function webFrontController($initfile)
268    {
269        if (isset($_SERVER) && isset($_SERVER['REQUEST_URI'])) {
270            $uri = parse_url($_SERVER['REQUEST_URI']);
271            $archive = realpath($_SERVER['SCRIPT_FILENAME']);
272            $subpath = str_replace('/' . basename($archive), '', $uri['path']);
273            if (!$subpath || $subpath == '/') {
274                if (isset($_GET['viewsource'])) {
275                    return self::viewSource($archive, $_GET['viewsource']);
276                }
277                if (isset($_GET['introspect'])) {
278                    return self::introspect($archive, $_GET['introspect']);
279                }
280                $subpath = '/' . $initfile;
281            }
282            if (!self::_fileExists($archive, substr($subpath, 1))) {
283                header("HTTP/1.0 404 Not Found");
284                return false;
285            }
286            foreach (self::$deny as $pattern) {
287                if (preg_match($pattern, $subpath)) {
288                    header("HTTP/1.0 404 Not Found");
289                    return false;
290                }
291            }
292            $inf = pathinfo(basename($subpath));
293            if (!isset($inf['extension'])) {
294                header('Content-Type: text/plain');
295                header('Content-Length: ' .
296                    self::_filesize($archive, substr($subpath, 1)));
297                readfile('phar://install-pear-nozlib.phar' . $subpath);
298                return false;
299            }
300            if (isset(self::$defaultphp[$inf['extension']])) {
301                include 'phar://install-pear-nozlib.phar' . $subpath;
302                return false;
303            }
304            if (isset(self::$defaultmimes[$inf['extension']])) {
305                header('Content-Type: ' . self::$defaultmimes[$inf['extension']]);
306                header('Content-Length: ' .
307                    self::_filesize($archive, substr($subpath, 1)));
308                readfile('phar://install-pear-nozlib.phar' . $subpath);
309                return false;
310            }
311            if (isset(self::$defaultphps[$inf['extension']])) {
312                header('Content-Type: text/html');
313                $c = highlight_file('phar://install-pear-nozlib.phar' . $subpath, true);
314                header('Content-Length: ' . strlen($c));
315                echo $c;
316                return false;
317            }
318            header('Content-Type: text/plain');
319            header('Content-Length: ' .
320                    self::_filesize($archive, substr($subpath, 1)));
321            readfile('phar://install-pear-nozlib.phar' . $subpath);
322        }
323    }
324
325    /**
326     * Detect end of stub
327     *
328     * @param string $buffer stub past '__HALT_'.'COMPILER();'
329     * @return end of stub, prior to length of manifest.
330     */
331    private static final function _endOfStubLength($buffer)
332    {
333        $pos = 0;
334        if (!strlen($buffer)) {
335            return $pos;
336        }
337        if (($buffer[0] == ' ' || $buffer[0] == "\n") && @substr($buffer, 1, 2) == '')
338        {
339            $pos += 3;
340            if ($buffer[$pos] == "\r" && $buffer[$pos+1] == "\n") {
341                $pos += 2;
342            }
343            else if ($buffer[$pos] == "\n") {
344                $pos += 1;
345            }
346        }
347        return $pos;
348    }
349
350    /**
351     * Allows loading an external Phar archive without include()ing it
352     *
353     * @param string $file  phar package to load
354     * @param string $alias alias to use
355     * @throws Exception
356     */
357    public static final function loadPhar($file, $alias = NULL)
358    {
359        $file = realpath($file);
360        if ($file) {
361            $fp = fopen($file, 'rb');
362            $buffer = '';
363            while (!feof($fp)) {
364                $buffer .= fread($fp, 8192);
365                // don't break phars
366                if ($pos = strpos($buffer, '__HALT_COMPI' . 'LER();')) {
367                    $buffer .= fread($fp, 5);
368                    fclose($fp);
369                    $pos += 18;
370                    $pos += self::_endOfStubLength(substr($buffer, $pos));
371                    return self::_mapPhar($file, $pos, $alias);
372                }
373            }
374            fclose($fp);
375        }
376    }
377
378    /**
379     * Map a full real file path to an alias used to refer to the .phar
380     *
381     * This function can only be called from the initialization of the .phar itself.
382     * Any attempt to call from outside the .phar or to re-alias the .phar will fail
383     * as a security measure.
384     * @param string $alias
385     * @param int $dataoffset the value of 42421                   
386     */
387    public static final function mapPhar($alias = NULL, $dataoffset = NULL)
388    {
389        try {
390            $trace = debug_backtrace();
391            $file = $trace[0]['file'];
392            // this ensures that this is safe
393            if (!in_array($file, get_included_files())) {
394                die('SECURITY ERROR: PHP_Archive::mapPhar can only be called from within ' .
395                    'the phar that initiates it');
396            }
397            $file = realpath($file);
398            if (!isset($dataoffset)) {
399                $dataoffset = constant('__COMPILER_HALT_OFFSET'.'__');
400                $fp = fopen($file, 'rb');
401                fseek($fp, $dataoffset, SEEK_SET);
402                $dataoffset = $dataoffset + self::_endOfStubLength(fread($fp, 5));
403                fclose($fp);
404            }
405
406            self::_mapPhar($file, $dataoffset);
407        } catch (Exception $e) {
408            die($e->getMessage());
409        }
410    }
411
412    /**
413     * Sub-function, allows recovery from errors
414     *
415     * @param unknown_type $file
416     * @param unknown_type $dataoffset
417     */
418    private static function _mapPhar($file, $dataoffset, $alias = NULL)
419    {
420        $file = realpath($file);
421        if (isset(self::$_manifest[$file])) {
422            return;
423        }
424        if (!is_array(self::$_pharMapping)) {
425            self::$_pharMapping = array();
426        }
427        $fp = fopen($file, 'rb');
428        // seek to __HALT_COMPILER_OFFSET__
429        fseek($fp, $dataoffset);
430        $manifest_length = unpack('Vlen', fread($fp, 4));
431        $manifest = '';
432        $last = '1';
433        while (strlen($last) && strlen($manifest) < $manifest_length['len']) {
434            $read = 8192;
435            if ($manifest_length['len'] - strlen($manifest) < 8192) {
436                $read = $manifest_length['len'] - strlen($manifest);
437            }
438            $last = fread($fp, $read);
439            $manifest .= $last;
440        }
441        if (strlen($manifest) < $manifest_length['len']) {
442            throw new Exception('ERROR: manifest length read was "' . 
443                strlen($manifest) .'" should be "' .
444                $manifest_length['len'] . '"');
445        }
446        $info = self::_unserializeManifest($manifest);
447        if ($info['alias']) {
448            $alias = $info['alias'];
449            $explicit = true;
450        } else {
451            if (!isset($alias)) {
452                $alias = $file;
453            }
454            $explicit = false;
455        }
456        self::$_manifest[$file] = $info['manifest'];
457        $compressed = $info['compressed'];
458        self::$_fileStart[$file] = ftell($fp);
459        fclose($fp);
460        if ($compressed & 0x00001000) {
461            if (!function_exists('gzinflate')) {
462                throw new Exception('Error: zlib extension is not enabled - gzinflate() function needed' .
463                    ' for compressed .phars');
464            }
465        }
466        if ($compressed & 0x00002000) {
467            if (!function_exists('bzdecompress')) {
468                throw new Exception('Error: bzip2 extension is not enabled - bzdecompress() function needed' .
469                    ' for compressed .phars');
470            }
471        }
472        if (isset(self::$_pharMapping[$alias])) {
473            throw new Exception('ERROR: PHP_Archive::mapPhar has already been called for alias "' .
474                $alias . '" cannot re-alias to "' . $file . '"');
475        }
476        self::$_pharMapping[$alias] = array($file, $compressed, $dataoffset, $explicit,
477            $info['metadata']);
478        self::$_pharFiles[$file] = $alias;
479    }
480
481    /**
482     * extract the manifest into an internal array
483     *
484     * @param string $manifest
485     * @return false|array
486     */
487    private static function _unserializeManifest($manifest)
488    {
489        // retrieve the number of files in the manifest
490        $info = unpack('V', substr($manifest, 0, 4));
491        $apiver = substr($manifest, 4, 2);
492        $apiver = bin2hex($apiver);
493        $apiver_dots = hexdec($apiver[0]) . '.' . hexdec($apiver[1]) . '.' . hexdec($apiver[2]);
494        $majorcompat = hexdec($apiver[0]);
495        $calcapi = explode('.', self::APIVersion());
496        if ($calcapi[0] != $majorcompat) {
497            throw new Exception('Phar is incompatible API version ' . $apiver_dots . ', but ' .
498                'PHP_Archive is API version '.self::APIVersion());
499        }
500        if ($calcapi[0] === '0') {
501            if (self::APIVersion() != $apiver_dots) {
502                throw new Exception('Phar is API version ' . $apiver_dots .
503                    ', but PHP_Archive is API version '.self::APIVersion(), E_USER_ERROR);
504            }
505        }
506        $flags = unpack('V', substr($manifest, 6, 4));
507        $ret = array('compressed' => $flags & 0x00003000);
508        // signature is not verified by default in PHP_Archive, phar is better
509        $ret['hassignature'] = $flags & 0x00010000;
510        $aliaslen = unpack('V', substr($manifest, 10, 4));
511        if ($aliaslen) {
512            $ret['alias'] = substr($manifest, 14, $aliaslen[1]);
513        } else {
514            $ret['alias'] = false;
515        }
516        $manifest = substr($manifest, 14 + $aliaslen[1]);
517        $metadatalen = unpack('V', substr($manifest, 0, 4));
518        if ($metadatalen[1]) {
519            $ret['metadata'] = unserialize(substr($manifest, 4, $metadatalen[1]));
520            $manifest = substr($manifest, 4 + $metadatalen[1]);
521        } else {
522            $ret['metadata'] = null;
523            $manifest = substr($manifest, 4);
524        }
525        $offset = 0;
526        $start = 0;
527        for ($i = 0; $i < $info[1]; $i++) {
528            // length of the file name
529            $len = unpack('V', substr($manifest, $start, 4));
530            $start += 4;
531            // file name
532            $savepath = substr($manifest, $start, $len[1]);
533            $start += $len[1];
534            // retrieve manifest data:
535            // 0 = uncompressed file size
536            // 1 = timestamp of when file was added to phar
537            // 2 = compressed filesize
538            // 3 = crc32
539            // 4 = flags
540            // 5 = metadata length
541            $ret['manifest'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($manifest, $start, 24)));
542            $ret['manifest'][$savepath][3] = sprintf('%u', $ret['manifest'][$savepath][3]
543                & 0xffffffff);
544            if ($ret['manifest'][$savepath][5]) {
545                $ret['manifest'][$savepath][6] = unserialize(substr($manifest, $start + 24,
546                    $ret['manifest'][$savepath][5]));
547            } else {
548                $ret['manifest'][$savepath][6] = null;
549            }
550            $ret['manifest'][$savepath][7] = $offset;
551            $offset += $ret['manifest'][$savepath][2];
552            $start += 24 + $ret['manifest'][$savepath][5];
553        }
554        return $ret;
555    }
556
557    /**
558     * @param string
559     */
560    private static function processFile($path)
561    {
562        if ($path == '.') {
563            return '';
564        }
565        $std = str_replace("\\", "/", $path);
566        while ($std != ($std = ereg_replace("[^\/:?]+/\.\./", "", $std))) ;
567        $std = str_replace("/./", "", $std);
568        if (strlen($std) > 1 && $std[0] == '/') {
569            $std = substr($std, 1);
570        }
571        if (strncmp($std, "./", 2) == 0) {
572            return substr($std, 2);
573        } else {
574            return $std;
575        }
576    }
577
578    /**
579     * Seek in the master archive to a matching file or directory
580     * @param string
581     */
582    protected function selectFile($path, $allowdirs = true)
583    {
584        $std = self::processFile($path);
585        if (isset(self::$_manifest[$this->_archiveName][$path])) {
586            $this->_setCurrentFile($path);
587            return true;
588        }
589        if (!$allowdirs) {
590            return 'Error: "' . $path . '" is not a file in phar "' . $this->_basename . '"';
591        }
592        foreach (self::$_manifest[$this->_archiveName] as $file => $info) {
593            if (empty($std) ||
594                  //$std is a directory
595                  strncmp($std.'/', $path, strlen($std)+1) == 0) {
596                $this->currentFilename = $this->internalFileLength = $this->currentStat = null;
597                return true;
598            }
599        }
600        return 'Error: "' . $path . '" not found in phar "' . $this->_basename . '"';
601    }
602
603    private function _setCurrentFile($path)
604    {
605        $this->currentStat = array(
606            2 => 0100444, // file mode, readable by all, writeable by none
607            4 => 0, // uid
608            5 => 0, // gid
609            7 => self::$_manifest[$this->_archiveName][$path][0], // size
610            9 => self::$_manifest[$this->_archiveName][$path][1], // creation time
611            );
612        $this->currentFilename = $path;
613        $this->internalFileLength = self::$_manifest[$this->_archiveName][$path][2];
614        // seek to offset of file header within the .phar
615        if (is_resource(@$this->fp)) {
616            fseek($this->fp, self::$_fileStart[$this->_archiveName] + self::$_manifest[$this->_archiveName][$path][7]);
617        }
618    }
619
620    private static function _fileExists($archive, $path)
621    {
622        return isset(self::$_manifest[$archive]) &&
623            isset(self::$_manifest[$archive][$path]);
624    }
625
626    private static function _filesize($archive, $path)
627    {
628        return self::$_manifest[$archive][$path][0];
629    }
630
631    /**
632     * Seek to a file within the master archive, and extract its contents
633     * @param string
634     * @return array|string an array containing an error message string is returned
635     *                      upon error, otherwise the file contents are returned
636     */
637    public function extractFile($path)
638    {
639        $this->fp = @fopen($this->_archiveName, "rb");
640        if (!$this->fp) {
641            return array('Error: cannot open phar "' . $this->_archiveName . '"');
642        }
643        if (($e = $this->selectFile($path, false)) === true) {
644            $data = '';
645            $count = $this->internalFileLength;
646            while ($count) {
647                if ($count < 8192) {
648                    $data .= @fread($this->fp, $count);
649                    $count = 0;
650                } else {
651                    $count -= 8192;
652                    $data .= @fread($this->fp, 8192);
653                }
654            }
655            @fclose($this->fp);
656            if (self::$_manifest[$this->_archiveName][$path][4] & self::GZ) {
657                $data = gzinflate($data);
658            } elseif (self::$_manifest[$this->_archiveName][$path][4] & self::BZ2) {
659                $data = bzdecompress($data);
660            }
661            if (!isset(self::$_manifest[$this->_archiveName][$path]['ok'])) {
662                if (strlen($data) != $this->currentStat[7]) {
663                    return array("Not valid internal .phar file (size error {$size} != " .
664                        $this->currentStat[7] . ")");
665                }
666                if (self::$_manifest[$this->_archiveName][$path][3] != sprintf("%u", crc32($data) & 0xffffffff)) {
667                    return array("Not valid internal .phar file (checksum error)");
668                }
669                self::$_manifest[$this->_archiveName][$path]['ok'] = true;
670            }
671            return $data;
672        } else {
673            @fclose($this->fp);
674            return array($e);
675        }
676    }
677
678    /**
679     * Parse urls like phar:///fullpath/to/my.phar/file.txt
680     *
681     * @param string $file
682     * @return false|array
683     */
684    static protected function parseUrl($file)
685    {
686        if (substr($file, 0, 7) != 'phar://') {
687            return false;
688        }
689        $file = substr($file, 7);
690    
691        $ret = array('scheme' => 'phar');
692        $pos_p = strpos($file, '.phar.php');
693        $pos_z = strpos($file, '.phar.gz');
694        $pos_b = strpos($file, '.phar.bz2');
695        if ($pos_p) {
696            if ($pos_z) {
697                return false;
698            }
699            $ret['host'] = substr($file, 0, $pos_p + strlen('.phar.php'));
700            $ret['path'] = substr($file, strlen($ret['host']));
701        } elseif ($pos_z) {
702            $ret['host'] = substr($file, 0, $pos_z + strlen('.phar.gz'));
703            $ret['path'] = substr($file, strlen($ret['host']));
704        } elseif ($pos_b) {
705            $ret['host'] = substr($file, 0, $pos_z + strlen('.phar.bz2'));
706            $ret['path'] = substr($file, strlen($ret['host']));
707        } elseif (($pos_p = strpos($file, ".phar")) !== false) {
708            $ret['host'] = substr($file, 0, $pos_p + strlen('.phar'));
709            $ret['path'] = substr($file, strlen($ret['host']));
710        } else {
711            return false;
712        }
713        if (!$ret['path']) {
714            $ret['path'] = '/';
715        }
716        return $ret;
717    }
718    
719    /**
720     * Locate the .phar archive in the include_path and detect the file to open within
721     * the archive.
722     *
723     * Possible parameters are phar://pharname.phar/filename_within_phar.ext
724     * @param string a file within the archive
725     * @return string the filename within the .phar to retrieve
726     */
727    public function initializeStream($file)
728    {
729        $file = self::processFile($file);
730        $info = @parse_url($file);
731        if (!$info) {
732            $info = self::parseUrl($file);
733        }
734        if (!$info) {
735            return false;
736        }
737        if (!isset($info['host'])) {
738            // malformed internal file
739            return false;
740        }
741        if (!isset(self::$_pharFiles[$info['host']]) &&
742              !isset(self::$_pharMapping[$info['host']])) {
743            try {
744                self::loadPhar($info['host']);
745                // use alias from here out
746                $info['host'] = self::$_pharFiles[$info['host']];
747            } catch (Exception $e) {
748                return false;
749            }
750        }
751        if (!isset($info['path'])) {
752            return false;
753        } elseif (strlen($info['path']) > 1) {
754            $info['path'] = substr($info['path'], 1);
755        }
756        if (isset(self::$_pharMapping[$info['host']])) {
757            $this->_basename = $info['host'];
758            $this->_archiveName = self::$_pharMapping[$info['host']][0];
759            $this->_compressed = self::$_pharMapping[$info['host']][1];
760        } elseif (isset(self::$_pharFiles[$info['host']])) {
761            $this->_archiveName = $info['host'];
762            $this->_basename = self::$_pharFiles[$info['host']];
763            $this->_compressed = self::$_pharMapping[$this->_basename][1];
764        }
765        $file = $info['path'];
766        return $file;
767    }
768
769    /**
770     * Open the requested file - PHP streams API
771     *
772     * @param string $file String provided by the Stream wrapper
773     * @access private
774     */
775    public function stream_open($file)
776    {
777        return $this->_streamOpen($file);
778    }
779
780    /**
781     * @param string filename to opne, or directory name
782     * @param bool if true, a directory will be matched, otherwise only files
783     *             will be matched
784     * @uses trigger_error()
785     * @return bool success of opening
786     * @access private
787     */
788    private function _streamOpen($file, $searchForDir = false)
789    {
790        $path = $this->initializeStream($file);
791        if (!$path) {
792            trigger_error('Error: Unknown phar in "' . $file . '"', E_USER_ERROR);
793        }
794        if (is_array($this->file = $this->extractFile($path))) {
795            trigger_error($this->file[0], E_USER_ERROR);
796            return false;
797        }
798        if ($path != $this->currentFilename) {
799            if (!$searchForDir) {
800                trigger_error("Cannot open '$file', is a directory", E_USER_ERROR);
801                return false;
802            } else {
803                $this->file = '';
804                return true;
805            }
806        }
807
808        if (!is_null($this->file) && $this->file !== false) {
809            return true;
810        } else {
811            return false;
812        }
813    }
814    
815    /**
816     * Read the data - PHP streams API
817     *
818     * @param int
819     * @access private
820     */
821    public function stream_read($count)
822    {
823        $ret = substr($this->file, $this->position, $count);
824        $this->position += strlen($ret);
825        return $ret;
826    }
827    
828    /**
829     * Whether we've hit the end of the file - PHP streams API
830     * @access private
831     */
832    function stream_eof()
833    {
834        return $this->position >= $this->currentStat[7];
835    }
836    
837    /**
838     * For seeking the stream - PHP streams API
839     * @param int
840     * @param SEEK_SET|SEEK_CUR|SEEK_END
841     * @access private
842     */
843    public function stream_seek($pos, $whence)
844    {
845        switch ($whence) {
846            case SEEK_SET:
847                if ($pos < 0) {
848                    return false;
849                }
850                $this->position = $pos;
851                break;
852            case SEEK_CUR:
853                if ($pos + $this->currentStat[7] < 0) {
854                    return false;
855                }
856                $this->position += $pos;
857                break;
858            case SEEK_END:
859                if ($pos + $this->currentStat[7] < 0) {
860                    return false;
861                }
862                $this->position = $pos + $this->currentStat[7];
863                break;
864            default:
865                return false;
866        }
867        return true;
868    }
869    
870    /**
871     * The current position in the stream - PHP streams API
872     * @access private
873     */
874    public function stream_tell()
875    {
876        return $this->position;
877    }
878
879    /**
880     * The result of an fstat call, returns mod time from creation, and file size -
881     * PHP streams API
882     * @uses _stream_stat()
883     * @access private
884     */
885    public function stream_stat()
886    {
887        return $this->_stream_stat();
888    }
889
890    /**
891     * Retrieve statistics on a file or directory within the .phar
892     * @param string file/directory to stat
893     * @access private
894     */
895    public function _stream_stat($file = null)
896    {
897        $std = $file ? self::processFile($file) : $this->currentFilename;
898        if ($file) {
899            if (isset(self::$_manifest[$this->_archiveName][$file])) {
900                $this->_setCurrentFile($file);
901                $isdir = false;
902            } else {
903                do {
904                    $isdir = false;
905                    if ($file == '/') {
906                        break;
907                    }
908                    foreach (self::$_manifest[$this->_archiveName] as $path => $info) {
909                        if (strpos($path, $file) === 0) {
910                            if (strlen($path) > strlen($file) &&
911                                  $path[strlen($file)] == '/') {
912                                break 2;
913                            }
914                        }
915                    }
916                    // no files exist and no directories match this string
917                    return false;
918                } while (false);
919                $isdir = true;
920            }
921        } else {
922            $isdir = false; // open streams must be files
923        }
924        $mode = $isdir ? 0040444 : 0100444;
925        // 040000 = dir, 010000 = file
926        // everything is readable, nothing is writeable
927        return array(
928           0, 0, $mode, 0, 0, 0, 0, 0, 0, 0, 0, 0, // non-associative indices
929           'dev' => 0, 'ino' => 0,
930           'mode' => $mode,
931           'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'blksize' => 0, 'blocks' => 0,
932           'size' => $this->currentStat[7],
933           'atime' => $this->currentStat[9],
934           'mtime' => $this->currentStat[9],
935           'ctime' => $this->currentStat[9],
936           );
937    }
938
939    /**
940     * Stat a closed file or directory - PHP streams API
941     * @param string
942     * @param int
943     * @access private
944     */
945    public function url_stat($url, $flags)
946    {
947        $path = $this->initializeStream($url);
948        return $this->_stream_stat($path);
949    }
950
951    /**
952     * Open a directory in the .phar for reading - PHP streams API
953     * @param string directory name
954     * @access private
955     */
956    public function dir_opendir($path)
957    {
958        $info = @parse_url($path);
959        if (!$info) {
960            $info = self::parseUrl($path);
961            if (!$info) {
962                trigger_error('Error: "' . $path . '" is a file, and cannot be opened with opendir',
963                    E_USER_ERROR);
964                return false;
965            }
966        }
967        $path = !empty($info['path']) ?
968            $info['host'] . $info['path'] : $info['host'] . '/';
969        $path = $this->initializeStream('phar://' . $path);
970        if (isset(self::$_manifest[$this->_archiveName][$path])) {
971            trigger_error('Error: "' . $path . '" is a file, and cannot be opened with opendir',
972                E_USER_ERROR);
973            return false;
974        }
975        if ($path == false) {
976            trigger_error('Error: Unknown phar in "' . $file . '"', E_USER_ERROR);
977            return false;
978        }
979        $this->fp = @fopen($this->_archiveName, "rb");
980        if (!$this->fp) {
981            trigger_error('Error: cannot open phar "' . $this->_archiveName . '"');
982            return false;
983        }
984        $this->_dirFiles = array();
985        foreach (self::$_manifest[$this->_archiveName] as $file => $info) {
986            if ($path == '/') {
987                if (strpos($file, '/')) {
988                    $a = explode('/', $file);
989                    $this->_dirFiles[array_shift($a)] = true;
990                } else {
991                    $this->_dirFiles[$file] = true;
992                }
993            } elseif (strpos($file, $path) === 0) {
994                $fname = substr($file, strlen($path) + 1);
995                if (strpos($fname, '/')) {
996                    // this is a directory
997                    $a = explode('/', $fname);
998                    $this->_dirFiles[array_shift($a)] = true;
999                } elseif ($file[strlen($path)] == '/') {
1000                    // this is a file
1001                    $this->_dirFiles[$fname] = true;
1002                }
1003            }
1004        }
1005        @fclose($this->fp);
1006        if (!count($this->_dirFiles)) {
1007            return false;
1008        }
1009        @uksort($this->_dirFiles, 'strnatcmp');
1010        return true;
1011    }
1012
1013    /**
1014     * Read the next directory entry - PHP streams API
1015     * @access private
1016     */
1017    public function dir_readdir()
1018    {
1019        $ret = key($this->_dirFiles);
1020        @next($this->_dirFiles);
1021        if (!$ret) {
1022            return false;
1023        }
1024        return $ret;
1025    }
1026
1027    /**
1028     * Close a directory handle opened with opendir() - PHP streams API
1029     * @access private
1030     */
1031    public function dir_closedir()
1032    {
1033        $this->_dirFiles = array();
1034        return true;
1035    }
1036
1037    /**
1038     * Rewind to the first directory entry - PHP streams API
1039     * @access private
1040     */
1041    public function dir_rewinddir()
1042    {
1043        @reset($this->_dirFiles);
1044        return true;
1045    }
1046
1047    /**
1048     * API version of this class
1049     * @return string
1050     */
1051    public static final function APIVersion()
1052    {
1053        return '1.0.0';
1054    }
1055
1056    /**
1057     * Retrieve Phar-specific metadata for a Phar archive
1058     *
1059     * @param string $phar full path to Phar archive, or alias
1060     * @return null|mixed The value that was serialized for the Phar
1061     *                    archive's metadata
1062     * @throws Exception
1063     */
1064    public static function getPharMetadata($phar)
1065    {
1066        if (isset(self::$_pharFiles[$phar])) {
1067            $phar = self::$_pharFiles[$phar];
1068        }
1069        if (!isset(self::$_pharMapping[$phar])) {
1070            throw new Exception('Unknown Phar archive: "' . $phar . '"');
1071        }
1072        return self::$_pharMapping[$phar][4];
1073    }
1074
1075    /**
1076     * Retrieve File-specific metadata for a Phar archive file
1077     *
1078     * @param string $phar full path to Phar archive, or alias
1079     * @param string $file relative path to file within Phar archive
1080     * @return null|mixed The value that was serialized for the Phar
1081     *                    archive's metadata
1082     * @throws Exception
1083     */
1084    public static function getFileMetadata($phar, $file)
1085    {
1086        if (!isset(self::$_pharFiles[$phar])) {
1087            if (!isset(self::$_pharMapping[$phar])) {
1088                throw new Exception('Unknown Phar archive: "' . $phar . '"');
1089            }
1090            $phar = self::$_pharMapping[$phar][0];
1091        }
1092        if (!isset(self::$_manifest[$phar])) {
1093            throw new Exception('Unknown Phar: "' . $phar . '"');
1094        }
1095        $file = self::processFile($file);
1096        if (!isset(self::$_manifest[$phar][$file])) {
1097            throw new Exception('Unknown file "' . $file . '" within Phar "'. $phar . '"');
1098        }
1099        return self::$_manifest[$phar][$file][6];
1100    }
1101
1102    /**
1103     * @return list of supported signature algorithmns.
1104     */
1105    public static function getsupportedsignatures()
1106    {
1107        $ret = array('MD5', 'SHA-1');
1108        if (extension_loaded('hash')) {
1109            $ret[] = 'SHA-256';
1110            $ret[] = 'SHA-512';
1111        }
1112        return $ret;
1113    }
1114}}
1115if (!class_exists('Phar')) {
1116    PHP_Archive::mapPhar(null, 42421                   );
1117} else {
1118    try {
1119        Phar::mapPhar();
1120    } catch (Exception $e) {
1121        echo $e->getMessage();
1122    }
1123}
1124if (class_exists('PHP_Archive') && !in_array('phar', stream_get_wrappers())) {
1125    stream_wrapper_register('phar', 'PHP_Archive');
1126}
1127
1128@ini_set('memory_limit', -1);
1129if (extension_loaded('phar')) {if (isset($_SERVER) && isset($_SERVER['REQUEST_URI'])) {
1130    $uri = parse_url($_SERVER['REQUEST_URI']);
1131    $archive = realpath($_SERVER['SCRIPT_FILENAME']);
1132    $subpath = str_replace('/' . basename($archive), '', $uri['path']);
1133    $mimetypes = array (
1134  'aif' => 'audio/x-aiff',
1135  'aiff' => 'audio/x-aiff',
1136  'arc' => 'application/octet-stream',
1137  'arj' => 'application/octet-stream',
1138  'art' => 'image/x-jg',
1139  'asf' => 'video/x-ms-asf',
1140  'asx' => 'video/x-ms-asf',
1141  'avi' => 'video/avi',
1142  'bin' => 'application/octet-stream',
1143  'bm' => 'image/bmp',
1144  'bmp' => 'image/bmp',
1145  'bz2' => 'application/x-bzip2',
1146  'css' => 'text/css',
1147  'doc' => 'application/msword',
1148  'dot' => 'application/msword',
1149  'dv' => 'video/x-dv',
1150  'dvi' => 'application/x-dvi',
1151  'eps' => 'application/postscript',
1152  'exe' => 'application/octet-stream',
1153  'gif' => 'image/gif',
1154  'gz' => 'application/x-gzip',
1155  'gzip' => 'application/x-gzip',
1156  'htm' => 'text/html',
1157  'html' => 'text/html',
1158  'ico' => 'image/x-icon',
1159  'jpe' => 'image/jpeg',
1160  'jpg' => 'image/jpeg',
1161  'jpeg' => 'image/jpeg',
1162  'js' => 'application/x-javascript',
1163  'log' => 'text/plain',
1164  'mid' => 'audio/x-midi',
1165  'mov' => 'video/quicktime',
1166  'mp2' => 'audio/mpeg',
1167  'mp3' => 'audio/mpeg3',
1168  'mpg' => 'audio/mpeg',
1169  'pdf' => 'aplication/pdf',
1170  'png' => 'image/png',
1171  'rtf' => 'application/rtf',
1172  'tif' => 'image/tiff',
1173  'tiff' => 'image/tiff',
1174  'txt' => 'text/plain',
1175  'xml' => 'text/xml',
1176);
1177    $phpfiles = array (
1178  'php' => true,
1179);
1180    $phpsfiles = array (
1181  'phps' => true,
1182);
1183    $deny = array (
1184  0 => '/.+\\.inc$/',
1185);
1186    $subpath = str_replace('/' . basename($archive), '', $uri['path']);
1187    if (!$subpath || $subpath == '/') {
1188        $subpath = '/PEAR.php';
1189    }
1190    if ($subpath[0] != '/') {
1191        $subpath = '/' . $subpath;
1192    }
1193    if (!@file_exists('phar://' . $archive . $subpath)) {
1194        header("HTTP/1.0 404 Not Found");
1195        exit;
1196    }
1197
1198    foreach ($deny as $pattern) {
1199        if (preg_match($pattern, $subpath)) {
1200            header("HTTP/1.0 404 Not Found");
1201            exit;
1202        }
1203    }
1204    $inf = pathinfo(basename($subpath));
1205    if (!isset($inf['extension'])) {
1206        header('Content-Type: text/plain');
1207        header('Content-Length: ' . filesize('phar://' . $archive . $subpath));
1208        readfile('phar://' . $archive . $subpath);
1209        exit;
1210    }
1211    if (isset($phpfiles[$inf['extension']])) {
1212        include 'phar://' . $archive . '/' . $subpath;
1213        exit;
1214    }
1215    if (isset($mimetypes[$inf['extension']])) {
1216        header('Content-Type: ' . $mimetypes[$inf['extension']]);
1217        header('Content-Length: ' . filesize('phar://' . $archive . $subpath));
1218        readfile('phar://' . $archive . $subpath);
1219        exit;
1220    }
1221    if (isset($phpsfiles[$inf['extension']])) {
1222        header('Content-Type: text/html');
1223        $c = highlight_file('phar://' . $archive . $subpath, true);
1224        header('Content-Length: ' . strlen($c));
1225        echo $c;
1226        exit;
1227    }
1228    header('Content-Type: text/plain');
1229    header('Content-Length: ' . filesize('phar://' . $archive . '/' . $subpath));
1230    readfile('phar://' . $archive . '/' . $subpath);
1231    exit;
1232}} else {if (!empty($_SERVER['REQUEST_URI'])) {PHP_Archive::webFrontController('PEAR.php');exit;}}
1233
1234
1235
1236require_once 'phar://install-pear-nozlib.phar/index.php';
1237__HALT_COMPILER();\kinstall-pear-nozlib.pharArchive/Tar.php��PQ���|OmArchive_Tar-1.3.11.tar��PQ�,��mConsole/Getopt.php�4�PQ�4��l1mConsole_Getopt-1.3.1.tarT�PQTΞm	index.phpm*�PQm*em�mOS/Guess.php�)�PQ�)LO�mPEAR-1.9.4.tar��PQ�3]�mPEAR.php���PQ���JsmPEAR/Autoloader.php��PQ���mPEAR/Builder.php�A�PQ�A�thmPEAR/ChannelFile.php���PQ���MomPEAR/ChannelFile/Parser.php<�PQ<I�$�mPEAR/Command.php
12382�PQ
12392%��CmPEAR/Command/Auth.php1240�PQ�
1241s`.mPEAR/Command/Auth.xml��PQ��4�{mPEAR/Command/Build.phpu	�PQu	�W(	mPEAR/Command/Build.xml�PQ�c�mPEAR/Command/Channels.php'��PQ'����mPEAR/Command/Channels.xmlz�PQz�w��mPEAR/Command/Common.php� �PQ� �dM�mPEAR/Command/Config.php�<�PQ�<�`mPEAR/Command/Config.xml6
1242�PQ6
1243���smPEAR/Command/Install.php2��PQ2�bԞCmPEAR/Command/Install.xml~!�PQ~!2�VmPEAR/Command/Mirror.php�PQp�7mPEAR/Command/Mirror.xmli�PQi݄<mPEAR/Command/Package.php���PQ����CmPEAR/Command/Package.xml6�PQ6Z hmPEAR/Command/Pickle.php'?�PQ'?��[VmPEAR/Command/Pickle.xml��PQ�d֣SmPEAR/Command/Registry.php}��PQ}��:��mPEAR/Command/Registry.xml��PQ����umPEAR/Command/Remote.php
1244v�PQ
1245v[/7<mPEAR/Command/Remote.xml��PQ�
1246�\�mPEAR/Command/Test.phpJ/�PQJ/p�mPEAR/Command/Test.xmli�PQi�%FmPEAR/Common.phpf�PQfd\��mPEAR/Config.php-
1247�PQ-
1248��>�mPEAR/Dependency2.php��PQ�ՌFmPEAR/DependencyDB.php�^�PQ�^<��mPEAR/Downloader.php�PQ@C�umPEAR/Downloader/Package.php�*�PQ�*9���mPEAR/ErrorStack.php΄�PQ΄�qa6mPEAR/Exception.php�6�PQ�6,��mPEAR/FixPHP5PEARWarnings.php��PQ��q�CmPEAR/Frontend.phpd�PQd�3/.mPEAR/Frontend/CLI.php�d�PQ�d�x�}mPEAR/Installer.php��PQ��_mPEAR/Installer/Role.php��PQ�Y��*mPEAR/Installer/Role/Cfg.php��PQ����mPEAR/Installer/Role/Cfg.xml��PQ���/mPEAR/Installer/Role/Common.php��PQ���mPEAR/Installer/Role/Data.phpS�PQS�܌mPEAR/Installer/Role/Data.xml��PQ�f�szmPEAR/Installer/Role/Doc.phpP�PQP_��/mPEAR/Installer/Role/Doc.xml��PQ�h&P*mPEAR/Installer/Role/Ext.phpP�PQP^z�wmPEAR/Installer/Role/Ext.xmlB�PQB���mPEAR/Installer/Role/Php.phpP�PQP��s�mPEAR/Installer/Role/Php.xml��PQ�z�q�mPEAR/Installer/Role/Script.phpY�PQY�2"�mPEAR/Installer/Role/Script.xml��PQ�@v��mPEAR/Installer/Role/Src.php��PQ���mPEAR/Installer/Role/Src.xml"�PQ"p��mPEAR/Installer/Role/Test.phpS�PQS�x�_mPEAR/Installer/Role/Test.xml��PQ�B] mPEAR/Installer/Role/Www.phpL�PQL�a�mPEAR/Installer/Role/Www.xml��PQ�>1H�mPEAR/PackageFile.php�>�PQ�>�Tʁm!PEAR/PackageFile/Generator/v1.phpv��PQv��Aw�m!PEAR/PackageFile/Generator/v2.php_��PQ_��k9amPEAR/PackageFile/Parser/v1.php�@�PQ�@;#�mPEAR/PackageFile/Parser/v2.php��PQ����mPEAR/PackageFile/v1.php_��PQ_�����mPEAR/PackageFile/v2.phpo�PQo��R�mPEAR/PackageFile/v2/rw.php���PQ��X;��m!PEAR/PackageFile/v2/Validator.phpiP�PQiPy��%mPEAR/Packager.php��PQ�uw�mPEAR/Registry.php8+�PQ8+>��m
1249PEAR/REST.php�C�PQ�C�Y�lmPEAR/REST/10.phpC��PQC��E%WmPEAR/REST/11.phpv,�PQv,��gsmPEAR/REST/13.php�-�PQ�-A�KmPEAR/RunTest.php��PQ��U�9mPEAR/Start.php�7�PQ�7 �}#mPEAR/Start/CLI.phpmR�PQmR�F�mPEAR/Task/Common.php�PQ�pM�mPEAR/Task/Postinstallscript.php�8�PQ�8��wm"PEAR/Task/Postinstallscript/rw.php��PQ���SmPEAR/Task/Replace.php��PQ���\mPEAR/Task/Replace/rw.phpr�PQr���mPEAR/Task/Unixeol.php	�PQ	Lՙ%mPEAR/Task/Unixeol/rw.php��PQ��uL mPEAR/Task/Windowseol.php	�PQ	�WJmPEAR/Task/Windowseol/rw.php��PQ�h�mPEAR/Validate.phpwV�PQwV��>mPEAR/Validator/PECL.php��PQ�b��nmPEAR/XMLParser.phpK�PQK���m	PEAR5.php?�PQ?�x�mStructures/Graph.php��PQ�r�*m,Structures/Graph/Manipulator/AcyclicTest.php��PQ�1s�m2Structures/Graph/Manipulator/TopologicalSorter.php��PQ���E�mStructures/Graph/Node.phpr+�PQr+�D_mStructures_Graph-1.0.4.tar,�PQ,��f�m
1250System.php]O�PQ]OyԽmXML/Util.php
1251w�PQ
1252w���omXML_Util-1.2.1.tar��PQ�7l�\m<?php
1253/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
1254
1255/**
1256 * File::CSV
1257 *
1258 * PHP versions 4 and 5
1259 *
1260 * Copyright (c) 1997-2008,
1261 * Vincent Blavet <vincent@phpconcept.net>
1262 * All rights reserved.
1263 *
1264 * Redistribution and use in source and binary forms, with or without
1265 * modification, are permitted provided that the following conditions are met:
1266 *
1267 *     * Redistributions of source code must retain the above copyright notice,
1268 *       this list of conditions and the following disclaimer.
1269 *     * Redistributions in binary form must reproduce the above copyright
1270 *       notice, this list of conditions and the following disclaimer in the
1271 *       documentation and/or other materials provided with the distribution.
1272 *
1273 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1274 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1275 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1276 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
1277 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1278 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1279 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
1280 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
1281 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1282 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1283 *
1284 * @category  File_Formats
1285 * @package   Archive_Tar
1286 * @author    Vincent Blavet <vincent@phpconcept.net>
1287 * @copyright 1997-2010 The Authors
1288 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
1289 * @version   CVS: $Id$
1290 * @link      http://pear.php.net/package/Archive_Tar
1291 */
1292
1293require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
1294
1295define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
1296define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
1297
1298/**
1299* Creates a (compressed) Tar archive
1300*
1301* @package Archive_Tar
1302* @author  Vincent Blavet <vincent@phpconcept.net>
1303* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
1304* @version $Revision$
1305*/
1306class Archive_Tar extends PEAR
1307{
1308    /**
1309    * @var string Name of the Tar
1310    */
1311    var $_tarname='';
1312
1313    /**
1314    * @var boolean if true, the Tar file will be gzipped
1315    */
1316    var $_compress=false;
1317
1318    /**
1319    * @var string Type of compression : 'none', 'gz' or 'bz2'
1320    */
1321    var $_compress_type='none';
1322
1323    /**
1324    * @var string Explode separator
1325    */
1326    var $_separator=' ';
1327
1328    /**
1329    * @var file descriptor
1330    */
1331    var $_file=0;
1332
1333    /**
1334    * @var string Local Tar name of a remote Tar (http:// or ftp://)
1335    */
1336    var $_temp_tarname='';
1337
1338    /**
1339    * @var string regular expression for ignoring files or directories
1340    */
1341    var $_ignore_regexp='';
1342
1343    /**
1344     * @var object PEAR_Error object
1345     */
1346    var $error_object=null; 
1347
1348    // {{{ constructor
1349    /**
1350    * Archive_Tar Class constructor. This flavour of the constructor only
1351    * declare a new Archive_Tar object, identifying it by the name of the
1352    * tar file.
1353    * If the compress argument is set the tar will be read or created as a
1354    * gzip or bz2 compressed TAR file.
1355    *
1356    * @param string $p_tarname  The name of the tar archive to create
1357    * @param string $p_compress can be null, 'gz' or 'bz2'. This
1358    *               parameter indicates if gzip or bz2 compression
1359    *               is required.  For compatibility reason the
1360    *               boolean value 'true' means 'gz'.
1361    *
1362    * @access public
1363    */
1364    function Archive_Tar($p_tarname, $p_compress = null)
1365    {
1366        $this->PEAR();
1367        $this->_compress = false;
1368        $this->_compress_type = 'none';
1369        if (($p_compress === null) || ($p_compress == '')) {
1370            if (@file_exists($p_tarname)) {
1371                if ($fp = @fopen($p_tarname, "rb")) {
1372                    // look for gzip magic cookie
1373                    $data = fread($fp, 2);
1374                    fclose($fp);
1375                    if ($data == "\37\213") {
1376                        $this->_compress = true;
1377                        $this->_compress_type = 'gz';
1378                        // No sure it's enought for a magic code ....
1379                    } elseif ($data == "BZ") {
1380                        $this->_compress = true;
1381                        $this->_compress_type = 'bz2';
1382                    }
1383                }
1384            } else {
1385                // probably a remote file or some file accessible
1386                // through a stream interface
1387                if (substr($p_tarname, -2) == 'gz') {
1388                    $this->_compress = true;
1389                    $this->_compress_type = 'gz';
1390                } elseif ((substr($p_tarname, -3) == 'bz2') ||
1391                          (substr($p_tarname, -2) == 'bz')) {
1392                    $this->_compress = true;
1393                    $this->_compress_type = 'bz2';
1394                }
1395            }
1396        } else {
1397            if (($p_compress === true) || ($p_compress == 'gz')) {
1398                $this->_compress = true;
1399                $this->_compress_type = 'gz';
1400            } else if ($p_compress == 'bz2') {
1401                $this->_compress = true;
1402                $this->_compress_type = 'bz2';
1403            } else {
1404                $this->_error("Unsupported compression type '$p_compress'\n".
1405                    "Supported types are 'gz' and 'bz2'.\n");
1406                return false;
1407            }
1408        }
1409        $this->_tarname = $p_tarname;
1410        if ($this->_compress) { // assert zlib or bz2 extension support
1411            if ($this->_compress_type == 'gz')
1412                $extname = 'zlib';
1413            else if ($this->_compress_type == 'bz2')
1414                $extname = 'bz2';
1415
1416            if (!extension_loaded($extname)) {
1417                PEAR::loadExtension($extname);
1418            }
1419            if (!extension_loaded($extname)) {
1420                $this->_error("The extension '$extname' couldn't be found.\n".
1421                    "Please make sure your version of PHP was built ".
1422                    "with '$extname' support.\n");
1423                return false;
1424            }
1425        }
1426    }
1427    // }}}
1428
1429    // {{{ destructor
1430    function _Archive_Tar()
1431    {
1432        $this->_close();
1433        // ----- Look for a local copy to delete
1434        if ($this->_temp_tarname != '')
1435            @unlink($this->_temp_tarname);
1436        $this->_PEAR();
1437    }
1438    // }}}
1439
1440    // {{{ create()
1441    /**
1442    * This method creates the archive file and add the files / directories
1443    * that are listed in $p_filelist.
1444    * If a file with the same name exist and is writable, it is replaced
1445    * by the new tar.
1446    * The method return false and a PEAR error text.
1447    * The $p_filelist parameter can be an array of string, each string
1448    * representing a filename or a directory name with their path if
1449    * needed. It can also be a single string with names separated by a
1450    * single blank.
1451    * For each directory added in the archive, the files and
1452    * sub-directories are also added.
1453    * See also createModify() method for more details.
1454    *
1455    * @param array $p_filelist An array of filenames and directory names, or a
1456    *              single string with names separated by a single
1457    *              blank space.
1458    *
1459    * @return true on success, false on error.
1460    * @see    createModify()
1461    * @access public
1462    */
1463    function create($p_filelist)
1464    {
1465        return $this->createModify($p_filelist, '', '');
1466    }
1467    // }}}
1468
1469    // {{{ add()
1470    /**
1471    * This method add the files / directories that are listed in $p_filelist in
1472    * the archive. If the archive does not exist it is created.
1473    * The method return false and a PEAR error text.
1474    * The files and directories listed are only added at the end of the archive,
1475    * even if a file with the same name is already archived.
1476    * See also createModify() method for more details.
1477    *
1478    * @param array $p_filelist An array of filenames and directory names, or a
1479    *              single string with names separated by a single
1480    *              blank space.
1481    *
1482    * @return true on success, false on error.
1483    * @see    createModify()
1484    * @access public
1485    */
1486    function add($p_filelist)
1487    {
1488        return $this->addModify($p_filelist, '', '');
1489    }
1490    // }}}
1491
1492    // {{{ extract()
1493    function extract($p_path='', $p_preserve=false)
1494    {
1495        return $this->extractModify($p_path, '', $p_preserve);
1496    }
1497    // }}}
1498
1499    // {{{ listContent()
1500    function listContent()
1501    {
1502        $v_list_detail = array();
1503
1504        if ($this->_openRead()) {
1505            if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
1506                unset($v_list_detail);
1507                $v_list_detail = 0;
1508            }
1509            $this->_close();
1510        }
1511
1512        return $v_list_detail;
1513    }
1514    // }}}
1515
1516    // {{{ createModify()
1517    /**
1518    * This method creates the archive file and add the files / directories
1519    * that are listed in $p_filelist.
1520    * If the file already exists and is writable, it is replaced by the
1521    * new tar. It is a create and not an add. If the file exists and is
1522    * read-only or is a directory it is not replaced. The method return
1523    * false and a PEAR error text.
1524    * The $p_filelist parameter can be an array of string, each string
1525    * representing a filename or a directory name with their path if
1526    * needed. It can also be a single string with names separated by a
1527    * single blank.
1528    * The path indicated in $p_remove_dir will be removed from the
1529    * memorized path of each file / directory listed when this path
1530    * exists. By default nothing is removed (empty path '')
1531    * The path indicated in $p_add_dir will be added at the beginning of
1532    * the memorized path of each file / directory listed. However it can
1533    * be set to empty ''. The adding of a path is done after the removing
1534    * of path.
1535    * The path add/remove ability enables the user to prepare an archive
1536    * for extraction in a different path than the origin files are.
1537    * See also addModify() method for file adding properties.
1538    *
1539    * @param array  $p_filelist   An array of filenames and directory names,
1540    *                             or a single string with names separated by
1541    *                             a single blank space.
1542    * @param string $p_add_dir    A string which contains a path to be added
1543    *                             to the memorized path of each element in
1544    *                             the list.
1545    * @param string $p_remove_dir A string which contains a path to be
1546    *                             removed from the memorized path of each
1547    *                             element in the list, when relevant.
1548    *
1549    * @return boolean true on success, false on error.
1550    * @access public
1551    * @see addModify()
1552    */
1553    function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
1554    {
1555        $v_result = true;
1556
1557        if (!$this->_openWrite())
1558            return false;
1559
1560        if ($p_filelist != '') {
1561            if (is_array($p_filelist))
1562                $v_list = $p_filelist;
1563            elseif (is_string($p_filelist))
1564                $v_list = explode($this->_separator, $p_filelist);
1565            else {
1566                $this->_cleanFile();
1567                $this->_error('Invalid file list');
1568                return false;
1569            }
1570
1571            $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
1572        }
1573
1574        if ($v_result) {
1575            $this->_writeFooter();
1576            $this->_close();
1577        } else
1578            $this->_cleanFile();
1579
1580        return $v_result;
1581    }
1582    // }}}
1583
1584    // {{{ addModify()
1585    /**
1586    * This method add the files / directories listed in $p_filelist at the
1587    * end of the existing archive. If the archive does not yet exists it
1588    * is created.
1589    * The $p_filelist parameter can be an array of string, each string
1590    * representing a filename or a directory name with their path if
1591    * needed. It can also be a single string with names separated by a
1592    * single blank.
1593    * The path indicated in $p_remove_dir will be removed from the
1594    * memorized path of each file / directory listed when this path
1595    * exists. By default nothing is removed (empty path '')
1596    * The path indicated in $p_add_dir will be added at the beginning of
1597    * the memorized path of each file / directory listed. However it can
1598    * be set to empty ''. The adding of a path is done after the removing
1599    * of path.
1600    * The path add/remove ability enables the user to prepare an archive
1601    * for extraction in a different path than the origin files are.
1602    * If a file/dir is already in the archive it will only be added at the
1603    * end of the archive. There is no update of the existing archived
1604    * file/dir. However while extracting the archive, the last file will
1605    * replace the first one. This results in a none optimization of the
1606    * archive size.
1607    * If a file/dir does not exist the file/dir is ignored. However an
1608    * error text is send to PEAR error.
1609    * If a file/dir is not readable the file/dir is ignored. However an
1610    * error text is send to PEAR error.
1611    *
1612    * @param array  $p_filelist   An array of filenames and directory
1613    *                             names, or a single string with names
1614    *                             separated by a single blank space.
1615    * @param string $p_add_dir    A string which contains a path to be
1616    *                             added to the memorized path of each
1617    *                             element in the list.
1618    * @param string $p_remove_dir A string which contains a path to be
1619    *                             removed from the memorized path of
1620    *                             each element in the list, when
1621    *                             relevant.
1622    *
1623    * @return true on success, false on error.
1624    * @access public
1625    */
1626    function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
1627    {
1628        $v_result = true;
1629
1630        if (!$this->_isArchive())
1631            $v_result = $this->createModify($p_filelist, $p_add_dir,
1632                                            $p_remove_dir);
1633        else {
1634            if (is_array($p_filelist))
1635                $v_list = $p_filelist;
1636            elseif (is_string($p_filelist))
1637                $v_list = explode($this->_separator, $p_filelist);
1638            else {
1639                $this->_error('Invalid file list');
1640                return false;
1641            }
1642
1643            $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
1644        }
1645
1646        return $v_result;
1647    }
1648    // }}}
1649
1650    // {{{ addString()
1651    /**
1652    * This method add a single string as a file at the
1653    * end of the existing archive. If the archive does not yet exists it
1654    * is created.
1655    *
1656    * @param string $p_filename A string which contains the full
1657    *                           filename path that will be associated
1658    *                           with the string.
1659    * @param string $p_string   The content of the file added in
1660    *                           the archive.
1661    * @param int    $p_datetime A custom date/time (unix timestamp)
1662    *                           for the file (optional).
1663    *
1664    * @return true on success, false on error.
1665    * @access public
1666    */
1667    function addString($p_filename, $p_string, $p_datetime = false)
1668    {
1669        $v_result = true;
1670
1671        if (!$this->_isArchive()) {
1672            if (!$this->_openWrite()) {
1673                return false;
1674            }
1675            $this->_close();
1676        }
1677
1678        if (!$this->_openAppend())
1679            return false;
1680
1681        // Need to check the get back to the temporary file ? ....
1682        $v_result = $this->_addString($p_filename, $p_string, $p_datetime);
1683
1684        $this->_writeFooter();
1685
1686        $this->_close();
1687
1688        return $v_result;
1689    }
1690    // }}}
1691
1692    // {{{ extractModify()
1693    /**
1694    * This method extract all the content of the archive in the directory
1695    * indicated by $p_path. When relevant the memorized path of the
1696    * files/dir can be modified by removing the $p_remove_path path at the
1697    * beginning of the file/dir path.
1698    * While extracting a file, if the directory path does not exists it is
1699    * created.
1700    * While extracting a file, if the file already exists it is replaced
1701    * without looking for last modification date.
1702    * While extracting a file, if the file already exists and is write
1703    * protected, the extraction is aborted.
1704    * While extracting a file, if a directory with the same name already
1705    * exists, the extraction is aborted.
1706    * While extracting a directory, if a file with the same name already
1707    * exists, the extraction is aborted.
1708    * While extracting a file/directory if the destination directory exist
1709    * and is write protected, or does not exist but can not be created,
1710    * the extraction is aborted.
1711    * If after extraction an extracted file does not show the correct
1712    * stored file size, the extraction is aborted.
1713    * When the extraction is aborted, a PEAR error text is set and false
1714    * is returned. However the result can be a partial extraction that may
1715    * need to be manually cleaned.
1716    *
1717    * @param string  $p_path        The path of the directory where the
1718    *                               files/dir need to by extracted.
1719    * @param string  $p_remove_path Part of the memorized path that can be
1720    *                               removed if present at the beginning of
1721    *                               the file/dir path.
1722    * @param boolean $p_preserve    Preserve user/group ownership of files
1723    *
1724    * @return boolean true on success, false on error.
1725    * @access public
1726    * @see    extractList()
1727    */
1728    function extractModify($p_path, $p_remove_path, $p_preserve=false)
1729    {
1730        $v_result = true;
1731        $v_list_detail = array();
1732
1733        if ($v_result = $this->_openRead()) {
1734            $v_result = $this->_extractList($p_path, $v_list_detail,
1735                "complete", 0, $p_remove_path, $p_preserve);
1736            $this->_close();
1737        }
1738
1739        return $v_result;
1740    }
1741    // }}}
1742
1743    // {{{ extractInString()
1744    /**
1745    * This method extract from the archive one file identified by $p_filename.
1746    * The return value is a string with the file content, or NULL on error.
1747    *
1748    * @param string $p_filename The path of the file to extract in a string.
1749    *
1750    * @return a string with the file content or NULL.
1751    * @access public
1752    */
1753    function extractInString($p_filename)
1754    {
1755        if ($this->_openRead()) {
1756            $v_result = $this->_extractInString($p_filename);
1757            $this->_close();
1758        } else {
1759            $v_result = null;
1760        }
1761
1762        return $v_result;
1763    }
1764    // }}}
1765
1766    // {{{ extractList()
1767    /**
1768    * This method extract from the archive only the files indicated in the
1769    * $p_filelist. These files are extracted in the current directory or
1770    * in the directory indicated by the optional $p_path parameter.
1771    * If indicated the $p_remove_path can be used in the same way as it is
1772    * used in extractModify() method.
1773    *
1774    * @param array   $p_filelist    An array of filenames and directory names,
1775    *                               or a single string with names separated
1776    *                               by a single blank space.
1777    * @param string  $p_path        The path of the directory where the
1778    *                               files/dir need to by extracted.
1779    * @param string  $p_remove_path Part of the memorized path that can be
1780    *                               removed if present at the beginning of
1781    *                               the file/dir path.
1782    * @param boolean $p_preserve    Preserve user/group ownership of files
1783    *
1784    * @return true on success, false on error.
1785    * @access public
1786    * @see    extractModify()
1787    */
1788    function extractList($p_filelist, $p_path='', $p_remove_path='', $p_preserve=false)
1789    {
1790        $v_result = true;
1791        $v_list_detail = array();
1792
1793        if (is_array($p_filelist))
1794            $v_list = $p_filelist;
1795        elseif (is_string($p_filelist))
1796            $v_list = explode($this->_separator, $p_filelist);
1797        else {
1798            $this->_error('Invalid string list');
1799            return false;
1800        }
1801
1802        if ($v_result = $this->_openRead()) {
1803            $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
1804                $v_list, $p_remove_path, $p_preserve);
1805            $this->_close();
1806        }
1807
1808        return $v_result;
1809    }
1810    // }}}
1811
1812    // {{{ setAttribute()
1813    /**
1814    * This method set specific attributes of the archive. It uses a variable
1815    * list of parameters, in the format attribute code + attribute values :
1816    * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
1817    *
1818    * @param mixed $argv variable list of attributes and values
1819    *
1820    * @return true on success, false on error.
1821    * @access public
1822    */
1823    function setAttribute()
1824    {
1825        $v_result = true;
1826
1827        // ----- Get the number of variable list of arguments
1828        if (($v_size = func_num_args()) == 0) {
1829            return true;
1830        }
1831
1832        // ----- Get the arguments
1833        $v_att_list = &func_get_args();
1834
1835        // ----- Read the attributes
1836        $i=0;
1837        while ($i<$v_size) {
1838
1839            // ----- Look for next option
1840            switch ($v_att_list[$i]) {
1841                // ----- Look for options that request a string value
1842                case ARCHIVE_TAR_ATT_SEPARATOR :
1843                    // ----- Check the number of parameters
1844                    if (($i+1) >= $v_size) {
1845                        $this->_error('Invalid number of parameters for '
1846						              .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
1847                        return false;
1848                    }
1849
1850                    // ----- Get the value
1851                    $this->_separator = $v_att_list[$i+1];
1852                    $i++;
1853                break;
1854
1855                default :
1856                    $this->_error('Unknow attribute code '.$v_att_list[$i].'');
1857                    return false;
1858            }
1859
1860            // ----- Next attribute
1861            $i++;
1862        }
1863
1864        return $v_result;
1865    }
1866    // }}}
1867
1868    // {{{ setIgnoreRegexp()
1869    /**
1870    * This method sets the regular expression for ignoring files and directories
1871    * at import, for example:
1872    * $arch->setIgnoreRegexp("#CVS|\.svn#");
1873    *
1874    * @param string $regexp regular expression defining which files or directories to ignore
1875    *
1876    * @access public
1877    */
1878    function setIgnoreRegexp($regexp)
1879    {
1880    	$this->_ignore_regexp = $regexp;
1881    }
1882    // }}}
1883
1884    // {{{ setIgnoreList()
1885    /**
1886    * This method sets the regular expression for ignoring all files and directories
1887    * matching the filenames in the array list at import, for example:
1888    * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool'));
1889    *
1890    * @param array $list a list of file or directory names to ignore
1891    *
1892    * @access public
1893    */
1894    function setIgnoreList($list)
1895    {
1896    	$regexp = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list);
1897    	$regexp = '#/'.join('$|/', $list).'#';
1898    	$this->setIgnoreRegexp($regexp);
1899    }
1900    // }}}
1901
1902    // {{{ _error()
1903    function _error($p_message)
1904    {
1905        $this->error_object = &$this->raiseError($p_message); 
1906    }
1907    // }}}
1908
1909    // {{{ _warning()
1910    function _warning($p_message)
1911    {
1912        $this->error_object = &$this->raiseError($p_message); 
1913    }
1914    // }}}
1915
1916    // {{{ _isArchive()
1917    function _isArchive($p_filename=null)
1918    {
1919        if ($p_filename == null) {
1920            $p_filename = $this->_tarname;
1921        }
1922        clearstatcache();
1923        return @is_file($p_filename) && !@is_link($p_filename);
1924    }
1925    // }}}
1926
1927    // {{{ _openWrite()
1928    function _openWrite()
1929    {
1930        if ($this->_compress_type == 'gz' && function_exists('gzopen'))
1931            $this->_file = @gzopen($this->_tarname, "wb9");
1932        else if ($this->_compress_type == 'bz2' && function_exists('bzopen'))
1933            $this->_file = @bzopen($this->_tarname, "w");
1934        else if ($this->_compress_type == 'none')
1935            $this->_file = @fopen($this->_tarname, "wb");
1936        else {
1937            $this->_error('Unknown or missing compression type ('
1938			              .$this->_compress_type.')');
1939            return false;
1940        }
1941
1942        if ($this->_file == 0) {
1943            $this->_error('Unable to open in write mode \''
1944			              .$this->_tarname.'\'');
1945            return false;
1946        }
1947
1948        return true;
1949    }
1950    // }}}
1951
1952    // {{{ _openRead()
1953    function _openRead()
1954    {
1955        if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
1956
1957          // ----- Look if a local copy need to be done
1958          if ($this->_temp_tarname == '') {
1959              $this->_temp_tarname = uniqid('tar').'.tmp';
1960              if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
1961                $this->_error('Unable to open in read mode \''
1962				              .$this->_tarname.'\'');
1963                $this->_temp_tarname = '';
1964                return false;
1965              }
1966              if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
1967                $this->_error('Unable to open in write mode \''
1968				              .$this->_temp_tarname.'\'');
1969                $this->_temp_tarname = '';
1970                return false;
1971              }
1972              while ($v_data = @fread($v_file_from, 1024))
1973                  @fwrite($v_file_to, $v_data);
1974              @fclose($v_file_from);
1975              @fclose($v_file_to);
1976          }
1977
1978          // ----- File to open if the local copy
1979          $v_filename = $this->_temp_tarname;
1980
1981        } else
1982          // ----- File to open if the normal Tar file
1983          $v_filename = $this->_tarname;
1984
1985        if ($this->_compress_type == 'gz' && function_exists('gzopen'))
1986            $this->_file = @gzopen($v_filename, "rb");
1987        else if ($this->_compress_type == 'bz2' && function_exists('bzopen'))
1988            $this->_file = @bzopen($v_filename, "r");
1989        else if ($this->_compress_type == 'none')
1990            $this->_file = @fopen($v_filename, "rb");
1991        else {
1992            $this->_error('Unknown or missing compression type ('
1993			              .$this->_compress_type.')');
1994            return false;
1995        }
1996
1997        if ($this->_file == 0) {
1998            $this->_error('Unable to open in read mode \''.$v_filename.'\'');
1999            return false;
2000        }
2001
2002        return true;
2003    }
2004    // }}}
2005
2006    // {{{ _openReadWrite()
2007    function _openReadWrite()
2008    {
2009        if ($this->_compress_type == 'gz')
2010            $this->_file = @gzopen($this->_tarname, "r+b");
2011        else if ($this->_compress_type == 'bz2') {
2012            $this->_error('Unable to open bz2 in read/write mode \''
2013			              .$this->_tarname.'\' (limitation of bz2 extension)');
2014            return false;
2015        } else if ($this->_compress_type == 'none')
2016            $this->_file = @fopen($this->_tarname, "r+b");
2017        else {
2018            $this->_error('Unknown or missing compression type ('
2019			              .$this->_compress_type.')');
2020            return false;
2021        }
2022
2023        if ($this->_file == 0) {
2024            $this->_error('Unable to open in read/write mode \''
2025			              .$this->_tarname.'\'');
2026            return false;
2027        }
2028
2029        return true;
2030    }
2031    // }}}
2032
2033    // {{{ _close()
2034    function _close()
2035    {
2036        //if (isset($this->_file)) {
2037        if (is_resource($this->_file)) {
2038            if ($this->_compress_type == 'gz')
2039                @gzclose($this->_file);
2040            else if ($this->_compress_type == 'bz2')
2041                @bzclose($this->_file);
2042            else if ($this->_compress_type == 'none')
2043                @fclose($this->_file);
2044            else
2045                $this->_error('Unknown or missing compression type ('
2046				              .$this->_compress_type.')');
2047
2048            $this->_file = 0;
2049        }
2050
2051        // ----- Look if a local copy need to be erase
2052        // Note that it might be interesting to keep the url for a time : ToDo
2053        if ($this->_temp_tarname != '') {
2054            @unlink($this->_temp_tarname);
2055            $this->_temp_tarname = '';
2056        }
2057
2058        return true;
2059    }
2060    // }}}
2061
2062    // {{{ _cleanFile()
2063    function _cleanFile()
2064    {
2065        $this->_close();
2066
2067        // ----- Look for a local copy
2068        if ($this->_temp_tarname != '') {
2069            // ----- Remove the local copy but not the remote tarname
2070            @unlink($this->_temp_tarname);
2071            $this->_temp_tarname = '';
2072        } else {
2073            // ----- Remove the local tarname file
2074            @unlink($this->_tarname);
2075        }
2076        $this->_tarname = '';
2077
2078        return true;
2079    }
2080    // }}}
2081
2082    // {{{ _writeBlock()
2083    function _writeBlock($p_binary_data, $p_len=null)
2084    {
2085      if (is_resource($this->_file)) {
2086          if ($p_len === null) {
2087              if ($this->_compress_type == 'gz')
2088                  @gzputs($this->_file, $p_binary_data);
2089              else if ($this->_compress_type == 'bz2')
2090                  @bzwrite($this->_file, $p_binary_data);
2091              else if ($this->_compress_type == 'none')
2092                  @fputs($this->_file, $p_binary_data);
2093              else
2094                  $this->_error('Unknown or missing compression type ('
2095				                .$this->_compress_type.')');
2096          } else {
2097              if ($this->_compress_type == 'gz')
2098                  @gzputs($this->_file, $p_binary_data, $p_len);
2099              else if ($this->_compress_type == 'bz2')
2100                  @bzwrite($this->_file, $p_binary_data, $p_len);
2101              else if ($this->_compress_type == 'none')
2102                  @fputs($this->_file, $p_binary_data, $p_len);
2103              else
2104                  $this->_error('Unknown or missing compression type ('
2105				                .$this->_compress_type.')');
2106
2107          }
2108      }
2109      return true;
2110    }
2111    // }}}
2112
2113    // {{{ _readBlock()
2114    function _readBlock()
2115    {
2116      $v_block = null;
2117      if (is_resource($this->_file)) {
2118          if ($this->_compress_type == 'gz')
2119              $v_block = @gzread($this->_file, 512);
2120          else if ($this->_compress_type == 'bz2')
2121              $v_block = @bzread($this->_file, 512);
2122          else if ($this->_compress_type == 'none')
2123              $v_block = @fread($this->_file, 512);
2124          else
2125              $this->_error('Unknown or missing compression type ('
2126			                .$this->_compress_type.')');
2127      }
2128      return $v_block;
2129    }
2130    // }}}
2131
2132    // {{{ _jumpBlock()
2133    function _jumpBlock($p_len=null)
2134    {
2135      if (is_resource($this->_file)) {
2136          if ($p_len === null)
2137              $p_len = 1;
2138
2139          if ($this->_compress_type == 'gz') {
2140              @gzseek($this->_file, gztell($this->_file)+($p_len*512));
2141          }
2142          else if ($this->_compress_type == 'bz2') {
2143              // ----- Replace missing bztell() and bzseek()
2144              for ($i=0; $i<$p_len; $i++)
2145                  $this->_readBlock();
2146          } else if ($this->_compress_type == 'none')
2147              @fseek($this->_file, $p_len*512, SEEK_CUR);
2148          else
2149              $this->_error('Unknown or missing compression type ('
2150			                .$this->_compress_type.')');
2151
2152      }
2153      return true;
2154    }
2155    // }}}
2156
2157    // {{{ _writeFooter()
2158    function _writeFooter()
2159    {
2160      if (is_resource($this->_file)) {
2161          // ----- Write the last 0 filled block for end of archive
2162          $v_binary_data = pack('a1024', '');
2163          $this->_writeBlock($v_binary_data);
2164      }
2165      return true;
2166    }
2167    // }}}
2168
2169    // {{{ _addList()
2170    function _addList($p_list, $p_add_dir, $p_remove_dir)
2171    {
2172      $v_result=true;
2173      $v_header = array();
2174
2175      // ----- Remove potential windows directory separator
2176      $p_add_dir = $this->_translateWinPath($p_add_dir);
2177      $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
2178
2179      if (!$this->_file) {
2180          $this->_error('Invalid file descriptor');
2181          return false;
2182      }
2183
2184      if (sizeof($p_list) == 0)
2185          return true;
2186
2187      foreach ($p_list as $v_filename) {
2188          if (!$v_result) {
2189              break;
2190          }
2191
2192        // ----- Skip the current tar name
2193        if ($v_filename == $this->_tarname)
2194            continue;
2195
2196        if ($v_filename == '')
2197            continue;
2198
2199       	// ----- ignore files and directories matching the ignore regular expression
2200       	if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/'.$v_filename)) {
2201            $this->_warning("File '$v_filename' ignored");
2202       	    continue;
2203       	}
2204
2205        if (!file_exists($v_filename) && !is_link($v_filename)) {
2206            $this->_warning("File '$v_filename' does not exist");
2207            continue;
2208        }
2209
2210        // ----- Add the file or directory header
2211        if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
2212            return false;
2213
2214        if (@is_dir($v_filename) && !@is_link($v_filename)) {
2215            if (!($p_hdir = opendir($v_filename))) {
2216                $this->_warning("Directory '$v_filename' can not be read");
2217                continue;
2218            }
2219            while (false !== ($p_hitem = readdir($p_hdir))) {
2220                if (($p_hitem != '.') && ($p_hitem != '..')) {
2221                    if ($v_filename != ".")
2222                        $p_temp_list[0] = $v_filename.'/'.$p_hitem;
2223                    else
2224                        $p_temp_list[0] = $p_hitem;
2225
2226                    $v_result = $this->_addList($p_temp_list,
2227					                            $p_add_dir,
2228												$p_remove_dir);
2229                }
2230            }
2231
2232            unset($p_temp_list);
2233            unset($p_hdir);
2234            unset($p_hitem);
2235        }
2236      }
2237
2238      return $v_result;
2239    }
2240    // }}}
2241
2242    // {{{ _addFile()
2243    function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
2244    {
2245      if (!$this->_file) {
2246          $this->_error('Invalid file descriptor');
2247          return false;
2248      }
2249
2250      if ($p_filename == '') {
2251          $this->_error('Invalid file name');
2252          return false;
2253      }
2254
2255      // ----- Calculate the stored filename
2256      $p_filename = $this->_translateWinPath($p_filename, false);;
2257      $v_stored_filename = $p_filename;
2258      if (strcmp($p_filename, $p_remove_dir) == 0) {
2259          return true;
2260      }
2261      if ($p_remove_dir != '') {
2262          if (substr($p_remove_dir, -1) != '/')
2263              $p_remove_dir .= '/';
2264
2265          if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
2266              $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
2267      }
2268      $v_stored_filename = $this->_translateWinPath($v_stored_filename);
2269      if ($p_add_dir != '') {
2270          if (substr($p_add_dir, -1) == '/')
2271              $v_stored_filename = $p_add_dir.$v_stored_filename;
2272          else
2273              $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
2274      }
2275
2276      $v_stored_filename = $this->_pathReduction($v_stored_filename);
2277
2278      if ($this->_isArchive($p_filename)) {
2279          if (($v_file = @fopen($p_filename, "rb")) == 0) {
2280              $this->_warning("Unable to open file '".$p_filename
2281			                  ."' in binary read mode");
2282              return true;
2283          }
2284
2285          if (!$this->_writeHeader($p_filename, $v_stored_filename))
2286              return false;
2287
2288          while (($v_buffer = fread($v_file, 512)) != '') {
2289              $v_binary_data = pack("a512", "$v_buffer");
2290              $this->_writeBlock($v_binary_data);
2291          }
2292
2293          fclose($v_file);
2294
2295      } else {
2296          // ----- Only header for dir
2297          if (!$this->_writeHeader($p_filename, $v_stored_filename))
2298              return false;
2299      }
2300
2301      return true;
2302    }
2303    // }}}
2304
2305    // {{{ _addString()
2306    function _addString($p_filename, $p_string, $p_datetime = false)
2307    {
2308      if (!$this->_file) {
2309          $this->_error('Invalid file descriptor');
2310          return false;
2311      }
2312
2313      if ($p_filename == '') {
2314          $this->_error('Invalid file name');
2315          return false;
2316      }
2317
2318      // ----- Calculate the stored filename
2319      $p_filename = $this->_translateWinPath($p_filename, false);;
2320      
2321      // ----- If datetime is not specified, set current time
2322      if ($p_datetime === false) {
2323          $p_datetime = time();
2324      }
2325
2326      if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
2327                                    $p_datetime, 384, "", 0, 0))
2328          return false;
2329
2330      $i=0;
2331      while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
2332          $v_binary_data = pack("a512", $v_buffer);
2333          $this->_writeBlock($v_binary_data);
2334      }
2335
2336      return true;
2337    }
2338    // }}}
2339
2340    // {{{ _writeHeader()
2341    function _writeHeader($p_filename, $p_stored_filename)
2342    {
2343        if ($p_stored_filename == '')
2344            $p_stored_filename = $p_filename;
2345        $v_reduce_filename = $this->_pathReduction($p_stored_filename);
2346
2347        if (strlen($v_reduce_filename) > 99) {
2348          if (!$this->_writeLongHeader($v_reduce_filename))
2349            return false;
2350        }
2351
2352        $v_info = lstat($p_filename);
2353        $v_uid = sprintf("%07s", DecOct($v_info[4]));
2354        $v_gid = sprintf("%07s", DecOct($v_info[5]));
2355        $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
2356
2357        $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
2358
2359        $v_linkname = '';
2360
2361        if (@is_link($p_filename)) {
2362          $v_typeflag = '2';
2363          $v_linkname = readlink($p_filename);
2364          $v_size = sprintf("%011s", DecOct(0));
2365        } elseif (@is_dir($p_filename)) {
2366          $v_typeflag = "5";
2367          $v_size = sprintf("%011s", DecOct(0));
2368        } else {
2369          $v_typeflag = '0';
2370          clearstatcache();
2371          $v_size = sprintf("%011s", DecOct($v_info['size']));
2372        }
2373
2374        $v_magic = 'ustar ';
2375
2376        $v_version = ' ';
2377        
2378        if (function_exists('posix_getpwuid'))
2379        {
2380          $userinfo = posix_getpwuid($v_info[4]);
2381          $groupinfo = posix_getgrgid($v_info[5]);
2382          
2383          $v_uname = $userinfo['name'];
2384          $v_gname = $groupinfo['name'];
2385        }
2386        else
2387        {
2388          $v_uname = '';
2389          $v_gname = '';
2390        }
2391
2392        $v_devmajor = '';
2393
2394        $v_devminor = '';
2395
2396        $v_prefix = '';
2397
2398        $v_binary_data_first = pack("a100a8a8a8a12a12",
2399		                            $v_reduce_filename, $v_perms, $v_uid,
2400									$v_gid, $v_size, $v_mtime);
2401        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
2402		                           $v_typeflag, $v_linkname, $v_magic,
2403								   $v_version, $v_uname, $v_gname,
2404								   $v_devmajor, $v_devminor, $v_prefix, '');
2405
2406        // ----- Calculate the checksum
2407        $v_checksum = 0;
2408        // ..... First part of the header
2409        for ($i=0; $i<148; $i++)
2410            $v_checksum += ord(substr($v_binary_data_first,$i,1));
2411        // ..... Ignore the checksum value and replace it by ' ' (space)
2412        for ($i=148; $i<156; $i++)
2413            $v_checksum += ord(' ');
2414        // ..... Last part of the header
2415        for ($i=156, $j=0; $i<512; $i++, $j++)
2416            $v_checksum += ord(substr($v_binary_data_last,$j,1));
2417
2418        // ----- Write the first 148 bytes of the header in the archive
2419        $this->_writeBlock($v_binary_data_first, 148);
2420
2421        // ----- Write the calculated checksum
2422        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
2423        $v_binary_data = pack("a8", $v_checksum);
2424        $this->_writeBlock($v_binary_data, 8);
2425
2426        // ----- Write the last 356 bytes of the header in the archive
2427        $this->_writeBlock($v_binary_data_last, 356);
2428
2429        return true;
2430    }
2431    // }}}
2432
2433    // {{{ _writeHeaderBlock()
2434    function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
2435	                           $p_type='', $p_uid=0, $p_gid=0)
2436    {
2437        $p_filename = $this->_pathReduction($p_filename);
2438
2439        if (strlen($p_filename) > 99) {
2440          if (!$this->_writeLongHeader($p_filename))
2441            return false;
2442        }
2443
2444        if ($p_type == "5") {
2445          $v_size = sprintf("%011s", DecOct(0));
2446        } else {
2447          $v_size = sprintf("%011s", DecOct($p_size));
2448        }
2449
2450        $v_uid = sprintf("%07s", DecOct($p_uid));
2451        $v_gid = sprintf("%07s", DecOct($p_gid));
2452        $v_perms = sprintf("%07s", DecOct($p_perms & 000777));
2453
2454        $v_mtime = sprintf("%11s", DecOct($p_mtime));
2455
2456        $v_linkname = '';
2457
2458        $v_magic = 'ustar ';
2459
2460        $v_version = ' ';
2461
2462        if (function_exists('posix_getpwuid'))
2463        {
2464          $userinfo = posix_getpwuid($p_uid);
2465          $groupinfo = posix_getgrgid($p_gid);
2466          
2467          $v_uname = $userinfo['name'];
2468          $v_gname = $groupinfo['name'];
2469        }
2470        else
2471        {
2472          $v_uname = '';
2473          $v_gname = '';
2474        }
2475        
2476        $v_devmajor = '';
2477
2478        $v_devminor = '';
2479
2480        $v_prefix = '';
2481
2482        $v_binary_data_first = pack("a100a8a8a8a12A12",
2483		                            $p_filename, $v_perms, $v_uid, $v_gid,
2484									$v_size, $v_mtime);
2485        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
2486		                           $p_type, $v_linkname, $v_magic,
2487								   $v_version, $v_uname, $v_gname,
2488								   $v_devmajor, $v_devminor, $v_prefix, '');
2489
2490        // ----- Calculate the checksum
2491        $v_checksum = 0;
2492        // ..... First part of the header
2493        for ($i=0; $i<148; $i++)
2494            $v_checksum += ord(substr($v_binary_data_first,$i,1));
2495        // ..... Ignore the checksum value and replace it by ' ' (space)
2496        for ($i=148; $i<156; $i++)
2497            $v_checksum += ord(' ');
2498        // ..... Last part of the header
2499        for ($i=156, $j=0; $i<512; $i++, $j++)
2500            $v_checksum += ord(substr($v_binary_data_last,$j,1));
2501
2502        // ----- Write the first 148 bytes of the header in the archive
2503        $this->_writeBlock($v_binary_data_first, 148);
2504
2505        // ----- Write the calculated checksum
2506        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
2507        $v_binary_data = pack("a8", $v_checksum);
2508        $this->_writeBlock($v_binary_data, 8);
2509
2510        // ----- Write the last 356 bytes of the header in the archive
2511        $this->_writeBlock($v_binary_data_last, 356);
2512
2513        return true;
2514    }
2515    // }}}
2516
2517    // {{{ _writeLongHeader()
2518    function _writeLongHeader($p_filename)
2519    {
2520        $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
2521
2522        $v_typeflag = 'L';
2523
2524        $v_linkname = '';
2525
2526        $v_magic = '';
2527
2528        $v_version = '';
2529
2530        $v_uname = '';
2531
2532        $v_gname = '';
2533
2534        $v_devmajor = '';
2535
2536        $v_devminor = '';
2537
2538        $v_prefix = '';
2539
2540        $v_binary_data_first = pack("a100a8a8a8a12a12",
2541		                            '././@LongLink', 0, 0, 0, $v_size, 0);
2542        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
2543		                           $v_typeflag, $v_linkname, $v_magic,
2544								   $v_version, $v_uname, $v_gname,
2545								   $v_devmajor, $v_devminor, $v_prefix, '');
2546
2547        // ----- Calculate the checksum
2548        $v_checksum = 0;
2549        // ..... First part of the header
2550        for ($i=0; $i<148; $i++)
2551            $v_checksum += ord(substr($v_binary_data_first,$i,1));
2552        // ..... Ignore the checksum value and replace it by ' ' (space)
2553        for ($i=148; $i<156; $i++)
2554            $v_checksum += ord(' ');
2555        // ..... Last part of the header
2556        for ($i=156, $j=0; $i<512; $i++, $j++)
2557            $v_checksum += ord(substr($v_binary_data_last,$j,1));
2558
2559        // ----- Write the first 148 bytes of the header in the archive
2560        $this->_writeBlock($v_binary_data_first, 148);
2561
2562        // ----- Write the calculated checksum
2563        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
2564        $v_binary_data = pack("a8", $v_checksum);
2565        $this->_writeBlock($v_binary_data, 8);
2566
2567        // ----- Write the last 356 bytes of the header in the archive
2568        $this->_writeBlock($v_binary_data_last, 356);
2569
2570        // ----- Write the filename as content of the block
2571        $i=0;
2572        while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
2573            $v_binary_data = pack("a512", "$v_buffer");
2574            $this->_writeBlock($v_binary_data);
2575        }
2576
2577        return true;
2578    }
2579    // }}}
2580
2581    // {{{ _readHeader()
2582    function _readHeader($v_binary_data, &$v_header)
2583    {
2584        if (strlen($v_binary_data)==0) {
2585            $v_header['filename'] = '';
2586            return true;
2587        }
2588
2589        if (strlen($v_binary_data) != 512) {
2590            $v_header['filename'] = '';
2591            $this->_error('Invalid block size : '.strlen($v_binary_data));
2592            return false;
2593        }
2594
2595        if (!is_array($v_header)) {
2596            $v_header = array();
2597        }
2598        // ----- Calculate the checksum
2599        $v_checksum = 0;
2600        // ..... First part of the header
2601        for ($i=0; $i<148; $i++)
2602            $v_checksum+=ord(substr($v_binary_data,$i,1));
2603        // ..... Ignore the checksum value and replace it by ' ' (space)
2604        for ($i=148; $i<156; $i++)
2605            $v_checksum += ord(' ');
2606        // ..... Last part of the header
2607        for ($i=156; $i<512; $i++)
2608           $v_checksum+=ord(substr($v_binary_data,$i,1));
2609
2610        if (version_compare(PHP_VERSION,"5.5.0-dev")<0) {
2611            $fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
2612                   "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
2613                   "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
2614        } else {
2615            $fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
2616                   "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
2617                   "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
2618        }
2619        $v_data = unpack($fmt, $v_binary_data);
2620
2621        if (strlen($v_data["prefix"]) > 0) {
2622            $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
2623        }
2624
2625        // ----- Extract the checksum
2626        $v_header['checksum'] = OctDec(trim($v_data['checksum']));
2627        if ($v_header['checksum'] != $v_checksum) {
2628            $v_header['filename'] = '';
2629
2630            // ----- Look for last block (empty block)
2631            if (($v_checksum == 256) && ($v_header['checksum'] == 0))
2632                return true;
2633
2634            $this->_error('Invalid checksum for file "'.$v_data['filename']
2635			              .'" : '.$v_checksum.' calculated, '
2636						  .$v_header['checksum'].' expected');
2637            return false;
2638        }
2639
2640        // ----- Extract the properties
2641        $v_header['filename'] = $v_data['filename'];
2642        if ($this->_maliciousFilename($v_header['filename'])) {
2643            $this->_error('Malicious .tar detected, file "' . $v_header['filename'] .
2644                '" will not install in desired directory tree');
2645            return false;
2646        }
2647        $v_header['mode'] = OctDec(trim($v_data['mode']));
2648        $v_header['uid'] = OctDec(trim($v_data['uid']));
2649        $v_header['gid'] = OctDec(trim($v_data['gid']));
2650        $v_header['size'] = OctDec(trim($v_data['size']));
2651        $v_header['mtime'] = OctDec(trim($v_data['mtime']));
2652        if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
2653          $v_header['size'] = 0;
2654        }
2655        $v_header['link'] = trim($v_data['link']);
2656        /* ----- All these fields are removed form the header because
2657		they do not carry interesting info
2658        $v_header[magic] = trim($v_data[magic]);
2659        $v_header[version] = trim($v_data[version]);
2660        $v_header[uname] = trim($v_data[uname]);
2661        $v_header[gname] = trim($v_data[gname]);
2662        $v_header[devmajor] = trim($v_data[devmajor]);
2663        $v_header[devminor] = trim($v_data[devminor]);
2664        */
2665
2666        return true;
2667    }
2668    // }}}
2669
2670    // {{{ _maliciousFilename()
2671    /**
2672     * Detect and report a malicious file name
2673     *
2674     * @param string $file
2675     *
2676     * @return bool
2677     * @access private
2678     */
2679    function _maliciousFilename($file)
2680    {
2681        if (strpos($file, '/../') !== false) {
2682            return true;
2683        }
2684        if (strpos($file, '../') === 0) {
2685            return true;
2686        }
2687        return false;
2688    }
2689    // }}}
2690
2691    // {{{ _readLongHeader()
2692    function _readLongHeader(&$v_header)
2693    {
2694      $v_filename = '';
2695      $n = floor($v_header['size']/512);
2696      for ($i=0; $i<$n; $i++) {
2697        $v_content = $this->_readBlock();
2698        $v_filename .= $v_content;
2699      }
2700      if (($v_header['size'] % 512) != 0) {
2701        $v_content = $this->_readBlock();
2702        $v_filename .= trim($v_content);
2703      }
2704
2705      // ----- Read the next header
2706      $v_binary_data = $this->_readBlock();
2707
2708      if (!$this->_readHeader($v_binary_data, $v_header))
2709        return false;
2710
2711      $v_filename = trim($v_filename);
2712      $v_header['filename'] = $v_filename;
2713        if ($this->_maliciousFilename($v_filename)) {
2714            $this->_error('Malicious .tar detected, file "' . $v_filename .
2715                '" will not install in desired directory tree');
2716            return false;
2717      }
2718
2719      return true;
2720    }
2721    // }}}
2722
2723    // {{{ _extractInString()
2724    /**
2725    * This method extract from the archive one file identified by $p_filename.
2726    * The return value is a string with the file content, or null on error.
2727    *
2728    * @param string $p_filename The path of the file to extract in a string.
2729    *
2730    * @return a string with the file content or null.
2731    * @access private
2732    */
2733    function _extractInString($p_filename)
2734    {
2735        $v_result_str = "";
2736
2737        While (strlen($v_binary_data = $this->_readBlock()) != 0)
2738        {
2739          if (!$this->_readHeader($v_binary_data, $v_header))
2740            return null;
2741
2742          if ($v_header['filename'] == '')
2743            continue;
2744
2745          // ----- Look for long filename
2746          if ($v_header['typeflag'] == 'L') {
2747            if (!$this->_readLongHeader($v_header))
2748              return null;
2749          }
2750
2751          if ($v_header['filename'] == $p_filename) {
2752              if ($v_header['typeflag'] == "5") {
2753                  $this->_error('Unable to extract in string a directory '
2754				                .'entry {'.$v_header['filename'].'}');
2755                  return null;
2756              } else {
2757                  $n = floor($v_header['size']/512);
2758                  for ($i=0; $i<$n; $i++) {
2759                      $v_result_str .= $this->_readBlock();
2760                  }
2761                  if (($v_header['size'] % 512) != 0) {
2762                      $v_content = $this->_readBlock();
2763                      $v_result_str .= substr($v_content, 0,
2764					                          ($v_header['size'] % 512));
2765                  }
2766                  return $v_result_str;
2767              }
2768          } else {
2769              $this->_jumpBlock(ceil(($v_header['size']/512)));
2770          }
2771        }
2772
2773        return null;
2774    }
2775    // }}}
2776
2777    // {{{ _extractList()
2778    function _extractList($p_path, &$p_list_detail, $p_mode,
2779                          $p_file_list, $p_remove_path, $p_preserve=false)
2780    {
2781    $v_result=true;
2782    $v_nb = 0;
2783    $v_extract_all = true;
2784    $v_listing = false;
2785
2786    $p_path = $this->_translateWinPath($p_path, false);
2787    if ($p_path == '' || (substr($p_path, 0, 1) != '/'
2788	    && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
2789      $p_path = "./".$p_path;
2790    }
2791    $p_remove_path = $this->_translateWinPath($p_remove_path);
2792
2793    // ----- Look for path to remove format (should end by /)
2794    if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
2795      $p_remove_path .= '/';
2796    $p_remove_path_size = strlen($p_remove_path);
2797
2798    switch ($p_mode) {
2799      case "complete" :
2800        $v_extract_all = true;
2801        $v_listing = false;
2802      break;
2803      case "partial" :
2804          $v_extract_all = false;
2805          $v_listing = false;
2806      break;
2807      case "list" :
2808          $v_extract_all = false;
2809          $v_listing = true;
2810      break;
2811      default :
2812        $this->_error('Invalid extract mode ('.$p_mode.')');
2813        return false;
2814    }
2815
2816    clearstatcache();
2817
2818    while (strlen($v_binary_data = $this->_readBlock()) != 0)
2819    {
2820      $v_extract_file = FALSE;
2821      $v_extraction_stopped = 0;
2822
2823      if (!$this->_readHeader($v_binary_data, $v_header))
2824        return false;
2825
2826      if ($v_header['filename'] == '') {
2827        continue;
2828      }
2829
2830      // ----- Look for long filename
2831      if ($v_header['typeflag'] == 'L') {
2832        if (!$this->_readLongHeader($v_header))
2833          return false;
2834      }
2835
2836      if ((!$v_extract_all) && (is_array($p_file_list))) {
2837        // ----- By default no unzip if the file is not found
2838        $v_extract_file = false;
2839
2840        for ($i=0; $i<sizeof($p_file_list); $i++) {
2841          // ----- Look if it is a directory
2842          if (substr($p_file_list[$i], -1) == '/') {
2843            // ----- Look if the directory is in the filename path
2844            if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
2845			    && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
2846				    == $p_file_list[$i])) {
2847              $v_extract_file = true;
2848              break;
2849            }
2850          }
2851
2852          // ----- It is a file, so compare the file names
2853          elseif ($p_file_list[$i] == $v_header['filename']) {
2854            $v_extract_file = true;
2855            break;
2856          }
2857        }
2858      } else {
2859        $v_extract_file = true;
2860      }
2861
2862      // ----- Look if this file need to be extracted
2863      if (($v_extract_file) && (!$v_listing))
2864      {
2865        if (($p_remove_path != '')
2866            && (substr($v_header['filename'].'/', 0, $p_remove_path_size)
2867			    == $p_remove_path)) {
2868          $v_header['filename'] = substr($v_header['filename'],
2869		                                 $p_remove_path_size);
2870          if( $v_header['filename'] == '' ){
2871            continue;
2872          }
2873        }
2874        if (($p_path != './') && ($p_path != '/')) {
2875          while (substr($p_path, -1) == '/')
2876            $p_path = substr($p_path, 0, strlen($p_path)-1);
2877
2878          if (substr($v_header['filename'], 0, 1) == '/')
2879              $v_header['filename'] = $p_path.$v_header['filename'];
2880          else
2881            $v_header['filename'] = $p_path.'/'.$v_header['filename'];
2882        }
2883        if (file_exists($v_header['filename'])) {
2884          if (   (@is_dir($v_header['filename']))
2885		      && ($v_header['typeflag'] == '')) {
2886            $this->_error('File '.$v_header['filename']
2887			              .' already exists as a directory');
2888            return false;
2889          }
2890          if (   ($this->_isArchive($v_header['filename']))
2891		      && ($v_header['typeflag'] == "5")) {
2892            $this->_error('Directory '.$v_header['filename']
2893			              .' already exists as a file');
2894            return false;
2895          }
2896          if (!is_writeable($v_header['filename'])) {
2897            $this->_error('File '.$v_header['filename']
2898			              .' already exists and is write protected');
2899            return false;
2900          }
2901          if (filemtime($v_header['filename']) > $v_header['mtime']) {
2902            // To be completed : An error or silent no replace ?
2903          }
2904        }
2905
2906        // ----- Check the directory availability and create it if necessary
2907        elseif (($v_result
2908		         = $this->_dirCheck(($v_header['typeflag'] == "5"
2909				                    ?$v_header['filename']
2910									:dirname($v_header['filename'])))) != 1) {
2911            $this->_error('Unable to create path for '.$v_header['filename']);
2912            return false;
2913        }
2914
2915        if ($v_extract_file) {
2916          if ($v_header['typeflag'] == "5") {
2917            if (!@file_exists($v_header['filename'])) {
2918                if (!@mkdir($v_header['filename'], 0777)) {
2919                    $this->_error('Unable to create directory {'
2920					              .$v_header['filename'].'}');
2921                    return false;
2922                }
2923            }
2924          } elseif ($v_header['typeflag'] == "2") {
2925              if (@file_exists($v_header['filename'])) {
2926                  @unlink($v_header['filename']);
2927              }
2928              if (!@symlink($v_header['link'], $v_header['filename'])) {
2929                  $this->_error('Unable to extract symbolic link {'
2930                                .$v_header['filename'].'}');
2931                  return false;
2932              }
2933          } else {
2934              if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
2935                  $this->_error('Error while opening {'.$v_header['filename']
2936				                .'} in write binary mode');
2937                  return false;
2938              } else {
2939                  $n = floor($v_header['size']/512);
2940                  for ($i=0; $i<$n; $i++) {
2941                      $v_content = $this->_readBlock();
2942                      fwrite($v_dest_file, $v_content, 512);
2943                  }
2944            if (($v_header['size'] % 512) != 0) {
2945              $v_content = $this->_readBlock();
2946              fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
2947            }
2948
2949            @fclose($v_dest_file);
2950            
2951            if ($p_preserve) {
2952                @chown($v_header['filename'], $v_header['uid']);
2953                @chgrp($v_header['filename'], $v_header['gid']);
2954            }
2955
2956            // ----- Change the file mode, mtime
2957            @touch($v_header['filename'], $v_header['mtime']);
2958            if ($v_header['mode'] & 0111) {
2959                // make file executable, obey umask
2960                $mode = fileperms($v_header['filename']) | (~umask() & 0111);
2961                @chmod($v_header['filename'], $mode);
2962            }
2963          }
2964
2965          // ----- Check the file size
2966          clearstatcache();
2967          if (!is_file($v_header['filename'])) {
2968              $this->_error('Extracted file '.$v_header['filename']
2969                            .'does not exist. Archive may be corrupted.');
2970              return false;
2971          }
2972          
2973          $filesize = filesize($v_header['filename']);
2974          if ($filesize != $v_header['size']) {
2975              $this->_error('Extracted file '.$v_header['filename']
2976                            .' does not have the correct file size \''
2977                            .$filesize
2978                            .'\' ('.$v_header['size']
2979                            .' expected). Archive may be corrupted.');
2980              return false;
2981          }
2982          }
2983        } else {
2984          $this->_jumpBlock(ceil(($v_header['size']/512)));
2985        }
2986      } else {
2987          $this->_jumpBlock(ceil(($v_header['size']/512)));
2988      }
2989
2990      /* TBC : Seems to be unused ...
2991      if ($this->_compress)
2992        $v_end_of_file = @gzeof($this->_file);
2993      else
2994        $v_end_of_file = @feof($this->_file);
2995        */
2996
2997      if ($v_listing || $v_extract_file || $v_extraction_stopped) {
2998        // ----- Log extracted files
2999        if (($v_file_dir = dirname($v_header['filename']))
3000		    == $v_header['filename'])
3001          $v_file_dir = '';
3002        if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
3003          $v_file_dir = '/';
3004
3005        $p_list_detail[$v_nb++] = $v_header;
3006        if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
3007            return true;
3008        }
3009      }
3010    }
3011
3012        return true;
3013    }
3014    // }}}
3015
3016    // {{{ _openAppend()
3017    function _openAppend()
3018    {
3019        if (filesize($this->_tarname) == 0)
3020          return $this->_openWrite();
3021
3022        if ($this->_compress) {
3023            $this->_close();
3024
3025            if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
3026                $this->_error('Error while renaming \''.$this->_tarname
3027				              .'\' to temporary file \''.$this->_tarname
3028							  .'.tmp\'');
3029                return false;
3030            }
3031
3032            if ($this->_compress_type == 'gz')
3033                $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
3034            elseif ($this->_compress_type == 'bz2')
3035                $v_temp_tar = @bzopen($this->_tarname.".tmp", "r");
3036
3037            if ($v_temp_tar == 0) {
3038                $this->_error('Unable to open file \''.$this->_tarname
3039				              .'.tmp\' in binary read mode');
3040                @rename($this->_tarname.".tmp", $this->_tarname);
3041                return false;
3042            }
3043
3044            if (!$this->_openWrite()) {
3045                @rename($this->_tarname.".tmp", $this->_tarname);
3046                return false;
3047            }
3048
3049            if ($this->_compress_type == 'gz') {
3050                $end_blocks = 0;
3051                
3052                while (!@gzeof($v_temp_tar)) {
3053                    $v_buffer = @gzread($v_temp_tar, 512);
3054                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
3055                        $end_blocks++;
3056                        // do not copy end blocks, we will re-make them
3057                        // after appending
3058                        continue;
3059                    } elseif ($end_blocks > 0) {
3060                        for ($i = 0; $i < $end_blocks; $i++) {
3061                            $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
3062                        }
3063                        $end_blocks = 0;
3064                    }
3065                    $v_binary_data = pack("a512", $v_buffer);
3066                    $this->_writeBlock($v_binary_data);
3067                }
3068
3069                @gzclose($v_temp_tar);
3070            }
3071            elseif ($this->_compress_type == 'bz2') {
3072                $end_blocks = 0;
3073                
3074                while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
3075                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
3076                        $end_blocks++;
3077                        // do not copy end blocks, we will re-make them
3078                        // after appending
3079                        continue;
3080                    } elseif ($end_blocks > 0) {
3081                        for ($i = 0; $i < $end_blocks; $i++) {
3082                            $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
3083                        }
3084                        $end_blocks = 0;
3085                    }
3086                    $v_binary_data = pack("a512", $v_buffer);
3087                    $this->_writeBlock($v_binary_data);
3088                }
3089
3090                @bzclose($v_temp_tar);
3091            }
3092
3093            if (!@unlink($this->_tarname.".tmp")) {
3094                $this->_error('Error while deleting temporary file \''
3095				              .$this->_tarname.'.tmp\'');
3096            }
3097
3098        } else {
3099            // ----- For not compressed tar, just add files before the last
3100			//       one or two 512 bytes block
3101            if (!$this->_openReadWrite())
3102               return false;
3103
3104            clearstatcache();
3105            $v_size = filesize($this->_tarname);
3106
3107            // We might have zero, one or two end blocks.
3108            // The standard is two, but we should try to handle
3109            // other cases.
3110            fseek($this->_file, $v_size - 1024);
3111            if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
3112                fseek($this->_file, $v_size - 1024);
3113            }
3114            elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
3115                fseek($this->_file, $v_size - 512);
3116            }
3117        }
3118
3119        return true;
3120    }
3121    // }}}
3122
3123    // {{{ _append()
3124    function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
3125    {
3126        if (!$this->_openAppend())
3127            return false;
3128
3129        if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
3130           $this->_writeFooter();
3131
3132        $this->_close();
3133
3134        return true;
3135    }
3136    // }}}
3137
3138    // {{{ _dirCheck()
3139
3140    /**
3141     * Check if a directory exists and create it (including parent
3142     * dirs) if not.
3143     *
3144     * @param string $p_dir directory to check
3145     *
3146     * @return bool true if the directory exists or was created
3147     */
3148    function _dirCheck($p_dir)
3149    {
3150        clearstatcache();
3151        if ((@is_dir($p_dir)) || ($p_dir == ''))
3152            return true;
3153
3154        $p_parent_dir = dirname($p_dir);
3155
3156        if (($p_parent_dir != $p_dir) &&
3157            ($p_parent_dir != '') &&
3158            (!$this->_dirCheck($p_parent_dir)))
3159             return false;
3160
3161        if (!@mkdir($p_dir, 0777)) {
3162            $this->_error("Unable to create directory '$p_dir'");
3163            return false;
3164        }
3165
3166        return true;
3167    }
3168
3169    // }}}
3170
3171    // {{{ _pathReduction()
3172
3173    /**
3174     * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
3175     * rand emove double slashes.
3176     *
3177     * @param string $p_dir path to reduce
3178     *
3179     * @return string reduced path
3180     *
3181     * @access private
3182     *
3183     */
3184    function _pathReduction($p_dir)
3185    {
3186        $v_result = '';
3187
3188        // ----- Look for not empty path
3189        if ($p_dir != '') {
3190            // ----- Explode path by directory names
3191            $v_list = explode('/', $p_dir);
3192
3193            // ----- Study directories from last to first
3194            for ($i=sizeof($v_list)-1; $i>=0; $i--) {
3195                // ----- Look for current path
3196                if ($v_list[$i] == ".") {
3197                    // ----- Ignore this directory
3198                    // Should be the first $i=0, but no check is done
3199                }
3200                else if ($v_list[$i] == "..") {
3201                    // ----- Ignore it and ignore the $i-1
3202                    $i--;
3203                }
3204                else if (   ($v_list[$i] == '')
3205				         && ($i!=(sizeof($v_list)-1))
3206						 && ($i!=0)) {
3207                    // ----- Ignore only the double '//' in path,
3208                    // but not the first and last /
3209                } else {
3210                    $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'
3211					            .$v_result:'');
3212                }
3213            }
3214        }
3215        
3216        if (defined('OS_WINDOWS') && OS_WINDOWS) {
3217            $v_result = strtr($v_result, '\\', '/');
3218        }
3219        
3220        return $v_result;
3221    }
3222
3223    // }}}
3224
3225    // {{{ _translateWinPath()
3226    function _translateWinPath($p_path, $p_remove_disk_letter=true)
3227    {
3228      if (defined('OS_WINDOWS') && OS_WINDOWS) {
3229          // ----- Look for potential disk letter
3230          if (   ($p_remove_disk_letter)
3231		      && (($v_position = strpos($p_path, ':')) != false)) {
3232              $p_path = substr($p_path, $v_position+1);
3233          }
3234          // ----- Change potential windows directory separator
3235          if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
3236              $p_path = strtr($p_path, '\\', '/');
3237          }
3238      }
3239      return $p_path;
3240    }
3241    // }}}
3242
3243}
3244?>
3245package.xml0000644000175000017500000002540212105433221012167 0ustar  druiddruid<?xml version="1.0" encoding="UTF-8"?>
3246<package packagerversion="1.9.4" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
3247 <name>Archive_Tar</name>
3248 <channel>pear.php.net</channel>
3249 <summary>Tar file management class</summary>
3250 <description>This class provides handling of tar files in PHP.
3251It supports creating, listing, extracting and adding to tar files.
3252Gzip support is available if PHP has the zlib extension built-in or
3253loaded. Bz2 compression is also supported with the bz2 extension loaded.</description>
3254 <lead>
3255  <name>Vincent Blavet</name>
3256  <user>vblavet</user>
3257  <email>vincent@phpconcept.net</email>
3258  <active>no</active>
3259 </lead>
3260 <lead>
3261  <name>Greg Beaver</name>
3262  <user>cellog</user>
3263  <email>greg@chiaraquartet.net</email>
3264  <active>no</active>
3265 </lead>
3266 <lead>
3267  <name>Michiel Rook</name>
3268  <user>mrook</user>
3269  <email>mrook@php.net</email>
3270  <active>yes</active>
3271 </lead>
3272 <helper>
3273  <name>Stig Bakken</name>
3274  <user>ssb</user>
3275  <email>stig@php.net</email>
3276  <active>no</active>
3277 </helper>
3278 <date>2013-02-09</date>
3279 <time>11:44:17</time>
3280 <version>
3281  <release>1.3.11</release>
3282  <api>1.3.1</api>
3283 </version>
3284 <stability>
3285  <release>stable</release>
3286  <api>stable</api>
3287 </stability>
3288 <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
3289 <notes>
3290* Fix Bug #19746: Broken with PHP 5.5 [mrook]
3291 * Implement Feature #11258: Custom date/time in files added on-the-fly [mrook]
3292 </notes>
3293 <contents>
3294  <dir name="/">
3295   <file baseinstalldir="/" md5sum="c6a0c1df229783f55d2c3e5e93c46d6e" name="Archive/Tar.php" role="php" />
3296   <file baseinstalldir="/" md5sum="29b03715377b18b1fafcff98a99cc9a7" name="docs/Archive_Tar.txt" role="doc" />
3297  </dir>
3298 </contents>
3299 <compatible>
3300  <name>PEAR</name>
3301  <channel>pear.php.net</channel>
3302  <min>1.8.0</min>
3303  <max>1.9.10</max>
3304 </compatible>
3305 <dependencies>
3306  <required>
3307   <php>
3308    <min>4.3.0</min>
3309   </php>
3310   <pearinstaller>
3311    <min>1.5.4</min>
3312   </pearinstaller>
3313  </required>
3314 </dependencies>
3315 <phprelease />
3316 <changelog>
3317  <release>
3318   <version>
3319    <release>1.3.10</release>
3320    <api>1.3.1</api>
3321   </version>
3322   <stability>
3323    <release>stable</release>
3324    <api>stable</api>
3325   </stability>
3326   <date>2012-04-10</date>
3327   <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD
3328   License</license>
3329   <notes>
3330* Fix Bug #13361: Unable to add() some files (ex. mp3) [mrook]
3331 * Fix Bug #19330: Class creates incorrect (non-readable) tar.gz file
3332 * [mrook]
3333   </notes>
3334  </release>
3335  <release>
3336   <version>
3337    <release>1.3.9</release>
3338    <api>1.3.1</api>
3339   </version>
3340   <stability>
3341    <release>stable</release>
3342    <api>stable</api>
3343   </stability>
3344   <date>2012-02-27</date>
3345   <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD   License</license>
3346   <notes>
3347* Fix Bug #16759: No error thrown from missing PHP zlib functions [mrook]
3348 * Fix Bug #18877: Incorrect handling of backslashes in filenames on Linux [mrook]
3349 * Fix Bug #19085: Error while packaging [mrook]
3350 * Fix Bug #19289: Invalid tar file generated [mrook]
3351   </notes>
3352  </release>
3353  <release>
3354   <version>
3355    <release>1.3.8</release>
3356    <api>1.3.1</api>
3357   </version>
3358   <stability>
3359    <release>stable</release>
3360    <api>stable</api>
3361   </stability>
3362   <date>2011-10-14</date>
3363   <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
3364   <notes>
3365* Fix Bug #17853: Test failure: dirtraversal.phpt [mrook]
3366 * Fix Bug #18512: dead links are not saved in tar file [mrook]
3367 * Fix Bug #18702: Unpacks incorrectly on long file names using header prefix [mrook]
3368 * Implement Feature #10145: Patch to return a Pear Error Object on failure [mrook]
3369 * Implement Feature #17491: Option to preserve permissions [mrook]
3370 * Implement Feature #17813: Prevent PHP notice when extracting corrupted archive [mrook]
3371   </notes>
3372  </release>
3373  <release>
3374   <version>
3375    <release>1.3.7</release>
3376    <api>1.3.1</api>
3377   </version>
3378   <stability>
3379    <release>stable</release>
3380    <api>stable</api>
3381   </stability>
3382   <date>2010-04-26</date>
3383   <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
3384   <notes>
3385PEAR compatibility update
3386   </notes>
3387  </release>
3388  <release>
3389   <version>
3390    <release>1.3.6</release>
3391    <api>1.3.1</api>
3392   </version>
3393   <stability>
3394    <release>stable</release>
3395    <api>stable</api>
3396   </stability>
3397   <date>2010-03-09</date>
3398   <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
3399   <notes>
3400* Fix Bug #16963: extractList can&apos;t extract zipped files from big tar [mrook]
3401 * Implement Feature #4013: Ignoring files and directories on creating an archive. [mrook]
3402   </notes>
3403  </release>
3404  <release>
3405   <version>
3406    <release>1.3.5</release>
3407    <api>1.3.1</api>
3408   </version>
3409   <stability>
3410    <release>stable</release>
3411    <api>stable</api>
3412   </stability>
3413   <date>2009-12-31</date>
3414   <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
3415   <notes>
3416* Fix Bug #16958: Update &apos;compatible&apos; tag in package.xml [mrook]
3417   </notes>
3418  </release>
3419  <release>
3420   <version>
3421    <release>1.3.4</release>
3422    <api>1.3.1</api>
3423   </version>
3424   <stability>
3425    <release>stable</release>
3426    <api>stable</api>
3427   </stability>
3428   <date>2009-12-30</date>
3429   <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
3430   <notes>
3431* Fix Bug #11871: wrong result of ::listContent() if filename begins or ends with space [mrook]
3432 * Fix Bug #12462: invalid tar magic [mrook]
3433 * Fix Bug #13918: Long filenames may get up to 511 0x00 bytes appended on read [mrook]
3434 * Fix Bug #16202: Bogus modification times [mrook]
3435 * Implement Feature #16212: Die is not exception [mrook]
3436   </notes>
3437  </release>
3438  <release>
3439   <version>
3440    <release>1.3.3</release>
3441    <api>1.3.1</api>
3442   </version>
3443   <stability>
3444    <release>stable</release>
3445    <api>stable</api>
3446   </stability>
3447   <date>2009-03-27</date>
3448   <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
3449   <notes>
3450Change the license to New BSD license
3451
3452   minor bugfix release
3453   * fix Bug #9921 compression with bzip2 fails [cellog]
3454   * fix Bug #11594 _readLongHeader leaves 0 bytes in filename [jamessas]
3455   * fix Bug #11769 Incorrect symlink handing [fajar99]
3456   </notes>
3457  </release>
3458  <release>
3459   <version>
3460    <release>1.3.2</release>
3461    <api>1.3.1</api>
3462   </version>
3463   <stability>
3464    <release>stable</release>
3465    <api>stable</api>
3466   </stability>
3467   <date>2007-01-03</date>
3468   <license uri="http://www.php.net/license">PHP License</license>
3469   <notes>
3470Correct Bug #4016
3471Remove duplicate remove error display with &apos;@&apos;
3472Correct Bug #3909 : Check existence of OS_WINDOWS constant
3473Correct Bug #5452 fix for &quot;lone zero block&quot; when untarring packages
3474Change filemode (from pear-core/Archive/Tar.php v.1.21)
3475Correct Bug #6486 Can not extract symlinks
3476Correct Bug #6933 Archive_Tar (Tar file management class) Directory traversal
3477Correct Bug #8114 Files added on-the-fly not storing date
3478Correct Bug #9352 Bug on _dirCheck function over nfs path
3479   </notes>
3480  </release>
3481  <release>
3482   <version>
3483    <release>1.3.1</release>
3484    <api>1.3.1</api>
3485   </version>
3486   <stability>
3487    <release>stable</release>
3488    <api>stable</api>
3489   </stability>
3490   <date>2005-03-17</date>
3491   <license uri="http://www.php.net/license">PHP License</license>
3492   <notes>
3493Correct Bug #3855
3494   </notes>
3495  </release>
3496  <release>
3497   <version>
3498    <release>1.3.0</release>
3499    <api>1.3.0</api>
3500   </version>
3501   <stability>
3502    <release>stable</release>
3503    <api>stable</api>
3504   </stability>
3505   <date>2005-03-06</date>
3506   <license uri="http://www.php.net/license">PHP License</license>
3507   <notes>
3508Bugs correction (2475, 2488, 2135, 2176)
3509   </notes>
3510  </release>
3511  <release>
3512   <version>
3513    <release>1.2</release>
3514    <api>1.2</api>
3515   </version>
3516   <stability>
3517    <release>stable</release>
3518    <api>stable</api>
3519   </stability>
3520   <date>2004-05-08</date>
3521   <license uri="http://www.php.net/license">PHP License</license>
3522   <notes>
3523Add support for other separator than the space char and bug
3524	correction
3525   </notes>
3526  </release>
3527  <release>
3528   <version>
3529    <release>1.1</release>
3530    <api>1.1</api>
3531   </version>
3532   <stability>
3533    <release>stable</release>
3534    <api>stable</api>
3535   </stability>
3536   <date>2003-05-28</date>
3537   <license uri="http://www.php.net/license">PHP License</license>
3538   <notes>
3539* Add support for BZ2 compression
3540* Add support for add and extract without using temporary files : methods addString() and extractInString()
3541   </notes>
3542  </release>
3543  <release>
3544   <version>
3545    <release>1.0</release>
3546    <api>1.0</api>
3547   </version>
3548   <stability>
3549    <release>stable</release>
3550    <api>stable</api>
3551   </stability>
3552   <date>2003-01-24</date>
3553   <license uri="http://www.php.net/license">PHP License</license>
3554   <notes>
3555Change status to stable
3556   </notes>
3557  </release>
3558  <release>
3559   <version>
3560    <release>0.10-b1</release>
3561    <api>0.10-b1</api>
3562   </version>
3563   <stability>
3564    <release>beta</release>
3565    <api>beta</api>
3566   </stability>
3567   <date>2003-01-08</date>
3568   <license uri="http://www.php.net/license">PHP License</license>
3569   <notes>
3570Add support for long filenames (greater than 99 characters)
3571   </notes>
3572  </release>
3573  <release>
3574   <version>
3575    <release>0.9</release>
3576    <api>0.9</api>
3577   </version>
3578   <stability>
3579    <release>stable</release>
3580    <api>stable</api>
3581   </stability>
3582   <date>2002-05-27</date>
3583   <license uri="http://www.php.net/license">PHP License</license>
3584   <notes>
3585Auto-detect gzip&apos;ed files
3586   </notes>
3587  </release>
3588  <release>
3589   <version>
3590    <release>0.4</release>
3591    <api>0.4</api>
3592   </version>
3593   <stability>
3594    <release>stable</release>
3595    <api>stable</api>
3596   </stability>
3597   <date>2002-05-20</date>
3598   <license uri="http://www.php.net/license">PHP License</license>
3599   <notes>
3600Windows bugfix: use forward slashes inside archives
3601   </notes>
3602  </release>
3603  <release>
3604   <version>
3605    <release>0.2</release>
3606    <api>0.2</api>
3607   </version>
3608   <stability>
3609    <release>stable</release>
3610    <api>stable</api>
3611   </stability>
3612   <date>2002-02-18</date>
3613   <license uri="http://www.php.net/license">PHP License</license>
3614   <notes>
3615From initial commit to stable
3616   </notes>
3617  </release>
3618  <release>
3619   <version>
3620    <release>0.3</release>
3621    <api>0.3</api>
3622   </version>
3623   <stability>
3624    <release>stable</release>
3625    <api>stable</api>
3626   </stability>
3627   <date>2002-04-13</date>
3628   <license uri="http://www.php.net/license">PHP License</license>
3629   <notes>
3630Windows bugfix: used wrong directory separators
3631   </notes>
3632  </release>
3633 </changelog>
3634</package>
3635Archive_Tar-1.3.11/Archive/Tar.php0000644000175000017500000020257612105433221015611 0ustar  druiddruid<?php
3636/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3637
3638/**
3639 * File::CSV
3640 *
3641 * PHP versions 4 and 5
3642 *
3643 * Copyright (c) 1997-2008,
3644 * Vincent Blavet <vincent@phpconcept.net>
3645 * All rights reserved.
3646 *
3647 * Redistribution and use in source and binary forms, with or without
3648 * modification, are permitted provided that the following conditions are met:
3649 *
3650 *     * Redistributions of source code must retain the above copyright notice,
3651 *       this list of conditions and the following disclaimer.
3652 *     * Redistributions in binary form must reproduce the above copyright
3653 *       notice, this list of conditions and the following disclaimer in the
3654 *       documentation and/or other materials provided with the distribution.
3655 *
3656 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
3657 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3658 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
3659 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
3660 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3661 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
3662 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
3663 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3664 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3665 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3666 *
3667 * @category  File_Formats
3668 * @package   Archive_Tar
3669 * @author    Vincent Blavet <vincent@phpconcept.net>
3670 * @copyright 1997-2010 The Authors
3671 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
3672 * @version   CVS: $Id$
3673 * @link      http://pear.php.net/package/Archive_Tar
3674 */
3675
3676require_once 'PEAR.php';
3677
3678define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
3679define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
3680
3681/**
3682* Creates a (compressed) Tar archive
3683*
3684* @package Archive_Tar
3685* @author  Vincent Blavet <vincent@phpconcept.net>
3686* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
3687* @version $Revision$
3688*/
3689class Archive_Tar extends PEAR
3690{
3691    /**
3692    * @var string Name of the Tar
3693    */
3694    var $_tarname='';
3695
3696    /**
3697    * @var boolean if true, the Tar file will be gzipped
3698    */
3699    var $_compress=false;
3700
3701    /**
3702    * @var string Type of compression : 'none', 'gz' or 'bz2'
3703    */
3704    var $_compress_type='none';
3705
3706    /**
3707    * @var string Explode separator
3708    */
3709    var $_separator=' ';
3710
3711    /**
3712    * @var file descriptor
3713    */
3714    var $_file=0;
3715
3716    /**
3717    * @var string Local Tar name of a remote Tar (http:// or ftp://)
3718    */
3719    var $_temp_tarname='';
3720
3721    /**
3722    * @var string regular expression for ignoring files or directories
3723    */
3724    var $_ignore_regexp='';
3725
3726    /**
3727     * @var object PEAR_Error object
3728     */
3729    var $error_object=null; 
3730
3731    // {{{ constructor
3732    /**
3733    * Archive_Tar Class constructor. This flavour of the constructor only
3734    * declare a new Archive_Tar object, identifying it by the name of the
3735    * tar file.
3736    * If the compress argument is set the tar will be read or created as a
3737    * gzip or bz2 compressed TAR file.
3738    *
3739    * @param string $p_tarname  The name of the tar archive to create
3740    * @param string $p_compress can be null, 'gz' or 'bz2'. This
3741    *               parameter indicates if gzip or bz2 compression
3742    *               is required.  For compatibility reason the
3743    *               boolean value 'true' means 'gz'.
3744    *
3745    * @access public
3746    */
3747    function Archive_Tar($p_tarname, $p_compress = null)
3748    {
3749        $this->PEAR();
3750        $this->_compress = false;
3751        $this->_compress_type = 'none';
3752        if (($p_compress === null) || ($p_compress == '')) {
3753            if (@file_exists($p_tarname)) {
3754                if ($fp = @fopen($p_tarname, "rb")) {
3755                    // look for gzip magic cookie
3756                    $data = fread($fp, 2);
3757                    fclose($fp);
3758                    if ($data == "\37\213") {
3759                        $this->_compress = true;
3760                        $this->_compress_type = 'gz';
3761                        // No sure it's enought for a magic code ....
3762                    } elseif ($data == "BZ") {
3763                        $this->_compress = true;
3764                        $this->_compress_type = 'bz2';
3765                    }
3766                }
3767            } else {
3768                // probably a remote file or some file accessible
3769                // through a stream interface
3770                if (substr($p_tarname, -2) == 'gz') {
3771                    $this->_compress = true;
3772                    $this->_compress_type = 'gz';
3773                } elseif ((substr($p_tarname, -3) == 'bz2') ||
3774                          (substr($p_tarname, -2) == 'bz')) {
3775                    $this->_compress = true;
3776                    $this->_compress_type = 'bz2';
3777                }
3778            }
3779        } else {
3780            if (($p_compress === true) || ($p_compress == 'gz')) {
3781                $this->_compress = true;
3782                $this->_compress_type = 'gz';
3783            } else if ($p_compress == 'bz2') {
3784                $this->_compress = true;
3785                $this->_compress_type = 'bz2';
3786            } else {
3787                $this->_error("Unsupported compression type '$p_compress'\n".
3788                    "Supported types are 'gz' and 'bz2'.\n");
3789                return false;
3790            }
3791        }
3792        $this->_tarname = $p_tarname;
3793        if ($this->_compress) { // assert zlib or bz2 extension support
3794            if ($this->_compress_type == 'gz')
3795                $extname = 'zlib';
3796            else if ($this->_compress_type == 'bz2')
3797                $extname = 'bz2';
3798
3799            if (!extension_loaded($extname)) {
3800                PEAR::loadExtension($extname);
3801            }
3802            if (!extension_loaded($extname)) {
3803                $this->_error("The extension '$extname' couldn't be found.\n".
3804                    "Please make sure your version of PHP was built ".
3805                    "with '$extname' support.\n");
3806                return false;
3807            }
3808        }
3809    }
3810    // }}}
3811
3812    // {{{ destructor
3813    function _Archive_Tar()
3814    {
3815        $this->_close();
3816        // ----- Look for a local copy to delete
3817        if ($this->_temp_tarname != '')
3818            @unlink($this->_temp_tarname);
3819        $this->_PEAR();
3820    }
3821    // }}}
3822
3823    // {{{ create()
3824    /**
3825    * This method creates the archive file and add the files / directories
3826    * that are listed in $p_filelist.
3827    * If a file with the same name exist and is writable, it is replaced
3828    * by the new tar.
3829    * The method return false and a PEAR error text.
3830    * The $p_filelist parameter can be an array of string, each string
3831    * representing a filename or a directory name with their path if
3832    * needed. It can also be a single string with names separated by a
3833    * single blank.
3834    * For each directory added in the archive, the files and
3835    * sub-directories are also added.
3836    * See also createModify() method for more details.
3837    *
3838    * @param array $p_filelist An array of filenames and directory names, or a
3839    *              single string with names separated by a single
3840    *              blank space.
3841    *
3842    * @return true on success, false on error.
3843    * @see    createModify()
3844    * @access public
3845    */
3846    function create($p_filelist)
3847    {
3848        return $this->createModify($p_filelist, '', '');
3849    }
3850    // }}}
3851
3852    // {{{ add()
3853    /**
3854    * This method add the files / directories that are listed in $p_filelist in
3855    * the archive. If the archive does not exist it is created.
3856    * The method return false and a PEAR error text.
3857    * The files and directories listed are only added at the end of the archive,
3858    * even if a file with the same name is already archived.
3859    * See also createModify() method for more details.
3860    *
3861    * @param array $p_filelist An array of filenames and directory names, or a
3862    *              single string with names separated by a single
3863    *              blank space.
3864    *
3865    * @return true on success, false on error.
3866    * @see    createModify()
3867    * @access public
3868    */
3869    function add($p_filelist)
3870    {
3871        return $this->addModify($p_filelist, '', '');
3872    }
3873    // }}}
3874
3875    // {{{ extract()
3876    function extract($p_path='', $p_preserve=false)
3877    {
3878        return $this->extractModify($p_path, '', $p_preserve);
3879    }
3880    // }}}
3881
3882    // {{{ listContent()
3883    function listContent()
3884    {
3885        $v_list_detail = array();
3886
3887        if ($this->_openRead()) {
3888            if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
3889                unset($v_list_detail);
3890                $v_list_detail = 0;
3891            }
3892            $this->_close();
3893        }
3894
3895        return $v_list_detail;
3896    }
3897    // }}}
3898
3899    // {{{ createModify()
3900    /**
3901    * This method creates the archive file and add the files / directories
3902    * that are listed in $p_filelist.
3903    * If the file already exists and is writable, it is replaced by the
3904    * new tar. It is a create and not an add. If the file exists and is
3905    * read-only or is a directory it is not replaced. The method return
3906    * false and a PEAR error text.
3907    * The $p_filelist parameter can be an array of string, each string
3908    * representing a filename or a directory name with their path if
3909    * needed. It can also be a single string with names separated by a
3910    * single blank.
3911    * The path indicated in $p_remove_dir will be removed from the
3912    * memorized path of each file / directory listed when this path
3913    * exists. By default nothing is removed (empty path '')
3914    * The path indicated in $p_add_dir will be added at the beginning of
3915    * the memorized path of each file / directory listed. However it can
3916    * be set to empty ''. The adding of a path is done after the removing
3917    * of path.
3918    * The path add/remove ability enables the user to prepare an archive
3919    * for extraction in a different path than the origin files are.
3920    * See also addModify() method for file adding properties.
3921    *
3922    * @param array  $p_filelist   An array of filenames and directory names,
3923    *                             or a single string with names separated by
3924    *                             a single blank space.
3925    * @param string $p_add_dir    A string which contains a path to be added
3926    *                             to the memorized path of each element in
3927    *                             the list.
3928    * @param string $p_remove_dir A string which contains a path to be
3929    *                             removed from the memorized path of each
3930    *                             element in the list, when relevant.
3931    *
3932    * @return boolean true on success, false on error.
3933    * @access public
3934    * @see addModify()
3935    */
3936    function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
3937    {
3938        $v_result = true;
3939
3940        if (!$this->_openWrite())
3941            return false;
3942
3943        if ($p_filelist != '') {
3944            if (is_array($p_filelist))
3945                $v_list = $p_filelist;
3946            elseif (is_string($p_filelist))
3947                $v_list = explode($this->_separator, $p_filelist);
3948            else {
3949                $this->_cleanFile();
3950                $this->_error('Invalid file list');
3951                return false;
3952            }
3953
3954            $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
3955        }
3956
3957        if ($v_result) {
3958            $this->_writeFooter();
3959            $this->_close();
3960        } else
3961            $this->_cleanFile();
3962
3963        return $v_result;
3964    }
3965    // }}}
3966
3967    // {{{ addModify()
3968    /**
3969    * This method add the files / directories listed in $p_filelist at the
3970    * end of the existing archive. If the archive does not yet exists it
3971    * is created.
3972    * The $p_filelist parameter can be an array of string, each string
3973    * representing a filename or a directory name with their path if
3974    * needed. It can also be a single string with names separated by a
3975    * single blank.
3976    * The path indicated in $p_remove_dir will be removed from the
3977    * memorized path of each file / directory listed when this path
3978    * exists. By default nothing is removed (empty path '')
3979    * The path indicated in $p_add_dir will be added at the beginning of
3980    * the memorized path of each file / directory listed. However it can
3981    * be set to empty ''. The adding of a path is done after the removing
3982    * of path.
3983    * The path add/remove ability enables the user to prepare an archive
3984    * for extraction in a different path than the origin files are.
3985    * If a file/dir is already in the archive it will only be added at the
3986    * end of the archive. There is no update of the existing archived
3987    * file/dir. However while extracting the archive, the last file will
3988    * replace the first one. This results in a none optimization of the
3989    * archive size.
3990    * If a file/dir does not exist the file/dir is ignored. However an
3991    * error text is send to PEAR error.
3992    * If a file/dir is not readable the file/dir is ignored. However an
3993    * error text is send to PEAR error.
3994    *
3995    * @param array  $p_filelist   An array of filenames and directory
3996    *                             names, or a single string with names
3997    *                             separated by a single blank space.
3998    * @param string $p_add_dir    A string which contains a path to be
3999    *                             added to the memorized path of each
4000    *                             element in the list.
4001    * @param string $p_remove_dir A string which contains a path to be
4002    *                             removed from the memorized path of
4003    *                             each element in the list, when
4004    *                             relevant.
4005    *
4006    * @return true on success, false on error.
4007    * @access public
4008    */
4009    function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
4010    {
4011        $v_result = true;
4012
4013        if (!$this->_isArchive())
4014            $v_result = $this->createModify($p_filelist, $p_add_dir,
4015                                            $p_remove_dir);
4016        else {
4017            if (is_array($p_filelist))
4018                $v_list = $p_filelist;
4019            elseif (is_string($p_filelist))
4020                $v_list = explode($this->_separator, $p_filelist);
4021            else {
4022                $this->_error('Invalid file list');
4023                return false;
4024            }
4025
4026            $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
4027        }
4028
4029        return $v_result;
4030    }
4031    // }}}
4032
4033    // {{{ addString()
4034    /**
4035    * This method add a single string as a file at the
4036    * end of the existing archive. If the archive does not yet exists it
4037    * is created.
4038    *
4039    * @param string $p_filename A string which contains the full
4040    *                           filename path that will be associated
4041    *                           with the string.
4042    * @param string $p_string   The content of the file added in
4043    *                           the archive.
4044    * @param int    $p_datetime A custom date/time (unix timestamp)
4045    *                           for the file (optional).
4046    *
4047    * @return true on success, false on error.
4048    * @access public
4049    */
4050    function addString($p_filename, $p_string, $p_datetime = false)
4051    {
4052        $v_result = true;
4053
4054        if (!$this->_isArchive()) {
4055            if (!$this->_openWrite()) {
4056                return false;
4057            }
4058            $this->_close();
4059        }
4060
4061        if (!$this->_openAppend())
4062            return false;
4063
4064        // Need to check the get back to the temporary file ? ....
4065        $v_result = $this->_addString($p_filename, $p_string, $p_datetime);
4066
4067        $this->_writeFooter();
4068
4069        $this->_close();
4070
4071        return $v_result;
4072    }
4073    // }}}
4074
4075    // {{{ extractModify()
4076    /**
4077    * This method extract all the content of the archive in the directory
4078    * indicated by $p_path. When relevant the memorized path of the
4079    * files/dir can be modified by removing the $p_remove_path path at the
4080    * beginning of the file/dir path.
4081    * While extracting a file, if the directory path does not exists it is
4082    * created.
4083    * While extracting a file, if the file already exists it is replaced
4084    * without looking for last modification date.
4085    * While extracting a file, if the file already exists and is write
4086    * protected, the extraction is aborted.
4087    * While extracting a file, if a directory with the same name already
4088    * exists, the extraction is aborted.
4089    * While extracting a directory, if a file with the same name already
4090    * exists, the extraction is aborted.
4091    * While extracting a file/directory if the destination directory exist
4092    * and is write protected, or does not exist but can not be created,
4093    * the extraction is aborted.
4094    * If after extraction an extracted file does not show the correct
4095    * stored file size, the extraction is aborted.
4096    * When the extraction is aborted, a PEAR error text is set and false
4097    * is returned. However the result can be a partial extraction that may
4098    * need to be manually cleaned.
4099    *
4100    * @param string  $p_path        The path of the directory where the
4101    *                               files/dir need to by extracted.
4102    * @param string  $p_remove_path Part of the memorized path that can be
4103    *                               removed if present at the beginning of
4104    *                               the file/dir path.
4105    * @param boolean $p_preserve    Preserve user/group ownership of files
4106    *
4107    * @return boolean true on success, false on error.
4108    * @access public
4109    * @see    extractList()
4110    */
4111    function extractModify($p_path, $p_remove_path, $p_preserve=false)
4112    {
4113        $v_result = true;
4114        $v_list_detail = array();
4115
4116        if ($v_result = $this->_openRead()) {
4117            $v_result = $this->_extractList($p_path, $v_list_detail,
4118                "complete", 0, $p_remove_path, $p_preserve);
4119            $this->_close();
4120        }
4121
4122        return $v_result;
4123    }
4124    // }}}
4125
4126    // {{{ extractInString()
4127    /**
4128    * This method extract from the archive one file identified by $p_filename.
4129    * The return value is a string with the file content, or NULL on error.
4130    *
4131    * @param string $p_filename The path of the file to extract in a string.
4132    *
4133    * @return a string with the file content or NULL.
4134    * @access public
4135    */
4136    function extractInString($p_filename)
4137    {
4138        if ($this->_openRead()) {
4139            $v_result = $this->_extractInString($p_filename);
4140            $this->_close();
4141        } else {
4142            $v_result = null;
4143        }
4144
4145        return $v_result;
4146    }
4147    // }}}
4148
4149    // {{{ extractList()
4150    /**
4151    * This method extract from the archive only the files indicated in the
4152    * $p_filelist. These files are extracted in the current directory or
4153    * in the directory indicated by the optional $p_path parameter.
4154    * If indicated the $p_remove_path can be used in the same way as it is
4155    * used in extractModify() method.
4156    *
4157    * @param array   $p_filelist    An array of filenames and directory names,
4158    *                               or a single string with names separated
4159    *                               by a single blank space.
4160    * @param string  $p_path        The path of the directory where the
4161    *                               files/dir need to by extracted.
4162    * @param string  $p_remove_path Part of the memorized path that can be
4163    *                               removed if present at the beginning of
4164    *                               the file/dir path.
4165    * @param boolean $p_preserve    Preserve user/group ownership of files
4166    *
4167    * @return true on success, false on error.
4168    * @access public
4169    * @see    extractModify()
4170    */
4171    function extractList($p_filelist, $p_path='', $p_remove_path='', $p_preserve=false)
4172    {
4173        $v_result = true;
4174        $v_list_detail = array();
4175
4176        if (is_array($p_filelist))
4177            $v_list = $p_filelist;
4178        elseif (is_string($p_filelist))
4179            $v_list = explode($this->_separator, $p_filelist);
4180        else {
4181            $this->_error('Invalid string list');
4182            return false;
4183        }
4184
4185        if ($v_result = $this->_openRead()) {
4186            $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
4187                $v_list, $p_remove_path, $p_preserve);
4188            $this->_close();
4189        }
4190
4191        return $v_result;
4192    }
4193    // }}}
4194
4195    // {{{ setAttribute()
4196    /**
4197    * This method set specific attributes of the archive. It uses a variable
4198    * list of parameters, in the format attribute code + attribute values :
4199    * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
4200    *
4201    * @param mixed $argv variable list of attributes and values
4202    *
4203    * @return true on success, false on error.
4204    * @access public
4205    */
4206    function setAttribute()
4207    {
4208        $v_result = true;
4209
4210        // ----- Get the number of variable list of arguments
4211        if (($v_size = func_num_args()) == 0) {
4212            return true;
4213        }
4214
4215        // ----- Get the arguments
4216        $v_att_list = &func_get_args();
4217
4218        // ----- Read the attributes
4219        $i=0;
4220        while ($i<$v_size) {
4221
4222            // ----- Look for next option
4223            switch ($v_att_list[$i]) {
4224                // ----- Look for options that request a string value
4225                case ARCHIVE_TAR_ATT_SEPARATOR :
4226                    // ----- Check the number of parameters
4227                    if (($i+1) >= $v_size) {
4228                        $this->_error('Invalid number of parameters for '
4229						              .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
4230                        return false;
4231                    }
4232
4233                    // ----- Get the value
4234                    $this->_separator = $v_att_list[$i+1];
4235                    $i++;
4236                break;
4237
4238                default :
4239                    $this->_error('Unknow attribute code '.$v_att_list[$i].'');
4240                    return false;
4241            }
4242
4243            // ----- Next attribute
4244            $i++;
4245        }
4246
4247        return $v_result;
4248    }
4249    // }}}
4250
4251    // {{{ setIgnoreRegexp()
4252    /**
4253    * This method sets the regular expression for ignoring files and directories
4254    * at import, for example:
4255    * $arch->setIgnoreRegexp("#CVS|\.svn#");
4256    *
4257    * @param string $regexp regular expression defining which files or directories to ignore
4258    *
4259    * @access public
4260    */
4261    function setIgnoreRegexp($regexp)
4262    {
4263    	$this->_ignore_regexp = $regexp;
4264    }
4265    // }}}
4266
4267    // {{{ setIgnoreList()
4268    /**
4269    * This method sets the regular expression for ignoring all files and directories
4270    * matching the filenames in the array list at import, for example:
4271    * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool'));
4272    *
4273    * @param array $list a list of file or directory names to ignore
4274    *
4275    * @access public
4276    */
4277    function setIgnoreList($list)
4278    {
4279    	$regexp = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list);
4280    	$regexp = '#/'.join('$|/', $list).'#';
4281    	$this->setIgnoreRegexp($regexp);
4282    }
4283    // }}}
4284
4285    // {{{ _error()
4286    function _error($p_message)
4287    {
4288        $this->error_object = &$this->raiseError($p_message); 
4289    }
4290    // }}}
4291
4292    // {{{ _warning()
4293    function _warning($p_message)
4294    {
4295        $this->error_object = &$this->raiseError($p_message); 
4296    }
4297    // }}}
4298
4299    // {{{ _isArchive()
4300    function _isArchive($p_filename=null)
4301    {
4302        if ($p_filename == null) {
4303            $p_filename = $this->_tarname;
4304        }
4305        clearstatcache();
4306        return @is_file($p_filename) && !@is_link($p_filename);
4307    }
4308    // }}}
4309
4310    // {{{ _openWrite()
4311    function _openWrite()
4312    {
4313        if ($this->_compress_type == 'gz' && function_exists('gzopen'))
4314            $this->_file = @gzopen($this->_tarname, "wb9");
4315        else if ($this->_compress_type == 'bz2' && function_exists('bzopen'))
4316            $this->_file = @bzopen($this->_tarname, "w");
4317        else if ($this->_compress_type == 'none')
4318            $this->_file = @fopen($this->_tarname, "wb");
4319        else {
4320            $this->_error('Unknown or missing compression type ('
4321			              .$this->_compress_type.')');
4322            return false;
4323        }
4324
4325        if ($this->_file == 0) {
4326            $this->_error('Unable to open in write mode \''
4327			              .$this->_tarname.'\'');
4328            return false;
4329        }
4330
4331        return true;
4332    }
4333    // }}}
4334
4335    // {{{ _openRead()
4336    function _openRead()
4337    {
4338        if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
4339
4340          // ----- Look if a local copy need to be done
4341          if ($this->_temp_tarname == '') {
4342              $this->_temp_tarname = uniqid('tar').'.tmp';
4343              if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
4344                $this->_error('Unable to open in read mode \''
4345				              .$this->_tarname.'\'');
4346                $this->_temp_tarname = '';
4347                return false;
4348              }
4349              if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
4350                $this->_error('Unable to open in write mode \''
4351				              .$this->_temp_tarname.'\'');
4352                $this->_temp_tarname = '';
4353                return false;
4354              }
4355              while ($v_data = @fread($v_file_from, 1024))
4356                  @fwrite($v_file_to, $v_data);
4357              @fclose($v_file_from);
4358              @fclose($v_file_to);
4359          }
4360
4361          // ----- File to open if the local copy
4362          $v_filename = $this->_temp_tarname;
4363
4364        } else
4365          // ----- File to open if the normal Tar file
4366          $v_filename = $this->_tarname;
4367
4368        if ($this->_compress_type == 'gz' && function_exists('gzopen'))
4369            $this->_file = @gzopen($v_filename, "rb");
4370        else if ($this->_compress_type == 'bz2' && function_exists('bzopen'))
4371            $this->_file = @bzopen($v_filename, "r");
4372        else if ($this->_compress_type == 'none')
4373            $this->_file = @fopen($v_filename, "rb");
4374        else {
4375            $this->_error('Unknown or missing compression type ('
4376			              .$this->_compress_type.')');
4377            return false;
4378        }
4379
4380        if ($this->_file == 0) {
4381            $this->_error('Unable to open in read mode \''.$v_filename.'\'');
4382            return false;
4383        }
4384
4385        return true;
4386    }
4387    // }}}
4388
4389    // {{{ _openReadWrite()
4390    function _openReadWrite()
4391    {
4392        if ($this->_compress_type == 'gz')
4393            $this->_file = @gzopen($this->_tarname, "r+b");
4394        else if ($this->_compress_type == 'bz2') {
4395            $this->_error('Unable to open bz2 in read/write mode \''
4396			              .$this->_tarname.'\' (limitation of bz2 extension)');
4397            return false;
4398        } else if ($this->_compress_type == 'none')
4399            $this->_file = @fopen($this->_tarname, "r+b");
4400        else {
4401            $this->_error('Unknown or missing compression type ('
4402			              .$this->_compress_type.')');
4403            return false;
4404        }
4405
4406        if ($this->_file == 0) {
4407            $this->_error('Unable to open in read/write mode \''
4408			              .$this->_tarname.'\'');
4409            return false;
4410        }
4411
4412        return true;
4413    }
4414    // }}}
4415
4416    // {{{ _close()
4417    function _close()
4418    {
4419        //if (isset($this->_file)) {
4420        if (is_resource($this->_file)) {
4421            if ($this->_compress_type == 'gz')
4422                @gzclose($this->_file);
4423            else if ($this->_compress_type == 'bz2')
4424                @bzclose($this->_file);
4425            else if ($this->_compress_type == 'none')
4426                @fclose($this->_file);
4427            else
4428                $this->_error('Unknown or missing compression type ('
4429				              .$this->_compress_type.')');
4430
4431            $this->_file = 0;
4432        }
4433
4434        // ----- Look if a local copy need to be erase
4435        // Note that it might be interesting to keep the url for a time : ToDo
4436        if ($this->_temp_tarname != '') {
4437            @unlink($this->_temp_tarname);
4438            $this->_temp_tarname = '';
4439        }
4440
4441        return true;
4442    }
4443    // }}}
4444
4445    // {{{ _cleanFile()
4446    function _cleanFile()
4447    {
4448        $this->_close();
4449
4450        // ----- Look for a local copy
4451        if ($this->_temp_tarname != '') {
4452            // ----- Remove the local copy but not the remote tarname
4453            @unlink($this->_temp_tarname);
4454            $this->_temp_tarname = '';
4455        } else {
4456            // ----- Remove the local tarname file
4457            @unlink($this->_tarname);
4458        }
4459        $this->_tarname = '';
4460
4461        return true;
4462    }
4463    // }}}
4464
4465    // {{{ _writeBlock()
4466    function _writeBlock($p_binary_data, $p_len=null)
4467    {
4468      if (is_resource($this->_file)) {
4469          if ($p_len === null) {
4470              if ($this->_compress_type == 'gz')
4471                  @gzputs($this->_file, $p_binary_data);
4472              else if ($this->_compress_type == 'bz2')
4473                  @bzwrite($this->_file, $p_binary_data);
4474              else if ($this->_compress_type == 'none')
4475                  @fputs($this->_file, $p_binary_data);
4476              else
4477                  $this->_error('Unknown or missing compression type ('
4478				                .$this->_compress_type.')');
4479          } else {
4480              if ($this->_compress_type == 'gz')
4481                  @gzputs($this->_file, $p_binary_data, $p_len);
4482              else if ($this->_compress_type == 'bz2')
4483                  @bzwrite($this->_file, $p_binary_data, $p_len);
4484              else if ($this->_compress_type == 'none')
4485                  @fputs($this->_file, $p_binary_data, $p_len);
4486              else
4487                  $this->_error('Unknown or missing compression type ('
4488				                .$this->_compress_type.')');
4489
4490          }
4491      }
4492      return true;
4493    }
4494    // }}}
4495
4496    // {{{ _readBlock()
4497    function _readBlock()
4498    {
4499      $v_block = null;
4500      if (is_resource($this->_file)) {
4501          if ($this->_compress_type == 'gz')
4502              $v_block = @gzread($this->_file, 512);
4503          else if ($this->_compress_type == 'bz2')
4504              $v_block = @bzread($this->_file, 512);
4505          else if ($this->_compress_type == 'none')
4506              $v_block = @fread($this->_file, 512);
4507          else
4508              $this->_error('Unknown or missing compression type ('
4509			                .$this->_compress_type.')');
4510      }
4511      return $v_block;
4512    }
4513    // }}}
4514
4515    // {{{ _jumpBlock()
4516    function _jumpBlock($p_len=null)
4517    {
4518      if (is_resource($this->_file)) {
4519          if ($p_len === null)
4520              $p_len = 1;
4521
4522          if ($this->_compress_type == 'gz') {
4523              @gzseek($this->_file, gztell($this->_file)+($p_len*512));
4524          }
4525          else if ($this->_compress_type == 'bz2') {
4526              // ----- Replace missing bztell() and bzseek()
4527              for ($i=0; $i<$p_len; $i++)
4528                  $this->_readBlock();
4529          } else if ($this->_compress_type == 'none')
4530              @fseek($this->_file, $p_len*512, SEEK_CUR);
4531          else
4532              $this->_error('Unknown or missing compression type ('
4533			                .$this->_compress_type.')');
4534
4535      }
4536      return true;
4537    }
4538    // }}}
4539
4540    // {{{ _writeFooter()
4541    function _writeFooter()
4542    {
4543      if (is_resource($this->_file)) {
4544          // ----- Write the last 0 filled block for end of archive
4545          $v_binary_data = pack('a1024', '');
4546          $this->_writeBlock($v_binary_data);
4547      }
4548      return true;
4549    }
4550    // }}}
4551
4552    // {{{ _addList()
4553    function _addList($p_list, $p_add_dir, $p_remove_dir)
4554    {
4555      $v_result=true;
4556      $v_header = array();
4557
4558      // ----- Remove potential windows directory separator
4559      $p_add_dir = $this->_translateWinPath($p_add_dir);
4560      $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
4561
4562      if (!$this->_file) {
4563          $this->_error('Invalid file descriptor');
4564          return false;
4565      }
4566
4567      if (sizeof($p_list) == 0)
4568          return true;
4569
4570      foreach ($p_list as $v_filename) {
4571          if (!$v_result) {
4572              break;
4573          }
4574
4575        // ----- Skip the current tar name
4576        if ($v_filename == $this->_tarname)
4577            continue;
4578
4579        if ($v_filename == '')
4580            continue;
4581
4582       	// ----- ignore files and directories matching the ignore regular expression
4583       	if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/'.$v_filename)) {
4584            $this->_warning("File '$v_filename' ignored");
4585       	    continue;
4586       	}
4587
4588        if (!file_exists($v_filename) && !is_link($v_filename)) {
4589            $this->_warning("File '$v_filename' does not exist");
4590            continue;
4591        }
4592
4593        // ----- Add the file or directory header
4594        if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
4595            return false;
4596
4597        if (@is_dir($v_filename) && !@is_link($v_filename)) {
4598            if (!($p_hdir = opendir($v_filename))) {
4599                $this->_warning("Directory '$v_filename' can not be read");
4600                continue;
4601            }
4602            while (false !== ($p_hitem = readdir($p_hdir))) {
4603                if (($p_hitem != '.') && ($p_hitem != '..')) {
4604                    if ($v_filename != ".")
4605                        $p_temp_list[0] = $v_filename.'/'.$p_hitem;
4606                    else
4607                        $p_temp_list[0] = $p_hitem;
4608
4609                    $v_result = $this->_addList($p_temp_list,
4610					                            $p_add_dir,
4611												$p_remove_dir);
4612                }
4613            }
4614
4615            unset($p_temp_list);
4616            unset($p_hdir);
4617            unset($p_hitem);
4618        }
4619      }
4620
4621      return $v_result;
4622    }
4623    // }}}
4624
4625    // {{{ _addFile()
4626    function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
4627    {
4628      if (!$this->_file) {
4629          $this->_error('Invalid file descriptor');
4630          return false;
4631      }
4632
4633      if ($p_filename == '') {
4634          $this->_error('Invalid file name');
4635          return false;
4636      }
4637
4638      // ----- Calculate the stored filename
4639      $p_filename = $this->_translateWinPath($p_filename, false);;
4640      $v_stored_filename = $p_filename;
4641      if (strcmp($p_filename, $p_remove_dir) == 0) {
4642          return true;
4643      }
4644      if ($p_remove_dir != '') {
4645          if (substr($p_remove_dir, -1) != '/')
4646              $p_remove_dir .= '/';
4647
4648          if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
4649              $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
4650      }
4651      $v_stored_filename = $this->_translateWinPath($v_stored_filename);
4652      if ($p_add_dir != '') {
4653          if (substr($p_add_dir, -1) == '/')
4654              $v_stored_filename = $p_add_dir.$v_stored_filename;
4655          else
4656              $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
4657      }
4658
4659      $v_stored_filename = $this->_pathReduction($v_stored_filename);
4660
4661      if ($this->_isArchive($p_filename)) {
4662          if (($v_file = @fopen($p_filename, "rb")) == 0) {
4663              $this->_warning("Unable to open file '".$p_filename
4664			                  ."' in binary read mode");
4665              return true;
4666          }
4667
4668          if (!$this->_writeHeader($p_filename, $v_stored_filename))
4669              return false;
4670
4671          while (($v_buffer = fread($v_file, 512)) != '') {
4672              $v_binary_data = pack("a512", "$v_buffer");
4673              $this->_writeBlock($v_binary_data);
4674          }
4675
4676          fclose($v_file);
4677
4678      } else {
4679          // ----- Only header for dir
4680          if (!$this->_writeHeader($p_filename, $v_stored_filename))
4681              return false;
4682      }
4683
4684      return true;
4685    }
4686    // }}}
4687
4688    // {{{ _addString()
4689    function _addString($p_filename, $p_string, $p_datetime = false)
4690    {
4691      if (!$this->_file) {
4692          $this->_error('Invalid file descriptor');
4693          return false;
4694      }
4695
4696      if ($p_filename == '') {
4697          $this->_error('Invalid file name');
4698          return false;
4699      }
4700
4701      // ----- Calculate the stored filename
4702      $p_filename = $this->_translateWinPath($p_filename, false);;
4703      
4704      // ----- If datetime is not specified, set current time
4705      if ($p_datetime === false) {
4706          $p_datetime = time();
4707      }
4708
4709      if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
4710                                    $p_datetime, 384, "", 0, 0))
4711          return false;
4712
4713      $i=0;
4714      while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
4715          $v_binary_data = pack("a512", $v_buffer);
4716          $this->_writeBlock($v_binary_data);
4717      }
4718
4719      return true;
4720    }
4721    // }}}
4722
4723    // {{{ _writeHeader()
4724    function _writeHeader($p_filename, $p_stored_filename)
4725    {
4726        if ($p_stored_filename == '')
4727            $p_stored_filename = $p_filename;
4728        $v_reduce_filename = $this->_pathReduction($p_stored_filename);
4729
4730        if (strlen($v_reduce_filename) > 99) {
4731          if (!$this->_writeLongHeader($v_reduce_filename))
4732            return false;
4733        }
4734
4735        $v_info = lstat($p_filename);
4736        $v_uid = sprintf("%07s", DecOct($v_info[4]));
4737        $v_gid = sprintf("%07s", DecOct($v_info[5]));
4738        $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
4739
4740        $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
4741
4742        $v_linkname = '';
4743
4744        if (@is_link($p_filename)) {
4745          $v_typeflag = '2';
4746          $v_linkname = readlink($p_filename);
4747          $v_size = sprintf("%011s", DecOct(0));
4748        } elseif (@is_dir($p_filename)) {
4749          $v_typeflag = "5";
4750          $v_size = sprintf("%011s", DecOct(0));
4751        } else {
4752          $v_typeflag = '0';
4753          clearstatcache();
4754          $v_size = sprintf("%011s", DecOct($v_info['size']));
4755        }
4756
4757        $v_magic = 'ustar ';
4758
4759        $v_version = ' ';
4760        
4761        if (function_exists('posix_getpwuid'))
4762        {
4763          $userinfo = posix_getpwuid($v_info[4]);
4764          $groupinfo = posix_getgrgid($v_info[5]);
4765          
4766          $v_uname = $userinfo['name'];
4767          $v_gname = $groupinfo['name'];
4768        }
4769        else
4770        {
4771          $v_uname = '';
4772          $v_gname = '';
4773        }
4774
4775        $v_devmajor = '';
4776
4777        $v_devminor = '';
4778
4779        $v_prefix = '';
4780
4781        $v_binary_data_first = pack("a100a8a8a8a12a12",
4782		                            $v_reduce_filename, $v_perms, $v_uid,
4783									$v_gid, $v_size, $v_mtime);
4784        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
4785		                           $v_typeflag, $v_linkname, $v_magic,
4786								   $v_version, $v_uname, $v_gname,
4787								   $v_devmajor, $v_devminor, $v_prefix, '');
4788
4789        // ----- Calculate the checksum
4790        $v_checksum = 0;
4791        // ..... First part of the header
4792        for ($i=0; $i<148; $i++)
4793            $v_checksum += ord(substr($v_binary_data_first,$i,1));
4794        // ..... Ignore the checksum value and replace it by ' ' (space)
4795        for ($i=148; $i<156; $i++)
4796            $v_checksum += ord(' ');
4797        // ..... Last part of the header
4798        for ($i=156, $j=0; $i<512; $i++, $j++)
4799            $v_checksum += ord(substr($v_binary_data_last,$j,1));
4800
4801        // ----- Write the first 148 bytes of the header in the archive
4802        $this->_writeBlock($v_binary_data_first, 148);
4803
4804        // ----- Write the calculated checksum
4805        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
4806        $v_binary_data = pack("a8", $v_checksum);
4807        $this->_writeBlock($v_binary_data, 8);
4808
4809        // ----- Write the last 356 bytes of the header in the archive
4810        $this->_writeBlock($v_binary_data_last, 356);
4811
4812        return true;
4813    }
4814    // }}}
4815
4816    // {{{ _writeHeaderBlock()
4817    function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
4818	                           $p_type='', $p_uid=0, $p_gid=0)
4819    {
4820        $p_filename = $this->_pathReduction($p_filename);
4821
4822        if (strlen($p_filename) > 99) {
4823          if (!$this->_writeLongHeader($p_filename))
4824            return false;
4825        }
4826
4827        if ($p_type == "5") {
4828          $v_size = sprintf("%011s", DecOct(0));
4829        } else {
4830          $v_size = sprintf("%011s", DecOct($p_size));
4831        }
4832
4833        $v_uid = sprintf("%07s", DecOct($p_uid));
4834        $v_gid = sprintf("%07s", DecOct($p_gid));
4835        $v_perms = sprintf("%07s", DecOct($p_perms & 000777));
4836
4837        $v_mtime = sprintf("%11s", DecOct($p_mtime));
4838
4839        $v_linkname = '';
4840
4841        $v_magic = 'ustar ';
4842
4843        $v_version = ' ';
4844
4845        if (function_exists('posix_getpwuid'))
4846        {
4847          $userinfo = posix_getpwuid($p_uid);
4848          $groupinfo = posix_getgrgid($p_gid);
4849          
4850          $v_uname = $userinfo['name'];
4851          $v_gname = $groupinfo['name'];
4852        }
4853        else
4854        {
4855          $v_uname = '';
4856          $v_gname = '';
4857        }
4858        
4859        $v_devmajor = '';
4860
4861        $v_devminor = '';
4862
4863        $v_prefix = '';
4864
4865        $v_binary_data_first = pack("a100a8a8a8a12A12",
4866		                            $p_filename, $v_perms, $v_uid, $v_gid,
4867									$v_size, $v_mtime);
4868        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
4869		                           $p_type, $v_linkname, $v_magic,
4870								   $v_version, $v_uname, $v_gname,
4871								   $v_devmajor, $v_devminor, $v_prefix, '');
4872
4873        // ----- Calculate the checksum
4874        $v_checksum = 0;
4875        // ..... First part of the header
4876        for ($i=0; $i<148; $i++)
4877            $v_checksum += ord(substr($v_binary_data_first,$i,1));
4878        // ..... Ignore the checksum value and replace it by ' ' (space)
4879        for ($i=148; $i<156; $i++)
4880            $v_checksum += ord(' ');
4881        // ..... Last part of the header
4882        for ($i=156, $j=0; $i<512; $i++, $j++)
4883            $v_checksum += ord(substr($v_binary_data_last,$j,1));
4884
4885        // ----- Write the first 148 bytes of the header in the archive
4886        $this->_writeBlock($v_binary_data_first, 148);
4887
4888        // ----- Write the calculated checksum
4889        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
4890        $v_binary_data = pack("a8", $v_checksum);
4891        $this->_writeBlock($v_binary_data, 8);
4892
4893        // ----- Write the last 356 bytes of the header in the archive
4894        $this->_writeBlock($v_binary_data_last, 356);
4895
4896        return true;
4897    }
4898    // }}}
4899
4900    // {{{ _writeLongHeader()
4901    function _writeLongHeader($p_filename)
4902    {
4903        $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
4904
4905        $v_typeflag = 'L';
4906
4907        $v_linkname = '';
4908
4909        $v_magic = '';
4910
4911        $v_version = '';
4912
4913        $v_uname = '';
4914
4915        $v_gname = '';
4916
4917        $v_devmajor = '';
4918
4919        $v_devminor = '';
4920
4921        $v_prefix = '';
4922
4923        $v_binary_data_first = pack("a100a8a8a8a12a12",
4924		                            '././@LongLink', 0, 0, 0, $v_size, 0);
4925        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
4926		                           $v_typeflag, $v_linkname, $v_magic,
4927								   $v_version, $v_uname, $v_gname,
4928								   $v_devmajor, $v_devminor, $v_prefix, '');
4929
4930        // ----- Calculate the checksum
4931        $v_checksum = 0;
4932        // ..... First part of the header
4933        for ($i=0; $i<148; $i++)
4934            $v_checksum += ord(substr($v_binary_data_first,$i,1));
4935        // ..... Ignore the checksum value and replace it by ' ' (space)
4936        for ($i=148; $i<156; $i++)
4937            $v_checksum += ord(' ');
4938        // ..... Last part of the header
4939        for ($i=156, $j=0; $i<512; $i++, $j++)
4940            $v_checksum += ord(substr($v_binary_data_last,$j,1));
4941
4942        // ----- Write the first 148 bytes of the header in the archive
4943        $this->_writeBlock($v_binary_data_first, 148);
4944
4945        // ----- Write the calculated checksum
4946        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
4947        $v_binary_data = pack("a8", $v_checksum);
4948        $this->_writeBlock($v_binary_data, 8);
4949
4950        // ----- Write the last 356 bytes of the header in the archive
4951        $this->_writeBlock($v_binary_data_last, 356);
4952
4953        // ----- Write the filename as content of the block
4954        $i=0;
4955        while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
4956            $v_binary_data = pack("a512", "$v_buffer");
4957            $this->_writeBlock($v_binary_data);
4958        }
4959
4960        return true;
4961    }
4962    // }}}
4963
4964    // {{{ _readHeader()
4965    function _readHeader($v_binary_data, &$v_header)
4966    {
4967        if (strlen($v_binary_data)==0) {
4968            $v_header['filename'] = '';
4969            return true;
4970        }
4971
4972        if (strlen($v_binary_data) != 512) {
4973            $v_header['filename'] = '';
4974            $this->_error('Invalid block size : '.strlen($v_binary_data));
4975            return false;
4976        }
4977
4978        if (!is_array($v_header)) {
4979            $v_header = array();
4980        }
4981        // ----- Calculate the checksum
4982        $v_checksum = 0;
4983        // ..... First part of the header
4984        for ($i=0; $i<148; $i++)
4985            $v_checksum+=ord(substr($v_binary_data,$i,1));
4986        // ..... Ignore the checksum value and replace it by ' ' (space)
4987        for ($i=148; $i<156; $i++)
4988            $v_checksum += ord(' ');
4989        // ..... Last part of the header
4990        for ($i=156; $i<512; $i++)
4991           $v_checksum+=ord(substr($v_binary_data,$i,1));
4992
4993        if (version_compare(PHP_VERSION,"5.5.0-dev")<0) {
4994            $fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
4995                   "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
4996                   "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
4997        } else {
4998            $fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
4999                   "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
5000                   "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
5001        }
5002        $v_data = unpack($fmt, $v_binary_data);
5003
5004        if (strlen($v_data["prefix"]) > 0) {
5005            $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
5006        }
5007
5008        // ----- Extract the checksum
5009        $v_header['checksum'] = OctDec(trim($v_data['checksum']));
5010        if ($v_header['checksum'] != $v_checksum) {
5011            $v_header['filename'] = '';
5012
5013            // ----- Look for last block (empty block)
5014            if (($v_checksum == 256) && ($v_header['checksum'] == 0))
5015                return true;
5016
5017            $this->_error('Invalid checksum for file "'.$v_data['filename']
5018			              .'" : '.$v_checksum.' calculated, '
5019						  .$v_header['checksum'].' expected');
5020            return false;
5021        }
5022
5023        // ----- Extract the properties
5024        $v_header['filename'] = $v_data['filename'];
5025        if ($this->_maliciousFilename($v_header['filename'])) {
5026            $this->_error('Malicious .tar detected, file "' . $v_header['filename'] .
5027                '" will not install in desired directory tree');
5028            return false;
5029        }
5030        $v_header['mode'] = OctDec(trim($v_data['mode']));
5031        $v_header['uid'] = OctDec(trim($v_data['uid']));
5032        $v_header['gid'] = OctDec(trim($v_data['gid']));
5033        $v_header['size'] = OctDec(trim($v_data['size']));
5034        $v_header['mtime'] = OctDec(trim($v_data['mtime']));
5035        if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
5036          $v_header['size'] = 0;
5037        }
5038        $v_header['link'] = trim($v_data['link']);
5039        /* ----- All these fields are removed form the header because
5040		they do not carry interesting info
5041        $v_header[magic] = trim($v_data[magic]);
5042        $v_header[version] = trim($v_data[version]);
5043        $v_header[uname] = trim($v_data[uname]);
5044        $v_header[gname] = trim($v_data[gname]);
5045        $v_header[devmajor] = trim($v_data[devmajor]);
5046        $v_header[devminor] = trim($v_data[devminor]);
5047        */
5048
5049        return true;
5050    }
5051    // }}}
5052
5053    // {{{ _maliciousFilename()
5054    /**
5055     * Detect and report a malicious file name
5056     *
5057     * @param string $file
5058     *
5059     * @return bool
5060     * @access private
5061     */
5062    function _maliciousFilename($file)
5063    {
5064        if (strpos($file, '/../') !== false) {
5065            return true;
5066        }
5067        if (strpos($file, '../') === 0) {
5068            return true;
5069        }
5070        return false;
5071    }
5072    // }}}
5073
5074    // {{{ _readLongHeader()
5075    function _readLongHeader(&$v_header)
5076    {
5077      $v_filename = '';
5078      $n = floor($v_header['size']/512);
5079      for ($i=0; $i<$n; $i++) {
5080        $v_content = $this->_readBlock();
5081        $v_filename .= $v_content;
5082      }
5083      if (($v_header['size'] % 512) != 0) {
5084        $v_content = $this->_readBlock();
5085        $v_filename .= trim($v_content);
5086      }
5087
5088      // ----- Read the next header
5089      $v_binary_data = $this->_readBlock();
5090
5091      if (!$this->_readHeader($v_binary_data, $v_header))
5092        return false;
5093
5094      $v_filename = trim($v_filename);
5095      $v_header['filename'] = $v_filename;
5096        if ($this->_maliciousFilename($v_filename)) {
5097            $this->_error('Malicious .tar detected, file "' . $v_filename .
5098                '" will not install in desired directory tree');
5099            return false;
5100      }
5101
5102      return true;
5103    }
5104    // }}}
5105
5106    // {{{ _extractInString()
5107    /**
5108    * This method extract from the archive one file identified by $p_filename.
5109    * The return value is a string with the file content, or null on error.
5110    *
5111    * @param string $p_filename The path of the file to extract in a string.
5112    *
5113    * @return a string with the file content or null.
5114    * @access private
5115    */
5116    function _extractInString($p_filename)
5117    {
5118        $v_result_str = "";
5119
5120        While (strlen($v_binary_data = $this->_readBlock()) != 0)
5121        {
5122          if (!$this->_readHeader($v_binary_data, $v_header))
5123            return null;
5124
5125          if ($v_header['filename'] == '')
5126            continue;
5127
5128          // ----- Look for long filename
5129          if ($v_header['typeflag'] == 'L') {
5130            if (!$this->_readLongHeader($v_header))
5131              return null;
5132          }
5133
5134          if ($v_header['filename'] == $p_filename) {
5135              if ($v_header['typeflag'] == "5") {
5136                  $this->_error('Unable to extract in string a directory '
5137				                .'entry {'.$v_header['filename'].'}');
5138                  return null;
5139              } else {
5140                  $n = floor($v_header['size']/512);
5141                  for ($i=0; $i<$n; $i++) {
5142                      $v_result_str .= $this->_readBlock();
5143                  }
5144                  if (($v_header['size'] % 512) != 0) {
5145                      $v_content = $this->_readBlock();
5146                      $v_result_str .= substr($v_content, 0,
5147					                          ($v_header['size'] % 512));
5148                  }
5149                  return $v_result_str;
5150              }
5151          } else {
5152              $this->_jumpBlock(ceil(($v_header['size']/512)));
5153          }
5154        }
5155
5156        return null;
5157    }
5158    // }}}
5159
5160    // {{{ _extractList()
5161    function _extractList($p_path, &$p_list_detail, $p_mode,
5162                          $p_file_list, $p_remove_path, $p_preserve=false)
5163    {
5164    $v_result=true;
5165    $v_nb = 0;
5166    $v_extract_all = true;
5167    $v_listing = false;
5168
5169    $p_path = $this->_translateWinPath($p_path, false);
5170    if ($p_path == '' || (substr($p_path, 0, 1) != '/'
5171	    && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
5172      $p_path = "./".$p_path;
5173    }
5174    $p_remove_path = $this->_translateWinPath($p_remove_path);
5175
5176    // ----- Look for path to remove format (should end by /)
5177    if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
5178      $p_remove_path .= '/';
5179    $p_remove_path_size = strlen($p_remove_path);
5180
5181    switch ($p_mode) {
5182      case "complete" :
5183        $v_extract_all = true;
5184        $v_listing = false;
5185      break;
5186      case "partial" :
5187          $v_extract_all = false;
5188          $v_listing = false;
5189      break;
5190      case "list" :
5191          $v_extract_all = false;
5192          $v_listing = true;
5193      break;
5194      default :
5195        $this->_error('Invalid extract mode ('.$p_mode.')');
5196        return false;
5197    }
5198
5199    clearstatcache();
5200
5201    while (strlen($v_binary_data = $this->_readBlock()) != 0)
5202    {
5203      $v_extract_file = FALSE;
5204      $v_extraction_stopped = 0;
5205
5206      if (!$this->_readHeader($v_binary_data, $v_header))
5207        return false;
5208
5209      if ($v_header['filename'] == '') {
5210        continue;
5211      }
5212
5213      // ----- Look for long filename
5214      if ($v_header['typeflag'] == 'L') {
5215        if (!$this->_readLongHeader($v_header))
5216          return false;
5217      }
5218
5219      if ((!$v_extract_all) && (is_array($p_file_list))) {
5220        // ----- By default no unzip if the file is not found
5221        $v_extract_file = false;
5222
5223        for ($i=0; $i<sizeof($p_file_list); $i++) {
5224          // ----- Look if it is a directory
5225          if (substr($p_file_list[$i], -1) == '/') {
5226            // ----- Look if the directory is in the filename path
5227            if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
5228			    && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
5229				    == $p_file_list[$i])) {
5230              $v_extract_file = true;
5231              break;
5232            }
5233          }
5234
5235          // ----- It is a file, so compare the file names
5236          elseif ($p_file_list[$i] == $v_header['filename']) {
5237            $v_extract_file = true;
5238            break;
5239          }
5240        }
5241      } else {
5242        $v_extract_file = true;
5243      }
5244
5245      // ----- Look if this file need to be extracted
5246      if (($v_extract_file) && (!$v_listing))
5247      {
5248        if (($p_remove_path != '')
5249            && (substr($v_header['filename'].'/', 0, $p_remove_path_size)
5250			    == $p_remove_path)) {
5251          $v_header['filename'] = substr($v_header['filename'],
5252		                                 $p_remove_path_size);
5253          if( $v_header['filename'] == '' ){
5254            continue;
5255          }
5256        }
5257        if (($p_path != './') && ($p_path != '/')) {
5258          while (substr($p_path, -1) == '/')
5259            $p_path = substr($p_path, 0, strlen($p_path)-1);
5260
5261          if (substr($v_header['filename'], 0, 1) == '/')
5262              $v_header['filename'] = $p_path.$v_header['filename'];
5263          else
5264            $v_header['filename'] = $p_path.'/'.$v_header['filename'];
5265        }
5266        if (file_exists($v_header['filename'])) {
5267          if (   (@is_dir($v_header['filename']))
5268		      && ($v_header['typeflag'] == '')) {
5269            $this->_error('File '.$v_header['filename']
5270			              .' already exists as a directory');
5271            return false;
5272          }
5273          if (   ($this->_isArchive($v_header['filename']))
5274		      && ($v_header['typeflag'] == "5")) {
5275            $this->_error('Directory '.$v_header['filename']
5276			              .' already exists as a file');
5277            return false;
5278          }
5279          if (!is_writeable($v_header['filename'])) {
5280            $this->_error('File '.$v_header['filename']
5281			              .' already exists and is write protected');
5282            return false;
5283          }
5284          if (filemtime($v_header['filename']) > $v_header['mtime']) {
5285            // To be completed : An error or silent no replace ?
5286          }
5287        }
5288
5289        // ----- Check the directory availability and create it if necessary
5290        elseif (($v_result
5291		         = $this->_dirCheck(($v_header['typeflag'] == "5"
5292				                    ?$v_header['filename']
5293									:dirname($v_header['filename'])))) != 1) {
5294            $this->_error('Unable to create path for '.$v_header['filename']);
5295            return false;
5296        }
5297
5298        if ($v_extract_file) {
5299          if ($v_header['typeflag'] == "5") {
5300            if (!@file_exists($v_header['filename'])) {
5301                if (!@mkdir($v_header['filename'], 0777)) {
5302                    $this->_error('Unable to create directory {'
5303					              .$v_header['filename'].'}');
5304                    return false;
5305                }
5306            }
5307          } elseif ($v_header['typeflag'] == "2") {
5308              if (@file_exists($v_header['filename'])) {
5309                  @unlink($v_header['filename']);
5310              }
5311              if (!@symlink($v_header['link'], $v_header['filename'])) {
5312                  $this->_error('Unable to extract symbolic link {'
5313                                .$v_header['filename'].'}');
5314                  return false;
5315              }
5316          } else {
5317              if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
5318                  $this->_error('Error while opening {'.$v_header['filename']
5319				                .'} in write binary mode');
5320                  return false;
5321              } else {
5322                  $n = floor($v_header['size']/512);
5323                  for ($i=0; $i<$n; $i++) {
5324                      $v_content = $this->_readBlock();
5325                      fwrite($v_dest_file, $v_content, 512);
5326                  }
5327            if (($v_header['size'] % 512) != 0) {
5328              $v_content = $this->_readBlock();
5329              fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
5330            }
5331
5332            @fclose($v_dest_file);
5333            
5334            if ($p_preserve) {
5335                @chown($v_header['filename'], $v_header['uid']);
5336                @chgrp($v_header['filename'], $v_header['gid']);
5337            }
5338
5339            // ----- Change the file mode, mtime
5340            @touch($v_header['filename'], $v_header['mtime']);
5341            if ($v_header['mode'] & 0111) {
5342                // make file executable, obey umask
5343                $mode = fileperms($v_header['filename']) | (~umask() & 0111);
5344                @chmod($v_header['filename'], $mode);
5345            }
5346          }
5347
5348          // ----- Check the file size
5349          clearstatcache();
5350          if (!is_file($v_header['filename'])) {
5351              $this->_error('Extracted file '.$v_header['filename']
5352                            .'does not exist. Archive may be corrupted.');
5353              return false;
5354          }
5355          
5356          $filesize = filesize($v_header['filename']);
5357          if ($filesize != $v_header['size']) {
5358              $this->_error('Extracted file '.$v_header['filename']
5359                            .' does not have the correct file size \''
5360                            .$filesize
5361                            .'\' ('.$v_header['size']
5362                            .' expected). Archive may be corrupted.');
5363              return false;
5364          }
5365          }
5366        } else {
5367          $this->_jumpBlock(ceil(($v_header['size']/512)));
5368        }
5369      } else {
5370          $this->_jumpBlock(ceil(($v_header['size']/512)));
5371      }
5372
5373      /* TBC : Seems to be unused ...
5374      if ($this->_compress)
5375        $v_end_of_file = @gzeof($this->_file);
5376      else
5377        $v_end_of_file = @feof($this->_file);
5378        */
5379
5380      if ($v_listing || $v_extract_file || $v_extraction_stopped) {
5381        // ----- Log extracted files
5382        if (($v_file_dir = dirname($v_header['filename']))
5383		    == $v_header['filename'])
5384          $v_file_dir = '';
5385        if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
5386          $v_file_dir = '/';
5387
5388        $p_list_detail[$v_nb++] = $v_header;
5389        if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
5390            return true;
5391        }
5392      }
5393    }
5394
5395        return true;
5396    }
5397    // }}}
5398
5399    // {{{ _openAppend()
5400    function _openAppend()
5401    {
5402        if (filesize($this->_tarname) == 0)
5403          return $this->_openWrite();
5404
5405        if ($this->_compress) {
5406            $this->_close();
5407
5408            if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
5409                $this->_error('Error while renaming \''.$this->_tarname
5410				              .'\' to temporary file \''.$this->_tarname
5411							  .'.tmp\'');
5412                return false;
5413            }
5414
5415            if ($this->_compress_type == 'gz')
5416                $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
5417            elseif ($this->_compress_type == 'bz2')
5418                $v_temp_tar = @bzopen($this->_tarname.".tmp", "r");
5419
5420            if ($v_temp_tar == 0) {
5421                $this->_error('Unable to open file \''.$this->_tarname
5422				              .'.tmp\' in binary read mode');
5423                @rename($this->_tarname.".tmp", $this->_tarname);
5424                return false;
5425            }
5426
5427            if (!$this->_openWrite()) {
5428                @rename($this->_tarname.".tmp", $this->_tarname);
5429                return false;
5430            }
5431
5432            if ($this->_compress_type == 'gz') {
5433                $end_blocks = 0;
5434                
5435                while (!@gzeof($v_temp_tar)) {
5436                    $v_buffer = @gzread($v_temp_tar, 512);
5437                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
5438                        $end_blocks++;
5439                        // do not copy end blocks, we will re-make them
5440                        // after appending
5441                        continue;
5442                    } elseif ($end_blocks > 0) {
5443                        for ($i = 0; $i < $end_blocks; $i++) {
5444                            $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
5445                        }
5446                        $end_blocks = 0;
5447                    }
5448                    $v_binary_data = pack("a512", $v_buffer);
5449                    $this->_writeBlock($v_binary_data);
5450                }
5451
5452                @gzclose($v_temp_tar);
5453            }
5454            elseif ($this->_compress_type == 'bz2') {
5455                $end_blocks = 0;
5456                
5457                while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
5458                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
5459                        $end_blocks++;
5460                        // do not copy end blocks, we will re-make them
5461                        // after appending
5462                        continue;
5463                    } elseif ($end_blocks > 0) {
5464                        for ($i = 0; $i < $end_blocks; $i++) {
5465                            $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
5466                        }
5467                        $end_blocks = 0;
5468                    }
5469                    $v_binary_data = pack("a512", $v_buffer);
5470                    $this->_writeBlock($v_binary_data);
5471                }
5472
5473                @bzclose($v_temp_tar);
5474            }
5475
5476            if (!@unlink($this->_tarname.".tmp")) {
5477                $this->_error('Error while deleting temporary file \''
5478				              .$this->_tarname.'.tmp\'');
5479            }
5480
5481        } else {
5482            // ----- For not compressed tar, just add files before the last
5483			//       one or two 512 bytes block
5484            if (!$this->_openReadWrite())
5485               return false;
5486
5487            clearstatcache();
5488            $v_size = filesize($this->_tarname);
5489
5490            // We might have zero, one or two end blocks.
5491            // The standard is two, but we should try to handle
5492            // other cases.
5493            fseek($this->_file, $v_size - 1024);
5494            if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
5495                fseek($this->_file, $v_size - 1024);
5496            }
5497            elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
5498                fseek($this->_file, $v_size - 512);
5499            }
5500        }
5501
5502        return true;
5503    }
5504    // }}}
5505
5506    // {{{ _append()
5507    function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
5508    {
5509        if (!$this->_openAppend())
5510            return false;
5511
5512        if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
5513           $this->_writeFooter();
5514
5515        $this->_close();
5516
5517        return true;
5518    }
5519    // }}}
5520
5521    // {{{ _dirCheck()
5522
5523    /**
5524     * Check if a directory exists and create it (including parent
5525     * dirs) if not.
5526     *
5527     * @param string $p_dir directory to check
5528     *
5529     * @return bool true if the directory exists or was created
5530     */
5531    function _dirCheck($p_dir)
5532    {
5533        clearstatcache();
5534        if ((@is_dir($p_dir)) || ($p_dir == ''))
5535            return true;
5536
5537        $p_parent_dir = dirname($p_dir);
5538
5539        if (($p_parent_dir != $p_dir) &&
5540            ($p_parent_dir != '') &&
5541            (!$this->_dirCheck($p_parent_dir)))
5542             return false;
5543
5544        if (!@mkdir($p_dir, 0777)) {
5545            $this->_error("Unable to create directory '$p_dir'");
5546            return false;
5547        }
5548
5549        return true;
5550    }
5551
5552    // }}}
5553
5554    // {{{ _pathReduction()
5555
5556    /**
5557     * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
5558     * rand emove double slashes.
5559     *
5560     * @param string $p_dir path to reduce
5561     *
5562     * @return string reduced path
5563     *
5564     * @access private
5565     *
5566     */
5567    function _pathReduction($p_dir)
5568    {
5569        $v_result = '';
5570
5571        // ----- Look for not empty path
5572        if ($p_dir != '') {
5573            // ----- Explode path by directory names
5574            $v_list = explode('/', $p_dir);
5575
5576            // ----- Study directories from last to first
5577            for ($i=sizeof($v_list)-1; $i>=0; $i--) {
5578                // ----- Look for current path
5579                if ($v_list[$i] == ".") {
5580                    // ----- Ignore this directory
5581                    // Should be the first $i=0, but no check is done
5582                }
5583                else if ($v_list[$i] == "..") {
5584                    // ----- Ignore it and ignore the $i-1
5585                    $i--;
5586                }
5587                else if (   ($v_list[$i] == '')
5588				         && ($i!=(sizeof($v_list)-1))
5589						 && ($i!=0)) {
5590                    // ----- Ignore only the double '//' in path,
5591                    // but not the first and last /
5592                } else {
5593                    $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'
5594					            .$v_result:'');
5595                }
5596            }
5597        }
5598        
5599        if (defined('OS_WINDOWS') && OS_WINDOWS) {
5600            $v_result = strtr($v_result, '\\', '/');
5601        }
5602        
5603        return $v_result;
5604    }
5605
5606    // }}}
5607
5608    // {{{ _translateWinPath()
5609    function _translateWinPath($p_path, $p_remove_disk_letter=true)
5610    {
5611      if (defined('OS_WINDOWS') && OS_WINDOWS) {
5612          // ----- Look for potential disk letter
5613          if (   ($p_remove_disk_letter)
5614		      && (($v_position = strpos($p_path, ':')) != false)) {
5615              $p_path = substr($p_path, $v_position+1);
5616          }
5617          // ----- Change potential windows directory separator
5618          if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
5619              $p_path = strtr($p_path, '\\', '/');
5620          }
5621      }
5622      return $p_path;
5623    }
5624    // }}}
5625
5626}
5627?>
5628Archive_Tar-1.3.11/docs/Archive_Tar.txt0000644000175000017500000004367312105433221016652 0ustar  druiddruidDocumentation for class Archive_Tar
5629===================================
5630Last update : 2001-08-15
5631
5632
5633
5634Overview :
5635----------
5636
5637  The Archive_Tar class helps in creating and managing GNU TAR format
5638  files compressed by GNU ZIP or not. 
5639  The class offers basic functions like creating an archive, adding
5640  files in the archive, extracting files from the archive and listing
5641  the archive content. 
5642  It also provide advanced functions that allow the adding and
5643  extraction of files with path manipulation. 
5644
5645
5646Sample :
5647--------
5648
5649  // ----- Creating the object (uncompressed archive)
5650  $tar_object = new Archive_Tar("tarname.tar");
5651  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);
5652
5653  // ----- Creating the archive
5654  $v_list[0]="file.txt";
5655  $v_list[1]="data/";
5656  $v_list[2]="file.log";
5657  $tar_object->create($v_list);
5658
5659  // ----- Adding files
5660  $v_list[0]="dev/file.txt";
5661  $v_list[1]="dev/data/";
5662  $v_list[2]="log/file.log";
5663  $tar_object->add($v_list);
5664
5665  // ----- Adding more files
5666  $tar_object->add("release/newfile.log release/readme.txt");
5667
5668  // ----- Listing the content
5669  if (($v_list  =  $tar_object->listContent()) != 0)
5670    for ($i=0; $i<sizeof($v_list); $i++)
5671    {
5672      echo "Filename :'".$v_list[$i][filename]."'<br>";
5673      echo " .size :'".$v_list[$i][size]."'<br>";
5674      echo " .mtime :'".$v_list[$i][mtime]."' (".date("l dS of F Y h:i:s A", $v_list[$i][mtime]).")<br>";
5675      echo " .mode :'".$v_list[$i][mode]."'<br>";
5676      echo " .uid :'".$v_list[$i][uid]."'<br>";
5677      echo " .gid :'".$v_list[$i][gid]."'<br>";
5678      echo " .typeflag :'".$v_list[$i][typeflag]."'<br>";
5679    }
5680
5681  // ----- Extracting the archive in directory "install"
5682  $tar_object->extract("install");
5683
5684
5685Public arguments :
5686------------------
5687
5688None
5689
5690
5691Public Methods :
5692----------------
5693
5694Method : Archive_Tar($p_tarname, $compress = null)
5695Description :
5696  Archive_Tar Class constructor. This flavour of the constructor only
5697  declare a new Archive_Tar object, identifying it by the name of the
5698  tar file.
5699  If the compress argument is set the tar will be read or created as a
5700  gzip or bz2 compressed TAR file. 
5701Arguments :
5702  $p_tarname : A valid filename for the tar archive file.
5703  $p_compress : can be null, 'gz' or 'bz2'. For
5704                compatibility reason it can also be true. This
5705                parameter indicates if gzip or bz2 compression
5706                is required. 
5707Return value :
5708  The Archive_Tar object.
5709Sample :
5710  $tar_object = new Archive_Tar("tarname.tar");
5711  $tar_object_compressed = new Archive_Tar("tarname.tgz", true);
5712How it works :
5713  Initialize the object.
5714
5715Method : create($p_filelist)
5716Description :
5717  This method creates the archive file and add the files / directories
5718  that are listed in $p_filelist. 
5719  If the file already exists and is writable, it is replaced by the
5720  new tar. It is a create and not an add. If the file exists and is
5721  read-only or is a directory it is not replaced. The method return
5722  false and a PEAR error text. 
5723  The $p_filelist parameter can be an array of string, each string
5724  representing a filename or a directory name with their path if
5725  needed. It can also be a single string with names separated by a
5726  single blank. 
5727  See also createModify() method for more details.
5728Arguments :
5729  $p_filelist : An array of filenames and directory names, or a single
5730  string with names separated by a single blank space. 
5731Return value :
5732  true on success, false on error.
5733Sample 1 :
5734  $tar_object = new Archive_Tar("tarname.tar");
5735  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
5736  $v_list[0]="file.txt";
5737  $v_list[1]="data/"; (Optional '/' at the end)
5738  $v_list[2]="file.log";
5739  $tar_object->create($v_list);
5740Sample 2 :
5741  $tar_object = new Archive_Tar("tarname.tar");
5742  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
5743  $tar_object->create("file.txt data/ file.log");
5744How it works :
5745  Just calling the createModify() method with the right parameters.
5746
5747Method : createModify($p_filelist, $p_add_dir, $p_remove_dir = "")
5748Description :
5749  This method creates the archive file and add the files / directories
5750  that are listed in $p_filelist. 
5751  If the file already exists and is writable, it is replaced by the
5752  new tar. It is a create and not an add. If the file exists and is
5753  read-only or is a directory it is not replaced. The method return
5754  false and a PEAR error text. 
5755  The $p_filelist parameter can be an array of string, each string
5756  representing a filename or a directory name with their path if
5757  needed. It can also be a single string with names separated by a
5758  single blank. 
5759  The path indicated in $p_remove_dir will be removed from the
5760  memorized path of each file / directory listed when this path
5761  exists. By default nothing is removed (empty path "") 
5762  The path indicated in $p_add_dir will be added at the beginning of
5763  the memorized path of each file / directory listed. However it can
5764  be set to empty "". The adding of a path is done after the removing
5765  of path. 
5766  The path add/remove ability enables the user to prepare an archive
5767  for extraction in a different path than the origin files are. 
5768  See also addModify() method for file adding properties.
5769Arguments :
5770  $p_filelist : An array of filenames and directory names, or a single
5771                string with names separated by a single blank space.
5772  $p_add_dir : A string which contains a path to be added to the
5773               memorized path of each element in the list. 
5774  $p_remove_dir : A string which contains a path to be removed from
5775                  the memorized path of each element in the list, when
5776		  relevant.
5777Return value :
5778  true on success, false on error.
5779Sample 1 :
5780  $tar_object = new Archive_Tar("tarname.tar");
5781  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
5782  $v_list[0]="file.txt";
5783  $v_list[1]="data/"; (Optional '/' at the end)
5784  $v_list[2]="file.log";
5785  $tar_object->createModify($v_list, "install");
5786  // files are stored in the archive as :
5787  //   install/file.txt
5788  //   install/data
5789  //   install/data/file1.txt
5790  //   install/data/... all the files and sub-dirs of data/
5791  //   install/file.log
5792Sample 2 :
5793  $tar_object = new Archive_Tar("tarname.tar");
5794  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
5795  $v_list[0]="dev/file.txt";
5796  $v_list[1]="dev/data/"; (Optional '/' at the end)
5797  $v_list[2]="log/file.log";
5798  $tar_object->createModify($v_list, "install", "dev");
5799  // files are stored in the archive as :
5800  //   install/file.txt
5801  //   install/data
5802  //   install/data/file1.txt
5803  //   install/data/... all the files and sub-dirs of data/
5804  //   install/log/file.log
5805How it works :
5806  Open the file in write mode (erasing the existing one if one),
5807  call the _addList() method for adding the files in an empty archive,
5808  add the tar footer (512 bytes block), close the tar file.
5809
5810
5811Method : addModify($p_filelist, $p_add_dir, $p_remove_dir="")
5812Description :
5813  This method add the files / directories listed in $p_filelist at the
5814  end of the existing archive. If the archive does not yet exists it
5815  is created.
5816  The $p_filelist parameter can be an array of string, each string
5817  representing a filename or a directory name with their path if
5818  needed. It can also be a single string with names separated by a
5819  single blank. 
5820  The path indicated in $p_remove_dir will be removed from the
5821  memorized path of each file / directory listed when this path
5822  exists. By default nothing is removed (empty path "") 
5823  The path indicated in $p_add_dir will be added at the beginning of
5824  the memorized path of each file / directory listed. However it can
5825  be set to empty "". The adding of a path is done after the removing
5826  of path. 
5827  The path add/remove ability enables the user to prepare an archive
5828  for extraction in a different path than the origin files are. 
5829  If a file/dir is already in the archive it will only be added at the
5830  end of the archive. There is no update of the existing archived
5831  file/dir. However while extracting the archive, the last file will
5832  replace the first one. This results in a none optimization of the
5833  archive size. 
5834  If a file/dir does not exist the file/dir is ignored. However an
5835  error text is send to PEAR error. 
5836  If a file/dir is not readable the file/dir is ignored. However an
5837  error text is send to PEAR error. 
5838  If the resulting filename/dirname (after the add/remove option or
5839  not) string is greater than 99 char, the file/dir is
5840  ignored. However an error text is send to PEAR error. 
5841Arguments :
5842  $p_filelist : An array of filenames and directory names, or a single
5843                string with names separated by a single blank space. 
5844  $p_add_dir : A string which contains a path to be added to the
5845               memorized path of each element in the list. 
5846  $p_remove_dir : A string which contains a path to be removed from
5847                  the memorized path of each element in the list, when
5848		  relevant.
5849Return value :
5850  true on success, false on error.
5851Sample 1 :
5852  $tar_object = new Archive_Tar("tarname.tar");
5853  [...]
5854  $v_list[0]="dev/file.txt";
5855  $v_list[1]="dev/data/"; (Optional '/' at the end)
5856  $v_list[2]="log/file.log";
5857  $tar_object->addModify($v_list, "install");
5858  // files are stored in the archive as :
5859  //   install/file.txt
5860  //   install/data
5861  //   install/data/file1.txt
5862  //   install/data/... all the files and sub-dirs of data/
5863  //   install/file.log
5864Sample 2 :
5865  $tar_object = new Archive_Tar("tarname.tar");
5866  [...]
5867  $v_list[0]="dev/file.txt";
5868  $v_list[1]="dev/data/"; (Optional '/' at the end)
5869  $v_list[2]="log/file.log";
5870  $tar_object->addModify($v_list, "install", "dev");
5871  // files are stored in the archive as :
5872  //   install/file.txt
5873  //   install/data
5874  //   install/data/file1.txt
5875  //   install/data/... all the files and sub-dirs of data/
5876  //   install/log/file.log
5877How it works :
5878  If the archive does not exists it create it and add the files.
5879  If the archive does exists and is not compressed, it open it, jump
5880  before the last empty 512 bytes block (tar footer) and add the files
5881  at this point.
5882  If the archive does exists and is compressed, a temporary copy file
5883  is created. This temporary file is then 'gzip' read block by block
5884  until the last empty block. The new files are then added in the
5885  compressed file.
5886  The adding of files is done by going through the file/dir list,
5887  adding files per files, in a recursive way through the
5888  directory. Each time a path need to be added/removed it is done
5889  before writing the file header in the archive.
5890
5891Method : add($p_filelist)
5892Description :
5893  This method add the files / directories listed in $p_filelist at the
5894  end of the existing archive. If the archive does not yet exists it
5895  is created. 
5896  The $p_filelist parameter can be an array of string, each string
5897  representing a filename or a directory name with their path if
5898  needed. It can also be a single string with names separated by a
5899  single blank. 
5900  See addModify() method for details and limitations.
5901Arguments :
5902  $p_filelist : An array of filenames and directory names, or a single
5903  string with names separated by a single blank space. 
5904Return value :
5905  true on success, false on error.
5906Sample 1 :
5907  $tar_object = new Archive_Tar("tarname.tar");
5908  [...]
5909  $v_list[0]="dev/file.txt";
5910  $v_list[1]="dev/data/"; (Optional '/' at the end)
5911  $v_list[2]="log/file.log";
5912  $tar_object->add($v_list);
5913Sample 2 :
5914  $tar_object = new Archive_Tar("tarname.tgz", true);
5915  [...]
5916  $v_list[0]="dev/file.txt";
5917  $v_list[1]="dev/data/"; (Optional '/' at the end)
5918  $v_list[2]="log/file.log";
5919  $tar_object->add($v_list);
5920How it works :
5921  Simply call the addModify() method with the right parameters.
5922
5923Method : addString($p_filename, $p_string)
5924Description :
5925  This method add a single string as a file at the
5926  end of the existing archive. If the archive does not yet exists it
5927  is created.
5928Arguments :
5929  $p_filename : A string which contains the full filename path
5930                that will be associated with the string.
5931  $p_string :   The content of the file added in the archive.
5932Return value :
5933  true on success, false on error.
5934Sample 1 :
5935  $v_archive = & new Archive_Tar($p_filename);
5936  $v_archive->setErrorHandling(PEAR_ERROR_PRINT);
5937  $v_result = $v_archive->addString('data/test.txt', 'This is the text of the string');
5938
5939
5940Method : extract($p_path = "")
5941Description :
5942  This method extract all the content of the archive in the directory
5943  indicated by $p_path.If $p_path is optional, if not set the archive
5944  is extracted in the current directory. 
5945  While extracting a file, if the directory path does not exists it is
5946  created. 
5947  See extractModify() for details and limitations.
5948Arguments :
5949  $p_path : Optional path where the files/dir need to by extracted.
5950Return value :
5951  true on success, false on error.
5952Sample :
5953  $tar_object = new Archive_Tar("tarname.tar");
5954  $tar_object->extract();
5955How it works :
5956  Simply call the extractModify() method with appropriate parameters.
5957
5958Method : extractModify($p_path, $p_remove_path)
5959Description :
5960  This method extract all the content of the archive in the directory
5961  indicated by $p_path. When relevant the memorized path of the
5962  files/dir can be modified by removing the $p_remove_path path at the
5963  beginning of the file/dir path. 
5964  While extracting a file, if the directory path does not exists it is
5965  created. 
5966  While extracting a file, if the file already exists it is replaced
5967  without looking for last modification date. 
5968  While extracting a file, if the file already exists and is write
5969  protected, the extraction is aborted. 
5970  While extracting a file, if a directory with the same name already
5971  exists, the extraction is aborted. 
5972  While extracting a directory, if a file with the same name already
5973  exists, the extraction is aborted. 
5974  While extracting a file/directory if the destination directory exist
5975  and is write protected, or does not exist but can not be created,
5976  the extraction is aborted. 
5977  If after extraction an extracted file does not show the correct
5978  stored file size, the extraction is aborted. 
5979  When the extraction is aborted, a PEAR error text is set and false
5980  is returned. However the result can be a partial extraction that may
5981  need to be manually cleaned. 
5982Arguments :
5983  $p_path : The path of the directory where the files/dir need to by
5984            extracted. 
5985  $p_remove_path : Part of the memorized path that can be removed if
5986                   present at the beginning of the file/dir path. 
5987Return value :
5988  true on success, false on error.
5989Sample :
5990  // Imagine tarname.tar with files :
5991  //   dev/data/file.txt
5992  //   dev/data/log.txt
5993  //   readme.txt
5994  $tar_object = new Archive_Tar("tarname.tar");
5995  $tar_object->extractModify("install", "dev");
5996  // Files will be extracted there :
5997  //   install/data/file.txt
5998  //   install/data/log.txt
5999  //   install/readme.txt
6000How it works :
6001  Open the archive and call a more generic function that can extract
6002  only a part of the archive or all the archive. 
6003  See extractList() method for more details.
6004
6005Method : extractInString($p_filename)
6006Description :
6007  This method extract from the archive one file identified by $p_filename.
6008  The return value is a string with the file content, or NULL on error. 
6009Arguments :
6010  $p_filename : The path of the file to extract in a string. 
6011Return value :
6012  a string with the file content or NULL.
6013Sample :
6014  // Imagine tarname.tar with files :
6015  //   dev/data/file.txt
6016  //   dev/data/log.txt
6017  //   dev/readme.txt
6018  $v_archive = & new Archive_Tar('tarname.tar');
6019  $v_archive->setErrorHandling(PEAR_ERROR_PRINT);
6020  $v_string = $v_archive->extractInString('dev/readme.txt');
6021  echo $v_string;
6022
6023Method : listContent()
6024Description :
6025  This method returns an array of arrays that describe each
6026  file/directory present in the archive. 
6027  The array is not sorted, so it show the position of the file in the
6028  archive. 
6029  The file informations are :
6030    $file[filename] : Name and path of the file/dir.
6031    $file[mode] : File permissions (result of fileperms())
6032    $file[uid] : user id
6033    $file[gid] : group id
6034    $file[size] : filesize
6035    $file[mtime] : Last modification time (result of filemtime())
6036    $file[typeflag] : "" for file, "5" for directory
6037Arguments :
6038Return value :
6039  An array of arrays or 0 on error.
6040Sample :
6041  $tar_object = new Archive_Tar("tarname.tar");
6042  if (($v_list  =  $tar_object->listContent()) != 0)
6043    for ($i=0; $i<sizeof($v_list); $i++)
6044    {
6045      echo "Filename :'".$v_list[$i][filename]."'<br>";
6046      echo " .size :'".$v_list[$i][size]."'<br>";
6047      echo " .mtime :'".$v_list[$i][mtime]."' (".
6048           date("l dS of F Y h:i:s A", $v_list[$i][mtime]).")<br>";
6049      echo " .mode :'".$v_list[$i][mode]."'<br>";
6050      echo " .uid :'".$v_list[$i][uid]."'<br>";
6051      echo " .gid :'".$v_list[$i][gid]."'<br>";
6052      echo " .typeflag :'".$v_list[$i][typeflag]."'<br>";
6053    }
6054How it works :
6055  Call the same function as an extract however with a flag to only go
6056  through the archive without extracting the files. 
6057
6058Method : extractList($p_filelist, $p_path = "", $p_remove_path = "")
6059Description :
6060  This method extract from the archive only the files indicated in the
6061  $p_filelist. These files are extracted in the current directory or
6062  in the directory indicated by the optional $p_path parameter. 
6063  If indicated the $p_remove_path can be used in the same way as it is
6064  used in extractModify() method. 
6065Arguments :
6066  $p_filelist : An array of filenames and directory names, or a single
6067                string with names separated by a single blank space. 
6068  $p_path : The path of the directory where the files/dir need to by
6069            extracted. 
6070  $p_remove_path : Part of the memorized path that can be removed if
6071                   present at the beginning of the file/dir path. 
6072Return value :
6073  true on success, false on error.
6074Sample :
6075  // Imagine tarname.tar with files :
6076  //   dev/data/file.txt
6077  //   dev/data/log.txt
6078  //   readme.txt
6079  $tar_object = new Archive_Tar("tarname.tar");
6080  $tar_object->extractList("dev/data/file.txt readme.txt", "install",
6081                           "dev");
6082  // Files will be extracted there :
6083  //   install/data/file.txt
6084  //   install/readme.txt
6085How it works :
6086  Go through the archive and extract only the files present in the
6087  list. 
6088
6089<?php
6090/* vim: set expandtab tabstop=4 shiftwidth=4: */
6091/**
6092 * PHP Version 5
6093 *
6094 * Copyright (c) 1997-2004 The PHP Group
6095 *
6096 * This source file is subject to version 3.0 of the PHP license,
6097 * that is bundled with this package in the file LICENSE, and is
6098 * available through the world-wide-web at the following url:
6099 * http://www.php.net/license/3_0.txt.
6100 * If you did not receive a copy of the PHP license and are unable to
6101 * obtain it through the world-wide-web, please send a note to
6102 * license@php.net so we can mail you a copy immediately.
6103 *
6104 * @category Console
6105 * @package  Console_Getopt
6106 * @author   Andrei Zmievski <andrei@php.net>
6107 * @license  http://www.php.net/license/3_0.txt PHP 3.0
6108 * @version  CVS: $Id: Getopt.php 306067 2010-12-08 00:13:31Z dufuz $
6109 * @link     http://pear.php.net/package/Console_Getopt
6110 */
6111
6112require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
6113
6114/**
6115 * Command-line options parsing class.
6116 *
6117 * @category Console
6118 * @package  Console_Getopt
6119 * @author   Andrei Zmievski <andrei@php.net>
6120 * @license  http://www.php.net/license/3_0.txt PHP 3.0
6121 * @link     http://pear.php.net/package/Console_Getopt
6122 */
6123class Console_Getopt
6124{
6125
6126    /**
6127     * Parses the command-line options.
6128     *
6129     * The first parameter to this function should be the list of command-line
6130     * arguments without the leading reference to the running program.
6131     *
6132     * The second parameter is a string of allowed short options. Each of the
6133     * option letters can be followed by a colon ':' to specify that the option
6134     * requires an argument, or a double colon '::' to specify that the option
6135     * takes an optional argument.
6136     *
6137     * The third argument is an optional array of allowed long options. The
6138     * leading '--' should not be included in the option name. Options that
6139     * require an argument should be followed by '=', and options that take an
6140     * option argument should be followed by '=='.
6141     *
6142     * The return value is an array of two elements: the list of parsed
6143     * options and the list of non-option command-line arguments. Each entry in
6144     * the list of parsed options is a pair of elements - the first one
6145     * specifies the option, and the second one specifies the option argument,
6146     * if there was one.
6147     *
6148     * Long and short options can be mixed.
6149     *
6150     * Most of the semantics of this function are based on GNU getopt_long().
6151     *
6152     * @param array  $args          an array of command-line arguments
6153     * @param string $short_options specifies the list of allowed short options
6154     * @param array  $long_options  specifies the list of allowed long options
6155     * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
6156     *
6157     * @return array two-element array containing the list of parsed options and
6158     * the non-option arguments
6159     * @access public
6160     */
6161    function getopt2($args, $short_options, $long_options = null, $skip_unknown = false)
6162    {
6163        return Console_Getopt::doGetopt(2, $args, $short_options, $long_options, $skip_unknown);
6164    }
6165
6166    /**
6167     * This function expects $args to start with the script name (POSIX-style).
6168     * Preserved for backwards compatibility.
6169     *
6170     * @param array  $args          an array of command-line arguments
6171     * @param string $short_options specifies the list of allowed short options
6172     * @param array  $long_options  specifies the list of allowed long options
6173     *
6174     * @see getopt2()
6175     * @return array two-element array containing the list of parsed options and
6176     * the non-option arguments
6177     */
6178    function getopt($args, $short_options, $long_options = null, $skip_unknown = false)
6179    {
6180        return Console_Getopt::doGetopt(1, $args, $short_options, $long_options, $skip_unknown);
6181    }
6182
6183    /**
6184     * The actual implementation of the argument parsing code.
6185     *
6186     * @param int    $version       Version to use
6187     * @param array  $args          an array of command-line arguments
6188     * @param string $short_options specifies the list of allowed short options
6189     * @param array  $long_options  specifies the list of allowed long options
6190     * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
6191     *
6192     * @return array
6193     */
6194    function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false)
6195    {
6196        // in case you pass directly readPHPArgv() as the first arg
6197        if (PEAR::isError($args)) {
6198            return $args;
6199        }
6200
6201        if (empty($args)) {
6202            return array(array(), array());
6203        }
6204
6205        $non_opts = $opts = array();
6206
6207        settype($args, 'array');
6208
6209        if ($long_options) {
6210            sort($long_options);
6211        }
6212
6213        /*
6214         * Preserve backwards compatibility with callers that relied on
6215         * erroneous POSIX fix.
6216         */
6217        if ($version < 2) {
6218            if (isset($args[0]{0}) && $args[0]{0} != '-') {
6219                array_shift($args);
6220            }
6221        }
6222
6223        reset($args);
6224        while (list($i, $arg) = each($args)) {
6225            /* The special element '--' means explicit end of
6226               options. Treat the rest of the arguments as non-options
6227               and end the loop. */
6228            if ($arg == '--') {
6229                $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
6230                break;
6231            }
6232
6233            if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
6234                $non_opts = array_merge($non_opts, array_slice($args, $i));
6235                break;
6236            } elseif (strlen($arg) > 1 && $arg{1} == '-') {
6237                $error = Console_Getopt::_parseLongOption(substr($arg, 2),
6238                                                          $long_options,
6239                                                          $opts,
6240                                                          $args,
6241                                                          $skip_unknown);
6242                if (PEAR::isError($error)) {
6243                    return $error;
6244                }
6245            } elseif ($arg == '-') {
6246                // - is stdin
6247                $non_opts = array_merge($non_opts, array_slice($args, $i));
6248                break;
6249            } else {
6250                $error = Console_Getopt::_parseShortOption(substr($arg, 1),
6251                                                           $short_options,
6252                                                           $opts,
6253                                                           $args,
6254                                                           $skip_unknown);
6255                if (PEAR::isError($error)) {
6256                    return $error;
6257                }
6258            }
6259        }
6260
6261        return array($opts, $non_opts);
6262    }
6263
6264    /**
6265     * Parse short option
6266     *
6267     * @param string     $arg           Argument
6268     * @param string[]   $short_options Available short options
6269     * @param string[][] &$opts
6270     * @param string[]   &$args
6271     * @param boolean    $skip_unknown suppresses Console_Getopt: unrecognized option
6272     *
6273     * @access private
6274     * @return void
6275     */
6276    function _parseShortOption($arg, $short_options, &$opts, &$args, $skip_unknown)
6277    {
6278        for ($i = 0; $i < strlen($arg); $i++) {
6279            $opt     = $arg{$i};
6280            $opt_arg = null;
6281
6282            /* Try to find the short option in the specifier string. */
6283            if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':') {
6284                if ($skip_unknown === true) {
6285                    break;
6286                }
6287
6288                $msg = "Console_Getopt: unrecognized option -- $opt";
6289                return PEAR::raiseError($msg);
6290            }
6291
6292            if (strlen($spec) > 1 && $spec{1} == ':') {
6293                if (strlen($spec) > 2 && $spec{2} == ':') {
6294                    if ($i + 1 < strlen($arg)) {
6295                        /* Option takes an optional argument. Use the remainder of
6296                           the arg string if there is anything left. */
6297                        $opts[] = array($opt, substr($arg, $i + 1));
6298                        break;
6299                    }
6300                } else {
6301                    /* Option requires an argument. Use the remainder of the arg
6302                       string if there is anything left. */
6303                    if ($i + 1 < strlen($arg)) {
6304                        $opts[] = array($opt,  substr($arg, $i + 1));
6305                        break;
6306                    } else if (list(, $opt_arg) = each($args)) {
6307                        /* Else use the next argument. */;
6308                        if (Console_Getopt::_isShortOpt($opt_arg)
6309                            || Console_Getopt::_isLongOpt($opt_arg)) {
6310                            $msg = "option requires an argument --$opt";
6311                            return PEAR::raiseError("Console_Getopt:" . $msg);
6312                        }
6313                    } else {
6314                        $msg = "option requires an argument --$opt";
6315                        return PEAR::raiseError("Console_Getopt:" . $msg);
6316                    }
6317                }
6318            }
6319
6320            $opts[] = array($opt, $opt_arg);
6321        }
6322    }
6323
6324    /**
6325     * Checks if an argument is a short option
6326     *
6327     * @param string $arg Argument to check
6328     *
6329     * @access private
6330     * @return bool
6331     */
6332    function _isShortOpt($arg)
6333    {
6334        return strlen($arg) == 2 && $arg[0] == '-'
6335               && preg_match('/[a-zA-Z]/', $arg[1]);
6336    }
6337
6338    /**
6339     * Checks if an argument is a long option
6340     *
6341     * @param string $arg Argument to check
6342     *
6343     * @access private
6344     * @return bool
6345     */
6346    function _isLongOpt($arg)
6347    {
6348        return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
6349               preg_match('/[a-zA-Z]+$/', substr($arg, 2));
6350    }
6351
6352    /**
6353     * Parse long option
6354     *
6355     * @param string     $arg          Argument
6356     * @param string[]   $long_options Available long options
6357     * @param string[][] &$opts
6358     * @param string[]   &$args
6359     *
6360     * @access private
6361     * @return void|PEAR_Error
6362     */
6363    function _parseLongOption($arg, $long_options, &$opts, &$args, $skip_unknown)
6364    {
6365        @list($opt, $opt_arg) = explode('=', $arg, 2);
6366
6367        $opt_len = strlen($opt);
6368
6369        for ($i = 0; $i < count($long_options); $i++) {
6370            $long_opt  = $long_options[$i];
6371            $opt_start = substr($long_opt, 0, $opt_len);
6372
6373            $long_opt_name = str_replace('=', '', $long_opt);
6374
6375            /* Option doesn't match. Go on to the next one. */
6376            if ($long_opt_name != $opt) {
6377                continue;
6378            }
6379
6380            $opt_rest = substr($long_opt, $opt_len);
6381
6382            /* Check that the options uniquely matches one of the allowed
6383               options. */
6384            if ($i + 1 < count($long_options)) {
6385                $next_option_rest = substr($long_options[$i + 1], $opt_len);
6386            } else {
6387                $next_option_rest = '';
6388            }
6389
6390            if ($opt_rest != '' && $opt{0} != '=' &&
6391                $i + 1 < count($long_options) &&
6392                $opt == substr($long_options[$i+1], 0, $opt_len) &&
6393                $next_option_rest != '' &&
6394                $next_option_rest{0} != '=') {
6395
6396                $msg = "Console_Getopt: option --$opt is ambiguous";
6397                return PEAR::raiseError($msg);
6398            }
6399
6400            if (substr($long_opt, -1) == '=') {
6401                if (substr($long_opt, -2) != '==') {
6402                    /* Long option requires an argument.
6403                       Take the next argument if one wasn't specified. */;
6404                    if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
6405                        $msg = "Console_Getopt: option requires an argument --$opt";
6406                        return PEAR::raiseError($msg);
6407                    }
6408
6409                    if (Console_Getopt::_isShortOpt($opt_arg)
6410                        || Console_Getopt::_isLongOpt($opt_arg)) {
6411                        $msg = "Console_Getopt: option requires an argument --$opt";
6412                        return PEAR::raiseError($msg);
6413                    }
6414                }
6415            } else if ($opt_arg) {
6416                $msg = "Console_Getopt: option --$opt doesn't allow an argument";
6417                return PEAR::raiseError($msg);
6418            }
6419
6420            $opts[] = array('--' . $opt, $opt_arg);
6421            return;
6422        }
6423
6424        if ($skip_unknown === true) {
6425            return;
6426        }
6427
6428        return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
6429    }
6430
6431    /**
6432     * Safely read the $argv PHP array across different PHP configurations.
6433     * Will take care on register_globals and register_argc_argv ini directives
6434     *
6435     * @access public
6436     * @return mixed the $argv PHP array or PEAR error if not registered
6437     */
6438    function readPHPArgv()
6439    {
6440        global $argv;
6441        if (!is_array($argv)) {
6442            if (!@is_array($_SERVER['argv'])) {
6443                if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
6444                    $msg = "Could not read cmd args (register_argc_argv=Off?)";
6445                    return PEAR::raiseError("Console_Getopt: " . $msg);
6446                }
6447                return $GLOBALS['HTTP_SERVER_VARS']['argv'];
6448            }
6449            return $_SERVER['argv'];
6450        }
6451        return $argv;
6452    }
6453
6454}package.xml0000644000076500000240000001212011535261415012145 0ustar  helgistaff<?xml version="1.0" encoding="UTF-8"?>
6455<package packagerversion="1.9.2" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
6456 <name>Console_Getopt</name>
6457 <channel>pear.php.net</channel>
6458 <summary>Command-line option parser</summary>
6459 <description>This is a PHP implementation of &quot;getopt&quot; supporting both
6460short and long options.</description>
6461 <lead>
6462  <name>Andrei Zmievski</name>
6463  <user>andrei</user>
6464  <email>andrei@php.net</email>
6465  <active>yes</active>
6466 </lead>
6467 <developer>
6468  <name>Stig Bakken</name>
6469  <user>ssb</user>
6470  <email>stig@php.net</email>
6471  <active>no</active>
6472 </developer>
6473 <helper>
6474  <name>Greg Beaver</name>
6475  <user>cellog</user>
6476  <email>cellog@php.net</email>
6477  <active>yes</active>
6478 </helper>
6479 <date>2011-03-07</date>
6480 <time>22:58:21</time>
6481 <version>
6482  <release>1.3.1</release>
6483  <api>1.3.0</api>
6484 </version>
6485 <stability>
6486  <release>stable</release>
6487  <api>stable</api>
6488 </stability>
6489 <license uri="http://www.php.net/license">PHP License</license>
6490 <notes>
6491* Change the minimum PEAR installer dep to be lower
6492 </notes>
6493 <contents>
6494  <dir name="/">
6495   <file md5sum="ed666da6b1c5d01c3ecbf1f588a70a60" name="Console/Getopt.php" role="php" />
6496  </dir>
6497 </contents>
6498 <compatible>
6499  <name>PEAR</name>
6500  <channel>pear.php.net</channel>
6501  <min>1.4.0</min>
6502  <max>1.10.0</max>
6503 </compatible>
6504 <dependencies>
6505  <required>
6506   <php>
6507    <min>4.3.0</min>
6508   </php>
6509   <pearinstaller>
6510    <min>1.8.0</min>
6511   </pearinstaller>
6512  </required>
6513 </dependencies>
6514 <phprelease />
6515 <changelog>
6516  <release>
6517   <date>2010-12-11</date>
6518   <time>20:20:13</time>
6519   <version>
6520    <release>1.3.0</release>
6521    <api>1.3.0</api>
6522   </version>
6523   <stability>
6524    <release>stable</release>
6525    <api>stable</api>
6526   </stability>
6527   <license uri="http://www.php.net/license">PHP License</license>
6528   <notes>
6529* Implement Request #13140: [PATCH] to skip unknown parameters. [patch by rquadling, improved on by dufuz]
6530   </notes>
6531  </release>
6532  <release>
6533   <date>2007-06-12</date>
6534   <version>
6535    <release>1.2.3</release>
6536    <api>1.2.1</api>
6537   </version>
6538   <stability>
6539    <release>stable</release>
6540    <api>stable</api>
6541   </stability>
6542   <license uri="http://www.php.net/license">PHP License</license>
6543   <notes>
6544* fix Bug #11068: No way to read plain &quot;-&quot; option [cardoe]
6545   </notes>
6546  </release>
6547  <release>
6548   <version>
6549    <release>1.2.2</release>
6550    <api>1.2.1</api>
6551   </version>
6552   <stability>
6553    <release>stable</release>
6554    <api>stable</api>
6555   </stability>
6556   <date>2007-02-17</date>
6557   <license uri="http://www.php.net/license">PHP License</license>
6558   <notes>
6559* fix Bug #4475: An ambiguous error occurred when specifying similar longoption name.
6560* fix Bug #10055: Not failing properly on short options missing required values
6561   </notes>
6562  </release>
6563  <release>
6564   <version>
6565    <release>1.2.1</release>
6566    <api>1.2.1</api>
6567   </version>
6568   <stability>
6569    <release>stable</release>
6570    <api>stable</api>
6571   </stability>
6572   <date>2006-12-08</date>
6573   <license uri="http://www.php.net/license">PHP License</license>
6574   <notes>
6575Fixed bugs #4448 (Long parameter values truncated with longoption parameter) and #7444 (Trailing spaces after php closing tag)
6576   </notes>
6577  </release>
6578  <release>
6579   <version>
6580    <release>1.2</release>
6581    <api>1.2</api>
6582   </version>
6583   <stability>
6584    <release>stable</release>
6585    <api>stable</api>
6586   </stability>
6587   <date>2003-12-11</date>
6588   <license uri="http://www.php.net/license">PHP License</license>
6589   <notes>
6590Fix to preserve BC with 1.0 and allow correct behaviour for new users
6591   </notes>
6592  </release>
6593  <release>
6594   <version>
6595    <release>1.0</release>
6596    <api>1.0</api>
6597   </version>
6598   <stability>
6599    <release>stable</release>
6600    <api>stable</api>
6601   </stability>
6602   <date>2002-09-13</date>
6603   <license uri="http://www.php.net/license">PHP License</license>
6604   <notes>
6605Stable release
6606   </notes>
6607  </release>
6608  <release>
6609   <version>
6610    <release>0.11</release>
6611    <api>0.11</api>
6612   </version>
6613   <stability>
6614    <release>beta</release>
6615    <api>beta</api>
6616   </stability>
6617   <date>2002-05-26</date>
6618   <license uri="http://www.php.net/license">PHP License</license>
6619   <notes>
6620POSIX getopt compatibility fix: treat first element of args
6621        array as command name
6622   </notes>
6623  </release>
6624  <release>
6625   <version>
6626    <release>0.10</release>
6627    <api>0.10</api>
6628   </version>
6629   <stability>
6630    <release>beta</release>
6631    <api>beta</api>
6632   </stability>
6633   <date>2002-05-12</date>
6634   <license uri="http://www.php.net/license">PHP License</license>
6635   <notes>
6636Packaging fix
6637   </notes>
6638  </release>
6639  <release>
6640   <version>
6641    <release>0.9</release>
6642    <api>0.9</api>
6643   </version>
6644   <stability>
6645    <release>beta</release>
6646    <api>beta</api>
6647   </stability>
6648   <date>2002-05-12</date>
6649   <license uri="http://www.php.net/license">PHP License</license>
6650   <notes>
6651Initial release
6652   </notes>
6653  </release>
6654 </changelog>
6655</package>
6656Console_Getopt-1.3.1/Console/Getopt.php0000644000076500000240000003223511535261415017000 0ustar  helgistaff<?php
6657/* vim: set expandtab tabstop=4 shiftwidth=4: */
6658/**
6659 * PHP Version 5
6660 *
6661 * Copyright (c) 1997-2004 The PHP Group
6662 *
6663 * This source file is subject to version 3.0 of the PHP license,
6664 * that is bundled with this package in the file LICENSE, and is
6665 * available through the world-wide-web at the following url:
6666 * http://www.php.net/license/3_0.txt.
6667 * If you did not receive a copy of the PHP license and are unable to
6668 * obtain it through the world-wide-web, please send a note to
6669 * license@php.net so we can mail you a copy immediately.
6670 *
6671 * @category Console
6672 * @package  Console_Getopt
6673 * @author   Andrei Zmievski <andrei@php.net>
6674 * @license  http://www.php.net/license/3_0.txt PHP 3.0
6675 * @version  CVS: $Id: Getopt.php 306067 2010-12-08 00:13:31Z dufuz $
6676 * @link     http://pear.php.net/package/Console_Getopt
6677 */
6678
6679require_once 'PEAR.php';
6680
6681/**
6682 * Command-line options parsing class.
6683 *
6684 * @category Console
6685 * @package  Console_Getopt
6686 * @author   Andrei Zmievski <andrei@php.net>
6687 * @license  http://www.php.net/license/3_0.txt PHP 3.0
6688 * @link     http://pear.php.net/package/Console_Getopt
6689 */
6690class Console_Getopt
6691{
6692
6693    /**
6694     * Parses the command-line options.
6695     *
6696     * The first parameter to this function should be the list of command-line
6697     * arguments without the leading reference to the running program.
6698     *
6699     * The second parameter is a string of allowed short options. Each of the
6700     * option letters can be followed by a colon ':' to specify that the option
6701     * requires an argument, or a double colon '::' to specify that the option
6702     * takes an optional argument.
6703     *
6704     * The third argument is an optional array of allowed long options. The
6705     * leading '--' should not be included in the option name. Options that
6706     * require an argument should be followed by '=', and options that take an
6707     * option argument should be followed by '=='.
6708     *
6709     * The return value is an array of two elements: the list of parsed
6710     * options and the list of non-option command-line arguments. Each entry in
6711     * the list of parsed options is a pair of elements - the first one
6712     * specifies the option, and the second one specifies the option argument,
6713     * if there was one.
6714     *
6715     * Long and short options can be mixed.
6716     *
6717     * Most of the semantics of this function are based on GNU getopt_long().
6718     *
6719     * @param array  $args          an array of command-line arguments
6720     * @param string $short_options specifies the list of allowed short options
6721     * @param array  $long_options  specifies the list of allowed long options
6722     * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
6723     *
6724     * @return array two-element array containing the list of parsed options and
6725     * the non-option arguments
6726     * @access public
6727     */
6728    function getopt2($args, $short_options, $long_options = null, $skip_unknown = false)
6729    {
6730        return Console_Getopt::doGetopt(2, $args, $short_options, $long_options, $skip_unknown);
6731    }
6732
6733    /**
6734     * This function expects $args to start with the script name (POSIX-style).
6735     * Preserved for backwards compatibility.
6736     *
6737     * @param array  $args          an array of command-line arguments
6738     * @param string $short_options specifies the list of allowed short options
6739     * @param array  $long_options  specifies the list of allowed long options
6740     *
6741     * @see getopt2()
6742     * @return array two-element array containing the list of parsed options and
6743     * the non-option arguments
6744     */
6745    function getopt($args, $short_options, $long_options = null, $skip_unknown = false)
6746    {
6747        return Console_Getopt::doGetopt(1, $args, $short_options, $long_options, $skip_unknown);
6748    }
6749
6750    /**
6751     * The actual implementation of the argument parsing code.
6752     *
6753     * @param int    $version       Version to use
6754     * @param array  $args          an array of command-line arguments
6755     * @param string $short_options specifies the list of allowed short options
6756     * @param array  $long_options  specifies the list of allowed long options
6757     * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
6758     *
6759     * @return array
6760     */
6761    function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false)
6762    {
6763        // in case you pass directly readPHPArgv() as the first arg
6764        if (PEAR::isError($args)) {
6765            return $args;
6766        }
6767
6768        if (empty($args)) {
6769            return array(array(), array());
6770        }
6771
6772        $non_opts = $opts = array();
6773
6774        settype($args, 'array');
6775
6776        if ($long_options) {
6777            sort($long_options);
6778        }
6779
6780        /*
6781         * Preserve backwards compatibility with callers that relied on
6782         * erroneous POSIX fix.
6783         */
6784        if ($version < 2) {
6785            if (isset($args[0]{0}) && $args[0]{0} != '-') {
6786                array_shift($args);
6787            }
6788        }
6789
6790        reset($args);
6791        while (list($i, $arg) = each($args)) {
6792            /* The special element '--' means explicit end of
6793               options. Treat the rest of the arguments as non-options
6794               and end the loop. */
6795            if ($arg == '--') {
6796                $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
6797                break;
6798            }
6799
6800            if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
6801                $non_opts = array_merge($non_opts, array_slice($args, $i));
6802                break;
6803            } elseif (strlen($arg) > 1 && $arg{1} == '-') {
6804                $error = Console_Getopt::_parseLongOption(substr($arg, 2),
6805                                                          $long_options,
6806                                                          $opts,
6807                                                          $args,
6808                                                          $skip_unknown);
6809                if (PEAR::isError($error)) {
6810                    return $error;
6811                }
6812            } elseif ($arg == '-') {
6813                // - is stdin
6814                $non_opts = array_merge($non_opts, array_slice($args, $i));
6815                break;
6816            } else {
6817                $error = Console_Getopt::_parseShortOption(substr($arg, 1),
6818                                                           $short_options,
6819                                                           $opts,
6820                                                           $args,
6821                                                           $skip_unknown);
6822                if (PEAR::isError($error)) {
6823                    return $error;
6824                }
6825            }
6826        }
6827
6828        return array($opts, $non_opts);
6829    }
6830
6831    /**
6832     * Parse short option
6833     *
6834     * @param string     $arg           Argument
6835     * @param string[]   $short_options Available short options
6836     * @param string[][] &$opts
6837     * @param string[]   &$args
6838     * @param boolean    $skip_unknown suppresses Console_Getopt: unrecognized option
6839     *
6840     * @access private
6841     * @return void
6842     */
6843    function _parseShortOption($arg, $short_options, &$opts, &$args, $skip_unknown)
6844    {
6845        for ($i = 0; $i < strlen($arg); $i++) {
6846            $opt     = $arg{$i};
6847            $opt_arg = null;
6848
6849            /* Try to find the short option in the specifier string. */
6850            if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':') {
6851                if ($skip_unknown === true) {
6852                    break;
6853                }
6854
6855                $msg = "Console_Getopt: unrecognized option -- $opt";
6856                return PEAR::raiseError($msg);
6857            }
6858
6859            if (strlen($spec) > 1 && $spec{1} == ':') {
6860                if (strlen($spec) > 2 && $spec{2} == ':') {
6861                    if ($i + 1 < strlen($arg)) {
6862                        /* Option takes an optional argument. Use the remainder of
6863                           the arg string if there is anything left. */
6864                        $opts[] = array($opt, substr($arg, $i + 1));
6865                        break;
6866                    }
6867                } else {
6868                    /* Option requires an argument. Use the remainder of the arg
6869                       string if there is anything left. */
6870                    if ($i + 1 < strlen($arg)) {
6871                        $opts[] = array($opt,  substr($arg, $i + 1));
6872                        break;
6873                    } else if (list(, $opt_arg) = each($args)) {
6874                        /* Else use the next argument. */;
6875                        if (Console_Getopt::_isShortOpt($opt_arg)
6876                            || Console_Getopt::_isLongOpt($opt_arg)) {
6877                            $msg = "option requires an argument --$opt";
6878                            return PEAR::raiseError("Console_Getopt:" . $msg);
6879                        }
6880                    } else {
6881                        $msg = "option requires an argument --$opt";
6882                        return PEAR::raiseError("Console_Getopt:" . $msg);
6883                    }
6884                }
6885            }
6886
6887            $opts[] = array($opt, $opt_arg);
6888        }
6889    }
6890
6891    /**
6892     * Checks if an argument is a short option
6893     *
6894     * @param string $arg Argument to check
6895     *
6896     * @access private
6897     * @return bool
6898     */
6899    function _isShortOpt($arg)
6900    {
6901        return strlen($arg) == 2 && $arg[0] == '-'
6902               && preg_match('/[a-zA-Z]/', $arg[1]);
6903    }
6904
6905    /**
6906     * Checks if an argument is a long option
6907     *
6908     * @param string $arg Argument to check
6909     *
6910     * @access private
6911     * @return bool
6912     */
6913    function _isLongOpt($arg)
6914    {
6915        return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
6916               preg_match('/[a-zA-Z]+$/', substr($arg, 2));
6917    }
6918
6919    /**
6920     * Parse long option
6921     *
6922     * @param string     $arg          Argument
6923     * @param string[]   $long_options Available long options
6924     * @param string[][] &$opts
6925     * @param string[]   &$args
6926     *
6927     * @access private
6928     * @return void|PEAR_Error
6929     */
6930    function _parseLongOption($arg, $long_options, &$opts, &$args, $skip_unknown)
6931    {
6932        @list($opt, $opt_arg) = explode('=', $arg, 2);
6933
6934        $opt_len = strlen($opt);
6935
6936        for ($i = 0; $i < count($long_options); $i++) {
6937            $long_opt  = $long_options[$i];
6938            $opt_start = substr($long_opt, 0, $opt_len);
6939
6940            $long_opt_name = str_replace('=', '', $long_opt);
6941
6942            /* Option doesn't match. Go on to the next one. */
6943            if ($long_opt_name != $opt) {
6944                continue;
6945            }
6946
6947            $opt_rest = substr($long_opt, $opt_len);
6948
6949            /* Check that the options uniquely matches one of the allowed
6950               options. */
6951            if ($i + 1 < count($long_options)) {
6952                $next_option_rest = substr($long_options[$i + 1], $opt_len);
6953            } else {
6954                $next_option_rest = '';
6955            }
6956
6957            if ($opt_rest != '' && $opt{0} != '=' &&
6958                $i + 1 < count($long_options) &&
6959                $opt == substr($long_options[$i+1], 0, $opt_len) &&
6960                $next_option_rest != '' &&
6961                $next_option_rest{0} != '=') {
6962
6963                $msg = "Console_Getopt: option --$opt is ambiguous";
6964                return PEAR::raiseError($msg);
6965            }
6966
6967            if (substr($long_opt, -1) == '=') {
6968                if (substr($long_opt, -2) != '==') {
6969                    /* Long option requires an argument.
6970                       Take the next argument if one wasn't specified. */;
6971                    if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
6972                        $msg = "Console_Getopt: option requires an argument --$opt";
6973                        return PEAR::raiseError($msg);
6974                    }
6975
6976                    if (Console_Getopt::_isShortOpt($opt_arg)
6977                        || Console_Getopt::_isLongOpt($opt_arg)) {
6978                        $msg = "Console_Getopt: option requires an argument --$opt";
6979                        return PEAR::raiseError($msg);
6980                    }
6981                }
6982            } else if ($opt_arg) {
6983                $msg = "Console_Getopt: option --$opt doesn't allow an argument";
6984                return PEAR::raiseError($msg);
6985            }
6986
6987            $opts[] = array('--' . $opt, $opt_arg);
6988            return;
6989        }
6990
6991        if ($skip_unknown === true) {
6992            return;
6993        }
6994
6995        return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
6996    }
6997
6998    /**
6999     * Safely read the $argv PHP array across different PHP configurations.
7000     * Will take care on register_globals and register_argc_argv ini directives
7001     *
7002     * @access public
7003     * @return mixed the $argv PHP array or PEAR error if not registered
7004     */
7005    function readPHPArgv()
7006    {
7007        global $argv;
7008        if (!is_array($argv)) {
7009            if (!@is_array($_SERVER['argv'])) {
7010                if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
7011                    $msg = "Could not read cmd args (register_argc_argv=Off?)";
7012                    return PEAR::raiseError("Console_Getopt: " . $msg);
7013                }
7014                return $GLOBALS['HTTP_SERVER_VARS']['argv'];
7015            }
7016            return $_SERVER['argv'];
7017        }
7018        return $argv;
7019    }
7020
7021}<?php
7022while (@ob_end_flush());
7023/* $Id$ */
7024
7025error_reporting(1803);
7026
7027if (ini_get('date.timezone') === '' && function_exists('date_default_timezone_set')) {
7028    date_default_timezone_set('UTC');
7029}
7030
7031$pear_dir = dirname(__FILE__);
7032ini_set('include_path', '');
7033if (function_exists('mb_internal_encoding')) {
7034    mb_internal_encoding('ASCII');
7035}
7036set_time_limit(0);
7037include_once 'phar://install-pear-nozlib.phar/PEAR.php';
7038include_once 'phar://install-pear-nozlib.phar/PEAR/Installer.php';
7039include_once 'phar://install-pear-nozlib.phar/PEAR/Registry.php';
7040include_once 'phar://install-pear-nozlib.phar/PEAR/PackageFile.php';
7041include_once 'phar://install-pear-nozlib.phar/PEAR/Downloader/Package.php';
7042include_once 'phar://install-pear-nozlib.phar/PEAR/Frontend.php';
7043$a = true;
7044if (!PEAR::loadExtension('xml')) {
7045    $a = false;
7046    echo "[PEAR] xml extension is required\n";
7047}
7048if (!PEAR::loadExtension('pcre')) {
7049    $a = false;
7050    echo "[PEAR] pcre extension is required\n";
7051}
7052if (!$a) {
7053    return -1;
7054}
7055
7056$force = false;
7057$install_files = array('Archive_Tar' => 'phar://install-pear-nozlib.phar/Archive_Tar-1.3.11.tar',
7058'Console_Getopt' => 'phar://install-pear-nozlib.phar/Console_Getopt-1.3.1.tar',
7059'PEAR' => 'phar://install-pear-nozlib.phar/PEAR-1.9.4.tar',
7060'Structures_Graph' => 'phar://install-pear-nozlib.phar/Structures_Graph-1.0.4.tar',
7061'XML_Util' => 'phar://install-pear-nozlib.phar/XML_Util-1.2.1.tar',
7062);
7063array_shift($argv);
7064$debug = false;
7065for ($i = 0; $i < sizeof($argv); $i++) {
7066    $arg = $argv[$i];
7067    $bn = basename($arg);
7068    if (preg_match('/package-(.*)\.xml$/', $bn, $matches) ||
7069        preg_match('/([A-Za-z0-9_:]+)-.*\.(tar|tgz)$/', $bn, $matches)) {
7070        $install_files[$matches[1]] = $arg;
7071    } elseif ($arg == '-a' || $arg == '--cache') {
7072        $cache_dir = $argv[$i+1];
7073        $i++;
7074    } elseif ($arg == '--force') {
7075        $force = true;
7076    } elseif ($arg == '-dp') {
7077        $prefix = $argv[$i+1];
7078        $i++;
7079    } elseif ($arg == '-ds') {
7080        $suffix = $argv[$i+1];
7081        $i++;
7082    } elseif ($arg == '-d' || $arg == '--dir') {
7083        $with_dir = $argv[$i+1];
7084        $i++;
7085    } elseif ($arg == '-b' || $arg == '--bin') {
7086        $bin_dir = $argv[$i+1];
7087        $i++;
7088    } elseif ($arg == '-c' || $arg == '--config') {
7089        $cfg_dir = $argv[$i+1];
7090        $i++;
7091    } elseif ($arg == '-w' || $arg == '--www') {
7092        $www_dir = $argv[$i+1];
7093        $i++;
7094    } elseif ($arg == '-p' || $arg == '--php') {
7095        $php_bin = $argv[$i+1];
7096        $i++;
7097    } elseif ($arg == '-o' || $arg == '--download') {
7098        $download_dir = $argv[$i+1];
7099        $i++;
7100    } elseif ($arg == '-m' || $arg == '--metadata') {
7101        $metadata_dir = $argv[$i+1];
7102        $i++;
7103    } elseif ($arg == '-t' || $arg == '--temp') {
7104        $temp_dir = $argv[$i+1];
7105        $i++;
7106    } elseif ($arg == '-A' || $arg == '--data') {
7107        $data_dir = $argv[$i+1];
7108        $i++;
7109    } elseif ($arg == '-D' || $arg == '--doc') {
7110        $doc_dir = $argv[$i+1];
7111        $i++;
7112    } elseif ($arg == '-T' || $arg == '--test') {
7113        $test_dir = $argv[$i+1];
7114        $i++;
7115    } elseif ($arg == '--debug') {
7116        $debug = 1;
7117    } elseif ($arg == '--extremedebug') {
7118        $debug = 2;
7119    }
7120}
7121
7122$config = PEAR_Config::singleton();
7123
7124if (PEAR::isError($config)) {
7125    $locs = PEAR_Config::getDefaultConfigFiles();
7126    die("ERROR: One of $locs[user] or $locs[system] is corrupt, please remove them and try again");
7127}
7128
7129// make sure we use only default values
7130$config_layers = $config->getLayers();
7131foreach ($config_layers as $layer) {
7132    if ($layer == 'default') continue;
7133    $config->removeLayer($layer);
7134}
7135$keys = $config->getKeys();
7136if ($debug) {
7137    $config->set('verbose', 5, 'default');
7138} else {
7139    $config->set('verbose', 0, 'default');
7140}
7141// PEAR executables
7142if (!empty($bin_dir)) {
7143    $config->set('bin_dir', $bin_dir, 'default');
7144}
7145
7146// Cache files
7147if (!empty($cache_dir)) {
7148    $config->set('cache_dir', $cache_dir, 'default');
7149}
7150
7151// Config files
7152if (!empty($cfg_dir)) {
7153    $config->set('cfg_dir', $cfg_dir, 'default');
7154}
7155
7156// Web files
7157if (!empty($www_dir)) {
7158    $config->set('www_dir', $www_dir, 'default');
7159}
7160
7161// Downloaded files
7162if (!empty($download_dir)) {
7163    $config->set('download_dir', $download_dir, 'default');
7164}
7165
7166// Temporary files
7167if (!empty($temp_dir)) {
7168    $config->set('temp_dir', $temp_dir, 'default');
7169}
7170
7171// Documentation files
7172if (!empty($doc_dir)) {
7173    $config->set('doc_dir', $doc_dir, 'default');
7174}
7175
7176// Data files
7177if (!empty($data_dir)) {
7178    $config->set('data_dir', $data_dir, 'default');
7179}
7180
7181// Unit tests
7182if (!empty($test_dir)) {
7183    $config->set('test_dir', $test_dir, 'default');
7184}
7185
7186// User supplied a dir prefix
7187if (!empty($with_dir)) {
7188    $ds = DIRECTORY_SEPARATOR;
7189    $config->set('php_dir', $with_dir, 'default');
7190    // Metadata
7191    if (!empty($metadata_dir)) {
7192        $config->set('metadata_dir', $metadata_dir, 'default');
7193    }
7194    if (empty($doc_dir)) {
7195        $config->set('doc_dir', $with_dir . $ds . 'doc', 'default');
7196    }
7197    if (empty($data_dir)) {
7198        $config->set('data_dir', $with_dir . $ds . 'data', 'default');
7199    }
7200    if (empty($test_dir)) {
7201        $config->set('test_dir', $with_dir . $ds . 'test', 'default');
7202    }
7203    if (empty($www_dir)) {
7204        $config->set('www_dir', $with_dir . $ds . 'htdocs', 'default');
7205    }
7206    if (empty($cfg_dir)) {
7207        $config->set('cfg_dir', $with_dir . $ds . 'cfg', 'default');
7208    }
7209    if (!is_writable($config->get('cache_dir'))) {
7210        include_once 'phar://install-pear-nozlib.phar/System.php';
7211        $cdir = System::mktemp(array('-d', 'pear'));
7212        if (PEAR::isError($cdir)) {
7213            $ui->outputData("[PEAR] cannot make new temporary directory: " . $cdir);
7214            die(1);
7215        }
7216        $oldcachedir = $config->get('cache_dir');
7217        $config->set('cache_dir', $cdir);
7218    }
7219}
7220
7221// PHP executable
7222if (!empty($php_bin)) {
7223    $config->set('php_bin', $php_bin);
7224}
7225
7226// PHP prefix
7227if (isset($prefix)) {
7228    if ($prefix != 'a') {
7229        if ($prefix[0] == 'a') {
7230            $prefix = substr($prefix, 1);
7231        }
7232        $config->set('php_prefix', $prefix, 'system');
7233    }
7234}
7235
7236// PHP suffix
7237if (isset($suffix)) {
7238    if ($suffix != 'a') {
7239        if ($suffix[0] == 'a') {
7240            $suffix = substr($suffix, 1);
7241        }
7242        $config->set('php_suffix', $suffix, 'system');
7243    }
7244}
7245
7246/* Print PEAR Conf (useful for debuging do NOT REMOVE) */
7247if ($debug) {
7248    sort($keys);
7249    foreach ($keys as $key) {
7250        echo $key . '    ' .
7251            $config->getPrompt($key) . ": " . $config->get($key, null, 'default') . "\n";
7252    }
7253    if ($debug == 2) { // extreme debugging
7254        exit;
7255    }
7256}
7257// end print
7258
7259$php_dir = $config->get('php_dir');
7260$options = array();
7261$options['upgrade'] = true;
7262$install_root = getenv('INSTALL_ROOT');
7263if (!empty($install_root)) {
7264    $options['packagingroot'] = $install_root;
7265    $reg = &new PEAR_Registry($options['packagingroot'], false, false, $metadata_dir);
7266} else {
7267    $reg = $config->getRegistry('default');
7268}
7269
7270$ui = PEAR_Frontend::singleton('PEAR_Frontend_CLI');
7271if (PEAR::isError($ui)) {
7272    die($ui->getMessage());
7273}
7274$installer = new PEAR_Installer($ui);
7275$pkg = new PEAR_PackageFile($config, $debug);
7276
7277foreach ($install_files as $package => $instfile) {
7278    $info = $pkg->fromAnyFile($instfile, PEAR_VALIDATE_INSTALLING);
7279    if (PEAR::isError($info)) {
7280        if (is_array($info->getUserInfo())) {
7281            foreach ($info->getUserInfo() as $err) {
7282                $ui->outputData(sprintf("[PEAR] %s: %s", $package, $err['message']));
7283            }
7284        }
7285        $ui->outputData(sprintf("[PEAR] %s: %s", $package, $info->getMessage()));
7286        continue;
7287    }
7288    $new_ver = $info->getVersion();
7289    $downloaderpackage = new PEAR_Downloader_Package($installer);
7290    $err = $downloaderpackage->initialize($instfile);
7291    if (PEAR::isError($err)) {
7292        $ui->outputData(sprintf("[PEAR] %s: %s", $package, $err->getMessage()));
7293        continue;
7294    }
7295    if ($reg->packageExists($package)) {
7296        $old_ver = $reg->packageInfo($package, 'version');
7297        if (version_compare($new_ver, $old_ver, 'gt')) {
7298            $installer->setOptions($options);
7299            $dp = array($downloaderpackage);
7300            $installer->setDownloadedPackages($dp);
7301            $err = $installer->install($downloaderpackage, $options);
7302            if (PEAR::isError($err)) {
7303                $ui->outputData(sprintf("[PEAR] %s: %s", $package, $err->getMessage()));
7304                continue;
7305            }
7306            $ui->outputData(sprintf("[PEAR] %-15s- upgraded:  %s", $package, $new_ver));
7307        } else {
7308            if ($force) {
7309                $options['force'] = true;
7310                $installer->setOptions($options);
7311                $dp = array($downloaderpackage);
7312                $installer->setDownloadedPackages($dp);
7313                $err = $installer->install($downloaderpackage, $options);
7314                if (PEAR::isError($err)) {
7315                    $ui->outputData(sprintf("[PEAR] %s: %s", $package, $err->getMessage()));
7316                    continue;
7317                }
7318                $ui->outputData(sprintf("[PEAR] %-15s- installed: %s", $package, $new_ver));
7319            } else {
7320                $ui->outputData(sprintf("[PEAR] %-15s- already installed: %s", $package, $old_ver));
7321            }
7322        }
7323    } else {
7324        $options['nodeps'] = true;
7325        $installer->setOptions($options);
7326        $dp = array($downloaderpackage);
7327        $installer->setDownloadedPackages($dp);
7328        $err = $installer->install($downloaderpackage, $options);
7329        if (PEAR::isError($err)) {
7330            $ui->outputData(sprintf("[PEAR] %s: %s", $package, $err->getMessage()));
7331            continue;
7332        }
7333        $ui->outputData(sprintf("[PEAR] %-15s- installed: %s", $package, $new_ver));
7334    }
7335    if ($package == 'PEAR') {
7336        if (is_file($ufile = $config->getConfFile('user'))) {
7337            $ui->outputData('Warning! a PEAR user config file already exists from ' .
7338                            'a previous PEAR installation at ' .
7339                            "'$ufile'. You may probably want to remove it.");
7340        }
7341        $config->set('verbose', 1, 'default');
7342        if (isset($oldcachedir)) {
7343            $config->set('cache_dir', $oldcachedir);
7344        }
7345        $data = array();
7346        foreach ($config->getKeys() as $key) {
7347            $data[$key] = $config->get($key);
7348        }
7349        $cnf_file = $config->getConfFile('system');
7350        if (!empty($install_root)) {
7351            $cnf_file = $install_root . DIRECTORY_SEPARATOR . $cnf_file;
7352        }
7353        $config->writeConfigFile($cnf_file, 'system', $data);
7354        $ui->outputData('Wrote PEAR system config file at: ' . $cnf_file);
7355        $ui->outputData('You may want to add: ' . $config->get('php_dir') . ' to your php.ini include_path');
7356    }
7357}
7358?>
7359<?php
7360/**
7361 * The OS_Guess class
7362 *
7363 * PHP versions 4 and 5
7364 *
7365 * @category   pear
7366 * @package    PEAR
7367 * @author     Stig Bakken <ssb@php.net>
7368 * @author     Gregory Beaver <cellog@php.net>
7369 * @copyright  1997-2009 The Authors
7370 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
7371 * @version    CVS: $Id: Guess.php 313023 2011-07-06 19:17:11Z dufuz $
7372 * @link       http://pear.php.net/package/PEAR
7373 * @since      File available since PEAR 0.1
7374 */
7375
7376// {{{ uname examples
7377
7378// php_uname() without args returns the same as 'uname -a', or a PHP-custom
7379// string for Windows.
7380// PHP versions prior to 4.3 return the uname of the host where PHP was built,
7381// as of 4.3 it returns the uname of the host running the PHP code.
7382//
7383// PC RedHat Linux 7.1:
7384// Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown
7385//
7386// PC Debian Potato:
7387// Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown
7388//
7389// PC FreeBSD 3.3:
7390// FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000     root@example.com:/usr/src/sys/compile/CONFIG  i386
7391//
7392// PC FreeBSD 4.3:
7393// FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001     root@example.com:/usr/src/sys/compile/CONFIG  i386
7394//
7395// PC FreeBSD 4.5:
7396// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb  6 23:59:23 CET 2002     root@example.com:/usr/src/sys/compile/CONFIG  i386
7397//
7398// PC FreeBSD 4.5 w/uname from GNU shellutils:
7399// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb  i386 unknown
7400//
7401// HP 9000/712 HP-UX 10:
7402// HP-UX iq B.10.10 A 9000/712 2008429113 two-user license
7403//
7404// HP 9000/712 HP-UX 10 w/uname from GNU shellutils:
7405// HP-UX host B.10.10 A 9000/712 unknown
7406//
7407// IBM RS6000/550 AIX 4.3:
7408// AIX host 3 4 000003531C00
7409//
7410// AIX 4.3 w/uname from GNU shellutils:
7411// AIX host 3 4 000003531C00 unknown
7412//
7413// SGI Onyx IRIX 6.5 w/uname from GNU shellutils:
7414// IRIX64 host 6.5 01091820 IP19 mips
7415//
7416// SGI Onyx IRIX 6.5:
7417// IRIX64 host 6.5 01091820 IP19
7418//
7419// SparcStation 20 Solaris 8 w/uname from GNU shellutils:
7420// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc
7421//
7422// SparcStation 20 Solaris 8:
7423// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20
7424//
7425// Mac OS X (Darwin)
7426// Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug  5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC  Power Macintosh
7427//
7428// Mac OS X early versions
7429//
7430
7431// }}}
7432
7433/* TODO:
7434 * - define endianness, to allow matchSignature("bigend") etc.
7435 */
7436
7437/**
7438 * Retrieves information about the current operating system
7439 *
7440 * This class uses php_uname() to grok information about the current OS
7441 *
7442 * @category   pear
7443 * @package    PEAR
7444 * @author     Stig Bakken <ssb@php.net>
7445 * @author     Gregory Beaver <cellog@php.net>
7446 * @copyright  1997-2009 The Authors
7447 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
7448 * @version    Release: 1.9.4
7449 * @link       http://pear.php.net/package/PEAR
7450 * @since      Class available since Release 0.1
7451 */
7452class OS_Guess
7453{
7454    var $sysname;
7455    var $nodename;
7456    var $cpu;
7457    var $release;
7458    var $extra;
7459
7460    function OS_Guess($uname = null)
7461    {
7462        list($this->sysname,
7463             $this->release,
7464             $this->cpu,
7465             $this->extra,
7466             $this->nodename) = $this->parseSignature($uname);
7467    }
7468
7469    function parseSignature($uname = null)
7470    {
7471        static $sysmap = array(
7472            'HP-UX' => 'hpux',
7473            'IRIX64' => 'irix',
7474        );
7475        static $cpumap = array(
7476            'i586' => 'i386',
7477            'i686' => 'i386',
7478            'ppc' => 'powerpc',
7479        );
7480        if ($uname === null) {
7481            $uname = php_uname();
7482        }
7483        $parts = preg_split('/\s+/', trim($uname));
7484        $n = count($parts);
7485
7486        $release  = $machine = $cpu = '';
7487        $sysname  = $parts[0];
7488        $nodename = $parts[1];
7489        $cpu      = $parts[$n-1];
7490        $extra = '';
7491        if ($cpu == 'unknown') {
7492            $cpu = $parts[$n - 2];
7493        }
7494
7495        switch ($sysname) {
7496            case 'AIX' :
7497                $release = "$parts[3].$parts[2]";
7498                break;
7499            case 'Windows' :
7500                switch ($parts[1]) {
7501                    case '95/98':
7502                        $release = '9x';
7503                        break;
7504                    default:
7505                        $release = $parts[1];
7506                        break;
7507                }
7508                $cpu = 'i386';
7509                break;
7510            case 'Linux' :
7511                $extra = $this->_detectGlibcVersion();
7512                // use only the first two digits from the kernel version
7513                $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]);
7514                break;
7515            case 'Mac' :
7516                $sysname = 'darwin';
7517                $nodename = $parts[2];
7518                $release = $parts[3];
7519                if ($cpu == 'Macintosh') {
7520                    if ($parts[$n - 2] == 'Power') {
7521                        $cpu = 'powerpc';
7522                    }
7523                }
7524                break;
7525            case 'Darwin' :
7526                if ($cpu == 'Macintosh') {
7527                    if ($parts[$n - 2] == 'Power') {
7528                        $cpu = 'powerpc';
7529                    }
7530                }
7531                $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]);
7532                break;
7533            default:
7534                $release = preg_replace('/-.*/', '', $parts[2]);
7535                break;
7536        }
7537
7538        if (isset($sysmap[$sysname])) {
7539            $sysname = $sysmap[$sysname];
7540        } else {
7541            $sysname = strtolower($sysname);
7542        }
7543        if (isset($cpumap[$cpu])) {
7544            $cpu = $cpumap[$cpu];
7545        }
7546        return array($sysname, $release, $cpu, $extra, $nodename);
7547    }
7548
7549    function _detectGlibcVersion()
7550    {
7551        static $glibc = false;
7552        if ($glibc !== false) {
7553            return $glibc; // no need to run this multiple times
7554        }
7555        $major = $minor = 0;
7556        include_once 'phar://install-pear-nozlib.phar/' . "System.php";
7557        // Use glibc's <features.h> header file to
7558        // get major and minor version number:
7559        if (@file_exists('/usr/include/features.h') &&
7560              @is_readable('/usr/include/features.h')) {
7561            if (!@file_exists('/usr/bin/cpp') || !@is_executable('/usr/bin/cpp')) {
7562                $features_file = fopen('/usr/include/features.h', 'rb');
7563                while (!feof($features_file)) {
7564                    $line = fgets($features_file, 8192);
7565                    if (!$line || (strpos($line, '#define') === false)) {
7566                        continue;
7567                    }
7568                    if (strpos($line, '__GLIBC__')) {
7569                        // major version number #define __GLIBC__ version
7570                        $line = preg_split('/\s+/', $line);
7571                        $glibc_major = trim($line[2]);
7572                        if (isset($glibc_minor)) {
7573                            break;
7574                        }
7575                        continue;
7576                    }
7577
7578                    if (strpos($line, '__GLIBC_MINOR__'))  {
7579                        // got the minor version number
7580                        // #define __GLIBC_MINOR__ version
7581                        $line = preg_split('/\s+/', $line);
7582                        $glibc_minor = trim($line[2]);
7583                        if (isset($glibc_major)) {
7584                            break;
7585                        }
7586                        continue;
7587                    }
7588                }
7589                fclose($features_file);
7590                if (!isset($glibc_major) || !isset($glibc_minor)) {
7591                    return $glibc = '';
7592                }
7593                return $glibc = 'glibc' . trim($glibc_major) . "." . trim($glibc_minor) ;
7594            } // no cpp
7595
7596            $tmpfile = System::mktemp("glibctest");
7597            $fp = fopen($tmpfile, "w");
7598            fwrite($fp, "#include <features.h>\n__GLIBC__ __GLIBC_MINOR__\n");
7599            fclose($fp);
7600            $cpp = popen("/usr/bin/cpp $tmpfile", "r");
7601            while ($line = fgets($cpp, 1024)) {
7602                if ($line{0} == '#' || trim($line) == '') {
7603                    continue;
7604                }
7605
7606                if (list($major, $minor) = explode(' ', trim($line))) {
7607                    break;
7608                }
7609            }
7610            pclose($cpp);
7611            unlink($tmpfile);
7612        } // features.h
7613
7614        if (!($major && $minor) && @is_link('/lib/libc.so.6')) {
7615            // Let's try reading the libc.so.6 symlink
7616            if (preg_match('/^libc-(.*)\.so$/', basename(readlink('/lib/libc.so.6')), $matches)) {
7617                list($major, $minor) = explode('.', $matches[1]);
7618            }
7619        }
7620
7621        if (!($major && $minor)) {
7622            return $glibc = '';
7623        }
7624
7625        return $glibc = "glibc{$major}.{$minor}";
7626    }
7627
7628    function getSignature()
7629    {
7630        if (empty($this->extra)) {
7631            return "{$this->sysname}-{$this->release}-{$this->cpu}";
7632        }
7633        return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}";
7634    }
7635
7636    function getSysname()
7637    {
7638        return $this->sysname;
7639    }
7640
7641    function getNodename()
7642    {
7643        return $this->nodename;
7644    }
7645
7646    function getCpu()
7647    {
7648        return $this->cpu;
7649    }
7650
7651    function getRelease()
7652    {
7653        return $this->release;
7654    }
7655
7656    function getExtra()
7657    {
7658        return $this->extra;
7659    }
7660
7661    function matchSignature($match)
7662    {
7663        $fragments = is_array($match) ? $match : explode('-', $match);
7664        $n = count($fragments);
7665        $matches = 0;
7666        if ($n > 0) {
7667            $matches += $this->_matchFragment($fragments[0], $this->sysname);
7668        }
7669        if ($n > 1) {
7670            $matches += $this->_matchFragment($fragments[1], $this->release);
7671        }
7672        if ($n > 2) {
7673            $matches += $this->_matchFragment($fragments[2], $this->cpu);
7674        }
7675        if ($n > 3) {
7676            $matches += $this->_matchFragment($fragments[3], $this->extra);
7677        }
7678        return ($matches == $n);
7679    }
7680
7681    function _matchFragment($fragment, $value)
7682    {
7683        if (strcspn($fragment, '*?') < strlen($fragment)) {
7684            $reg = '/^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '\\z/';
7685            return preg_match($reg, $value);
7686        }
7687        return ($fragment == '*' || !strcasecmp($fragment, $value));
7688    }
7689
7690}
7691/*
7692 * Local Variables:
7693 * indent-tabs-mode: nil
7694 * c-basic-offset: 4
7695 * End:
7696 */package2.xml0000644000076500000240000013062211605156614012241 0ustar  helgistaff<?xml version="1.0" encoding="UTF-8"?>
7697<package packagerversion="1.9.4" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
7698 <name>PEAR</name>
7699 <channel>pear.php.net</channel>
7700 <summary>PEAR Base System</summary>
7701 <description>The PEAR package contains:
7702 * the PEAR installer, for creating, distributing
7703   and installing packages
7704 * the PEAR_Exception PHP5 error handling mechanism
7705 * the PEAR_ErrorStack advanced error handling mechanism
7706 * the PEAR_Error error handling mechanism
7707 * the OS_Guess class for retrieving info about the OS
7708   where PHP is running on
7709 * the System class for quick handling of common operations
7710   with files and directories
7711 * the PEAR base class
7712  Features in a nutshell:
7713  * full support for channels
7714  * pre-download dependency validation
7715  * new package.xml 2.0 format allows tremendous flexibility while maintaining BC
7716  * support for optional dependency groups and limited support for sub-packaging
7717  * robust dependency support
7718  * full dependency validation on uninstall
7719  * remote install for hosts with only ftp access - no more problems with
7720    restricted host installation
7721  * full support for mirroring
7722  * support for bundling several packages into a single tarball
7723  * support for static dependencies on a url-based package
7724  * support for custom file roles and installation tasks</description>
7725 <lead>
7726  <name>Greg Beaver</name>
7727  <user>cellog</user>
7728  <email>cellog@php.net</email>
7729  <active>no</active>
7730 </lead>
7731 <lead>
7732  <name>Pierre-Alain Joye</name>
7733  <user>pajoye</user>
7734  <email>pierre@php.net</email>
7735  <active>no</active>
7736 </lead>
7737 <lead>
7738  <name>Stig Bakken</name>
7739  <user>ssb</user>
7740  <email>stig@php.net</email>
7741  <active>no</active>
7742 </lead>
7743 <lead>
7744  <name>Tomas V.V.Cox</name>
7745  <user>cox</user>
7746  <email>cox@idecnet.com</email>
7747  <active>no</active>
7748 </lead>
7749 <lead>
7750  <name>Helgi Thormar</name>
7751  <user>dufuz</user>
7752  <email>dufuz@php.net</email>
7753  <active>yes</active>
7754 </lead>
7755 <developer>
7756  <name>Tias Guns</name>
7757  <user>tias</user>
7758  <email>tias@php.net</email>
7759  <active>yes</active>
7760 </developer>
7761 <helper>
7762  <name>Tim Jackson</name>
7763  <user>timj</user>
7764  <email>timj@php.net</email>
7765  <active>no</active>
7766 </helper>
7767 <helper>
7768  <name>Bertrand Gugger</name>
7769  <user>toggg</user>
7770  <email>toggg@php.net</email>
7771  <active>no</active>
7772 </helper>
7773 <helper>
7774  <name>Martin Jansen</name>
7775  <user>mj</user>
7776  <email>mj@php.net</email>
7777  <active>no</active>
7778 </helper>
7779 <date>2011-07-06</date>
7780 <time>22:11:24</time>
7781 <version>
7782  <release>1.9.4</release>
7783  <api>1.9.4</api>
7784 </version>
7785 <stability>
7786  <release>stable</release>
7787  <api>stable</api>
7788 </stability>
7789 <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
7790 <notes>
7791Bug Fixes:
7792* Bug #17350: &quot;pear install --force&quot; doesn&apos;t uninstall files from previous pkg versions [dufuz]
7793* Bug #18362: A whitespace TEMP_DIR path breaks install/upgrade functionality [dufuz]
7794* Bug #18440: bad tmp folder path on install : Unable to create path for C:/Program/tmp [dufuz]
7795* Bug #18581: &quot;config-get -c&quot; not returning channel&apos;s configuration when using alias [dufuz]
7796* Bug #18639: regression: installing xdebug fails most likely due to another fix [dufuz]
7797
7798Features
7799* All System (the class) functions can now take in spaced paths as long as they are surrounded in quotes.
7800  Prior to this it was possible to do that by passing all values in as an array (by product of #18362, #18440) [dufuz]
7801 </notes>
7802 <contents>
7803  <dir name="/">
7804   <file md5sum="a74724b2a02b50afb0e71f78b7661a4c" name="OS/Guess.php" role="php">
7805    <tasks:replace from="@package_version@" to="version" type="package-info" />
7806   </file>
7807   <file md5sum="d453b69059dceb53e5a61b9881fe2336" name="PEAR/ChannelFile/Parser.php" role="php">
7808    <tasks:replace from="@package_version@" to="version" type="package-info" />
7809   </file>
7810   <file md5sum="8fd87e64002e11fd86eb2f3fbfee6599" name="PEAR/Command/Auth.xml" role="php" />
7811   <file md5sum="314f2756d74b4cc529e77be45847b3ac" name="PEAR/Command/Auth.php" role="php">
7812    <tasks:replace from="@package_version@" to="version" type="package-info" />
7813   </file>
7814   <file md5sum="73602fd7f051eaf8d37452d0e3063bdb" name="PEAR/Command/Build.xml" role="php" />
7815   <file md5sum="aa7961eff628901fd6a08074715d18ac" name="PEAR/Command/Build.php" role="php">
7816    <tasks:replace from="@package_version@" to="version" type="package-info" />
7817   </file>
7818   <file md5sum="6d5aab4d4308c3005b5f584c7783a031" name="PEAR/Command/Channels.xml" role="php" />
7819   <file md5sum="d61f91d95657304c3e7facf3f4a3ab7b" name="PEAR/Command/Channels.php" role="php">
7820    <tasks:replace from="@package_version@" to="version" type="package-info" />
7821   </file>
7822   <file md5sum="1214f0f5edb4d7531e54b32e75c3b162" name="PEAR/Command/Common.php" role="php">
7823    <tasks:replace from="@package_version@" to="version" type="package-info" />
7824   </file>
7825   <file md5sum="91f189cb9423b5e87ee0abc5ea1a2be3" name="PEAR/Command/Config.xml" role="php" />
7826   <file md5sum="2cca5bfb4005291d3a234b9cc00e4091" name="PEAR/Command/Config.php" role="php">
7827    <tasks:replace from="@package_version@" to="version" type="package-info" />
7828   </file>
7829   <file md5sum="2db0386b865d3f9a29f9126728722ece" name="PEAR/Command/Install.xml" role="php" />
7830   <file md5sum="32d3ee477937685cffec094424390c18" name="PEAR/Command/Install.php" role="php">
7831    <tasks:replace from="@package_version@" to="version" type="package-info" />
7832   </file>
7833   <file md5sum="5cb62a04c0a268f4edd64a49a3895c92" name="PEAR/Command/Mirror.xml" role="php" />
7834   <file md5sum="bc0b8df989565f87f1693ac7a2ce0e4b" name="PEAR/Command/Mirror.php" role="php">
7835    <tasks:replace from="@package_version@" to="version" type="package-info" />
7836   </file>
7837   <file md5sum="9367dcd7e4dbdde423f9c4c7d3f3a919" name="PEAR/Command/Package.xml" role="php" />
7838   <file md5sum="68f45817fe926173b45018e1b1dec108" name="PEAR/Command/Package.php" role="php">
7839    <tasks:replace from="@DATA-DIR@" to="data_dir" type="pear-config" />
7840    <tasks:replace from="@package_version@" to="version" type="package-info" />
7841   </file>
7842   <file md5sum="28dc842ea725d8787b9f9c3dbca5aa22" name="PEAR/Command/Pickle.xml" role="php" />
7843   <file md5sum="583e8d10daab8e16b2e2a9fc3498b1ac" name="PEAR/Command/Pickle.php" role="php">
7844    <tasks:replace from="@package_version@" to="version" type="package-info" />
7845   </file>
7846   <file md5sum="49b046cfc14747f0365e02e9c3f0e6dc" name="PEAR/Command/Registry.xml" role="php" />
7847   <file md5sum="59629a5d63b0d5e54f49de509a1a6cf9" name="PEAR/Command/Registry.php" role="php">
7848    <tasks:replace from="@package_version@" to="version" type="package-info" />
7849   </file>
7850   <file md5sum="29c02e823879b4e3e291f6b36fb339f1" name="PEAR/Command/Remote.xml" role="php" />
7851   <file md5sum="36c02597c5db0b5148aef6e49d3b2604" name="PEAR/Command/Remote.php" role="php">
7852    <tasks:replace from="@package_version@" to="version" type="package-info" />
7853   </file>
7854   <file md5sum="a50c32015005e0761cc3b04679b29ed0" name="PEAR/Command/Test.xml" role="php" />
7855   <file md5sum="8794af46ada0364362db0e131607e6d1" name="PEAR/Command/Test.php" role="php">
7856    <tasks:replace from="@package_version@" to="version" type="package-info" />
7857   </file>
7858   <file md5sum="2a4bfddd597fcb1d63b2a06ea4544b91" name="PEAR/Downloader/Package.php" role="php">
7859    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
7860   </file>
7861   <file md5sum="8c8ebc9143aeff5fd5803c63e1cd0070" name="PEAR/Frontend/CLI.php" role="php">
7862    <tasks:replace from="@package_version@" to="version" type="package-info" />
7863   </file>
7864   <file md5sum="6a0785b8e9678b7283d56eba9aaea390" name="PEAR/Installer/Role/Common.php" role="php">
7865    <tasks:replace from="@package_version@" to="version" type="package-info" />
7866   </file>
7867   <file md5sum="d8c62e6275e3aaa7784290912406092c" name="PEAR/Installer/Role/Cfg.xml" role="php" />
7868   <file md5sum="faee4c4408cf77aa73c608b54ed3a587" name="PEAR/Installer/Role/Cfg.php" role="php">
7869    <tasks:replace from="@package_version@" to="version" type="package-info" />
7870   </file>
7871   <file md5sum="89a4a2a286e842d45a98974f40a0565c" name="PEAR/Installer/Role/Data.xml" role="php" />
7872   <file md5sum="6273b7a0c754f7ec61a9ad0e7476ab35" name="PEAR/Installer/Role/Data.php" role="php">
7873    <tasks:replace from="@package_version@" to="version" type="package-info" />
7874   </file>
7875   <file md5sum="b1ce0fe105251c3b75209d6518ee69ac" name="PEAR/Installer/Role/Doc.xml" role="php" />
7876   <file md5sum="f965a64b0fc06036ed4299ab51521874" name="PEAR/Installer/Role/Doc.php" role="php">
7877    <tasks:replace from="@package_version@" to="version" type="package-info" />
7878   </file>
7879   <file md5sum="af71c0ad42d16a323afe24a4f884ef15" name="PEAR/Installer/Role/Ext.xml" role="php" />
7880   <file md5sum="8222eb9dc206801ebd0495720e94490f" name="PEAR/Installer/Role/Ext.php" role="php">
7881    <tasks:replace from="@package_version@" to="version" type="package-info" />
7882   </file>
7883   <file md5sum="ef88f0321d3e481c2130c95122cf76d8" name="PEAR/Installer/Role/Php.xml" role="php" />
7884   <file md5sum="5bb9fe55b02ec58880243234bd847e57" name="PEAR/Installer/Role/Php.php" role="php">
7885    <tasks:replace from="@package_version@" to="version" type="package-info" />
7886   </file>
7887   <file md5sum="746461dc3b48af6d24094cb0211608f2" name="PEAR/Installer/Role/Script.xml" role="php" />
7888   <file md5sum="769a902fe324b627357860b1b922aa1c" name="PEAR/Installer/Role/Script.php" role="php">
7889    <tasks:replace from="@package_version@" to="version" type="package-info" />
7890   </file>
7891   <file md5sum="e147d63f168ea156fc2be38caaa63804" name="PEAR/Installer/Role/Src.xml" role="php" />
7892   <file md5sum="8f38f7e74a60e90a7e1363b6f509b426" name="PEAR/Installer/Role/Src.php" role="php">
7893    <tasks:replace from="@package_version@" to="version" type="package-info" />
7894   </file>
7895   <file md5sum="a24b596ec987aa5688fc19e8ed4e97ea" name="PEAR/Installer/Role/Test.xml" role="php" />
7896   <file md5sum="b12694a189904c77db170d89f8aa9b61" name="PEAR/Installer/Role/Test.php" role="php">
7897    <tasks:replace from="@package_version@" to="version" type="package-info" />
7898   </file>
7899   <file md5sum="7641e71c5785bb33a4261ebe25ed0fd7" name="PEAR/Installer/Role/Www.xml" role="php" />
7900   <file md5sum="0a3b8808e8c7f26a7f2e3c115fd09fdc" name="PEAR/Installer/Role/Www.php" role="php">
7901    <tasks:replace from="@package_version@" to="version" type="package-info" />
7902   </file>
7903   <file md5sum="bd136be2697af553a4ba35f7eaca7f68" name="PEAR/Installer/Role.php" role="php">
7904    <tasks:replace from="@package_version@" to="version" type="package-info" />
7905   </file>
7906   <file md5sum="7db4fa3f069fc2ed490464b1efe6d801" name="PEAR/PackageFile/Generator/v1.php" role="php">
7907    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
7908   </file>
7909   <file md5sum="8294267046359e521dc226ebbbed8d37" name="PEAR/PackageFile/Generator/v2.php" role="php">
7910    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
7911   </file>
7912   <file md5sum="ef7d06b95df06047a46df12187997a4f" name="PEAR/PackageFile/Parser/v1.php" role="php">
7913    <tasks:replace from="@package_version@" to="version" type="package-info" />
7914   </file>
7915   <file md5sum="98b457406963b0ba61d6206ee3b22615" name="PEAR/PackageFile/Parser/v2.php" role="php">
7916    <tasks:replace from="@package_version@" to="version" type="package-info" />
7917   </file>
7918   <file md5sum="6cb5b523de08db7641d53fbd01161806" name="PEAR/PackageFile/v2/rw.php" role="php">
7919    <tasks:replace from="@package_version@" to="version" type="package-info" />
7920   </file>
7921   <file md5sum="4bb188500dcecfca3ee085572a6f88d2" name="PEAR/PackageFile/v2/Validator.php" role="php">
7922    <tasks:replace from="@package_version@" to="version" type="package-info" />
7923   </file>
7924   <file md5sum="6211d1048871e00e38509001e3a0248a" name="PEAR/PackageFile/v1.php" role="php">
7925    <tasks:replace from="@package_version@" to="version" type="package-info" />
7926   </file>
7927   <file md5sum="76d76e677471159a4bd4595190f849f8" name="PEAR/PackageFile/v2.php" role="php">
7928    <tasks:replace from="@package_version@" to="version" type="package-info" />
7929   </file>
7930   <file md5sum="e4a28d36a4e0e4d406034acc92f563de" name="PEAR/REST/10.php" role="php">
7931    <tasks:replace from="@package_version@" to="version" type="package-info" />
7932   </file>
7933   <file md5sum="256b8c9bd6f2411f17054a8bb8e56fa5" name="PEAR/REST/11.php" role="php">
7934    <tasks:replace from="@package_version@" to="version" type="package-info" />
7935   </file>
7936   <file md5sum="45b3138f8102e186da7cfdf8d2bd4150" name="PEAR/REST/13.php" role="php">
7937    <tasks:replace from="@package_version@" to="version" type="package-info" />
7938   </file>
7939   <file md5sum="77205d37ba0c4233ee343a78ddb6cd9d" name="PEAR/Task/Postinstallscript/rw.php" role="php">
7940    <tasks:replace from="@package_version@" to="version" type="package-info" />
7941   </file>
7942   <file md5sum="3c27b135962a1826a4e7d6b977b0b288" name="PEAR/Task/Replace/rw.php" role="php">
7943    <tasks:replace from="@package_version@" to="version" type="package-info" />
7944   </file>
7945   <file md5sum="0441c97aa22e4c281f1974df324a5cfb" name="PEAR/Task/Unixeol/rw.php" role="php">
7946    <tasks:replace from="@package_version@" to="version" type="package-info" />
7947   </file>
7948   <file md5sum="e9a7f560abe1db1c114b0cc7f3134a4a" name="PEAR/Task/Windowseol/rw.php" role="php">
7949    <tasks:replace from="@package_version@" to="version" type="package-info" />
7950   </file>
7951   <file md5sum="53e67f33a146cf9ee2d951239ab95ba0" name="PEAR/Task/Common.php" role="php">
7952    <tasks:replace from="@package_version@" to="version" type="package-info" />
7953   </file>
7954   <file md5sum="9eed944f3f795c52caba0cb87665cd7f" name="PEAR/Task/Postinstallscript.php" role="php">
7955    <tasks:replace from="@package_version@" to="version" type="package-info" />
7956   </file>
7957   <file md5sum="b51702374ca8c34dd4e540f8848f8f57" name="PEAR/Task/Replace.php" role="php">
7958    <tasks:replace from="@package_version@" to="version" type="package-info" />
7959   </file>
7960   <file md5sum="7a280f398b108bc030a126a10e75af7e" name="PEAR/Task/Unixeol.php" role="php">
7961    <tasks:replace from="@package_version@" to="version" type="package-info" />
7962   </file>
7963   <file md5sum="c321484c304ebb4de50cd204b6a61c6d" name="PEAR/Task/Windowseol.php" role="php">
7964    <tasks:replace from="@package_version@" to="version" type="package-info" />
7965   </file>
7966   <file md5sum="b8d475e539a47999dd78ddc0f2831b0e" name="PEAR/Validator/PECL.php" role="php">
7967    <tasks:replace from="@package_version@" to="version" type="package-info" />
7968   </file>
7969   <file md5sum="598ad0a7c788a4ee154501223f607fa5" name="PEAR/Autoloader.php" role="php">
7970    <tasks:replace from="@package_version@" to="version" type="package-info" />
7971   </file>
7972   <file md5sum="3a236b984c43be942864e9a2c2f7f618" name="PEAR/Builder.php" role="php">
7973    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
7974   </file>
7975   <file md5sum="fe1651f49d9b4e77e252c817c5ac8fb8" name="PEAR/ChannelFile.php" role="php">
7976    <tasks:replace from="@package_version@" to="version" type="package-info" />
7977   </file>
7978   <file md5sum="8dfb2616d94dc0d54ca05d6d2a9a3d60" name="PEAR/Command.php" role="php">
7979    <tasks:replace from="@package_version@" to="version" type="package-info" />
7980   </file>
7981   <file md5sum="3134df96c00615c595bc3c74c0fb605f" name="PEAR/Common.php" role="php">
7982    <tasks:replace from="@package_version@" to="version" type="package-info" />
7983   </file>
7984   <file md5sum="ef2e1652bed814e36676d605c9a7b599" name="PEAR/Config.php" role="php">
7985    <tasks:replace from="@package_version@" to="version" type="package-info" />
7986   </file>
7987   <file md5sum="708a3a44875759e005b881179b3d5ed4" name="PEAR/DependencyDB.php" role="php">
7988    <tasks:replace from="@package_version@" to="version" type="package-info" />
7989   </file>
7990   <file md5sum="0744ce601546afb988e2542b27d82aa9" name="PEAR/Dependency2.php" role="php">
7991    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
7992   </file>
7993   <file md5sum="3f62de6899c2ca37c89810e3510b77b9" name="PEAR/Downloader.php" role="php">
7994    <tasks:replace from="@package_version@" to="version" type="package-info" />
7995   </file>
7996   <file md5sum="3d1ee44f11a5c927f98149041cbba536" name="PEAR/ErrorStack.php" role="php">
7997    <tasks:replace from="@package_version@" to="version" type="package-info" />
7998   </file>
7999   <file md5sum="ebea74b4b24af93a0982536ae6faeb76" name="PEAR/Exception.php" role="php">
8000    <tasks:replace from="@package_version@" to="version" type="package-info" />
8001   </file>
8002   <file md5sum="e0e4cbcec4a972fbad779d0f9d323120" name="PEAR/FixPHP5PEARWarnings.php" role="php" />
8003   <file md5sum="e00af62b175d4a15430be6394b09afaa" name="PEAR/Frontend.php" role="php">
8004    <tasks:replace from="@package_version@" to="version" type="package-info" />
8005   </file>
8006   <file md5sum="5b77738449ea8d539883d520f0f05d2a" name="PEAR/Installer.php" role="php">
8007    <tasks:replace from="@package_version@" to="version" type="package-info" />
8008   </file>
8009   <file md5sum="60600f35203b87481eb354b9b58c05f9" name="PEAR/PackageFile.php" role="php">
8010    <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
8011   </file>
8012   <file md5sum="5e553c0ac9530fc8757735675f0837ee" name="PEAR/Packager.php" role="php">
8013    <tasks:replace from="@package_version@" to="version" type="package-info" />
8014   </file>
8015   <file md5sum="5ff450e917d7128af3dc5d596e02bd04" name="PEAR/Registry.php" role="php">
8016    <tasks:replace from="@package_version@" to="version" type="package-info" />
8017   </file>
8018   <file md5sum="9d0e60efa1884de7d2a812e815cbd30e" name="PEAR/REST.php" role="php">
8019    <tasks:replace from="@package_version@" to="version" type="package-info" />
8020   </file>
8021   <file md5sum="20315d45a87c2c9fb9992f5e27a0e354" name="PEAR/RunTest.php" role="php">
8022    <tasks:replace from="@package_version@" to="version" type="package-info" />
8023   </file>
8024   <file md5sum="709e306baa715e79696de0fcd7f5db6a" name="PEAR/Validate.php" role="php">
8025    <tasks:replace from="@package_version@" to="version" type="package-info" />
8026   </file>
8027   <file md5sum="496230695ea1e3f7e0eb1a1e7030383f" name="PEAR/XMLParser.php" role="php">
8028    <tasks:replace from="@package_version@" to="version" type="package-info" />
8029   </file>
8030   <file baseinstalldir="/" md5sum="d888d06143e3cac0dae78bbb2e761366" name="scripts/pear.bat" role="script">
8031    <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
8032    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
8033    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
8034    <tasks:windowseol />
8035   </file>
8036   <file baseinstalldir="/" md5sum="762c9aeb3a91f7160a12896ce197acb6" name="scripts/peardev.bat" role="script">
8037    <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
8038    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
8039    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
8040    <tasks:windowseol />
8041   </file>
8042   <file baseinstalldir="/" md5sum="2bd742ce721c8dd5aa164613c063a8a9" name="scripts/pecl.bat" role="script">
8043    <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
8044    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
8045    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
8046    <tasks:windowseol />
8047   </file>
8048   <file baseinstalldir="/" md5sum="8ac139504e80bede470aef6d405100b6" name="scripts/pear.sh" role="script">
8049    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
8050    <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
8051    <tasks:replace from="@pear_version@" to="version" type="package-info" />
8052    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
8053    <tasks:unixeol />
8054   </file>
8055   <file baseinstalldir="/" md5sum="08ea03525b4ba914dfd9ec69c4238cf4" name="scripts/peardev.sh" role="script">
8056    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
8057    <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
8058    <tasks:replace from="@pear_version@" to="version" type="package-info" />
8059    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
8060    <tasks:unixeol />
8061   </file>
8062   <file baseinstalldir="/" md5sum="41fab77b7808267d88b8e06097a1fbbe" name="scripts/pecl.sh" role="script">
8063    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
8064    <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
8065    <tasks:replace from="@pear_version@" to="version" type="package-info" />
8066    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
8067    <tasks:unixeol />
8068   </file>
8069   <file baseinstalldir="/" md5sum="fc547ba1ab80167d9c9cfcbb439d1537" name="scripts/pearcmd.php" role="php">
8070    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
8071    <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
8072    <tasks:replace from="@pear_version@" to="version" type="package-info" />
8073    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
8074   </file>
8075   <file baseinstalldir="/" md5sum="e758c3c57ace46749eb39868fe98b2ca" name="scripts/peclcmd.php" role="php">
8076    <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
8077    <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
8078    <tasks:replace from="@pear_version@" to="version" type="package-info" />
8079    <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
8080   </file>
8081   <file md5sum="45b44486d8090de17b2a8b4211fab247" name="LICENSE" role="doc" />
8082   <file md5sum="8e9d494169d0dea3d7b24bae14d85aba" name="INSTALL" role="doc" />
8083   <file md5sum="4a49bc83a392934e57af45c70a589fda" name="package.dtd" role="data" />
8084   <file md5sum="1a8f67d58009372a6cbcddd638b128cf" name="PEAR5.php" role="php" />
8085   <file md5sum="962bdcdbc2e4467f047b233fa45296bd" name="PEAR.php" role="php">
8086    <tasks:replace from="@package_version@" to="version" type="package-info" />
8087   </file>
8088   <file md5sum="b10231f9dd564ac24951758b67f6e155" name="README" role="doc" />
8089   <file md5sum="1d62197f092116e711e6c9b7ed0c4fe9" name="System.php" role="php">
8090    <tasks:replace from="@package_version@" to="version" type="package-info" />
8091   </file>
8092   <file md5sum="acd010e3bc43c0f72df584acde7b9158" name="template.spec" role="data" />
8093  </dir>
8094 </contents>
8095 <dependencies>
8096  <required>
8097   <php>
8098    <min>4.4.0</min>
8099    <exclude>5.0</exclude>
8100    <exclude>5.1.0</exclude>
8101    <exclude>5.1.1</exclude>
8102    <exclude>5.1.2</exclude>
8103    <exclude>5.1.3</exclude>
8104    <exclude>5.1.4</exclude>
8105    <exclude>5.1.5</exclude>
8106   </php>
8107   <pearinstaller>
8108    <min>1.4.3</min>
8109   </pearinstaller>
8110   <package>
8111    <name>Archive_Tar</name>
8112    <channel>pear.php.net</channel>
8113    <min>1.3.7</min>
8114   </package>
8115   <package>
8116    <name>Structures_Graph</name>
8117    <channel>pear.php.net</channel>
8118    <min>1.0.2</min>
8119    <recommended>1.0.4</recommended>
8120   </package>
8121   <package>
8122    <name>Console_Getopt</name>
8123    <channel>pear.php.net</channel>
8124    <min>1.2</min>
8125    <recommended>1.2.3</recommended>
8126   </package>
8127   <package>
8128    <name>XML_Util</name>
8129    <channel>pear.php.net</channel>
8130    <min>1.2.0</min>
8131    <recommended>1.2.1</recommended>
8132   </package>
8133   <package>
8134    <name>PEAR_Frontend_Web</name>
8135    <channel>pear.php.net</channel>
8136    <max>0.4</max>
8137    <conflicts />
8138   </package>
8139   <package>
8140    <name>PEAR_Frontend_Gtk</name>
8141    <channel>pear.php.net</channel>
8142    <max>0.4.0</max>
8143    <exclude>0.4.0</exclude>
8144    <conflicts />
8145   </package>
8146   <extension>
8147    <name>xml</name>
8148   </extension>
8149   <extension>
8150    <name>pcre</name>
8151   </extension>
8152  </required>
8153  <group hint="PEAR's web-based installer" name="webinstaller">
8154   <package>
8155    <name>PEAR_Frontend_Web</name>
8156    <channel>pear.php.net</channel>
8157    <min>0.5.1</min>
8158   </package>
8159  </group>
8160  <group hint="PEAR's PHP-GTK-based installer" name="gtkinstaller">
8161   <package>
8162    <name>PEAR_Frontend_Gtk</name>
8163    <channel>pear.php.net</channel>
8164    <min>0.4.0</min>
8165   </package>
8166  </group>
8167  <group hint="PEAR's PHP-GTK2-based installer" name="gtk2installer">
8168   <package>
8169    <name>PEAR_Frontend_Gtk2</name>
8170    <channel>pear.php.net</channel>
8171   </package>
8172  </group>
8173 </dependencies>
8174 <phprelease>
8175  <installconditions>
8176   <os>
8177    <name>windows</name>
8178   </os>
8179  </installconditions>
8180  <filelist>
8181   <install as="pear.bat" name="scripts/pear.bat" />
8182   <install as="peardev.bat" name="scripts/peardev.bat" />
8183   <install as="pecl.bat" name="scripts/pecl.bat" />
8184   <install as="pearcmd.php" name="scripts/pearcmd.php" />
8185   <install as="peclcmd.php" name="scripts/peclcmd.php" />
8186   <ignore name="scripts/peardev.sh" />
8187   <ignore name="scripts/pear.sh" />
8188   <ignore name="scripts/pecl.sh" />
8189  </filelist>
8190 </phprelease>
8191 <phprelease>
8192  <filelist>
8193   <install as="pear" name="scripts/pear.sh" />
8194   <install as="peardev" name="scripts/peardev.sh" />
8195   <install as="pecl" name="scripts/pecl.sh" />
8196   <install as="pearcmd.php" name="scripts/pearcmd.php" />
8197   <install as="peclcmd.php" name="scripts/peclcmd.php" />
8198   <ignore name="scripts/pear.bat" />
8199   <ignore name="scripts/peardev.bat" />
8200   <ignore name="scripts/pecl.bat" />
8201  </filelist>
8202 </phprelease>
8203 <changelog>
8204  <release>
8205   <version>
8206    <release>1.8.0alpha1</release>
8207    <api>1.8.0</api>
8208   </version>
8209   <stability>
8210    <release>alpha</release>
8211    <api>stable</api>
8212   </stability>
8213   <date>2009-03-09</date>
8214   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8215   <notes>
8216* Implement Request #10373: if pref_state=stable and installed package=beta, allow up to latest beta version [dufuz]
8217* Implement Request #10581: login / logout should map to channel-login / channel-logout [dufuz]
8218* Implement Request #10825: Only display the &quot;invalid or missing package file&quot;-error if it makes sense [dufuz]
8219* Implement Request #11170: script to generate Command/[command].xml [dufuz]
8220* Implement Request #11176: improve channel ... has updated its protocols message [dufuz]
8221* Implement Request #12706: pear list -a hard to read [dufuz]
8222* Implement Request #11353: upgrade-all and upgrade commands to upgrade within the same stability level [dufuz]
8223* Implement Request #13015: Add https discovery for channel.xml [dufuz / initial patch by Martin Roos]
8224* Implement Request #13927: install-pear.php should have option to set www_dir [timj]
8225* Implement Request #14324: Make the pear install command behave similar to apt-get [dufuz]
8226* Implement Request #14325: make pear upgrade with no params behave like pear upgrade-all [dufuz]
8227  - upgrade-all can be considered deprecated in favor of calling upgrade with no parameters to replicate
8228    better what other package managers are doing. upgrade-all will still work as intended.
8229* Implement Request #14504: add a channel parameter support to the upgrade function [dufuz]
8230  - Options -c ezc and --channel=ezc got added to upgrade and upgrade-all to allow for
8231    channel specific upgrades
8232* Implement Request #14556: install-pear-nozlib.phar should get download_dir config and other options [cweiske]
8233* Implement Request #15566: Add doc.php.net as a default channel [dufuz / saltybeagle]
8234
8235* Fix PHP Bug #43857: --program-suffix not always reflected everywhere [cellog]
8236* Fix PHP Bug #47323: strotime warnings in make install [dufuz]
8237
8238* Fix Bug #13908: pear info command and maintainers inactive not mentioned [dufuz]
8239* Fix Bug #13926: install-pear.php does not set cfg_dir if -d option set with no -c option [timj]
8240* Fix Bug #13943: tests fail when php.exe path contains spaces [dufuz / jorrit]
8241* Fix Bug #13953: config-set/config-show with channel alias fail [cellog]
8242* Fix Bug #13958: When a phpt tests exit() or die() xdebug coverage is not generated, patch by izi (David Jean Louis) [izi / dufuz]
8243* Fix Bug #14041: Unpredictable unit test processing sequence [dufuz]
8244* Fix Bug #14140: Strict warning not suppressed in the shutdown function [dufuz]
8245* Fix Bug #14210: pear list -ia brings warnings [dufuz]
8246* Fix Bug #14274: PEAR packager mangles package.xml encoding, then complains about it [dufuz]
8247* Fix Bug #14287: cannot upgrade from stable to beta via -beta when config is set to stable [dufuz]
8248* Fix Bug #14300: Package files themselves can not be served over https [dufuz / initial patch by Martin Roos]
8249* Fix Bug #14437: openbasedir warning when loading config [dufuz]
8250* Fix Bug #14558: PackageFile.php creates tmp directory outside configured temp_dir [cweiske]
8251* Fix Bug #14947: downloadHttp() is missing Host part of the HTTP Request when using Proxy [ifeghali]
8252* Fix Bug #14977: PEAR/Frontend.php doesn&apos;t require_once PEAR.php [dufuz]
8253* Fix Bug #15750: Unreachable code in PEAR_Downloader [dufuz]
8254* Fix Bug #15979: Package files incorrectly removed when splitting a package into multiple pkgs [dufuz]
8255* Fix Bug #15914: pear upgrade installs different version if desired version not found [dufuz]
8256
8257NOTE!
8258Functions that have been deprecated for 3+ years in PEAR_Common, please take a moment
8259to migrate over to one of the alternatives that have ben provided:
8260* PEAR_Common-&gt;downloadHttp (use PEAR_Downloader-&gt;downloadHttp instead)
8261* PEAR_Common-&gt;infoFromTgzFile (use PEAR_PackageFile-&gt;fromTgzFile instead)
8262* PEAR_Common-&gt;infoFromDescriptionFile (use PEAR_PackageFile-&gt;fromPackageFile instead)
8263* PEAR_Common-&gt;infoFromString (use PEAR_PackageFile-&gt;fromXmlstring instead)
8264* PEAR_Common-&gt;infoFromArray (use PEAR_PackageFile-&gt;fromAnyFile instead)
8265* PEAR_Common-&gt;xmlFromInfo (use a PEAR_PackageFile_v* object&apos;s generator instead)
8266* PEAR_Common-&gt;validatePackageInfo (use the validation of PEAR_PackageFile objects)
8267* PEAR_Common-&gt;analyzeSourceCode (use a PEAR_PackageFile_v* object instead)
8268* PEAR_Common-&gt;detectDependencies (use PEAR_Downloader_Package-&gt;detectDependencies instead)
8269* PEAR_Common-&gt;buildProvidesArray (use PEAR_PackageFile_v1-&gt;_buildProvidesArray or
8270  PEAR_PackageFile_v2_Validator-&gt;_buildProvidesArray)
8271
8272PHP 4.4 and 5.1.6 are now the minimum PHP requirements, for brave souls
8273pear upgrade -f PEAR will allow people with lower versions
8274to upgrade to this release but no guarantees will be made that it will work properly.
8275
8276Support for XML RPC channels has been dropped - The only ones that used it
8277(pear.php.net and pecl.php.net) have used the REST interface for years now.
8278SOAP support also removed as it was only proof of concept.
8279
8280Move codebase from the PHP License to New BSD 2 clause license
8281   </notes>
8282  </release>
8283  <release>
8284   <date>2009-03-27</date>
8285   <version>
8286    <release>1.8.0RC1</release>
8287    <api>1.8.0</api>
8288   </version>
8289   <stability>
8290    <release>beta</release>
8291    <api>stable</api>
8292   </stability>
8293   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8294   <notes>
8295* Fix Bug #14331: pear cvstag only works from inside the package directory [dufuz]
8296* Fix Bug #16045: E_Notice: Undefined index: channel in PEAR/DependencyDB.php [dufuz]
8297
8298* Implemented Request #11230: better error message when mirror not in channel.xml file [dufuz]
8299* Implemented Request #13150: Add support for following HTTP 302 redirects [dufuz]
8300   </notes>
8301  </release>
8302  <release>
8303   <date>2009-04-10</date>
8304   <version>
8305    <release>1.8.0</release>
8306    <api>1.8.0</api>
8307   </version>
8308   <stability>
8309    <release>stable</release>
8310    <api>stable</api>
8311   </stability>
8312   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8313   <notes>
8314Changes since RC1:
8315  * Fix Bug #14792: Bad md5sum for files with replaced content [dufuz]
8316  * Fix Bug #16057:-r is limited to 4 directories in depth [dufuz]
8317  * Fix Bug #16077: PEAR5::getStaticProperty does not return a reference to the property [dufuz]
8318
8319  Remove custom XML_Util class in favor of using upstream XML_Util package as dependency
8320
8321RC1 Release Notes:
8322  * Fix Bug #14331: pear cvstag only works from inside the package directory [dufuz]
8323  * Fix Bug #16045: E_Notice: Undefined index: channel in PEAR/DependencyDB.php [dufuz]
8324
8325  * Implemented Request #11230: better error message when mirror not in channel.xml file [dufuz]
8326  * Implemented Request #13150: Add support for following HTTP 302 redirects [dufuz]
8327
8328Alpha1 Release Notes:
8329  * Implement Request #10373: if pref_state=stable and installed package=beta, allow up to latest beta version [dufuz]
8330  * Implement Request #10581: login / logout should map to channel-login / channel-logout [dufuz]
8331  * Implement Request #10825: Only display the &quot;invalid or missing package file&quot;-error if it makes sense [dufuz]
8332  * Implement Request #11170: script to generate Command/[command].xml [dufuz]
8333  * Implement Request #11176: improve channel ... has updated its protocols message [dufuz]
8334  * Implement Request #12706: pear list -a hard to read [dufuz]
8335  * Implement Request #11353: upgrade-all and upgrade commands to upgrade within the same stability level [dufuz]
8336  * Implement Request #13015: Add https discovery for channel.xml [dufuz / initial patch by Martin Roos]
8337  * Implement Request #13927: install-pear.php should have option to set www_dir [timj]
8338  * Implement Request #14324: Make the pear install command behave similar to apt-get [dufuz]
8339  * Implement Request #14325: make pear upgrade with no params behave like pear upgrade-all [dufuz]
8340    - upgrade-all can be considered deprecated in favor of calling upgrade with no parameters to replicate
8341      better what other package managers are doing. upgrade-all will still work as intended.
8342  * Implement Request #14504: add a channel parameter support to the upgrade function [dufuz]
8343    - Options -c ezc and --channel=ezc got added to upgrade and upgrade-all to allow for
8344      channel specific upgrades
8345  * Implement Request #14556: install-pear-nozlib.phar should get download_dir config and other options [cweiske]
8346  * Implement Request #15566: Add doc.php.net as a default channel [dufuz / saltybeagle]
8347
8348  * Fix PHP Bug #43857: --program-suffix not always reflected everywhere [cellog]
8349  * Fix PHP Bug #47323: strotime warnings in make install [dufuz]
8350
8351  * Fix Bug #13908: pear info command and maintainers inactive not mentioned [dufuz]
8352  * Fix Bug #13926: install-pear.php does not set cfg_dir if -d option set with no -c option [timj]
8353  * Fix Bug #13943: tests fail when php.exe path contains spaces [dufuz / jorrit]
8354  * Fix Bug #13953: config-set/config-show with channel alias fail [cellog]
8355  * Fix Bug #13958: When a phpt tests exit() or die() xdebug coverage is not generated, patch by izi (David Jean Louis) [izi / dufuz]
8356  * Fix Bug #14041: Unpredictable unit test processing sequence [dufuz]
8357  * Fix Bug #14140: Strict warning not suppressed in the shutdown function [dufuz]
8358  * Fix Bug #14210: pear list -ia brings warnings [dufuz]
8359  * Fix Bug #14274: PEAR packager mangles package.xml encoding, then complains about it [dufuz]
8360  * Fix Bug #14287: cannot upgrade from stable to beta via -beta when config is set to stable [dufuz]
8361  * Fix Bug #14300: Package files themselves can not be served over https [dufuz / initial patch by Martin Roos]
8362  * Fix Bug #14437: openbasedir warning when loading config [dufuz]
8363  * Fix Bug #14558: PackageFile.php creates tmp directory outside configured temp_dir [cweiske]
8364  * Fix Bug #14947: downloadHttp() is missing Host part of the HTTP Request when using Proxy [ifeghali]
8365  * Fix Bug #14977: PEAR/Frontend.php doesn&apos;t require_once PEAR.php [dufuz]
8366  * Fix Bug #15750: Unreachable code in PEAR_Downloader [dufuz]
8367  * Fix Bug #15979: Package files incorrectly removed when splitting a package into multiple pkgs [dufuz]
8368  * Fix Bug #15914: pear upgrade installs different version if desired version not found [dufuz]
8369
8370  NOTE!
8371  Functions that have been deprecated for 3+ years in PEAR_Common, please take a moment
8372  to migrate over to one of the alternatives that have ben provided:
8373  * PEAR_Common-&gt;downloadHttp (use PEAR_Downloader-&gt;downloadHttp instead)
8374  * PEAR_Common-&gt;infoFromTgzFile (use PEAR_PackageFile-&gt;fromTgzFile instead)
8375  * PEAR_Common-&gt;infoFromDescriptionFile (use PEAR_PackageFile-&gt;fromPackageFile instead)
8376  * PEAR_Common-&gt;infoFromString (use PEAR_PackageFile-&gt;fromXmlstring instead)
8377  * PEAR_Common-&gt;infoFromArray (use PEAR_PackageFile-&gt;fromAnyFile instead)
8378  * PEAR_Common-&gt;xmlFromInfo (use a PEAR_PackageFile_v* object&apos;s generator instead)
8379  * PEAR_Common-&gt;validatePackageInfo (use the validation of PEAR_PackageFile objects)
8380  * PEAR_Common-&gt;analyzeSourceCode (use a PEAR_PackageFile_v* object instead)
8381  * PEAR_Common-&gt;detectDependencies (use PEAR_Downloader_Package-&gt;detectDependencies instead)
8382  * PEAR_Common-&gt;buildProvidesArray (use PEAR_PackageFile_v1-&gt;_buildProvidesArray or
8383    PEAR_PackageFile_v2_Validator-&gt;_buildProvidesArray)
8384
8385  PHP 4.4 and 5.1.6 are now the minimum PHP requirements, for brave souls
8386  pear upgrade -f PEAR will allow people with lower versions
8387  to upgrade to this release but no guarantees will be made that it will work properly.
8388
8389  Support for XML RPC channels has been dropped - The only ones that used it
8390  (pear.php.net and pecl.php.net) have used the REST interface for years now.
8391  SOAP support also removed as it was only proof of concept.
8392
8393  Move codebase from the PHP License to New BSD 2 clause license
8394   </notes>
8395  </release>
8396  <release>
8397   <date>2009-04-15</date>
8398   <version>
8399    <release>1.8.1</release>
8400    <api>1.8.1</api>
8401   </version>
8402   <stability>
8403    <release>stable</release>
8404    <api>stable</api>
8405   </stability>
8406   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8407   <notes>
8408* Fix Bug #16099 	PEAR crash on PHP4 (parse error) [dufuz]
8409   </notes>
8410  </release>
8411  <release>
8412   <date>2009-08-18</date>
8413   <version>
8414    <release>1.9.0RC1</release>
8415    <api>1.9.0RC1</api>
8416   </version>
8417   <stability>
8418    <release>beta</release>
8419    <api>stable</api>
8420   </stability>
8421   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8422   <notes>
8423* Implement Request #16213: add alias to list-channels output [dufuz]
8424* Implement Request #16378: pear svntag [dufuz]
8425* Implement Request #16386: PEAR_Config::remove() does not support specifying a channel [timj]
8426* Implement Request #16396: package-dependencies should allow package names [dufuz]
8427
8428* Fix Bug #11181: pear requests channel.xml from main server instead from mirror [dufuz]
8429* Fix Bug #14493: pear install --offline doesn&apos;t print out errors [dufuz]
8430* Fix Bug #11348: pear package-dependencies isn&apos;t well explained [dufuz]
8431* Fix Bug #16108: PEAR_PackageFile_Generator_v2 PHP4 parse error when running upgrade-all [dufuz]
8432* Fix Bug #16113: Installing certain packages fails due incorrect encoding handling [dufuz]
8433* Fix Bug #16122: PEAR RunTest failed to run as expected [dufuz]
8434* Fix Bug #16366: compiling 5.2.10 leads to non-functioning pear [dufuz]
8435* Fix Bug #16387: channel-logout does not support logging out from a non-default channel [timj]
8436* Fix Bug #16444: Setting preferred mirror fails [dufuz]
8437* Fix the shutdown functions where a index might not exist and thus raise a notice [derick]
8438   </notes>
8439  </release>
8440  <release>
8441   <date>2009-08-20</date>
8442   <version>
8443    <release>1.9.0RC2</release>
8444    <api>1.9.0RC2</api>
8445   </version>
8446   <stability>
8447    <release>beta</release>
8448    <api>stable</api>
8449   </stability>
8450   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8451   <notes>
8452* REST 1.4 file was occasionally being included but REST 1.4 is not intended for this release cycle [dufuz]
8453   </notes>
8454  </release>
8455  <release>
8456   <date>2009-08-21</date>
8457   <version>
8458    <release>1.9.0RC3</release>
8459    <api>1.9.0RC3</api>
8460   </version>
8461   <stability>
8462    <release>beta</release>
8463    <api>stable</api>
8464   </stability>
8465   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8466   <notes>
8467* Improved svntag support to handle packages like PEAR it self [dufuz]
8468   </notes>
8469  </release>
8470  <release>
8471   <date>2009-08-23</date>
8472   <version>
8473    <release>1.9.0RC4</release>
8474    <api>1.9.0RC4</api>
8475   </version>
8476   <stability>
8477    <release>beta</release>
8478    <api>stable</api>
8479   </stability>
8480   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8481   <notes>
8482* Fixed a problem where the original channel could not be set as a preferred_mirror again [dufuz]
8483* Make sure channel aliases can&apos;t be made to start with - [dufuz]
8484* Output issues with pear search [dufuz]
8485* Fixed couple of stray notices [dufuz]
8486   </notes>
8487  </release>
8488  <release>
8489   <date>2009-09-03</date>
8490   <version>
8491    <release>1.9.0</release>
8492    <api>1.9.0</api>
8493   </version>
8494   <stability>
8495    <release>stable</release>
8496    <api>stable</api>
8497   </stability>
8498   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8499   <notes>
8500* Fix  Bug #16547: The phar for PEAR installer uses ereg() which is deprecated [dufuz]
8501   </notes>
8502  </release>
8503  <release>
8504   <date>2010-05-26</date>
8505   <version>
8506    <release>1.9.1</release>
8507    <api>1.9.1</api>
8508   </version>
8509   <stability>
8510    <release>stable</release>
8511    <api>stable</api>
8512   </stability>
8513   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8514   <notes>
8515* svntag improvements, tag package files passed into the command and better directory checks [dufuz]
8516* rely on Structures_Graph minimum version instead of recommended version [saltybeagle]
8517* Fix Bug #12613: running go-pear.phar from C:\ fails [dufuz]
8518* Fix Bug #14841: Installing pear into directory with space fails [dufuz]
8519* Fix Bug #16644: pear.bat returns syntax error when parenthesis are in install path. [dufuz] [patch by bwaters (Bryan Waters)]
8520* Fix Bug #16767: Use of Depreciated HTML Attributes in the Exception class [dufuz] [patch by fuhrysteve (Stephen J. Fuhry)]
8521* Fix Bug #16864: &quot;pear list-upgrades -i&quot; issues E_WARNINGS [dufuz] [patch by rquadling (Richard Quadling)]
8522* Fix Bug #17220: command `pear help` outputs to stderr instead of stdout [dufuz]
8523* Fix Bug #17234: channel-discover adds port to HTTP Host header [dufuz]
8524* Fix Bug #17292: Code Coverage in PEAR_RunTest does not work with namespaces [sebastian]
8525* Fix Bug #17359: loadExtension() fails over missing dl() when used in multithread env [dufuz]
8526* Fix Bug #17378: pear info $package fails if directory with that name exists [dufuz]
8527   </notes>
8528  </release>
8529  <release>
8530   <date>2011-02-28</date>
8531   <time>18:30:00</time>
8532   <version>
8533    <release>1.9.2</release>
8534    <api>1.9.2</api>
8535   </version>
8536   <stability>
8537    <release>stable</release>
8538    <api>stable</api>
8539   </stability>
8540   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8541   <notes>
8542Important! This is a security fix release. The advisory can be found at
8543http://pear.php.net/advisory-20110228.txt
8544
8545    Bugs:
8546    * Fixed Bug #17463: Regression: On Windows, svntag [patch by doconnor]
8547    * Fixed Bug #17641: pecl-list doesn&apos;t sort packages by name [dufuz]
8548    * Fixed Bug #17781: invalid argument warning on foreach due to an empty optional dependencie [dufuz]
8549    * Fixed Bug #17801: PEAR run-tests wrongly detects php-cgi [patch by David Jean Louis (izi)]
8550    * Fixed Bug #17839: pear svntag does not tag package.xml file [dufuz]
8551    * Fixed Bug #17986: PEAR Installer cannot handle files moved between packages [dufuz]
8552    * Fixed Bug #17997: Strange output if directories are not writeable [dufuz]
8553    * Fixed Bug #18001: PEAR/RunTest coverage fails [dufuz]
8554    * Fixed Bug #18056 [SECURITY]: Symlink attack in PEAR install [dufuz]
8555    * Fixed Bug #18218: &quot;pear package&quot; does not allow the use of late static binding [dufuz and Christer Edvartsen]
8556    * Fixed Bug #18238: Wrong return code from &quot;pear help&quot; [till]
8557    * Fixed Bug #18308: Broken error message about missing channel validator [yunosh]
8558
8559    This feature is implemented as a result of #18056
8560    * Implemented Request #16648: Use TMPDIR for builds instead of /var/tmp [dufuz]
8561   </notes>
8562  </release>
8563  <release>
8564   <date>2011-06-04</date>
8565   <time>15:30:00</time>
8566   <version>
8567    <release>1.9.3</release>
8568    <api>1.9.2</api>
8569   </version>
8570   <stability>
8571    <release>stable</release>
8572    <api>stable</api>
8573   </stability>
8574   <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
8575   <notes>
8576* Fixed Bug #17744: Empty changelog causes fatal error in setChangelogentry [dufuz]
8577* Fixed Bug #18340: raiseErro typo [doconnor]
8578* Fixed Bug #18349: package.xml version not recognized when single quoted [dufuz]
8579* Fixed Bug #18364: date.timezone errors for sh/bat files when TZ is not set in php.ini [dufuz]
8580* Fixed Bug #18388: Parentheses error in REST.php line 232 [dufuz]
8581* Fixed Bug #18428: invalid preg_match patterns [glen]
8582* Fixed Bug #18486: REST/10.php does not check error condition [dufuz]
8583* Fixed a problem in RunTest and code coverage. Correctly register the
8584  code coverage shutdown function in case we are inside a namespace. [sebastian]
8585* Fixed a bug with extensions not providing their config.m4 and co in the root directory of
8586  their pecl package but rather in a sub directory, such as xhprof. [dufuz]
8587   </notes>
8588  </release>
8589 </changelog>
8590</package>
8591PEAR-1.9.4/OS/Guess.php0000644000076500000240000002463411605156614013365 0ustar  helgistaff<?php
8592/**
8593 * The OS_Guess class
8594 *
8595 * PHP versions 4 and 5
8596 *
8597 * @category   pear
8598 * @package    PEAR
8599 * @author     Stig Bakken <ssb@php.net>
8600 * @author     Gregory Beaver <cellog@php.net>
8601 * @copyright  1997-2009 The Authors
8602 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
8603 * @version    CVS: $Id: Guess.php 313023 2011-07-06 19:17:11Z dufuz $
8604 * @link       http://pear.php.net/package/PEAR
8605 * @since      File available since PEAR 0.1
8606 */
8607
8608// {{{ uname examples
8609
8610// php_uname() without args returns the same as 'uname -a', or a PHP-custom
8611// string for Windows.
8612// PHP versions prior to 4.3 return the uname of the host where PHP was built,
8613// as of 4.3 it returns the uname of the host running the PHP code.
8614//
8615// PC RedHat Linux 7.1:
8616// Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown
8617//
8618// PC Debian Potato:
8619// Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown
8620//
8621// PC FreeBSD 3.3:
8622// FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000     root@example.com:/usr/src/sys/compile/CONFIG  i386
8623//
8624// PC FreeBSD 4.3:
8625// FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001     root@example.com:/usr/src/sys/compile/CONFIG  i386
8626//
8627// PC FreeBSD 4.5:
8628// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb  6 23:59:23 CET 2002     root@example.com:/usr/src/sys/compile/CONFIG  i386
8629//
8630// PC FreeBSD 4.5 w/uname from GNU shellutils:
8631// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb  i386 unknown
8632//
8633// HP 9000/712 HP-UX 10:
8634// HP-UX iq B.10.10 A 9000/712 2008429113 two-user license
8635//
8636// HP 9000/712 HP-UX 10 w/uname from GNU shellutils:
8637// HP-UX host B.10.10 A 9000/712 unknown
8638//
8639// IBM RS6000/550 AIX 4.3:
8640// AIX host 3 4 000003531C00
8641//
8642// AIX 4.3 w/uname from GNU shellutils:
8643// AIX host 3 4 000003531C00 unknown
8644//
8645// SGI Onyx IRIX 6.5 w/uname from GNU shellutils:
8646// IRIX64 host 6.5 01091820 IP19 mips
8647//
8648// SGI Onyx IRIX 6.5:
8649// IRIX64 host 6.5 01091820 IP19
8650//
8651// SparcStation 20 Solaris 8 w/uname from GNU shellutils:
8652// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc
8653//
8654// SparcStation 20 Solaris 8:
8655// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20
8656//
8657// Mac OS X (Darwin)
8658// Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug  5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC  Power Macintosh
8659//
8660// Mac OS X early versions
8661//
8662
8663// }}}
8664
8665/* TODO:
8666 * - define endianness, to allow matchSignature("bigend") etc.
8667 */
8668
8669/**
8670 * Retrieves information about the current operating system
8671 *
8672 * This class uses php_uname() to grok information about the current OS
8673 *
8674 * @category   pear
8675 * @package    PEAR
8676 * @author     Stig Bakken <ssb@php.net>
8677 * @author     Gregory Beaver <cellog@php.net>
8678 * @copyright  1997-2009 The Authors
8679 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
8680 * @version    Release: 1.9.4
8681 * @link       http://pear.php.net/package/PEAR
8682 * @since      Class available since Release 0.1
8683 */
8684class OS_Guess
8685{
8686    var $sysname;
8687    var $nodename;
8688    var $cpu;
8689    var $release;
8690    var $extra;
8691
8692    function OS_Guess($uname = null)
8693    {
8694        list($this->sysname,
8695             $this->release,
8696             $this->cpu,
8697             $this->extra,
8698             $this->nodename) = $this->parseSignature($uname);
8699    }
8700
8701    function parseSignature($uname = null)
8702    {
8703        static $sysmap = array(
8704            'HP-UX' => 'hpux',
8705            'IRIX64' => 'irix',
8706        );
8707        static $cpumap = array(
8708            'i586' => 'i386',
8709            'i686' => 'i386',
8710            'ppc' => 'powerpc',
8711        );
8712        if ($uname === null) {
8713            $uname = php_uname();
8714        }
8715        $parts = preg_split('/\s+/', trim($uname));
8716        $n = count($parts);
8717
8718        $release  = $machine = $cpu = '';
8719        $sysname  = $parts[0];
8720        $nodename = $parts[1];
8721        $cpu      = $parts[$n-1];
8722        $extra = '';
8723        if ($cpu == 'unknown') {
8724            $cpu = $parts[$n - 2];
8725        }
8726
8727        switch ($sysname) {
8728            case 'AIX' :
8729                $release = "$parts[3].$parts[2]";
8730                break;
8731            case 'Windows' :
8732                switch ($parts[1]) {
8733                    case '95/98':
8734                        $release = '9x';
8735                        break;
8736                    default:
8737                        $release = $parts[1];
8738                        break;
8739                }
8740                $cpu = 'i386';
8741                break;
8742            case 'Linux' :
8743                $extra = $this->_detectGlibcVersion();
8744                // use only the first two digits from the kernel version
8745                $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]);
8746                break;
8747            case 'Mac' :
8748                $sysname = 'darwin';
8749                $nodename = $parts[2];
8750                $release = $parts[3];
8751                if ($cpu == 'Macintosh') {
8752                    if ($parts[$n - 2] == 'Power') {
8753                        $cpu = 'powerpc';
8754                    }
8755                }
8756                break;
8757            case 'Darwin' :
8758                if ($cpu == 'Macintosh') {
8759                    if ($parts[$n - 2] == 'Power') {
8760                        $cpu = 'powerpc';
8761                    }
8762                }
8763                $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]);
8764                break;
8765            default:
8766                $release = preg_replace('/-.*/', '', $parts[2]);
8767                break;
8768        }
8769
8770        if (isset($sysmap[$sysname])) {
8771            $sysname = $sysmap[$sysname];
8772        } else {
8773            $sysname = strtolower($sysname);
8774        }
8775        if (isset($cpumap[$cpu])) {
8776            $cpu = $cpumap[$cpu];
8777        }
8778        return array($sysname, $release, $cpu, $extra, $nodename);
8779    }
8780
8781    function _detectGlibcVersion()
8782    {
8783        static $glibc = false;
8784        if ($glibc !== false) {
8785            return $glibc; // no need to run this multiple times
8786        }
8787        $major = $minor = 0;
8788        include_once "System.php";
8789        // Use glibc's <features.h> header file to
8790        // get major and minor version number:
8791        if (@file_exists('/usr/include/features.h') &&
8792              @is_readable('/usr/include/features.h')) {
8793            if (!@file_exists('/usr/bin/cpp') || !@is_executable('/usr/bin/cpp')) {
8794                $features_file = fopen('/usr/include/features.h', 'rb');
8795                while (!feof($features_file)) {
8796                    $line = fgets($features_file, 8192);
8797                    if (!$line || (strpos($line, '#define') === false)) {
8798                        continue;
8799                    }
8800                    if (strpos($line, '__GLIBC__')) {
8801                        // major version number #define __GLIBC__ version
8802                        $line = preg_split('/\s+/', $line);
8803                        $glibc_major = trim($line[2]);
8804                        if (isset($glibc_minor)) {
8805                            break;
8806                        }
8807                        continue;
8808                    }
8809
8810                    if (strpos($line, '__GLIBC_MINOR__'))  {
8811                        // got the minor version number
8812                        // #define __GLIBC_MINOR__ version
8813                        $line = preg_split('/\s+/', $line);
8814                        $glibc_minor = trim($line[2]);
8815                        if (isset($glibc_major)) {
8816                            break;
8817                        }
8818                        continue;
8819                    }
8820                }
8821                fclose($features_file);
8822                if (!isset($glibc_major) || !isset($glibc_minor)) {
8823                    return $glibc = '';
8824                }
8825                return $glibc = 'glibc' . trim($glibc_major) . "." . trim($glibc_minor) ;
8826            } // no cpp
8827
8828            $tmpfile = System::mktemp("glibctest");
8829            $fp = fopen($tmpfile, "w");
8830            fwrite($fp, "#include <features.h>\n__GLIBC__ __GLIBC_MINOR__\n");
8831            fclose($fp);
8832            $cpp = popen("/usr/bin/cpp $tmpfile", "r");
8833            while ($line = fgets($cpp, 1024)) {
8834                if ($line{0} == '#' || trim($line) == '') {
8835                    continue;
8836                }
8837
8838                if (list($major, $minor) = explode(' ', trim($line))) {
8839                    break;
8840                }
8841            }
8842            pclose($cpp);
8843            unlink($tmpfile);
8844        } // features.h
8845
8846        if (!($major && $minor) && @is_link('/lib/libc.so.6')) {
8847            // Let's try reading the libc.so.6 symlink
8848            if (preg_match('/^libc-(.*)\.so$/', basename(readlink('/lib/libc.so.6')), $matches)) {
8849                list($major, $minor) = explode('.', $matches[1]);
8850            }
8851        }
8852
8853        if (!($major && $minor)) {
8854            return $glibc = '';
8855        }
8856
8857        return $glibc = "glibc{$major}.{$minor}";
8858    }
8859
8860    function getSignature()
8861    {
8862        if (empty($this->extra)) {
8863            return "{$this->sysname}-{$this->release}-{$this->cpu}";
8864        }
8865        return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}";
8866    }
8867
8868    function getSysname()
8869    {
8870        return $this->sysname;
8871    }
8872
8873    function getNodename()
8874    {
8875        return $this->nodename;
8876    }
8877
8878    function getCpu()
8879    {
8880        return $this->cpu;
8881    }
8882
8883    function getRelease()
8884    {
8885        return $this->release;
8886    }
8887
8888    function getExtra()
8889    {
8890        return $this->extra;
8891    }
8892
8893    function matchSignature($match)
8894    {
8895        $fragments = is_array($match) ? $match : explode('-', $match);
8896        $n = count($fragments);
8897        $matches = 0;
8898        if ($n > 0) {
8899            $matches += $this->_matchFragment($fragments[0], $this->sysname);
8900        }
8901        if ($n > 1) {
8902            $matches += $this->_matchFragment($fragments[1], $this->release);
8903        }
8904        if ($n > 2) {
8905            $matches += $this->_matchFragment($fragments[2], $this->cpu);
8906        }
8907        if ($n > 3) {
8908            $matches += $this->_matchFragment($fragments[3], $this->extra);
8909        }
8910        return ($matches == $n);
8911    }
8912
8913    function _matchFragment($fragment, $value)
8914    {
8915        if (strcspn($fragment, '*?') < strlen($fragment)) {
8916            $reg = '/^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '\\z/';
8917            return preg_match($reg, $value);
8918        }
8919        return ($fragment == '*' || !strcasecmp($fragment, $value));
8920    }
8921
8922}
8923/*
8924 * Local Variables:
8925 * indent-tabs-mode: nil
8926 * c-basic-offset: 4
8927 * End:
8928 */PEAR-1.9.4/PEAR/ChannelFile/Parser.php0000644000076500000240000000336211605156614016104 0ustar  helgistaff<?php
8929/**
8930 * PEAR_ChannelFile_Parser for parsing channel.xml
8931 *
8932 * PHP versions 4 and 5
8933 *
8934 * @category   pear
8935 * @package    PEAR
8936 * @author     Greg Beaver <cellog@php.net>
8937 * @copyright  1997-2009 The Authors
8938 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
8939 * @version    CVS: $Id: Parser.php 313023 2011-07-06 19:17:11Z dufuz $
8940 * @link       http://pear.php.net/package/PEAR
8941 * @since      File available since Release 1.4.0a1
8942 */
8943
8944/**
8945 * base xml parser class
8946 */
8947require_once 'PEAR/XMLParser.php';
8948require_once 'PEAR/ChannelFile.php';
8949/**
8950 * Parser for channel.xml
8951 * @category   pear
8952 * @package    PEAR
8953 * @author     Greg Beaver <cellog@php.net>
8954 * @copyright  1997-2009 The Authors
8955 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
8956 * @version    Release: 1.9.4
8957 * @link       http://pear.php.net/package/PEAR
8958 * @since      Class available since Release 1.4.0a1
8959 */
8960class PEAR_ChannelFile_Parser extends PEAR_XMLParser
8961{
8962    var $_config;
8963    var $_logger;
8964    var $_registry;
8965
8966    function setConfig(&$c)
8967    {
8968        $this->_config = &$c;
8969        $this->_registry = &$c->getRegistry();
8970    }
8971
8972    function setLogger(&$l)
8973    {
8974        $this->_logger = &$l;
8975    }
8976
8977    function parse($data, $file)
8978    {
8979        if (PEAR::isError($err = parent::parse($data, $file))) {
8980            return $err;
8981        }
8982
8983        $ret = new PEAR_ChannelFile;
8984        $ret->setConfig($this->_config);
8985        if (isset($this->_logger)) {
8986            $ret->setLogger($this->_logger);
8987        }
8988
8989        $ret->fromArray($this->_unserializedData);
8990        // make sure the filelist is in the easy to read format needed
8991        $ret->flattenFilelist();
8992        $ret->setPackagefile($file, $archive);
8993        return $ret;
8994    }
8995}PEAR-1.9.4/PEAR/Command/Auth.xml0000644000076500000240000000231411605156614014764 0ustar  helgistaff<commands version="1.0">
8996 <login>
8997  <summary>Connects and authenticates to remote server [Deprecated in favor of channel-login]</summary>
8998  <function>doLogin</function>
8999  <shortcut>li</shortcut>
9000  <options />
9001  <doc>&lt;channel name&gt;
9002WARNING: This function is deprecated in favor of using channel-login
9003
9004Log in to a remote channel server.  If &lt;channel name&gt; is not supplied,
9005the default channel is used. To use remote functions in the installer
9006that require any kind of privileges, you need to log in first.  The
9007username and password you enter here will be stored in your per-user
9008PEAR configuration (~/.pearrc on Unix-like systems).  After logging
9009in, your username and password will be sent along in subsequent
9010operations on the remote server.</doc>
9011 </login>
9012 <logout>
9013  <summary>Logs out from the remote server [Deprecated in favor of channel-logout]</summary>
9014  <function>doLogout</function>
9015  <shortcut>lo</shortcut>
9016  <options />
9017  <doc>
9018WARNING: This function is deprecated in favor of using channel-logout
9019
9020Logs out from the remote server.  This command does not actually
9021connect to the remote server, it only deletes the stored username and
9022password from your user configuration.</doc>
9023 </logout>
9024</commands>PEAR-1.9.4/PEAR/Command/Auth.php0000644000076500000240000000513611605156614014760 0ustar  helgistaff<?php
9025/**
9026 * PEAR_Command_Auth (login, logout commands)
9027 *
9028 * PHP versions 4 and 5
9029 *
9030 * @category   pear
9031 * @package    PEAR
9032 * @author     Stig Bakken <ssb@php.net>
9033 * @author     Greg Beaver <cellog@php.net>
9034 * @copyright  1997-2009 The Authors
9035 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
9036 * @version    CVS: $Id: Auth.php 313023 2011-07-06 19:17:11Z dufuz $
9037 * @link       http://pear.php.net/package/PEAR
9038 * @since      File available since Release 0.1
9039 * @deprecated since 1.8.0alpha1
9040 */
9041
9042/**
9043 * base class
9044 */
9045require_once 'PEAR/Command/Channels.php';
9046
9047/**
9048 * PEAR commands for login/logout
9049 *
9050 * @category   pear
9051 * @package    PEAR
9052 * @author     Stig Bakken <ssb@php.net>
9053 * @author     Greg Beaver <cellog@php.net>
9054 * @copyright  1997-2009 The Authors
9055 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
9056 * @version    Release: 1.9.4
9057 * @link       http://pear.php.net/package/PEAR
9058 * @since      Class available since Release 0.1
9059 * @deprecated since 1.8.0alpha1
9060 */
9061class PEAR_Command_Auth extends PEAR_Command_Channels
9062{
9063    var $commands = array(
9064        'login' => array(
9065            'summary' => 'Connects and authenticates to remote server [Deprecated in favor of channel-login]',
9066            'shortcut' => 'li',
9067            'function' => 'doLogin',
9068            'options' => array(),
9069            'doc' => '<channel name>
9070WARNING: This function is deprecated in favor of using channel-login
9071
9072Log in to a remote channel server.  If <channel name> is not supplied,
9073the default channel is used. To use remote functions in the installer
9074that require any kind of privileges, you need to log in first.  The
9075username and password you enter here will be stored in your per-user
9076PEAR configuration (~/.pearrc on Unix-like systems).  After logging
9077in, your username and password will be sent along in subsequent
9078operations on the remote server.',
9079            ),
9080        'logout' => array(
9081            'summary' => 'Logs out from the remote server [Deprecated in favor of channel-logout]',
9082            'shortcut' => 'lo',
9083            'function' => 'doLogout',
9084            'options' => array(),
9085            'doc' => '
9086WARNING: This function is deprecated in favor of using channel-logout
9087
9088Logs out from the remote server.  This command does not actually
9089connect to the remote server, it only deletes the stored username and
9090password from your user configuration.',
9091            )
9092
9093        );
9094
9095    /**
9096     * PEAR_Command_Auth constructor.
9097     *
9098     * @access public
9099     */
9100    function PEAR_Command_Auth(&$ui, &$config)
9101    {
9102        parent::PEAR_Command_Channels($ui, $config);
9103    }
9104}PEAR-1.9.4/PEAR/Command/Build.xml0000644000076500000240000000040411605156614015120 0ustar  helgistaff<commands version="1.0">
9105 <build>
9106  <summary>Build an Extension From C Source</summary>
9107  <function>doBuild</function>
9108  <shortcut>b</shortcut>
9109  <options />
9110  <doc>[package.xml]
9111Builds one or more extensions contained in a package.</doc>
9112 </build>
9113</commands>PEAR-1.9.4/PEAR/Command/Build.php0000644000076500000240000000445311605156614015117 0ustar  helgistaff<?php
9114/**
9115 * PEAR_Command_Auth (build command)
9116 *
9117 * PHP versions 4 and 5
9118 *
9119 * @category   pear
9120 * @package    PEAR
9121 * @author     Stig Bakken <ssb@php.net>
9122 * @author     Tomas V.V.Cox <cox@idecnet.com>
9123 * @author     Greg Beaver <cellog@php.net>
9124 * @copyright  1997-2009 The Authors
9125 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
9126 * @version    CVS: $Id: Build.php 313023 2011-07-06 19:17:11Z dufuz $
9127 * @link       http://pear.php.net/package/PEAR
9128 * @since      File available since Release 0.1
9129 */
9130
9131/**
9132 * base class
9133 */
9134require_once 'PEAR/Command/Common.php';
9135
9136/**
9137 * PEAR commands for building extensions.
9138 *
9139 * @category   pear
9140 * @package    PEAR
9141 * @author     Stig Bakken <ssb@php.net>
9142 * @author     Tomas V.V.Cox <cox@idecnet.com>
9143 * @author     Greg Beaver <cellog@php.net>
9144 * @copyright  1997-2009 The Authors
9145 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
9146 * @version    Release: 1.9.4
9147 * @link       http://pear.php.net/package/PEAR
9148 * @since      Class available since Release 0.1
9149 */
9150class PEAR_Command_Build extends PEAR_Command_Common
9151{
9152    var $commands = array(
9153        'build' => array(
9154            'summary' => 'Build an Extension From C Source',
9155            'function' => 'doBuild',
9156            'shortcut' => 'b',
9157            'options' => array(),
9158            'doc' => '[package.xml]
9159Builds one or more extensions contained in a package.'
9160            ),
9161        );
9162
9163    /**
9164     * PEAR_Command_Build constructor.
9165     *
9166     * @access public
9167     */
9168    function PEAR_Command_Build(&$ui, &$config)
9169    {
9170        parent::PEAR_Command_Common($ui, $config);
9171    }
9172
9173    function doBuild($command, $options, $params)
9174    {
9175        require_once 'PEAR/Builder.php';
9176        if (sizeof($params) < 1) {
9177            $params[0] = 'package.xml';
9178        }
9179
9180        $builder = &new PEAR_Builder($this->ui);
9181        $this->debug = $this->config->get('verbose');
9182        $err = $builder->build($params[0], array(&$this, 'buildCallback'));
9183        if (PEAR::isError($err)) {
9184            return $err;
9185        }
9186
9187        return true;
9188    }
9189
9190    function buildCallback($what, $data)
9191    {
9192        if (($what == 'cmdoutput' && $this->debug > 1) ||
9193            ($what == 'output' && $this->debug > 0)) {
9194            $this->ui->outputData(rtrim($data), 'build');
9195        }
9196    }
9197}PEAR-1.9.4/PEAR/Command/Channels.xml0000644000076500000240000001017211605156614015617 0ustar  helgistaff<commands version="1.0">
9198 <list-channels>
9199  <summary>List Available Channels</summary>
9200  <function>doList</function>
9201  <shortcut>lc</shortcut>
9202  <options />
9203  <doc>
9204List all available channels for installation.
9205</doc>
9206 </list-channels>
9207 <update-channels>
9208  <summary>Update the Channel List</summary>
9209  <function>doUpdateAll</function>
9210  <shortcut>uc</shortcut>
9211  <options />
9212  <doc>
9213List all installed packages in all channels.
9214</doc>
9215 </update-channels>
9216 <channel-delete>
9217  <summary>Remove a Channel From the List</summary>
9218  <function>doDelete</function>
9219  <shortcut>cde</shortcut>
9220  <options />
9221  <doc>&lt;channel name&gt;
9222Delete a channel from the registry.  You may not
9223remove any channel that has installed packages.
9224</doc>
9225 </channel-delete>
9226 <channel-add>
9227  <summary>Add a Channel</summary>
9228  <function>doAdd</function>
9229  <shortcut>ca</shortcut>
9230  <options />
9231  <doc>&lt;channel.xml&gt;
9232Add a private channel to the channel list.  Note that all
9233public channels should be synced using &quot;update-channels&quot;.
9234Parameter may be either a local file or remote URL to a
9235channel.xml.
9236</doc>
9237 </channel-add>
9238 <channel-update>
9239  <summary>Update an Existing Channel</summary>
9240  <function>doUpdate</function>
9241  <shortcut>cu</shortcut>
9242  <options>
9243   <force>
9244    <shortopt>f</shortopt>
9245    <doc>will force download of new channel.xml if an existing channel name is used</doc>
9246   </force>
9247   <channel>
9248    <shortopt>c</shortopt>
9249    <doc>will force download of new channel.xml if an existing channel name is used</doc>
9250    <arg>CHANNEL</arg>
9251   </channel>
9252  </options>
9253  <doc>[&lt;channel.xml&gt;|&lt;channel name&gt;]
9254Update a channel in the channel list directly.  Note that all
9255public channels can be synced using &quot;update-channels&quot;.
9256Parameter may be a local or remote channel.xml, or the name of
9257an existing channel.
9258</doc>
9259 </channel-update>
9260 <channel-info>
9261  <summary>Retrieve Information on a Channel</summary>
9262  <function>doInfo</function>
9263  <shortcut>ci</shortcut>
9264  <options />
9265  <doc>&lt;package&gt;
9266List the files in an installed package.
9267</doc>
9268 </channel-info>
9269 <channel-alias>
9270  <summary>Specify an alias to a channel name</summary>
9271  <function>doAlias</function>
9272  <shortcut>cha</shortcut>
9273  <options />
9274  <doc>&lt;channel&gt; &lt;alias&gt;
9275Specify a specific alias to use for a channel name.
9276The alias may not be an existing channel name or
9277alias.
9278</doc>
9279 </channel-alias>
9280 <channel-discover>
9281  <summary>Initialize a Channel from its server</summary>
9282  <function>doDiscover</function>
9283  <shortcut>di</shortcut>
9284  <options />
9285  <doc>[&lt;channel.xml&gt;|&lt;channel name&gt;]
9286Initialize a channel from its server and create a local channel.xml.
9287If &lt;channel name&gt; is in the format &quot;&lt;username&gt;:&lt;password&gt;@&lt;channel&gt;&quot; then
9288&lt;username&gt; and &lt;password&gt; will be set as the login username/password for
9289&lt;channel&gt;. Use caution when passing the username/password in this way, as
9290it may allow other users on your computer to briefly view your username/
9291password via the system&#039;s process list.
9292</doc>
9293 </channel-discover>
9294 <channel-login>
9295  <summary>Connects and authenticates to remote channel server</summary>
9296  <function>doLogin</function>
9297  <shortcut>cli</shortcut>
9298  <options />
9299  <doc>&lt;channel name&gt;
9300Log in to a remote channel server.  If &lt;channel name&gt; is not supplied,
9301the default channel is used. To use remote functions in the installer
9302that require any kind of privileges, you need to log in first.  The
9303username and password you enter here will be stored in your per-user
9304PEAR configuration (~/.pearrc on Unix-like systems).  After logging
9305in, your username and password will be sent along in subsequent
9306operations on the remote server.</doc>
9307 </channel-login>
9308 <channel-logout>
9309  <summary>Logs out from the remote channel server</summary>
9310  <function>doLogout</function>
9311  <shortcut>clo</shortcut>
9312  <options />
9313  <doc>&lt;channel name&gt;
9314Logs out from a remote channel server.  If &lt;channel name&gt; is not supplied,
9315the default channel is used. This command does not actually connect to the
9316remote server, it only deletes the stored username and password from your user
9317configuration.</doc>
9318 </channel-logout>
9319</commands>PEAR-1.9.4/PEAR/Command/Channels.php0000644000076500000240000010137711605156614015616 0ustar  helgistaff<?php
9320// /* vim: set expandtab tabstop=4 shiftwidth=4: */
9321/**
9322 * PEAR_Command_Channels (list-channels, update-channels, channel-delete, channel-add,
9323 * channel-update, channel-info, channel-alias, channel-discover commands)
9324 *
9325 * PHP versions 4 and 5
9326 *
9327 * @category   pear
9328 * @package    PEAR
9329 * @author     Stig Bakken <ssb@php.net>
9330 * @author     Greg Beaver <cellog@php.net>
9331 * @copyright  1997-2009 The Authors
9332 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
9333 * @version    CVS: $Id: Channels.php 313023 2011-07-06 19:17:11Z dufuz $
9334 * @link       http://pear.php.net/package/PEAR
9335 * @since      File available since Release 1.4.0a1
9336 */
9337
9338/**
9339 * base class
9340 */
9341require_once 'PEAR/Command/Common.php';
9342
9343define('PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS', -500);
9344
9345/**
9346 * PEAR commands for managing channels.
9347 *
9348 * @category   pear
9349 * @package    PEAR
9350 * @author     Greg Beaver <cellog@php.net>
9351 * @copyright  1997-2009 The Authors
9352 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
9353 * @version    Release: 1.9.4
9354 * @link       http://pear.php.net/package/PEAR
9355 * @since      Class available since Release 1.4.0a1
9356 */
9357class PEAR_Command_Channels extends PEAR_Command_Common
9358{
9359    var $commands = array(
9360        'list-channels' => array(
9361            'summary' => 'List Available Channels',
9362            'function' => 'doList',
9363            'shortcut' => 'lc',
9364            'options' => array(),
9365            'doc' => '
9366List all available channels for installation.
9367',
9368            ),
9369        'update-channels' => array(
9370            'summary' => 'Update the Channel List',
9371            'function' => 'doUpdateAll',
9372            'shortcut' => 'uc',
9373            'options' => array(),
9374            'doc' => '
9375List all installed packages in all channels.
9376'
9377            ),
9378        'channel-delete' => array(
9379            'summary' => 'Remove a Channel From the List',
9380            'function' => 'doDelete',
9381            'shortcut' => 'cde',
9382            'options' => array(),
9383            'doc' => '<channel name>
9384Delete a channel from the registry.  You may not
9385remove any channel that has installed packages.
9386'
9387            ),
9388        'channel-add' => array(
9389            'summary' => 'Add a Channel',
9390            'function' => 'doAdd',
9391            'shortcut' => 'ca',
9392            'options' => array(),
9393            'doc' => '<channel.xml>
9394Add a private channel to the channel list.  Note that all
9395public channels should be synced using "update-channels".
9396Parameter may be either a local file or remote URL to a
9397channel.xml.
9398'
9399            ),
9400        'channel-update' => array(
9401            'summary' => 'Update an Existing Channel',
9402            'function' => 'doUpdate',
9403            'shortcut' => 'cu',
9404            'options' => array(
9405                'force' => array(
9406                    'shortopt' => 'f',
9407                    'doc' => 'will force download of new channel.xml if an existing channel name is used',
9408                    ),
9409                'channel' => array(
9410                    'shortopt' => 'c',
9411                    'arg' => 'CHANNEL',
9412                    'doc' => 'will force download of new channel.xml if an existing channel name is used',
9413                    ),
9414),
9415            'doc' => '[<channel.xml>|<channel name>]
9416Update a channel in the channel list directly.  Note that all
9417public channels can be synced using "update-channels".
9418Parameter may be a local or remote channel.xml, or the name of
9419an existing channel.
9420'
9421            ),
9422        'channel-info' => array(
9423            'summary' => 'Retrieve Information on a Channel',
9424            'function' => 'doInfo',
9425            'shortcut' => 'ci',
9426            'options' => array(),
9427            'doc' => '<package>
9428List the files in an installed package.
9429'
9430            ),
9431        'channel-alias' => array(
9432            'summary' => 'Specify an alias to a channel name',
9433            'function' => 'doAlias',
9434            'shortcut' => 'cha',
9435            'options' => array(),
9436            'doc' => '<channel> <alias>
9437Specify a specific alias to use for a channel name.
9438The alias may not be an existing channel name or
9439alias.
9440'
9441            ),
9442        'channel-discover' => array(
9443            'summary' => 'Initialize a Channel from its server',
9444            'function' => 'doDiscover',
9445            'shortcut' => 'di',
9446            'options' => array(),
9447            'doc' => '[<channel.xml>|<channel name>]
9448Initialize a channel from its server and create a local channel.xml.
9449If <channel name> is in the format "<username>:<password>@<channel>" then
9450<username> and <password> will be set as the login username/password for
9451<channel>. Use caution when passing the username/password in this way, as
9452it may allow other users on your computer to briefly view your username/
9453password via the system\'s process list.
9454'
9455            ),
9456        'channel-login' => array(
9457            'summary' => 'Connects and authenticates to remote channel server',
9458            'shortcut' => 'cli',
9459            'function' => 'doLogin',
9460            'options' => array(),
9461            'doc' => '<channel name>
9462Log in to a remote channel server.  If <channel name> is not supplied,
9463the default channel is used. To use remote functions in the installer
9464that require any kind of privileges, you need to log in first.  The
9465username and password you enter here will be stored in your per-user
9466PEAR configuration (~/.pearrc on Unix-like systems).  After logging
9467in, your username and password will be sent along in subsequent
9468operations on the remote server.',
9469            ),
9470        'channel-logout' => array(
9471            'summary' => 'Logs out from the remote channel server',
9472            'shortcut' => 'clo',
9473            'function' => 'doLogout',
9474            'options' => array(),
9475            'doc' => '<channel name>
9476Logs out from a remote channel server.  If <channel name> is not supplied,
9477the default channel is used. This command does not actually connect to the
9478remote server, it only deletes the stored username and password from your user
9479configuration.',
9480            ),
9481        );
9482
9483    /**
9484     * PEAR_Command_Registry constructor.
9485     *
9486     * @access public
9487     */
9488    function PEAR_Command_Channels(&$ui, &$config)
9489    {
9490        parent::PEAR_Command_Common($ui, $config);
9491    }
9492
9493    function _sortChannels($a, $b)
9494    {
9495        return strnatcasecmp($a->getName(), $b->getName());
9496    }
9497
9498    function doList($command, $options, $params)
9499    {
9500        $reg = &$this->config->getRegistry();
9501        $registered = $reg->getChannels();
9502        usort($registered, array(&$this, '_sortchannels'));
9503        $i = $j = 0;
9504        $data = array(
9505            'caption' => 'Registered Channels:',
9506            'border' => true,
9507            'headline' => array('Channel', 'Alias', 'Summary')
9508            );
9509        foreach ($registered as $channel) {
9510            $data['data'][] = array($channel->getName(),
9511                                    $channel->getAlias(),
9512                                    $channel->getSummary());
9513        }
9514
9515        if (count($registered) === 0) {
9516            $data = '(no registered channels)';
9517        }
9518        $this->ui->outputData($data, $command);
9519        return true;
9520    }
9521
9522    function doUpdateAll($command, $options, $params)
9523    {
9524        $reg = &$this->config->getRegistry();
9525        $channels = $reg->getChannels();
9526
9527        $success = true;
9528        foreach ($channels as $channel) {
9529            if ($channel->getName() != '__uri') {
9530                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
9531                $err = $this->doUpdate('channel-update',
9532                                          $options,
9533                                          array($channel->getName()));
9534                if (PEAR::isError($err)) {
9535                    $this->ui->outputData($err->getMessage(), $command);
9536                    $success = false;
9537                } else {
9538                    $success &= $err;
9539                }
9540            }
9541        }
9542        return $success;
9543    }
9544
9545    function doInfo($command, $options, $params)
9546    {
9547        if (count($params) !== 1) {
9548            return $this->raiseError("No channel specified");
9549        }
9550
9551        $reg     = &$this->config->getRegistry();
9552        $channel = strtolower($params[0]);
9553        if ($reg->channelExists($channel)) {
9554            $chan = $reg->getChannel($channel);
9555            if (PEAR::isError($chan)) {
9556                return $this->raiseError($chan);
9557            }
9558        } else {
9559            if (strpos($channel, '://')) {
9560                $downloader = &$this->getDownloader();
9561                $tmpdir = $this->config->get('temp_dir');
9562                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
9563                $loc = $downloader->downloadHttp($channel, $this->ui, $tmpdir);
9564                PEAR::staticPopErrorHandling();
9565                if (PEAR::isError($loc)) {
9566                    return $this->raiseError('Cannot open "' . $channel .
9567                        '" (' . $loc->getMessage() . ')');
9568                } else {
9569                    $contents = implode('', file($loc));
9570                }
9571            } else {
9572                if (!file_exists($params[0])) {
9573                    return $this->raiseError('Unknown channel "' . $channel . '"');
9574                }
9575
9576                $fp = fopen($params[0], 'r');
9577                if (!$fp) {
9578                    return $this->raiseError('Cannot open "' . $params[0] . '"');
9579                }
9580
9581                $contents = '';
9582                while (!feof($fp)) {
9583                    $contents .= fread($fp, 1024);
9584                }
9585                fclose($fp);
9586            }
9587
9588            if (!class_exists('PEAR_ChannelFile')) {
9589                require_once 'PEAR/ChannelFile.php';
9590            }
9591
9592            $chan = new PEAR_ChannelFile;
9593            $chan->fromXmlString($contents);
9594            $chan->validate();
9595            if ($errs = $chan->getErrors(true)) {
9596                foreach ($errs as $err) {
9597                    $this->ui->outputData($err['level'] . ': ' . $err['message']);
9598                }
9599                return $this->raiseError('Channel file "' . $params[0] . '" is not valid');
9600            }
9601        }
9602
9603        if (!$chan) {
9604            return $this->raiseError('Serious error: Channel "' . $params[0] .
9605                '" has a corrupted registry entry');
9606        }
9607
9608        $channel = $chan->getName();
9609        $caption = 'Channel ' . $channel . ' Information:';
9610        $data1 = array(
9611            'caption' => $caption,
9612            'border' => true);
9613        $data1['data']['server'] = array('Name and Server', $chan->getName());
9614        if ($chan->getAlias() != $chan->getName()) {
9615            $data1['data']['alias'] = array('Alias', $chan->getAlias());
9616        }
9617
9618        $data1['data']['summary'] = array('Summary', $chan->getSummary());
9619        $validate = $chan->getValidationPackage();
9620        $data1['data']['vpackage'] = array('Validation Package Name', $validate['_content']);
9621        $data1['data']['vpackageversion'] =
9622            array('Validation Package Version', $validate['attribs']['version']);
9623        $d = array();
9624        $d['main'] = $data1;
9625
9626        $data['data'] = array();
9627        $data['caption'] = 'Server Capabilities';
9628        $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
9629        if ($chan->supportsREST()) {
9630            if ($chan->supportsREST()) {
9631                $funcs = $chan->getFunctions('rest');
9632                if (!isset($funcs[0])) {
9633                    $funcs = array($funcs);
9634                }
9635                foreach ($funcs as $protocol) {
9636                    $data['data'][] = array('rest', $protocol['attribs']['type'],
9637                        $protocol['_content']);
9638                }
9639            }
9640        } else {
9641            $data['data'][] = array('No supported protocols');
9642        }
9643
9644        $d['protocols'] = $data;
9645        $data['data'] = array();
9646        $mirrors = $chan->getMirrors();
9647        if ($mirrors) {
9648            $data['caption'] = 'Channel ' . $channel . ' Mirrors:';
9649            unset($data['headline']);
9650            foreach ($mirrors as $mirror) {
9651                $data['data'][] = array($mirror['attribs']['host']);
9652                $d['mirrors'] = $data;
9653            }
9654
9655            foreach ($mirrors as $i => $mirror) {
9656                $data['data'] = array();
9657                $data['caption'] = 'Mirror ' . $mirror['attribs']['host'] . ' Capabilities';
9658                $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
9659                if ($chan->supportsREST($mirror['attribs']['host'])) {
9660                    if ($chan->supportsREST($mirror['attribs']['host'])) {
9661                        $funcs = $chan->getFunctions('rest', $mirror['attribs']['host']);
9662                        if (!isset($funcs[0])) {
9663                            $funcs = array($funcs);
9664                        }
9665
9666                        foreach ($funcs as $protocol) {
9667                            $data['data'][] = array('rest', $protocol['attribs']['type'],
9668                                $protocol['_content']);
9669                        }
9670                    }
9671                } else {
9672                    $data['data'][] = array('No supported protocols');
9673                }
9674                $d['mirrorprotocols' . $i] = $data;
9675            }
9676        }
9677        $this->ui->outputData($d, 'channel-info');
9678    }
9679
9680    // }}}
9681
9682    function doDelete($command, $options, $params)
9683    {
9684        if (count($params) !== 1) {
9685            return $this->raiseError('channel-delete: no channel specified');
9686        }
9687
9688        $reg = &$this->config->getRegistry();
9689        if (!$reg->channelExists($params[0])) {
9690            return $this->raiseError('channel-delete: channel "' . $params[0] . '" does not exist');
9691        }
9692
9693        $channel = $reg->channelName($params[0]);
9694        if ($channel == 'pear.php.net') {
9695            return $this->raiseError('Cannot delete the pear.php.net channel');
9696        }
9697
9698        if ($channel == 'pecl.php.net') {
9699            return $this->raiseError('Cannot delete the pecl.php.net channel');
9700        }
9701
9702        if ($channel == 'doc.php.net') {
9703            return $this->raiseError('Cannot delete the doc.php.net channel');
9704        }
9705
9706        if ($channel == '__uri') {
9707            return $this->raiseError('Cannot delete the __uri pseudo-channel');
9708        }
9709
9710        if (PEAR::isError($err = $reg->listPackages($channel))) {
9711            return $err;
9712        }
9713
9714        if (count($err)) {
9715            return $this->raiseError('Channel "' . $channel .
9716                '" has installed packages, cannot delete');
9717        }
9718
9719        if (!$reg->deleteChannel($channel)) {
9720            return $this->raiseError('Channel "' . $channel . '" deletion failed');
9721        } else {
9722            $this->config->deleteChannel($channel);
9723            $this->ui->outputData('Channel "' . $channel . '" deleted', $command);
9724        }
9725    }
9726
9727    function doAdd($command, $options, $params)
9728    {
9729        if (count($params) !== 1) {
9730            return $this->raiseError('channel-add: no channel file specified');
9731        }
9732
9733        if (strpos($params[0], '://')) {
9734            $downloader = &$this->getDownloader();
9735            $tmpdir = $this->config->get('temp_dir');
9736            if (!file_exists($tmpdir)) {
9737                require_once 'System.php';
9738                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
9739                $err = System::mkdir(array('-p', $tmpdir));
9740                PEAR::staticPopErrorHandling();
9741                if (PEAR::isError($err)) {
9742                    return $this->raiseError('channel-add: temp_dir does not exist: "' .
9743                        $tmpdir .
9744                        '" - You can change this location with "pear config-set temp_dir"');
9745                }
9746            }
9747
9748            if (!is_writable($tmpdir)) {
9749                return $this->raiseError('channel-add: temp_dir is not writable: "' .
9750                    $tmpdir .
9751                    '" - You can change this location with "pear config-set temp_dir"');
9752            }
9753
9754            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
9755            $loc = $downloader->downloadHttp($params[0], $this->ui, $tmpdir, null, false);
9756            PEAR::staticPopErrorHandling();
9757            if (PEAR::isError($loc)) {
9758                return $this->raiseError('channel-add: Cannot open "' . $params[0] .
9759                    '" (' . $loc->getMessage() . ')');
9760            }
9761
9762            list($loc, $lastmodified) = $loc;
9763            $contents = implode('', file($loc));
9764        } else {
9765            $lastmodified = $fp = false;
9766            if (file_exists($params[0])) {
9767                $fp = fopen($params[0], 'r');
9768            }
9769
9770            if (!$fp) {
9771                return $this->raiseError('channel-add: cannot open "' . $params[0] . '"');
9772            }
9773
9774            $contents = '';
9775            while (!feof($fp)) {
9776                $contents .= fread($fp, 1024);
9777            }
9778            fclose($fp);
9779        }
9780
9781        if (!class_exists('PEAR_ChannelFile')) {
9782            require_once 'PEAR/ChannelFile.php';
9783        }
9784
9785        $channel = new PEAR_ChannelFile;
9786        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
9787        $result = $channel->fromXmlString($contents);
9788        PEAR::staticPopErrorHandling();
9789        if (!$result) {
9790            $exit = false;
9791            if (count($errors = $channel->getErrors(true))) {
9792                foreach ($errors as $error) {
9793                    $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
9794                    if (!$exit) {
9795                        $exit = $error['level'] == 'error' ? true : false;
9796                    }
9797                }
9798                if ($exit) {
9799                    return $this->raiseError('channel-add: invalid channel.xml file');
9800                }
9801            }
9802        }
9803
9804        $reg = &$this->config->getRegistry();
9805        if ($reg->channelExists($channel->getName())) {
9806            return $this->raiseError('channel-add: Channel "' . $channel->getName() .
9807                '" exists, use channel-update to update entry', PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS);
9808        }
9809
9810        $ret = $reg->addChannel($channel, $lastmodified);
9811        if (PEAR::isError($ret)) {
9812            return $ret;
9813        }
9814
9815        if (!$ret) {
9816            return $this->raiseError('channel-add: adding Channel "' . $channel->getName() .
9817                '" to registry failed');
9818        }
9819
9820        $this->config->setChannels($reg->listChannels());
9821        $this->config->writeConfigFile();
9822        $this->ui->outputData('Adding Channel "' . $channel->getName() . '" succeeded', $command);
9823    }
9824
9825    function doUpdate($command, $options, $params)
9826    {
9827        if (count($params) !== 1) {
9828            return $this->raiseError("No channel file specified");
9829        }
9830
9831        $tmpdir = $this->config->get('temp_dir');
9832        if (!file_exists($tmpdir)) {
9833            require_once 'System.php';
9834            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
9835            $err = System::mkdir(array('-p', $tmpdir));
9836            PEAR::staticPopErrorHandling();
9837            if (PEAR::isError($err)) {
9838                return $this->raiseError('channel-add: temp_dir does not exist: "' .
9839                    $tmpdir .
9840                    '" - You can change this location with "pear config-set temp_dir"');
9841            }
9842        }
9843
9844        if (!is_writable($tmpdir)) {
9845            return $this->raiseError('channel-add: temp_dir is not writable: "' .
9846                $tmpdir .
9847                '" - You can change this location with "pear config-set temp_dir"');
9848        }
9849
9850        $reg = &$this->config->getRegistry();
9851        $lastmodified = false;
9852        if ((!file_exists($params[0]) || is_dir($params[0]))
9853              && $reg->channelExists(strtolower($params[0]))) {
9854            $c = $reg->getChannel(strtolower($params[0]));
9855            if (PEAR::isError($c)) {
9856                return $this->raiseError($c);
9857            }
9858
9859            $this->ui->outputData("Updating channel \"$params[0]\"", $command);
9860            $dl = &$this->getDownloader(array());
9861            // if force is specified, use a timestamp of "1" to force retrieval
9862            $lastmodified = isset($options['force']) ? false : $c->lastModified();
9863            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
9864            $contents = $dl->downloadHttp('http://' . $c->getName() . '/channel.xml',
9865                $this->ui, $tmpdir, null, $lastmodified);
9866            PEAR::staticPopErrorHandling();
9867            if (PEAR::isError($contents)) {
9868                // Attempt to fall back to https
9869                $this->ui->outputData("Channel \"$params[0]\" is not responding over http://, failed with message: " . $contents->getMessage());
9870                $this->ui->outputData("Trying channel \"$params[0]\" over https:// instead");
9871                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
9872                $contents = $dl->downloadHttp('https://' . $c->getName() . '/channel.xml',
9873                    $this->ui, $tmpdir, null, $lastmodified);
9874                PEAR::staticPopErrorHandling();
9875                if (PEAR::isError($contents)) {
9876                    return $this->raiseError('Cannot retrieve channel.xml for channel "' .
9877                        $c->getName() . '" (' . $contents->getMessage() . ')');
9878                }
9879            }
9880
9881            list($contents, $lastmodified) = $contents;
9882            if (!$contents) {
9883                $this->ui->outputData("Channel \"$params[0]\" is up to date");
9884                return;
9885            }
9886
9887            $contents = implode('', file($contents));
9888            if (!class_exists('PEAR_ChannelFile')) {
9889                require_once 'PEAR/ChannelFile.php';
9890            }
9891
9892            $channel = new PEAR_ChannelFile;
9893            $channel->fromXmlString($contents);
9894            if (!$channel->getErrors()) {
9895                // security check: is the downloaded file for the channel we got it from?
9896                if (strtolower($channel->getName()) != strtolower($c->getName())) {
9897                    if (!isset($options['force'])) {
9898                        return $this->raiseError('ERROR: downloaded channel definition file' .
9899                            ' for channel "' . $channel->getName() . '" from channel "' .
9900                            strtolower($c->getName()) . '"');
9901                    }
9902
9903                    $this->ui->log(0, 'WARNING: downloaded channel definition file' .
9904                        ' for channel "' . $channel->getName() . '" from channel "' .
9905                        strtolower($c->getName()) . '"');
9906                }
9907            }
9908        } else {
9909            if (strpos($params[0], '://')) {
9910                $dl = &$this->getDownloader();
9911                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
9912                $loc = $dl->downloadHttp($params[0],
9913                    $this->ui, $tmpdir, null, $lastmodified);
9914                PEAR::staticPopErrorHandling();
9915                if (PEAR::isError($loc)) {
9916                    return $this->raiseError("Cannot open " . $params[0] .
9917                         ' (' . $loc->getMessage() . ')');
9918                }
9919
9920                list($loc, $lastmodified) = $loc;
9921                $contents = implode('', file($loc));
9922            } else {
9923                $fp = false;
9924                if (file_exists($params[0])) {
9925                    $fp = fopen($params[0], 'r');
9926                }
9927
9928                if (!$fp) {
9929                    return $this->raiseError("Cannot open " . $params[0]);
9930                }
9931
9932                $contents = '';
9933                while (!feof($fp)) {
9934                    $contents .= fread($fp, 1024);
9935                }
9936                fclose($fp);
9937            }
9938
9939            if (!class_exists('PEAR_ChannelFile')) {
9940                require_once 'PEAR/ChannelFile.php';
9941            }
9942
9943            $channel = new PEAR_ChannelFile;
9944            $channel->fromXmlString($contents);
9945        }
9946
9947        $exit = false;
9948        if (count($errors = $channel->getErrors(true))) {
9949            foreach ($errors as $error) {
9950                $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
9951                if (!$exit) {
9952                    $exit = $error['level'] == 'error' ? true : false;
9953                }
9954            }
9955            if ($exit) {
9956                return $this->raiseError('Invalid channel.xml file');
9957            }
9958        }
9959
9960        if (!$reg->channelExists($channel->getName())) {
9961            return $this->raiseError('Error: Channel "' . $channel->getName() .
9962                '" does not exist, use channel-add to add an entry');
9963        }
9964
9965        $ret = $reg->updateChannel($channel, $lastmodified);
9966        if (PEAR::isError($ret)) {
9967            return $ret;
9968        }
9969
9970        if (!$ret) {
9971            return $this->raiseError('Updating Channel "' . $channel->getName() .
9972                '" in registry failed');
9973        }
9974
9975        $this->config->setChannels($reg->listChannels());
9976        $this->config->writeConfigFile();
9977        $this->ui->outputData('Update of Channel "' . $channel->getName() . '" succeeded');
9978    }
9979
9980    function &getDownloader()
9981    {
9982        if (!class_exists('PEAR_Downloader')) {
9983            require_once 'PEAR/Downloader.php';
9984        }
9985        $a = new PEAR_Downloader($this->ui, array(), $this->config);
9986        return $a;
9987    }
9988
9989    function doAlias($command, $options, $params)
9990    {
9991        if (count($params) === 1) {
9992            return $this->raiseError('No channel alias specified');
9993        }
9994
9995        if (count($params) !== 2 || (!empty($params[1]) && $params[1]{0} == '-')) {
9996            return $this->raiseError(
9997                'Invalid format, correct is: channel-alias channel alias');
9998        }
9999
10000        $reg = &$this->config->getRegistry();
10001        if (!$reg->channelExists($params[0], true)) {
10002            $extra = '';
10003            if ($reg->isAlias($params[0])) {
10004                $extra = ' (use "channel-alias ' . $reg->channelName($params[0]) . ' ' .
10005                    strtolower($params[1]) . '")';
10006            }
10007
10008            return $this->raiseError('"' . $params[0] . '" is not a valid channel' . $extra);
10009        }
10010
10011        if ($reg->isAlias($params[1])) {
10012            return $this->raiseError('Channel "' . $reg->channelName($params[1]) . '" is ' .
10013                'already aliased to "' . strtolower($params[1]) . '", cannot re-alias');
10014        }
10015
10016        $chan = &$reg->getChannel($params[0]);
10017        if (PEAR::isError($chan)) {
10018            return $this->raiseError('Corrupt registry?  Error retrieving channel "' . $params[0] .
10019                '" information (' . $chan->getMessage() . ')');
10020        }
10021
10022        // make it a local alias
10023        if (!$chan->setAlias(strtolower($params[1]), true)) {
10024            return $this->raiseError('Alias "' . strtolower($params[1]) .
10025                '" is not a valid channel alias');
10026        }
10027
10028        $reg->updateChannel($chan);
10029        $this->ui->outputData('Channel "' . $chan->getName() . '" aliased successfully to "' .
10030            strtolower($params[1]) . '"');
10031    }
10032
10033    /**
10034     * The channel-discover command
10035     *
10036     * @param string $command command name
10037     * @param array  $options option_name => value
10038     * @param array  $params  list of additional parameters.
10039     *               $params[0] should contain a string with either:
10040     *               - <channel name> or
10041     *               - <username>:<password>@<channel name>
10042     * @return null|PEAR_Error
10043     */
10044    function doDiscover($command, $options, $params)
10045    {
10046        if (count($params) !== 1) {
10047            return $this->raiseError("No channel server specified");
10048        }
10049
10050        // Look for the possible input format "<username>:<password>@<channel>"
10051        if (preg_match('/^(.+):(.+)@(.+)\\z/', $params[0], $matches)) {
10052            $username = $matches[1];
10053            $password = $matches[2];
10054            $channel  = $matches[3];
10055        } else {
10056            $channel = $params[0];
10057        }
10058
10059        $reg = &$this->config->getRegistry();
10060        if ($reg->channelExists($channel)) {
10061            if (!$reg->isAlias($channel)) {
10062                return $this->raiseError("Channel \"$channel\" is already initialized", PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS);
10063            }
10064
10065            return $this->raiseError("A channel alias named \"$channel\" " .
10066                'already exists, aliasing channel "' . $reg->channelName($channel)
10067                . '"');
10068        }
10069
10070        $this->pushErrorHandling(PEAR_ERROR_RETURN);
10071        $err = $this->doAdd($command, $options, array('http://' . $channel . '/channel.xml'));
10072        $this->popErrorHandling();
10073        if (PEAR::isError($err)) {
10074            if ($err->getCode() === PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS) {
10075                return $this->raiseError("Discovery of channel \"$channel\" failed (" .
10076                    $err->getMessage() . ')');
10077            }
10078            // Attempt fetch via https
10079            $this->ui->outputData("Discovering channel $channel over http:// failed with message: " . $err->getMessage());
10080            $this->ui->outputData("Trying to discover channel $channel over https:// instead");
10081            $this->pushErrorHandling(PEAR_ERROR_RETURN);
10082            $err = $this->doAdd($command, $options, array('https://' . $channel . '/channel.xml'));
10083            $this->popErrorHandling();
10084            if (PEAR::isError($err)) {
10085                return $this->raiseError("Discovery of channel \"$channel\" failed (" .
10086                    $err->getMessage() . ')');
10087            }
10088        }
10089
10090        // Store username/password if they were given
10091        // Arguably we should do a logintest on the channel here, but since
10092        // that's awkward on a REST-based channel (even "pear login" doesn't
10093        // do it for those), and XML-RPC is deprecated, it's fairly pointless.
10094        if (isset($username)) {
10095            $this->config->set('username', $username, 'user', $channel);
10096            $this->config->set('password', $password, 'user', $channel);
10097            $this->config->store();
10098            $this->ui->outputData("Stored login for channel \"$channel\" using username \"$username\"", $command);
10099        }
10100
10101        $this->ui->outputData("Discovery of channel \"$channel\" succeeded", $command);
10102    }
10103
10104    /**
10105     * Execute the 'login' command.
10106     *
10107     * @param string $command command name
10108     * @param array $options option_name => value
10109     * @param array $params list of additional parameters
10110     *
10111     * @return bool TRUE on success or
10112     * a PEAR error on failure
10113     *
10114     * @access public
10115     */
10116    function doLogin($command, $options, $params)
10117    {
10118        $reg = &$this->config->getRegistry();
10119
10120        // If a parameter is supplied, use that as the channel to log in to
10121        $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel');
10122
10123        $chan = $reg->getChannel($channel);
10124        if (PEAR::isError($chan)) {
10125            return $this->raiseError($chan);
10126        }
10127
10128        $server   = $this->config->get('preferred_mirror', null, $channel);
10129        $username = $this->config->get('username',         null, $channel);
10130        if (empty($username)) {
10131            $username = isset($_ENV['USER']) ? $_ENV['USER'] : null;
10132        }
10133        $this->ui->outputData("Logging in to $server.", $command);
10134
10135        list($username, $password) = $this->ui->userDialog(
10136            $command,
10137            array('Username', 'Password'),
10138            array('text',     'password'),
10139            array($username,  '')
10140            );
10141        $username = trim($username);
10142        $password = trim($password);
10143
10144        $ourfile = $this->config->getConfFile('user');
10145        if (!$ourfile) {
10146            $ourfile = $this->config->getConfFile('system');
10147        }
10148
10149        $this->config->set('username', $username, 'user', $channel);
10150        $this->config->set('password', $password, 'user', $channel);
10151
10152        if ($chan->supportsREST()) {
10153            $ok = true;
10154        }
10155
10156        if ($ok !== true) {
10157            return $this->raiseError('Login failed!');
10158        }
10159
10160        $this->ui->outputData("Logged in.", $command);
10161        // avoid changing any temporary settings changed with -d
10162        $ourconfig = new PEAR_Config($ourfile, $ourfile);
10163        $ourconfig->set('username', $username, 'user', $channel);
10164        $ourconfig->set('password', $password, 'user', $channel);
10165        $ourconfig->store();
10166
10167        return true;
10168    }
10169
10170    /**
10171     * Execute the 'logout' command.
10172     *
10173     * @param string $command command name
10174     * @param array $options option_name => value
10175     * @param array $params list of additional parameters
10176     *
10177     * @return bool TRUE on success or
10178     * a PEAR error on failure
10179     *
10180     * @access public
10181     */
10182    function doLogout($command, $options, $params)
10183    {
10184        $reg     = &$this->config->getRegistry();
10185
10186        // If a parameter is supplied, use that as the channel to log in to
10187        $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel');
10188
10189        $chan    = $reg->getChannel($channel);
10190        if (PEAR::isError($chan)) {
10191            return $this->raiseError($chan);
10192        }
10193
10194        $server = $this->config->get('preferred_mirror', null, $channel);
10195        $this->ui->outputData("Logging out from $server.", $command);
10196        $this->config->remove('username', 'user', $channel);
10197        $this->config->remove('password', 'user', $channel);
10198        $this->config->store();
10199        return true;
10200    }
10201}PEAR-1.9.4/PEAR/Command/Common.php0000644000076500000240000002014411605156614015303 0ustar  helgistaff<?php
10202/**
10203 * PEAR_Command_Common base class
10204 *
10205 * PHP versions 4 and 5
10206 *
10207 * @category   pear
10208 * @package    PEAR
10209 * @author     Stig Bakken <ssb@php.net>
10210 * @author     Greg Beaver <cellog@php.net>
10211 * @copyright  1997-2009 The Authors
10212 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
10213 * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
10214 * @link       http://pear.php.net/package/PEAR
10215 * @since      File available since Release 0.1
10216 */
10217
10218/**
10219 * base class
10220 */
10221require_once 'PEAR.php';
10222
10223/**
10224 * PEAR commands base class
10225 *
10226 * @category   pear
10227 * @package    PEAR
10228 * @author     Stig Bakken <ssb@php.net>
10229 * @author     Greg Beaver <cellog@php.net>
10230 * @copyright  1997-2009 The Authors
10231 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
10232 * @version    Release: 1.9.4
10233 * @link       http://pear.php.net/package/PEAR
10234 * @since      Class available since Release 0.1
10235 */
10236class PEAR_Command_Common extends PEAR
10237{
10238    /**
10239     * PEAR_Config object used to pass user system and configuration
10240     * on when executing commands
10241     *
10242     * @var PEAR_Config
10243     */
10244    var $config;
10245    /**
10246     * @var PEAR_Registry
10247     * @access protected
10248     */
10249    var $_registry;
10250
10251    /**
10252     * User Interface object, for all interaction with the user.
10253     * @var object
10254     */
10255    var $ui;
10256
10257    var $_deps_rel_trans = array(
10258                                 'lt' => '<',
10259                                 'le' => '<=',
10260                                 'eq' => '=',
10261                                 'ne' => '!=',
10262                                 'gt' => '>',
10263                                 'ge' => '>=',
10264                                 'has' => '=='
10265                                 );
10266
10267    var $_deps_type_trans = array(
10268                                  'pkg' => 'package',
10269                                  'ext' => 'extension',
10270                                  'php' => 'PHP',
10271                                  'prog' => 'external program',
10272                                  'ldlib' => 'external library for linking',
10273                                  'rtlib' => 'external runtime library',
10274                                  'os' => 'operating system',
10275                                  'websrv' => 'web server',
10276                                  'sapi' => 'SAPI backend'
10277                                  );
10278
10279    /**
10280     * PEAR_Command_Common constructor.
10281     *
10282     * @access public
10283     */
10284    function PEAR_Command_Common(&$ui, &$config)
10285    {
10286        parent::PEAR();
10287        $this->config = &$config;
10288        $this->ui = &$ui;
10289    }
10290
10291    /**
10292     * Return a list of all the commands defined by this class.
10293     * @return array list of commands
10294     * @access public
10295     */
10296    function getCommands()
10297    {
10298        $ret = array();
10299        foreach (array_keys($this->commands) as $command) {
10300            $ret[$command] = $this->commands[$command]['summary'];
10301        }
10302
10303        return $ret;
10304    }
10305
10306    /**
10307     * Return a list of all the command shortcuts defined by this class.
10308     * @return array shortcut => command
10309     * @access public
10310     */
10311    function getShortcuts()
10312    {
10313        $ret = array();
10314        foreach (array_keys($this->commands) as $command) {
10315            if (isset($this->commands[$command]['shortcut'])) {
10316                $ret[$this->commands[$command]['shortcut']] = $command;
10317            }
10318        }
10319
10320        return $ret;
10321    }
10322
10323    function getOptions($command)
10324    {
10325        $shortcuts = $this->getShortcuts();
10326        if (isset($shortcuts[$command])) {
10327            $command = $shortcuts[$command];
10328        }
10329
10330        if (isset($this->commands[$command]) &&
10331              isset($this->commands[$command]['options'])) {
10332            return $this->commands[$command]['options'];
10333        }
10334
10335        return null;
10336    }
10337
10338    function getGetoptArgs($command, &$short_args, &$long_args)
10339    {
10340        $short_args = '';
10341        $long_args = array();
10342        if (empty($this->commands[$command]) || empty($this->commands[$command]['options'])) {
10343            return;
10344        }
10345
10346        reset($this->commands[$command]['options']);
10347        while (list($option, $info) = each($this->commands[$command]['options'])) {
10348            $larg = $sarg = '';
10349            if (isset($info['arg'])) {
10350                if ($info['arg']{0} == '(') {
10351                    $larg = '==';
10352                    $sarg = '::';
10353                    $arg = substr($info['arg'], 1, -1);
10354                } else {
10355                    $larg = '=';
10356                    $sarg = ':';
10357                    $arg = $info['arg'];
10358                }
10359            }
10360
10361            if (isset($info['shortopt'])) {
10362                $short_args .= $info['shortopt'] . $sarg;
10363            }
10364
10365            $long_args[] = $option . $larg;
10366        }
10367    }
10368
10369    /**
10370    * Returns the help message for the given command
10371    *
10372    * @param string $command The command
10373    * @return mixed A fail string if the command does not have help or
10374    *               a two elements array containing [0]=>help string,
10375    *               [1]=> help string for the accepted cmd args
10376    */
10377    function getHelp($command)
10378    {
10379        $config = &PEAR_Config::singleton();
10380        if (!isset($this->commands[$command])) {
10381            return "No such command \"$command\"";
10382        }
10383
10384        $help = null;
10385        if (isset($this->commands[$command]['doc'])) {
10386            $help = $this->commands[$command]['doc'];
10387        }
10388
10389        if (empty($help)) {
10390            // XXX (cox) Fallback to summary if there is no doc (show both?)
10391            if (!isset($this->commands[$command]['summary'])) {
10392                return "No help for command \"$command\"";
10393            }
10394            $help = $this->commands[$command]['summary'];
10395        }
10396
10397        if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) {
10398            foreach($matches[0] as $k => $v) {
10399                $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help);
10400            }
10401        }
10402
10403        return array($help, $this->getHelpArgs($command));
10404    }
10405
10406    /**
10407     * Returns the help for the accepted arguments of a command
10408     *
10409     * @param  string $command
10410     * @return string The help string
10411     */
10412    function getHelpArgs($command)
10413    {
10414        if (isset($this->commands[$command]['options']) &&
10415            count($this->commands[$command]['options']))
10416        {
10417            $help = "Options:\n";
10418            foreach ($this->commands[$command]['options'] as $k => $v) {
10419                if (isset($v['arg'])) {
10420                    if ($v['arg'][0] == '(') {
10421                        $arg = substr($v['arg'], 1, -1);
10422                        $sapp = " [$arg]";
10423                        $lapp = "[=$arg]";
10424                    } else {
10425                        $sapp = " $v[arg]";
10426                        $lapp = "=$v[arg]";
10427                    }
10428                } else {
10429                    $sapp = $lapp = "";
10430                }
10431
10432                if (isset($v['shortopt'])) {
10433                    $s = $v['shortopt'];
10434                    $help .= "  -$s$sapp, --$k$lapp\n";
10435                } else {
10436                    $help .= "  --$k$lapp\n";
10437                }
10438
10439                $p = "        ";
10440                $doc = rtrim(str_replace("\n", "\n$p", $v['doc']));
10441                $help .= "        $doc\n";
10442            }
10443
10444            return $help;
10445        }
10446
10447        return null;
10448    }
10449
10450    function run($command, $options, $params)
10451    {
10452        if (empty($this->commands[$command]['function'])) {
10453            // look for shortcuts
10454            foreach (array_keys($this->commands) as $cmd) {
10455                if (isset($this->commands[$cmd]['shortcut']) && $this->commands[$cmd]['shortcut'] == $command) {
10456                    if (empty($this->commands[$cmd]['function'])) {
10457                        return $this->raiseError("unknown command `$command'");
10458                    } else {
10459                        $func = $this->commands[$cmd]['function'];
10460                    }
10461                    $command = $cmd;
10462
10463                    //$command = $this->commands[$cmd]['function'];
10464                    break;
10465                }
10466            }
10467        } else {
10468            $func = $this->commands[$command]['function'];
10469        }
10470
10471        return $this->$func($command, $options, $params);
10472    }
10473}PEAR-1.9.4/PEAR/Command/Config.xml0000644000076500000240000000646611605156614015304 0ustar  helgistaff<commands version="1.0">
10474 <config-show>
10475  <summary>Show All Settings</summary>
10476  <function>doConfigShow</function>
10477  <shortcut>csh</shortcut>
10478  <options>
10479   <channel>
10480    <shortopt>c</shortopt>
10481    <doc>show configuration variables for another channel</doc>
10482    <arg>CHAN</arg>
10483   </channel>
10484  </options>
10485  <doc>[layer]
10486Displays all configuration values.  An optional argument
10487may be used to tell which configuration layer to display.  Valid
10488configuration layers are &quot;user&quot;, &quot;system&quot; and &quot;default&quot;. To display
10489configurations for different channels, set the default_channel
10490configuration variable and run config-show again.
10491</doc>
10492 </config-show>
10493 <config-get>
10494  <summary>Show One Setting</summary>
10495  <function>doConfigGet</function>
10496  <shortcut>cg</shortcut>
10497  <options>
10498   <channel>
10499    <shortopt>c</shortopt>
10500    <doc>show configuration variables for another channel</doc>
10501    <arg>CHAN</arg>
10502   </channel>
10503  </options>
10504  <doc>&lt;parameter&gt; [layer]
10505Displays the value of one configuration parameter.  The
10506first argument is the name of the parameter, an optional second argument
10507may be used to tell which configuration layer to look in.  Valid configuration
10508layers are &quot;user&quot;, &quot;system&quot; and &quot;default&quot;.  If no layer is specified, a value
10509will be picked from the first layer that defines the parameter, in the order
10510just specified.  The configuration value will be retrieved for the channel
10511specified by the default_channel configuration variable.
10512</doc>
10513 </config-get>
10514 <config-set>
10515  <summary>Change Setting</summary>
10516  <function>doConfigSet</function>
10517  <shortcut>cs</shortcut>
10518  <options>
10519   <channel>
10520    <shortopt>c</shortopt>
10521    <doc>show configuration variables for another channel</doc>
10522    <arg>CHAN</arg>
10523   </channel>
10524  </options>
10525  <doc>&lt;parameter&gt; &lt;value&gt; [layer]
10526Sets the value of one configuration parameter.  The first argument is
10527the name of the parameter, the second argument is the new value.  Some
10528parameters are subject to validation, and the command will fail with
10529an error message if the new value does not make sense.  An optional
10530third argument may be used to specify in which layer to set the
10531configuration parameter.  The default layer is &quot;user&quot;.  The
10532configuration value will be set for the current channel, which
10533is controlled by the default_channel configuration variable.
10534</doc>
10535 </config-set>
10536 <config-help>
10537  <summary>Show Information About Setting</summary>
10538  <function>doConfigHelp</function>
10539  <shortcut>ch</shortcut>
10540  <options />
10541  <doc>[parameter]
10542Displays help for a configuration parameter.  Without arguments it
10543displays help for all configuration parameters.
10544</doc>
10545 </config-help>
10546 <config-create>
10547  <summary>Create a Default configuration file</summary>
10548  <function>doConfigCreate</function>
10549  <shortcut>coc</shortcut>
10550  <options>
10551   <windows>
10552    <shortopt>w</shortopt>
10553    <doc>create a config file for a windows install</doc>
10554   </windows>
10555  </options>
10556  <doc>&lt;root path&gt; &lt;filename&gt;
10557Create a default configuration file with all directory configuration
10558variables set to subdirectories of &lt;root path&gt;, and save it as &lt;filename&gt;.
10559This is useful especially for creating a configuration file for a remote
10560PEAR installation (using the --remoteconfig option of install, upgrade,
10561and uninstall).
10562</doc>
10563 </config-create>
10564</commands>PEAR-1.9.4/PEAR/Command/Config.php0000644000076500000240000003615611605156614015272 0ustar  helgistaff<?php
10565/**
10566 * PEAR_Command_Config (config-show, config-get, config-set, config-help, config-create commands)
10567 *
10568 * PHP versions 4 and 5
10569 *
10570 * @category   pear
10571 * @package    PEAR
10572 * @author     Stig Bakken <ssb@php.net>
10573 * @author     Greg Beaver <cellog@php.net>
10574 * @copyright  1997-2009 The Authors
10575 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
10576 * @version    CVS: $Id: Config.php 313024 2011-07-06 19:51:24Z dufuz $
10577 * @link       http://pear.php.net/package/PEAR
10578 * @since      File available since Release 0.1
10579 */
10580
10581/**
10582 * base class
10583 */
10584require_once 'PEAR/Command/Common.php';
10585
10586/**
10587 * PEAR commands for managing configuration data.
10588 *
10589 * @category   pear
10590 * @package    PEAR
10591 * @author     Stig Bakken <ssb@php.net>
10592 * @author     Greg Beaver <cellog@php.net>
10593 * @copyright  1997-2009 The Authors
10594 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
10595 * @version    Release: 1.9.4
10596 * @link       http://pear.php.net/package/PEAR
10597 * @since      Class available since Release 0.1
10598 */
10599class PEAR_Command_Config extends PEAR_Command_Common
10600{
10601    var $commands = array(
10602        'config-show' => array(
10603            'summary' => 'Show All Settings',
10604            'function' => 'doConfigShow',
10605            'shortcut' => 'csh',
10606            'options' => array(
10607                'channel' => array(
10608                    'shortopt' => 'c',
10609                    'doc' => 'show configuration variables for another channel',
10610                    'arg' => 'CHAN',
10611                    ),
10612),
10613            'doc' => '[layer]
10614Displays all configuration values.  An optional argument
10615may be used to tell which configuration layer to display.  Valid
10616configuration layers are "user", "system" and "default". To display
10617configurations for different channels, set the default_channel
10618configuration variable and run config-show again.
10619',
10620            ),
10621        'config-get' => array(
10622            'summary' => 'Show One Setting',
10623            'function' => 'doConfigGet',
10624            'shortcut' => 'cg',
10625            'options' => array(
10626                'channel' => array(
10627                    'shortopt' => 'c',
10628                    'doc' => 'show configuration variables for another channel',
10629                    'arg' => 'CHAN',
10630                    ),
10631),
10632            'doc' => '<parameter> [layer]
10633Displays the value of one configuration parameter.  The
10634first argument is the name of the parameter, an optional second argument
10635may be used to tell which configuration layer to look in.  Valid configuration
10636layers are "user", "system" and "default".  If no layer is specified, a value
10637will be picked from the first layer that defines the parameter, in the order
10638just specified.  The configuration value will be retrieved for the channel
10639specified by the default_channel configuration variable.
10640',
10641            ),
10642        'config-set' => array(
10643            'summary' => 'Change Setting',
10644            'function' => 'doConfigSet',
10645            'shortcut' => 'cs',
10646            'options' => array(
10647                'channel' => array(
10648                    'shortopt' => 'c',
10649                    'doc' => 'show configuration variables for another channel',
10650                    'arg' => 'CHAN',
10651                    ),
10652),
10653            'doc' => '<parameter> <value> [layer]
10654Sets the value of one configuration parameter.  The first argument is
10655the name of the parameter, the second argument is the new value.  Some
10656parameters are subject to validation, and the command will fail with
10657an error message if the new value does not make sense.  An optional
10658third argument may be used to specify in which layer to set the
10659configuration parameter.  The default layer is "user".  The
10660configuration value will be set for the current channel, which
10661is controlled by the default_channel configuration variable.
10662',
10663            ),
10664        'config-help' => array(
10665            'summary' => 'Show Information About Setting',
10666            'function' => 'doConfigHelp',
10667            'shortcut' => 'ch',
10668            'options' => array(),
10669            'doc' => '[parameter]
10670Displays help for a configuration parameter.  Without arguments it
10671displays help for all configuration parameters.
10672',
10673           ),
10674        'config-create' => array(
10675            'summary' => 'Create a Default configuration file',
10676            'function' => 'doConfigCreate',
10677            'shortcut' => 'coc',
10678            'options' => array(
10679                'windows' => array(
10680                    'shortopt' => 'w',
10681                    'doc' => 'create a config file for a windows install',
10682                    ),
10683            ),
10684            'doc' => '<root path> <filename>
10685Create a default configuration file with all directory configuration
10686variables set to subdirectories of <root path>, and save it as <filename>.
10687This is useful especially for creating a configuration file for a remote
10688PEAR installation (using the --remoteconfig option of install, upgrade,
10689and uninstall).
10690',
10691            ),
10692        );
10693
10694    /**
10695     * PEAR_Command_Config constructor.
10696     *
10697     * @access public
10698     */
10699    function PEAR_Command_Config(&$ui, &$config)
10700    {
10701        parent::PEAR_Command_Common($ui, $config);
10702    }
10703
10704    function doConfigShow($command, $options, $params)
10705    {
10706        $layer = null;
10707        if (is_array($params)) {
10708            $layer = isset($params[0]) ? $params[0] : null;
10709        }
10710
10711        // $params[0] -> the layer
10712        if ($error = $this->_checkLayer($layer)) {
10713            return $this->raiseError("config-show:$error");
10714        }
10715
10716        $keys = $this->config->getKeys();
10717        sort($keys);
10718        $channel = isset($options['channel']) ? $options['channel'] :
10719            $this->config->get('default_channel');
10720        $reg = &$this->config->getRegistry();
10721        if (!$reg->channelExists($channel)) {
10722            return $this->raiseError('Channel "' . $channel . '" does not exist');
10723        }
10724
10725        $channel = $reg->channelName($channel);
10726        $data = array('caption' => 'Configuration (channel ' . $channel . '):');
10727        foreach ($keys as $key) {
10728            $type = $this->config->getType($key);
10729            $value = $this->config->get($key, $layer, $channel);
10730            if ($type == 'password' && $value) {
10731                $value = '********';
10732            }
10733
10734            if ($value === false) {
10735                $value = 'false';
10736            } elseif ($value === true) {
10737                $value = 'true';
10738            }
10739
10740            $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value);
10741        }
10742
10743        foreach ($this->config->getLayers() as $layer) {
10744            $data['data']['Config Files'][] = array(ucfirst($layer) . ' Configuration File', 'Filename' , $this->config->getConfFile($layer));
10745        }
10746
10747        $this->ui->outputData($data, $command);
10748        return true;
10749    }
10750
10751    function doConfigGet($command, $options, $params)
10752    {
10753        $args_cnt = is_array($params) ? count($params) : 0;
10754        switch ($args_cnt) {
10755            case 1:
10756                $config_key = $params[0];
10757                $layer = null;
10758                break;
10759            case 2:
10760                $config_key = $params[0];
10761                $layer = $params[1];
10762                if ($error = $this->_checkLayer($layer)) {
10763                    return $this->raiseError("config-get:$error");
10764                }
10765                break;
10766            case 0:
10767            default:
10768                return $this->raiseError("config-get expects 1 or 2 parameters");
10769        }
10770
10771        $reg = &$this->config->getRegistry();
10772        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
10773        if (!$reg->channelExists($channel)) {
10774            return $this->raiseError('Channel "' . $channel . '" does not exist');
10775        }
10776
10777        $channel = $reg->channelName($channel);
10778        $this->ui->outputData($this->config->get($config_key, $layer, $channel), $command);
10779        return true;
10780    }
10781
10782    function doConfigSet($command, $options, $params)
10783    {
10784        // $param[0] -> a parameter to set
10785        // $param[1] -> the value for the parameter
10786        // $param[2] -> the layer
10787        $failmsg = '';
10788        if (count($params) < 2 || count($params) > 3) {
10789            $failmsg .= "config-set expects 2 or 3 parameters";
10790            return PEAR::raiseError($failmsg);
10791        }
10792
10793        if (isset($params[2]) && ($error = $this->_checkLayer($params[2]))) {
10794            $failmsg .= $error;
10795            return PEAR::raiseError("config-set:$failmsg");
10796        }
10797
10798        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
10799        $reg = &$this->config->getRegistry();
10800        if (!$reg->channelExists($channel)) {
10801            return $this->raiseError('Channel "' . $channel . '" does not exist');
10802        }
10803
10804        $channel = $reg->channelName($channel);
10805        if ($params[0] == 'default_channel' && !$reg->channelExists($params[1])) {
10806            return $this->raiseError('Channel "' . $params[1] . '" does not exist');
10807        }
10808
10809        if ($params[0] == 'preferred_mirror'
10810            && (
10811                !$reg->mirrorExists($channel, $params[1]) &&
10812                (!$reg->channelExists($params[1]) || $channel != $params[1])
10813            )
10814        ) {
10815            $msg  = 'Channel Mirror "' . $params[1] . '" does not exist';
10816            $msg .= ' in your registry for channel "' . $channel . '".';
10817            $msg .= "\n" . 'Attempt to run "pear channel-update ' . $channel .'"';
10818            $msg .= ' if you believe this mirror should exist as you may';
10819            $msg .= ' have outdated channel information.';
10820            return $this->raiseError($msg);
10821        }
10822
10823        if (count($params) == 2) {
10824            array_push($params, 'user');
10825            $layer = 'user';
10826        } else {
10827            $layer = $params[2];
10828        }
10829
10830        array_push($params, $channel);
10831        if (!call_user_func_array(array(&$this->config, 'set'), $params)) {
10832            array_pop($params);
10833            $failmsg = "config-set (" . implode(", ", $params) . ") failed, channel $channel";
10834        } else {
10835            $this->config->store($layer);
10836        }
10837
10838        if ($failmsg) {
10839            return $this->raiseError($failmsg);
10840        }
10841
10842        $this->ui->outputData('config-set succeeded', $command);
10843        return true;
10844    }
10845
10846    function doConfigHelp($command, $options, $params)
10847    {
10848        if (empty($params)) {
10849            $params = $this->config->getKeys();
10850        }
10851
10852        $data['caption']  = "Config help" . ((count($params) == 1) ? " for $params[0]" : '');
10853        $data['headline'] = array('Name', 'Type', 'Description');
10854        $data['border']   = true;
10855        foreach ($params as $name) {
10856            $type = $this->config->getType($name);
10857            $docs = $this->config->getDocs($name);
10858            if ($type == 'set') {
10859                $docs = rtrim($docs) . "\nValid set: " .
10860                    implode(' ', $this->config->getSetValues($name));
10861            }
10862
10863            $data['data'][] = array($name, $type, $docs);
10864        }
10865
10866        $this->ui->outputData($data, $command);
10867    }
10868
10869    function doConfigCreate($command, $options, $params)
10870    {
10871        if (count($params) != 2) {
10872            return PEAR::raiseError('config-create: must have 2 parameters, root path and ' .
10873                'filename to save as');
10874        }
10875
10876        $root = $params[0];
10877        // Clean up the DIRECTORY_SEPARATOR mess
10878        $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
10879        $root = preg_replace(array('!\\\\+!', '!/+!', "!$ds2+!"),
10880                             array('/', '/', '/'),
10881                            $root);
10882        if ($root{0} != '/') {
10883            if (!isset($options['windows'])) {
10884                return PEAR::raiseError('Root directory must be an absolute path beginning ' .
10885                    'with "/", was: "' . $root . '"');
10886            }
10887
10888            if (!preg_match('/^[A-Za-z]:/', $root)) {
10889                return PEAR::raiseError('Root directory must be an absolute path beginning ' .
10890                    'with "\\" or "C:\\", was: "' . $root . '"');
10891            }
10892        }
10893
10894        $windows = isset($options['windows']);
10895        if ($windows) {
10896            $root = str_replace('/', '\\', $root);
10897        }
10898
10899        if (!file_exists($params[1]) && !@touch($params[1])) {
10900            return PEAR::raiseError('Could not create "' . $params[1] . '"');
10901        }
10902
10903        $params[1] = realpath($params[1]);
10904        $config = &new PEAR_Config($params[1], '#no#system#config#', false, false);
10905        if ($root{strlen($root) - 1} == '/') {
10906            $root = substr($root, 0, strlen($root) - 1);
10907        }
10908
10909        $config->noRegistry();
10910        $config->set('php_dir', $windows ? "$root\\pear\\php" : "$root/pear/php", 'user');
10911        $config->set('data_dir', $windows ? "$root\\pear\\data" : "$root/pear/data");
10912        $config->set('www_dir', $windows ? "$root\\pear\\www" : "$root/pear/www");
10913        $config->set('cfg_dir', $windows ? "$root\\pear\\cfg" : "$root/pear/cfg");
10914        $config->set('ext_dir', $windows ? "$root\\pear\\ext" : "$root/pear/ext");
10915        $config->set('doc_dir', $windows ? "$root\\pear\\docs" : "$root/pear/docs");
10916        $config->set('test_dir', $windows ? "$root\\pear\\tests" : "$root/pear/tests");
10917        $config->set('cache_dir', $windows ? "$root\\pear\\cache" : "$root/pear/cache");
10918        $config->set('download_dir', $windows ? "$root\\pear\\download" : "$root/pear/download");
10919        $config->set('temp_dir', $windows ? "$root\\pear\\temp" : "$root/pear/temp");
10920        $config->set('bin_dir', $windows ? "$root\\pear" : "$root/pear");
10921        $config->writeConfigFile();
10922        $this->_showConfig($config);
10923        $this->ui->outputData('Successfully created default configuration file "' . $params[1] . '"',
10924            $command);
10925    }
10926
10927    function _showConfig(&$config)
10928    {
10929        $params = array('user');
10930        $keys = $config->getKeys();
10931        sort($keys);
10932        $channel = 'pear.php.net';
10933        $data = array('caption' => 'Configuration (channel ' . $channel . '):');
10934        foreach ($keys as $key) {
10935            $type = $config->getType($key);
10936            $value = $config->get($key, 'user', $channel);
10937            if ($type == 'password' && $value) {
10938                $value = '********';
10939            }
10940
10941            if ($value === false) {
10942                $value = 'false';
10943            } elseif ($value === true) {
10944                $value = 'true';
10945            }
10946            $data['data'][$config->getGroup($key)][] =
10947                array($config->getPrompt($key) , $key, $value);
10948        }
10949
10950        foreach ($config->getLayers() as $layer) {
10951            $data['data']['Config Files'][] =
10952                array(ucfirst($layer) . ' Configuration File', 'Filename' ,
10953                    $config->getConfFile($layer));
10954        }
10955
10956        $this->ui->outputData($data, 'config-show');
10957        return true;
10958    }
10959
10960    /**
10961     * Checks if a layer is defined or not
10962     *
10963     * @param string $layer The layer to search for
10964     * @return mixed False on no error or the error message
10965     */
10966    function _checkLayer($layer = null)
10967    {
10968        if (!empty($layer) && $layer != 'default') {
10969            $layers = $this->config->getLayers();
10970            if (!in_array($layer, $layers)) {
10971                return " only the layers: \"" . implode('" or "', $layers) . "\" are supported";
10972            }
10973        }
10974
10975        return false;
10976    }
10977}PEAR-1.9.4/PEAR/Command/Install.xml0000644000076500000240000002057611605156614015503 0ustar  helgistaff<commands version="1.0">
10978 <install>
10979  <summary>Install Package</summary>
10980  <function>doInstall</function>
10981  <shortcut>i</shortcut>
10982  <options>
10983   <force>
10984    <shortopt>f</shortopt>
10985    <doc>will overwrite newer installed packages</doc>
10986   </force>
10987   <loose>
10988    <shortopt>l</shortopt>
10989    <doc>do not check for recommended dependency version</doc>
10990   </loose>
10991   <nodeps>
10992    <shortopt>n</shortopt>
10993    <doc>ignore dependencies, install anyway</doc>
10994   </nodeps>
10995   <register-only>
10996    <shortopt>r</shortopt>
10997    <doc>do not install files, only register the package as installed</doc>
10998   </register-only>
10999   <soft>
11000    <shortopt>s</shortopt>
11001    <doc>soft install, fail silently, or upgrade if already installed</doc>
11002   </soft>
11003   <nobuild>
11004    <shortopt>B</shortopt>
11005    <doc>don&#039;t build C extensions</doc>
11006   </nobuild>
11007   <nocompress>
11008    <shortopt>Z</shortopt>
11009    <doc>request uncompressed files when downloading</doc>
11010   </nocompress>
11011   <installroot>
11012    <shortopt>R</shortopt>
11013    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT), use packagingroot for RPM</doc>
11014    <arg>DIR</arg>
11015   </installroot>
11016   <packagingroot>
11017    <shortopt>P</shortopt>
11018    <doc>root directory used when packaging files, like RPM packaging</doc>
11019    <arg>DIR</arg>
11020   </packagingroot>
11021   <ignore-errors>
11022    <shortopt></shortopt>
11023    <doc>force install even if there were errors</doc>
11024   </ignore-errors>
11025   <alldeps>
11026    <shortopt>a</shortopt>
11027    <doc>install all required and optional dependencies</doc>
11028   </alldeps>
11029   <onlyreqdeps>
11030    <shortopt>o</shortopt>
11031    <doc>install all required dependencies</doc>
11032   </onlyreqdeps>
11033   <offline>
11034    <shortopt>O</shortopt>
11035    <doc>do not attempt to download any urls or contact channels</doc>
11036   </offline>
11037   <pretend>
11038    <shortopt>p</shortopt>
11039    <doc>Only list the packages that would be downloaded</doc>
11040   </pretend>
11041  </options>
11042  <doc>[channel/]&lt;package&gt; ...
11043Installs one or more PEAR packages.  You can specify a package to
11044install in four ways:
11045
11046&quot;Package-1.0.tgz&quot; : installs from a local file
11047
11048&quot;http://example.com/Package-1.0.tgz"; : installs from
11049anywhere on the net.
11050
11051&quot;package.xml&quot; : installs the package described in
11052package.xml.  Useful for testing, or for wrapping a PEAR package in
11053another package manager such as RPM.
11054
11055&quot;Package[-version/state][.tar]&quot; : queries your default channel&#039;s server
11056({config master_server}) and downloads the newest package with
11057the preferred quality/state ({config preferred_state}).
11058
11059To retrieve Package version 1.1, use &quot;Package-1.1,&quot; to retrieve
11060Package state beta, use &quot;Package-beta.&quot;  To retrieve an uncompressed
11061file, append .tar (make sure there is no file by the same name first)
11062
11063To download a package from another channel, prefix with the channel name like
11064&quot;channel/Package&quot;
11065
11066More than one package may be specified at once.  It is ok to mix these
11067four ways of specifying packages.
11068</doc>
11069 </install>
11070 <upgrade>
11071  <summary>Upgrade Package</summary>
11072  <function>doInstall</function>
11073  <shortcut>up</shortcut>
11074  <options>
11075   <channel>
11076    <shortopt>c</shortopt>
11077    <doc>upgrade packages from a specific channel</doc>
11078    <arg>CHAN</arg>
11079   </channel>
11080   <force>
11081    <shortopt>f</shortopt>
11082    <doc>overwrite newer installed packages</doc>
11083   </force>
11084   <loose>
11085    <shortopt>l</shortopt>
11086    <doc>do not check for recommended dependency version</doc>
11087   </loose>
11088   <nodeps>
11089    <shortopt>n</shortopt>
11090    <doc>ignore dependencies, upgrade anyway</doc>
11091   </nodeps>
11092   <register-only>
11093    <shortopt>r</shortopt>
11094    <doc>do not install files, only register the package as upgraded</doc>
11095   </register-only>
11096   <nobuild>
11097    <shortopt>B</shortopt>
11098    <doc>don&#039;t build C extensions</doc>
11099   </nobuild>
11100   <nocompress>
11101    <shortopt>Z</shortopt>
11102    <doc>request uncompressed files when downloading</doc>
11103   </nocompress>
11104   <installroot>
11105    <shortopt>R</shortopt>
11106    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT)</doc>
11107    <arg>DIR</arg>
11108   </installroot>
11109   <ignore-errors>
11110    <shortopt></shortopt>
11111    <doc>force install even if there were errors</doc>
11112   </ignore-errors>
11113   <alldeps>
11114    <shortopt>a</shortopt>
11115    <doc>install all required and optional dependencies</doc>
11116   </alldeps>
11117   <onlyreqdeps>
11118    <shortopt>o</shortopt>
11119    <doc>install all required dependencies</doc>
11120   </onlyreqdeps>
11121   <offline>
11122    <shortopt>O</shortopt>
11123    <doc>do not attempt to download any urls or contact channels</doc>
11124   </offline>
11125   <pretend>
11126    <shortopt>p</shortopt>
11127    <doc>Only list the packages that would be downloaded</doc>
11128   </pretend>
11129  </options>
11130  <doc>&lt;package&gt; ...
11131Upgrades one or more PEAR packages.  See documentation for the
11132&quot;install&quot; command for ways to specify a package.
11133
11134When upgrading, your package will be updated if the provided new
11135package has a higher version number (use the -f option if you need to
11136upgrade anyway).
11137
11138More than one package may be specified at once.
11139</doc>
11140 </upgrade>
11141 <upgrade-all>
11142  <summary>Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]</summary>
11143  <function>doUpgradeAll</function>
11144  <shortcut>ua</shortcut>
11145  <options>
11146   <channel>
11147    <shortopt>c</shortopt>
11148    <doc>upgrade packages from a specific channel</doc>
11149    <arg>CHAN</arg>
11150   </channel>
11151   <nodeps>
11152    <shortopt>n</shortopt>
11153    <doc>ignore dependencies, upgrade anyway</doc>
11154   </nodeps>
11155   <register-only>
11156    <shortopt>r</shortopt>
11157    <doc>do not install files, only register the package as upgraded</doc>
11158   </register-only>
11159   <nobuild>
11160    <shortopt>B</shortopt>
11161    <doc>don&#039;t build C extensions</doc>
11162   </nobuild>
11163   <nocompress>
11164    <shortopt>Z</shortopt>
11165    <doc>request uncompressed files when downloading</doc>
11166   </nocompress>
11167   <installroot>
11168    <shortopt>R</shortopt>
11169    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT), use packagingroot for RPM</doc>
11170    <arg>DIR</arg>
11171   </installroot>
11172   <ignore-errors>
11173    <shortopt></shortopt>
11174    <doc>force install even if there were errors</doc>
11175   </ignore-errors>
11176   <loose>
11177    <shortopt></shortopt>
11178    <doc>do not check for recommended dependency version</doc>
11179   </loose>
11180  </options>
11181  <doc>
11182WARNING: This function is deprecated in favor of using the upgrade command with no params
11183
11184Upgrades all packages that have a newer release available.  Upgrades are
11185done only if there is a release available of the state specified in
11186&quot;preferred_state&quot; (currently {config preferred_state}), or a state considered
11187more stable.
11188</doc>
11189 </upgrade-all>
11190 <uninstall>
11191  <summary>Un-install Package</summary>
11192  <function>doUninstall</function>
11193  <shortcut>un</shortcut>
11194  <options>
11195   <nodeps>
11196    <shortopt>n</shortopt>
11197    <doc>ignore dependencies, uninstall anyway</doc>
11198   </nodeps>
11199   <register-only>
11200    <shortopt>r</shortopt>
11201    <doc>do not remove files, only register the packages as not installed</doc>
11202   </register-only>
11203   <installroot>
11204    <shortopt>R</shortopt>
11205    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT)</doc>
11206    <arg>DIR</arg>
11207   </installroot>
11208   <ignore-errors>
11209    <shortopt></shortopt>
11210    <doc>force install even if there were errors</doc>
11211   </ignore-errors>
11212   <offline>
11213    <shortopt>O</shortopt>
11214    <doc>do not attempt to uninstall remotely</doc>
11215   </offline>
11216  </options>
11217  <doc>[channel/]&lt;package&gt; ...
11218Uninstalls one or more PEAR packages.  More than one package may be
11219specified at once.  Prefix with channel name to uninstall from a
11220channel not in your default channel ({config default_channel})
11221</doc>
11222 </uninstall>
11223 <bundle>
11224  <summary>Unpacks a Pecl Package</summary>
11225  <function>doBundle</function>
11226  <shortcut>bun</shortcut>
11227  <options>
11228   <destination>
11229    <shortopt>d</shortopt>
11230    <doc>Optional destination directory for unpacking (defaults to current path or &quot;ext&quot; if exists)</doc>
11231    <arg>DIR</arg>
11232   </destination>
11233   <force>
11234    <shortopt>f</shortopt>
11235    <doc>Force the unpacking even if there were errors in the package</doc>
11236   </force>
11237  </options>
11238  <doc>&lt;package&gt;
11239Unpacks a Pecl Package into the selected location. It will download the
11240package if needed.
11241</doc>
11242 </bundle>
11243 <run-scripts>
11244  <summary>Run Post-Install Scripts bundled with a package</summary>
11245  <function>doRunScripts</function>
11246  <shortcut>rs</shortcut>
11247  <options />
11248  <doc>&lt;package&gt;
11249Run post-installation scripts in package &lt;package&gt;, if any exist.
11250</doc>
11251 </run-scripts>
11252</commands>PEAR-1.9.4/PEAR/Command/Install.php0000644000076500000240000014323611605156614015471 0ustar  helgistaff<?php
11253/**
11254 * PEAR_Command_Install (install, upgrade, upgrade-all, uninstall, bundle, run-scripts commands)
11255 *
11256 * PHP versions 4 and 5
11257 *
11258 * @category   pear
11259 * @package    PEAR
11260 * @author     Stig Bakken <ssb@php.net>
11261 * @author     Greg Beaver <cellog@php.net>
11262 * @copyright  1997-2009 The Authors
11263 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
11264 * @version    CVS: $Id: Install.php 313023 2011-07-06 19:17:11Z dufuz $
11265 * @link       http://pear.php.net/package/PEAR
11266 * @since      File available since Release 0.1
11267 */
11268
11269/**
11270 * base class
11271 */
11272require_once 'PEAR/Command/Common.php';
11273
11274/**
11275 * PEAR commands for installation or deinstallation/upgrading of
11276 * packages.
11277 *
11278 * @category   pear
11279 * @package    PEAR
11280 * @author     Stig Bakken <ssb@php.net>
11281 * @author     Greg Beaver <cellog@php.net>
11282 * @copyright  1997-2009 The Authors
11283 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
11284 * @version    Release: 1.9.4
11285 * @link       http://pear.php.net/package/PEAR
11286 * @since      Class available since Release 0.1
11287 */
11288class PEAR_Command_Install extends PEAR_Command_Common
11289{
11290    // {{{ properties
11291
11292    var $commands = array(
11293        'install' => array(
11294            'summary' => 'Install Package',
11295            'function' => 'doInstall',
11296            'shortcut' => 'i',
11297            'options' => array(
11298                'force' => array(
11299                    'shortopt' => 'f',
11300                    'doc' => 'will overwrite newer installed packages',
11301                    ),
11302                'loose' => array(
11303                    'shortopt' => 'l',
11304                    'doc' => 'do not check for recommended dependency version',
11305                    ),
11306                'nodeps' => array(
11307                    'shortopt' => 'n',
11308                    'doc' => 'ignore dependencies, install anyway',
11309                    ),
11310                'register-only' => array(
11311                    'shortopt' => 'r',
11312                    'doc' => 'do not install files, only register the package as installed',
11313                    ),
11314                'soft' => array(
11315                    'shortopt' => 's',
11316                    'doc' => 'soft install, fail silently, or upgrade if already installed',
11317                    ),
11318                'nobuild' => array(
11319                    'shortopt' => 'B',
11320                    'doc' => 'don\'t build C extensions',
11321                    ),
11322                'nocompress' => array(
11323                    'shortopt' => 'Z',
11324                    'doc' => 'request uncompressed files when downloading',
11325                    ),
11326                'installroot' => array(
11327                    'shortopt' => 'R',
11328                    'arg' => 'DIR',
11329                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
11330                    ),
11331                'packagingroot' => array(
11332                    'shortopt' => 'P',
11333                    'arg' => 'DIR',
11334                    'doc' => 'root directory used when packaging files, like RPM packaging',
11335                    ),
11336                'ignore-errors' => array(
11337                    'doc' => 'force install even if there were errors',
11338                    ),
11339                'alldeps' => array(
11340                    'shortopt' => 'a',
11341                    'doc' => 'install all required and optional dependencies',
11342                    ),
11343                'onlyreqdeps' => array(
11344                    'shortopt' => 'o',
11345                    'doc' => 'install all required dependencies',
11346                    ),
11347                'offline' => array(
11348                    'shortopt' => 'O',
11349                    'doc' => 'do not attempt to download any urls or contact channels',
11350                    ),
11351                'pretend' => array(
11352                    'shortopt' => 'p',
11353                    'doc' => 'Only list the packages that would be downloaded',
11354                    ),
11355                ),
11356            'doc' => '[channel/]<package> ...
11357Installs one or more PEAR packages.  You can specify a package to
11358install in four ways:
11359
11360"Package-1.0.tgz" : installs from a local file
11361
11362"http://example.com/Package-1.0.tgz" : installs from
11363anywhere on the net.
11364
11365"package.xml" : installs the package described in
11366package.xml.  Useful for testing, or for wrapping a PEAR package in
11367another package manager such as RPM.
11368
11369"Package[-version/state][.tar]" : queries your default channel\'s server
11370({config master_server}) and downloads the newest package with
11371the preferred quality/state ({config preferred_state}).
11372
11373To retrieve Package version 1.1, use "Package-1.1," to retrieve
11374Package state beta, use "Package-beta."  To retrieve an uncompressed
11375file, append .tar (make sure there is no file by the same name first)
11376
11377To download a package from another channel, prefix with the channel name like
11378"channel/Package"
11379
11380More than one package may be specified at once.  It is ok to mix these
11381four ways of specifying packages.
11382'),
11383        'upgrade' => array(
11384            'summary' => 'Upgrade Package',
11385            'function' => 'doInstall',
11386            'shortcut' => 'up',
11387            'options' => array(
11388                'channel' => array(
11389                    'shortopt' => 'c',
11390                    'doc' => 'upgrade packages from a specific channel',
11391                    'arg' => 'CHAN',
11392                    ),
11393                'force' => array(
11394                    'shortopt' => 'f',
11395                    'doc' => 'overwrite newer installed packages',
11396                    ),
11397                'loose' => array(
11398                    'shortopt' => 'l',
11399                    'doc' => 'do not check for recommended dependency version',
11400                    ),
11401                'nodeps' => array(
11402                    'shortopt' => 'n',
11403                    'doc' => 'ignore dependencies, upgrade anyway',
11404                    ),
11405                'register-only' => array(
11406                    'shortopt' => 'r',
11407                    'doc' => 'do not install files, only register the package as upgraded',
11408                    ),
11409                'nobuild' => array(
11410                    'shortopt' => 'B',
11411                    'doc' => 'don\'t build C extensions',
11412                    ),
11413                'nocompress' => array(
11414                    'shortopt' => 'Z',
11415                    'doc' => 'request uncompressed files when downloading',
11416                    ),
11417                'installroot' => array(
11418                    'shortopt' => 'R',
11419                    'arg' => 'DIR',
11420                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
11421                    ),
11422                'ignore-errors' => array(
11423                    'doc' => 'force install even if there were errors',
11424                    ),
11425                'alldeps' => array(
11426                    'shortopt' => 'a',
11427                    'doc' => 'install all required and optional dependencies',
11428                    ),
11429                'onlyreqdeps' => array(
11430                    'shortopt' => 'o',
11431                    'doc' => 'install all required dependencies',
11432                    ),
11433                'offline' => array(
11434                    'shortopt' => 'O',
11435                    'doc' => 'do not attempt to download any urls or contact channels',
11436                    ),
11437                'pretend' => array(
11438                    'shortopt' => 'p',
11439                    'doc' => 'Only list the packages that would be downloaded',
11440                    ),
11441                ),
11442            'doc' => '<package> ...
11443Upgrades one or more PEAR packages.  See documentation for the
11444"install" command for ways to specify a package.
11445
11446When upgrading, your package will be updated if the provided new
11447package has a higher version number (use the -f option if you need to
11448upgrade anyway).
11449
11450More than one package may be specified at once.
11451'),
11452        'upgrade-all' => array(
11453            'summary' => 'Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]',
11454            'function' => 'doUpgradeAll',
11455            'shortcut' => 'ua',
11456            'options' => array(
11457                'channel' => array(
11458                    'shortopt' => 'c',
11459                    'doc' => 'upgrade packages from a specific channel',
11460                    'arg' => 'CHAN',
11461                    ),
11462                'nodeps' => array(
11463                    'shortopt' => 'n',
11464                    'doc' => 'ignore dependencies, upgrade anyway',
11465                    ),
11466                'register-only' => array(
11467                    'shortopt' => 'r',
11468                    'doc' => 'do not install files, only register the package as upgraded',
11469                    ),
11470                'nobuild' => array(
11471                    'shortopt' => 'B',
11472                    'doc' => 'don\'t build C extensions',
11473                    ),
11474                'nocompress' => array(
11475                    'shortopt' => 'Z',
11476                    'doc' => 'request uncompressed files when downloading',
11477                    ),
11478                'installroot' => array(
11479                    'shortopt' => 'R',
11480                    'arg' => 'DIR',
11481                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
11482                    ),
11483                'ignore-errors' => array(
11484                    'doc' => 'force install even if there were errors',
11485                    ),
11486                'loose' => array(
11487                    'doc' => 'do not check for recommended dependency version',
11488                    ),
11489                ),
11490            'doc' => '
11491WARNING: This function is deprecated in favor of using the upgrade command with no params
11492
11493Upgrades all packages that have a newer release available.  Upgrades are
11494done only if there is a release available of the state specified in
11495"preferred_state" (currently {config preferred_state}), or a state considered
11496more stable.
11497'),
11498        'uninstall' => array(
11499            'summary' => 'Un-install Package',
11500            'function' => 'doUninstall',
11501            'shortcut' => 'un',
11502            'options' => array(
11503                'nodeps' => array(
11504                    'shortopt' => 'n',
11505                    'doc' => 'ignore dependencies, uninstall anyway',
11506                    ),
11507                'register-only' => array(
11508                    'shortopt' => 'r',
11509                    'doc' => 'do not remove files, only register the packages as not installed',
11510                    ),
11511                'installroot' => array(
11512                    'shortopt' => 'R',
11513                    'arg' => 'DIR',
11514                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
11515                    ),
11516                'ignore-errors' => array(
11517                    'doc' => 'force install even if there were errors',
11518                    ),
11519                'offline' => array(
11520                    'shortopt' => 'O',
11521                    'doc' => 'do not attempt to uninstall remotely',
11522                    ),
11523                ),
11524            'doc' => '[channel/]<package> ...
11525Uninstalls one or more PEAR packages.  More than one package may be
11526specified at once.  Prefix with channel name to uninstall from a
11527channel not in your default channel ({config default_channel})
11528'),
11529        'bundle' => array(
11530            'summary' => 'Unpacks a Pecl Package',
11531            'function' => 'doBundle',
11532            'shortcut' => 'bun',
11533            'options' => array(
11534                'destination' => array(
11535                   'shortopt' => 'd',
11536                    'arg' => 'DIR',
11537                    'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)',
11538                    ),
11539                'force' => array(
11540                    'shortopt' => 'f',
11541                    'doc' => 'Force the unpacking even if there were errors in the package',
11542                ),
11543            ),
11544            'doc' => '<package>
11545Unpacks a Pecl Package into the selected location. It will download the
11546package if needed.
11547'),
11548        'run-scripts' => array(
11549            'summary' => 'Run Post-Install Scripts bundled with a package',
11550            'function' => 'doRunScripts',
11551            'shortcut' => 'rs',
11552            'options' => array(
11553            ),
11554            'doc' => '<package>
11555Run post-installation scripts in package <package>, if any exist.
11556'),
11557    );
11558
11559    // }}}
11560    // {{{ constructor
11561
11562    /**
11563     * PEAR_Command_Install constructor.
11564     *
11565     * @access public
11566     */
11567    function PEAR_Command_Install(&$ui, &$config)
11568    {
11569        parent::PEAR_Command_Common($ui, $config);
11570    }
11571
11572    // }}}
11573
11574    /**
11575     * For unit testing purposes
11576     */
11577    function &getDownloader(&$ui, $options, &$config)
11578    {
11579        if (!class_exists('PEAR_Downloader')) {
11580            require_once 'PEAR/Downloader.php';
11581        }
11582        $a = &new PEAR_Downloader($ui, $options, $config);
11583        return $a;
11584    }
11585
11586    /**
11587     * For unit testing purposes
11588     */
11589    function &getInstaller(&$ui)
11590    {
11591        if (!class_exists('PEAR_Installer')) {
11592            require_once 'PEAR/Installer.php';
11593        }
11594        $a = &new PEAR_Installer($ui);
11595        return $a;
11596    }
11597
11598    function enableExtension($binaries, $type)
11599    {
11600        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
11601            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
11602        }
11603        $ini = $this->_parseIni($phpini);
11604        if (PEAR::isError($ini)) {
11605            return $ini;
11606        }
11607        $line = 0;
11608        if ($type == 'extsrc' || $type == 'extbin') {
11609            $search = 'extensions';
11610            $enable = 'extension';
11611        } else {
11612            $search = 'zend_extensions';
11613            ob_start();
11614            phpinfo(INFO_GENERAL);
11615            $info = ob_get_contents();
11616            ob_end_clean();
11617            $debug = function_exists('leak') ? '_debug' : '';
11618            $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
11619            $enable = 'zend_extension' . $debug . $ts;
11620        }
11621        foreach ($ini[$search] as $line => $extension) {
11622            if (in_array($extension, $binaries, true) || in_array(
11623                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
11624                // already enabled - assume if one is, all are
11625                return true;
11626            }
11627        }
11628        if ($line) {
11629            $newini = array_slice($ini['all'], 0, $line);
11630        } else {
11631            $newini = array();
11632        }
11633        foreach ($binaries as $binary) {
11634            if ($ini['extension_dir']) {
11635                $binary = basename($binary);
11636            }
11637            $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n");
11638        }
11639        $newini = array_merge($newini, array_slice($ini['all'], $line));
11640        $fp = @fopen($phpini, 'wb');
11641        if (!$fp) {
11642            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
11643        }
11644        foreach ($newini as $line) {
11645            fwrite($fp, $line);
11646        }
11647        fclose($fp);
11648        return true;
11649    }
11650
11651    function disableExtension($binaries, $type)
11652    {
11653        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
11654            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
11655        }
11656        $ini = $this->_parseIni($phpini);
11657        if (PEAR::isError($ini)) {
11658            return $ini;
11659        }
11660        $line = 0;
11661        if ($type == 'extsrc' || $type == 'extbin') {
11662            $search = 'extensions';
11663            $enable = 'extension';
11664        } else {
11665            $search = 'zend_extensions';
11666            ob_start();
11667            phpinfo(INFO_GENERAL);
11668            $info = ob_get_contents();
11669            ob_end_clean();
11670            $debug = function_exists('leak') ? '_debug' : '';
11671            $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
11672            $enable = 'zend_extension' . $debug . $ts;
11673        }
11674        $found = false;
11675        foreach ($ini[$search] as $line => $extension) {
11676            if (in_array($extension, $binaries, true) || in_array(
11677                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
11678                $found = true;
11679                break;
11680            }
11681        }
11682        if (!$found) {
11683            // not enabled
11684            return true;
11685        }
11686        $fp = @fopen($phpini, 'wb');
11687        if (!$fp) {
11688            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
11689        }
11690        if ($line) {
11691            $newini = array_slice($ini['all'], 0, $line);
11692            // delete the enable line
11693            $newini = array_merge($newini, array_slice($ini['all'], $line + 1));
11694        } else {
11695            $newini = array_slice($ini['all'], 1);
11696        }
11697        foreach ($newini as $line) {
11698            fwrite($fp, $line);
11699        }
11700        fclose($fp);
11701        return true;
11702    }
11703
11704    function _parseIni($filename)
11705    {
11706        if (!file_exists($filename)) {
11707            return PEAR::raiseError('php.ini "' . $filename . '" does not exist');
11708        }
11709
11710        if (filesize($filename) > 300000) {
11711            return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting');
11712        }
11713
11714        ob_start();
11715        phpinfo(INFO_GENERAL);
11716        $info = ob_get_contents();
11717        ob_end_clean();
11718        $debug = function_exists('leak') ? '_debug' : '';
11719        $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
11720        $zend_extension_line = 'zend_extension' . $debug . $ts;
11721        $all = @file($filename);
11722        if (!$all) {
11723            return PEAR::raiseError('php.ini "' . $filename .'" could not be read');
11724        }
11725        $zend_extensions = $extensions = array();
11726        // assume this is right, but pull from the php.ini if it is found
11727        $extension_dir = ini_get('extension_dir');
11728        foreach ($all as $linenum => $line) {
11729            $line = trim($line);
11730            if (!$line) {
11731                continue;
11732            }
11733            if ($line[0] == ';') {
11734                continue;
11735            }
11736            if (strtolower(substr($line, 0, 13)) == 'extension_dir') {
11737                $line = trim(substr($line, 13));
11738                if ($line[0] == '=') {
11739                    $x = trim(substr($line, 1));
11740                    $x = explode(';', $x);
11741                    $extension_dir = str_replace('"', '', array_shift($x));
11742                    continue;
11743                }
11744            }
11745            if (strtolower(substr($line, 0, 9)) == 'extension') {
11746                $line = trim(substr($line, 9));
11747                if ($line[0] == '=') {
11748                    $x = trim(substr($line, 1));
11749                    $x = explode(';', $x);
11750                    $extensions[$linenum] = str_replace('"', '', array_shift($x));
11751                    continue;
11752                }
11753            }
11754            if (strtolower(substr($line, 0, strlen($zend_extension_line))) ==
11755                  $zend_extension_line) {
11756                $line = trim(substr($line, strlen($zend_extension_line)));
11757                if ($line[0] == '=') {
11758                    $x = trim(substr($line, 1));
11759                    $x = explode(';', $x);
11760                    $zend_extensions[$linenum] = str_replace('"', '', array_shift($x));
11761                    continue;
11762                }
11763            }
11764        }
11765        return array(
11766            'extensions' => $extensions,
11767            'zend_extensions' => $zend_extensions,
11768            'extension_dir' => $extension_dir,
11769            'all' => $all,
11770        );
11771    }
11772
11773    // {{{ doInstall()
11774
11775    function doInstall($command, $options, $params)
11776    {
11777        if (!class_exists('PEAR_PackageFile')) {
11778            require_once 'PEAR/PackageFile.php';
11779        }
11780
11781        if (isset($options['installroot']) && isset($options['packagingroot'])) {
11782            return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot');
11783        }
11784
11785        $reg = &$this->config->getRegistry();
11786        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
11787        if (!$reg->channelExists($channel)) {
11788            return $this->raiseError('Channel "' . $channel . '" does not exist');
11789        }
11790
11791        if (empty($this->installer)) {
11792            $this->installer = &$this->getInstaller($this->ui);
11793        }
11794
11795        if ($command == 'upgrade' || $command == 'upgrade-all') {
11796            // If people run the upgrade command but pass nothing, emulate a upgrade-all
11797            if ($command == 'upgrade' && empty($params)) {
11798                return $this->doUpgradeAll($command, $options, $params);
11799            }
11800            $options['upgrade'] = true;
11801        } else {
11802            $packages = $params;
11803        }
11804
11805        $instreg = &$reg; // instreg used to check if package is installed
11806        if (isset($options['packagingroot']) && !isset($options['upgrade'])) {
11807            $packrootphp_dir = $this->installer->_prependPath(
11808                $this->config->get('php_dir', null, 'pear.php.net'),
11809                $options['packagingroot']);
11810            $instreg = new PEAR_Registry($packrootphp_dir); // other instreg!
11811
11812            if ($this->config->get('verbose') > 2) {
11813                $this->ui->outputData('using package root: ' . $options['packagingroot']);
11814            }
11815        }
11816
11817        $abstractpackages = $otherpackages = array();
11818        // parse params
11819        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
11820
11821        foreach ($params as $param) {
11822            if (strpos($param, 'http://') === 0) {
11823                $otherpackages[] = $param;
11824                continue;
11825            }
11826
11827            if (strpos($param, 'channel://') === false && @file_exists($param)) {
11828                if (isset($options['force'])) {
11829                    $otherpackages[] = $param;
11830                    continue;
11831                }
11832
11833                $pkg = new PEAR_PackageFile($this->config);
11834                $pf  = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING);
11835                if (PEAR::isError($pf)) {
11836                    $otherpackages[] = $param;
11837                    continue;
11838                }
11839
11840                $exists   = $reg->packageExists($pf->getPackage(), $pf->getChannel());
11841                $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel());
11842                $version_compare = version_compare($pf->getVersion(), $pversion, '<=');
11843                if ($exists && $version_compare) {
11844                    if ($this->config->get('verbose')) {
11845                        $this->ui->outputData('Ignoring installed package ' .
11846                            $reg->parsedPackageNameToString(
11847                            array('package' => $pf->getPackage(),
11848                                  'channel' => $pf->getChannel()), true));
11849                    }
11850                    continue;
11851                }
11852                $otherpackages[] = $param;
11853                continue;
11854            }
11855
11856            $e = $reg->parsePackageName($param, $channel);
11857            if (PEAR::isError($e)) {
11858                $otherpackages[] = $param;
11859            } else {
11860                $abstractpackages[] = $e;
11861            }
11862        }
11863        PEAR::staticPopErrorHandling();
11864
11865        // if there are any local package .tgz or remote static url, we can't
11866        // filter.  The filter only works for abstract packages
11867        if (count($abstractpackages) && !isset($options['force'])) {
11868            // when not being forced, only do necessary upgrades/installs
11869            if (isset($options['upgrade'])) {
11870                $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command);
11871            } else {
11872                $count = count($abstractpackages);
11873                foreach ($abstractpackages as $i => $package) {
11874                    if (isset($package['group'])) {
11875                        // do not filter out install groups
11876                        continue;
11877                    }
11878
11879                    if ($instreg->packageExists($package['package'], $package['channel'])) {
11880                        if ($count > 1) {
11881                            if ($this->config->get('verbose')) {
11882                                $this->ui->outputData('Ignoring installed package ' .
11883                                    $reg->parsedPackageNameToString($package, true));
11884                            }
11885                            unset($abstractpackages[$i]);
11886                        } elseif ($count === 1) {
11887                            // Lets try to upgrade it since it's already installed
11888                            $options['upgrade'] = true;
11889                        }
11890                    }
11891                }
11892            }
11893            $abstractpackages =
11894                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
11895        } elseif (count($abstractpackages)) {
11896            $abstractpackages =
11897                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
11898        }
11899
11900        $packages = array_merge($abstractpackages, $otherpackages);
11901        if (!count($packages)) {
11902            $c = '';
11903            if (isset($options['channel'])){
11904                $c .= ' in channel "' . $options['channel'] . '"';
11905            }
11906            $this->ui->outputData('Nothing to ' . $command . $c);
11907            return true;
11908        }
11909
11910        $this->downloader = &$this->getDownloader($this->ui, $options, $this->config);
11911        $errors = $downloaded = $binaries = array();
11912        $downloaded = &$this->downloader->download($packages);
11913        if (PEAR::isError($downloaded)) {
11914            return $this->raiseError($downloaded);
11915        }
11916
11917        $errors = $this->downloader->getErrorMsgs();
11918        if (count($errors)) {
11919            $err = array();
11920            $err['data'] = array();
11921            foreach ($errors as $error) {
11922                if ($error !== null) {
11923                    $err['data'][] = array($error);
11924                }
11925            }
11926
11927            if (!empty($err['data'])) {
11928                $err['headline'] = 'Install Errors';
11929                $this->ui->outputData($err);
11930            }
11931
11932            if (!count($downloaded)) {
11933                return $this->raiseError("$command failed");
11934            }
11935        }
11936
11937        $data = array(
11938            'headline' => 'Packages that would be Installed'
11939        );
11940
11941        if (isset($options['pretend'])) {
11942            foreach ($downloaded as $package) {
11943                $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage()));
11944            }
11945            $this->ui->outputData($data, 'pretend');
11946            return true;
11947        }
11948
11949        $this->installer->setOptions($options);
11950        $this->installer->sortPackagesForInstall($downloaded);
11951        if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) {
11952            $this->raiseError($err->getMessage());
11953            return true;
11954        }
11955
11956        $binaries = $extrainfo = array();
11957        foreach ($downloaded as $param) {
11958            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
11959            $info = $this->installer->install($param, $options);
11960            PEAR::staticPopErrorHandling();
11961            if (PEAR::isError($info)) {
11962                $oldinfo = $info;
11963                $pkg = &$param->getPackageFile();
11964                if ($info->getCode() != PEAR_INSTALLER_NOBINARY) {
11965                    if (!($info = $pkg->installBinary($this->installer))) {
11966                        $this->ui->outputData('ERROR: ' .$oldinfo->getMessage());
11967                        continue;
11968                    }
11969
11970                    // we just installed a different package than requested,
11971                    // let's change the param and info so that the rest of this works
11972                    $param = $info[0];
11973                    $info  = $info[1];
11974                }
11975            }
11976
11977            if (!is_array($info)) {
11978                return $this->raiseError("$command failed");
11979            }
11980
11981            if ($param->getPackageType() == 'extsrc' ||
11982                  $param->getPackageType() == 'extbin' ||
11983                  $param->getPackageType() == 'zendextsrc' ||
11984                  $param->getPackageType() == 'zendextbin'
11985            ) {
11986                $pkg = &$param->getPackageFile();
11987                if ($instbin = $pkg->getInstalledBinary()) {
11988                    $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel());
11989                } else {
11990                    $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel());
11991                }
11992
11993                foreach ($instpkg->getFilelist() as $name => $atts) {
11994                    $pinfo = pathinfo($atts['installed_as']);
11995                    if (!isset($pinfo['extension']) ||
11996                          in_array($pinfo['extension'], array('c', 'h'))
11997                    ) {
11998                        continue; // make sure we don't match php_blah.h
11999                    }
12000
12001                    if ((strpos($pinfo['basename'], 'php_') === 0 &&
12002                          $pinfo['extension'] == 'dll') ||
12003                          // most unices
12004                          $pinfo['extension'] == 'so' ||
12005                          // hp-ux
12006                          $pinfo['extension'] == 'sl') {
12007                        $binaries[] = array($atts['installed_as'], $pinfo);
12008                        break;
12009                    }
12010                }
12011
12012                if (count($binaries)) {
12013                    foreach ($binaries as $pinfo) {
12014                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
12015                        $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType());
12016                        PEAR::staticPopErrorHandling();
12017                        if (PEAR::isError($ret)) {
12018                            $extrainfo[] = $ret->getMessage();
12019                            if ($param->getPackageType() == 'extsrc' ||
12020                                  $param->getPackageType() == 'extbin') {
12021                                $exttype = 'extension';
12022                            } else {
12023                                ob_start();
12024                                phpinfo(INFO_GENERAL);
12025                                $info = ob_get_contents();
12026                                ob_end_clean();
12027                                $debug = function_exists('leak') ? '_debug' : '';
12028                                $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
12029                                $exttype = 'zend_extension' . $debug . $ts;
12030                            }
12031                            $extrainfo[] = 'You should add "' . $exttype . '=' .
12032                                $pinfo[1]['basename'] . '" to php.ini';
12033                        } else {
12034                            $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() .
12035                                ' enabled in php.ini';
12036                        }
12037                    }
12038                }
12039            }
12040
12041            if ($this->config->get('verbose') > 0) {
12042                $chan = $param->getChannel();
12043                $label = $reg->parsedPackageNameToString(
12044                    array(
12045                        'channel' => $chan,
12046                        'package' => $param->getPackage(),
12047                        'version' => $param->getVersion(),
12048                    ));
12049                $out = array('data' => "$command ok: $label");
12050                if (isset($info['release_warnings'])) {
12051                    $out['release_warnings'] = $info['release_warnings'];
12052                }
12053                $this->ui->outputData($out, $command);
12054
12055                if (!isset($options['register-only']) && !isset($options['offline'])) {
12056                    if ($this->config->isDefinedLayer('ftp')) {
12057                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
12058                        $info = $this->installer->ftpInstall($param);
12059                        PEAR::staticPopErrorHandling();
12060                        if (PEAR::isError($info)) {
12061                            $this->ui->outputData($info->getMessage());
12062                            $this->ui->outputData("remote install failed: $label");
12063                        } else {
12064                            $this->ui->outputData("remote install ok: $label");
12065                        }
12066                    }
12067                }
12068            }
12069
12070            $deps = $param->getDeps();
12071            if ($deps) {
12072                if (isset($deps['group'])) {
12073                    $groups = $deps['group'];
12074                    if (!isset($groups[0])) {
12075                        $groups = array($groups);
12076                    }
12077
12078                    foreach ($groups as $group) {
12079                        if ($group['attribs']['name'] == 'default') {
12080                            // default group is always installed, unless the user
12081                            // explicitly chooses to install another group
12082                            continue;
12083                        }
12084                        $extrainfo[] = $param->getPackage() . ': Optional feature ' .
12085                            $group['attribs']['name'] . ' available (' .
12086                            $group['attribs']['hint'] . ')';
12087                    }
12088
12089                    $extrainfo[] = $param->getPackage() .
12090                        ': To install optional features use "pear install ' .
12091                        $reg->parsedPackageNameToString(
12092                            array('package' => $param->getPackage(),
12093                                  'channel' => $param->getChannel()), true) .
12094                              '#featurename"';
12095                }
12096            }
12097
12098            $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel());
12099            // $pkg may be NULL if install is a 'fake' install via --packagingroot
12100            if (is_object($pkg)) {
12101                $pkg->setConfig($this->config);
12102                if ($list = $pkg->listPostinstallScripts()) {
12103                    $pn = $reg->parsedPackageNameToString(array('channel' =>
12104                       $param->getChannel(), 'package' => $param->getPackage()), true);
12105                    $extrainfo[] = $pn . ' has post-install scripts:';
12106                    foreach ($list as $file) {
12107                        $extrainfo[] = $file;
12108                    }
12109                    $extrainfo[] = $param->getPackage() .
12110                        ': Use "pear run-scripts ' . $pn . '" to finish setup.';
12111                    $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES';
12112                }
12113            }
12114        }
12115
12116        if (count($extrainfo)) {
12117            foreach ($extrainfo as $info) {
12118                $this->ui->outputData($info);
12119            }
12120        }
12121
12122        return true;
12123    }
12124
12125    // }}}
12126    // {{{ doUpgradeAll()
12127
12128    function doUpgradeAll($command, $options, $params)
12129    {
12130        $reg = &$this->config->getRegistry();
12131        $upgrade = array();
12132
12133        if (isset($options['channel'])) {
12134            $channels = array($options['channel']);
12135        } else {
12136            $channels = $reg->listChannels();
12137        }
12138
12139        foreach ($channels as $channel) {
12140            if ($channel == '__uri') {
12141                continue;
12142            }
12143
12144            // parse name with channel
12145            foreach ($reg->listPackages($channel) as $name) {
12146                $upgrade[] = $reg->parsedPackageNameToString(array(
12147                        'channel' => $channel,
12148                        'package' => $name
12149                    ));
12150            }
12151        }
12152
12153        $err = $this->doInstall($command, $options, $upgrade);
12154        if (PEAR::isError($err)) {
12155            $this->ui->outputData($err->getMessage(), $command);
12156        }
12157   }
12158
12159    // }}}
12160    // {{{ doUninstall()
12161
12162    function doUninstall($command, $options, $params)
12163    {
12164        if (count($params) < 1) {
12165            return $this->raiseError("Please supply the package(s) you want to uninstall");
12166        }
12167
12168        if (empty($this->installer)) {
12169            $this->installer = &$this->getInstaller($this->ui);
12170        }
12171
12172        if (isset($options['remoteconfig'])) {
12173            $e = $this->config->readFTPConfigFile($options['remoteconfig']);
12174            if (!PEAR::isError($e)) {
12175                $this->installer->setConfig($this->config);
12176            }
12177        }
12178
12179        $reg = &$this->config->getRegistry();
12180        $newparams = array();
12181        $binaries = array();
12182        $badparams = array();
12183        foreach ($params as $pkg) {
12184            $channel = $this->config->get('default_channel');
12185            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
12186            $parsed = $reg->parsePackageName($pkg, $channel);
12187            PEAR::staticPopErrorHandling();
12188            if (!$parsed || PEAR::isError($parsed)) {
12189                $badparams[] = $pkg;
12190                continue;
12191            }
12192            $package = $parsed['package'];
12193            $channel = $parsed['channel'];
12194            $info = &$reg->getPackage($package, $channel);
12195            if ($info === null &&
12196                 ($channel == 'pear.php.net' || $channel == 'pecl.php.net')) {
12197                // make sure this isn't a package that has flipped from pear to pecl but
12198                // used a package.xml 1.0
12199                $testc = ($channel == 'pear.php.net') ? 'pecl.php.net' : 'pear.php.net';
12200                $info = &$reg->getPackage($package, $testc);
12201                if ($info !== null) {
12202                    $channel = $testc;
12203                }
12204            }
12205            if ($info === null) {
12206                $badparams[] = $pkg;
12207            } else {
12208                $newparams[] = &$info;
12209                // check for binary packages (this is an alias for those packages if so)
12210                if ($installedbinary = $info->getInstalledBinary()) {
12211                    $this->ui->log('adding binary package ' .
12212                        $reg->parsedPackageNameToString(array('channel' => $channel,
12213                            'package' => $installedbinary), true));
12214                    $newparams[] = &$reg->getPackage($installedbinary, $channel);
12215                }
12216                // add the contents of a dependency group to the list of installed packages
12217                if (isset($parsed['group'])) {
12218                    $group = $info->getDependencyGroup($parsed['group']);
12219                    if ($group) {
12220                        $installed = $reg->getInstalledGroup($group);
12221                        if ($installed) {
12222                            foreach ($installed as $i => $p) {
12223                                $newparams[] = &$installed[$i];
12224                            }
12225                        }
12226                    }
12227                }
12228            }
12229        }
12230        $err = $this->installer->sortPackagesForUninstall($newparams);
12231        if (PEAR::isError($err)) {
12232            $this->ui->outputData($err->getMessage(), $command);
12233            return true;
12234        }
12235        $params = $newparams;
12236        // twist this to use it to check on whether dependent packages are also being uninstalled
12237        // for circular dependencies like subpackages
12238        $this->installer->setUninstallPackages($newparams);
12239        $params = array_merge($params, $badparams);
12240        $binaries = array();
12241        foreach ($params as $pkg) {
12242            $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
12243            if ($err = $this->installer->uninstall($pkg, $options)) {
12244                $this->installer->popErrorHandling();
12245                if (PEAR::isError($err)) {
12246                    $this->ui->outputData($err->getMessage(), $command);
12247                    continue;
12248                }
12249                if ($pkg->getPackageType() == 'extsrc' ||
12250                      $pkg->getPackageType() == 'extbin' ||
12251                      $pkg->getPackageType() == 'zendextsrc' ||
12252                      $pkg->getPackageType() == 'zendextbin') {
12253                    if ($instbin = $pkg->getInstalledBinary()) {
12254                        continue; // this will be uninstalled later
12255                    }
12256
12257                    foreach ($pkg->getFilelist() as $name => $atts) {
12258                        $pinfo = pathinfo($atts['installed_as']);
12259                        if (!isset($pinfo['extension']) ||
12260                              in_array($pinfo['extension'], array('c', 'h'))) {
12261                            continue; // make sure we don't match php_blah.h
12262                        }
12263                        if ((strpos($pinfo['basename'], 'php_') === 0 &&
12264                              $pinfo['extension'] == 'dll') ||
12265                              // most unices
12266                              $pinfo['extension'] == 'so' ||
12267                              // hp-ux
12268                              $pinfo['extension'] == 'sl') {
12269                            $binaries[] = array($atts['installed_as'], $pinfo);
12270                            break;
12271                        }
12272                    }
12273                    if (count($binaries)) {
12274                        foreach ($binaries as $pinfo) {
12275                            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
12276                            $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType());
12277                            PEAR::staticPopErrorHandling();
12278                            if (PEAR::isError($ret)) {
12279                                $extrainfo[] = $ret->getMessage();
12280                                if ($pkg->getPackageType() == 'extsrc' ||
12281                                      $pkg->getPackageType() == 'extbin') {
12282                                    $exttype = 'extension';
12283                                } else {
12284                                    ob_start();
12285                                    phpinfo(INFO_GENERAL);
12286                                    $info = ob_get_contents();
12287                                    ob_end_clean();
12288                                    $debug = function_exists('leak') ? '_debug' : '';
12289                                    $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
12290                                    $exttype = 'zend_extension' . $debug . $ts;
12291                                }
12292                                $this->ui->outputData('Unable to remove "' . $exttype . '=' .
12293                                    $pinfo[1]['basename'] . '" from php.ini', $command);
12294                            } else {
12295                                $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() .
12296                                    ' disabled in php.ini', $command);
12297                            }
12298                        }
12299                    }
12300                }
12301                $savepkg = $pkg;
12302                if ($this->config->get('verbose') > 0) {
12303                    if (is_object($pkg)) {
12304                        $pkg = $reg->parsedPackageNameToString($pkg);
12305                    }
12306                    $this->ui->outputData("uninstall ok: $pkg", $command);
12307                }
12308                if (!isset($options['offline']) && is_object($savepkg) &&
12309                      defined('PEAR_REMOTEINSTALL_OK')) {
12310                    if ($this->config->isDefinedLayer('ftp')) {
12311                        $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
12312                        $info = $this->installer->ftpUninstall($savepkg);
12313                        $this->installer->popErrorHandling();
12314                        if (PEAR::isError($info)) {
12315                            $this->ui->outputData($info->getMessage());
12316                            $this->ui->outputData("remote uninstall failed: $pkg");
12317                        } else {
12318                            $this->ui->outputData("remote uninstall ok: $pkg");
12319                        }
12320                    }
12321                }
12322            } else {
12323                $this->installer->popErrorHandling();
12324                if (!is_object($pkg)) {
12325                    return $this->raiseError("uninstall failed: $pkg");
12326                }
12327                $pkg = $reg->parsedPackageNameToString($pkg);
12328            }
12329        }
12330
12331        return true;
12332    }
12333
12334    // }}}
12335
12336
12337    // }}}
12338    // {{{ doBundle()
12339    /*
12340    (cox) It just downloads and untars the package, does not do
12341            any check that the PEAR_Installer::_installFile() does.
12342    */
12343
12344    function doBundle($command, $options, $params)
12345    {
12346        $opts = array(
12347            'force'        => true,
12348            'nodeps'       => true,
12349            'soft'         => true,
12350            'downloadonly' => true
12351        );
12352        $downloader = &$this->getDownloader($this->ui, $opts, $this->config);
12353        $reg = &$this->config->getRegistry();
12354        if (count($params) < 1) {
12355            return $this->raiseError("Please supply the package you want to bundle");
12356        }
12357
12358        if (isset($options['destination'])) {
12359            if (!is_dir($options['destination'])) {
12360                System::mkdir('-p ' . $options['destination']);
12361            }
12362            $dest = realpath($options['destination']);
12363        } else {
12364            $pwd  = getcwd();
12365            $dir  = $pwd . DIRECTORY_SEPARATOR . 'ext';
12366            $dest = is_dir($dir) ? $dir : $pwd;
12367        }
12368        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
12369        $err = $downloader->setDownloadDir($dest);
12370        PEAR::staticPopErrorHandling();
12371        if (PEAR::isError($err)) {
12372            return PEAR::raiseError('download directory "' . $dest .
12373                '" is not writeable.');
12374        }
12375        $result = &$downloader->download(array($params[0]));
12376        if (PEAR::isError($result)) {
12377            return $result;
12378        }
12379        if (!isset($result[0])) {
12380            return $this->raiseError('unable to unpack ' . $params[0]);
12381        }
12382        $pkgfile = &$result[0]->getPackageFile();
12383        $pkgname = $pkgfile->getName();
12384        $pkgversion = $pkgfile->getVersion();
12385
12386        // Unpacking -------------------------------------------------
12387        $dest .= DIRECTORY_SEPARATOR . $pkgname;
12388        $orig = $pkgname . '-' . $pkgversion;
12389
12390        $tar = &new Archive_Tar($pkgfile->getArchiveFile());
12391        if (!$tar->extractModify($dest, $orig)) {
12392            return $this->raiseError('unable to unpack ' . $pkgfile->getArchiveFile());
12393        }
12394        $this->ui->outputData("Package ready at '$dest'");
12395    // }}}
12396    }
12397
12398    // }}}
12399
12400    function doRunScripts($command, $options, $params)
12401    {
12402        if (!isset($params[0])) {
12403            return $this->raiseError('run-scripts expects 1 parameter: a package name');
12404        }
12405
12406        $reg = &$this->config->getRegistry();
12407        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
12408        $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
12409        PEAR::staticPopErrorHandling();
12410        if (PEAR::isError($parsed)) {
12411            return $this->raiseError($parsed);
12412        }
12413
12414        $package = &$reg->getPackage($parsed['package'], $parsed['channel']);
12415        if (!is_object($package)) {
12416            return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry');
12417        }
12418
12419        $package->setConfig($this->config);
12420        $package->runPostinstallScripts();
12421        $this->ui->outputData('Install scripts complete', $command);
12422        return true;
12423    }
12424
12425    /**
12426     * Given a list of packages, filter out those ones that are already up to date
12427     *
12428     * @param $packages: packages, in parsed array format !
12429     * @return list of packages that can be upgraded
12430     */
12431    function _filterUptodatePackages($packages, $command)
12432    {
12433        $reg = &$this->config->getRegistry();
12434        $latestReleases = array();
12435
12436        $ret = array();
12437        foreach ($packages as $package) {
12438            if (isset($package['group'])) {
12439                $ret[] = $package;
12440                continue;
12441            }
12442
12443            $channel = $package['channel'];
12444            $name    = $package['package'];
12445            if (!$reg->packageExists($name, $channel)) {
12446                $ret[] = $package;
12447                continue;
12448            }
12449
12450            if (!isset($latestReleases[$channel])) {
12451                // fill in cache for this channel
12452                $chan = &$reg->getChannel($channel);
12453                if (PEAR::isError($chan)) {
12454                    return $this->raiseError($chan);
12455                }
12456
12457                $base2 = false;
12458                $preferred_mirror = $this->config->get('preferred_mirror', null, $channel);
12459                if ($chan->supportsREST($preferred_mirror) &&
12460                    (
12461                       //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) ||
12462                       ($base  = $chan->getBaseURL('REST1.0', $preferred_mirror))
12463                    )
12464                ) {
12465                    $dorest = true;
12466                }
12467
12468                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
12469                if (!isset($package['state'])) {
12470                    $state = $this->config->get('preferred_state', null, $channel);
12471                } else {
12472                    $state = $package['state'];
12473                }
12474
12475                if ($dorest) {
12476                    if ($base2) {
12477                        $rest = &$this->config->getREST('1.4', array());
12478                        $base = $base2;
12479                    } else {
12480                        $rest = &$this->config->getREST('1.0', array());
12481                    }
12482
12483                    $installed = array_flip($reg->listPackages($channel));
12484                    $latest    = $rest->listLatestUpgrades($base, $state, $installed, $channel, $reg);
12485                }
12486
12487                PEAR::staticPopErrorHandling();
12488                if (PEAR::isError($latest)) {
12489                    $this->ui->outputData('Error getting channel info from ' . $channel .
12490                        ': ' . $latest->getMessage());
12491                    continue;
12492                }
12493
12494                $latestReleases[$channel] = array_change_key_case($latest);
12495            }
12496
12497            // check package for latest release
12498            $name_lower = strtolower($name);
12499            if (isset($latestReleases[$channel][$name_lower])) {
12500                // if not set, up to date
12501                $inst_version    = $reg->packageInfo($name, 'version', $channel);
12502                $channel_version = $latestReleases[$channel][$name_lower]['version'];
12503                if (version_compare($channel_version, $inst_version, 'le')) {
12504                    // installed version is up-to-date
12505                    continue;
12506                }
12507
12508                // maintain BC
12509                if ($command == 'upgrade-all') {
12510                    $this->ui->outputData(array('data' => 'Will upgrade ' .
12511                        $reg->parsedPackageNameToString($package)), $command);
12512                }
12513                $ret[] = $package;
12514            }
12515        }
12516
12517        return $ret;
12518    }
12519}PEAR-1.9.4/PEAR/Command/Mirror.xml0000644000076500000240000000115111605156614015333 0ustar  helgistaff<commands version="1.0">
12520 <download-all>
12521  <summary>Downloads each available package from the default channel</summary>
12522  <function>doDownloadAll</function>
12523  <shortcut>da</shortcut>
12524  <options>
12525   <channel>
12526    <shortopt>c</shortopt>
12527    <doc>specify a channel other than the default channel</doc>
12528    <arg>CHAN</arg>
12529   </channel>
12530  </options>
12531  <doc>
12532Requests a list of available packages from the default channel ({config default_channel})
12533and downloads them to current working directory.  Note: only
12534packages within preferred_state ({config preferred_state}) will be downloaded</doc>
12535 </download-all>
12536</commands>PEAR-1.9.4/PEAR/Command/Mirror.php0000644000076500000240000001076211605156614015332 0ustar  helgistaff<?php
12537/**
12538 * PEAR_Command_Mirror (download-all command)
12539 *
12540 * PHP versions 4 and 5
12541 *
12542 * @category   pear
12543 * @package    PEAR
12544 * @author     Alexander Merz <alexmerz@php.net>
12545 * @copyright  1997-2009 The Authors
12546 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
12547 * @version    CVS: $Id: Mirror.php 313023 2011-07-06 19:17:11Z dufuz $
12548 * @link       http://pear.php.net/package/PEAR
12549 * @since      File available since Release 1.2.0
12550 */
12551
12552/**
12553 * base class
12554 */
12555require_once 'PEAR/Command/Common.php';
12556
12557/**
12558 * PEAR commands for providing file mirrors
12559 *
12560 * @category   pear
12561 * @package    PEAR
12562 * @author     Alexander Merz <alexmerz@php.net>
12563 * @copyright  1997-2009 The Authors
12564 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
12565 * @version    Release: 1.9.4
12566 * @link       http://pear.php.net/package/PEAR
12567 * @since      Class available since Release 1.2.0
12568 */
12569class PEAR_Command_Mirror extends PEAR_Command_Common
12570{
12571    var $commands = array(
12572        'download-all' => array(
12573            'summary' => 'Downloads each available package from the default channel',
12574            'function' => 'doDownloadAll',
12575            'shortcut' => 'da',
12576            'options' => array(
12577                'channel' =>
12578                    array(
12579                    'shortopt' => 'c',
12580                    'doc' => 'specify a channel other than the default channel',
12581                    'arg' => 'CHAN',
12582                    ),
12583                ),
12584            'doc' => '
12585Requests a list of available packages from the default channel ({config default_channel})
12586and downloads them to current working directory.  Note: only
12587packages within preferred_state ({config preferred_state}) will be downloaded'
12588            ),
12589        );
12590
12591    /**
12592     * PEAR_Command_Mirror constructor.
12593     *
12594     * @access public
12595     * @param object PEAR_Frontend a reference to an frontend
12596     * @param object PEAR_Config a reference to the configuration data
12597     */
12598    function PEAR_Command_Mirror(&$ui, &$config)
12599    {
12600        parent::PEAR_Command_Common($ui, $config);
12601    }
12602
12603    /**
12604     * For unit-testing
12605     */
12606    function &factory($a)
12607    {
12608        $a = &PEAR_Command::factory($a, $this->config);
12609        return $a;
12610    }
12611
12612    /**
12613    * retrieves a list of avaible Packages from master server
12614    * and downloads them
12615    *
12616    * @access public
12617    * @param string $command the command
12618    * @param array $options the command options before the command
12619    * @param array $params the stuff after the command name
12620    * @return bool true if succesful
12621    * @throw PEAR_Error
12622    */
12623    function doDownloadAll($command, $options, $params)
12624    {
12625        $savechannel = $this->config->get('default_channel');
12626        $reg = &$this->config->getRegistry();
12627        $channel = isset($options['channel']) ? $options['channel'] :
12628            $this->config->get('default_channel');
12629        if (!$reg->channelExists($channel)) {
12630            $this->config->set('default_channel', $savechannel);
12631            return $this->raiseError('Channel "' . $channel . '" does not exist');
12632        }
12633        $this->config->set('default_channel', $channel);
12634
12635        $this->ui->outputData('Using Channel ' . $this->config->get('default_channel'));
12636        $chan = $reg->getChannel($channel);
12637        if (PEAR::isError($chan)) {
12638            return $this->raiseError($chan);
12639        }
12640
12641        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
12642              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
12643            $rest = &$this->config->getREST('1.0', array());
12644            $remoteInfo = array_flip($rest->listPackages($base, $channel));
12645        }
12646
12647        if (PEAR::isError($remoteInfo)) {
12648            return $remoteInfo;
12649        }
12650
12651        $cmd = &$this->factory("download");
12652        if (PEAR::isError($cmd)) {
12653            return $cmd;
12654        }
12655
12656        $this->ui->outputData('Using Preferred State of ' .
12657            $this->config->get('preferred_state'));
12658        $this->ui->outputData('Gathering release information, please wait...');
12659
12660        /**
12661         * Error handling not necessary, because already done by
12662         * the download command
12663         */
12664        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
12665        $err = $cmd->run('download', array('downloadonly' => true), array_keys($remoteInfo));
12666        PEAR::staticPopErrorHandling();
12667        $this->config->set('default_channel', $savechannel);
12668        if (PEAR::isError($err)) {
12669            $this->ui->outputData($err->getMessage());
12670        }
12671
12672        return true;
12673    }
12674}PEAR-1.9.4/PEAR/Command/Package.xml0000644000076500000240000001606611605156614015427 0ustar  helgistaff<commands version="1.0">
12675 <package>
12676  <summary>Build Package</summary>
12677  <function>doPackage</function>
12678  <shortcut>p</shortcut>
12679  <options>
12680   <nocompress>
12681    <shortopt>Z</shortopt>
12682    <doc>Do not gzip the package file</doc>
12683   </nocompress>
12684   <showname>
12685    <shortopt>n</shortopt>
12686    <doc>Print the name of the packaged file.</doc>
12687   </showname>
12688  </options>
12689  <doc>[descfile] [descfile2]
12690Creates a PEAR package from its description file (usually called
12691package.xml).  If a second packagefile is passed in, then
12692the packager will check to make sure that one is a package.xml
12693version 1.0, and the other is a package.xml version 2.0.  The
12694package.xml version 1.0 will be saved as &quot;package.xml&quot; in the archive,
12695and the other as &quot;package2.xml&quot; in the archive&quot;
12696</doc>
12697 </package>
12698 <package-validate>
12699  <summary>Validate Package Consistency</summary>
12700  <function>doPackageValidate</function>
12701  <shortcut>pv</shortcut>
12702  <options />
12703  <doc>
12704</doc>
12705 </package-validate>
12706 <cvsdiff>
12707  <summary>Run a &quot;cvs diff&quot; for all files in a package</summary>
12708  <function>doCvsDiff</function>
12709  <shortcut>cd</shortcut>
12710  <options>
12711   <quiet>
12712    <shortopt>q</shortopt>
12713    <doc>Be quiet</doc>
12714   </quiet>
12715   <reallyquiet>
12716    <shortopt>Q</shortopt>
12717    <doc>Be really quiet</doc>
12718   </reallyquiet>
12719   <date>
12720    <shortopt>D</shortopt>
12721    <doc>Diff against revision of DATE</doc>
12722    <arg>DATE</arg>
12723   </date>
12724   <release>
12725    <shortopt>R</shortopt>
12726    <doc>Diff against tag for package release REL</doc>
12727    <arg>REL</arg>
12728   </release>
12729   <revision>
12730    <shortopt>r</shortopt>
12731    <doc>Diff against revision REV</doc>
12732    <arg>REV</arg>
12733   </revision>
12734   <context>
12735    <shortopt>c</shortopt>
12736    <doc>Generate context diff</doc>
12737   </context>
12738   <unified>
12739    <shortopt>u</shortopt>
12740    <doc>Generate unified diff</doc>
12741   </unified>
12742   <ignore-case>
12743    <shortopt>i</shortopt>
12744    <doc>Ignore case, consider upper- and lower-case letters equivalent</doc>
12745   </ignore-case>
12746   <ignore-whitespace>
12747    <shortopt>b</shortopt>
12748    <doc>Ignore changes in amount of white space</doc>
12749   </ignore-whitespace>
12750   <ignore-blank-lines>
12751    <shortopt>B</shortopt>
12752    <doc>Ignore changes that insert or delete blank lines</doc>
12753   </ignore-blank-lines>
12754   <brief>
12755    <shortopt></shortopt>
12756    <doc>Report only whether the files differ, no details</doc>
12757   </brief>
12758   <dry-run>
12759    <shortopt>n</shortopt>
12760    <doc>Don&#039;t do anything, just pretend</doc>
12761   </dry-run>
12762  </options>
12763  <doc>&lt;package.xml&gt;
12764Compares all the files in a package.  Without any options, this
12765command will compare the current code with the last checked-in code.
12766Using the -r or -R option you may compare the current code with that
12767of a specific release.
12768</doc>
12769 </cvsdiff>
12770 <svntag>
12771  <summary>Set SVN Release Tag</summary>
12772  <function>doSvnTag</function>
12773  <shortcut>sv</shortcut>
12774  <options>
12775   <quiet>
12776    <shortopt>q</shortopt>
12777    <doc>Be quiet</doc>
12778   </quiet>
12779   <slide>
12780    <shortopt>F</shortopt>
12781    <doc>Move (slide) tag if it exists</doc>
12782   </slide>
12783   <delete>
12784    <shortopt>d</shortopt>
12785    <doc>Remove tag</doc>
12786   </delete>
12787   <dry-run>
12788    <shortopt>n</shortopt>
12789    <doc>Don&#039;t do anything, just pretend</doc>
12790   </dry-run>
12791  </options>
12792  <doc>&lt;package.xml&gt; [files...]
12793 Sets a SVN tag on all files in a package.  Use this command after you have
12794 packaged a distribution tarball with the &quot;package&quot; command to tag what
12795 revisions of what files were in that release.  If need to fix something
12796 after running svntag once, but before the tarball is released to the public,
12797 use the &quot;slide&quot; option to move the release tag.
12798
12799 to include files (such as a second package.xml, or tests not included in the
12800 release), pass them as additional parameters.
12801 </doc>
12802 </svntag>
12803 <cvstag>
12804  <summary>Set CVS Release Tag</summary>
12805  <function>doCvsTag</function>
12806  <shortcut>ct</shortcut>
12807  <options>
12808   <quiet>
12809    <shortopt>q</shortopt>
12810    <doc>Be quiet</doc>
12811   </quiet>
12812   <reallyquiet>
12813    <shortopt>Q</shortopt>
12814    <doc>Be really quiet</doc>
12815   </reallyquiet>
12816   <slide>
12817    <shortopt>F</shortopt>
12818    <doc>Move (slide) tag if it exists</doc>
12819   </slide>
12820   <delete>
12821    <shortopt>d</shortopt>
12822    <doc>Remove tag</doc>
12823   </delete>
12824   <dry-run>
12825    <shortopt>n</shortopt>
12826    <doc>Don&#039;t do anything, just pretend</doc>
12827   </dry-run>
12828  </options>
12829  <doc>&lt;package.xml&gt; [files...]
12830Sets a CVS tag on all files in a package.  Use this command after you have
12831packaged a distribution tarball with the &quot;package&quot; command to tag what
12832revisions of what files were in that release.  If need to fix something
12833after running cvstag once, but before the tarball is released to the public,
12834use the &quot;slide&quot; option to move the release tag.
12835
12836to include files (such as a second package.xml, or tests not included in the
12837release), pass them as additional parameters.
12838</doc>
12839 </cvstag>
12840 <package-dependencies>
12841  <summary>Show package dependencies</summary>
12842  <function>doPackageDependencies</function>
12843  <shortcut>pd</shortcut>
12844  <options />
12845  <doc>&lt;package-file&gt; or &lt;package.xml&gt; or &lt;install-package-name&gt;
12846List all dependencies the package has.
12847Can take a tgz / tar file, package.xml or a package name of an installed package.</doc>
12848 </package-dependencies>
12849 <sign>
12850  <summary>Sign a package distribution file</summary>
12851  <function>doSign</function>
12852  <shortcut>si</shortcut>
12853  <options>
12854   <verbose>
12855    <shortopt>v</shortopt>
12856    <doc>Display GnuPG output</doc>
12857   </verbose>
12858  </options>
12859  <doc>&lt;package-file&gt;
12860Signs a package distribution (.tar or .tgz) file with GnuPG.</doc>
12861 </sign>
12862 <makerpm>
12863  <summary>Builds an RPM spec file from a PEAR package</summary>
12864  <function>doMakeRPM</function>
12865  <shortcut>rpm</shortcut>
12866  <options>
12867   <spec-template>
12868    <shortopt>t</shortopt>
12869    <doc>Use FILE as RPM spec file template</doc>
12870    <arg>FILE</arg>
12871   </spec-template>
12872   <rpm-pkgname>
12873    <shortopt>p</shortopt>
12874    <doc>Use FORMAT as format string for RPM package name, %s is replaced
12875by the PEAR package name, defaults to &quot;PEAR::%s&quot;.</doc>
12876    <arg>FORMAT</arg>
12877   </rpm-pkgname>
12878  </options>
12879  <doc>&lt;package-file&gt;
12880
12881Creates an RPM .spec file for wrapping a PEAR package inside an RPM
12882package.  Intended to be used from the SPECS directory, with the PEAR
12883package tarball in the SOURCES directory:
12884
12885$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
12886Wrote RPM spec file PEAR::Net_Geo-1.0.spec
12887$ rpm -bb PEAR::Net_Socket-1.0.spec
12888...
12889Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
12890</doc>
12891 </makerpm>
12892 <convert>
12893  <summary>Convert a package.xml 1.0 to package.xml 2.0 format</summary>
12894  <function>doConvert</function>
12895  <shortcut>c2</shortcut>
12896  <options>
12897   <flat>
12898    <shortopt>f</shortopt>
12899    <doc>do not beautify the filelist.</doc>
12900   </flat>
12901  </options>
12902  <doc>[descfile] [descfile2]
12903Converts a package.xml in 1.0 format into a package.xml
12904in 2.0 format.  The new file will be named package2.xml by default,
12905and package.xml will be used as the old file by default.
12906This is not the most intelligent conversion, and should only be
12907used for automated conversion or learning the format.
12908</doc>
12909 </convert>
12910</commands>PEAR-1.9.4/PEAR/Command/Package.php0000644000076500000240000011652311605156614015415 0ustar  helgistaff<?php
12911/**
12912 * PEAR_Command_Package (package, package-validate, cvsdiff, cvstag, package-dependencies,
12913 * sign, makerpm, convert commands)
12914 *
12915 * PHP versions 4 and 5
12916 *
12917 * @category   pear
12918 * @package    PEAR
12919 * @author     Stig Bakken <ssb@php.net>
12920 * @author     Martin Jansen <mj@php.net>
12921 * @author     Greg Beaver <cellog@php.net>
12922 * @copyright  1997-2009 The Authors
12923 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
12924 * @version    CVS: $Id: Package.php 313024 2011-07-06 19:51:24Z dufuz $
12925 * @link       http://pear.php.net/package/PEAR
12926 * @since      File available since Release 0.1
12927 */
12928
12929/**
12930 * base class
12931 */
12932require_once 'PEAR/Command/Common.php';
12933
12934/**
12935 * PEAR commands for login/logout
12936 *
12937 * @category   pear
12938 * @package    PEAR
12939 * @author     Stig Bakken <ssb@php.net>
12940 * @author     Martin Jansen <mj@php.net>
12941 * @author     Greg Beaver <cellog@php.net>
12942 * @copyright  1997-2009 The Authors
12943 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
12944 * @version    Release: @package_version@
12945 * @link       http://pear.php.net/package/PEAR
12946 * @since      Class available since Release 0.1
12947 */
12948
12949class PEAR_Command_Package extends PEAR_Command_Common
12950{
12951    var $commands = array(
12952        'package' => array(
12953            'summary' => 'Build Package',
12954            'function' => 'doPackage',
12955            'shortcut' => 'p',
12956            'options' => array(
12957                'nocompress' => array(
12958                    'shortopt' => 'Z',
12959                    'doc' => 'Do not gzip the package file'
12960                    ),
12961                'showname' => array(
12962                    'shortopt' => 'n',
12963                    'doc' => 'Print the name of the packaged file.',
12964                    ),
12965                ),
12966            'doc' => '[descfile] [descfile2]
12967Creates a PEAR package from its description file (usually called
12968package.xml).  If a second packagefile is passed in, then
12969the packager will check to make sure that one is a package.xml
12970version 1.0, and the other is a package.xml version 2.0.  The
12971package.xml version 1.0 will be saved as "package.xml" in the archive,
12972and the other as "package2.xml" in the archive"
12973'
12974            ),
12975        'package-validate' => array(
12976            'summary' => 'Validate Package Consistency',
12977            'function' => 'doPackageValidate',
12978            'shortcut' => 'pv',
12979            'options' => array(),
12980            'doc' => '
12981',
12982            ),
12983        'cvsdiff' => array(
12984            'summary' => 'Run a "cvs diff" for all files in a package',
12985            'function' => 'doCvsDiff',
12986            'shortcut' => 'cd',
12987            'options' => array(
12988                'quiet' => array(
12989                    'shortopt' => 'q',
12990                    'doc' => 'Be quiet',
12991                    ),
12992                'reallyquiet' => array(
12993                    'shortopt' => 'Q',
12994                    'doc' => 'Be really quiet',
12995                    ),
12996                'date' => array(
12997                    'shortopt' => 'D',
12998                    'doc' => 'Diff against revision of DATE',
12999                    'arg' => 'DATE',
13000                    ),
13001                'release' => array(
13002                    'shortopt' => 'R',
13003                    'doc' => 'Diff against tag for package release REL',
13004                    'arg' => 'REL',
13005                    ),
13006                'revision' => array(
13007                    'shortopt' => 'r',
13008                    'doc' => 'Diff against revision REV',
13009                    'arg' => 'REV',
13010                    ),
13011                'context' => array(
13012                    'shortopt' => 'c',
13013                    'doc' => 'Generate context diff',
13014                    ),
13015                'unified' => array(
13016                    'shortopt' => 'u',
13017                    'doc' => 'Generate unified diff',
13018                    ),
13019                'ignore-case' => array(
13020                    'shortopt' => 'i',
13021                    'doc' => 'Ignore case, consider upper- and lower-case letters equivalent',
13022                    ),
13023                'ignore-whitespace' => array(
13024                    'shortopt' => 'b',
13025                    'doc' => 'Ignore changes in amount of white space',
13026                    ),
13027                'ignore-blank-lines' => array(
13028                    'shortopt' => 'B',
13029                    'doc' => 'Ignore changes that insert or delete blank lines',
13030                    ),
13031                'brief' => array(
13032                    'doc' => 'Report only whether the files differ, no details',
13033                    ),
13034                'dry-run' => array(
13035                    'shortopt' => 'n',
13036                    'doc' => 'Don\'t do anything, just pretend',
13037                    ),
13038                ),
13039            'doc' => '<package.xml>
13040Compares all the files in a package.  Without any options, this
13041command will compare the current code with the last checked-in code.
13042Using the -r or -R option you may compare the current code with that
13043of a specific release.
13044',
13045            ),
13046         'svntag' => array(
13047             'summary' => 'Set SVN Release Tag',
13048             'function' => 'doSvnTag',
13049             'shortcut' => 'sv',
13050             'options' => array(
13051                 'quiet' => array(
13052                     'shortopt' => 'q',
13053                     'doc' => 'Be quiet',
13054                     ),
13055                 'slide' => array(
13056                     'shortopt' => 'F',
13057                     'doc' => 'Move (slide) tag if it exists',
13058                     ),
13059                 'delete' => array(
13060                     'shortopt' => 'd',
13061                     'doc' => 'Remove tag',
13062                     ),
13063                 'dry-run' => array(
13064                     'shortopt' => 'n',
13065                     'doc' => 'Don\'t do anything, just pretend',
13066                     ),
13067                 ),
13068             'doc' => '<package.xml> [files...]
13069 Sets a SVN tag on all files in a package.  Use this command after you have
13070 packaged a distribution tarball with the "package" command to tag what
13071 revisions of what files were in that release.  If need to fix something
13072 after running svntag once, but before the tarball is released to the public,
13073 use the "slide" option to move the release tag.
13074
13075 to include files (such as a second package.xml, or tests not included in the
13076 release), pass them as additional parameters.
13077 ',
13078             ),
13079        'cvstag' => array(
13080            'summary' => 'Set CVS Release Tag',
13081            'function' => 'doCvsTag',
13082            'shortcut' => 'ct',
13083            'options' => array(
13084                'quiet' => array(
13085                    'shortopt' => 'q',
13086                    'doc' => 'Be quiet',
13087                    ),
13088                'reallyquiet' => array(
13089                    'shortopt' => 'Q',
13090                    'doc' => 'Be really quiet',
13091                    ),
13092                'slide' => array(
13093                    'shortopt' => 'F',
13094                    'doc' => 'Move (slide) tag if it exists',
13095                    ),
13096                'delete' => array(
13097                    'shortopt' => 'd',
13098                    'doc' => 'Remove tag',
13099                    ),
13100                'dry-run' => array(
13101                    'shortopt' => 'n',
13102                    'doc' => 'Don\'t do anything, just pretend',
13103                    ),
13104                ),
13105            'doc' => '<package.xml> [files...]
13106Sets a CVS tag on all files in a package.  Use this command after you have
13107packaged a distribution tarball with the "package" command to tag what
13108revisions of what files were in that release.  If need to fix something
13109after running cvstag once, but before the tarball is released to the public,
13110use the "slide" option to move the release tag.
13111
13112to include files (such as a second package.xml, or tests not included in the
13113release), pass them as additional parameters.
13114',
13115            ),
13116        'package-dependencies' => array(
13117            'summary' => 'Show package dependencies',
13118            'function' => 'doPackageDependencies',
13119            'shortcut' => 'pd',
13120            'options' => array(),
13121            'doc' => '<package-file> or <package.xml> or <install-package-name>
13122List all dependencies the package has.
13123Can take a tgz / tar file, package.xml or a package name of an installed package.'
13124            ),
13125        'sign' => array(
13126            'summary' => 'Sign a package distribution file',
13127            'function' => 'doSign',
13128            'shortcut' => 'si',
13129            'options' => array(
13130                'verbose' => array(
13131                    'shortopt' => 'v',
13132                    'doc' => 'Display GnuPG output',
13133                    ),
13134            ),
13135            'doc' => '<package-file>
13136Signs a package distribution (.tar or .tgz) file with GnuPG.',
13137            ),
13138        'makerpm' => array(
13139            'summary' => 'Builds an RPM spec file from a PEAR package',
13140            'function' => 'doMakeRPM',
13141            'shortcut' => 'rpm',
13142            'options' => array(
13143                'spec-template' => array(
13144                    'shortopt' => 't',
13145                    'arg' => 'FILE',
13146                    'doc' => 'Use FILE as RPM spec file template'
13147                    ),
13148                'rpm-pkgname' => array(
13149                    'shortopt' => 'p',
13150                    'arg' => 'FORMAT',
13151                    'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced
13152by the PEAR package name, defaults to "PEAR::%s".',
13153                    ),
13154                ),
13155            'doc' => '<package-file>
13156
13157Creates an RPM .spec file for wrapping a PEAR package inside an RPM
13158package.  Intended to be used from the SPECS directory, with the PEAR
13159package tarball in the SOURCES directory:
13160
13161$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
13162Wrote RPM spec file PEAR::Net_Geo-1.0.spec
13163$ rpm -bb PEAR::Net_Socket-1.0.spec
13164...
13165Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
13166',
13167            ),
13168        'convert' => array(
13169            'summary' => 'Convert a package.xml 1.0 to package.xml 2.0 format',
13170            'function' => 'doConvert',
13171            'shortcut' => 'c2',
13172            'options' => array(
13173                'flat' => array(
13174                    'shortopt' => 'f',
13175                    'doc' => 'do not beautify the filelist.',
13176                    ),
13177                ),
13178            'doc' => '[descfile] [descfile2]
13179Converts a package.xml in 1.0 format into a package.xml
13180in 2.0 format.  The new file will be named package2.xml by default,
13181and package.xml will be used as the old file by default.
13182This is not the most intelligent conversion, and should only be
13183used for automated conversion or learning the format.
13184'
13185            ),
13186        );
13187
13188    var $output;
13189
13190    /**
13191     * PEAR_Command_Package constructor.
13192     *
13193     * @access public
13194     */
13195    function PEAR_Command_Package(&$ui, &$config)
13196    {
13197        parent::PEAR_Command_Common($ui, $config);
13198    }
13199
13200    function _displayValidationResults($err, $warn, $strict = false)
13201    {
13202        foreach ($err as $e) {
13203            $this->output .= "Error: $e\n";
13204        }
13205        foreach ($warn as $w) {
13206            $this->output .= "Warning: $w\n";
13207        }
13208        $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n",
13209                                       sizeof($err), sizeof($warn));
13210        if ($strict && count($err) > 0) {
13211            $this->output .= "Fix these errors and try again.";
13212            return false;
13213        }
13214        return true;
13215    }
13216
13217    function &getPackager()
13218    {
13219        if (!class_exists('PEAR_Packager')) {
13220            require_once 'PEAR/Packager.php';
13221        }
13222        $a = &new PEAR_Packager;
13223        return $a;
13224    }
13225
13226    function &getPackageFile($config, $debug = false)
13227    {
13228        if (!class_exists('PEAR_Common')) {
13229            require_once 'PEAR/Common.php';
13230        }
13231        if (!class_exists('PEAR_PackageFile')) {
13232            require_once 'PEAR/PackageFile.php';
13233        }
13234        $a = &new PEAR_PackageFile($config, $debug);
13235        $common = new PEAR_Common;
13236        $common->ui = $this->ui;
13237        $a->setLogger($common);
13238        return $a;
13239    }
13240
13241    function doPackage($command, $options, $params)
13242    {
13243        $this->output = '';
13244        $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml';
13245        $pkg2 = isset($params[1]) ? $params[1] : null;
13246        if (!$pkg2 && !isset($params[0]) && file_exists('package2.xml')) {
13247            $pkg2 = 'package2.xml';
13248        }
13249
13250        $packager = &$this->getPackager();
13251        $compress = empty($options['nocompress']) ? true : false;
13252        $result   = $packager->package($pkginfofile, $compress, $pkg2);
13253        if (PEAR::isError($result)) {
13254            return $this->raiseError($result);
13255        }
13256
13257        // Don't want output, only the package file name just created
13258        if (isset($options['showname'])) {
13259            $this->output = $result;
13260        }
13261
13262        if ($this->output) {
13263            $this->ui->outputData($this->output, $command);
13264        }
13265
13266        return true;
13267    }
13268
13269    function doPackageValidate($command, $options, $params)
13270    {
13271        $this->output = '';
13272        if (count($params) < 1) {
13273            $params[0] = 'package.xml';
13274        }
13275
13276        $obj = &$this->getPackageFile($this->config, $this->_debug);
13277        $obj->rawReturn();
13278        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
13279        $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
13280        if (PEAR::isError($info)) {
13281            $info = $obj->fromPackageFile($params[0], PEAR_VALIDATE_NORMAL);
13282        } else {
13283            $archive = $info->getArchiveFile();
13284            $tar = &new Archive_Tar($archive);
13285            $tar->extract(dirname($info->getPackageFile()));
13286            $info->setPackageFile(dirname($info->getPackageFile()) . DIRECTORY_SEPARATOR .
13287                $info->getPackage() . '-' . $info->getVersion() . DIRECTORY_SEPARATOR .
13288                basename($info->getPackageFile()));
13289        }
13290
13291        PEAR::staticPopErrorHandling();
13292        if (PEAR::isError($info)) {
13293            return $this->raiseError($info);
13294        }
13295
13296        $valid = false;
13297        if ($info->getPackagexmlVersion() == '2.0') {
13298            if ($valid = $info->validate(PEAR_VALIDATE_NORMAL)) {
13299                $info->flattenFileList();
13300                $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
13301            }
13302        } else {
13303            $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
13304        }
13305
13306        $err = $warn = array();
13307        if ($errors = $info->getValidationWarnings()) {
13308            foreach ($errors as $error) {
13309                if ($error['level'] == 'warning') {
13310                    $warn[] = $error['message'];
13311                } else {
13312                    $err[] = $error['message'];
13313                }
13314            }
13315        }
13316
13317        $this->_displayValidationResults($err, $warn);
13318        $this->ui->outputData($this->output, $command);
13319        return true;
13320    }
13321
13322    function doSvnTag($command, $options, $params)
13323    {
13324        $this->output = '';
13325        $_cmd = $command;
13326        if (count($params) < 1) {
13327            $help = $this->getHelp($command);
13328            return $this->raiseError("$command: missing parameter: $help[0]");
13329        }
13330
13331        $packageFile = realpath($params[0]);
13332        $dir = dirname($packageFile);
13333        $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1);
13334        $obj  = &$this->getPackageFile($this->config, $this->_debug);
13335        $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL);
13336        if (PEAR::isError($info)) {
13337            return $this->raiseError($info);
13338        }
13339
13340        $err = $warn = array();
13341        if (!$info->validate()) {
13342            foreach ($info->getValidationWarnings() as $error) {
13343                if ($error['level'] == 'warning') {
13344                    $warn[] = $error['message'];
13345                } else {
13346                    $err[] = $error['message'];
13347                }
13348            }
13349        }
13350
13351        if (!$this->_displayValidationResults($err, $warn, true)) {
13352            $this->ui->outputData($this->output, $command);
13353            return $this->raiseError('SVN tag failed');
13354        }
13355
13356        $version    = $info->getVersion();
13357        $package    = $info->getName();
13358        $svntag     = "$package-$version";
13359
13360        if (isset($options['delete'])) {
13361            return $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options);
13362        }
13363
13364        $path = $this->_svnFindPath($packageFile);
13365
13366        // Check if there are any modified files
13367        $fp = popen('svn st --xml ' . dirname($packageFile), "r");
13368        $out = '';
13369        while ($line = fgets($fp, 1024)) {
13370            $out .= rtrim($line)."\n";
13371        }
13372        pclose($fp);
13373
13374        if (!isset($options['quiet']) && strpos($out, 'item="modified"')) {
13375            $params = array(array(
13376                'name' => 'modified',
13377                'type' => 'yesno',
13378                'default' => 'no',
13379                'prompt' => 'You have files in your SVN checkout (' . $path['from']  . ') that have been modified but not commited, do you still want to tag ' . $version . '?',
13380            ));
13381            $answers = $this->ui->confirmDialog($params);
13382
13383            if (!in_array($answers['modified'], array('y', 'yes', 'on', '1'))) {
13384                return true;
13385            }
13386        }
13387
13388        if (isset($options['slide'])) {
13389            $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options);
13390        }
13391
13392        // Check if tag already exists
13393        $releaseTag = $path['local']['base'] . 'tags' . DIRECTORY_SEPARATOR . $svntag;
13394        $existsCommand = 'svn ls ' . $path['base'] . 'tags/';
13395
13396        $fp = popen($existsCommand, "r");
13397        $out = '';
13398        while ($line = fgets($fp, 1024)) {
13399            $out .= rtrim($line)."\n";
13400        }
13401        pclose($fp);
13402
13403        if (in_array($svntag . DIRECTORY_SEPARATOR, explode("\n", $out))) {
13404            $this->ui->outputData($this->output, $command);
13405            return $this->raiseError('SVN tag ' . $svntag . ' for ' . $package . ' already exists.');
13406        } elseif (file_exists($path['local']['base'] . 'tags') === false) {
13407            return $this->raiseError('Can not locate the tags directory at ' . $path['local']['base'] . 'tags');
13408        } elseif (is_writeable($path['local']['base'] . 'tags') === false) {
13409            return $this->raiseError('Can not write to the tag directory at ' . $path['local']['base'] . 'tags');
13410        } else {
13411            $makeCommand = 'svn mkdir ' . $releaseTag;
13412            $this->output .= "+ $makeCommand\n";
13413            if (empty($options['dry-run'])) {
13414                // We need to create the tag dir.
13415                $fp = popen($makeCommand, "r");
13416                $out = '';
13417                while ($line = fgets($fp, 1024)) {
13418                    $out .= rtrim($line)."\n";
13419                }
13420                pclose($fp);
13421                $this->output .= "$out\n";
13422            }
13423        }
13424
13425        $command = 'svn';
13426        if (isset($options['quiet'])) {
13427            $command .= ' -q';
13428        }
13429
13430        $command .= ' copy --parents ';
13431
13432        $dir   = dirname($packageFile);
13433        $dir   = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1);
13434        $files = array_keys($info->getFilelist());
13435        if (!in_array(basename($packageFile), $files)) {
13436            $files[] = basename($packageFile);
13437        }
13438
13439        array_shift($params);
13440        if (count($params)) {
13441            // add in additional files to be tagged (package files and such)
13442            $files = array_merge($files, $params);
13443        }
13444
13445        $commands = array();
13446        foreach ($files as $file) {
13447            if (!file_exists($file)) {
13448                $file = $dir . DIRECTORY_SEPARATOR . $file;
13449            }
13450            $commands[] = $command . ' ' . escapeshellarg($file) . ' ' .
13451                          escapeshellarg($releaseTag . DIRECTORY_SEPARATOR . $file);
13452        }
13453
13454        $this->output .= implode("\n", $commands) . "\n";
13455        if (empty($options['dry-run'])) {
13456            foreach ($commands as $command) {
13457                $fp = popen($command, "r");
13458                while ($line = fgets($fp, 1024)) {
13459                    $this->output .= rtrim($line)."\n";
13460                }
13461                pclose($fp);
13462            }
13463        }
13464
13465        $command = 'svn ci -m "Tagging the ' . $version  . ' release" ' . $releaseTag . "\n";
13466        $this->output .= "+ $command\n";
13467        if (empty($options['dry-run'])) {
13468            $fp = popen($command, "r");
13469            while ($line = fgets($fp, 1024)) {
13470                $this->output .= rtrim($line)."\n";
13471            }
13472            pclose($fp);
13473        }
13474
13475        $this->ui->outputData($this->output, $_cmd);
13476        return true;
13477    }
13478
13479    function _svnFindPath($file)
13480    {
13481        $xml = '';
13482        $command = "svn info --xml $file";
13483        $fp = popen($command, "r");
13484        while ($line = fgets($fp, 1024)) {
13485            $xml .= rtrim($line)."\n";
13486        }
13487        pclose($fp);
13488        $url_tag = strpos($xml, '<url>');
13489        $url = substr($xml, $url_tag + 5, strpos($xml, '</url>', $url_tag + 5) - ($url_tag + 5));
13490
13491        $path = array();
13492        $path['from'] = substr($url, 0, strrpos($url, '/'));
13493        $path['base'] = substr($path['from'], 0, strrpos($path['from'], '/') + 1);
13494
13495        // Figure out the local paths - see http://pear.php.net/bugs/17463
13496        $pos = strpos($file, DIRECTORY_SEPARATOR . 'trunk' . DIRECTORY_SEPARATOR);
13497        if ($pos === false) {
13498            $pos = strpos($file, DIRECTORY_SEPARATOR . 'branches' . DIRECTORY_SEPARATOR);
13499        }
13500        $path['local']['base'] = substr($file, 0, $pos + 1);
13501
13502        return $path;
13503    }
13504
13505    function _svnRemoveTag($version, $package, $tag, $packageFile, $options)
13506    {
13507        $command = 'svn';
13508
13509        if (isset($options['quiet'])) {
13510            $command .= ' -q';
13511        }
13512
13513        $command .= ' remove';
13514        $command .= ' -m "Removing tag for the ' . $version  . ' release."';
13515
13516        $path = $this->_svnFindPath($packageFile);
13517        $command .= ' ' . $path['base'] . 'tags/' . $tag;
13518
13519
13520        if ($this->config->get('verbose') > 1) {
13521            $this->output .= "+ $command\n";
13522        }
13523
13524        $this->output .= "+ $command\n";
13525        if (empty($options['dry-run'])) {
13526            $fp = popen($command, "r");
13527            while ($line = fgets($fp, 1024)) {
13528                $this->output .= rtrim($line)."\n";
13529            }
13530            pclose($fp);
13531        }
13532
13533        $this->ui->outputData($this->output, $command);
13534        return true;
13535    }
13536
13537    function doCvsTag($command, $options, $params)
13538    {
13539        $this->output = '';
13540        $_cmd = $command;
13541        if (count($params) < 1) {
13542            $help = $this->getHelp($command);
13543            return $this->raiseError("$command: missing parameter: $help[0]");
13544        }
13545
13546        $packageFile = realpath($params[0]);
13547        $obj  = &$this->getPackageFile($this->config, $this->_debug);
13548        $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL);
13549        if (PEAR::isError($info)) {
13550            return $this->raiseError($info);
13551        }
13552
13553        $err = $warn = array();
13554        if (!$info->validate()) {
13555            foreach ($info->getValidationWarnings() as $error) {
13556                if ($error['level'] == 'warning') {
13557                    $warn[] = $error['message'];
13558                } else {
13559                    $err[] = $error['message'];
13560                }
13561            }
13562        }
13563
13564        if (!$this->_displayValidationResults($err, $warn, true)) {
13565            $this->ui->outputData($this->output, $command);
13566            return $this->raiseError('CVS tag failed');
13567        }
13568
13569        $version    = $info->getVersion();
13570        $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version);
13571        $cvstag     = "RELEASE_$cvsversion";
13572        $files      = array_keys($info->getFilelist());
13573        $command = 'cvs';
13574        if (isset($options['quiet'])) {
13575            $command .= ' -q';
13576        }
13577
13578        if (isset($options['reallyquiet'])) {
13579            $command .= ' -Q';
13580        }
13581
13582        $command .= ' tag';
13583        if (isset($options['slide'])) {
13584            $command .= ' -F';
13585        }
13586
13587        if (isset($options['delete'])) {
13588            $command .= ' -d';
13589        }
13590
13591        $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]);
13592        array_shift($params);
13593        if (count($params)) {
13594            // add in additional files to be tagged
13595            $files = array_merge($files, $params);
13596        }
13597
13598        $dir = dirname($packageFile);
13599        $dir = substr($dir, strrpos($dir, '/') + 1);
13600        foreach ($files as $file) {
13601            if (!file_exists($file)) {
13602                $file = $dir . DIRECTORY_SEPARATOR . $file;
13603            }
13604            $command .= ' ' . escapeshellarg($file);
13605        }
13606
13607        if ($this->config->get('verbose') > 1) {
13608            $this->output .= "+ $command\n";
13609        }
13610
13611        $this->output .= "+ $command\n";
13612        if (empty($options['dry-run'])) {
13613            $fp = popen($command, "r");
13614            while ($line = fgets($fp, 1024)) {
13615                $this->output .= rtrim($line)."\n";
13616            }
13617            pclose($fp);
13618        }
13619
13620        $this->ui->outputData($this->output, $_cmd);
13621        return true;
13622    }
13623
13624    function doCvsDiff($command, $options, $params)
13625    {
13626        $this->output = '';
13627        if (sizeof($params) < 1) {
13628            $help = $this->getHelp($command);
13629            return $this->raiseError("$command: missing parameter: $help[0]");
13630        }
13631
13632        $file = realpath($params[0]);
13633        $obj  = &$this->getPackageFile($this->config, $this->_debug);
13634        $info = $obj->fromAnyFile($file, PEAR_VALIDATE_NORMAL);
13635        if (PEAR::isError($info)) {
13636            return $this->raiseError($info);
13637        }
13638
13639        $err = $warn = array();
13640        if (!$info->validate()) {
13641            foreach ($info->getValidationWarnings() as $error) {
13642                if ($error['level'] == 'warning') {
13643                    $warn[] = $error['message'];
13644                } else {
13645                    $err[] = $error['message'];
13646                }
13647            }
13648        }
13649
13650        if (!$this->_displayValidationResults($err, $warn, true)) {
13651            $this->ui->outputData($this->output, $command);
13652            return $this->raiseError('CVS diff failed');
13653        }
13654
13655        $info1 = $info->getFilelist();
13656        $files = $info1;
13657        $cmd = "cvs";
13658        if (isset($options['quiet'])) {
13659            $cmd .= ' -q';
13660            unset($options['quiet']);
13661        }
13662
13663        if (isset($options['reallyquiet'])) {
13664            $cmd .= ' -Q';
13665            unset($options['reallyquiet']);
13666        }
13667
13668        if (isset($options['release'])) {
13669            $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']);
13670            $cvstag = "RELEASE_$cvsversion";
13671            $options['revision'] = $cvstag;
13672            unset($options['release']);
13673        }
13674
13675        $execute = true;
13676        if (isset($options['dry-run'])) {
13677            $execute = false;
13678            unset($options['dry-run']);
13679        }
13680
13681        $cmd .= ' diff';
13682        // the rest of the options are passed right on to "cvs diff"
13683        foreach ($options as $option => $optarg) {
13684            $arg = $short = false;
13685            if (isset($this->commands[$command]['options'][$option])) {
13686                $arg = $this->commands[$command]['options'][$option]['arg'];
13687                $short = $this->commands[$command]['options'][$option]['shortopt'];
13688            }
13689            $cmd .= $short ? " -$short" : " --$option";
13690            if ($arg && $optarg) {
13691                $cmd .= ($short ? '' : '=') . escapeshellarg($optarg);
13692            }
13693        }
13694
13695        foreach ($files as $file) {
13696            $cmd .= ' ' . escapeshellarg($file['name']);
13697        }
13698
13699        if ($this->config->get('verbose') > 1) {
13700            $this->output .= "+ $cmd\n";
13701        }
13702
13703        if ($execute) {
13704            $fp = popen($cmd, "r");
13705            while ($line = fgets($fp, 1024)) {
13706                $this->output .= rtrim($line)."\n";
13707            }
13708            pclose($fp);
13709        }
13710
13711        $this->ui->outputData($this->output, $command);
13712        return true;
13713    }
13714
13715    function doPackageDependencies($command, $options, $params)
13716    {
13717        // $params[0] -> the PEAR package to list its information
13718        if (count($params) !== 1) {
13719            return $this->raiseError("bad parameter(s), try \"help $command\"");
13720        }
13721
13722        $obj = &$this->getPackageFile($this->config, $this->_debug);
13723        if (is_file($params[0]) || strpos($params[0], '.xml') > 0) {
13724           $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
13725        } else {
13726            $reg  = $this->config->getRegistry();
13727            $info = $obj->fromArray($reg->packageInfo($params[0]));
13728        }
13729
13730        if (PEAR::isError($info)) {
13731            return $this->raiseError($info);
13732        }
13733
13734        $deps = $info->getDeps();
13735        if (is_array($deps)) {
13736            if ($info->getPackagexmlVersion() == '1.0') {
13737                $data = array(
13738                    'caption' => 'Dependencies for pear/' . $info->getPackage(),
13739                    'border' => true,
13740                    'headline' => array("Required?", "Type", "Name", "Relation", "Version"),
13741                    );
13742
13743                foreach ($deps as $d) {
13744                    if (isset($d['optional'])) {
13745                        if ($d['optional'] == 'yes') {
13746                            $req = 'No';
13747                        } else {
13748                            $req = 'Yes';
13749                        }
13750                    } else {
13751                        $req = 'Yes';
13752                    }
13753
13754                    if (isset($this->_deps_rel_trans[$d['rel']])) {
13755                        $rel = $this->_deps_rel_trans[$d['rel']];
13756                    } else {
13757                        $rel = $d['rel'];
13758                    }
13759
13760                    if (isset($this->_deps_type_trans[$d['type']])) {
13761                        $type = ucfirst($this->_deps_type_trans[$d['type']]);
13762                    } else {
13763                        $type = $d['type'];
13764                    }
13765
13766                    if (isset($d['name'])) {
13767                        $name = $d['name'];
13768                    } else {
13769                        $name = '';
13770                    }
13771
13772                    if (isset($d['version'])) {
13773                        $version = $d['version'];
13774                    } else {
13775                        $version = '';
13776                    }
13777
13778                    $data['data'][] = array($req, $type, $name, $rel, $version);
13779                }
13780            } else { // package.xml 2.0 dependencies display
13781                require_once 'PEAR/Dependency2.php';
13782                $deps = $info->getDependencies();
13783                $reg = &$this->config->getRegistry();
13784                if (is_array($deps)) {
13785                    $d = new PEAR_Dependency2($this->config, array(), '');
13786                    $data = array(
13787                        'caption' => 'Dependencies for ' . $info->getPackage(),
13788                        'border' => true,
13789                        'headline' => array("Required?", "Type", "Name", 'Versioning', 'Group'),
13790                        );
13791                    foreach ($deps as $type => $subd) {
13792                        $req = ($type == 'required') ? 'Yes' : 'No';
13793                        if ($type == 'group') {
13794                            $group = $subd['attribs']['name'];
13795                        } else {
13796                            $group = '';
13797                        }
13798
13799                        if (!isset($subd[0])) {
13800                            $subd = array($subd);
13801                        }
13802
13803                        foreach ($subd as $groupa) {
13804                            foreach ($groupa as $deptype => $depinfo) {
13805                                if ($deptype == 'attribs') {
13806                                    continue;
13807                                }
13808
13809                                if ($deptype == 'pearinstaller') {
13810                                    $deptype = 'pear Installer';
13811                                }
13812
13813                                if (!isset($depinfo[0])) {
13814                                    $depinfo = array($depinfo);
13815                                }
13816
13817                                foreach ($depinfo as $inf) {
13818                                    $name = '';
13819                                    if (isset($inf['channel'])) {
13820                                        $alias = $reg->channelAlias($inf['channel']);
13821                                        if (!$alias) {
13822                                            $alias = '(channel?) ' .$inf['channel'];
13823                                        }
13824                                        $name = $alias . '/';
13825
13826                                    }
13827                                    if (isset($inf['name'])) {
13828                                        $name .= $inf['name'];
13829                                    } elseif (isset($inf['pattern'])) {
13830                                        $name .= $inf['pattern'];
13831                                    } else {
13832                                        $name .= '';
13833                                    }
13834
13835                                    if (isset($inf['uri'])) {
13836                                        $name .= ' [' . $inf['uri'] .  ']';
13837                                    }
13838
13839                                    if (isset($inf['conflicts'])) {
13840                                        $ver = 'conflicts';
13841                                    } else {
13842                                        $ver = $d->_getExtraString($inf);
13843                                    }
13844
13845                                    $data['data'][] = array($req, ucfirst($deptype), $name,
13846                                        $ver, $group);
13847                                }
13848                            }
13849                        }
13850                    }
13851                }
13852            }
13853
13854            $this->ui->outputData($data, $command);
13855            return true;
13856        }
13857
13858        // Fallback
13859        $this->ui->outputData("This package does not have any dependencies.", $command);
13860    }
13861
13862    function doSign($command, $options, $params)
13863    {
13864        // should move most of this code into PEAR_Packager
13865        // so it'll be easy to implement "pear package --sign"
13866        if (count($params) !== 1) {
13867            return $this->raiseError("bad parameter(s), try \"help $command\"");
13868        }
13869
13870        require_once 'System.php';
13871        require_once 'Archive/Tar.php';
13872
13873        if (!file_exists($params[0])) {
13874            return $this->raiseError("file does not exist: $params[0]");
13875        }
13876
13877        $obj = $this->getPackageFile($this->config, $this->_debug);
13878        $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
13879        if (PEAR::isError($info)) {
13880            return $this->raiseError($info);
13881        }
13882
13883        $tar = new Archive_Tar($params[0]);
13884
13885        $tmpdir = $this->config->get('temp_dir');
13886        $tmpdir = System::mktemp(' -t "' . $tmpdir . '" -d pearsign');
13887        if (!$tar->extractList('package2.xml package.xml package.sig', $tmpdir)) {
13888            return $this->raiseError("failed to extract tar file");
13889        }
13890
13891        if (file_exists("$tmpdir/package.sig")) {
13892            return $this->raiseError("package already signed");
13893        }
13894
13895        $packagexml = 'package.xml';
13896        if (file_exists("$tmpdir/package2.xml")) {
13897            $packagexml = 'package2.xml';
13898        }
13899
13900        if (file_exists("$tmpdir/package.sig")) {
13901            unlink("$tmpdir/package.sig");
13902        }
13903
13904        if (!file_exists("$tmpdir/$packagexml")) {
13905            return $this->raiseError("Extracted file $tmpdir/$packagexml not found.");
13906        }
13907
13908        $input = $this->ui->userDialog($command,
13909                                       array('GnuPG Passphrase'),
13910                                       array('password'));
13911        if (!isset($input[0])) {
13912            //use empty passphrase
13913            $input[0] = '';
13914        }
13915
13916        $devnull = (isset($options['verbose'])) ? '' : ' 2>/dev/null';
13917        $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/$packagexml" . $devnull, "w");
13918        if (!$gpg) {
13919            return $this->raiseError("gpg command failed");
13920        }
13921
13922        fwrite($gpg, "$input[0]\n");
13923        if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) {
13924            return $this->raiseError("gpg sign failed");
13925        }
13926
13927        if (!$tar->addModify("$tmpdir/package.sig", '', $tmpdir)) {
13928            return $this->raiseError('failed adding signature to file');
13929        }
13930
13931        $this->ui->outputData("Package signed.", $command);
13932        return true;
13933    }
13934
13935    /**
13936     * For unit testing purposes
13937     */
13938    function &getInstaller(&$ui)
13939    {
13940        if (!class_exists('PEAR_Installer')) {
13941            require_once 'PEAR/Installer.php';
13942        }
13943        $a = &new PEAR_Installer($ui);
13944        return $a;
13945    }
13946
13947    /**
13948     * For unit testing purposes
13949     */
13950    function &getCommandPackaging(&$ui, &$config)
13951    {
13952        if (!class_exists('PEAR_Command_Packaging')) {
13953            if ($fp = @fopen('PEAR/Command/Packaging.php', 'r', true)) {
13954                fclose($fp);
13955                include_once 'PEAR/Command/Packaging.php';
13956            }
13957        }
13958
13959        if (class_exists('PEAR_Command_Packaging')) {
13960            $a = &new PEAR_Command_Packaging($ui, $config);
13961        } else {
13962            $a = null;
13963        }
13964
13965        return $a;
13966    }
13967
13968    function doMakeRPM($command, $options, $params)
13969    {
13970
13971        // Check to see if PEAR_Command_Packaging is installed, and
13972        // transparently switch to use the "make-rpm-spec" command from it
13973        // instead, if it does. Otherwise, continue to use the old version
13974        // of "makerpm" supplied with this package (PEAR).
13975        $packaging_cmd = $this->getCommandPackaging($this->ui, $this->config);
13976        if ($packaging_cmd !== null) {
13977            $this->ui->outputData('PEAR_Command_Packaging is installed; using '.
13978                'newer "make-rpm-spec" command instead');
13979            return $packaging_cmd->run('make-rpm-spec', $options, $params);
13980        }
13981
13982        $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '.
13983          'improved version is available via "pear make-rpm-spec", which '.
13984          'is available by installing PEAR_Command_Packaging');
13985        return true;
13986    }
13987
13988    function doConvert($command, $options, $params)
13989    {
13990        $packagexml    = isset($params[0]) ? $params[0] : 'package.xml';
13991        $newpackagexml = isset($params[1]) ? $params[1] : dirname($packagexml) .
13992            DIRECTORY_SEPARATOR . 'package2.xml';
13993        $pkg = &$this->getPackageFile($this->config, $this->_debug);
13994        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
13995        $pf = $pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL);
13996        PEAR::staticPopErrorHandling();
13997        if (PEAR::isError($pf)) {
13998            if (is_array($pf->getUserInfo())) {
13999                foreach ($pf->getUserInfo() as $warning) {
14000                    $this->ui->outputData($warning['message']);
14001                }
14002            }
14003            return $this->raiseError($pf);
14004        }
14005
14006        if (is_a($pf, 'PEAR_PackageFile_v2')) {
14007            $this->ui->outputData($packagexml . ' is already a package.xml version 2.0');
14008            return true;
14009        }
14010
14011        $gen   = &$pf->getDefaultGenerator();
14012        $newpf = &$gen->toV2();
14013        $newpf->setPackagefile($newpackagexml);
14014        $gen = &$newpf->getDefaultGenerator();
14015        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
14016        $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL);
14017        $saved = $gen->toPackageFile(dirname($newpackagexml), $state, basename($newpackagexml));
14018        PEAR::staticPopErrorHandling();
14019        if (PEAR::isError($saved)) {
14020            if (is_array($saved->getUserInfo())) {
14021                foreach ($saved->getUserInfo() as $warning) {
14022                    $this->ui->outputData($warning['message']);
14023                }
14024            }
14025
14026            $this->ui->outputData($saved->getMessage());
14027            return true;
14028        }
14029
14030        $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"');
14031        return true;
14032    }
14033}PEAR-1.9.4/PEAR/Command/Pickle.xml0000644000076500000240000000223311605156614015272 0ustar  helgistaff<commands version="1.0">
14034 <pickle>
14035  <summary>Build PECL Package</summary>
14036  <function>doPackage</function>
14037  <shortcut>pi</shortcut>
14038  <options>
14039   <nocompress>
14040    <shortopt>Z</shortopt>
14041    <doc>Do not gzip the package file</doc>
14042   </nocompress>
14043   <showname>
14044    <shortopt>n</shortopt>
14045    <doc>Print the name of the packaged file.</doc>
14046   </showname>
14047  </options>
14048  <doc>[descfile]
14049Creates a PECL package from its package2.xml file.
14050
14051An automatic conversion will be made to a package.xml 1.0 and written out to
14052disk in the current directory as &quot;package.xml&quot;.  Note that
14053only simple package.xml 2.0 will be converted.  package.xml 2.0 with:
14054
14055 - dependency types other than required/optional PECL package/ext/php/pearinstaller
14056 - more than one extsrcrelease or zendextsrcrelease
14057 - zendextbinrelease, extbinrelease, phprelease, or bundle release type
14058 - dependency groups
14059 - ignore tags in release filelist
14060 - tasks other than replace
14061 - custom roles
14062
14063will cause pickle to fail, and output an error message.  If your package2.xml
14064uses any of these features, you are best off using PEAR_PackageFileManager to
14065generate both package.xml.
14066</doc>
14067 </pickle>
14068</commands>PEAR-1.9.4/PEAR/Command/Pickle.php0000644000076500000240000003715611605156614015275 0ustar  helgistaff<?php
14069/**
14070 * PEAR_Command_Pickle (pickle command)
14071 *
14072 * PHP versions 4 and 5
14073 *
14074 * @category   pear
14075 * @package    PEAR
14076 * @author     Greg Beaver <cellog@php.net>
14077 * @copyright  2005-2009 The Authors
14078 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
14079 * @version    CVS: $Id: Pickle.php 313023 2011-07-06 19:17:11Z dufuz $
14080 * @link       http://pear.php.net/package/PEAR
14081 * @since      File available since Release 1.4.1
14082 */
14083
14084/**
14085 * base class
14086 */
14087require_once 'PEAR/Command/Common.php';
14088
14089/**
14090 * PEAR commands for login/logout
14091 *
14092 * @category   pear
14093 * @package    PEAR
14094 * @author     Greg Beaver <cellog@php.net>
14095 * @copyright  2005-2009 The Authors
14096 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
14097 * @version    Release: 1.9.4
14098 * @link       http://pear.php.net/package/PEAR
14099 * @since      Class available since Release 1.4.1
14100 */
14101
14102class PEAR_Command_Pickle extends PEAR_Command_Common
14103{
14104    var $commands = array(
14105        'pickle' => array(
14106            'summary' => 'Build PECL Package',
14107            'function' => 'doPackage',
14108            'shortcut' => 'pi',
14109            'options' => array(
14110                'nocompress' => array(
14111                    'shortopt' => 'Z',
14112                    'doc' => 'Do not gzip the package file'
14113                    ),
14114                'showname' => array(
14115                    'shortopt' => 'n',
14116                    'doc' => 'Print the name of the packaged file.',
14117                    ),
14118                ),
14119            'doc' => '[descfile]
14120Creates a PECL package from its package2.xml file.
14121
14122An automatic conversion will be made to a package.xml 1.0 and written out to
14123disk in the current directory as "package.xml".  Note that
14124only simple package.xml 2.0 will be converted.  package.xml 2.0 with:
14125
14126 - dependency types other than required/optional PECL package/ext/php/pearinstaller
14127 - more than one extsrcrelease or zendextsrcrelease
14128 - zendextbinrelease, extbinrelease, phprelease, or bundle release type
14129 - dependency groups
14130 - ignore tags in release filelist
14131 - tasks other than replace
14132 - custom roles
14133
14134will cause pickle to fail, and output an error message.  If your package2.xml
14135uses any of these features, you are best off using PEAR_PackageFileManager to
14136generate both package.xml.
14137'
14138            ),
14139        );
14140
14141    /**
14142     * PEAR_Command_Package constructor.
14143     *
14144     * @access public
14145     */
14146    function PEAR_Command_Pickle(&$ui, &$config)
14147    {
14148        parent::PEAR_Command_Common($ui, $config);
14149    }
14150
14151    /**
14152     * For unit-testing ease
14153     *
14154     * @return PEAR_Packager
14155     */
14156    function &getPackager()
14157    {
14158        if (!class_exists('PEAR_Packager')) {
14159            require_once 'PEAR/Packager.php';
14160        }
14161
14162        $a = &new PEAR_Packager;
14163        return $a;
14164    }
14165
14166    /**
14167     * For unit-testing ease
14168     *
14169     * @param PEAR_Config $config
14170     * @param bool $debug
14171     * @param string|null $tmpdir
14172     * @return PEAR_PackageFile
14173     */
14174    function &getPackageFile($config, $debug = false)
14175    {
14176        if (!class_exists('PEAR_Common')) {
14177            require_once 'PEAR/Common.php';
14178        }
14179
14180        if (!class_exists('PEAR_PackageFile')) {
14181            require_once 'PEAR/PackageFile.php';
14182        }
14183
14184        $a = &new PEAR_PackageFile($config, $debug);
14185        $common = new PEAR_Common;
14186        $common->ui = $this->ui;
14187        $a->setLogger($common);
14188        return $a;
14189    }
14190
14191    function doPackage($command, $options, $params)
14192    {
14193        $this->output = '';
14194        $pkginfofile = isset($params[0]) ? $params[0] : 'package2.xml';
14195        $packager = &$this->getPackager();
14196        if (PEAR::isError($err = $this->_convertPackage($pkginfofile))) {
14197            return $err;
14198        }
14199
14200        $compress = empty($options['nocompress']) ? true : false;
14201        $result = $packager->package($pkginfofile, $compress, 'package.xml');
14202        if (PEAR::isError($result)) {
14203            return $this->raiseError($result);
14204        }
14205
14206        // Don't want output, only the package file name just created
14207        if (isset($options['showname'])) {
14208            $this->ui->outputData($result, $command);
14209        }
14210
14211        return true;
14212    }
14213
14214    function _convertPackage($packagexml)
14215    {
14216        $pkg = &$this->getPackageFile($this->config);
14217        $pf2 = &$pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL);
14218        if (!is_a($pf2, 'PEAR_PackageFile_v2')) {
14219            return $this->raiseError('Cannot process "' .
14220                $packagexml . '", is not a package.xml 2.0');
14221        }
14222
14223        require_once 'PEAR/PackageFile/v1.php';
14224        $pf = new PEAR_PackageFile_v1;
14225        $pf->setConfig($this->config);
14226        if ($pf2->getPackageType() != 'extsrc' && $pf2->getPackageType() != 'zendextsrc') {
14227            return $this->raiseError('Cannot safely convert "' . $packagexml .
14228            '", is not an extension source package.  Using a PEAR_PackageFileManager-based ' .
14229            'script is an option');
14230        }
14231
14232        if (is_array($pf2->getUsesRole())) {
14233            return $this->raiseError('Cannot safely convert "' . $packagexml .
14234            '", contains custom roles.  Using a PEAR_PackageFileManager-based script or ' .
14235            'the convert command is an option');
14236        }
14237
14238        if (is_array($pf2->getUsesTask())) {
14239            return $this->raiseError('Cannot safely convert "' . $packagexml .
14240            '", contains custom tasks.  Using a PEAR_PackageFileManager-based script or ' .
14241            'the convert command is an option');
14242        }
14243
14244        $deps = $pf2->getDependencies();
14245        if (isset($deps['group'])) {
14246            return $this->raiseError('Cannot safely convert "' . $packagexml .
14247            '", contains dependency groups.  Using a PEAR_PackageFileManager-based script ' .
14248            'or the convert command is an option');
14249        }
14250
14251        if (isset($deps['required']['subpackage']) ||
14252              isset($deps['optional']['subpackage'])) {
14253            return $this->raiseError('Cannot safely convert "' . $packagexml .
14254            '", contains subpackage dependencies.  Using a PEAR_PackageFileManager-based  '.
14255            'script is an option');
14256        }
14257
14258        if (isset($deps['required']['os'])) {
14259            return $this->raiseError('Cannot safely convert "' . $packagexml .
14260            '", contains os dependencies.  Using a PEAR_PackageFileManager-based  '.
14261            'script is an option');
14262        }
14263
14264        if (isset($deps['required']['arch'])) {
14265            return $this->raiseError('Cannot safely convert "' . $packagexml .
14266            '", contains arch dependencies.  Using a PEAR_PackageFileManager-based  '.
14267            'script is an option');
14268        }
14269
14270        $pf->setPackage($pf2->getPackage());
14271        $pf->setSummary($pf2->getSummary());
14272        $pf->setDescription($pf2->getDescription());
14273        foreach ($pf2->getMaintainers() as $maintainer) {
14274            $pf->addMaintainer($maintainer['role'], $maintainer['handle'],
14275                $maintainer['name'], $maintainer['email']);
14276        }
14277
14278        $pf->setVersion($pf2->getVersion());
14279        $pf->setDate($pf2->getDate());
14280        $pf->setLicense($pf2->getLicense());
14281        $pf->setState($pf2->getState());
14282        $pf->setNotes($pf2->getNotes());
14283        $pf->addPhpDep($deps['required']['php']['min'], 'ge');
14284        if (isset($deps['required']['php']['max'])) {
14285            $pf->addPhpDep($deps['required']['php']['max'], 'le');
14286        }
14287
14288        if (isset($deps['required']['package'])) {
14289            if (!isset($deps['required']['package'][0])) {
14290                $deps['required']['package'] = array($deps['required']['package']);
14291            }
14292
14293            foreach ($deps['required']['package'] as $dep) {
14294                if (!isset($dep['channel'])) {
14295                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
14296                    ' contains uri-based dependency on a package.  Using a ' .
14297                    'PEAR_PackageFileManager-based script is an option');
14298                }
14299
14300                if ($dep['channel'] != 'pear.php.net'
14301                    && $dep['channel'] != 'pecl.php.net'
14302                    && $dep['channel'] != 'doc.php.net') {
14303                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
14304                    ' contains dependency on a non-standard channel package.  Using a ' .
14305                    'PEAR_PackageFileManager-based script is an option');
14306                }
14307
14308                if (isset($dep['conflicts'])) {
14309                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
14310                    ' contains conflicts dependency.  Using a ' .
14311                    'PEAR_PackageFileManager-based script is an option');
14312                }
14313
14314                if (isset($dep['exclude'])) {
14315                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
14316                }
14317
14318                if (isset($dep['min'])) {
14319                    $pf->addPackageDep($dep['name'], $dep['min'], 'ge');
14320                }
14321
14322                if (isset($dep['max'])) {
14323                    $pf->addPackageDep($dep['name'], $dep['max'], 'le');
14324                }
14325            }
14326        }
14327
14328        if (isset($deps['required']['extension'])) {
14329            if (!isset($deps['required']['extension'][0])) {
14330                $deps['required']['extension'] = array($deps['required']['extension']);
14331            }
14332
14333            foreach ($deps['required']['extension'] as $dep) {
14334                if (isset($dep['conflicts'])) {
14335                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
14336                    ' contains conflicts dependency.  Using a ' .
14337                    'PEAR_PackageFileManager-based script is an option');
14338                }
14339
14340                if (isset($dep['exclude'])) {
14341                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
14342                }
14343
14344                if (isset($dep['min'])) {
14345                    $pf->addExtensionDep($dep['name'], $dep['min'], 'ge');
14346                }
14347
14348                if (isset($dep['max'])) {
14349                    $pf->addExtensionDep($dep['name'], $dep['max'], 'le');
14350                }
14351            }
14352        }
14353
14354        if (isset($deps['optional']['package'])) {
14355            if (!isset($deps['optional']['package'][0])) {
14356                $deps['optional']['package'] = array($deps['optional']['package']);
14357            }
14358
14359            foreach ($deps['optional']['package'] as $dep) {
14360                if (!isset($dep['channel'])) {
14361                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
14362                    ' contains uri-based dependency on a package.  Using a ' .
14363                    'PEAR_PackageFileManager-based script is an option');
14364                }
14365
14366                if ($dep['channel'] != 'pear.php.net'
14367                    && $dep['channel'] != 'pecl.php.net'
14368                    && $dep['channel'] != 'doc.php.net') {
14369                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
14370                    ' contains dependency on a non-standard channel package.  Using a ' .
14371                    'PEAR_PackageFileManager-based script is an option');
14372                }
14373
14374                if (isset($dep['exclude'])) {
14375                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
14376                }
14377
14378                if (isset($dep['min'])) {
14379                    $pf->addPackageDep($dep['name'], $dep['min'], 'ge', 'yes');
14380                }
14381
14382                if (isset($dep['max'])) {
14383                    $pf->addPackageDep($dep['name'], $dep['max'], 'le', 'yes');
14384                }
14385            }
14386        }
14387
14388        if (isset($deps['optional']['extension'])) {
14389            if (!isset($deps['optional']['extension'][0])) {
14390                $deps['optional']['extension'] = array($deps['optional']['extension']);
14391            }
14392
14393            foreach ($deps['optional']['extension'] as $dep) {
14394                if (isset($dep['exclude'])) {
14395                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
14396                }
14397
14398                if (isset($dep['min'])) {
14399                    $pf->addExtensionDep($dep['name'], $dep['min'], 'ge', 'yes');
14400                }
14401
14402                if (isset($dep['max'])) {
14403                    $pf->addExtensionDep($dep['name'], $dep['max'], 'le', 'yes');
14404                }
14405            }
14406        }
14407
14408        $contents = $pf2->getContents();
14409        $release  = $pf2->getReleases();
14410        if (isset($releases[0])) {
14411            return $this->raiseError('Cannot safely process "' . $packagexml . '" contains '
14412            . 'multiple extsrcrelease/zendextsrcrelease tags.  Using a PEAR_PackageFileManager-based script ' .
14413            'or the convert command is an option');
14414        }
14415
14416        if ($configoptions = $pf2->getConfigureOptions()) {
14417            foreach ($configoptions as $option) {
14418                $default = isset($option['default']) ? $option['default'] : false;
14419                $pf->addConfigureOption($option['name'], $option['prompt'], $default);
14420            }
14421        }
14422
14423        if (isset($release['filelist']['ignore'])) {
14424            return $this->raiseError('Cannot safely process "' . $packagexml . '" contains '
14425            . 'ignore tags.  Using a PEAR_PackageFileManager-based script or the convert' .
14426            ' command is an option');
14427        }
14428
14429        if (isset($release['filelist']['install']) &&
14430              !isset($release['filelist']['install'][0])) {
14431            $release['filelist']['install'] = array($release['filelist']['install']);
14432        }
14433
14434        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
14435            $baseinstalldir = $contents['dir']['attribs']['baseinstalldir'];
14436        } else {
14437            $baseinstalldir = false;
14438        }
14439
14440        if (!isset($contents['dir']['file'][0])) {
14441            $contents['dir']['file'] = array($contents['dir']['file']);
14442        }
14443
14444        foreach ($contents['dir']['file'] as $file) {
14445            if ($baseinstalldir && !isset($file['attribs']['baseinstalldir'])) {
14446                $file['attribs']['baseinstalldir'] = $baseinstalldir;
14447            }
14448
14449            $processFile = $file;
14450            unset($processFile['attribs']);
14451            if (count($processFile)) {
14452                foreach ($processFile as $name => $task) {
14453                    if ($name != $pf2->getTasksNs() . ':replace') {
14454                        return $this->raiseError('Cannot safely process "' . $packagexml .
14455                        '" contains tasks other than replace.  Using a ' .
14456                        'PEAR_PackageFileManager-based script is an option.');
14457                    }
14458                    $file['attribs']['replace'][] = $task;
14459                }
14460            }
14461
14462            if (!in_array($file['attribs']['role'], PEAR_Common::getFileRoles())) {
14463                return $this->raiseError('Cannot safely convert "' . $packagexml .
14464                '", contains custom roles.  Using a PEAR_PackageFileManager-based script ' .
14465                'or the convert command is an option');
14466            }
14467
14468            if (isset($release['filelist']['install'])) {
14469                foreach ($release['filelist']['install'] as $installas) {
14470                    if ($installas['attribs']['name'] == $file['attribs']['name']) {
14471                        $file['attribs']['install-as'] = $installas['attribs']['as'];
14472                    }
14473                }
14474            }
14475
14476            $pf->addFile('/', $file['attribs']['name'], $file['attribs']);
14477        }
14478
14479        if ($pf2->getChangeLog()) {
14480            $this->ui->outputData('WARNING: changelog is not translated to package.xml ' .
14481                '1.0, use PEAR_PackageFileManager-based script if you need changelog-' .
14482                'translation for package.xml 1.0');
14483        }
14484
14485        $gen = &$pf->getDefaultGenerator();
14486        $gen->toPackageFile('.');
14487    }
14488}PEAR-1.9.4/PEAR/Command/Registry.xml0000644000076500000240000000337611605156614015704 0ustar  helgistaff<commands version="1.0">
14489 <list>
14490  <summary>List Installed Packages In The Default Channel</summary>
14491  <function>doList</function>
14492  <shortcut>l</shortcut>
14493  <options>
14494   <channel>
14495    <shortopt>c</shortopt>
14496    <doc>list installed packages from this channel</doc>
14497    <arg>CHAN</arg>
14498   </channel>
14499   <allchannels>
14500    <shortopt>a</shortopt>
14501    <doc>list installed packages from all channels</doc>
14502   </allchannels>
14503   <channelinfo>
14504    <shortopt>i</shortopt>
14505    <doc>output fully channel-aware data, even on failure</doc>
14506   </channelinfo>
14507  </options>
14508  <doc>&lt;package&gt;
14509If invoked without parameters, this command lists the PEAR packages
14510installed in your php_dir ({config php_dir}).  With a parameter, it
14511lists the files in a package.
14512</doc>
14513 </list>
14514 <list-files>
14515  <summary>List Files In Installed Package</summary>
14516  <function>doFileList</function>
14517  <shortcut>fl</shortcut>
14518  <options />
14519  <doc>&lt;package&gt;
14520List the files in an installed package.
14521</doc>
14522 </list-files>
14523 <shell-test>
14524  <summary>Shell Script Test</summary>
14525  <function>doShellTest</function>
14526  <shortcut>st</shortcut>
14527  <options />
14528  <doc>&lt;package&gt; [[relation] version]
14529Tests if a package is installed in the system. Will exit(1) if it is not.
14530   &lt;relation&gt;   The version comparison operator. One of:
14531                &lt;, lt, &lt;=, le, &gt;, gt, &gt;=, ge, ==, =, eq, !=, &lt;&gt;, ne
14532   &lt;version&gt;    The version to compare with
14533</doc>
14534 </shell-test>
14535 <info>
14536  <summary>Display information about a package</summary>
14537  <function>doInfo</function>
14538  <shortcut>in</shortcut>
14539  <options />
14540  <doc>&lt;package&gt;
14541Displays information about a package. The package argument may be a
14542local package file, an URL to a package file, or the name of an
14543installed package.</doc>
14544 </info>
14545</commands>PEAR-1.9.4/PEAR/Command/Registry.php0000644000076500000240000013241611605156614015671 0ustar  helgistaff<?php
14546/**
14547 * PEAR_Command_Registry (list, list-files, shell-test, info commands)
14548 *
14549 * PHP versions 4 and 5
14550 *
14551 * @category   pear
14552 * @package    PEAR
14553 * @author     Stig Bakken <ssb@php.net>
14554 * @author     Greg Beaver <cellog@php.net>
14555 * @copyright  1997-2009 The Authors
14556 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
14557 * @version    CVS: $Id: Registry.php 313023 2011-07-06 19:17:11Z dufuz $
14558 * @link       http://pear.php.net/package/PEAR
14559 * @since      File available since Release 0.1
14560 */
14561
14562/**
14563 * base class
14564 */
14565require_once 'PEAR/Command/Common.php';
14566
14567/**
14568 * PEAR commands for registry manipulation
14569 *
14570 * @category   pear
14571 * @package    PEAR
14572 * @author     Stig Bakken <ssb@php.net>
14573 * @author     Greg Beaver <cellog@php.net>
14574 * @copyright  1997-2009 The Authors
14575 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
14576 * @version    Release: 1.9.4
14577 * @link       http://pear.php.net/package/PEAR
14578 * @since      Class available since Release 0.1
14579 */
14580class PEAR_Command_Registry extends PEAR_Command_Common
14581{
14582    var $commands = array(
14583        'list' => array(
14584            'summary' => 'List Installed Packages In The Default Channel',
14585            'function' => 'doList',
14586            'shortcut' => 'l',
14587            'options' => array(
14588                'channel' => array(
14589                    'shortopt' => 'c',
14590                    'doc' => 'list installed packages from this channel',
14591                    'arg' => 'CHAN',
14592                    ),
14593                'allchannels' => array(
14594                    'shortopt' => 'a',
14595                    'doc' => 'list installed packages from all channels',
14596                    ),
14597                'channelinfo' => array(
14598                    'shortopt' => 'i',
14599                    'doc' => 'output fully channel-aware data, even on failure',
14600                    ),
14601                ),
14602            'doc' => '<package>
14603If invoked without parameters, this command lists the PEAR packages
14604installed in your php_dir ({config php_dir}).  With a parameter, it
14605lists the files in a package.
14606',
14607            ),
14608        'list-files' => array(
14609            'summary' => 'List Files In Installed Package',
14610            'function' => 'doFileList',
14611            'shortcut' => 'fl',
14612            'options' => array(),
14613            'doc' => '<package>
14614List the files in an installed package.
14615'
14616            ),
14617        'shell-test' => array(
14618            'summary' => 'Shell Script Test',
14619            'function' => 'doShellTest',
14620            'shortcut' => 'st',
14621            'options' => array(),
14622            'doc' => '<package> [[relation] version]
14623Tests if a package is installed in the system. Will exit(1) if it is not.
14624   <relation>   The version comparison operator. One of:
14625                <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
14626   <version>    The version to compare with
14627'),
14628        'info' => array(
14629            'summary'  => 'Display information about a package',
14630            'function' => 'doInfo',
14631            'shortcut' => 'in',
14632            'options'  => array(),
14633            'doc'      => '<package>
14634Displays information about a package. The package argument may be a
14635local package file, an URL to a package file, or the name of an
14636installed package.'
14637            )
14638        );
14639
14640    /**
14641     * PEAR_Command_Registry constructor.
14642     *
14643     * @access public
14644     */
14645    function PEAR_Command_Registry(&$ui, &$config)
14646    {
14647        parent::PEAR_Command_Common($ui, $config);
14648    }
14649
14650    function _sortinfo($a, $b)
14651    {
14652        $apackage = isset($a['package']) ? $a['package'] : $a['name'];
14653        $bpackage = isset($b['package']) ? $b['package'] : $b['name'];
14654        return strcmp($apackage, $bpackage);
14655    }
14656
14657    function doList($command, $options, $params)
14658    {
14659        $reg = &$this->config->getRegistry();
14660        $channelinfo = isset($options['channelinfo']);
14661        if (isset($options['allchannels']) && !$channelinfo) {
14662            return $this->doListAll($command, array(), $params);
14663        }
14664
14665        if (isset($options['allchannels']) && $channelinfo) {
14666            // allchannels with $channelinfo
14667            unset($options['allchannels']);
14668            $channels = $reg->getChannels();
14669            $errors = array();
14670            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
14671            foreach ($channels as $channel) {
14672                $options['channel'] = $channel->getName();
14673                $ret = $this->doList($command, $options, $params);
14674
14675                if (PEAR::isError($ret)) {
14676                    $errors[] = $ret;
14677                }
14678            }
14679
14680            PEAR::staticPopErrorHandling();
14681            if (count($errors)) {
14682                // for now, only give first error
14683                return PEAR::raiseError($errors[0]);
14684            }
14685
14686            return true;
14687        }
14688
14689        if (count($params) === 1) {
14690            return $this->doFileList($command, $options, $params);
14691        }
14692
14693        if (isset($options['channel'])) {
14694            if (!$reg->channelExists($options['channel'])) {
14695                return $this->raiseError('Channel "' . $options['channel'] .'" does not exist');
14696            }
14697
14698            $channel = $reg->channelName($options['channel']);
14699        } else {
14700            $channel = $this->config->get('default_channel');
14701        }
14702
14703        $installed = $reg->packageInfo(null, null, $channel);
14704        usort($installed, array(&$this, '_sortinfo'));
14705
14706        $data = array(
14707            'caption' => 'Installed packages, channel ' .
14708                $channel . ':',
14709            'border' => true,
14710            'headline' => array('Package', 'Version', 'State'),
14711            'channel' => $channel,
14712            );
14713        if ($channelinfo) {
14714            $data['headline'] = array('Channel', 'Package', 'Version', 'State');
14715        }
14716
14717        if (count($installed) && !isset($data['data'])) {
14718            $data['data'] = array();
14719        }
14720
14721        foreach ($installed as $package) {
14722            $pobj = $reg->getPackage(isset($package['package']) ?
14723                                        $package['package'] : $package['name'], $channel);
14724            if ($channelinfo) {
14725                $packageinfo = array($pobj->getChannel(), $pobj->getPackage(), $pobj->getVersion(),
14726                                    $pobj->getState() ? $pobj->getState() : null);
14727            } else {
14728                $packageinfo = array($pobj->getPackage(), $pobj->getVersion(),
14729                                    $pobj->getState() ? $pobj->getState() : null);
14730            }
14731            $data['data'][] = $packageinfo;
14732        }
14733
14734        if (count($installed) === 0) {
14735            if (!$channelinfo) {
14736                $data = '(no packages installed from channel ' . $channel . ')';
14737            } else {
14738                $data = array(
14739                    'caption' => 'Installed packages, channel ' .
14740                        $channel . ':',
14741                    'border' => true,
14742                    'channel' => $channel,
14743                    'data' => array(array('(no packages installed)')),
14744                );
14745            }
14746        }
14747
14748        $this->ui->outputData($data, $command);
14749        return true;
14750    }
14751
14752    function doListAll($command, $options, $params)
14753    {
14754        // This duplicate code is deprecated over
14755        // list --channelinfo, which gives identical
14756        // output for list and list --allchannels.
14757        $reg = &$this->config->getRegistry();
14758        $installed = $reg->packageInfo(null, null, null);
14759        foreach ($installed as $channel => $packages) {
14760            usort($packages, array($this, '_sortinfo'));
14761            $data = array(
14762                'caption'  => 'Installed packages, channel ' . $channel . ':',
14763                'border'   => true,
14764                'headline' => array('Package', 'Version', 'State'),
14765                'channel'  => $channel
14766            );
14767
14768            foreach ($packages as $package) {
14769                $p = isset($package['package']) ? $package['package'] : $package['name'];
14770                $pobj = $reg->getPackage($p, $channel);
14771                $data['data'][] = array($pobj->getPackage(), $pobj->getVersion(),
14772                                        $pobj->getState() ? $pobj->getState() : null);
14773            }
14774
14775            // Adds a blank line after each section
14776            $data['data'][] = array();
14777
14778            if (count($packages) === 0) {
14779                $data = array(
14780                    'caption' => 'Installed packages, channel ' . $channel . ':',
14781                    'border' => true,
14782                    'data' => array(array('(no packages installed)'), array()),
14783                    'channel' => $channel
14784                    );
14785            }
14786            $this->ui->outputData($data, $command);
14787        }
14788        return true;
14789    }
14790
14791    function doFileList($command, $options, $params)
14792    {
14793        if (count($params) !== 1) {
14794            return $this->raiseError('list-files expects 1 parameter');
14795        }
14796
14797        $reg = &$this->config->getRegistry();
14798        $fp = false;
14799        if (!is_dir($params[0]) && (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))) {
14800            if ($fp) {
14801                fclose($fp);
14802            }
14803
14804            if (!class_exists('PEAR_PackageFile')) {
14805                require_once 'PEAR/PackageFile.php';
14806            }
14807
14808            $pkg = &new PEAR_PackageFile($this->config, $this->_debug);
14809            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
14810            $info = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
14811            PEAR::staticPopErrorHandling();
14812            $headings = array('Package File', 'Install Path');
14813            $installed = false;
14814        } else {
14815            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
14816            $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
14817            PEAR::staticPopErrorHandling();
14818            if (PEAR::isError($parsed)) {
14819                return $this->raiseError($parsed);
14820            }
14821
14822            $info = &$reg->getPackage($parsed['package'], $parsed['channel']);
14823            $headings = array('Type', 'Install Path');
14824            $installed = true;
14825        }
14826
14827        if (PEAR::isError($info)) {
14828            return $this->raiseError($info);
14829        }
14830
14831        if ($info === null) {
14832            return $this->raiseError("`$params[0]' not installed");
14833        }
14834
14835        $list = ($info->getPackagexmlVersion() == '1.0' || $installed) ?
14836            $info->getFilelist() : $info->getContents();
14837        if ($installed) {
14838            $caption = 'Installed Files For ' . $params[0];
14839        } else {
14840            $caption = 'Contents of ' . basename($params[0]);
14841        }
14842
14843        $data = array(
14844            'caption' => $caption,
14845            'border' => true,
14846            'headline' => $headings);
14847        if ($info->getPackagexmlVersion() == '1.0' || $installed) {
14848            foreach ($list as $file => $att) {
14849                if ($installed) {
14850                    if (empty($att['installed_as'])) {
14851                        continue;
14852                    }
14853                    $data['data'][] = array($att['role'], $att['installed_as']);
14854                } else {
14855                    if (isset($att['baseinstalldir']) && !in_array($att['role'],
14856                          array('test', 'data', 'doc'))) {
14857                        $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR .
14858                            $file;
14859                    } else {
14860                        $dest = $file;
14861                    }
14862                    switch ($att['role']) {
14863                        case 'test':
14864                        case 'data':
14865                        case 'doc':
14866                            $role = $att['role'];
14867                            if ($role == 'test') {
14868                                $role .= 's';
14869                            }
14870                            $dest = $this->config->get($role . '_dir') . DIRECTORY_SEPARATOR .
14871                                $info->getPackage() . DIRECTORY_SEPARATOR . $dest;
14872                            break;
14873                        case 'php':
14874                        default:
14875                            $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR .
14876                                $dest;
14877                    }
14878                    $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
14879                    $dest = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
14880                                                    array(DIRECTORY_SEPARATOR,
14881                                                          DIRECTORY_SEPARATOR,
14882                                                          DIRECTORY_SEPARATOR),
14883                                                    $dest);
14884                    $file = preg_replace('!/+!', '/', $file);
14885                    $data['data'][] = array($file, $dest);
14886                }
14887            }
14888        } else { // package.xml 2.0, not installed
14889            if (!isset($list['dir']['file'][0])) {
14890                $list['dir']['file'] = array($list['dir']['file']);
14891            }
14892
14893            foreach ($list['dir']['file'] as $att) {
14894                $att = $att['attribs'];
14895                $file = $att['name'];
14896                $role = &PEAR_Installer_Role::factory($info, $att['role'], $this->config);
14897                $role->setup($this, $info, $att, $file);
14898                if (!$role->isInstallable()) {
14899                    $dest = '(not installable)';
14900                } else {
14901                    $dest = $role->processInstallation($info, $att, $file, '');
14902                    if (PEAR::isError($dest)) {
14903                        $dest = '(Unknown role "' . $att['role'] . ')';
14904                    } else {
14905                        list(,, $dest) = $dest;
14906                    }
14907                }
14908                $data['data'][] = array($file, $dest);
14909            }
14910        }
14911
14912        $this->ui->outputData($data, $command);
14913        return true;
14914    }
14915
14916    function doShellTest($command, $options, $params)
14917    {
14918        if (count($params) < 1) {
14919            return PEAR::raiseError('ERROR, usage: pear shell-test packagename [[relation] version]');
14920        }
14921
14922        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
14923        $reg = &$this->config->getRegistry();
14924        $info = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
14925        if (PEAR::isError($info)) {
14926            exit(1); // invalid package name
14927        }
14928
14929        $package = $info['package'];
14930        $channel = $info['channel'];
14931        // "pear shell-test Foo"
14932        if (!$reg->packageExists($package, $channel)) {
14933            if ($channel == 'pecl.php.net') {
14934                if ($reg->packageExists($package, 'pear.php.net')) {
14935                    $channel = 'pear.php.net'; // magically change channels for extensions
14936                }
14937            }
14938        }
14939
14940        if (count($params) === 1) {
14941            if (!$reg->packageExists($package, $channel)) {
14942                exit(1);
14943            }
14944            // "pear shell-test Foo 1.0"
14945        } elseif (count($params) === 2) {
14946            $v = $reg->packageInfo($package, 'version', $channel);
14947            if (!$v || !version_compare("$v", "{$params[1]}", "ge")) {
14948                exit(1);
14949            }
14950            // "pear shell-test Foo ge 1.0"
14951        } elseif (count($params) === 3) {
14952            $v = $reg->packageInfo($package, 'version', $channel);
14953            if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) {
14954                exit(1);
14955            }
14956        } else {
14957            PEAR::staticPopErrorHandling();
14958            $this->raiseError("$command: expects 1 to 3 parameters");
14959            exit(1);
14960        }
14961    }
14962
14963    function doInfo($command, $options, $params)
14964    {
14965        if (count($params) !== 1) {
14966            return $this->raiseError('pear info expects 1 parameter');
14967        }
14968
14969        $info = $fp = false;
14970        $reg = &$this->config->getRegistry();
14971        if (is_file($params[0]) && !is_dir($params[0]) &&
14972            (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))
14973        ) {
14974            if ($fp) {
14975                fclose($fp);
14976            }
14977
14978            if (!class_exists('PEAR_PackageFile')) {
14979                require_once 'PEAR/PackageFile.php';
14980            }
14981
14982            $pkg = &new PEAR_PackageFile($this->config, $this->_debug);
14983            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
14984            $obj = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
14985            PEAR::staticPopErrorHandling();
14986            if (PEAR::isError($obj)) {
14987                $uinfo = $obj->getUserInfo();
14988                if (is_array($uinfo)) {
14989                    foreach ($uinfo as $message) {
14990                        if (is_array($message)) {
14991                            $message = $message['message'];
14992                        }
14993                        $this->ui->outputData($message);
14994                    }
14995                }
14996
14997                return $this->raiseError($obj);
14998            }
14999
15000            if ($obj->getPackagexmlVersion() != '1.0') {
15001                return $this->_doInfo2($command, $options, $params, $obj, false);
15002            }
15003
15004            $info = $obj->toArray();
15005        } else {
15006            $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
15007            if (PEAR::isError($parsed)) {
15008                return $this->raiseError($parsed);
15009            }
15010
15011            $package = $parsed['package'];
15012            $channel = $parsed['channel'];
15013            $info = $reg->packageInfo($package, null, $channel);
15014            if (isset($info['old'])) {
15015                $obj = $reg->getPackage($package, $channel);
15016                return $this->_doInfo2($command, $options, $params, $obj, true);
15017            }
15018        }
15019
15020        if (PEAR::isError($info)) {
15021            return $info;
15022        }
15023
15024        if (empty($info)) {
15025            $this->raiseError("No information found for `$params[0]'");
15026            return;
15027        }
15028
15029        unset($info['filelist']);
15030        unset($info['dirtree']);
15031        unset($info['changelog']);
15032        if (isset($info['xsdversion'])) {
15033            $info['package.xml version'] = $info['xsdversion'];
15034            unset($info['xsdversion']);
15035        }
15036
15037        if (isset($info['packagerversion'])) {
15038            $info['packaged with PEAR version'] = $info['packagerversion'];
15039            unset($info['packagerversion']);
15040        }
15041
15042        $keys = array_keys($info);
15043        $longtext = array('description', 'summary');
15044        foreach ($keys as $key) {
15045            if (is_array($info[$key])) {
15046                switch ($key) {
15047                    case 'maintainers': {
15048                        $i = 0;
15049                        $mstr = '';
15050                        foreach ($info[$key] as $m) {
15051                            if ($i++ > 0) {
15052                                $mstr .= "\n";
15053                            }
15054                            $mstr .= $m['name'] . " <";
15055                            if (isset($m['email'])) {
15056                                $mstr .= $m['email'];
15057                            } else {
15058                                $mstr .= $m['handle'] . '@php.net';
15059                            }
15060                            $mstr .= "> ($m[role])";
15061                        }
15062                        $info[$key] = $mstr;
15063                        break;
15064                    }
15065                    case 'release_deps': {
15066                        $i = 0;
15067                        $dstr = '';
15068                        foreach ($info[$key] as $d) {
15069                            if (isset($this->_deps_rel_trans[$d['rel']])) {
15070                                $rel = $this->_deps_rel_trans[$d['rel']];
15071                            } else {
15072                                $rel = $d['rel'];
15073                            }
15074                            if (isset($this->_deps_type_trans[$d['type']])) {
15075                                $type = ucfirst($this->_deps_type_trans[$d['type']]);
15076                            } else {
15077                                $type = $d['type'];
15078                            }
15079                            if (isset($d['name'])) {
15080                                $name = $d['name'] . ' ';
15081                            } else {
15082                                $name = '';
15083                            }
15084                            if (isset($d['version'])) {
15085                                $version = $d['version'] . ' ';
15086                            } else {
15087                                $version = '';
15088                            }
15089                            if (isset($d['optional']) && $d['optional'] == 'yes') {
15090                                $optional = ' (optional)';
15091                            } else {
15092                                $optional = '';
15093                            }
15094                            $dstr .= "$type $name$rel $version$optional\n";
15095                        }
15096                        $info[$key] = $dstr;
15097                        break;
15098                    }
15099                    case 'provides' : {
15100                        $debug = $this->config->get('verbose');
15101                        if ($debug < 2) {
15102                            $pstr = 'Classes: ';
15103                        } else {
15104                            $pstr = '';
15105                        }
15106                        $i = 0;
15107                        foreach ($info[$key] as $p) {
15108                            if ($debug < 2 && $p['type'] != "class") {
15109                                continue;
15110                            }
15111                            // Only print classes when verbosity mode is < 2
15112                            if ($debug < 2) {
15113                                if ($i++ > 0) {
15114                                    $pstr .= ", ";
15115                                }
15116                                $pstr .= $p['name'];
15117                            } else {
15118                                if ($i++ > 0) {
15119                                    $pstr .= "\n";
15120                                }
15121                                $pstr .= ucfirst($p['type']) . " " . $p['name'];
15122                                if (isset($p['explicit']) && $p['explicit'] == 1) {
15123                                    $pstr .= " (explicit)";
15124                                }
15125                            }
15126                        }
15127                        $info[$key] = $pstr;
15128                        break;
15129                    }
15130                    case 'configure_options' : {
15131                        foreach ($info[$key] as $i => $p) {
15132                            $info[$key][$i] = array_map(null, array_keys($p), array_values($p));
15133                            $info[$key][$i] = array_map(create_function('$a',
15134                                'return join(" = ",$a);'), $info[$key][$i]);
15135                            $info[$key][$i] = implode(', ', $info[$key][$i]);
15136                        }
15137                        $info[$key] = implode("\n", $info[$key]);
15138                        break;
15139                    }
15140                    default: {
15141                        $info[$key] = implode(", ", $info[$key]);
15142                        break;
15143                    }
15144                }
15145            }
15146
15147            if ($key == '_lastmodified') {
15148                $hdate = date('Y-m-d', $info[$key]);
15149                unset($info[$key]);
15150                $info['Last Modified'] = $hdate;
15151            } elseif ($key == '_lastversion') {
15152                $info['Previous Installed Version'] = $info[$key] ? $info[$key] : '- None -';
15153                unset($info[$key]);
15154            } else {
15155                $info[$key] = trim($info[$key]);
15156                if (in_array($key, $longtext)) {
15157                    $info[$key] = preg_replace('/  +/', ' ', $info[$key]);
15158                }
15159            }
15160        }
15161
15162        $caption = 'About ' . $info['package'] . '-' . $info['version'];
15163        $data = array(
15164            'caption' => $caption,
15165            'border' => true);
15166        foreach ($info as $key => $value) {
15167            $key = ucwords(trim(str_replace('_', ' ', $key)));
15168            $data['data'][] = array($key, $value);
15169        }
15170        $data['raw'] = $info;
15171
15172        $this->ui->outputData($data, 'package-info');
15173    }
15174
15175    /**
15176     * @access private
15177     */
15178    function _doInfo2($command, $options, $params, &$obj, $installed)
15179    {
15180        $reg = &$this->config->getRegistry();
15181        $caption = 'About ' . $obj->getChannel() . '/' .$obj->getPackage() . '-' .
15182            $obj->getVersion();
15183        $data = array(
15184            'caption' => $caption,
15185            'border' => true);
15186        switch ($obj->getPackageType()) {
15187            case 'php' :
15188                $release = 'PEAR-style PHP-based Package';
15189            break;
15190            case 'extsrc' :
15191                $release = 'PECL-style PHP extension (source code)';
15192            break;
15193            case 'zendextsrc' :
15194                $release = 'PECL-style Zend extension (source code)';
15195            break;
15196            case 'extbin' :
15197                $release = 'PECL-style PHP extension (binary)';
15198            break;
15199            case 'zendextbin' :
15200                $release = 'PECL-style Zend extension (binary)';
15201            break;
15202            case 'bundle' :
15203                $release = 'Package bundle (collection of packages)';
15204            break;
15205        }
15206        $extends = $obj->getExtends();
15207        $extends = $extends ?
15208            $obj->getPackage() . ' (extends ' . $extends . ')' : $obj->getPackage();
15209        if ($src = $obj->getSourcePackage()) {
15210            $extends .= ' (source package ' . $src['channel'] . '/' . $src['package'] . ')';
15211        }
15212
15213        $info = array(
15214            'Release Type' => $release,
15215            'Name' => $extends,
15216            'Channel' => $obj->getChannel(),
15217            'Summary' => preg_replace('/  +/', ' ', $obj->getSummary()),
15218            'Description' => preg_replace('/  +/', ' ', $obj->getDescription()),
15219            );
15220        $info['Maintainers'] = '';
15221        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
15222            $leads = $obj->{"get{$role}s"}();
15223            if (!$leads) {
15224                continue;
15225            }
15226
15227            if (isset($leads['active'])) {
15228                $leads = array($leads);
15229            }
15230
15231            foreach ($leads as $lead) {
15232                if (!empty($info['Maintainers'])) {
15233                    $info['Maintainers'] .= "\n";
15234                }
15235
15236                $active = $lead['active'] == 'no' ? ', inactive' : '';
15237                $info['Maintainers'] .= $lead['name'] . ' <';
15238                $info['Maintainers'] .= $lead['email'] . "> ($role$active)";
15239            }
15240        }
15241
15242        $info['Release Date'] = $obj->getDate();
15243        if ($time = $obj->getTime()) {
15244            $info['Release Date'] .= ' ' . $time;
15245        }
15246
15247        $info['Release Version'] = $obj->getVersion() . ' (' . $obj->getState() . ')';
15248        $info['API Version'] = $obj->getVersion('api') . ' (' . $obj->getState('api') . ')';
15249        $info['License'] = $obj->getLicense();
15250        $uri = $obj->getLicenseLocation();
15251        if ($uri) {
15252            if (isset($uri['uri'])) {
15253                $info['License'] .= ' (' . $uri['uri'] . ')';
15254            } else {
15255                $extra = $obj->getInstalledLocation($info['filesource']);
15256                if ($extra) {
15257                    $info['License'] .= ' (' . $uri['filesource'] . ')';
15258                }
15259            }
15260        }
15261
15262        $info['Release Notes'] = $obj->getNotes();
15263        if ($compat = $obj->getCompatible()) {
15264            if (!isset($compat[0])) {
15265                $compat = array($compat);
15266            }
15267
15268            $info['Compatible with'] = '';
15269            foreach ($compat as $package) {
15270                $info['Compatible with'] .= $package['channel'] . '/' . $package['name'] .
15271                    "\nVersions >= " . $package['min'] . ', <= ' . $package['max'];
15272                if (isset($package['exclude'])) {
15273                    if (is_array($package['exclude'])) {
15274                        $package['exclude'] = implode(', ', $package['exclude']);
15275                    }
15276
15277                    if (!isset($info['Not Compatible with'])) {
15278                        $info['Not Compatible with'] = '';
15279                    } else {
15280                        $info['Not Compatible with'] .= "\n";
15281                    }
15282                    $info['Not Compatible with'] .= $package['channel'] . '/' .
15283                        $package['name'] . "\nVersions " . $package['exclude'];
15284                }
15285            }
15286        }
15287
15288        $usesrole = $obj->getUsesrole();
15289        if ($usesrole) {
15290            if (!isset($usesrole[0])) {
15291                $usesrole = array($usesrole);
15292            }
15293
15294            foreach ($usesrole as $roledata) {
15295                if (isset($info['Uses Custom Roles'])) {
15296                    $info['Uses Custom Roles'] .= "\n";
15297                } else {
15298                    $info['Uses Custom Roles'] = '';
15299                }
15300
15301                if (isset($roledata['package'])) {
15302                    $rolepackage = $reg->parsedPackageNameToString($roledata, true);
15303                } else {
15304                    $rolepackage = $roledata['uri'];
15305                }
15306                $info['Uses Custom Roles'] .= $roledata['role'] . ' (' . $rolepackage . ')';
15307            }
15308        }
15309
15310        $usestask = $obj->getUsestask();
15311        if ($usestask) {
15312            if (!isset($usestask[0])) {
15313                $usestask = array($usestask);
15314            }
15315
15316            foreach ($usestask as $taskdata) {
15317                if (isset($info['Uses Custom Tasks'])) {
15318                    $info['Uses Custom Tasks'] .= "\n";
15319                } else {
15320                    $info['Uses Custom Tasks'] = '';
15321                }
15322
15323                if (isset($taskdata['package'])) {
15324                    $taskpackage = $reg->parsedPackageNameToString($taskdata, true);
15325                } else {
15326                    $taskpackage = $taskdata['uri'];
15327                }
15328                $info['Uses Custom Tasks'] .= $taskdata['task'] . ' (' . $taskpackage . ')';
15329            }
15330        }
15331
15332        $deps = $obj->getDependencies();
15333        $info['Required Dependencies'] = 'PHP version ' . $deps['required']['php']['min'];
15334        if (isset($deps['required']['php']['max'])) {
15335            $info['Required Dependencies'] .= '-' . $deps['required']['php']['max'] . "\n";
15336        } else {
15337            $info['Required Dependencies'] .= "\n";
15338        }
15339
15340        if (isset($deps['required']['php']['exclude'])) {
15341            if (!isset($info['Not Compatible with'])) {
15342                $info['Not Compatible with'] = '';
15343            } else {
15344                $info['Not Compatible with'] .= "\n";
15345            }
15346
15347            if (is_array($deps['required']['php']['exclude'])) {
15348                $deps['required']['php']['exclude'] =
15349                    implode(', ', $deps['required']['php']['exclude']);
15350            }
15351            $info['Not Compatible with'] .= "PHP versions\n  " .
15352                $deps['required']['php']['exclude'];
15353        }
15354
15355        $info['Required Dependencies'] .= 'PEAR installer version';
15356        if (isset($deps['required']['pearinstaller']['max'])) {
15357            $info['Required Dependencies'] .= 's ' .
15358                $deps['required']['pearinstaller']['min'] . '-' .
15359                $deps['required']['pearinstaller']['max'];
15360        } else {
15361            $info['Required Dependencies'] .= ' ' .
15362                $deps['required']['pearinstaller']['min'] . ' or newer';
15363        }
15364
15365        if (isset($deps['required']['pearinstaller']['exclude'])) {
15366            if (!isset($info['Not Compatible with'])) {
15367                $info['Not Compatible with'] = '';
15368            } else {
15369                $info['Not Compatible with'] .= "\n";
15370            }
15371
15372            if (is_array($deps['required']['pearinstaller']['exclude'])) {
15373                $deps['required']['pearinstaller']['exclude'] =
15374                    implode(', ', $deps['required']['pearinstaller']['exclude']);
15375            }
15376            $info['Not Compatible with'] .= "PEAR installer\n  Versions " .
15377                $deps['required']['pearinstaller']['exclude'];
15378        }
15379
15380        foreach (array('Package', 'Extension') as $type) {
15381            $index = strtolower($type);
15382            if (isset($deps['required'][$index])) {
15383                if (isset($deps['required'][$index]['name'])) {
15384                    $deps['required'][$index] = array($deps['required'][$index]);
15385                }
15386
15387                foreach ($deps['required'][$index] as $package) {
15388                    if (isset($package['conflicts'])) {
15389                        $infoindex = 'Not Compatible with';
15390                        if (!isset($info['Not Compatible with'])) {
15391                            $info['Not Compatible with'] = '';
15392                        } else {
15393                            $info['Not Compatible with'] .= "\n";
15394                        }
15395                    } else {
15396                        $infoindex = 'Required Dependencies';
15397                        $info[$infoindex] .= "\n";
15398                    }
15399
15400                    if ($index == 'extension') {
15401                        $name = $package['name'];
15402                    } else {
15403                        if (isset($package['channel'])) {
15404                            $name = $package['channel'] . '/' . $package['name'];
15405                        } else {
15406                            $name = '__uri/' . $package['name'] . ' (static URI)';
15407                        }
15408                    }
15409
15410                    $info[$infoindex] .= "$type $name";
15411                    if (isset($package['uri'])) {
15412                        $info[$infoindex] .= "\n  Download URI: $package[uri]";
15413                        continue;
15414                    }
15415
15416                    if (isset($package['max']) && isset($package['min'])) {
15417                        $info[$infoindex] .= " \n  Versions " .
15418                            $package['min'] . '-' . $package['max'];
15419                    } elseif (isset($package['min'])) {
15420                        $info[$infoindex] .= " \n  Version " .
15421                            $package['min'] . ' or newer';
15422                    } elseif (isset($package['max'])) {
15423                        $info[$infoindex] .= " \n  Version " .
15424                            $package['max'] . ' or older';
15425                    }
15426
15427                    if (isset($package['recommended'])) {
15428                        $info[$infoindex] .= "\n  Recommended version: $package[recommended]";
15429                    }
15430
15431                    if (isset($package['exclude'])) {
15432                        if (!isset($info['Not Compatible with'])) {
15433                            $info['Not Compatible with'] = '';
15434                        } else {
15435                            $info['Not Compatible with'] .= "\n";
15436                        }
15437
15438                        if (is_array($package['exclude'])) {
15439                            $package['exclude'] = implode(', ', $package['exclude']);
15440                        }
15441
15442                        $package['package'] = $package['name']; // for parsedPackageNameToString
15443                         if (isset($package['conflicts'])) {
15444                            $info['Not Compatible with'] .= '=> except ';
15445                        }
15446                       $info['Not Compatible with'] .= 'Package ' .
15447                            $reg->parsedPackageNameToString($package, true);
15448                        $info['Not Compatible with'] .= "\n  Versions " . $package['exclude'];
15449                    }
15450                }
15451            }
15452        }
15453
15454        if (isset($deps['required']['os'])) {
15455            if (isset($deps['required']['os']['name'])) {
15456                $dep['required']['os']['name'] = array($dep['required']['os']['name']);
15457            }
15458
15459            foreach ($dep['required']['os'] as $os) {
15460                if (isset($os['conflicts']) && $os['conflicts'] == 'yes') {
15461                    if (!isset($info['Not Compatible with'])) {
15462                        $info['Not Compatible with'] = '';
15463                    } else {
15464                        $info['Not Compatible with'] .= "\n";
15465                    }
15466                    $info['Not Compatible with'] .= "$os[name] Operating System";
15467                } else {
15468                    $info['Required Dependencies'] .= "\n";
15469                    $info['Required Dependencies'] .= "$os[name] Operating System";
15470                }
15471            }
15472        }
15473
15474        if (isset($deps['required']['arch'])) {
15475            if (isset($deps['required']['arch']['pattern'])) {
15476                $dep['required']['arch']['pattern'] = array($dep['required']['os']['pattern']);
15477            }
15478
15479            foreach ($dep['required']['arch'] as $os) {
15480                if (isset($os['conflicts']) && $os['conflicts'] == 'yes') {
15481                    if (!isset($info['Not Compatible with'])) {
15482                        $info['Not Compatible with'] = '';
15483                    } else {
15484                        $info['Not Compatible with'] .= "\n";
15485                    }
15486                    $info['Not Compatible with'] .= "OS/Arch matching pattern '/$os[pattern]/'";
15487                } else {
15488                    $info['Required Dependencies'] .= "\n";
15489                    $info['Required Dependencies'] .= "OS/Arch matching pattern '/$os[pattern]/'";
15490                }
15491            }
15492        }
15493
15494        if (isset($deps['optional'])) {
15495            foreach (array('Package', 'Extension') as $type) {
15496                $index = strtolower($type);
15497                if (isset($deps['optional'][$index])) {
15498                    if (isset($deps['optional'][$index]['name'])) {
15499                        $deps['optional'][$index] = array($deps['optional'][$index]);
15500                    }
15501
15502                    foreach ($deps['optional'][$index] as $package) {
15503                        if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
15504                            $infoindex = 'Not Compatible with';
15505                            if (!isset($info['Not Compatible with'])) {
15506                                $info['Not Compatible with'] = '';
15507                            } else {
15508                                $info['Not Compatible with'] .= "\n";
15509                            }
15510                        } else {
15511                            $infoindex = 'Optional Dependencies';
15512                            if (!isset($info['Optional Dependencies'])) {
15513                                $info['Optional Dependencies'] = '';
15514                            } else {
15515                                $info['Optional Dependencies'] .= "\n";
15516                            }
15517                        }
15518
15519                        if ($index == 'extension') {
15520                            $name = $package['name'];
15521                        } else {
15522                            if (isset($package['channel'])) {
15523                                $name = $package['channel'] . '/' . $package['name'];
15524                            } else {
15525                                $name = '__uri/' . $package['name'] . ' (static URI)';
15526                            }
15527                        }
15528
15529                        $info[$infoindex] .= "$type $name";
15530                        if (isset($package['uri'])) {
15531                            $info[$infoindex] .= "\n  Download URI: $package[uri]";
15532                            continue;
15533                        }
15534
15535                        if ($infoindex == 'Not Compatible with') {
15536                            // conflicts is only used to say that all versions conflict
15537                            continue;
15538                        }
15539
15540                        if (isset($package['max']) && isset($package['min'])) {
15541                            $info[$infoindex] .= " \n  Versions " .
15542                                $package['min'] . '-' . $package['max'];
15543                        } elseif (isset($package['min'])) {
15544                            $info[$infoindex] .= " \n  Version " .
15545                                $package['min'] . ' or newer';
15546                        } elseif (isset($package['max'])) {
15547                            $info[$infoindex] .= " \n  Version " .
15548                                $package['min'] . ' or older';
15549                        }
15550
15551                        if (isset($package['recommended'])) {
15552                            $info[$infoindex] .= "\n  Recommended version: $package[recommended]";
15553                        }
15554
15555                        if (isset($package['exclude'])) {
15556                            if (!isset($info['Not Compatible with'])) {
15557                                $info['Not Compatible with'] = '';
15558                            } else {
15559                                $info['Not Compatible with'] .= "\n";
15560                            }
15561
15562                            if (is_array($package['exclude'])) {
15563                                $package['exclude'] = implode(', ', $package['exclude']);
15564                            }
15565
15566                            $info['Not Compatible with'] .= "Package $package\n  Versions " .
15567                                $package['exclude'];
15568                        }
15569                    }
15570                }
15571            }
15572        }
15573
15574        if (isset($deps['group'])) {
15575            if (!isset($deps['group'][0])) {
15576                $deps['group'] = array($deps['group']);
15577            }
15578
15579            foreach ($deps['group'] as $group) {
15580                $info['Dependency Group ' . $group['attribs']['name']] = $group['attribs']['hint'];
15581                $groupindex = $group['attribs']['name'] . ' Contents';
15582                $info[$groupindex] = '';
15583                foreach (array('Package', 'Extension') as $type) {
15584                    $index = strtolower($type);
15585                    if (isset($group[$index])) {
15586                        if (isset($group[$index]['name'])) {
15587                            $group[$index] = array($group[$index]);
15588                        }
15589
15590                        foreach ($group[$index] as $package) {
15591                            if (!empty($info[$groupindex])) {
15592                                $info[$groupindex] .= "\n";
15593                            }
15594
15595                            if ($index == 'extension') {
15596                                $name = $package['name'];
15597                            } else {
15598                                if (isset($package['channel'])) {
15599                                    $name = $package['channel'] . '/' . $package['name'];
15600                                } else {
15601                                    $name = '__uri/' . $package['name'] . ' (static URI)';
15602                                }
15603                            }
15604
15605                            if (isset($package['uri'])) {
15606                                if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
15607                                    $info[$groupindex] .= "Not Compatible with $type $name";
15608                                } else {
15609                                    $info[$groupindex] .= "$type $name";
15610                                }
15611
15612                                $info[$groupindex] .= "\n  Download URI: $package[uri]";
15613                                continue;
15614                            }
15615
15616                            if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
15617                                $info[$groupindex] .= "Not Compatible with $type $name";
15618                                continue;
15619                            }
15620
15621                            $info[$groupindex] .= "$type $name";
15622                            if (isset($package['max']) && isset($package['min'])) {
15623                                $info[$groupindex] .= " \n  Versions " .
15624                                    $package['min'] . '-' . $package['max'];
15625                            } elseif (isset($package['min'])) {
15626                                $info[$groupindex] .= " \n  Version " .
15627                                    $package['min'] . ' or newer';
15628                            } elseif (isset($package['max'])) {
15629                                $info[$groupindex] .= " \n  Version " .
15630                                    $package['min'] . ' or older';
15631                            }
15632
15633                            if (isset($package['recommended'])) {
15634                                $info[$groupindex] .= "\n  Recommended version: $package[recommended]";
15635                            }
15636
15637                            if (isset($package['exclude'])) {
15638                                if (!isset($info['Not Compatible with'])) {
15639                                    $info['Not Compatible with'] = '';
15640                                } else {
15641                                    $info[$groupindex] .= "Not Compatible with\n";
15642                                }
15643
15644                                if (is_array($package['exclude'])) {
15645                                    $package['exclude'] = implode(', ', $package['exclude']);
15646                                }
15647                                $info[$groupindex] .= "  Package $package\n  Versions " .
15648                                    $package['exclude'];
15649                            }
15650                        }
15651                    }
15652                }
15653            }
15654        }
15655
15656        if ($obj->getPackageType() == 'bundle') {
15657            $info['Bundled Packages'] = '';
15658            foreach ($obj->getBundledPackages() as $package) {
15659                if (!empty($info['Bundled Packages'])) {
15660                    $info['Bundled Packages'] .= "\n";
15661                }
15662
15663                if (isset($package['uri'])) {
15664                    $info['Bundled Packages'] .= '__uri/' . $package['name'];
15665                    $info['Bundled Packages'] .= "\n  (URI: $package[uri]";
15666                } else {
15667                    $info['Bundled Packages'] .= $package['channel'] . '/' . $package['name'];
15668                }
15669            }
15670        }
15671
15672        $info['package.xml version'] = '2.0';
15673        if ($installed) {
15674            if ($obj->getLastModified()) {
15675                $info['Last Modified'] = date('Y-m-d H:i', $obj->getLastModified());
15676            }
15677
15678            $v = $obj->getLastInstalledVersion();
15679            $info['Previous Installed Version'] = $v ? $v : '- None -';
15680        }
15681
15682        foreach ($info as $key => $value) {
15683            $data['data'][] = array($key, $value);
15684        }
15685
15686        $data['raw'] = $obj->getArray(); // no validation needed
15687        $this->ui->outputData($data, 'package-info');
15688    }
15689}PEAR-1.9.4/PEAR/Command/Remote.xml0000644000076500000240000000635711605156614015331 0ustar  helgistaff<commands version="1.0">
15690 <remote-info>
15691  <summary>Information About Remote Packages</summary>
15692  <function>doRemoteInfo</function>
15693  <shortcut>ri</shortcut>
15694  <options />
15695  <doc>&lt;package&gt;
15696Get details on a package from the server.</doc>
15697 </remote-info>
15698 <list-upgrades>
15699  <summary>List Available Upgrades</summary>
15700  <function>doListUpgrades</function>
15701  <shortcut>lu</shortcut>
15702  <options>
15703   <channelinfo>
15704    <shortopt>i</shortopt>
15705    <doc>output fully channel-aware data, even on failure</doc>
15706   </channelinfo>
15707  </options>
15708  <doc>[preferred_state]
15709List releases on the server of packages you have installed where
15710a newer version is available with the same release state (stable etc.)
15711or the state passed as the second parameter.</doc>
15712 </list-upgrades>
15713 <remote-list>
15714  <summary>List Remote Packages</summary>
15715  <function>doRemoteList</function>
15716  <shortcut>rl</shortcut>
15717  <options>
15718   <channel>
15719    <shortopt>c</shortopt>
15720    <doc>specify a channel other than the default channel</doc>
15721    <arg>CHAN</arg>
15722   </channel>
15723  </options>
15724  <doc>
15725Lists the packages available on the configured server along with the
15726latest stable release of each package.</doc>
15727 </remote-list>
15728 <search>
15729  <summary>Search remote package database</summary>
15730  <function>doSearch</function>
15731  <shortcut>sp</shortcut>
15732  <options>
15733   <channel>
15734    <shortopt>c</shortopt>
15735    <doc>specify a channel other than the default channel</doc>
15736    <arg>CHAN</arg>
15737   </channel>
15738   <allchannels>
15739    <shortopt>a</shortopt>
15740    <doc>search packages from all known channels</doc>
15741   </allchannels>
15742   <channelinfo>
15743    <shortopt>i</shortopt>
15744    <doc>output fully channel-aware data, even on failure</doc>
15745   </channelinfo>
15746  </options>
15747  <doc>[packagename] [packageinfo]
15748Lists all packages which match the search parameters.  The first
15749parameter is a fragment of a packagename.  The default channel
15750will be used unless explicitly overridden.  The second parameter
15751will be used to match any portion of the summary/description</doc>
15752 </search>
15753 <list-all>
15754  <summary>List All Packages</summary>
15755  <function>doListAll</function>
15756  <shortcut>la</shortcut>
15757  <options>
15758   <channel>
15759    <shortopt>c</shortopt>
15760    <doc>specify a channel other than the default channel</doc>
15761    <arg>CHAN</arg>
15762   </channel>
15763   <channelinfo>
15764    <shortopt>i</shortopt>
15765    <doc>output fully channel-aware data, even on failure</doc>
15766   </channelinfo>
15767  </options>
15768  <doc>
15769Lists the packages available on the configured server along with the
15770latest stable release of each package.</doc>
15771 </list-all>
15772 <download>
15773  <summary>Download Package</summary>
15774  <function>doDownload</function>
15775  <shortcut>d</shortcut>
15776  <options>
15777   <nocompress>
15778    <shortopt>Z</shortopt>
15779    <doc>download an uncompressed (.tar) file</doc>
15780   </nocompress>
15781  </options>
15782  <doc>&lt;package&gt;...
15783Download package tarballs.  The files will be named as suggested by the
15784server, for example if you download the DB package and the latest stable
15785version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.</doc>
15786 </download>
15787 <clear-cache>
15788  <summary>Clear Web Services Cache</summary>
15789  <function>doClearCache</function>
15790  <shortcut>cc</shortcut>
15791  <options />
15792  <doc>
15793Clear the XML-RPC/REST cache.  See also the cache_ttl configuration
15794parameter.
15795</doc>
15796 </clear-cache>
15797</commands>PEAR-1.9.4/PEAR/Command/Remote.php0000644000076500000240000007256611605156614015325 0ustar  helgistaff<?php
15798/**
15799 * PEAR_Command_Remote (remote-info, list-upgrades, remote-list, search, list-all, download,
15800 * clear-cache commands)
15801 *
15802 * PHP versions 4 and 5
15803 *
15804 * @category   pear
15805 * @package    PEAR
15806 * @author     Stig Bakken <ssb@php.net>
15807 * @author     Greg Beaver <cellog@php.net>
15808 * @copyright  1997-2009 The Authors
15809 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
15810 * @version    CVS: $Id: Remote.php 313023 2011-07-06 19:17:11Z dufuz $
15811 * @link       http://pear.php.net/package/PEAR
15812 * @since      File available since Release 0.1
15813 */
15814
15815/**
15816 * base class
15817 */
15818require_once 'PEAR/Command/Common.php';
15819require_once 'PEAR/REST.php';
15820
15821/**
15822 * PEAR commands for remote server querying
15823 *
15824 * @category   pear
15825 * @package    PEAR
15826 * @author     Stig Bakken <ssb@php.net>
15827 * @author     Greg Beaver <cellog@php.net>
15828 * @copyright  1997-2009 The Authors
15829 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
15830 * @version    Release: 1.9.4
15831 * @link       http://pear.php.net/package/PEAR
15832 * @since      Class available since Release 0.1
15833 */
15834class PEAR_Command_Remote extends PEAR_Command_Common
15835{
15836    var $commands = array(
15837        'remote-info' => array(
15838            'summary' => 'Information About Remote Packages',
15839            'function' => 'doRemoteInfo',
15840            'shortcut' => 'ri',
15841            'options' => array(),
15842            'doc' => '<package>
15843Get details on a package from the server.',
15844            ),
15845        'list-upgrades' => array(
15846            'summary' => 'List Available Upgrades',
15847            'function' => 'doListUpgrades',
15848            'shortcut' => 'lu',
15849            'options' => array(
15850                'channelinfo' => array(
15851                    'shortopt' => 'i',
15852                    'doc' => 'output fully channel-aware data, even on failure',
15853                    ),
15854            ),
15855            'doc' => '[preferred_state]
15856List releases on the server of packages you have installed where
15857a newer version is available with the same release state (stable etc.)
15858or the state passed as the second parameter.'
15859            ),
15860        'remote-list' => array(
15861            'summary' => 'List Remote Packages',
15862            'function' => 'doRemoteList',
15863            'shortcut' => 'rl',
15864            'options' => array(
15865                'channel' =>
15866                    array(
15867                    'shortopt' => 'c',
15868                    'doc' => 'specify a channel other than the default channel',
15869                    'arg' => 'CHAN',
15870                    )
15871                ),
15872            'doc' => '
15873Lists the packages available on the configured server along with the
15874latest stable release of each package.',
15875            ),
15876        'search' => array(
15877            'summary' => 'Search remote package database',
15878            'function' => 'doSearch',
15879            'shortcut' => 'sp',
15880            'options' => array(
15881                'channel' =>
15882                    array(
15883                    'shortopt' => 'c',
15884                    'doc' => 'specify a channel other than the default channel',
15885                    'arg' => 'CHAN',
15886                    ),
15887                'allchannels' => array(
15888                    'shortopt' => 'a',
15889                    'doc' => 'search packages from all known channels',
15890                    ),
15891                'channelinfo' => array(
15892                    'shortopt' => 'i',
15893                    'doc' => 'output fully channel-aware data, even on failure',
15894                    ),
15895                ),
15896            'doc' => '[packagename] [packageinfo]
15897Lists all packages which match the search parameters.  The first
15898parameter is a fragment of a packagename.  The default channel
15899will be used unless explicitly overridden.  The second parameter
15900will be used to match any portion of the summary/description',
15901            ),
15902        'list-all' => array(
15903            'summary' => 'List All Packages',
15904            'function' => 'doListAll',
15905            'shortcut' => 'la',
15906            'options' => array(
15907                'channel' =>
15908                    array(
15909                    'shortopt' => 'c',
15910                    'doc' => 'specify a channel other than the default channel',
15911                    'arg' => 'CHAN',
15912                    ),
15913                'channelinfo' => array(
15914                    'shortopt' => 'i',
15915                    'doc' => 'output fully channel-aware data, even on failure',
15916                    ),
15917                ),
15918            'doc' => '
15919Lists the packages available on the configured server along with the
15920latest stable release of each package.',
15921            ),
15922        'download' => array(
15923            'summary' => 'Download Package',
15924            'function' => 'doDownload',
15925            'shortcut' => 'd',
15926            'options' => array(
15927                'nocompress' => array(
15928                    'shortopt' => 'Z',
15929                    'doc' => 'download an uncompressed (.tar) file',
15930                    ),
15931                ),
15932            'doc' => '<package>...
15933Download package tarballs.  The files will be named as suggested by the
15934server, for example if you download the DB package and the latest stable
15935version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.',
15936            ),
15937        'clear-cache' => array(
15938            'summary' => 'Clear Web Services Cache',
15939            'function' => 'doClearCache',
15940            'shortcut' => 'cc',
15941            'options' => array(),
15942            'doc' => '
15943Clear the REST cache. See also the cache_ttl configuration
15944parameter.
15945',
15946            ),
15947        );
15948
15949    /**
15950     * PEAR_Command_Remote constructor.
15951     *
15952     * @access public
15953     */
15954    function PEAR_Command_Remote(&$ui, &$config)
15955    {
15956        parent::PEAR_Command_Common($ui, $config);
15957    }
15958
15959    function _checkChannelForStatus($channel, $chan)
15960    {
15961        if (PEAR::isError($chan)) {
15962            $this->raiseError($chan);
15963        }
15964        if (!is_a($chan, 'PEAR_ChannelFile')) {
15965            return $this->raiseError('Internal corruption error: invalid channel "' .
15966                $channel . '"');
15967        }
15968        $rest = new PEAR_REST($this->config);
15969        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
15970        $mirror = $this->config->get('preferred_mirror', null,
15971                                     $channel);
15972        $a = $rest->downloadHttp('http://' . $channel .
15973            '/channel.xml', $chan->lastModified());
15974        PEAR::staticPopErrorHandling();
15975        if (!PEAR::isError($a) && $a) {
15976            $this->ui->outputData('WARNING: channel "' . $channel . '" has ' .
15977                'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $channel .
15978                '" to update');
15979        }
15980    }
15981
15982    function doRemoteInfo($command, $options, $params)
15983    {
15984        if (sizeof($params) != 1) {
15985            return $this->raiseError("$command expects one param: the remote package name");
15986        }
15987        $savechannel = $channel = $this->config->get('default_channel');
15988        $reg = &$this->config->getRegistry();
15989        $package = $params[0];
15990        $parsed = $reg->parsePackageName($package, $channel);
15991        if (PEAR::isError($parsed)) {
15992            return $this->raiseError('Invalid package name "' . $package . '"');
15993        }
15994
15995        $channel = $parsed['channel'];
15996        $this->config->set('default_channel', $channel);
15997        $chan = $reg->getChannel($channel);
15998        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
15999            return $e;
16000        }
16001
16002        $mirror = $this->config->get('preferred_mirror');
16003        if ($chan->supportsREST($mirror) && $base = $chan->getBaseURL('REST1.0', $mirror)) {
16004            $rest = &$this->config->getREST('1.0', array());
16005            $info = $rest->packageInfo($base, $parsed['package'], $channel);
16006        }
16007
16008        if (!isset($info)) {
16009            return $this->raiseError('No supported protocol was found');
16010        }
16011
16012        if (PEAR::isError($info)) {
16013            $this->config->set('default_channel', $savechannel);
16014            return $this->raiseError($info);
16015        }
16016
16017        if (!isset($info['name'])) {
16018            return $this->raiseError('No remote package "' . $package . '" was found');
16019        }
16020
16021        $installed = $reg->packageInfo($info['name'], null, $channel);
16022        $info['installed'] = $installed['version'] ? $installed['version'] : '- no -';
16023        if (is_array($info['installed'])) {
16024            $info['installed'] = $info['installed']['release'];
16025        }
16026
16027        $this->ui->outputData($info, $command);
16028        $this->config->set('default_channel', $savechannel);
16029
16030        return true;
16031    }
16032
16033    function doRemoteList($command, $options, $params)
16034    {
16035        $savechannel = $channel = $this->config->get('default_channel');
16036        $reg = &$this->config->getRegistry();
16037        if (isset($options['channel'])) {
16038            $channel = $options['channel'];
16039            if (!$reg->channelExists($channel)) {
16040                return $this->raiseError('Channel "' . $channel . '" does not exist');
16041            }
16042
16043            $this->config->set('default_channel', $channel);
16044        }
16045
16046        $chan = $reg->getChannel($channel);
16047        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
16048            return $e;
16049        }
16050
16051        $list_options = false;
16052        if ($this->config->get('preferred_state') == 'stable') {
16053            $list_options = true;
16054        }
16055
16056        $available = array();
16057        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
16058              $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))
16059        ) {
16060            // use faster list-all if available
16061            $rest = &$this->config->getREST('1.1', array());
16062            $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName());
16063        } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
16064              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
16065            $rest = &$this->config->getREST('1.0', array());
16066            $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName());
16067        }
16068
16069        if (PEAR::isError($available)) {
16070            $this->config->set('default_channel', $savechannel);
16071            return $this->raiseError($available);
16072        }
16073
16074        $i = $j = 0;
16075        $data = array(
16076            'caption' => 'Channel ' . $channel . ' Available packages:',
16077            'border' => true,
16078            'headline' => array('Package', 'Version'),
16079            'channel' => $channel
16080            );
16081
16082        if (count($available) == 0) {
16083            $data = '(no packages available yet)';
16084        } else {
16085            foreach ($available as $name => $info) {
16086                $version = (isset($info['stable']) && $info['stable']) ? $info['stable'] : '-n/a-';
16087                $data['data'][] = array($name, $version);
16088            }
16089        }
16090        $this->ui->outputData($data, $command);
16091        $this->config->set('default_channel', $savechannel);
16092        return true;
16093    }
16094
16095    function doListAll($command, $options, $params)
16096    {
16097        $savechannel = $channel = $this->config->get('default_channel');
16098        $reg = &$this->config->getRegistry();
16099        if (isset($options['channel'])) {
16100            $channel = $options['channel'];
16101            if (!$reg->channelExists($channel)) {
16102                return $this->raiseError("Channel \"$channel\" does not exist");
16103            }
16104
16105            $this->config->set('default_channel', $channel);
16106        }
16107
16108        $list_options = false;
16109        if ($this->config->get('preferred_state') == 'stable') {
16110            $list_options = true;
16111        }
16112
16113        $chan = $reg->getChannel($channel);
16114        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
16115            return $e;
16116        }
16117
16118        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
16119              $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))) {
16120            // use faster list-all if available
16121            $rest = &$this->config->getREST('1.1', array());
16122            $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName());
16123        } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
16124              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
16125            $rest = &$this->config->getREST('1.0', array());
16126            $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName());
16127        }
16128
16129        if (PEAR::isError($available)) {
16130            $this->config->set('default_channel', $savechannel);
16131            return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "' . $available->getMessage() . '")');
16132        }
16133
16134        $data = array(
16135            'caption' => 'All packages [Channel ' . $channel . ']:',
16136            'border' => true,
16137            'headline' => array('Package', 'Latest', 'Local'),
16138            'channel' => $channel,
16139            );
16140
16141        if (isset($options['channelinfo'])) {
16142            // add full channelinfo
16143            $data['caption'] = 'Channel ' . $channel . ' All packages:';
16144            $data['headline'] = array('Channel', 'Package', 'Latest', 'Local',
16145                'Description', 'Dependencies');
16146        }
16147        $local_pkgs = $reg->listPackages($channel);
16148
16149        foreach ($available as $name => $info) {
16150            $installed = $reg->packageInfo($name, null, $channel);
16151            if (is_array($installed['version'])) {
16152                $installed['version'] = $installed['version']['release'];
16153            }
16154            $desc = $info['summary'];
16155            if (isset($params[$name])) {
16156                $desc .= "\n\n".$info['description'];
16157            }
16158            if (isset($options['mode']))
16159            {
16160                if ($options['mode'] == 'installed' && !isset($installed['version'])) {
16161                    continue;
16162                }
16163                if ($options['mode'] == 'notinstalled' && isset($installed['version'])) {
16164                    continue;
16165                }
16166                if ($options['mode'] == 'upgrades'
16167                      && (!isset($installed['version']) || version_compare($installed['version'],
16168                      $info['stable'], '>='))) {
16169                    continue;
16170                }
16171            }
16172            $pos = array_search(strtolower($name), $local_pkgs);
16173            if ($pos !== false) {
16174                unset($local_pkgs[$pos]);
16175            }
16176
16177            if (isset($info['stable']) && !$info['stable']) {
16178                $info['stable'] = null;
16179            }
16180
16181            if (isset($options['channelinfo'])) {
16182                // add full channelinfo
16183                if ($info['stable'] === $info['unstable']) {
16184                    $state = $info['state'];
16185                } else {
16186                    $state = 'stable';
16187                }
16188                $latest = $info['stable'].' ('.$state.')';
16189                $local = '';
16190                if (isset($installed['version'])) {
16191                    $inst_state = $reg->packageInfo($name, 'release_state', $channel);
16192                    $local = $installed['version'].' ('.$inst_state.')';
16193                }
16194
16195                $packageinfo = array(
16196                    $channel,
16197                    $name,
16198                    $latest,
16199                    $local,
16200                    isset($desc) ? $desc : null,
16201                    isset($info['deps']) ? $info['deps'] : null,
16202                );
16203            } else {
16204                $packageinfo = array(
16205                    $reg->channelAlias($channel) . '/' . $name,
16206                    isset($info['stable']) ? $info['stable'] : null,
16207                    isset($installed['version']) ? $installed['version'] : null,
16208                    isset($desc) ? $desc : null,
16209                    isset($info['deps']) ? $info['deps'] : null,
16210                );
16211            }
16212            $data['data'][$info['category']][] = $packageinfo;
16213        }
16214
16215        if (isset($options['mode']) && in_array($options['mode'], array('notinstalled', 'upgrades'))) {
16216            $this->config->set('default_channel', $savechannel);
16217            $this->ui->outputData($data, $command);
16218            return true;
16219        }
16220
16221        foreach ($local_pkgs as $name) {
16222            $info = &$reg->getPackage($name, $channel);
16223            $data['data']['Local'][] = array(
16224                $reg->channelAlias($channel) . '/' . $info->getPackage(),
16225                '',
16226                $info->getVersion(),
16227                $info->getSummary(),
16228                $info->getDeps()
16229                );
16230        }
16231
16232        $this->config->set('default_channel', $savechannel);
16233        $this->ui->outputData($data, $command);
16234        return true;
16235    }
16236
16237    function doSearch($command, $options, $params)
16238    {
16239        if ((!isset($params[0]) || empty($params[0]))
16240            && (!isset($params[1]) || empty($params[1])))
16241        {
16242            return $this->raiseError('no valid search string supplied');
16243        }
16244
16245        $channelinfo = isset($options['channelinfo']);
16246        $reg = &$this->config->getRegistry();
16247        if (isset($options['allchannels'])) {
16248            // search all channels
16249            unset($options['allchannels']);
16250            $channels = $reg->getChannels();
16251            $errors = array();
16252            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
16253            foreach ($channels as $channel) {
16254                if ($channel->getName() != '__uri') {
16255                    $options['channel'] = $channel->getName();
16256                    $ret = $this->doSearch($command, $options, $params);
16257                    if (PEAR::isError($ret)) {
16258                        $errors[] = $ret;
16259                    }
16260                }
16261            }
16262
16263            PEAR::staticPopErrorHandling();
16264            if (count($errors) !== 0) {
16265                // for now, only give first error
16266                return PEAR::raiseError($errors[0]);
16267            }
16268
16269            return true;
16270        }
16271
16272        $savechannel = $channel = $this->config->get('default_channel');
16273        $package = strtolower($params[0]);
16274        $summary = isset($params[1]) ? $params[1] : false;
16275        if (isset($options['channel'])) {
16276            $reg = &$this->config->getRegistry();
16277            $channel = $options['channel'];
16278            if (!$reg->channelExists($channel)) {
16279                return $this->raiseError('Channel "' . $channel . '" does not exist');
16280            }
16281
16282            $this->config->set('default_channel', $channel);
16283        }
16284
16285        $chan = $reg->getChannel($channel);
16286        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
16287            return $e;
16288        }
16289
16290        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
16291              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
16292            $rest = &$this->config->getREST('1.0', array());
16293            $available = $rest->listAll($base, false, false, $package, $summary, $chan->getName());
16294        }
16295
16296        if (PEAR::isError($available)) {
16297            $this->config->set('default_channel', $savechannel);
16298            return $this->raiseError($available);
16299        }
16300
16301        if (!$available && !$channelinfo) {
16302            // clean exit when not found, no error !
16303            $data = 'no packages found that match pattern "' . $package . '", for channel '.$channel.'.';
16304            $this->ui->outputData($data);
16305            $this->config->set('default_channel', $channel);
16306            return true;
16307        }
16308
16309        if ($channelinfo) {
16310            $data = array(
16311                'caption' => 'Matched packages, channel ' . $channel . ':',
16312                'border' => true,
16313                'headline' => array('Channel', 'Package', 'Stable/(Latest)', 'Local'),
16314                'channel' => $channel
16315                );
16316        } else {
16317            $data = array(
16318                'caption' => 'Matched packages, channel ' . $channel . ':',
16319                'border' => true,
16320                'headline' => array('Package', 'Stable/(Latest)', 'Local'),
16321                'channel' => $channel
16322                );
16323        }
16324
16325        if (!$available && $channelinfo) {
16326            unset($data['headline']);
16327            $data['data'] = 'No packages found that match pattern "' . $package . '".';
16328            $available = array();
16329        }
16330
16331        foreach ($available as $name => $info) {
16332            $installed = $reg->packageInfo($name, null, $channel);
16333            $desc = $info['summary'];
16334            if (isset($params[$name]))
16335                $desc .= "\n\n".$info['description'];
16336
16337            if (!isset($info['stable']) || !$info['stable']) {
16338                $version_remote = 'none';
16339            } else {
16340                if ($info['unstable']) {
16341                    $version_remote = $info['unstable'];
16342                } else {
16343                    $version_remote = $info['stable'];
16344                }
16345                $version_remote .= ' ('.$info['state'].')';
16346            }
16347            $version = is_array($installed['version']) ? $installed['version']['release'] :
16348                $installed['version'];
16349            if ($channelinfo) {
16350                $packageinfo = array(
16351                    $channel,
16352                    $name,
16353                    $version_remote,
16354                    $version,
16355                    $desc,
16356                );
16357            } else {
16358                $packageinfo = array(
16359                    $name,
16360                    $version_remote,
16361                    $version,
16362                    $desc,
16363                );
16364            }
16365            $data['data'][$info['category']][] = $packageinfo;
16366        }
16367
16368        $this->ui->outputData($data, $command);
16369        $this->config->set('default_channel', $channel);
16370        return true;
16371    }
16372
16373    function &getDownloader($options)
16374    {
16375        if (!class_exists('PEAR_Downloader')) {
16376            require_once 'PEAR/Downloader.php';
16377        }
16378        $a = &new PEAR_Downloader($this->ui, $options, $this->config);
16379        return $a;
16380    }
16381
16382    function doDownload($command, $options, $params)
16383    {
16384        // make certain that dependencies are ignored
16385        $options['downloadonly'] = 1;
16386
16387        // eliminate error messages for preferred_state-related errors
16388        /* TODO: Should be an option, but until now download does respect
16389           prefered state */
16390        /* $options['ignorepreferred_state'] = 1; */
16391        // eliminate error messages for preferred_state-related errors
16392
16393        $downloader = &$this->getDownloader($options);
16394        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
16395        $e = $downloader->setDownloadDir(getcwd());
16396        PEAR::staticPopErrorHandling();
16397        if (PEAR::isError($e)) {
16398            return $this->raiseError('Current directory is not writeable, cannot download');
16399        }
16400
16401        $errors = array();
16402        $downloaded = array();
16403        $err = $downloader->download($params);
16404        if (PEAR::isError($err)) {
16405            return $err;
16406        }
16407
16408        $errors = $downloader->getErrorMsgs();
16409        if (count($errors)) {
16410            foreach ($errors as $error) {
16411                if ($error !== null) {
16412                    $this->ui->outputData($error);
16413                }
16414            }
16415
16416            return $this->raiseError("$command failed");
16417        }
16418
16419        $downloaded = $downloader->getDownloadedPackages();
16420        foreach ($downloaded as $pkg) {
16421            $this->ui->outputData("File $pkg[file] downloaded", $command);
16422        }
16423
16424        return true;
16425    }
16426
16427    function downloadCallback($msg, $params = null)
16428    {
16429        if ($msg == 'done') {
16430            $this->bytes_downloaded = $params;
16431        }
16432    }
16433
16434    function doListUpgrades($command, $options, $params)
16435    {
16436        require_once 'PEAR/Common.php';
16437        if (isset($params[0]) && !is_array(PEAR_Common::betterStates($params[0]))) {
16438            return $this->raiseError($params[0] . ' is not a valid state (stable/beta/alpha/devel/etc.) try "pear help list-upgrades"');
16439        }
16440
16441        $savechannel = $channel = $this->config->get('default_channel');
16442        $reg = &$this->config->getRegistry();
16443        foreach ($reg->listChannels() as $channel) {
16444            $inst = array_flip($reg->listPackages($channel));
16445            if (!count($inst)) {
16446                continue;
16447            }
16448
16449            if ($channel == '__uri') {
16450                continue;
16451            }
16452
16453            $this->config->set('default_channel', $channel);
16454            $state = empty($params[0]) ? $this->config->get('preferred_state') : $params[0];
16455
16456            $caption = $channel . ' Available Upgrades';
16457            $chan = $reg->getChannel($channel);
16458            if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
16459                return $e;
16460            }
16461
16462            $latest = array();
16463            $base2  = false;
16464            $preferred_mirror = $this->config->get('preferred_mirror');
16465            if ($chan->supportsREST($preferred_mirror) &&
16466                (
16467                   //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) ||
16468                   ($base  = $chan->getBaseURL('REST1.0', $preferred_mirror))
16469                )
16470
16471            ) {
16472                if ($base2) {
16473                    $rest = &$this->config->getREST('1.4', array());
16474                    $base = $base2;
16475                } else {
16476                    $rest = &$this->config->getREST('1.0', array());
16477                }
16478
16479                if (empty($state) || $state == 'any') {
16480                    $state = false;
16481                } else {
16482                    $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')';
16483                }
16484
16485                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
16486                $latest = $rest->listLatestUpgrades($base, $state, $inst, $channel, $reg);
16487                PEAR::staticPopErrorHandling();
16488            }
16489
16490            if (PEAR::isError($latest)) {
16491                $this->ui->outputData($latest->getMessage());
16492                continue;
16493            }
16494
16495            $caption .= ':';
16496            if (PEAR::isError($latest)) {
16497                $this->config->set('default_channel', $savechannel);
16498                return $latest;
16499            }
16500
16501            $data = array(
16502                'caption' => $caption,
16503                'border' => 1,
16504                'headline' => array('Channel', 'Package', 'Local', 'Remote', 'Size'),
16505                'channel' => $channel
16506                );
16507
16508            foreach ((array)$latest as $pkg => $info) {
16509                $package = strtolower($pkg);
16510                if (!isset($inst[$package])) {
16511                    // skip packages we don't have installed
16512                    continue;
16513                }
16514
16515                extract($info);
16516                $inst_version = $reg->packageInfo($package, 'version', $channel);
16517                $inst_state   = $reg->packageInfo($package, 'release_state', $channel);
16518                if (version_compare("$version", "$inst_version", "le")) {
16519                    // installed version is up-to-date
16520                    continue;
16521                }
16522
16523                if ($filesize >= 20480) {
16524                    $filesize += 1024 - ($filesize % 1024);
16525                    $fs = sprintf("%dkB", $filesize / 1024);
16526                } elseif ($filesize > 0) {
16527                    $filesize += 103 - ($filesize % 103);
16528                    $fs = sprintf("%.1fkB", $filesize / 1024.0);
16529                } else {
16530                    $fs = "  -"; // XXX center instead
16531                }
16532
16533                $data['data'][] = array($channel, $pkg, "$inst_version ($inst_state)", "$version ($state)", $fs);
16534            }
16535
16536            if (isset($options['channelinfo'])) {
16537                if (empty($data['data'])) {
16538                    unset($data['headline']);
16539                    if (count($inst) == 0) {
16540                        $data['data'] = '(no packages installed)';
16541                    } else {
16542                        $data['data'] = '(no upgrades available)';
16543                    }
16544                }
16545                $this->ui->outputData($data, $command);
16546            } else {
16547                if (empty($data['data'])) {
16548                    $this->ui->outputData('Channel ' . $channel . ': No upgrades available');
16549                } else {
16550                    $this->ui->outputData($data, $command);
16551                }
16552            }
16553        }
16554
16555        $this->config->set('default_channel', $savechannel);
16556        return true;
16557    }
16558
16559    function doClearCache($command, $options, $params)
16560    {
16561        $cache_dir = $this->config->get('cache_dir');
16562        $verbose   = $this->config->get('verbose');
16563        $output = '';
16564        if (!file_exists($cache_dir) || !is_dir($cache_dir)) {
16565            return $this->raiseError("$cache_dir does not exist or is not a directory");
16566        }
16567
16568        if (!($dp = @opendir($cache_dir))) {
16569            return $this->raiseError("opendir($cache_dir) failed: $php_errormsg");
16570        }
16571
16572        if ($verbose >= 1) {
16573            $output .= "reading directory $cache_dir\n";
16574        }
16575
16576        $num = 0;
16577        while ($ent = readdir($dp)) {
16578            if (preg_match('/rest.cache(file|id)\\z/', $ent)) {
16579                $path = $cache_dir . DIRECTORY_SEPARATOR . $ent;
16580                if (file_exists($path)) {
16581                    $ok = @unlink($path);
16582                } else {
16583                    $ok = false;
16584                    $php_errormsg = '';
16585                }
16586
16587                if ($ok) {
16588                    if ($verbose >= 2) {
16589                        $output .= "deleted $path\n";
16590                    }
16591                    $num++;
16592                } elseif ($verbose >= 1) {
16593                    $output .= "failed to delete $path $php_errormsg\n";
16594                }
16595            }
16596        }
16597
16598        closedir($dp);
16599        if ($verbose >= 1) {
16600            $output .= "$num cache entries cleared\n";
16601        }
16602
16603        $this->ui->outputData(rtrim($output), $command);
16604        return $num;
16605    }
16606}PEAR-1.9.4/PEAR/Command/Test.xml0000644000076500000240000000315111605156614015002 0ustar  helgistaff<commands version="1.0">
16607 <run-tests>
16608  <summary>Run Regression Tests</summary>
16609  <function>doRunTests</function>
16610  <shortcut>rt</shortcut>
16611  <options>
16612   <recur>
16613    <shortopt>r</shortopt>
16614    <doc>Run tests in child directories, recursively.  4 dirs deep maximum</doc>
16615   </recur>
16616   <ini>
16617    <shortopt>i</shortopt>
16618    <doc>actual string of settings to pass to php in format &quot; -d setting=blah&quot;</doc>
16619    <arg>SETTINGS</arg>
16620   </ini>
16621   <realtimelog>
16622    <shortopt>l</shortopt>
16623    <doc>Log test runs/results as they are run</doc>
16624   </realtimelog>
16625   <quiet>
16626    <shortopt>q</shortopt>
16627    <doc>Only display detail for failed tests</doc>
16628   </quiet>
16629   <simple>
16630    <shortopt>s</shortopt>
16631    <doc>Display simple output for all tests</doc>
16632   </simple>
16633   <package>
16634    <shortopt>p</shortopt>
16635    <doc>Treat parameters as installed packages from which to run tests</doc>
16636   </package>
16637   <phpunit>
16638    <shortopt>u</shortopt>
16639    <doc>Search parameters for AllTests.php, and use that to run phpunit-based tests
16640If none is found, all .phpt tests will be tried instead.</doc>
16641   </phpunit>
16642   <tapoutput>
16643    <shortopt>t</shortopt>
16644    <doc>Output run-tests.log in TAP-compliant format</doc>
16645   </tapoutput>
16646   <cgi>
16647    <shortopt>c</shortopt>
16648    <doc>CGI php executable (needed for tests with POST/GET section)</doc>
16649    <arg>PHPCGI</arg>
16650   </cgi>
16651   <coverage>
16652    <shortopt>x</shortopt>
16653    <doc>Generate a code coverage report (requires Xdebug 2.0.0+)</doc>
16654   </coverage>
16655  </options>
16656  <doc>[testfile|dir ...]
16657Run regression tests with PHP&#039;s regression testing script (run-tests.php).</doc>
16658 </run-tests>
16659</commands>PEAR-1.9.4/PEAR/Command/Test.php0000644000076500000240000002726611605156614015006 0ustar  helgistaff<?php
16660/**
16661 * PEAR_Command_Test (run-tests)
16662 *
16663 * PHP versions 4 and 5
16664 *
16665 * @category   pear
16666 * @package    PEAR
16667 * @author     Stig Bakken <ssb@php.net>
16668 * @author     Martin Jansen <mj@php.net>
16669 * @author     Greg Beaver <cellog@php.net>
16670 * @copyright  1997-2009 The Authors
16671 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
16672 * @version    CVS: $Id: Test.php 313023 2011-07-06 19:17:11Z dufuz $
16673 * @link       http://pear.php.net/package/PEAR
16674 * @since      File available since Release 0.1
16675 */
16676
16677/**
16678 * base class
16679 */
16680require_once 'PEAR/Command/Common.php';
16681
16682/**
16683 * PEAR commands for login/logout
16684 *
16685 * @category   pear
16686 * @package    PEAR
16687 * @author     Stig Bakken <ssb@php.net>
16688 * @author     Martin Jansen <mj@php.net>
16689 * @author     Greg Beaver <cellog@php.net>
16690 * @copyright  1997-2009 The Authors
16691 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
16692 * @version    Release: 1.9.4
16693 * @link       http://pear.php.net/package/PEAR
16694 * @since      Class available since Release 0.1
16695 */
16696
16697class PEAR_Command_Test extends PEAR_Command_Common
16698{
16699    var $commands = array(
16700        'run-tests' => array(
16701            'summary' => 'Run Regression Tests',
16702            'function' => 'doRunTests',
16703            'shortcut' => 'rt',
16704            'options' => array(
16705                'recur' => array(
16706                    'shortopt' => 'r',
16707                    'doc' => 'Run tests in child directories, recursively.  4 dirs deep maximum',
16708                ),
16709                'ini' => array(
16710                    'shortopt' => 'i',
16711                    'doc' => 'actual string of settings to pass to php in format " -d setting=blah"',
16712                    'arg' => 'SETTINGS'
16713                ),
16714                'realtimelog' => array(
16715                    'shortopt' => 'l',
16716                    'doc' => 'Log test runs/results as they are run',
16717                ),
16718                'quiet' => array(
16719                    'shortopt' => 'q',
16720                    'doc' => 'Only display detail for failed tests',
16721                ),
16722                'simple' => array(
16723                    'shortopt' => 's',
16724                    'doc' => 'Display simple output for all tests',
16725                ),
16726                'package' => array(
16727                    'shortopt' => 'p',
16728                    'doc' => 'Treat parameters as installed packages from which to run tests',
16729                ),
16730                'phpunit' => array(
16731                    'shortopt' => 'u',
16732                    'doc' => 'Search parameters for AllTests.php, and use that to run phpunit-based tests
16733If none is found, all .phpt tests will be tried instead.',
16734                ),
16735                'tapoutput' => array(
16736                    'shortopt' => 't',
16737                    'doc' => 'Output run-tests.log in TAP-compliant format',
16738                ),
16739                'cgi' => array(
16740                    'shortopt' => 'c',
16741                    'doc' => 'CGI php executable (needed for tests with POST/GET section)',
16742                    'arg' => 'PHPCGI',
16743                ),
16744                'coverage' => array(
16745                    'shortopt' => 'x',
16746                    'doc'      => 'Generate a code coverage report (requires Xdebug 2.0.0+)',
16747                ),
16748            ),
16749            'doc' => '[testfile|dir ...]
16750Run regression tests with PHP\'s regression testing script (run-tests.php).',
16751            ),
16752        );
16753
16754    var $output;
16755
16756    /**
16757     * PEAR_Command_Test constructor.
16758     *
16759     * @access public
16760     */
16761    function PEAR_Command_Test(&$ui, &$config)
16762    {
16763        parent::PEAR_Command_Common($ui, $config);
16764    }
16765
16766    function doRunTests($command, $options, $params)
16767    {
16768        if (isset($options['phpunit']) && isset($options['tapoutput'])) {
16769            return $this->raiseError('ERROR: cannot use both --phpunit and --tapoutput at the same time');
16770        }
16771
16772        require_once 'PEAR/Common.php';
16773        require_once 'System.php';
16774        $log = new PEAR_Common;
16775        $log->ui = &$this->ui; // slightly hacky, but it will work
16776        $tests = array();
16777        $depth = isset($options['recur']) ? 14 : 1;
16778
16779        if (!count($params)) {
16780            $params[] = '.';
16781        }
16782
16783        if (isset($options['package'])) {
16784            $oldparams = $params;
16785            $params = array();
16786            $reg = &$this->config->getRegistry();
16787            foreach ($oldparams as $param) {
16788                $pname = $reg->parsePackageName($param, $this->config->get('default_channel'));
16789                if (PEAR::isError($pname)) {
16790                    return $this->raiseError($pname);
16791                }
16792
16793                $package = &$reg->getPackage($pname['package'], $pname['channel']);
16794                if (!$package) {
16795                    return PEAR::raiseError('Unknown package "' .
16796                        $reg->parsedPackageNameToString($pname) . '"');
16797                }
16798
16799                $filelist = $package->getFilelist();
16800                foreach ($filelist as $name => $atts) {
16801                    if (isset($atts['role']) && $atts['role'] != 'test') {
16802                        continue;
16803                    }
16804
16805                    if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $name)) {
16806                        $params[] = $atts['installed_as'];
16807                        continue;
16808                    } elseif (!preg_match('/\.phpt\\z/', $name)) {
16809                        continue;
16810                    }
16811                    $params[] = $atts['installed_as'];
16812                }
16813            }
16814        }
16815
16816        foreach ($params as $p) {
16817            if (is_dir($p)) {
16818                if (isset($options['phpunit'])) {
16819                    $dir = System::find(array($p, '-type', 'f',
16820                                                '-maxdepth', $depth,
16821                                                '-name', 'AllTests.php'));
16822                    if (count($dir)) {
16823                        foreach ($dir as $p) {
16824                            $p = realpath($p);
16825                            if (!count($tests) ||
16826                                  (count($tests) && strlen($p) < strlen($tests[0]))) {
16827                                // this is in a higher-level directory, use this one instead.
16828                                $tests = array($p);
16829                            }
16830                        }
16831                    }
16832                    continue;
16833                }
16834
16835                $args  = array($p, '-type', 'f', '-name', '*.phpt');
16836            } else {
16837                if (isset($options['phpunit'])) {
16838                    if (preg_match('/AllTests\.php\\z/i', $p)) {
16839                        $p = realpath($p);
16840                        if (!count($tests) ||
16841                              (count($tests) && strlen($p) < strlen($tests[0]))) {
16842                            // this is in a higher-level directory, use this one instead.
16843                            $tests = array($p);
16844                        }
16845                    }
16846                    continue;
16847                }
16848
16849                if (file_exists($p) && preg_match('/\.phpt$/', $p)) {
16850                    $tests[] = $p;
16851                    continue;
16852                }
16853
16854                if (!preg_match('/\.phpt\\z/', $p)) {
16855                    $p .= '.phpt';
16856                }
16857
16858                $args  = array(dirname($p), '-type', 'f', '-name', $p);
16859            }
16860
16861            if (!isset($options['recur'])) {
16862                $args[] = '-maxdepth';
16863                $args[] = 1;
16864            }
16865
16866            $dir   = System::find($args);
16867            $tests = array_merge($tests, $dir);
16868        }
16869
16870        $ini_settings = '';
16871        if (isset($options['ini'])) {
16872            $ini_settings .= $options['ini'];
16873        }
16874
16875        if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) {
16876            $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}";
16877        }
16878
16879        if ($ini_settings) {
16880            $this->ui->outputData('Using INI settings: "' . $ini_settings . '"');
16881        }
16882
16883        $skipped = $passed = $failed = array();
16884        $tests_count = count($tests);
16885        $this->ui->outputData('Running ' . $tests_count . ' tests', $command);
16886        $start = time();
16887        if (isset($options['realtimelog']) && file_exists('run-tests.log')) {
16888            unlink('run-tests.log');
16889        }
16890
16891        if (isset($options['tapoutput'])) {
16892            $tap = '1..' . $tests_count . "\n";
16893        }
16894
16895        require_once 'PEAR/RunTest.php';
16896        $run = new PEAR_RunTest($log, $options);
16897        $run->tests_count = $tests_count;
16898
16899        if (isset($options['coverage']) && extension_loaded('xdebug')){
16900            $run->xdebug_loaded = true;
16901        } else {
16902            $run->xdebug_loaded = false;
16903        }
16904
16905        $j = $i = 1;
16906        foreach ($tests as $t) {
16907            if (isset($options['realtimelog'])) {
16908                $fp = @fopen('run-tests.log', 'a');
16909                if ($fp) {
16910                    fwrite($fp, "Running test [$i / $tests_count] $t...");
16911                    fclose($fp);
16912                }
16913            }
16914            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
16915            if (isset($options['phpunit'])) {
16916                $result = $run->runPHPUnit($t, $ini_settings);
16917            } else {
16918                $result = $run->run($t, $ini_settings, $j);
16919            }
16920            PEAR::staticPopErrorHandling();
16921            if (PEAR::isError($result)) {
16922                $this->ui->log($result->getMessage());
16923                continue;
16924            }
16925
16926            if (isset($options['tapoutput'])) {
16927                $tap .= $result[0] . ' ' . $i . $result[1] . "\n";
16928                continue;
16929            }
16930
16931            if (isset($options['realtimelog'])) {
16932                $fp = @fopen('run-tests.log', 'a');
16933                if ($fp) {
16934                    fwrite($fp, "$result\n");
16935                    fclose($fp);
16936                }
16937            }
16938
16939            if ($result == 'FAILED') {
16940                $failed[] = $t;
16941            }
16942            if ($result == 'PASSED') {
16943                $passed[] = $t;
16944            }
16945            if ($result == 'SKIPPED') {
16946                $skipped[] = $t;
16947            }
16948
16949            $j++;
16950        }
16951
16952        $total = date('i:s', time() - $start);
16953        if (isset($options['tapoutput'])) {
16954            $fp = @fopen('run-tests.log', 'w');
16955            if ($fp) {
16956                fwrite($fp, $tap, strlen($tap));
16957                fclose($fp);
16958                $this->ui->outputData('wrote TAP-format log to "' .realpath('run-tests.log') .
16959                    '"', $command);
16960            }
16961        } else {
16962            if (count($failed)) {
16963                $output = "TOTAL TIME: $total\n";
16964                $output .= count($passed) . " PASSED TESTS\n";
16965                $output .= count($skipped) . " SKIPPED TESTS\n";
16966                $output .= count($failed) . " FAILED TESTS:\n";
16967                foreach ($failed as $failure) {
16968                    $output .= $failure . "\n";
16969                }
16970
16971                $mode = isset($options['realtimelog']) ? 'a' : 'w';
16972                $fp   = @fopen('run-tests.log', $mode);
16973
16974                if ($fp) {
16975                    fwrite($fp, $output, strlen($output));
16976                    fclose($fp);
16977                    $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command);
16978                }
16979            } elseif (file_exists('run-tests.log') && !is_dir('run-tests.log')) {
16980                @unlink('run-tests.log');
16981            }
16982        }
16983        $this->ui->outputData('TOTAL TIME: ' . $total);
16984        $this->ui->outputData(count($passed) . ' PASSED TESTS', $command);
16985        $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command);
16986        if (count($failed)) {
16987            $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command);
16988            foreach ($failed as $failure) {
16989                $this->ui->outputData($failure, $command);
16990            }
16991        }
16992
16993        return true;
16994    }
16995}PEAR-1.9.4/PEAR/Downloader/Package.php0000644000076500000240000022464611605156614016143 0ustar  helgistaff<?php
16996/**
16997 * PEAR_Downloader_Package
16998 *
16999 * PHP versions 4 and 5
17000 *
17001 * @category   pear
17002 * @package    PEAR
17003 * @author     Greg Beaver <cellog@php.net>
17004 * @copyright  1997-2009 The Authors
17005 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
17006 * @version    CVS: $Id: Package.php 313023 2011-07-06 19:17:11Z dufuz $
17007 * @link       http://pear.php.net/package/PEAR
17008 * @since      File available since Release 1.4.0a1
17009 */
17010
17011/**
17012 * Error code when parameter initialization fails because no releases
17013 * exist within preferred_state, but releases do exist
17014 */
17015define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003);
17016/**
17017 * Error code when parameter initialization fails because no releases
17018 * exist that will work with the existing PHP version
17019 */
17020define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004);
17021
17022/**
17023 * Coordinates download parameters and manages their dependencies
17024 * prior to downloading them.
17025 *
17026 * Input can come from three sources:
17027 *
17028 * - local files (archives or package.xml)
17029 * - remote files (downloadable urls)
17030 * - abstract package names
17031 *
17032 * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires
17033 * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the
17034 * format returned of dependencies is slightly different from that used in package.xml.
17035 *
17036 * This class hides the differences between these elements, and makes automatic
17037 * dependency resolution a piece of cake.  It also manages conflicts when
17038 * two classes depend on incompatible dependencies, or differing versions of the same
17039 * package dependency.  In addition, download will not be attempted if the php version is
17040 * not supported, PEAR installer version is not supported, or non-PECL extensions are not
17041 * installed.
17042 * @category   pear
17043 * @package    PEAR
17044 * @author     Greg Beaver <cellog@php.net>
17045 * @copyright  1997-2009 The Authors
17046 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
17047 * @version    Release: 1.9.4
17048 * @link       http://pear.php.net/package/PEAR
17049 * @since      Class available since Release 1.4.0a1
17050 */
17051class PEAR_Downloader_Package
17052{
17053    /**
17054     * @var PEAR_Downloader
17055     */
17056    var $_downloader;
17057    /**
17058     * @var PEAR_Config
17059     */
17060    var $_config;
17061    /**
17062     * @var PEAR_Registry
17063     */
17064    var $_registry;
17065    /**
17066     * Used to implement packagingroot properly
17067     * @var PEAR_Registry
17068     */
17069    var $_installRegistry;
17070    /**
17071     * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2
17072     */
17073    var $_packagefile;
17074    /**
17075     * @var array
17076     */
17077    var $_parsedname;
17078    /**
17079     * @var array
17080     */
17081    var $_downloadURL;
17082    /**
17083     * @var array
17084     */
17085    var $_downloadDeps = array();
17086    /**
17087     * @var boolean
17088     */
17089    var $_valid = false;
17090    /**
17091     * @var boolean
17092     */
17093    var $_analyzed = false;
17094    /**
17095     * if this or a parent package was invoked with Package-state, this is set to the
17096     * state variable.
17097     *
17098     * This allows temporary reassignment of preferred_state for a parent package and all of
17099     * its dependencies.
17100     * @var string|false
17101     */
17102    var $_explicitState = false;
17103    /**
17104     * If this package is invoked with Package#group, this variable will be true
17105     */
17106    var $_explicitGroup = false;
17107    /**
17108     * Package type local|url
17109     * @var string
17110     */
17111    var $_type;
17112    /**
17113     * Contents of package.xml, if downloaded from a remote channel
17114     * @var string|false
17115     * @access private
17116     */
17117    var $_rawpackagefile;
17118    /**
17119     * @var boolean
17120     * @access private
17121     */
17122    var $_validated = false;
17123
17124    /**
17125     * @param PEAR_Downloader
17126     */
17127    function PEAR_Downloader_Package(&$downloader)
17128    {
17129        $this->_downloader = &$downloader;
17130        $this->_config = &$this->_downloader->config;
17131        $this->_registry = &$this->_config->getRegistry();
17132        $options = $downloader->getOptions();
17133        if (isset($options['packagingroot'])) {
17134            $this->_config->setInstallRoot($options['packagingroot']);
17135            $this->_installRegistry = &$this->_config->getRegistry();
17136            $this->_config->setInstallRoot(false);
17137        } else {
17138            $this->_installRegistry = &$this->_registry;
17139        }
17140        $this->_valid = $this->_analyzed = false;
17141    }
17142
17143    /**
17144     * Parse the input and determine whether this is a local file, a remote uri, or an
17145     * abstract package name.
17146     *
17147     * This is the heart of the PEAR_Downloader_Package(), and is used in
17148     * {@link PEAR_Downloader::download()}
17149     * @param string
17150     * @return bool|PEAR_Error
17151     */
17152    function initialize($param)
17153    {
17154        $origErr = $this->_fromFile($param);
17155        if ($this->_valid) {
17156            return true;
17157        }
17158
17159        $options = $this->_downloader->getOptions();
17160        if (isset($options['offline'])) {
17161            if (PEAR::isError($origErr) && !isset($options['soft'])) {
17162                foreach ($origErr->getUserInfo() as $userInfo) {
17163                    if (isset($userInfo['message'])) {
17164                        $this->_downloader->log(0, $userInfo['message']);
17165                    }
17166                }
17167
17168                $this->_downloader->log(0, $origErr->getMessage());
17169            }
17170
17171            return PEAR::raiseError('Cannot download non-local package "' . $param . '"');
17172        }
17173
17174        $err = $this->_fromUrl($param);
17175        if (PEAR::isError($err) || !$this->_valid) {
17176            if ($this->_type == 'url') {
17177                if (PEAR::isError($err) && !isset($options['soft'])) {
17178                    $this->_downloader->log(0, $err->getMessage());
17179                }
17180
17181                return PEAR::raiseError("Invalid or missing remote package file");
17182            }
17183
17184            $err = $this->_fromString($param);
17185            if (PEAR::isError($err) || !$this->_valid) {
17186                if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) {
17187                    return false; // instruct the downloader to silently skip
17188                }
17189
17190                if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) {
17191                    if (is_array($origErr->getUserInfo())) {
17192                        foreach ($origErr->getUserInfo() as $err) {
17193                            if (is_array($err)) {
17194                                $err = $err['message'];
17195                            }
17196
17197                            if (!isset($options['soft'])) {
17198                                $this->_downloader->log(0, $err);
17199                            }
17200                        }
17201                    }
17202
17203                    if (!isset($options['soft'])) {
17204                        $this->_downloader->log(0, $origErr->getMessage());
17205                    }
17206
17207                    if (is_array($param)) {
17208                        $param = $this->_registry->parsedPackageNameToString($param, true);
17209                    }
17210
17211                    if (!isset($options['soft'])) {
17212                        $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
17213                    }
17214
17215                    // Passing no message back - already logged above
17216                    return PEAR::raiseError();
17217                }
17218
17219                if (PEAR::isError($err) && !isset($options['soft'])) {
17220                    $this->_downloader->log(0, $err->getMessage());
17221                }
17222
17223                if (is_array($param)) {
17224                    $param = $this->_registry->parsedPackageNameToString($param, true);
17225                }
17226
17227                if (!isset($options['soft'])) {
17228                    $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
17229                }
17230
17231                // Passing no message back - already logged above
17232                return PEAR::raiseError();
17233            }
17234        }
17235
17236        return true;
17237    }
17238
17239    /**
17240     * Retrieve any non-local packages
17241     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error
17242     */
17243    function &download()
17244    {
17245        if (isset($this->_packagefile)) {
17246            return $this->_packagefile;
17247        }
17248
17249        if (isset($this->_downloadURL['url'])) {
17250            $this->_isvalid = false;
17251            $info = $this->getParsedPackage();
17252            foreach ($info as $i => $p) {
17253                $info[$i] = strtolower($p);
17254            }
17255
17256            $err = $this->_fromUrl($this->_downloadURL['url'],
17257                $this->_registry->parsedPackageNameToString($this->_parsedname, true));
17258            $newinfo = $this->getParsedPackage();
17259            foreach ($newinfo as $i => $p) {
17260                $newinfo[$i] = strtolower($p);
17261            }
17262
17263            if ($info != $newinfo) {
17264                do {
17265                    if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') {
17266                        $info['channel'] = 'pear.php.net';
17267                        if ($info == $newinfo) {
17268                            // skip the channel check if a pecl package says it's a PEAR package
17269                            break;
17270                        }
17271                    }
17272                    if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') {
17273                        $info['channel'] = 'pecl.php.net';
17274                        if ($info == $newinfo) {
17275                            // skip the channel check if a pecl package says it's a PEAR package
17276                            break;
17277                        }
17278                    }
17279
17280                    return PEAR::raiseError('CRITICAL ERROR: We are ' .
17281                        $this->_registry->parsedPackageNameToString($info) . ', but the file ' .
17282                        'downloaded claims to be ' .
17283                        $this->_registry->parsedPackageNameToString($this->getParsedPackage()));
17284                } while (false);
17285            }
17286
17287            if (PEAR::isError($err) || !$this->_valid) {
17288                return $err;
17289            }
17290        }
17291
17292        $this->_type = 'local';
17293        return $this->_packagefile;
17294    }
17295
17296    function &getPackageFile()
17297    {
17298        return $this->_packagefile;
17299    }
17300
17301    function &getDownloader()
17302    {
17303        return $this->_downloader;
17304    }
17305
17306    function getType()
17307    {
17308        return $this->_type;
17309    }
17310
17311    /**
17312     * Like {@link initialize()}, but operates on a dependency
17313     */
17314    function fromDepURL($dep)
17315    {
17316        $this->_downloadURL = $dep;
17317        if (isset($dep['uri'])) {
17318            $options = $this->_downloader->getOptions();
17319            if (!extension_loaded("zlib") || isset($options['nocompress'])) {
17320                $ext = '.tar';
17321            } else {
17322                $ext = '.tgz';
17323            }
17324
17325            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
17326            $err = $this->_fromUrl($dep['uri'] . $ext);
17327            PEAR::popErrorHandling();
17328            if (PEAR::isError($err)) {
17329                if (!isset($options['soft'])) {
17330                    $this->_downloader->log(0, $err->getMessage());
17331                }
17332
17333                return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' .
17334                    'cannot download');
17335            }
17336        } else {
17337            $this->_parsedname =
17338                array(
17339                    'package' => $dep['info']->getPackage(),
17340                    'channel' => $dep['info']->getChannel(),
17341                    'version' => $dep['version']
17342                );
17343            if (!isset($dep['nodefault'])) {
17344                $this->_parsedname['group'] = 'default'; // download the default dependency group
17345                $this->_explicitGroup = false;
17346            }
17347
17348            $this->_rawpackagefile = $dep['raw'];
17349        }
17350    }
17351
17352    function detectDependencies($params)
17353    {
17354        $options = $this->_downloader->getOptions();
17355        if (isset($options['downloadonly'])) {
17356            return;
17357        }
17358
17359        if (isset($options['offline'])) {
17360            $this->_downloader->log(3, 'Skipping dependency download check, --offline specified');
17361            return;
17362        }
17363
17364        $pname = $this->getParsedPackage();
17365        if (!$pname) {
17366            return;
17367        }
17368
17369        $deps = $this->getDeps();
17370        if (!$deps) {
17371            return;
17372        }
17373
17374        if (isset($deps['required'])) { // package.xml 2.0
17375            return $this->_detect2($deps, $pname, $options, $params);
17376        }
17377
17378        return $this->_detect1($deps, $pname, $options, $params);
17379    }
17380
17381    function setValidated()
17382    {
17383        $this->_validated = true;
17384    }
17385
17386    function alreadyValidated()
17387    {
17388        return $this->_validated;
17389    }
17390
17391    /**
17392     * Remove packages to be downloaded that are already installed
17393     * @param array of PEAR_Downloader_Package objects
17394     * @static
17395     */
17396    function removeInstalled(&$params)
17397    {
17398        if (!isset($params[0])) {
17399            return;
17400        }
17401
17402        $options = $params[0]->_downloader->getOptions();
17403        if (!isset($options['downloadonly'])) {
17404            foreach ($params as $i => $param) {
17405                $package = $param->getPackage();
17406                $channel = $param->getChannel();
17407                // remove self if already installed with this version
17408                // this does not need any pecl magic - we only remove exact matches
17409                if ($param->_installRegistry->packageExists($package, $channel)) {
17410                    $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel);
17411                    if (version_compare($packageVersion, $param->getVersion(), '==')) {
17412                        if (!isset($options['force'])) {
17413                            $info = $param->getParsedPackage();
17414                            unset($info['version']);
17415                            unset($info['state']);
17416                            if (!isset($options['soft'])) {
17417                                $param->_downloader->log(1, 'Skipping package "' .
17418                                    $param->getShortName() .
17419                                    '", already installed as version ' . $packageVersion);
17420                            }
17421                            $params[$i] = false;
17422                        }
17423                    } elseif (!isset($options['force']) && !isset($options['upgrade']) &&
17424                          !isset($options['soft'])) {
17425                        $info = $param->getParsedPackage();
17426                        $param->_downloader->log(1, 'Skipping package "' .
17427                            $param->getShortName() .
17428                            '", already installed as version ' . $packageVersion);
17429                        $params[$i] = false;
17430                    }
17431                }
17432            }
17433        }
17434
17435        PEAR_Downloader_Package::removeDuplicates($params);
17436    }
17437
17438    function _detect2($deps, $pname, $options, $params)
17439    {
17440        $this->_downloadDeps = array();
17441        $groupnotfound = false;
17442        foreach (array('package', 'subpackage') as $packagetype) {
17443            // get required dependency group
17444            if (isset($deps['required'][$packagetype])) {
17445                if (isset($deps['required'][$packagetype][0])) {
17446                    foreach ($deps['required'][$packagetype] as $dep) {
17447                        if (isset($dep['conflicts'])) {
17448                            // skip any package that this package conflicts with
17449                            continue;
17450                        }
17451                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
17452                        if (is_array($ret)) {
17453                            $this->_downloadDeps[] = $ret;
17454                        } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
17455                            $this->_downloader->log(0, $ret->getMessage());
17456                        }
17457                    }
17458                } else {
17459                    $dep = $deps['required'][$packagetype];
17460                    if (!isset($dep['conflicts'])) {
17461                        // skip any package that this package conflicts with
17462                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
17463                        if (is_array($ret)) {
17464                            $this->_downloadDeps[] = $ret;
17465                        } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
17466                            $this->_downloader->log(0, $ret->getMessage());
17467                        }
17468                    }
17469                }
17470            }
17471
17472            // get optional dependency group, if any
17473            if (isset($deps['optional'][$packagetype])) {
17474                $skipnames = array();
17475                if (!isset($deps['optional'][$packagetype][0])) {
17476                    $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]);
17477                }
17478
17479                foreach ($deps['optional'][$packagetype] as $dep) {
17480                    $skip = false;
17481                    if (!isset($options['alldeps'])) {
17482                        $dep['package'] = $dep['name'];
17483                        if (!isset($options['soft'])) {
17484                            $this->_downloader->log(3, 'Notice: package "' .
17485                              $this->_registry->parsedPackageNameToString($this->getParsedPackage(),
17486                                    true) . '" optional dependency "' .
17487                                $this->_registry->parsedPackageNameToString(array('package' =>
17488                                    $dep['name'], 'channel' => 'pear.php.net'), true) .
17489                                '" will not be automatically downloaded');
17490                        }
17491                        $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true);
17492                        $skip = true;
17493                        unset($dep['package']);
17494                    }
17495
17496                    $ret = $this->_detect2Dep($dep, $pname, 'optional', $params);
17497                    if (PEAR::isError($ret) && !isset($options['soft'])) {
17498                        $this->_downloader->log(0, $ret->getMessage());
17499                    }
17500
17501                    if (!$ret) {
17502                        $dep['package'] = $dep['name'];
17503                        $skip = count($skipnames) ?
17504                            $skipnames[count($skipnames) - 1] : '';
17505                        if ($skip ==
17506                              $this->_registry->parsedPackageNameToString($dep, true)) {
17507                            array_pop($skipnames);
17508                        }
17509                    }
17510
17511                    if (!$skip && is_array($ret)) {
17512                        $this->_downloadDeps[] = $ret;
17513                    }
17514                }
17515
17516                if (count($skipnames)) {
17517                    if (!isset($options['soft'])) {
17518                        $this->_downloader->log(1, 'Did not download optional dependencies: ' .
17519                            implode(', ', $skipnames) .
17520                            ', use --alldeps to download automatically');
17521                    }
17522                }
17523            }
17524
17525            // get requested dependency group, if any
17526            $groupname = $this->getGroup();
17527            $explicit  = $this->_explicitGroup;
17528            if (!$groupname) {
17529                if (!$this->canDefault()) {
17530                    continue;
17531                }
17532
17533                $groupname = 'default'; // try the default dependency group
17534            }
17535
17536            if ($groupnotfound) {
17537                continue;
17538            }
17539
17540            if (isset($deps['group'])) {
17541                if (isset($deps['group']['attribs'])) {
17542                    if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) {
17543                        $group = $deps['group'];
17544                    } elseif ($explicit) {
17545                        if (!isset($options['soft'])) {
17546                            $this->_downloader->log(0, 'Warning: package "' .
17547                                $this->_registry->parsedPackageNameToString($pname, true) .
17548                                '" has no dependency ' . 'group named "' . $groupname . '"');
17549                        }
17550
17551                        $groupnotfound = true;
17552                        continue;
17553                    }
17554                } else {
17555                    $found = false;
17556                    foreach ($deps['group'] as $group) {
17557                        if (strtolower($group['attribs']['name']) == strtolower($groupname)) {
17558                            $found = true;
17559                            break;
17560                        }
17561                    }
17562
17563                    if (!$found) {
17564                        if ($explicit) {
17565                            if (!isset($options['soft'])) {
17566                                $this->_downloader->log(0, 'Warning: package "' .
17567                                    $this->_registry->parsedPackageNameToString($pname, true) .
17568                                    '" has no dependency ' . 'group named "' . $groupname . '"');
17569                            }
17570                        }
17571
17572                        $groupnotfound = true;
17573                        continue;
17574                    }
17575                }
17576            }
17577
17578            if (isset($group) && isset($group[$packagetype])) {
17579                if (isset($group[$packagetype][0])) {
17580                    foreach ($group[$packagetype] as $dep) {
17581                        $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' .
17582                            $group['attribs']['name'] . '"', $params);
17583                        if (is_array($ret)) {
17584                            $this->_downloadDeps[] = $ret;
17585                        } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
17586                            $this->_downloader->log(0, $ret->getMessage());
17587                        }
17588                    }
17589                } else {
17590                    $ret = $this->_detect2Dep($group[$packagetype], $pname,
17591                        'dependency group "' .
17592                        $group['attribs']['name'] . '"', $params);
17593                    if (is_array($ret)) {
17594                        $this->_downloadDeps[] = $ret;
17595                    } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
17596                        $this->_downloader->log(0, $ret->getMessage());
17597                    }
17598                }
17599            }
17600        }
17601    }
17602
17603    function _detect2Dep($dep, $pname, $group, $params)
17604    {
17605        if (isset($dep['conflicts'])) {
17606            return true;
17607        }
17608
17609        $options = $this->_downloader->getOptions();
17610        if (isset($dep['uri'])) {
17611            return array('uri' => $dep['uri'], 'dep' => $dep);;
17612        }
17613
17614        $testdep = $dep;
17615        $testdep['package'] = $dep['name'];
17616        if (PEAR_Downloader_Package::willDownload($testdep, $params)) {
17617            $dep['package'] = $dep['name'];
17618            if (!isset($options['soft'])) {
17619                $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group .
17620                    ' dependency "' .
17621                    $this->_registry->parsedPackageNameToString($dep, true) .
17622                    '", will be installed');
17623            }
17624            return false;
17625        }
17626
17627        $options = $this->_downloader->getOptions();
17628        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
17629        if ($this->_explicitState) {
17630            $pname['state'] = $this->_explicitState;
17631        }
17632
17633        $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
17634        if (PEAR::isError($url)) {
17635            PEAR::popErrorHandling();
17636            return $url;
17637        }
17638
17639        $dep['package'] = $dep['name'];
17640        $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' &&
17641            !isset($options['alldeps']), true);
17642        PEAR::popErrorHandling();
17643        if (PEAR::isError($ret)) {
17644            if (!isset($options['soft'])) {
17645                $this->_downloader->log(0, $ret->getMessage());
17646            }
17647
17648            return false;
17649        }
17650
17651        // check to see if a dep is already installed and is the same or newer
17652        if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) {
17653            $oper = 'has';
17654        } else {
17655            $oper = 'gt';
17656        }
17657
17658        // do not try to move this before getDepPackageDownloadURL
17659        // we can't determine whether upgrade is necessary until we know what
17660        // version would be downloaded
17661        if (!isset($options['force']) && $this->isInstalled($ret, $oper)) {
17662            $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']);
17663            $dep['package'] = $dep['name'];
17664            if (!isset($options['soft'])) {
17665                $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
17666                    ' dependency "' .
17667                $this->_registry->parsedPackageNameToString($dep, true) .
17668                    '" version ' . $url['version'] . ', already installed as version ' .
17669                    $version);
17670            }
17671
17672            return false;
17673        }
17674
17675        if (isset($dep['nodefault'])) {
17676            $ret['nodefault'] = true;
17677        }
17678
17679        return $ret;
17680    }
17681
17682    function _detect1($deps, $pname, $options, $params)
17683    {
17684        $this->_downloadDeps = array();
17685        $skipnames = array();
17686        foreach ($deps as $dep) {
17687            $nodownload = false;
17688            if (isset ($dep['type']) && $dep['type'] === 'pkg') {
17689                $dep['channel'] = 'pear.php.net';
17690                $dep['package'] = $dep['name'];
17691                switch ($dep['rel']) {
17692                    case 'not' :
17693                        continue 2;
17694                    case 'ge' :
17695                    case 'eq' :
17696                    case 'gt' :
17697                    case 'has' :
17698                        $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
17699                            'required' :
17700                            'optional';
17701                        if (PEAR_Downloader_Package::willDownload($dep, $params)) {
17702                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
17703                                . ' dependency "' .
17704                                $this->_registry->parsedPackageNameToString($dep, true) .
17705                                '", will be installed');
17706                            continue 2;
17707                        }
17708                        $fakedp = new PEAR_PackageFile_v1;
17709                        $fakedp->setPackage($dep['name']);
17710                        // skip internet check if we are not upgrading (bug #5810)
17711                        if (!isset($options['upgrade']) && $this->isInstalled(
17712                              $fakedp, $dep['rel'])) {
17713                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
17714                                . ' dependency "' .
17715                                $this->_registry->parsedPackageNameToString($dep, true) .
17716                                '", is already installed');
17717                            continue 2;
17718                        }
17719                }
17720
17721                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
17722                if ($this->_explicitState) {
17723                    $pname['state'] = $this->_explicitState;
17724                }
17725
17726                $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
17727                $chan = 'pear.php.net';
17728                if (PEAR::isError($url)) {
17729                    // check to see if this is a pecl package that has jumped
17730                    // from pear.php.net to pecl.php.net channel
17731                    if (!class_exists('PEAR_Dependency2')) {
17732                        require_once 'PEAR/Dependency2.php';
17733                    }
17734
17735                    $newdep = PEAR_Dependency2::normalizeDep($dep);
17736                    $newdep = $newdep[0];
17737                    $newdep['channel'] = 'pecl.php.net';
17738                    $chan = 'pecl.php.net';
17739                    $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname);
17740                    $obj = &$this->_installRegistry->getPackage($dep['name']);
17741                    if (PEAR::isError($url)) {
17742                        PEAR::popErrorHandling();
17743                        if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) {
17744                            $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
17745                                'required' :
17746                                'optional';
17747                            $dep['package'] = $dep['name'];
17748                            if (!isset($options['soft'])) {
17749                                $this->_downloader->log(3, $this->getShortName() .
17750                                    ': Skipping ' . $group . ' dependency "' .
17751                                    $this->_registry->parsedPackageNameToString($dep, true) .
17752                                    '", already installed as version ' . $obj->getVersion());
17753                            }
17754                            $skip = count($skipnames) ?
17755                                $skipnames[count($skipnames) - 1] : '';
17756                            if ($skip ==
17757                                  $this->_registry->parsedPackageNameToString($dep, true)) {
17758                                array_pop($skipnames);
17759                            }
17760                            continue;
17761                        } else {
17762                            if (isset($dep['optional']) && $dep['optional'] == 'yes') {
17763                                $this->_downloader->log(2, $this->getShortName() .
17764                                    ': Skipping optional dependency "' .
17765                                    $this->_registry->parsedPackageNameToString($dep, true) .
17766                                    '", no releases exist');
17767                                continue;
17768                            } else {
17769                                return $url;
17770                            }
17771                        }
17772                    }
17773                }
17774
17775                PEAR::popErrorHandling();
17776                if (!isset($options['alldeps'])) {
17777                    if (isset($dep['optional']) && $dep['optional'] == 'yes') {
17778                        if (!isset($options['soft'])) {
17779                            $this->_downloader->log(3, 'Notice: package "' .
17780                                $this->getShortName() .
17781                                '" optional dependency "' .
17782                                $this->_registry->parsedPackageNameToString(
17783                                    array('channel' => $chan, 'package' =>
17784                                    $dep['name']), true) .
17785                                '" will not be automatically downloaded');
17786                        }
17787                        $skipnames[] = $this->_registry->parsedPackageNameToString(
17788                                array('channel' => $chan, 'package' =>
17789                                $dep['name']), true);
17790                        $nodownload = true;
17791                    }
17792                }
17793
17794                if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) {
17795                    if (!isset($dep['optional']) || $dep['optional'] == 'no') {
17796                        if (!isset($options['soft'])) {
17797                            $this->_downloader->log(3, 'Notice: package "' .
17798                                $this->getShortName() .
17799                                '" required dependency "' .
17800                                $this->_registry->parsedPackageNameToString(
17801                                    array('channel' => $chan, 'package' =>
17802                                    $dep['name']), true) .
17803                                '" will not be automatically downloaded');
17804                        }
17805                        $skipnames[] = $this->_registry->parsedPackageNameToString(
17806                                array('channel' => $chan, 'package' =>
17807                                $dep['name']), true);
17808                        $nodownload = true;
17809                    }
17810                }
17811
17812                // check to see if a dep is already installed
17813                // do not try to move this before getDepPackageDownloadURL
17814                // we can't determine whether upgrade is necessary until we know what
17815                // version would be downloaded
17816                if (!isset($options['force']) && $this->isInstalled(
17817                        $url, $dep['rel'])) {
17818                    $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
17819                        'required' :
17820                        'optional';
17821                    $dep['package'] = $dep['name'];
17822                    if (isset($newdep)) {
17823                        $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']);
17824                    } else {
17825                        $version = $this->_installRegistry->packageInfo($dep['name'], 'version');
17826                    }
17827
17828                    $dep['version'] = $url['version'];
17829                    if (!isset($options['soft'])) {
17830                        $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
17831                            ' dependency "' .
17832                            $this->_registry->parsedPackageNameToString($dep, true) .
17833                            '", already installed as version ' . $version);
17834                    }
17835
17836                    $skip = count($skipnames) ?
17837                        $skipnames[count($skipnames) - 1] : '';
17838                    if ($skip ==
17839                          $this->_registry->parsedPackageNameToString($dep, true)) {
17840                        array_pop($skipnames);
17841                    }
17842
17843                    continue;
17844                }
17845
17846                if ($nodownload) {
17847                    continue;
17848                }
17849
17850                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
17851                if (isset($newdep)) {
17852                    $dep = $newdep;
17853                }
17854
17855                $dep['package'] = $dep['name'];
17856                $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params,
17857                    isset($dep['optional']) && $dep['optional'] == 'yes' &&
17858                    !isset($options['alldeps']), true);
17859                PEAR::popErrorHandling();
17860                if (PEAR::isError($ret)) {
17861                    if (!isset($options['soft'])) {
17862                        $this->_downloader->log(0, $ret->getMessage());
17863                    }
17864                    continue;
17865                }
17866
17867                $this->_downloadDeps[] = $ret;
17868            }
17869        }
17870
17871        if (count($skipnames)) {
17872            if (!isset($options['soft'])) {
17873                $this->_downloader->log(1, 'Did not download dependencies: ' .
17874                    implode(', ', $skipnames) .
17875                    ', use --alldeps or --onlyreqdeps to download automatically');
17876            }
17877        }
17878    }
17879
17880    function setDownloadURL($pkg)
17881    {
17882        $this->_downloadURL = $pkg;
17883    }
17884
17885    /**
17886     * Set the package.xml object for this downloaded package
17887     *
17888     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg
17889     */
17890    function setPackageFile(&$pkg)
17891    {
17892        $this->_packagefile = &$pkg;
17893    }
17894
17895    function getShortName()
17896    {
17897        return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(),
17898            'package' => $this->getPackage()), true);
17899    }
17900
17901    function getParsedPackage()
17902    {
17903        if (isset($this->_packagefile) || isset($this->_parsedname)) {
17904            return array('channel' => $this->getChannel(),
17905                'package' => $this->getPackage(),
17906                'version' => $this->getVersion());
17907        }
17908
17909        return false;
17910    }
17911
17912    function getDownloadURL()
17913    {
17914        return $this->_downloadURL;
17915    }
17916
17917    function canDefault()
17918    {
17919        if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) {
17920            return false;
17921        }
17922
17923        return true;
17924    }
17925
17926    function getPackage()
17927    {
17928        if (isset($this->_packagefile)) {
17929            return $this->_packagefile->getPackage();
17930        } elseif (isset($this->_downloadURL['info'])) {
17931            return $this->_downloadURL['info']->getPackage();
17932        }
17933
17934        return false;
17935    }
17936
17937    /**
17938     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
17939     */
17940    function isSubpackage(&$pf)
17941    {
17942        if (isset($this->_packagefile)) {
17943            return $this->_packagefile->isSubpackage($pf);
17944        } elseif (isset($this->_downloadURL['info'])) {
17945            return $this->_downloadURL['info']->isSubpackage($pf);
17946        }
17947
17948        return false;
17949    }
17950
17951    function getPackageType()
17952    {
17953        if (isset($this->_packagefile)) {
17954            return $this->_packagefile->getPackageType();
17955        } elseif (isset($this->_downloadURL['info'])) {
17956            return $this->_downloadURL['info']->getPackageType();
17957        }
17958
17959        return false;
17960    }
17961
17962    function isBundle()
17963    {
17964        if (isset($this->_packagefile)) {
17965            return $this->_packagefile->getPackageType() == 'bundle';
17966        }
17967
17968        return false;
17969    }
17970
17971    function getPackageXmlVersion()
17972    {
17973        if (isset($this->_packagefile)) {
17974            return $this->_packagefile->getPackagexmlVersion();
17975        } elseif (isset($this->_downloadURL['info'])) {
17976            return $this->_downloadURL['info']->getPackagexmlVersion();
17977        }
17978
17979        return '1.0';
17980    }
17981
17982    function getChannel()
17983    {
17984        if (isset($this->_packagefile)) {
17985            return $this->_packagefile->getChannel();
17986        } elseif (isset($this->_downloadURL['info'])) {
17987            return $this->_downloadURL['info']->getChannel();
17988        }
17989
17990        return false;
17991    }
17992
17993    function getURI()
17994    {
17995        if (isset($this->_packagefile)) {
17996            return $this->_packagefile->getURI();
17997        } elseif (isset($this->_downloadURL['info'])) {
17998            return $this->_downloadURL['info']->getURI();
17999        }
18000
18001        return false;
18002    }
18003
18004    function getVersion()
18005    {
18006        if (isset($this->_packagefile)) {
18007            return $this->_packagefile->getVersion();
18008        } elseif (isset($this->_downloadURL['version'])) {
18009            return $this->_downloadURL['version'];
18010        }
18011
18012        return false;
18013    }
18014
18015    function isCompatible($pf)
18016    {
18017        if (isset($this->_packagefile)) {
18018            return $this->_packagefile->isCompatible($pf);
18019        } elseif (isset($this->_downloadURL['info'])) {
18020            return $this->_downloadURL['info']->isCompatible($pf);
18021        }
18022
18023        return true;
18024    }
18025
18026    function setGroup($group)
18027    {
18028        $this->_parsedname['group'] = $group;
18029    }
18030
18031    function getGroup()
18032    {
18033        if (isset($this->_parsedname['group'])) {
18034            return $this->_parsedname['group'];
18035        }
18036
18037        return '';
18038    }
18039
18040    function isExtension($name)
18041    {
18042        if (isset($this->_packagefile)) {
18043            return $this->_packagefile->isExtension($name);
18044        } elseif (isset($this->_downloadURL['info'])) {
18045            if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') {
18046                return $this->_downloadURL['info']->getProvidesExtension() == $name;
18047            }
18048
18049            return false;
18050        }
18051
18052        return false;
18053    }
18054
18055    function getDeps()
18056    {
18057        if (isset($this->_packagefile)) {
18058            $ver = $this->_packagefile->getPackagexmlVersion();
18059            if (version_compare($ver, '2.0', '>=')) {
18060                return $this->_packagefile->getDeps(true);
18061            }
18062
18063            return $this->_packagefile->getDeps();
18064        } elseif (isset($this->_downloadURL['info'])) {
18065            $ver = $this->_downloadURL['info']->getPackagexmlVersion();
18066            if (version_compare($ver, '2.0', '>=')) {
18067                return $this->_downloadURL['info']->getDeps(true);
18068            }
18069
18070            return $this->_downloadURL['info']->getDeps();
18071        }
18072
18073        return array();
18074    }
18075
18076    /**
18077     * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency
18078     *                     returned from getDepDownloadURL()
18079     */
18080    function isEqual($param)
18081    {
18082        if (is_object($param)) {
18083            $channel = $param->getChannel();
18084            $package = $param->getPackage();
18085            if ($param->getURI()) {
18086                $param = array(
18087                    'channel' => $param->getChannel(),
18088                    'package' => $param->getPackage(),
18089                    'version' => $param->getVersion(),
18090                    'uri' => $param->getURI(),
18091                );
18092            } else {
18093                $param = array(
18094                    'channel' => $param->getChannel(),
18095                    'package' => $param->getPackage(),
18096                    'version' => $param->getVersion(),
18097                );
18098            }
18099        } else {
18100            if (isset($param['uri'])) {
18101                if ($this->getChannel() != '__uri') {
18102                    return false;
18103                }
18104                return $param['uri'] == $this->getURI();
18105            }
18106
18107            $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage();
18108            $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel();
18109            if (isset($param['rel'])) {
18110                if (!class_exists('PEAR_Dependency2')) {
18111                    require_once 'PEAR/Dependency2.php';
18112                }
18113
18114                $newdep = PEAR_Dependency2::normalizeDep($param);
18115                $newdep = $newdep[0];
18116            } elseif (isset($param['min'])) {
18117                $newdep = $param;
18118            }
18119        }
18120
18121        if (isset($newdep)) {
18122            if (!isset($newdep['min'])) {
18123                $newdep['min'] = '0';
18124            }
18125
18126            if (!isset($newdep['max'])) {
18127                $newdep['max'] = '100000000000000000000';
18128            }
18129
18130            // use magic to support pecl packages suddenly jumping to the pecl channel
18131            // we need to support both dependency possibilities
18132            if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') {
18133                if ($package == $this->getPackage()) {
18134                    $channel = 'pecl.php.net';
18135                }
18136            }
18137            if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
18138                if ($package == $this->getPackage()) {
18139                    $channel = 'pear.php.net';
18140                }
18141            }
18142
18143            return (strtolower($package) == strtolower($this->getPackage()) &&
18144                $channel == $this->getChannel() &&
18145                version_compare($newdep['min'], $this->getVersion(), '<=') &&
18146                version_compare($newdep['max'], $this->getVersion(), '>='));
18147        }
18148
18149        // use magic to support pecl packages suddenly jumping to the pecl channel
18150        if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
18151            if (strtolower($package) == strtolower($this->getPackage())) {
18152                $channel = 'pear.php.net';
18153            }
18154        }
18155
18156        if (isset($param['version'])) {
18157            return (strtolower($package) == strtolower($this->getPackage()) &&
18158                $channel == $this->getChannel() &&
18159                $param['version'] == $this->getVersion());
18160        }
18161
18162        return strtolower($package) == strtolower($this->getPackage()) &&
18163            $channel == $this->getChannel();
18164    }
18165
18166    function isInstalled($dep, $oper = '==')
18167    {
18168        if (!$dep) {
18169            return false;
18170        }
18171
18172        if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') {
18173            return false;
18174        }
18175
18176        if (is_object($dep)) {
18177            $package = $dep->getPackage();
18178            $channel = $dep->getChannel();
18179            if ($dep->getURI()) {
18180                $dep = array(
18181                    'uri' => $dep->getURI(),
18182                    'version' => $dep->getVersion(),
18183                );
18184            } else {
18185                $dep = array(
18186                    'version' => $dep->getVersion(),
18187                );
18188            }
18189        } else {
18190            if (isset($dep['uri'])) {
18191                $channel = '__uri';
18192                $package = $dep['dep']['name'];
18193            } else {
18194                $channel = $dep['info']->getChannel();
18195                $package = $dep['info']->getPackage();
18196            }
18197        }
18198
18199        $options = $this->_downloader->getOptions();
18200        $test    = $this->_installRegistry->packageExists($package, $channel);
18201        if (!$test && $channel == 'pecl.php.net') {
18202            // do magic to allow upgrading from old pecl packages to new ones
18203            $test = $this->_installRegistry->packageExists($package, 'pear.php.net');
18204            $channel = 'pear.php.net';
18205        }
18206
18207        if ($test) {
18208            if (isset($dep['uri'])) {
18209                if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) {
18210                    return true;
18211                }
18212            }
18213
18214            if (isset($options['upgrade'])) {
18215                $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel);
18216                if (version_compare($packageVersion, $dep['version'], '>=')) {
18217                    return true;
18218                }
18219
18220                return false;
18221            }
18222
18223            return true;
18224        }
18225
18226        return false;
18227    }
18228
18229    /**
18230     * Detect duplicate package names with differing versions
18231     *
18232     * If a user requests to install Date 1.4.6 and Date 1.4.7,
18233     * for instance, this is a logic error.  This method
18234     * detects this situation.
18235     *
18236     * @param array $params array of PEAR_Downloader_Package objects
18237     * @param array $errorparams empty array
18238     * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts
18239     */
18240    function detectStupidDuplicates($params, &$errorparams)
18241    {
18242        $existing = array();
18243        foreach ($params as $i => $param) {
18244            $package = $param->getPackage();
18245            $channel = $param->getChannel();
18246            $group   = $param->getGroup();
18247            if (!isset($existing[$channel . '/' . $package])) {
18248                $existing[$channel . '/' . $package] = array();
18249            }
18250
18251            if (!isset($existing[$channel . '/' . $package][$group])) {
18252                $existing[$channel . '/' . $package][$group] = array();
18253            }
18254
18255            $existing[$channel . '/' . $package][$group][] = $i;
18256        }
18257
18258        $indices = array();
18259        foreach ($existing as $package => $groups) {
18260            foreach ($groups as $group => $dupes) {
18261                if (count($dupes) > 1) {
18262                    $indices = $indices + $dupes;
18263                }
18264            }
18265        }
18266
18267        $indices = array_unique($indices);
18268        foreach ($indices as $index) {
18269            $errorparams[] = $params[$index];
18270        }
18271
18272        return count($errorparams);
18273    }
18274
18275    /**
18276     * @param array
18277     * @param bool ignore install groups - for final removal of dupe packages
18278     * @static
18279     */
18280    function removeDuplicates(&$params, $ignoreGroups = false)
18281    {
18282        $pnames = array();
18283        foreach ($params as $i => $param) {
18284            if (!$param) {
18285                continue;
18286            }
18287
18288            if ($param->getPackage()) {
18289                $group = $ignoreGroups ? '' : $param->getGroup();
18290                $pnames[$i] = $param->getChannel() . '/' .
18291                    $param->getPackage() . '-' . $param->getVersion() . '#' . $group;
18292            }
18293        }
18294
18295        $pnames = array_unique($pnames);
18296        $unset  = array_diff(array_keys($params), array_keys($pnames));
18297        $testp  = array_flip($pnames);
18298        foreach ($params as $i => $param) {
18299            if (!$param) {
18300                $unset[] = $i;
18301                continue;
18302            }
18303
18304            if (!is_a($param, 'PEAR_Downloader_Package')) {
18305                $unset[] = $i;
18306                continue;
18307            }
18308
18309            $group = $ignoreGroups ? '' : $param->getGroup();
18310            if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' .
18311                  $param->getVersion() . '#' . $group])) {
18312                $unset[] = $i;
18313            }
18314        }
18315
18316        foreach ($unset as $i) {
18317            unset($params[$i]);
18318        }
18319
18320        $ret = array();
18321        foreach ($params as $i => $param) {
18322            $ret[] = &$params[$i];
18323        }
18324
18325        $params = array();
18326        foreach ($ret as $i => $param) {
18327            $params[] = &$ret[$i];
18328        }
18329    }
18330
18331    function explicitState()
18332    {
18333        return $this->_explicitState;
18334    }
18335
18336    function setExplicitState($s)
18337    {
18338        $this->_explicitState = $s;
18339    }
18340
18341    /**
18342     * @static
18343     */
18344    function mergeDependencies(&$params)
18345    {
18346        $bundles = $newparams = array();
18347        foreach ($params as $i => $param) {
18348            if (!$param->isBundle()) {
18349                continue;
18350            }
18351
18352            $bundles[] = $i;
18353            $pf = &$param->getPackageFile();
18354            $newdeps = array();
18355            $contents = $pf->getBundledPackages();
18356            if (!is_array($contents)) {
18357                $contents = array($contents);
18358            }
18359
18360            foreach ($contents as $file) {
18361                $filecontents = $pf->getFileContents($file);
18362                $dl = &$param->getDownloader();
18363                $options = $dl->getOptions();
18364                if (PEAR::isError($dir = $dl->getDownloadDir())) {
18365                    return $dir;
18366                }
18367
18368                $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb');
18369                if (!$fp) {
18370                    continue;
18371                }
18372
18373                // FIXME do symlink check
18374
18375                fwrite($fp, $filecontents, strlen($filecontents));
18376                fclose($fp);
18377                if ($s = $params[$i]->explicitState()) {
18378                    $obj->setExplicitState($s);
18379                }
18380
18381                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
18382                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
18383                if (PEAR::isError($dir = $dl->getDownloadDir())) {
18384                    PEAR::popErrorHandling();
18385                    return $dir;
18386                }
18387
18388                $e = $obj->_fromFile($a = $dir . DIRECTORY_SEPARATOR . $file);
18389                PEAR::popErrorHandling();
18390                if (PEAR::isError($e)) {
18391                    if (!isset($options['soft'])) {
18392                        $dl->log(0, $e->getMessage());
18393                    }
18394                    continue;
18395                }
18396
18397                $j = &$obj;
18398                if (!PEAR_Downloader_Package::willDownload($j,
18399                      array_merge($params, $newparams)) && !$param->isInstalled($j)) {
18400                    $newparams[] = &$j;
18401                }
18402            }
18403        }
18404
18405        foreach ($bundles as $i) {
18406            unset($params[$i]); // remove bundles - only their contents matter for installation
18407        }
18408
18409        PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices
18410        if (count($newparams)) { // add in bundled packages for install
18411            foreach ($newparams as $i => $unused) {
18412                $params[] = &$newparams[$i];
18413            }
18414            $newparams = array();
18415        }
18416
18417        foreach ($params as $i => $param) {
18418            $newdeps = array();
18419            foreach ($param->_downloadDeps as $dep) {
18420                $merge = array_merge($params, $newparams);
18421                if (!PEAR_Downloader_Package::willDownload($dep, $merge)
18422                    && !$param->isInstalled($dep)
18423                ) {
18424                    $newdeps[] = $dep;
18425                } else {
18426                    //var_dump($dep);
18427                    // detect versioning conflicts here
18428                }
18429            }
18430
18431            // convert the dependencies into PEAR_Downloader_Package objects for the next time around
18432            $params[$i]->_downloadDeps = array();
18433            foreach ($newdeps as $dep) {
18434                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
18435                if ($s = $params[$i]->explicitState()) {
18436                    $obj->setExplicitState($s);
18437                }
18438
18439                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
18440                $e = $obj->fromDepURL($dep);
18441                PEAR::popErrorHandling();
18442                if (PEAR::isError($e)) {
18443                    if (!isset($options['soft'])) {
18444                        $obj->_downloader->log(0, $e->getMessage());
18445                    }
18446                    continue;
18447                }
18448
18449                $e = $obj->detectDependencies($params);
18450                if (PEAR::isError($e)) {
18451                    if (!isset($options['soft'])) {
18452                        $obj->_downloader->log(0, $e->getMessage());
18453                    }
18454                }
18455
18456                $j = &$obj;
18457                $newparams[] = &$j;
18458            }
18459        }
18460
18461        if (count($newparams)) {
18462            foreach ($newparams as $i => $unused) {
18463                $params[] = &$newparams[$i];
18464            }
18465            return true;
18466        }
18467
18468        return false;
18469    }
18470
18471
18472    /**
18473     * @static
18474     */
18475    function willDownload($param, $params)
18476    {
18477        if (!is_array($params)) {
18478            return false;
18479        }
18480
18481        foreach ($params as $obj) {
18482            if ($obj->isEqual($param)) {
18483                return true;
18484            }
18485        }
18486
18487        return false;
18488    }
18489
18490    /**
18491     * For simpler unit-testing
18492     * @param PEAR_Config
18493     * @param int
18494     * @param string
18495     */
18496    function &getPackagefileObject(&$c, $d)
18497    {
18498        $a = &new PEAR_PackageFile($c, $d);
18499        return $a;
18500    }
18501
18502    /**
18503     * This will retrieve from a local file if possible, and parse out
18504     * a group name as well.  The original parameter will be modified to reflect this.
18505     * @param string|array can be a parsed package name as well
18506     * @access private
18507     */
18508    function _fromFile(&$param)
18509    {
18510        $saveparam = $param;
18511        if (is_string($param)) {
18512            if (!@file_exists($param)) {
18513                $test = explode('#', $param);
18514                $group = array_pop($test);
18515                if (@file_exists(implode('#', $test))) {
18516                    $this->setGroup($group);
18517                    $param = implode('#', $test);
18518                    $this->_explicitGroup = true;
18519                }
18520            }
18521
18522            if (@is_file($param)) {
18523                $this->_type = 'local';
18524                $options = $this->_downloader->getOptions();
18525                $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug);
18526                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
18527                $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING);
18528                PEAR::popErrorHandling();
18529                if (PEAR::isError($pf)) {
18530                    $this->_valid = false;
18531                    $param = $saveparam;
18532                    return $pf;
18533                }
18534                $this->_packagefile = &$pf;
18535                if (!$this->getGroup()) {
18536                    $this->setGroup('default'); // install the default dependency group
18537                }
18538                return $this->_valid = true;
18539            }
18540        }
18541        $param = $saveparam;
18542        return $this->_valid = false;
18543    }
18544
18545    function _fromUrl($param, $saveparam = '')
18546    {
18547        if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) {
18548            $options = $this->_downloader->getOptions();
18549            $this->_type = 'url';
18550            $callback = $this->_downloader->ui ?
18551                array(&$this->_downloader, '_downloadCallback') : null;
18552            $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN);
18553            if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
18554                $this->_downloader->popErrorHandling();
18555                return $dir;
18556            }
18557
18558            $this->_downloader->log(3, 'Downloading "' . $param . '"');
18559            $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui,
18560                $dir, $callback, null, false, $this->getChannel());
18561            $this->_downloader->popErrorHandling();
18562            if (PEAR::isError($file)) {
18563                if (!empty($saveparam)) {
18564                    $saveparam = ", cannot download \"$saveparam\"";
18565                }
18566                $err = PEAR::raiseError('Could not download from "' . $param .
18567                    '"' . $saveparam . ' (' . $file->getMessage() . ')');
18568                    return $err;
18569            }
18570
18571            if ($this->_rawpackagefile) {
18572                require_once 'Archive/Tar.php';
18573                $tar = &new Archive_Tar($file);
18574                $packagexml = $tar->extractInString('package2.xml');
18575                if (!$packagexml) {
18576                    $packagexml = $tar->extractInString('package.xml');
18577                }
18578
18579                if (str_replace(array("\n", "\r"), array('',''), $packagexml) !=
18580                      str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) {
18581                    if ($this->getChannel() != 'pear.php.net') {
18582                        return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' .
18583                            'not match value returned from xml-rpc');
18584                    }
18585
18586                    // be more lax for the existing PEAR packages that have not-ok
18587                    // characters in their package.xml
18588                    $this->_downloader->log(0, 'CRITICAL WARNING: The "' .
18589                        $this->getPackage() . '" package has invalid characters in its ' .
18590                        'package.xml.  The next version of PEAR may not be able to install ' .
18591                        'this package for security reasons.  Please open a bug report at ' .
18592                        'http://pear.php.net/package/' . $this->getPackage() . '/bugs');
18593                }
18594            }
18595
18596            // whew, download worked!
18597            $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug);
18598
18599            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
18600            $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING);
18601            PEAR::popErrorHandling();
18602            if (PEAR::isError($pf)) {
18603                if (is_array($pf->getUserInfo())) {
18604                    foreach ($pf->getUserInfo() as $err) {
18605                        if (is_array($err)) {
18606                            $err = $err['message'];
18607                        }
18608
18609                        if (!isset($options['soft'])) {
18610                            $this->_downloader->log(0, "Validation Error: $err");
18611                        }
18612                    }
18613                }
18614
18615                if (!isset($options['soft'])) {
18616                    $this->_downloader->log(0, $pf->getMessage());
18617                }
18618
18619                ///FIXME need to pass back some error code that we can use to match with to cancel all further operations
18620                /// At least stop all deps of this package from being installed
18621                $out = $saveparam ? $saveparam : $param;
18622                $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive');
18623                $this->_valid = false;
18624                return $err;
18625            }
18626
18627            $this->_packagefile = &$pf;
18628            $this->setGroup('default'); // install the default dependency group
18629            return $this->_valid = true;
18630        }
18631
18632        return $this->_valid = false;
18633    }
18634
18635    /**
18636     *
18637     * @param string|array pass in an array of format
18638     *                     array(
18639     *                      'package' => 'pname',
18640     *                     ['channel' => 'channame',]
18641     *                     ['version' => 'version',]
18642     *                     ['state' => 'state',])
18643     *                     or a string of format [channame/]pname[-version|-state]
18644     */
18645    function _fromString($param)
18646    {
18647        $options = $this->_downloader->getOptions();
18648        $channel = $this->_config->get('default_channel');
18649        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
18650        $pname = $this->_registry->parsePackageName($param, $channel);
18651        PEAR::popErrorHandling();
18652        if (PEAR::isError($pname)) {
18653            if ($pname->getCode() == 'invalid') {
18654                $this->_valid = false;
18655                return false;
18656            }
18657
18658            if ($pname->getCode() == 'channel') {
18659                $parsed = $pname->getUserInfo();
18660                if ($this->_downloader->discover($parsed['channel'])) {
18661                    if ($this->_config->get('auto_discover')) {
18662                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
18663                        $pname = $this->_registry->parsePackageName($param, $channel);
18664                        PEAR::popErrorHandling();
18665                    } else {
18666                        if (!isset($options['soft'])) {
18667                            $this->_downloader->log(0, 'Channel "' . $parsed['channel'] .
18668                                '" is not initialized, use ' .
18669                                '"pear channel-discover ' . $parsed['channel'] . '" to initialize' .
18670                                'or pear config-set auto_discover 1');
18671                        }
18672                    }
18673                }
18674
18675                if (PEAR::isError($pname)) {
18676                    if (!isset($options['soft'])) {
18677                        $this->_downloader->log(0, $pname->getMessage());
18678                    }
18679
18680                    if (is_array($param)) {
18681                        $param = $this->_registry->parsedPackageNameToString($param);
18682                    }
18683
18684                    $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
18685                    $this->_valid = false;
18686                    return $err;
18687                }
18688            } else {
18689                if (!isset($options['soft'])) {
18690                    $this->_downloader->log(0, $pname->getMessage());
18691                }
18692
18693                $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
18694                $this->_valid = false;
18695                return $err;
18696            }
18697        }
18698
18699        if (!isset($this->_type)) {
18700            $this->_type = 'rest';
18701        }
18702
18703        $this->_parsedname    = $pname;
18704        $this->_explicitState = isset($pname['state']) ? $pname['state'] : false;
18705        $this->_explicitGroup = isset($pname['group']) ? true : false;
18706
18707        $info = $this->_downloader->_getPackageDownloadUrl($pname);
18708        if (PEAR::isError($info)) {
18709            if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') {
18710                // try pecl
18711                $pname['channel'] = 'pecl.php.net';
18712                if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) {
18713                    if (!PEAR::isError($test)) {
18714                        $info = PEAR::raiseError($info->getMessage() . ' - package ' .
18715                            $this->_registry->parsedPackageNameToString($pname, true) .
18716                            ' can be installed with "pecl install ' . $pname['package'] .
18717                            '"');
18718                    } else {
18719                        $pname['channel'] = 'pear.php.net';
18720                    }
18721                } else {
18722                    $pname['channel'] = 'pear.php.net';
18723                }
18724            }
18725
18726            return $info;
18727        }
18728
18729        $this->_rawpackagefile = $info['raw'];
18730        $ret = $this->_analyzeDownloadURL($info, $param, $pname);
18731        if (PEAR::isError($ret)) {
18732            return $ret;
18733        }
18734
18735        if ($ret) {
18736            $this->_downloadURL = $ret;
18737            return $this->_valid = (bool) $ret;
18738        }
18739    }
18740
18741    /**
18742     * @param array output of package.getDownloadURL
18743     * @param string|array|object information for detecting packages to be downloaded, and
18744     *                            for errors
18745     * @param array name information of the package
18746     * @param array|null packages to be downloaded
18747     * @param bool is this an optional dependency?
18748     * @param bool is this any kind of dependency?
18749     * @access private
18750     */
18751    function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false,
18752                                 $isdependency = false)
18753    {
18754        if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) {
18755            return false;
18756        }
18757
18758        if ($info === false) {
18759            $saveparam = !is_string($param) ? ", cannot download \"$param\"" : '';
18760
18761            // no releases exist
18762            return PEAR::raiseError('No releases for package "' .
18763                $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam);
18764        }
18765
18766        if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) {
18767            $err = false;
18768            if ($pname['channel'] == 'pecl.php.net') {
18769                if ($info['info']->getChannel() != 'pear.php.net') {
18770                    $err = true;
18771                }
18772            } elseif ($info['info']->getChannel() == 'pecl.php.net') {
18773                if ($pname['channel'] != 'pear.php.net') {
18774                    $err = true;
18775                }
18776            } else {
18777                $err = true;
18778            }
18779
18780            if ($err) {
18781                return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] .
18782                    '" retrieved another channel\'s name for download! ("' .
18783                    $info['info']->getChannel() . '")');
18784            }
18785        }
18786
18787        $preferred_state = $this->_config->get('preferred_state');
18788        if (!isset($info['url'])) {
18789            $package_version = $this->_registry->packageInfo($info['info']->getPackage(),
18790            'version', $info['info']->getChannel());
18791            if ($this->isInstalled($info)) {
18792                if ($isdependency && version_compare($info['version'], $package_version, '<=')) {
18793                    // ignore bogus errors of "failed to download dependency"
18794                    // if it is already installed and the one that would be
18795                    // downloaded is older or the same version (Bug #7219)
18796                    return false;
18797                }
18798            }
18799
18800            if ($info['version'] === $package_version) {
18801                if (!isset($options['soft'])) {
18802                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
18803                        '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' .
18804                        ' (' . $package_version . ') is the same as the locally installed one.');
18805                }
18806
18807                return false;
18808            }
18809
18810            if (version_compare($info['version'], $package_version, '<=')) {
18811                if (!isset($options['soft'])) {
18812                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
18813                        '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' .
18814                        ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').');
18815                }
18816
18817                return false;
18818            }
18819
18820            $instead =  ', will instead download version ' . $info['version'] .
18821                        ', stability "' . $info['info']->getState() . '"';
18822            // releases exist, but we failed to get any
18823            if (isset($this->_downloader->_options['force'])) {
18824                if (isset($pname['version'])) {
18825                    $vs = ', version "' . $pname['version'] . '"';
18826                } elseif (isset($pname['state'])) {
18827                    $vs = ', stability "' . $pname['state'] . '"';
18828                } elseif ($param == 'dependency') {
18829                    if (!class_exists('PEAR_Common')) {
18830                        require_once 'PEAR/Common.php';
18831                    }
18832
18833                    if (!in_array($info['info']->getState(),
18834                          PEAR_Common::betterStates($preferred_state, true))) {
18835                        if ($optional) {
18836                            // don't spit out confusing error message
18837                            return $this->_downloader->_getPackageDownloadUrl(
18838                                array('package' => $pname['package'],
18839                                      'channel' => $pname['channel'],
18840                                      'version' => $info['version']));
18841                        }
18842                        $vs = ' within preferred state "' . $preferred_state .
18843                            '"';
18844                    } else {
18845                        if (!class_exists('PEAR_Dependency2')) {
18846                            require_once 'PEAR/Dependency2.php';
18847                        }
18848
18849                        if ($optional) {
18850                            // don't spit out confusing error message
18851                            return $this->_downloader->_getPackageDownloadUrl(
18852                                array('package' => $pname['package'],
18853                                      'channel' => $pname['channel'],
18854                                      'version' => $info['version']));
18855                        }
18856                        $vs = PEAR_Dependency2::_getExtraString($pname);
18857                        $instead = '';
18858                    }
18859                } else {
18860                    $vs = ' within preferred state "' . $preferred_state . '"';
18861                }
18862
18863                if (!isset($options['soft'])) {
18864                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
18865                        '/' . $pname['package'] . $vs . $instead);
18866                }
18867
18868                // download the latest release
18869                return $this->_downloader->_getPackageDownloadUrl(
18870                    array('package' => $pname['package'],
18871                          'channel' => $pname['channel'],
18872                          'version' => $info['version']));
18873            } else {
18874                if (isset($info['php']) && $info['php']) {
18875                    $err = PEAR::raiseError('Failed to download ' .
18876                        $this->_registry->parsedPackageNameToString(
18877                            array('channel' => $pname['channel'],
18878                                  'package' => $pname['package']),
18879                                true) .
18880                        ', latest release is version ' . $info['php']['v'] .
18881                        ', but it requires PHP version "' .
18882                        $info['php']['m'] . '", use "' .
18883                        $this->_registry->parsedPackageNameToString(
18884                            array('channel' => $pname['channel'], 'package' => $pname['package'],
18885                            'version' => $info['php']['v'])) . '" to install',
18886                            PEAR_DOWNLOADER_PACKAGE_PHPVERSION);
18887                    return $err;
18888                }
18889
18890                // construct helpful error message
18891                if (isset($pname['version'])) {
18892                    $vs = ', version "' . $pname['version'] . '"';
18893                } elseif (isset($pname['state'])) {
18894                    $vs = ', stability "' . $pname['state'] . '"';
18895                } elseif ($param == 'dependency') {
18896                    if (!class_exists('PEAR_Common')) {
18897                        require_once 'PEAR/Common.php';
18898                    }
18899
18900                    if (!in_array($info['info']->getState(),
18901                          PEAR_Common::betterStates($preferred_state, true))) {
18902                        if ($optional) {
18903                            // don't spit out confusing error message, and don't die on
18904                            // optional dep failure!
18905                            return $this->_downloader->_getPackageDownloadUrl(
18906                                array('package' => $pname['package'],
18907                                      'channel' => $pname['channel'],
18908                                      'version' => $info['version']));
18909                        }
18910                        $vs = ' within preferred state "' . $preferred_state . '"';
18911                    } else {
18912                        if (!class_exists('PEAR_Dependency2')) {
18913                            require_once 'PEAR/Dependency2.php';
18914                        }
18915
18916                        if ($optional) {
18917                            // don't spit out confusing error message, and don't die on
18918                            // optional dep failure!
18919                            return $this->_downloader->_getPackageDownloadUrl(
18920                                array('package' => $pname['package'],
18921                                      'channel' => $pname['channel'],
18922                                      'version' => $info['version']));
18923                        }
18924                        $vs = PEAR_Dependency2::_getExtraString($pname);
18925                    }
18926                } else {
18927                    $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"';
18928                }
18929
18930                $options = $this->_downloader->getOptions();
18931                // this is only set by the "download-all" command
18932                if (isset($options['ignorepreferred_state'])) {
18933                    $err = PEAR::raiseError(
18934                        'Failed to download ' . $this->_registry->parsedPackageNameToString(
18935                            array('channel' => $pname['channel'], 'package' => $pname['package']),
18936                                true)
18937                         . $vs .
18938                        ', latest release is version ' . $info['version'] .
18939                        ', stability "' . $info['info']->getState() . '", use "' .
18940                        $this->_registry->parsedPackageNameToString(
18941                            array('channel' => $pname['channel'], 'package' => $pname['package'],
18942                            'version' => $info['version'])) . '" to install',
18943                            PEAR_DOWNLOADER_PACKAGE_STATE);
18944                    return $err;
18945                }
18946
18947                // Checks if the user has a package installed already and checks the release against
18948                // the state against the installed package, this allows upgrades for packages
18949                // with lower stability than the preferred_state
18950                $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']);
18951                if (!$this->isInstalled($info)
18952                    || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true))
18953                ) {
18954                    $err = PEAR::raiseError(
18955                        'Failed to download ' . $this->_registry->parsedPackageNameToString(
18956                            array('channel' => $pname['channel'], 'package' => $pname['package']),
18957                                true)
18958                         . $vs .
18959                        ', latest release is version ' . $info['version'] .
18960                        ', stability "' . $info['info']->getState() . '", use "' .
18961                        $this->_registry->parsedPackageNameToString(
18962                            array('channel' => $pname['channel'], 'package' => $pname['package'],
18963                            'version' => $info['version'])) . '" to install');
18964                    return $err;
18965                }
18966            }
18967        }
18968
18969        if (isset($info['deprecated']) && $info['deprecated']) {
18970            $this->_downloader->log(0,
18971                'WARNING: "' .
18972                    $this->_registry->parsedPackageNameToString(
18973                            array('channel' => $info['info']->getChannel(),
18974                                  'package' => $info['info']->getPackage()), true) .
18975                '" is deprecated in favor of "' .
18976                    $this->_registry->parsedPackageNameToString($info['deprecated'], true) .
18977                '"');
18978        }
18979
18980        return $info;
18981    }
18982}PEAR-1.9.4/PEAR/Frontend/CLI.php0000644000076500000240000006221611605156614014671 0ustar  helgistaff<?php
18983/**
18984 * PEAR_Frontend_CLI
18985 *
18986 * PHP versions 4 and 5
18987 *
18988 * @category   pear
18989 * @package    PEAR
18990 * @author     Stig Bakken <ssb@php.net>
18991 * @author     Greg Beaver <cellog@php.net>
18992 * @copyright  1997-2009 The Authors
18993 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
18994 * @version    CVS: $Id: CLI.php 313023 2011-07-06 19:17:11Z dufuz $
18995 * @link       http://pear.php.net/package/PEAR
18996 * @since      File available since Release 0.1
18997 */
18998/**
18999 * base class
19000 */
19001require_once 'PEAR/Frontend.php';
19002
19003/**
19004 * Command-line Frontend for the PEAR Installer
19005 * @category   pear
19006 * @package    PEAR
19007 * @author     Stig Bakken <ssb@php.net>
19008 * @author     Greg Beaver <cellog@php.net>
19009 * @copyright  1997-2009 The Authors
19010 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
19011 * @version    Release: 1.9.4
19012 * @link       http://pear.php.net/package/PEAR
19013 * @since      Class available since Release 0.1
19014 */
19015class PEAR_Frontend_CLI extends PEAR_Frontend
19016{
19017    /**
19018     * What type of user interface this frontend is for.
19019     * @var string
19020     * @access public
19021     */
19022    var $type = 'CLI';
19023    var $lp = ''; // line prefix
19024
19025    var $params = array();
19026    var $term = array(
19027        'bold'   => '',
19028        'normal' => '',
19029    );
19030
19031    function PEAR_Frontend_CLI()
19032    {
19033        parent::PEAR();
19034        $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1
19035        if (function_exists('posix_isatty') && !posix_isatty(1)) {
19036            // output is being redirected to a file or through a pipe
19037        } elseif ($term) {
19038            if (preg_match('/^(xterm|vt220|linux)/', $term)) {
19039                $this->term['bold']   = sprintf("%c%c%c%c", 27, 91, 49, 109);
19040                $this->term['normal'] = sprintf("%c%c%c", 27, 91, 109);
19041            } elseif (preg_match('/^vt100/', $term)) {
19042                $this->term['bold']   = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0);
19043                $this->term['normal'] = sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0);
19044            }
19045        } elseif (OS_WINDOWS) {
19046            // XXX add ANSI codes here
19047        }
19048    }
19049
19050    /**
19051     * @param object PEAR_Error object
19052     */
19053    function displayError($e)
19054    {
19055        return $this->_displayLine($e->getMessage());
19056    }
19057
19058    /**
19059     * @param object PEAR_Error object
19060     */
19061    function displayFatalError($eobj)
19062    {
19063        $this->displayError($eobj);
19064        if (class_exists('PEAR_Config')) {
19065            $config = &PEAR_Config::singleton();
19066            if ($config->get('verbose') > 5) {
19067                if (function_exists('debug_print_backtrace')) {
19068                    debug_print_backtrace();
19069                    exit(1);
19070                }
19071
19072                $raised = false;
19073                foreach (debug_backtrace() as $i => $frame) {
19074                    if (!$raised) {
19075                        if (isset($frame['class'])
19076                            && strtolower($frame['class']) == 'pear'
19077                            && strtolower($frame['function']) == 'raiseerror'
19078                        ) {
19079                            $raised = true;
19080                        } else {
19081                            continue;
19082                        }
19083                    }
19084
19085                    $frame['class']    = !isset($frame['class'])    ? '' : $frame['class'];
19086                    $frame['type']     = !isset($frame['type'])     ? '' : $frame['type'];
19087                    $frame['function'] = !isset($frame['function']) ? '' : $frame['function'];
19088                    $frame['line']     = !isset($frame['line'])     ? '' : $frame['line'];
19089                    $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]");
19090                }
19091            }
19092        }
19093
19094        exit(1);
19095    }
19096
19097    /**
19098     * Instruct the runInstallScript method to skip a paramgroup that matches the
19099     * id value passed in.
19100     *
19101     * This method is useful for dynamically configuring which sections of a post-install script
19102     * will be run based on the user's setup, which is very useful for making flexible
19103     * post-install scripts without losing the cross-Frontend ability to retrieve user input
19104     * @param string
19105     */
19106    function skipParamgroup($id)
19107    {
19108        $this->_skipSections[$id] = true;
19109    }
19110
19111    function runPostinstallScripts(&$scripts)
19112    {
19113        foreach ($scripts as $i => $script) {
19114            $this->runInstallScript($scripts[$i]->_params, $scripts[$i]->_obj);
19115        }
19116    }
19117
19118    /**
19119     * @param array $xml contents of postinstallscript tag
19120     * @param object $script post-installation script
19121     * @param string install|upgrade
19122     */
19123    function runInstallScript($xml, &$script)
19124    {
19125        $this->_skipSections = array();
19126        if (!is_array($xml) || !isset($xml['paramgroup'])) {
19127            $script->run(array(), '_default');
19128            return;
19129        }
19130
19131        $completedPhases = array();
19132        if (!isset($xml['paramgroup'][0])) {
19133            $xml['paramgroup'] = array($xml['paramgroup']);
19134        }
19135
19136        foreach ($xml['paramgroup'] as $group) {
19137            if (isset($this->_skipSections[$group['id']])) {
19138                // the post-install script chose to skip this section dynamically
19139                continue;
19140            }
19141
19142            if (isset($group['name'])) {
19143                $paramname = explode('::', $group['name']);
19144                if ($lastgroup['id'] != $paramname[0]) {
19145                    continue;
19146                }
19147
19148                $group['name'] = $paramname[1];
19149                if (!isset($answers)) {
19150                    return;
19151                }
19152
19153                if (isset($answers[$group['name']])) {
19154                    switch ($group['conditiontype']) {
19155                        case '=' :
19156                            if ($answers[$group['name']] != $group['value']) {
19157                                continue 2;
19158                            }
19159                        break;
19160                        case '!=' :
19161                            if ($answers[$group['name']] == $group['value']) {
19162                                continue 2;
19163                            }
19164                        break;
19165                        case 'preg_match' :
19166                            if (!@preg_match('/' . $group['value'] . '/',
19167                                  $answers[$group['name']])) {
19168                                continue 2;
19169                            }
19170                        break;
19171                        default :
19172                        return;
19173                    }
19174                }
19175            }
19176
19177            $lastgroup = $group;
19178            if (isset($group['instructions'])) {
19179                $this->_display($group['instructions']);
19180            }
19181
19182            if (!isset($group['param'][0])) {
19183                $group['param'] = array($group['param']);
19184            }
19185
19186            if (isset($group['param'])) {
19187                if (method_exists($script, 'postProcessPrompts')) {
19188                    $prompts = $script->postProcessPrompts($group['param'], $group['id']);
19189                    if (!is_array($prompts) || count($prompts) != count($group['param'])) {
19190                        $this->outputData('postinstall', 'Error: post-install script did not ' .
19191                            'return proper post-processed prompts');
19192                        $prompts = $group['param'];
19193                    } else {
19194                        foreach ($prompts as $i => $var) {
19195                            if (!is_array($var) || !isset($var['prompt']) ||
19196                                  !isset($var['name']) ||
19197                                  ($var['name'] != $group['param'][$i]['name']) ||
19198                                  ($var['type'] != $group['param'][$i]['type'])
19199                            ) {
19200                                $this->outputData('postinstall', 'Error: post-install script ' .
19201                                    'modified the variables or prompts, severe security risk. ' .
19202                                    'Will instead use the defaults from the package.xml');
19203                                $prompts = $group['param'];
19204                            }
19205                        }
19206                    }
19207
19208                    $answers = $this->confirmDialog($prompts);
19209                } else {
19210                    $answers = $this->confirmDialog($group['param']);
19211                }
19212            }
19213
19214            if ((isset($answers) && $answers) || !isset($group['param'])) {
19215                if (!isset($answers)) {
19216                    $answers = array();
19217                }
19218
19219                array_unshift($completedPhases, $group['id']);
19220                if (!$script->run($answers, $group['id'])) {
19221                    $script->run($completedPhases, '_undoOnError');
19222                    return;
19223                }
19224            } else {
19225                $script->run($completedPhases, '_undoOnError');
19226                return;
19227            }
19228        }
19229    }
19230
19231    /**
19232     * Ask for user input, confirm the answers and continue until the user is satisfied
19233     * @param array an array of arrays, format array('name' => 'paramname', 'prompt' =>
19234     *              'text to display', 'type' => 'string'[, default => 'default value'])
19235     * @return array
19236     */
19237    function confirmDialog($params)
19238    {
19239        $answers = $prompts = $types = array();
19240        foreach ($params as $param) {
19241            $prompts[$param['name']] = $param['prompt'];
19242            $types[$param['name']]   = $param['type'];
19243            $answers[$param['name']] = isset($param['default']) ? $param['default'] : '';
19244        }
19245
19246        $tried = false;
19247        do {
19248            if ($tried) {
19249                $i = 1;
19250                foreach ($answers as $var => $value) {
19251                    if (!strlen($value)) {
19252                        echo $this->bold("* Enter an answer for #" . $i . ": ({$prompts[$var]})\n");
19253                    }
19254                    $i++;
19255                }
19256            }
19257
19258            $answers = $this->userDialog('', $prompts, $types, $answers);
19259            $tried   = true;
19260        } while (is_array($answers) && count(array_filter($answers)) != count($prompts));
19261
19262        return $answers;
19263    }
19264
19265    function userDialog($command, $prompts, $types = array(), $defaults = array(), $screensize = 20)
19266    {
19267        if (!is_array($prompts)) {
19268            return array();
19269        }
19270
19271        $testprompts = array_keys($prompts);
19272        $result      = $defaults;
19273
19274        reset($prompts);
19275        if (count($prompts) === 1) {
19276            foreach ($prompts as $key => $prompt) {
19277                $type    = $types[$key];
19278                $default = @$defaults[$key];
19279                print "$prompt ";
19280                if ($default) {
19281                    print "[$default] ";
19282                }
19283                print ": ";
19284
19285                $line         = fgets(STDIN, 2048);
19286                $result[$key] =  ($default && trim($line) == '') ? $default : trim($line);
19287            }
19288
19289            return $result;
19290        }
19291
19292        $first_run = true;
19293        while (true) {
19294            $descLength = max(array_map('strlen', $prompts));
19295            $descFormat = "%-{$descLength}s";
19296            $last       = count($prompts);
19297
19298            $i = 0;
19299            foreach ($prompts as $n => $var) {
19300                $res = isset($result[$n]) ? $result[$n] : null;
19301                printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], $res);
19302            }
19303            print "\n1-$last, 'all', 'abort', or Enter to continue: ";
19304
19305            $tmp = trim(fgets(STDIN, 1024));
19306            if (empty($tmp)) {
19307                break;
19308            }
19309
19310            if ($tmp == 'abort') {
19311                return false;
19312            }
19313
19314            if (isset($testprompts[(int)$tmp - 1])) {
19315                $var     = $testprompts[(int)$tmp - 1];
19316                $desc    = $prompts[$var];
19317                $current = @$result[$var];
19318                print "$desc [$current] : ";
19319                $tmp = trim(fgets(STDIN, 1024));
19320                if ($tmp !== '') {
19321                    $result[$var] = $tmp;
19322                }
19323            } elseif ($tmp == 'all') {
19324                foreach ($prompts as $var => $desc) {
19325                    $current = $result[$var];
19326                    print "$desc [$current] : ";
19327                    $tmp = trim(fgets(STDIN, 1024));
19328                    if (trim($tmp) !== '') {
19329                        $result[$var] = trim($tmp);
19330                    }
19331                }
19332            }
19333
19334            $first_run = false;
19335        }
19336
19337        return $result;
19338    }
19339
19340    function userConfirm($prompt, $default = 'yes')
19341    {
19342        trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR);
19343        static $positives = array('y', 'yes', 'on', '1');
19344        static $negatives = array('n', 'no', 'off', '0');
19345        print "$this->lp$prompt [$default] : ";
19346        $fp = fopen("php://stdin", "r");
19347        $line = fgets($fp, 2048);
19348        fclose($fp);
19349        $answer = strtolower(trim($line));
19350        if (empty($answer)) {
19351            $answer = $default;
19352        }
19353        if (in_array($answer, $positives)) {
19354            return true;
19355        }
19356        if (in_array($answer, $negatives)) {
19357            return false;
19358        }
19359        if (in_array($default, $positives)) {
19360            return true;
19361        }
19362        return false;
19363    }
19364
19365    function outputData($data, $command = '_default')
19366    {
19367        switch ($command) {
19368            case 'channel-info':
19369                foreach ($data as $type => $section) {
19370                    if ($type == 'main') {
19371                        $section['data'] = array_values($section['data']);
19372                    }
19373
19374                    $this->outputData($section);
19375                }
19376                break;
19377            case 'install':
19378            case 'upgrade':
19379            case 'upgrade-all':
19380                if (is_array($data) && isset($data['release_warnings'])) {
19381                    $this->_displayLine('');
19382                    $this->_startTable(array(
19383                        'border' => false,
19384                        'caption' => 'Release Warnings'
19385                    ));
19386                    $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55)));
19387                    $this->_endTable();
19388                    $this->_displayLine('');
19389                }
19390
19391                $this->_displayLine(is_array($data) ? $data['data'] : $data);
19392                break;
19393            case 'search':
19394                $this->_startTable($data);
19395                if (isset($data['headline']) && is_array($data['headline'])) {
19396                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
19397                }
19398
19399                $packages = array();
19400                foreach($data['data'] as $category) {
19401                    foreach($category as $name => $pkg) {
19402                        $packages[$pkg[0]] = $pkg;
19403                    }
19404                }
19405
19406                $p = array_keys($packages);
19407                natcasesort($p);
19408                foreach ($p as $name) {
19409                    $this->_tableRow($packages[$name], null, array(1 => array('wrap' => 55)));
19410                }
19411
19412                $this->_endTable();
19413                break;
19414            case 'list-all':
19415                if (!isset($data['data'])) {
19416                      $this->_displayLine('No packages in channel');
19417                      break;
19418                }
19419
19420                $this->_startTable($data);
19421                if (isset($data['headline']) && is_array($data['headline'])) {
19422                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
19423                }
19424
19425                $packages = array();
19426                foreach($data['data'] as $category) {
19427                    foreach($category as $name => $pkg) {
19428                        $packages[$pkg[0]] = $pkg;
19429                    }
19430                }
19431
19432                $p = array_keys($packages);
19433                natcasesort($p);
19434                foreach ($p as $name) {
19435                    $pkg = $packages[$name];
19436                    unset($pkg[4], $pkg[5]);
19437                    $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
19438                }
19439
19440                $this->_endTable();
19441                break;
19442            case 'config-show':
19443                $data['border'] = false;
19444                $opts = array(
19445                    0 => array('wrap' => 30),
19446                    1 => array('wrap' => 20),
19447                    2 => array('wrap' => 35)
19448                );
19449
19450                $this->_startTable($data);
19451                if (isset($data['headline']) && is_array($data['headline'])) {
19452                    $this->_tableRow($data['headline'], array('bold' => true), $opts);
19453                }
19454
19455                foreach ($data['data'] as $group) {
19456                    foreach ($group as $value) {
19457                        if ($value[2] == '') {
19458                            $value[2] = "<not set>";
19459                        }
19460
19461                        $this->_tableRow($value, null, $opts);
19462                    }
19463                }
19464
19465                $this->_endTable();
19466                break;
19467            case 'remote-info':
19468                $d = $data;
19469                $data = array(
19470                    'caption' => 'Package details:',
19471                    'border'  => false,
19472                    'data'    => array(
19473                        array("Latest",      $data['stable']),
19474                        array("Installed",   $data['installed']),
19475                        array("Package",     $data['name']),
19476                        array("License",     $data['license']),
19477                        array("Category",    $data['category']),
19478                        array("Summary",     $data['summary']),
19479                        array("Description", $data['description']),
19480                    ),
19481                );
19482
19483                if (isset($d['deprecated']) && $d['deprecated']) {
19484                    $conf = &PEAR_Config::singleton();
19485                    $reg = $conf->getRegistry();
19486                    $name = $reg->parsedPackageNameToString($d['deprecated'], true);
19487                    $data['data'][] = array('Deprecated! use', $name);
19488                }
19489            default: {
19490                if (is_array($data)) {
19491                    $this->_startTable($data);
19492                    $count = count($data['data'][0]);
19493                    if ($count == 2) {
19494                        $opts = array(0 => array('wrap' => 25),
19495                                      1 => array('wrap' => 48)
19496                        );
19497                    } elseif ($count == 3) {
19498                        $opts = array(0 => array('wrap' => 30),
19499                                      1 => array('wrap' => 20),
19500                                      2 => array('wrap' => 35)
19501                        );
19502                    } else {
19503                        $opts = null;
19504                    }
19505                    if (isset($data['headline']) && is_array($data['headline'])) {
19506                        $this->_tableRow($data['headline'],
19507                                         array('bold' => true),
19508                                         $opts);
19509                    }
19510
19511                    if (is_array($data['data'])) {
19512                        foreach($data['data'] as $row) {
19513                            $this->_tableRow($row, null, $opts);
19514                        }
19515                    } else {
19516                        $this->_tableRow(array($data['data']), null, $opts);
19517                     }
19518                    $this->_endTable();
19519                } else {
19520                    $this->_displayLine($data);
19521                }
19522            }
19523        }
19524    }
19525
19526    function log($text, $append_crlf = true)
19527    {
19528        if ($append_crlf) {
19529            return $this->_displayLine($text);
19530        }
19531
19532        return $this->_display($text);
19533    }
19534
19535    function bold($text)
19536    {
19537        if (empty($this->term['bold'])) {
19538            return strtoupper($text);
19539        }
19540
19541        return $this->term['bold'] . $text . $this->term['normal'];
19542    }
19543
19544    function _displayHeading($title)
19545    {
19546        print $this->lp.$this->bold($title)."\n";
19547        print $this->lp.str_repeat("=", strlen($title))."\n";
19548    }
19549
19550    function _startTable($params = array())
19551    {
19552        $params['table_data'] = array();
19553        $params['widest']     = array();  // indexed by column
19554        $params['highest']    = array(); // indexed by row
19555        $params['ncols']      = 0;
19556        $this->params         = $params;
19557    }
19558
19559    function _tableRow($columns, $rowparams = array(), $colparams = array())
19560    {
19561        $highest = 1;
19562        for ($i = 0; $i < count($columns); $i++) {
19563            $col = &$columns[$i];
19564            if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) {
19565                $col = wordwrap($col, $colparams[$i]['wrap']);
19566            }
19567
19568            if (strpos($col, "\n") !== false) {
19569                $multiline = explode("\n", $col);
19570                $w = 0;
19571                foreach ($multiline as $n => $line) {
19572                    $len = strlen($line);
19573                    if ($len > $w) {
19574                        $w = $len;
19575                    }
19576                }
19577                $lines = count($multiline);
19578            } else {
19579                $w = strlen($col);
19580            }
19581
19582            if (isset($this->params['widest'][$i])) {
19583                if ($w > $this->params['widest'][$i]) {
19584                    $this->params['widest'][$i] = $w;
19585                }
19586            } else {
19587                $this->params['widest'][$i] = $w;
19588            }
19589
19590            $tmp = count_chars($columns[$i], 1);
19591            // handle unix, mac and windows formats
19592            $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1;
19593            if ($lines > $highest) {
19594                $highest = $lines;
19595            }
19596        }
19597
19598        if (count($columns) > $this->params['ncols']) {
19599            $this->params['ncols'] = count($columns);
19600        }
19601
19602        $new_row = array(
19603            'data'      => $columns,
19604            'height'    => $highest,
19605            'rowparams' => $rowparams,
19606            'colparams' => $colparams,
19607        );
19608        $this->params['table_data'][] = $new_row;
19609    }
19610
19611    function _endTable()
19612    {
19613        extract($this->params);
19614        if (!empty($caption)) {
19615            $this->_displayHeading($caption);
19616        }
19617
19618        if (count($table_data) === 0) {
19619            return;
19620        }
19621
19622        if (!isset($width)) {
19623            $width = $widest;
19624        } else {
19625            for ($i = 0; $i < $ncols; $i++) {
19626                if (!isset($width[$i])) {
19627                    $width[$i] = $widest[$i];
19628                }
19629            }
19630        }
19631
19632        $border = false;
19633        if (empty($border)) {
19634            $cellstart  = '';
19635            $cellend    = ' ';
19636            $rowend     = '';
19637            $padrowend  = false;
19638            $borderline = '';
19639        } else {
19640            $cellstart  = '| ';
19641            $cellend    = ' ';
19642            $rowend     = '|';
19643            $padrowend  = true;
19644            $borderline = '+';
19645            foreach ($width as $w) {
19646                $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1);
19647                $borderline .= '+';
19648            }
19649        }
19650
19651        if ($borderline) {
19652            $this->_displayLine($borderline);
19653        }
19654
19655        for ($i = 0; $i < count($table_data); $i++) {
19656            extract($table_data[$i]);
19657            if (!is_array($rowparams)) {
19658                $rowparams = array();
19659            }
19660
19661            if (!is_array($colparams)) {
19662                $colparams = array();
19663            }
19664
19665            $rowlines = array();
19666            if ($height > 1) {
19667                for ($c = 0; $c < count($data); $c++) {
19668                    $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]);
19669                    if (count($rowlines[$c]) < $height) {
19670                        $rowlines[$c] = array_pad($rowlines[$c], $height, '');
19671                    }
19672                }
19673            } else {
19674                for ($c = 0; $c < count($data); $c++) {
19675                    $rowlines[$c] = array($data[$c]);
19676                }
19677            }
19678
19679            for ($r = 0; $r < $height; $r++) {
19680                $rowtext = '';
19681                for ($c = 0; $c < count($data); $c++) {
19682                    if (isset($colparams[$c])) {
19683                        $attribs = array_merge($rowparams, $colparams);
19684                    } else {
19685                        $attribs = $rowparams;
19686                    }
19687
19688                    $w = isset($width[$c]) ? $width[$c] : 0;
19689                    //$cell = $data[$c];
19690                    $cell = $rowlines[$c][$r];
19691                    $l = strlen($cell);
19692                    if ($l > $w) {
19693                        $cell = substr($cell, 0, $w);
19694                    }
19695
19696                    if (isset($attribs['bold'])) {
19697                        $cell = $this->bold($cell);
19698                    }
19699
19700                    if ($l < $w) {
19701                        // not using str_pad here because we may
19702                        // add bold escape characters to $cell
19703                        $cell .= str_repeat(' ', $w - $l);
19704                    }
19705
19706                    $rowtext .= $cellstart . $cell . $cellend;
19707                }
19708
19709                if (!$border) {
19710                    $rowtext = rtrim($rowtext);
19711                }
19712
19713                $rowtext .= $rowend;
19714                $this->_displayLine($rowtext);
19715            }
19716        }
19717
19718        if ($borderline) {
19719            $this->_displayLine($borderline);
19720        }
19721    }
19722
19723    function _displayLine($text)
19724    {
19725        print "$this->lp$text\n";
19726    }
19727
19728    function _display($text)
19729    {
19730        print $text;
19731    }
19732}PEAR-1.9.4/PEAR/Installer/Role/Common.php0000644000076500000240000001423311605156614016565 0ustar  helgistaff<?php
19733/**
19734 * Base class for all installation roles.
19735 *
19736 * PHP versions 4 and 5
19737 *
19738 * @category   pear
19739 * @package    PEAR
19740 * @author     Greg Beaver <cellog@php.net>
19741 * @copyright  1997-2006 The PHP Group
19742 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
19743 * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
19744 * @link       http://pear.php.net/package/PEAR
19745 * @since      File available since Release 1.4.0a1
19746 */
19747/**
19748 * Base class for all installation roles.
19749 *
19750 * This class allows extensibility of file roles.  Packages with complex
19751 * customization can now provide custom file roles along with the possibility of
19752 * adding configuration values to match.
19753 * @category   pear
19754 * @package    PEAR
19755 * @author     Greg Beaver <cellog@php.net>
19756 * @copyright  1997-2006 The PHP Group
19757 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
19758 * @version    Release: 1.9.4
19759 * @link       http://pear.php.net/package/PEAR
19760 * @since      Class available since Release 1.4.0a1
19761 */
19762class PEAR_Installer_Role_Common
19763{
19764    /**
19765     * @var PEAR_Config
19766     * @access protected
19767     */
19768    var $config;
19769
19770    /**
19771     * @param PEAR_Config
19772     */
19773    function PEAR_Installer_Role_Common(&$config)
19774    {
19775        $this->config = $config;
19776    }
19777
19778    /**
19779     * Retrieve configuration information about a file role from its XML info
19780     *
19781     * @param string $role Role Classname, as in "PEAR_Installer_Role_Data"
19782     * @return array
19783     */
19784    function getInfo($role)
19785    {
19786        if (empty($GLOBALS['_PEAR_INSTALLER_ROLES'][$role])) {
19787            return PEAR::raiseError('Unknown Role class: "' . $role . '"');
19788        }
19789        return $GLOBALS['_PEAR_INSTALLER_ROLES'][$role];
19790    }
19791
19792    /**
19793     * This is called for each file to set up the directories and files
19794     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
19795     * @param array attributes from the <file> tag
19796     * @param string file name
19797     * @return array an array consisting of:
19798     *
19799     *    1 the original, pre-baseinstalldir installation directory
19800     *    2 the final installation directory
19801     *    3 the full path to the final location of the file
19802     *    4 the location of the pre-installation file
19803     */
19804    function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
19805    {
19806        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
19807            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
19808        if (PEAR::isError($roleInfo)) {
19809            return $roleInfo;
19810        }
19811        if (!$roleInfo['locationconfig']) {
19812            return false;
19813        }
19814        if ($roleInfo['honorsbaseinstall']) {
19815            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'], $layer,
19816                $pkg->getChannel());
19817            if (!empty($atts['baseinstalldir'])) {
19818                $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
19819            }
19820        } elseif ($roleInfo['unusualbaseinstall']) {
19821            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
19822                    $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
19823            if (!empty($atts['baseinstalldir'])) {
19824                $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
19825            }
19826        } else {
19827            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
19828                    $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
19829        }
19830        if (dirname($file) != '.' && empty($atts['install-as'])) {
19831            $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
19832        }
19833        if (empty($atts['install-as'])) {
19834            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
19835        } else {
19836            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
19837        }
19838        $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
19839
19840        // Clean up the DIRECTORY_SEPARATOR mess
19841        $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
19842        
19843        list($dest_dir, $dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
19844                                                    array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR,
19845                                                          DIRECTORY_SEPARATOR),
19846                                                    array($dest_dir, $dest_file, $orig_file));
19847        return array($save_destdir, $dest_dir, $dest_file, $orig_file);
19848    }
19849
19850    /**
19851     * Get the name of the configuration variable that specifies the location of this file
19852     * @return string|false
19853     */
19854    function getLocationConfig()
19855    {
19856        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
19857            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
19858        if (PEAR::isError($roleInfo)) {
19859            return $roleInfo;
19860        }
19861        return $roleInfo['locationconfig'];
19862    }
19863
19864    /**
19865     * Do any unusual setup here
19866     * @param PEAR_Installer
19867     * @param PEAR_PackageFile_v2
19868     * @param array file attributes
19869     * @param string file name
19870     */
19871    function setup(&$installer, $pkg, $atts, $file)
19872    {
19873    }
19874
19875    function isExecutable()
19876    {
19877        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
19878            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
19879        if (PEAR::isError($roleInfo)) {
19880            return $roleInfo;
19881        }
19882        return $roleInfo['executable'];
19883    }
19884
19885    function isInstallable()
19886    {
19887        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
19888            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
19889        if (PEAR::isError($roleInfo)) {
19890            return $roleInfo;
19891        }
19892        return $roleInfo['installable'];
19893    }
19894
19895    function isExtension()
19896    {
19897        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
19898            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
19899        if (PEAR::isError($roleInfo)) {
19900            return $roleInfo;
19901        }
19902        return $roleInfo['phpextension'];
19903    }
19904}
19905?>PEAR-1.9.4/PEAR/Installer/Role/Cfg.xml0000644000076500000240000000064511605156614016047 0ustar  helgistaff<role version="1.0">
19906 <releasetypes>php</releasetypes>
19907 <releasetypes>extsrc</releasetypes>
19908 <releasetypes>extbin</releasetypes>
19909 <releasetypes>zendextsrc</releasetypes>
19910 <releasetypes>zendextbin</releasetypes>
19911 <installable>1</installable>
19912 <locationconfig>cfg_dir</locationconfig>
19913 <honorsbaseinstall />
19914 <unusualbaseinstall>1</unusualbaseinstall>
19915 <phpfile />
19916 <executable />
19917 <phpextension />
19918 <config_vars />
19919</role>PEAR-1.9.4/PEAR/Installer/Role/Cfg.php0000644000076500000240000000770211605156614016037 0ustar  helgistaff<?php
19920/**
19921 * PEAR_Installer_Role_Cfg
19922 *
19923 * PHP versions 4 and 5
19924 *
19925 * @category   pear
19926 * @package    PEAR
19927 * @author     Greg Beaver <cellog@php.net>
19928 * @copyright  2007-2009 The Authors
19929 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
19930 * @version    CVS: $Id: Cfg.php 313023 2011-07-06 19:17:11Z dufuz $
19931 * @link       http://pear.php.net/package/PEAR
19932 * @since      File available since Release 1.7.0
19933 */
19934
19935/**
19936 * @category   pear
19937 * @package    PEAR
19938 * @author     Greg Beaver <cellog@php.net>
19939 * @copyright  2007-2009 The Authors
19940 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
19941 * @version    Release: 1.9.4
19942 * @link       http://pear.php.net/package/PEAR
19943 * @since      Class available since Release 1.7.0
19944 */
19945class PEAR_Installer_Role_Cfg extends PEAR_Installer_Role_Common
19946{
19947    /**
19948     * @var PEAR_Installer
19949     */
19950    var $installer;
19951
19952    /**
19953     * the md5 of the original file
19954     *
19955     * @var unknown_type
19956     */
19957    var $md5 = null;
19958
19959    /**
19960     * Do any unusual setup here
19961     * @param PEAR_Installer
19962     * @param PEAR_PackageFile_v2
19963     * @param array file attributes
19964     * @param string file name
19965     */
19966    function setup(&$installer, $pkg, $atts, $file)
19967    {
19968        $this->installer = &$installer;
19969        $reg = &$this->installer->config->getRegistry();
19970        $package = $reg->getPackage($pkg->getPackage(), $pkg->getChannel());
19971        if ($package) {
19972            $filelist = $package->getFilelist();
19973            if (isset($filelist[$file]) && isset($filelist[$file]['md5sum'])) {
19974                $this->md5 = $filelist[$file]['md5sum'];
19975            }
19976        }
19977    }
19978
19979    function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
19980    {
19981        $test = parent::processInstallation($pkg, $atts, $file, $tmp_path, $layer);
19982        if (@file_exists($test[2]) && @file_exists($test[3])) {
19983            $md5 = md5_file($test[2]);
19984            // configuration has already been installed, check for mods
19985            if ($md5 !== $this->md5 && $md5 !== md5_file($test[3])) {
19986                // configuration has been modified, so save our version as
19987                // configfile-version
19988                $old = $test[2];
19989                $test[2] .= '.new-' . $pkg->getVersion();
19990                // backup original and re-install it
19991                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
19992                $tmpcfg = $this->config->get('temp_dir');
19993                $newloc = System::mkdir(array('-p', $tmpcfg));
19994                if (!$newloc) {
19995                    // try temp_dir
19996                    $newloc = System::mktemp(array('-d'));
19997                    if (!$newloc || PEAR::isError($newloc)) {
19998                        PEAR::popErrorHandling();
19999                        return PEAR::raiseError('Could not save existing configuration file '.
20000                            $old . ', unable to install.  Please set temp_dir ' .
20001                            'configuration variable to a writeable location and try again');
20002                    }
20003                } else {
20004                    $newloc = $tmpcfg;
20005                }
20006
20007                $temp_file = $newloc . DIRECTORY_SEPARATOR . uniqid('savefile');
20008                if (!@copy($old, $temp_file)) {
20009                    PEAR::popErrorHandling();
20010                    return PEAR::raiseError('Could not save existing configuration file '.
20011                        $old . ', unable to install.  Please set temp_dir ' .
20012                        'configuration variable to a writeable location and try again');
20013                }
20014
20015                PEAR::popErrorHandling();
20016                $this->installer->log(0, "WARNING: configuration file $old is being installed as $test[2], you should manually merge in changes to the existing configuration file");
20017                $this->installer->addFileOperation('rename', array($temp_file, $old, false));
20018                $this->installer->addFileOperation('delete', array($temp_file));
20019            }
20020        }
20021
20022        return $test;
20023    }
20024}PEAR-1.9.4/PEAR/Installer/Role/Data.xml0000644000076500000240000000062211605156614016214 0ustar  helgistaff<role version="1.0">
20025 <releasetypes>php</releasetypes>
20026 <releasetypes>extsrc</releasetypes>
20027 <releasetypes>extbin</releasetypes>
20028 <releasetypes>zendextsrc</releasetypes>
20029 <releasetypes>zendextbin</releasetypes>
20030 <installable>1</installable>
20031 <locationconfig>data_dir</locationconfig>
20032 <honorsbaseinstall />
20033 <unusualbaseinstall />
20034 <phpfile />
20035 <executable />
20036 <phpextension />
20037 <config_vars />
20038</role>PEAR-1.9.4/PEAR/Installer/Role/Data.php0000644000076500000240000000152311605156614016204 0ustar  helgistaff<?php
20039/**
20040 * PEAR_Installer_Role_Data
20041 *
20042 * PHP versions 4 and 5
20043 *
20044 * @category   pear
20045 * @package    PEAR
20046 * @author     Greg Beaver <cellog@php.net>
20047 * @copyright  1997-2009 The Authors
20048 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20049 * @version    CVS: $Id: Data.php 313023 2011-07-06 19:17:11Z dufuz $
20050 * @link       http://pear.php.net/package/PEAR
20051 * @since      File available since Release 1.4.0a1
20052 */
20053
20054/**
20055 * @category   pear
20056 * @package    PEAR
20057 * @author     Greg Beaver <cellog@php.net>
20058 * @copyright  1997-2009 The Authors
20059 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20060 * @version    Release: 1.9.4
20061 * @link       http://pear.php.net/package/PEAR
20062 * @since      Class available since Release 1.4.0a1
20063 */
20064class PEAR_Installer_Role_Data extends PEAR_Installer_Role_Common {}
20065?>PEAR-1.9.4/PEAR/Installer/Role/Doc.xml0000644000076500000240000000062111605156614016047 0ustar  helgistaff<role version="1.0">
20066 <releasetypes>php</releasetypes>
20067 <releasetypes>extsrc</releasetypes>
20068 <releasetypes>extbin</releasetypes>
20069 <releasetypes>zendextsrc</releasetypes>
20070 <releasetypes>zendextbin</releasetypes>
20071 <installable>1</installable>
20072 <locationconfig>doc_dir</locationconfig>
20073 <honorsbaseinstall />
20074 <unusualbaseinstall />
20075 <phpfile />
20076 <executable />
20077 <phpextension />
20078 <config_vars />
20079</role>PEAR-1.9.4/PEAR/Installer/Role/Doc.php0000644000076500000240000000152011605156614016035 0ustar  helgistaff<?php
20080/**
20081 * PEAR_Installer_Role_Doc
20082 *
20083 * PHP versions 4 and 5
20084 *
20085 * @category   pear
20086 * @package    PEAR
20087 * @author     Greg Beaver <cellog@php.net>
20088 * @copyright  1997-2009 The Authors
20089 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20090 * @version    CVS: $Id: Doc.php 313023 2011-07-06 19:17:11Z dufuz $
20091 * @link       http://pear.php.net/package/PEAR
20092 * @since      File available since Release 1.4.0a1
20093 */
20094
20095/**
20096 * @category   pear
20097 * @package    PEAR
20098 * @author     Greg Beaver <cellog@php.net>
20099 * @copyright  1997-2009 The Authors
20100 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20101 * @version    Release: 1.9.4
20102 * @link       http://pear.php.net/package/PEAR
20103 * @since      Class available since Release 1.4.0a1
20104 */
20105class PEAR_Installer_Role_Doc extends PEAR_Installer_Role_Common {}
20106?>PEAR-1.9.4/PEAR/Installer/Role/Ext.xml0000644000076500000240000000050211605156614016100 0ustar  helgistaff<role version="1.0">
20107 <releasetypes>extbin</releasetypes>
20108 <releasetypes>zendextbin</releasetypes>
20109 <installable>1</installable>
20110 <locationconfig>ext_dir</locationconfig>
20111 <honorsbaseinstall>1</honorsbaseinstall>
20112 <unusualbaseinstall />
20113 <phpfile />
20114 <executable />
20115 <phpextension>1</phpextension>
20116 <config_vars />
20117</role>PEAR-1.9.4/PEAR/Installer/Role/Ext.php0000644000076500000240000000152011605156614016070 0ustar  helgistaff<?php
20118/**
20119 * PEAR_Installer_Role_Ext
20120 *
20121 * PHP versions 4 and 5
20122 *
20123 * @category   pear
20124 * @package    PEAR
20125 * @author     Greg Beaver <cellog@php.net>
20126 * @copyright  1997-2009 The Authors
20127 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20128 * @version    CVS: $Id: Ext.php 313023 2011-07-06 19:17:11Z dufuz $
20129 * @link       http://pear.php.net/package/PEAR
20130 * @since      File available since Release 1.4.0a1
20131 */
20132
20133/**
20134 * @category   pear
20135 * @package    PEAR
20136 * @author     Greg Beaver <cellog@php.net>
20137 * @copyright  1997-2009 The Authors
20138 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20139 * @version    Release: 1.9.4
20140 * @link       http://pear.php.net/package/PEAR
20141 * @since      Class available since Release 1.4.0a1
20142 */
20143class PEAR_Installer_Role_Ext extends PEAR_Installer_Role_Common {}
20144?>PEAR-1.9.4/PEAR/Installer/Role/Php.xml0000644000076500000240000000065511605156614016100 0ustar  helgistaff<role version="1.0">
20145 <releasetypes>php</releasetypes>
20146 <releasetypes>extsrc</releasetypes>
20147 <releasetypes>extbin</releasetypes>
20148 <releasetypes>zendextsrc</releasetypes>
20149 <releasetypes>zendextbin</releasetypes>
20150 <installable>1</installable>
20151 <locationconfig>php_dir</locationconfig>
20152 <honorsbaseinstall>1</honorsbaseinstall>
20153 <unusualbaseinstall />
20154 <phpfile>1</phpfile>
20155 <executable />
20156 <phpextension />
20157 <config_vars />
20158</role>PEAR-1.9.4/PEAR/Installer/Role/Php.php0000644000076500000240000000152011605156614016057 0ustar  helgistaff<?php
20159/**
20160 * PEAR_Installer_Role_Php
20161 *
20162 * PHP versions 4 and 5
20163 *
20164 * @category   pear
20165 * @package    PEAR
20166 * @author     Greg Beaver <cellog@php.net>
20167 * @copyright  1997-2009 The Authors
20168 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20169 * @version    CVS: $Id: Php.php 313023 2011-07-06 19:17:11Z dufuz $
20170 * @link       http://pear.php.net/package/PEAR
20171 * @since      File available since Release 1.4.0a1
20172 */
20173
20174/**
20175 * @category   pear
20176 * @package    PEAR
20177 * @author     Greg Beaver <cellog@php.net>
20178 * @copyright  1997-2009 The Authors
20179 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20180 * @version    Release: 1.9.4
20181 * @link       http://pear.php.net/package/PEAR
20182 * @since      Class available since Release 1.4.0a1
20183 */
20184class PEAR_Installer_Role_Php extends PEAR_Installer_Role_Common {}
20185?>PEAR-1.9.4/PEAR/Installer/Role/Script.xml0000644000076500000240000000066011605156614016611 0ustar  helgistaff<role version="1.0">
20186 <releasetypes>php</releasetypes>
20187 <releasetypes>extsrc</releasetypes>
20188 <releasetypes>extbin</releasetypes>
20189 <releasetypes>zendextsrc</releasetypes>
20190 <releasetypes>zendextbin</releasetypes>
20191 <installable>1</installable>
20192 <locationconfig>bin_dir</locationconfig>
20193 <honorsbaseinstall>1</honorsbaseinstall>
20194 <unusualbaseinstall />
20195 <phpfile />
20196 <executable>1</executable>
20197 <phpextension />
20198 <config_vars />
20199</role>PEAR-1.9.4/PEAR/Installer/Role/Script.php0000644000076500000240000000153111605156614016576 0ustar  helgistaff<?php
20200/**
20201 * PEAR_Installer_Role_Script
20202 *
20203 * PHP versions 4 and 5
20204 *
20205 * @category   pear
20206 * @package    PEAR
20207 * @author     Greg Beaver <cellog@php.net>
20208 * @copyright  1997-2009 The Authors
20209 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20210 * @version    CVS: $Id: Script.php 313023 2011-07-06 19:17:11Z dufuz $
20211 * @link       http://pear.php.net/package/PEAR
20212 * @since      File available since Release 1.4.0a1
20213 */
20214
20215/**
20216 * @category   pear
20217 * @package    PEAR
20218 * @author     Greg Beaver <cellog@php.net>
20219 * @copyright  1997-2009 The Authors
20220 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20221 * @version    Release: 1.9.4
20222 * @link       http://pear.php.net/package/PEAR
20223 * @since      Class available since Release 1.4.0a1
20224 */
20225class PEAR_Installer_Role_Script extends PEAR_Installer_Role_Common {}
20226?>PEAR-1.9.4/PEAR/Installer/Role/Src.xml0000644000076500000240000000044211605156614016072 0ustar  helgistaff<role version="1.0">
20227 <releasetypes>extsrc</releasetypes>
20228 <releasetypes>zendextsrc</releasetypes>
20229 <installable>1</installable>
20230 <locationconfig>temp_dir</locationconfig>
20231 <honorsbaseinstall />
20232 <unusualbaseinstall />
20233 <phpfile />
20234 <executable />
20235 <phpextension />
20236 <config_vars />
20237</role>PEAR-1.9.4/PEAR/Installer/Role/Src.php0000644000076500000240000000166511605156614016071 0ustar  helgistaff<?php
20238/**
20239 * PEAR_Installer_Role_Src
20240 *
20241 * PHP versions 4 and 5
20242 *
20243 * @category   pear
20244 * @package    PEAR
20245 * @author     Greg Beaver <cellog@php.net>
20246 * @copyright  1997-2009 The Authors
20247 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20248 * @version    CVS: $Id: Src.php 313023 2011-07-06 19:17:11Z dufuz $
20249 * @link       http://pear.php.net/package/PEAR
20250 * @since      File available since Release 1.4.0a1
20251 */
20252
20253/**
20254 * @category   pear
20255 * @package    PEAR
20256 * @author     Greg Beaver <cellog@php.net>
20257 * @copyright  1997-2009 The Authors
20258 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20259 * @version    Release: 1.9.4
20260 * @link       http://pear.php.net/package/PEAR
20261 * @since      Class available since Release 1.4.0a1
20262 */
20263class PEAR_Installer_Role_Src extends PEAR_Installer_Role_Common
20264{
20265    function setup(&$installer, $pkg, $atts, $file)
20266    {
20267        $installer->source_files++;
20268    }
20269}
20270?>PEAR-1.9.4/PEAR/Installer/Role/Test.xml0000644000076500000240000000062211605156614016262 0ustar  helgistaff<role version="1.0">
20271 <releasetypes>php</releasetypes>
20272 <releasetypes>extsrc</releasetypes>
20273 <releasetypes>extbin</releasetypes>
20274 <releasetypes>zendextsrc</releasetypes>
20275 <releasetypes>zendextbin</releasetypes>
20276 <installable>1</installable>
20277 <locationconfig>test_dir</locationconfig>
20278 <honorsbaseinstall />
20279 <unusualbaseinstall />
20280 <phpfile />
20281 <executable />
20282 <phpextension />
20283 <config_vars />
20284</role>PEAR-1.9.4/PEAR/Installer/Role/Test.php0000644000076500000240000000152311605156614016252 0ustar  helgistaff<?php
20285/**
20286 * PEAR_Installer_Role_Test
20287 *
20288 * PHP versions 4 and 5
20289 *
20290 * @category   pear
20291 * @package    PEAR
20292 * @author     Greg Beaver <cellog@php.net>
20293 * @copyright  1997-2009 The Authors
20294 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20295 * @version    CVS: $Id: Test.php 313023 2011-07-06 19:17:11Z dufuz $
20296 * @link       http://pear.php.net/package/PEAR
20297 * @since      File available since Release 1.4.0a1
20298 */
20299
20300/**
20301 * @category   pear
20302 * @package    PEAR
20303 * @author     Greg Beaver <cellog@php.net>
20304 * @copyright  1997-2009 The Authors
20305 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20306 * @version    Release: 1.9.4
20307 * @link       http://pear.php.net/package/PEAR
20308 * @since      Class available since Release 1.4.0a1
20309 */
20310class PEAR_Installer_Role_Test extends PEAR_Installer_Role_Common {}
20311?>PEAR-1.9.4/PEAR/Installer/Role/Www.xml0000644000076500000240000000064411605156614016133 0ustar  helgistaff<role version="1.0">
20312 <releasetypes>php</releasetypes>
20313 <releasetypes>extsrc</releasetypes>
20314 <releasetypes>extbin</releasetypes>
20315 <releasetypes>zendextsrc</releasetypes>
20316 <releasetypes>zendextbin</releasetypes>
20317 <installable>1</installable>
20318 <locationconfig>www_dir</locationconfig>
20319 <honorsbaseinstall>1</honorsbaseinstall>
20320 <unusualbaseinstall />
20321 <phpfile />
20322 <executable />
20323 <phpextension />
20324 <config_vars />
20325</role>PEAR-1.9.4/PEAR/Installer/Role/Www.php0000644000076500000240000000151411605156614016117 0ustar  helgistaff<?php
20326/**
20327 * PEAR_Installer_Role_Www
20328 *
20329 * PHP versions 4 and 5
20330 *
20331 * @category   pear
20332 * @package    PEAR
20333 * @author     Greg Beaver <cellog@php.net>
20334 * @copyright  2007-2009 The Authors
20335 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20336 * @version    CVS: $Id: Www.php 313023 2011-07-06 19:17:11Z dufuz $
20337 * @link       http://pear.php.net/package/PEAR
20338 * @since      File available since Release 1.7.0
20339 */
20340
20341/**
20342 * @category   pear
20343 * @package    PEAR
20344 * @author     Greg Beaver <cellog@php.net>
20345 * @copyright  2007-2009 The Authors
20346 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20347 * @version    Release: 1.9.4
20348 * @link       http://pear.php.net/package/PEAR
20349 * @since      Class available since Release 1.7.0
20350 */
20351class PEAR_Installer_Role_Www extends PEAR_Installer_Role_Common {}
20352?>PEAR-1.9.4/PEAR/Installer/Role.php0000644000076500000240000001746411605156614015346 0ustar  helgistaff<?php
20353/**
20354 * PEAR_Installer_Role
20355 *
20356 * PHP versions 4 and 5
20357 *
20358 * @category   pear
20359 * @package    PEAR
20360 * @author     Greg Beaver <cellog@php.net>
20361 * @copyright  1997-2009 The Authors
20362 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20363 * @version    CVS: $Id: Role.php 313023 2011-07-06 19:17:11Z dufuz $
20364 * @link       http://pear.php.net/package/PEAR
20365 * @since      File available since Release 1.4.0a1
20366 */
20367
20368/**
20369 * base class for installer roles
20370 */
20371require_once 'PEAR/Installer/Role/Common.php';
20372require_once 'PEAR/XMLParser.php';
20373/**
20374 * @category   pear
20375 * @package    PEAR
20376 * @author     Greg Beaver <cellog@php.net>
20377 * @copyright  1997-2009 The Authors
20378 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20379 * @version    Release: 1.9.4
20380 * @link       http://pear.php.net/package/PEAR
20381 * @since      Class available since Release 1.4.0a1
20382 */
20383class PEAR_Installer_Role
20384{
20385    /**
20386     * Set up any additional configuration variables that file roles require
20387     *
20388     * Never call this directly, it is called by the PEAR_Config constructor
20389     * @param PEAR_Config
20390     * @access private
20391     * @static
20392     */
20393    function initializeConfig(&$config)
20394    {
20395        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
20396            PEAR_Installer_Role::registerRoles();
20397        }
20398
20399        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $class => $info) {
20400            if (!$info['config_vars']) {
20401                continue;
20402            }
20403
20404            $config->_addConfigVars($class, $info['config_vars']);
20405        }
20406    }
20407
20408    /**
20409     * @param PEAR_PackageFile_v2
20410     * @param string role name
20411     * @param PEAR_Config
20412     * @return PEAR_Installer_Role_Common
20413     * @static
20414     */
20415    function &factory($pkg, $role, &$config)
20416    {
20417        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
20418            PEAR_Installer_Role::registerRoles();
20419        }
20420
20421        if (!in_array($role, PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
20422            $a = false;
20423            return $a;
20424        }
20425
20426        $a = 'PEAR_Installer_Role_' . ucfirst($role);
20427        if (!class_exists($a)) {
20428            require_once str_replace('_', '/', $a) . '.php';
20429        }
20430
20431        $b = new $a($config);
20432        return $b;
20433    }
20434
20435    /**
20436     * Get a list of file roles that are valid for the particular release type.
20437     *
20438     * For instance, src files serve no purpose in regular php releases.
20439     * @param string
20440     * @param bool clear cache
20441     * @return array
20442     * @static
20443     */
20444    function getValidRoles($release, $clear = false)
20445    {
20446        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
20447            PEAR_Installer_Role::registerRoles();
20448        }
20449
20450        static $ret = array();
20451        if ($clear) {
20452            $ret = array();
20453        }
20454
20455        if (isset($ret[$release])) {
20456            return $ret[$release];
20457        }
20458
20459        $ret[$release] = array();
20460        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
20461            if (in_array($release, $okreleases['releasetypes'])) {
20462                $ret[$release][] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
20463            }
20464        }
20465
20466        return $ret[$release];
20467    }
20468
20469    /**
20470     * Get a list of roles that require their files to be installed
20471     *
20472     * Most roles must be installed, but src and package roles, for instance
20473     * are pseudo-roles.  src files are compiled into a new extension.  Package
20474     * roles are actually fully bundled releases of a package
20475     * @param bool clear cache
20476     * @return array
20477     * @static
20478     */
20479    function getInstallableRoles($clear = false)
20480    {
20481        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
20482            PEAR_Installer_Role::registerRoles();
20483        }
20484
20485        static $ret;
20486        if ($clear) {
20487            unset($ret);
20488        }
20489
20490        if (isset($ret)) {
20491            return $ret;
20492        }
20493
20494        $ret = array();
20495        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
20496            if ($okreleases['installable']) {
20497                $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
20498            }
20499        }
20500
20501        return $ret;
20502    }
20503
20504    /**
20505     * Return an array of roles that are affected by the baseinstalldir attribute
20506     *
20507     * Most roles ignore this attribute, and instead install directly into:
20508     * PackageName/filepath
20509     * so a tests file tests/file.phpt is installed into PackageName/tests/filepath.php
20510     * @param bool clear cache
20511     * @return array
20512     * @static
20513     */
20514    function getBaseinstallRoles($clear = false)
20515    {
20516        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
20517            PEAR_Installer_Role::registerRoles();
20518        }
20519
20520        static $ret;
20521        if ($clear) {
20522            unset($ret);
20523        }
20524
20525        if (isset($ret)) {
20526            return $ret;
20527        }
20528
20529        $ret = array();
20530        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
20531            if ($okreleases['honorsbaseinstall']) {
20532                $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
20533            }
20534        }
20535
20536        return $ret;
20537    }
20538
20539    /**
20540     * Return an array of file roles that should be analyzed for PHP content at package time,
20541     * like the "php" role.
20542     * @param bool clear cache
20543     * @return array
20544     * @static
20545     */
20546    function getPhpRoles($clear = false)
20547    {
20548        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
20549            PEAR_Installer_Role::registerRoles();
20550        }
20551
20552        static $ret;
20553        if ($clear) {
20554            unset($ret);
20555        }
20556
20557        if (isset($ret)) {
20558            return $ret;
20559        }
20560
20561        $ret = array();
20562        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
20563            if ($okreleases['phpfile']) {
20564                $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
20565            }
20566        }
20567
20568        return $ret;
20569    }
20570
20571    /**
20572     * Scan through the Command directory looking for classes
20573     * and see what commands they implement.
20574     * @param string which directory to look for classes, defaults to
20575     *               the Installer/Roles subdirectory of
20576     *               the directory from where this file (__FILE__) is
20577     *               included.
20578     *
20579     * @return bool TRUE on success, a PEAR error on failure
20580     * @access public
20581     * @static
20582     */
20583    function registerRoles($dir = null)
20584    {
20585        $GLOBALS['_PEAR_INSTALLER_ROLES'] = array();
20586        $parser = new PEAR_XMLParser;
20587        if ($dir === null) {
20588            $dir = dirname(__FILE__) . '/Role';
20589        }
20590
20591        if (!file_exists($dir) || !is_dir($dir)) {
20592            return PEAR::raiseError("registerRoles: opendir($dir) failed: does not exist/is not directory");
20593        }
20594
20595        $dp = @opendir($dir);
20596        if (empty($dp)) {
20597            return PEAR::raiseError("registerRoles: opendir($dir) failed: $php_errmsg");
20598        }
20599
20600        while ($entry = readdir($dp)) {
20601            if ($entry{0} == '.' || substr($entry, -4) != '.xml') {
20602                continue;
20603            }
20604
20605            $class = "PEAR_Installer_Role_".substr($entry, 0, -4);
20606            // List of roles
20607            if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'][$class])) {
20608                $file = "$dir/$entry";
20609                $parser->parse(file_get_contents($file));
20610                $data = $parser->getData();
20611                if (!is_array($data['releasetypes'])) {
20612                    $data['releasetypes'] = array($data['releasetypes']);
20613                }
20614
20615                $GLOBALS['_PEAR_INSTALLER_ROLES'][$class] = $data;
20616            }
20617        }
20618
20619        closedir($dp);
20620        ksort($GLOBALS['_PEAR_INSTALLER_ROLES']);
20621        PEAR_Installer_Role::getBaseinstallRoles(true);
20622        PEAR_Installer_Role::getInstallableRoles(true);
20623        PEAR_Installer_Role::getPhpRoles(true);
20624        PEAR_Installer_Role::getValidRoles('****', true);
20625        return true;
20626    }
20627}PEAR-1.9.4/PEAR/PackageFile/Generator/v1.php0000644000076500000240000014234211605156614017111 0ustar  helgistaff<?php
20628/**
20629 * package.xml generation class, package.xml version 1.0
20630 *
20631 * PHP versions 4 and 5
20632 *
20633 * @category   pear
20634 * @package    PEAR
20635 * @author     Greg Beaver <cellog@php.net>
20636 * @copyright  1997-2009 The Authors
20637 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20638 * @version    CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $
20639 * @link       http://pear.php.net/package/PEAR
20640 * @since      File available since Release 1.4.0a1
20641 */
20642/**
20643 * needed for PEAR_VALIDATE_* constants
20644 */
20645require_once 'PEAR/Validate.php';
20646require_once 'System.php';
20647require_once 'PEAR/PackageFile/v2.php';
20648/**
20649 * This class converts a PEAR_PackageFile_v1 object into any output format.
20650 *
20651 * Supported output formats include array, XML string, and a PEAR_PackageFile_v2
20652 * object, for converting package.xml 1.0 into package.xml 2.0 with no sweat.
20653 * @category   pear
20654 * @package    PEAR
20655 * @author     Greg Beaver <cellog@php.net>
20656 * @copyright  1997-2009 The Authors
20657 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
20658 * @version    Release: 1.9.4
20659 * @link       http://pear.php.net/package/PEAR
20660 * @since      Class available since Release 1.4.0a1
20661 */
20662class PEAR_PackageFile_Generator_v1
20663{
20664    /**
20665     * @var PEAR_PackageFile_v1
20666     */
20667    var $_packagefile;
20668    function PEAR_PackageFile_Generator_v1(&$packagefile)
20669    {
20670        $this->_packagefile = &$packagefile;
20671    }
20672
20673    function getPackagerVersion()
20674    {
20675        return '1.9.4';
20676    }
20677
20678    /**
20679     * @param PEAR_Packager
20680     * @param bool if true, a .tgz is written, otherwise a .tar is written
20681     * @param string|null directory in which to save the .tgz
20682     * @return string|PEAR_Error location of package or error object
20683     */
20684    function toTgz(&$packager, $compress = true, $where = null)
20685    {
20686        require_once 'Archive/Tar.php';
20687        if ($where === null) {
20688            if (!($where = System::mktemp(array('-d')))) {
20689                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: mktemp failed');
20690            }
20691        } elseif (!@System::mkDir(array('-p', $where))) {
20692            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: "' . $where . '" could' .
20693                ' not be created');
20694        }
20695        if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') &&
20696              !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) {
20697            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: unable to save package.xml as' .
20698                ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"');
20699        }
20700        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
20701            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: invalid package file');
20702        }
20703        $pkginfo = $this->_packagefile->getArray();
20704        $ext = $compress ? '.tgz' : '.tar';
20705        $pkgver = $pkginfo['package'] . '-' . $pkginfo['version'];
20706        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
20707        if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) &&
20708              !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) {
20709            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: cannot create tgz file "' .
20710                getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"');
20711        }
20712        if ($pkgfile = $this->_packagefile->getPackageFile()) {
20713            $pkgdir = dirname(realpath($pkgfile));
20714            $pkgfile = basename($pkgfile);
20715        } else {
20716            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: package file object must ' .
20717                'be created from a real file');
20718        }
20719        // {{{ Create the package file list
20720        $filelist = array();
20721        $i = 0;
20722
20723        foreach ($this->_packagefile->getFilelist() as $fname => $atts) {
20724            $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
20725            if (!file_exists($file)) {
20726                return PEAR::raiseError("File does not exist: $fname");
20727            } else {
20728                $filelist[$i++] = $file;
20729                if (!isset($atts['md5sum'])) {
20730                    $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($file));
20731                }
20732                $packager->log(2, "Adding file $fname");
20733            }
20734        }
20735        // }}}
20736        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
20737        if ($packagexml) {
20738            $tar =& new Archive_Tar($dest_package, $compress);
20739            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
20740            // ----- Creates with the package.xml file
20741            $ok = $tar->createModify(array($packagexml), '', $where);
20742            if (PEAR::isError($ok)) {
20743                return $ok;
20744            } elseif (!$ok) {
20745                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
20746            }
20747            // ----- Add the content of the package
20748            if (!$tar->addModify($filelist, $pkgver, $pkgdir)) {
20749                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
20750            }
20751            return $dest_package;
20752        }
20753    }
20754
20755    /**
20756     * @param string|null directory to place the package.xml in, or null for a temporary dir
20757     * @param int one of the PEAR_VALIDATE_* constants
20758     * @param string name of the generated file
20759     * @param bool if true, then no analysis will be performed on role="php" files
20760     * @return string|PEAR_Error path to the created file on success
20761     */
20762    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml',
20763                           $nofilechecking = false)
20764    {
20765        if (!$this->_packagefile->validate($state, $nofilechecking)) {
20766            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: invalid package.xml',
20767                null, null, null, $this->_packagefile->getValidationWarnings());
20768        }
20769        if ($where === null) {
20770            if (!($where = System::mktemp(array('-d')))) {
20771                return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: mktemp failed');
20772            }
20773        } elseif (!@System::mkDir(array('-p', $where))) {
20774            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: "' . $where . '" could' .
20775                ' not be created');
20776        }
20777        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
20778        $np = @fopen($newpkgfile, 'wb');
20779        if (!$np) {
20780            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: unable to save ' .
20781               "$name as $newpkgfile");
20782        }
20783        fwrite($np, $this->toXml($state, true));
20784        fclose($np);
20785        return $newpkgfile;
20786    }
20787
20788    /**
20789     * fix both XML encoding to be UTF8, and replace standard XML entities < > " & '
20790     *
20791     * @param string $string
20792     * @return string
20793     * @access private
20794     */
20795    function _fixXmlEncoding($string)
20796    {
20797        if (version_compare(phpversion(), '5.0.0', 'lt')) {
20798            $string = utf8_encode($string);
20799        }
20800        return strtr($string, array(
20801                                          '&'  => '&amp;',
20802                                          '>'  => '&gt;',
20803                                          '<'  => '&lt;',
20804                                          '"'  => '&quot;',
20805                                          '\'' => '&apos;' ));
20806    }
20807
20808    /**
20809     * Return an XML document based on the package info (as returned
20810     * by the PEAR_Common::infoFrom* methods).
20811     *
20812     * @return string XML data
20813     */
20814    function toXml($state = PEAR_VALIDATE_NORMAL, $nofilevalidation = false)
20815    {
20816        $this->_packagefile->setDate(date('Y-m-d'));
20817        if (!$this->_packagefile->validate($state, $nofilevalidation)) {
20818            return false;
20819        }
20820        $pkginfo = $this->_packagefile->getArray();
20821        static $maint_map = array(
20822            "handle" => "user",
20823            "name" => "name",
20824            "email" => "email",
20825            "role" => "role",
20826            );
20827        $ret = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
20828        $ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/dtd/package-1.0\">\n";
20829        $ret .= "<package version=\"1.0\" packagerversion=\"1.9.4\">\n" .
20830" <name>$pkginfo[package]</name>";
20831        if (isset($pkginfo['extends'])) {
20832            $ret .= "\n<extends>$pkginfo[extends]</extends>";
20833        }
20834        $ret .=
20835 "\n <summary>".$this->_fixXmlEncoding($pkginfo['summary'])."</summary>\n" .
20836" <description>".trim($this->_fixXmlEncoding($pkginfo['description']))."\n </description>\n" .
20837" <maintainers>\n";
20838        foreach ($pkginfo['maintainers'] as $maint) {
20839            $ret .= "  <maintainer>\n";
20840            foreach ($maint_map as $idx => $elm) {
20841                $ret .= "   <$elm>";
20842                $ret .= $this->_fixXmlEncoding($maint[$idx]);
20843                $ret .= "</$elm>\n";
20844            }
20845            $ret .= "  </maintainer>\n";
20846        }
20847        $ret .= "  </maintainers>\n";
20848        $ret .= $this->_makeReleaseXml($pkginfo, false, $state);
20849        if (isset($pkginfo['changelog']) && count($pkginfo['changelog']) > 0) {
20850            $ret .= " <changelog>\n";
20851            foreach ($pkginfo['changelog'] as $oldrelease) {
20852                $ret .= $this->_makeReleaseXml($oldrelease, true);
20853            }
20854            $ret .= " </changelog>\n";
20855        }
20856        $ret .= "</package>\n";
20857        return $ret;
20858    }
20859
20860    // }}}
20861    // {{{ _makeReleaseXml()
20862
20863    /**
20864     * Generate part of an XML description with release information.
20865     *
20866     * @param array  $pkginfo    array with release information
20867     * @param bool   $changelog  whether the result will be in a changelog element
20868     *
20869     * @return string XML data
20870     *
20871     * @access private
20872     */
20873    function _makeReleaseXml($pkginfo, $changelog = false, $state = PEAR_VALIDATE_NORMAL)
20874    {
20875        // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
20876        $indent = $changelog ? "  " : "";
20877        $ret = "$indent <release>\n";
20878        if (!empty($pkginfo['version'])) {
20879            $ret .= "$indent  <version>$pkginfo[version]</version>\n";
20880        }
20881        if (!empty($pkginfo['release_date'])) {
20882            $ret .= "$indent  <date>$pkginfo[release_date]</date>\n";
20883        }
20884        if (!empty($pkginfo['release_license'])) {
20885            $ret .= "$indent  <license>$pkginfo[release_license]</license>\n";
20886        }
20887        if (!empty($pkginfo['release_state'])) {
20888            $ret .= "$indent  <state>$pkginfo[release_state]</state>\n";
20889        }
20890        if (!empty($pkginfo['release_notes'])) {
20891            $ret .= "$indent  <notes>".trim($this->_fixXmlEncoding($pkginfo['release_notes']))
20892            ."\n$indent  </notes>\n";
20893        }
20894        if (!empty($pkginfo['release_warnings'])) {
20895            $ret .= "$indent  <warnings>".$this->_fixXmlEncoding($pkginfo['release_warnings'])."</warnings>\n";
20896        }
20897        if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
20898            $ret .= "$indent  <deps>\n";
20899            foreach ($pkginfo['release_deps'] as $dep) {
20900                $ret .= "$indent   <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
20901                if (isset($dep['version'])) {
20902                    $ret .= " version=\"$dep[version]\"";
20903                }
20904                if (isset($dep['optional'])) {
20905                    $ret .= " optional=\"$dep[optional]\"";
20906                }
20907                if (isset($dep['name'])) {
20908                    $ret .= ">$dep[name]</dep>\n";
20909                } else {
20910                    $ret .= "/>\n";
20911                }
20912            }
20913            $ret .= "$indent  </deps>\n";
20914        }
20915        if (isset($pkginfo['configure_options'])) {
20916            $ret .= "$indent  <configureoptions>\n";
20917            foreach ($pkginfo['configure_options'] as $c) {
20918                $ret .= "$indent   <configureoption name=\"".
20919                    $this->_fixXmlEncoding($c['name']) . "\"";
20920                if (isset($c['default'])) {
20921                    $ret .= " default=\"" . $this->_fixXmlEncoding($c['default']) . "\"";
20922                }
20923                $ret .= " prompt=\"" . $this->_fixXmlEncoding($c['prompt']) . "\"";
20924                $ret .= "/>\n";
20925            }
20926            $ret .= "$indent  </configureoptions>\n";
20927        }
20928        if (isset($pkginfo['provides'])) {
20929            foreach ($pkginfo['provides'] as $key => $what) {
20930                $ret .= "$indent  <provides type=\"$what[type]\" ";
20931                $ret .= "name=\"$what[name]\" ";
20932                if (isset($what['extends'])) {
20933                    $ret .= "extends=\"$what[extends]\" ";
20934                }
20935                $ret .= "/>\n";
20936            }
20937        }
20938        if (isset($pkginfo['filelist'])) {
20939            $ret .= "$indent  <filelist>\n";
20940            if ($state ^ PEAR_VALIDATE_PACKAGING) {
20941                $ret .= $this->recursiveXmlFilelist($pkginfo['filelist']);
20942            } else {
20943                foreach ($pkginfo['filelist'] as $file => $fa) {
20944                    if (!isset($fa['role'])) {
20945                        $fa['role'] = '';
20946                    }
20947                    $ret .= "$indent   <file role=\"$fa[role]\"";
20948                    if (isset($fa['baseinstalldir'])) {
20949                        $ret .= ' baseinstalldir="' .
20950                            $this->_fixXmlEncoding($fa['baseinstalldir']) . '"';
20951                    }
20952                    if (isset($fa['md5sum'])) {
20953                        $ret .= " md5sum=\"$fa[md5sum]\"";
20954                    }
20955                    if (isset($fa['platform'])) {
20956                        $ret .= " platform=\"$fa[platform]\"";
20957                    }
20958                    if (!empty($fa['install-as'])) {
20959                        $ret .= ' install-as="' .
20960                            $this->_fixXmlEncoding($fa['install-as']) . '"';
20961                    }
20962                    $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
20963                    if (empty($fa['replacements'])) {
20964                        $ret .= "/>\n";
20965                    } else {
20966                        $ret .= ">\n";
20967                        foreach ($fa['replacements'] as $r) {
20968                            $ret .= "$indent    <replace";
20969                            foreach ($r as $k => $v) {
20970                                $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
20971                            }
20972                            $ret .= "/>\n";
20973                        }
20974                        $ret .= "$indent   </file>\n";
20975                    }
20976                }
20977            }
20978            $ret .= "$indent  </filelist>\n";
20979        }
20980        $ret .= "$indent </release>\n";
20981        return $ret;
20982    }
20983
20984    /**
20985     * @param array
20986     * @access protected
20987     */
20988    function recursiveXmlFilelist($list)
20989    {
20990        $this->_dirs = array();
20991        foreach ($list as $file => $attributes) {
20992            $this->_addDir($this->_dirs, explode('/', dirname($file)), $file, $attributes);
20993        }
20994        return $this->_formatDir($this->_dirs);
20995    }
20996
20997    /**
20998     * @param array
20999     * @param array
21000     * @param string|null
21001     * @param array|null
21002     * @access private
21003     */
21004    function _addDir(&$dirs, $dir, $file = null, $attributes = null)
21005    {
21006        if ($dir == array() || $dir == array('.')) {
21007            $dirs['files'][basename($file)] = $attributes;
21008            return;
21009        }
21010        $curdir = array_shift($dir);
21011        if (!isset($dirs['dirs'][$curdir])) {
21012            $dirs['dirs'][$curdir] = array();
21013        }
21014        $this->_addDir($dirs['dirs'][$curdir], $dir, $file, $attributes);
21015    }
21016
21017    /**
21018     * @param array
21019     * @param string
21020     * @param string
21021     * @access private
21022     */
21023    function _formatDir($dirs, $indent = '', $curdir = '')
21024    {
21025        $ret = '';
21026        if (!count($dirs)) {
21027            return '';
21028        }
21029        if (isset($dirs['dirs'])) {
21030            uksort($dirs['dirs'], 'strnatcasecmp');
21031            foreach ($dirs['dirs'] as $dir => $contents) {
21032                $usedir = "$curdir/$dir";
21033                $ret .= "$indent   <dir name=\"$dir\">\n";
21034                $ret .= $this->_formatDir($contents, "$indent ", $usedir);
21035                $ret .= "$indent   </dir> <!-- $usedir -->\n";
21036            }
21037        }
21038        if (isset($dirs['files'])) {
21039            uksort($dirs['files'], 'strnatcasecmp');
21040            foreach ($dirs['files'] as $file => $attribs) {
21041                $ret .= $this->_formatFile($file, $attribs, $indent);
21042            }
21043        }
21044        return $ret;
21045    }
21046
21047    /**
21048     * @param string
21049     * @param array
21050     * @param string
21051     * @access private
21052     */
21053    function _formatFile($file, $attributes, $indent)
21054    {
21055        $ret = "$indent   <file role=\"$attributes[role]\"";
21056        if (isset($attributes['baseinstalldir'])) {
21057            $ret .= ' baseinstalldir="' .
21058                $this->_fixXmlEncoding($attributes['baseinstalldir']) . '"';
21059        }
21060        if (isset($attributes['md5sum'])) {
21061            $ret .= " md5sum=\"$attributes[md5sum]\"";
21062        }
21063        if (isset($attributes['platform'])) {
21064            $ret .= " platform=\"$attributes[platform]\"";
21065        }
21066        if (!empty($attributes['install-as'])) {
21067            $ret .= ' install-as="' .
21068                $this->_fixXmlEncoding($attributes['install-as']) . '"';
21069        }
21070        $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
21071        if (empty($attributes['replacements'])) {
21072            $ret .= "/>\n";
21073        } else {
21074            $ret .= ">\n";
21075            foreach ($attributes['replacements'] as $r) {
21076                $ret .= "$indent    <replace";
21077                foreach ($r as $k => $v) {
21078                    $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
21079                }
21080                $ret .= "/>\n";
21081            }
21082            $ret .= "$indent   </file>\n";
21083        }
21084        return $ret;
21085    }
21086
21087    // {{{ _unIndent()
21088
21089    /**
21090     * Unindent given string (?)
21091     *
21092     * @param string $str The string that has to be unindented.
21093     * @return string
21094     * @access private
21095     */
21096    function _unIndent($str)
21097    {
21098        // remove leading newlines
21099        $str = preg_replace('/^[\r\n]+/', '', $str);
21100        // find whitespace at the beginning of the first line
21101        $indent_len = strspn($str, " \t");
21102        $indent = substr($str, 0, $indent_len);
21103        $data = '';
21104        // remove the same amount of whitespace from following lines
21105        foreach (explode("\n", $str) as $line) {
21106            if (substr($line, 0, $indent_len) == $indent) {
21107                $data .= substr($line, $indent_len) . "\n";
21108            }
21109        }
21110        return $data;
21111    }
21112
21113    /**
21114     * @return array
21115     */
21116    function dependenciesToV2()
21117    {
21118        $arr = array();
21119        $this->_convertDependencies2_0($arr);
21120        return $arr['dependencies'];
21121    }
21122
21123    /**
21124     * Convert a package.xml version 1.0 into version 2.0
21125     *
21126     * Note that this does a basic conversion, to allow more advanced
21127     * features like bundles and multiple releases
21128     * @param string the classname to instantiate and return.  This must be
21129     *               PEAR_PackageFile_v2 or a descendant
21130     * @param boolean if true, only valid, deterministic package.xml 1.0 as defined by the
21131     *                strictest parameters will be converted
21132     * @return PEAR_PackageFile_v2|PEAR_Error
21133     */
21134    function &toV2($class = 'PEAR_PackageFile_v2', $strict = false)
21135    {
21136        if ($strict) {
21137            if (!$this->_packagefile->validate()) {
21138                $a = PEAR::raiseError('invalid package.xml version 1.0 cannot be converted' .
21139                    ' to version 2.0', null, null, null,
21140                    $this->_packagefile->getValidationWarnings(true));
21141                return $a;
21142            }
21143        }
21144
21145        $arr = array(
21146            'attribs' => array(
21147                             'version' => '2.0',
21148                             'xmlns' => 'http://pear.php.net/dtd/package-2.0',
21149                             'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
21150                             'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
21151                             'xsi:schemaLocation' => "http://pear.php.net/dtd/tasks-1.0\n" .
21152"http://pear.php.net/dtd/tasks-1.0.xsd\n" .
21153"http://pear.php.net/dtd/package-2.0\n" .
21154'http://pear.php.net/dtd/package-2.0.xsd',
21155                         ),
21156            'name' => $this->_packagefile->getPackage(),
21157            'channel' => 'pear.php.net',
21158        );
21159        $arr['summary'] = $this->_packagefile->getSummary();
21160        $arr['description'] = $this->_packagefile->getDescription();
21161        $maintainers = $this->_packagefile->getMaintainers();
21162        foreach ($maintainers as $maintainer) {
21163            if ($maintainer['role'] != 'lead') {
21164                continue;
21165            }
21166            $new = array(
21167                'name' => $maintainer['name'],
21168                'user' => $maintainer['handle'],
21169                'email' => $maintainer['email'],
21170                'active' => 'yes',
21171            );
21172            $arr['lead'][] = $new;
21173        }
21174
21175        if (!isset($arr['lead'])) { // some people... you know?
21176            $arr['lead'] = array(
21177                'name' => 'unknown',
21178                'user' => 'unknown',
21179                'email' => 'noleadmaintainer@example.com',
21180                'active' => 'no',
21181            );
21182        }
21183
21184        if (count($arr['lead']) == 1) {
21185            $arr['lead'] = $arr['lead'][0];
21186        }
21187
21188        foreach ($maintainers as $maintainer) {
21189            if ($maintainer['role'] == 'lead') {
21190                continue;
21191            }
21192            $new = array(
21193                'name' => $maintainer['name'],
21194                'user' => $maintainer['handle'],
21195                'email' => $maintainer['email'],
21196                'active' => 'yes',
21197            );
21198            $arr[$maintainer['role']][] = $new;
21199        }
21200
21201        if (isset($arr['developer']) && count($arr['developer']) == 1) {
21202            $arr['developer'] = $arr['developer'][0];
21203        }
21204
21205        if (isset($arr['contributor']) && count($arr['contributor']) == 1) {
21206            $arr['contributor'] = $arr['contributor'][0];
21207        }
21208
21209        if (isset($arr['helper']) && count($arr['helper']) == 1) {
21210            $arr['helper'] = $arr['helper'][0];
21211        }
21212
21213        $arr['date'] = $this->_packagefile->getDate();
21214        $arr['version'] =
21215            array(
21216                'release' => $this->_packagefile->getVersion(),
21217                'api' => $this->_packagefile->getVersion(),
21218            );
21219        $arr['stability'] =
21220            array(
21221                'release' => $this->_packagefile->getState(),
21222                'api' => $this->_packagefile->getState(),
21223            );
21224        $licensemap =
21225            array(
21226                'php' => 'http://www.php.net/license',
21227                'php license' => 'http://www.php.net/license',
21228                'lgpl' => 'http://www.gnu.org/copyleft/lesser.html',
21229                'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
21230                'bsd style' => 'http://www.opensource.org/licenses/bsd-license.php',
21231                'bsd-style' => 'http://www.opensource.org/licenses/bsd-license.php',
21232                'mit' => 'http://www.opensource.org/licenses/mit-license.php',
21233                'gpl' => 'http://www.gnu.org/copyleft/gpl.html',
21234                'apache' => 'http://www.opensource.org/licenses/apache2.0.php'
21235            );
21236
21237        if (isset($licensemap[strtolower($this->_packagefile->getLicense())])) {
21238            $arr['license'] = array(
21239                'attribs' => array('uri' =>
21240                    $licensemap[strtolower($this->_packagefile->getLicense())]),
21241                '_content' => $this->_packagefile->getLicense()
21242                );
21243        } else {
21244            // don't use bogus uri
21245            $arr['license'] = $this->_packagefile->getLicense();
21246        }
21247
21248        $arr['notes'] = $this->_packagefile->getNotes();
21249        $temp = array();
21250        $arr['contents'] = $this->_convertFilelist2_0($temp);
21251        $this->_convertDependencies2_0($arr);
21252        $release = ($this->_packagefile->getConfigureOptions() || $this->_isExtension) ?
21253            'extsrcrelease' : 'phprelease';
21254        if ($release == 'extsrcrelease') {
21255            $arr['channel'] = 'pecl.php.net';
21256            $arr['providesextension'] = $arr['name']; // assumption
21257        }
21258
21259        $arr[$release] = array();
21260        if ($this->_packagefile->getConfigureOptions()) {
21261            $arr[$release]['configureoption'] = $this->_packagefile->getConfigureOptions();
21262            foreach ($arr[$release]['configureoption'] as $i => $opt) {
21263                $arr[$release]['configureoption'][$i] = array('attribs' => $opt);
21264            }
21265            if (count($arr[$release]['configureoption']) == 1) {
21266                $arr[$release]['configureoption'] = $arr[$release]['configureoption'][0];
21267            }
21268        }
21269
21270        $this->_convertRelease2_0($arr[$release], $temp);
21271        if ($release == 'extsrcrelease' && count($arr[$release]) > 1) {
21272            // multiple extsrcrelease tags added in PEAR 1.4.1
21273            $arr['dependencies']['required']['pearinstaller']['min'] = '1.4.1';
21274        }
21275
21276        if ($cl = $this->_packagefile->getChangelog()) {
21277            foreach ($cl as $release) {
21278                $rel = array();
21279                $rel['version'] =
21280                    array(
21281                        'release' => $release['version'],
21282                        'api' => $release['version'],
21283                    );
21284                if (!isset($release['release_state'])) {
21285                    $release['release_state'] = 'stable';
21286                }
21287
21288                $rel['stability'] =
21289                    array(
21290                        'release' => $release['release_state'],
21291                        'api' => $release['release_state'],
21292                    );
21293                if (isset($release['release_date'])) {
21294                    $rel['date'] = $release['release_date'];
21295                } else {
21296                    $rel['date'] = date('Y-m-d');
21297                }
21298
21299                if (isset($release['release_license'])) {
21300                    if (isset($licensemap[strtolower($release['release_license'])])) {
21301                        $uri = $licensemap[strtolower($release['release_license'])];
21302                    } else {
21303                        $uri = 'http://www.example.com';
21304                    }
21305                    $rel['license'] = array(
21306                            'attribs' => array('uri' => $uri),
21307                            '_content' => $release['release_license']
21308                        );
21309                } else {
21310                    $rel['license'] = $arr['license'];
21311                }
21312
21313                if (!isset($release['release_notes'])) {
21314                    $release['release_notes'] = 'no release notes';
21315                }
21316
21317                $rel['notes'] = $release['release_notes'];
21318                $arr['changelog']['release'][] = $rel;
21319            }
21320        }
21321
21322        $ret = new $class;
21323        $ret->setConfig($this->_packagefile->_config);
21324        if (isset($this->_packagefile->_logger) && is_object($this->_packagefile->_logger)) {
21325            $ret->setLogger($this->_packagefile->_logger);
21326        }
21327
21328        $ret->fromArray($arr);
21329        return $ret;
21330    }
21331
21332    /**
21333     * @param array
21334     * @param bool
21335     * @access private
21336     */
21337    function _convertDependencies2_0(&$release, $internal = false)
21338    {
21339        $peardep = array('pearinstaller' =>
21340            array('min' => '1.4.0b1')); // this is a lot safer
21341        $required = $optional = array();
21342        $release['dependencies'] = array('required' => array());
21343        if ($this->_packagefile->hasDeps()) {
21344            foreach ($this->_packagefile->getDeps() as $dep) {
21345                if (!isset($dep['optional']) || $dep['optional'] == 'no') {
21346                    $required[] = $dep;
21347                } else {
21348                    $optional[] = $dep;
21349                }
21350            }
21351            foreach (array('required', 'optional') as $arr) {
21352                $deps = array();
21353                foreach ($$arr as $dep) {
21354                    // organize deps by dependency type and name
21355                    if (!isset($deps[$dep['type']])) {
21356                        $deps[$dep['type']] = array();
21357                    }
21358                    if (isset($dep['name'])) {
21359                        $deps[$dep['type']][$dep['name']][] = $dep;
21360                    } else {
21361                        $deps[$dep['type']][] = $dep;
21362                    }
21363                }
21364                do {
21365                    if (isset($deps['php'])) {
21366                        $php = array();
21367                        if (count($deps['php']) > 1) {
21368                            $php = $this->_processPhpDeps($deps['php']);
21369                        } else {
21370                            if (!isset($deps['php'][0])) {
21371                                list($key, $blah) = each ($deps['php']); // stupid buggy versions
21372                                $deps['php'] = array($blah[0]);
21373                            }
21374                            $php = $this->_processDep($deps['php'][0]);
21375                            if (!$php) {
21376                                break; // poor mans throw
21377                            }
21378                        }
21379                        $release['dependencies'][$arr]['php'] = $php;
21380                    }
21381                } while (false);
21382                do {
21383                    if (isset($deps['pkg'])) {
21384                        $pkg = array();
21385                        $pkg = $this->_processMultipleDepsName($deps['pkg']);
21386                        if (!$pkg) {
21387                            break; // poor mans throw
21388                        }
21389                        $release['dependencies'][$arr]['package'] = $pkg;
21390                    }
21391                } while (false);
21392                do {
21393                    if (isset($deps['ext'])) {
21394                        $pkg = array();
21395                        $pkg = $this->_processMultipleDepsName($deps['ext']);
21396                        $release['dependencies'][$arr]['extension'] = $pkg;
21397                    }
21398                } while (false);
21399                // skip sapi - it's not supported so nobody will have used it
21400                // skip os - it's not supported in 1.0
21401            }
21402        }
21403        if (isset($release['dependencies']['required'])) {
21404            $release['dependencies']['required'] =
21405                array_merge($peardep, $release['dependencies']['required']);
21406        } else {
21407            $release['dependencies']['required'] = $peardep;
21408        }
21409        if (!isset($release['dependencies']['required']['php'])) {
21410            $release['dependencies']['required']['php'] =
21411                array('min' => '4.0.0');
21412        }
21413        $order = array();
21414        $bewm = $release['dependencies']['required'];
21415        $order['php'] = $bewm['php'];
21416        $order['pearinstaller'] = $bewm['pearinstaller'];
21417        isset($bewm['package']) ? $order['package'] = $bewm['package'] :0;
21418        isset($bewm['extension']) ? $order['extension'] = $bewm['extension'] :0;
21419        $release['dependencies']['required'] = $order;
21420    }
21421
21422    /**
21423     * @param array
21424     * @access private
21425     */
21426    function _convertFilelist2_0(&$package)
21427    {
21428        $ret = array('dir' =>
21429                    array(
21430                        'attribs' => array('name' => '/'),
21431                        'file' => array()
21432                        )
21433                    );
21434        $package['platform'] =
21435        $package['install-as'] = array();
21436        $this->_isExtension = false;
21437        foreach ($this->_packagefile->getFilelist() as $name => $file) {
21438            $file['name'] = $name;
21439            if (isset($file['role']) && $file['role'] == 'src') {
21440                $this->_isExtension = true;
21441            }
21442            if (isset($file['replacements'])) {
21443                $repl = $file['replacements'];
21444                unset($file['replacements']);
21445            } else {
21446                unset($repl);
21447            }
21448            if (isset($file['install-as'])) {
21449                $package['install-as'][$name] = $file['install-as'];
21450                unset($file['install-as']);
21451            }
21452            if (isset($file['platform'])) {
21453                $package['platform'][$name] = $file['platform'];
21454                unset($file['platform']);
21455            }
21456            $file = array('attribs' => $file);
21457            if (isset($repl)) {
21458                foreach ($repl as $replace ) {
21459                    $file['tasks:replace'][] = array('attribs' => $replace);
21460                }
21461                if (count($repl) == 1) {
21462                    $file['tasks:replace'] = $file['tasks:replace'][0];
21463                }
21464            }
21465            $ret['dir']['file'][] = $file;
21466        }
21467        return $ret;
21468    }
21469
21470    /**
21471     * Post-process special files with install-as/platform attributes and
21472     * make the release tag.
21473     *
21474     * This complex method follows this work-flow to create the release tags:
21475     *
21476     * <pre>
21477     * - if any install-as/platform exist, create a generic release and fill it with
21478     *   o <install as=..> tags for <file name=... install-as=...>
21479     *   o <install as=..> tags for <file name=... platform=!... install-as=..>
21480     *   o <ignore> tags for <file name=... platform=...>
21481     *   o <ignore> tags for <file name=... platform=... install-as=..>
21482     * - create a release for each platform encountered and fill with
21483     *   o <install as..> tags for <file name=... install-as=...>
21484     *   o <install as..> tags for <file name=... platform=this platform install-as=..>
21485     *   o <install as..> tags for <file name=... platform=!other platform install-as=..>
21486     *   o <ignore> tags for <file name=... platform=!this platform>
21487     *   o <ignore> tags for <file name=... platform=other platform>
21488     *   o <ignore> tags for <file name=... platform=other platform install-as=..>
21489     *   o <ignore> tags for <file name=... platform=!this platform install-as=..>
21490     * </pre>
21491     *
21492     * It does this by accessing the $package parameter, which contains an array with
21493     * indices:
21494     *
21495     *  - platform: mapping of file => OS the file should be installed on
21496     *  - install-as: mapping of file => installed name
21497     *  - osmap: mapping of OS => list of files that should be installed
21498     *    on that OS
21499     *  - notosmap: mapping of OS => list of files that should not be
21500     *    installed on that OS
21501     *
21502     * @param array
21503     * @param array
21504     * @access private
21505     */
21506    function _convertRelease2_0(&$release, $package)
21507    {
21508        //- if any install-as/platform exist, create a generic release and fill it with
21509        if (count($package['platform']) || count($package['install-as'])) {
21510            $generic = array();
21511            $genericIgnore = array();
21512            foreach ($package['install-as'] as $file => $as) {
21513                //o <install as=..> tags for <file name=... install-as=...>
21514                if (!isset($package['platform'][$file])) {
21515                    $generic[] = $file;
21516                    continue;
21517                }
21518                //o <install as=..> tags for <file name=... platform=!... install-as=..>
21519                if (isset($package['platform'][$file]) &&
21520                      $package['platform'][$file]{0} == '!') {
21521                    $generic[] = $file;
21522                    continue;
21523                }
21524                //o <ignore> tags for <file name=... platform=... install-as=..>
21525                if (isset($package['platform'][$file]) &&
21526                      $package['platform'][$file]{0} != '!') {
21527                    $genericIgnore[] = $file;
21528                    continue;
21529                }
21530            }
21531            foreach ($package['platform'] as $file => $platform) {
21532                if (isset($package['install-as'][$file])) {
21533                    continue;
21534                }
21535                if ($platform{0} != '!') {
21536                    //o <ignore> tags for <file name=... platform=...>
21537                    $genericIgnore[] = $file;
21538                }
21539            }
21540            if (count($package['platform'])) {
21541                $oses = $notplatform = $platform = array();
21542                foreach ($package['platform'] as $file => $os) {
21543                    // get a list of oses
21544                    if ($os{0} == '!') {
21545                        if (isset($oses[substr($os, 1)])) {
21546                            continue;
21547                        }
21548                        $oses[substr($os, 1)] = count($oses);
21549                    } else {
21550                        if (isset($oses[$os])) {
21551                            continue;
21552                        }
21553                        $oses[$os] = count($oses);
21554                    }
21555                }
21556                //- create a release for each platform encountered and fill with
21557                foreach ($oses as $os => $releaseNum) {
21558                    $release[$releaseNum]['installconditions']['os']['name'] = $os;
21559                    $release[$releaseNum]['filelist'] = array('install' => array(),
21560                        'ignore' => array());
21561                    foreach ($package['install-as'] as $file => $as) {
21562                        //o <install as=..> tags for <file name=... install-as=...>
21563                        if (!isset($package['platform'][$file])) {
21564                            $release[$releaseNum]['filelist']['install'][] =
21565                                array(
21566                                    'attribs' => array(
21567                                        'name' => $file,
21568                                        'as' => $as,
21569                                    ),
21570                                );
21571                            continue;
21572                        }
21573                        //o <install as..> tags for
21574                        //  <file name=... platform=this platform install-as=..>
21575                        if (isset($package['platform'][$file]) &&
21576                              $package['platform'][$file] == $os) {
21577                            $release[$releaseNum]['filelist']['install'][] =
21578                                array(
21579                                    'attribs' => array(
21580                                        'name' => $file,
21581                                        'as' => $as,
21582                                    ),
21583                                );
21584                            continue;
21585                        }
21586                        //o <install as..> tags for
21587                        //  <file name=... platform=!other platform install-as=..>
21588                        if (isset($package['platform'][$file]) &&
21589                              $package['platform'][$file] != "!$os" &&
21590                              $package['platform'][$file]{0} == '!') {
21591                            $release[$releaseNum]['filelist']['install'][] =
21592                                array(
21593                                    'attribs' => array(
21594                                        'name' => $file,
21595                                        'as' => $as,
21596                                    ),
21597                                );
21598                            continue;
21599                        }
21600                        //o <ignore> tags for
21601                        //  <file name=... platform=!this platform install-as=..>
21602                        if (isset($package['platform'][$file]) &&
21603                              $package['platform'][$file] == "!$os") {
21604                            $release[$releaseNum]['filelist']['ignore'][] =
21605                                array(
21606                                    'attribs' => array(
21607                                        'name' => $file,
21608                                    ),
21609                                );
21610                            continue;
21611                        }
21612                        //o <ignore> tags for
21613                        //  <file name=... platform=other platform install-as=..>
21614                        if (isset($package['platform'][$file]) &&
21615                              $package['platform'][$file]{0} != '!' &&
21616                              $package['platform'][$file] != $os) {
21617                            $release[$releaseNum]['filelist']['ignore'][] =
21618                                array(
21619                                    'attribs' => array(
21620                                        'name' => $file,
21621                                    ),
21622                                );
21623                            continue;
21624                        }
21625                    }
21626                    foreach ($package['platform'] as $file => $platform) {
21627                        if (isset($package['install-as'][$file])) {
21628                            continue;
21629                        }
21630                        //o <ignore> tags for <file name=... platform=!this platform>
21631                        if ($platform == "!$os") {
21632                            $release[$releaseNum]['filelist']['ignore'][] =
21633                                array(
21634                                    'attribs' => array(
21635                                        'name' => $file,
21636                                    ),
21637                                );
21638                            continue;
21639                        }
21640                        //o <ignore> tags for <file name=... platform=other platform>
21641                        if ($platform{0} != '!' && $platform != $os) {
21642                            $release[$releaseNum]['filelist']['ignore'][] =
21643                                array(
21644                                    'attribs' => array(
21645                                        'name' => $file,
21646                                    ),
21647                                );
21648                        }
21649                    }
21650                    if (!count($release[$releaseNum]['filelist']['install'])) {
21651                        unset($release[$releaseNum]['filelist']['install']);
21652                    }
21653                    if (!count($release[$releaseNum]['filelist']['ignore'])) {
21654                        unset($release[$releaseNum]['filelist']['ignore']);
21655                    }
21656                }
21657                if (count($generic) || count($genericIgnore)) {
21658                    $release[count($oses)] = array();
21659                    if (count($generic)) {
21660                        foreach ($generic as $file) {
21661                            if (isset($package['install-as'][$file])) {
21662                                $installas = $package['install-as'][$file];
21663                            } else {
21664                                $installas = $file;
21665                            }
21666                            $release[count($oses)]['filelist']['install'][] =
21667                                array(
21668                                    'attribs' => array(
21669                                        'name' => $file,
21670                                        'as' => $installas,
21671                                    )
21672                                );
21673                        }
21674                    }
21675                    if (count($genericIgnore)) {
21676                        foreach ($genericIgnore as $file) {
21677                            $release[count($oses)]['filelist']['ignore'][] =
21678                                array(
21679                                    'attribs' => array(
21680                                        'name' => $file,
21681                                    )
21682                                );
21683                        }
21684                    }
21685                }
21686                // cleanup
21687                foreach ($release as $i => $rel) {
21688                    if (isset($rel['filelist']['install']) &&
21689                          count($rel['filelist']['install']) == 1) {
21690                        $release[$i]['filelist']['install'] =
21691                            $release[$i]['filelist']['install'][0];
21692                    }
21693                    if (isset($rel['filelist']['ignore']) &&
21694                          count($rel['filelist']['ignore']) == 1) {
21695                        $release[$i]['filelist']['ignore'] =
21696                            $release[$i]['filelist']['ignore'][0];
21697                    }
21698                }
21699                if (count($release) == 1) {
21700                    $release = $release[0];
21701                }
21702            } else {
21703                // no platform atts, but some install-as atts
21704                foreach ($package['install-as'] as $file => $value) {
21705                    $release['filelist']['install'][] =
21706                        array(
21707                            'attribs' => array(
21708                                'name' => $file,
21709                                'as' => $value
21710                            )
21711                        );
21712                }
21713                if (count($release['filelist']['install']) == 1) {
21714                    $release['filelist']['install'] = $release['filelist']['install'][0];
21715                }
21716            }
21717        }
21718    }
21719
21720    /**
21721     * @param array
21722     * @return array
21723     * @access private
21724     */
21725    function _processDep($dep)
21726    {
21727        if ($dep['type'] == 'php') {
21728            if ($dep['rel'] == 'has') {
21729                // come on - everyone has php!
21730                return false;
21731            }
21732        }
21733        $php = array();
21734        if ($dep['type'] != 'php') {
21735            $php['name'] = $dep['name'];
21736            if ($dep['type'] == 'pkg') {
21737                $php['channel'] = 'pear.php.net';
21738            }
21739        }
21740        switch ($dep['rel']) {
21741            case 'gt' :
21742                $php['min'] = $dep['version'];
21743                $php['exclude'] = $dep['version'];
21744            break;
21745            case 'ge' :
21746                if (!isset($dep['version'])) {
21747                    if ($dep['type'] == 'php') {
21748                        if (isset($dep['name'])) {
21749                            $dep['version'] = $dep['name'];
21750                        }
21751                    }
21752                }
21753                $php['min'] = $dep['version'];
21754            break;
21755            case 'lt' :
21756                $php['max'] = $dep['version'];
21757                $php['exclude'] = $dep['version'];
21758            break;
21759            case 'le' :
21760                $php['max'] = $dep['version'];
21761            break;
21762            case 'eq' :
21763                $php['min'] = $dep['version'];
21764                $php['max'] = $dep['version'];
21765            break;
21766            case 'ne' :
21767                $php['exclude'] = $dep['version'];
21768            break;
21769            case 'not' :
21770                $php['conflicts'] = 'yes';
21771            break;
21772        }
21773        return $php;
21774    }
21775
21776    /**
21777     * @param array
21778     * @return array
21779     */
21780    function _processPhpDeps($deps)
21781    {
21782        $test = array();
21783        foreach ($deps as $dep) {
21784            $test[] = $this->_processDep($dep);
21785        }
21786        $min = array();
21787        $max = array();
21788        foreach ($test as $dep) {
21789            if (!$dep) {
21790                continue;
21791            }
21792            if (isset($dep['min'])) {
21793                $min[$dep['min']] = count($min);
21794            }
21795            if (isset($dep['max'])) {
21796                $max[$dep['max']] = count($max);
21797            }
21798        }
21799        if (count($min) > 0) {
21800            uksort($min, 'version_compare');
21801        }
21802        if (count($max) > 0) {
21803            uksort($max, 'version_compare');
21804        }
21805        if (count($min)) {
21806            // get the highest minimum
21807            $min = array_pop($a = array_flip($min));
21808        } else {
21809            $min = false;
21810        }
21811        if (count($max)) {
21812            // get the lowest maximum
21813            $max = array_shift($a = array_flip($max));
21814        } else {
21815            $max = false;
21816        }
21817        if ($min) {
21818            $php['min'] = $min;
21819        }
21820        if ($max) {
21821            $php['max'] = $max;
21822        }
21823        $exclude = array();
21824        foreach ($test as $dep) {
21825            if (!isset($dep['exclude'])) {
21826                continue;
21827            }
21828            $exclude[] = $dep['exclude'];
21829        }
21830        if (count($exclude)) {
21831            $php['exclude'] = $exclude;
21832        }
21833        return $php;
21834    }
21835
21836    /**
21837     * process multiple dependencies that have a name, like package deps
21838     * @param array
21839     * @return array
21840     * @access private
21841     */
21842    function _processMultipleDepsName($deps)
21843    {
21844        $ret = $tests = array();
21845        foreach ($deps as $name => $dep) {
21846            foreach ($dep as $d) {
21847                $tests[$name][] = $this->_processDep($d);
21848            }
21849        }
21850
21851        foreach ($tests as $name => $test) {
21852            $max = $min = $php = array();
21853            $php['name'] = $name;
21854            foreach ($test as $dep) {
21855                if (!$dep) {
21856                    continue;
21857                }
21858                if (isset($dep['channel'])) {
21859                    $php['channel'] = 'pear.php.net';
21860                }
21861                if (isset($dep['conflicts']) && $dep['conflicts'] == 'yes') {
21862                    $php['conflicts'] = 'yes';
21863                }
21864                if (isset($dep['min'])) {
21865                    $min[$dep['min']] = count($min);
21866                }
21867                if (isset($dep['max'])) {
21868                    $max[$dep['max']] = count($max);
21869                }
21870            }
21871            if (count($min) > 0) {
21872                uksort($min, 'version_compare');
21873            }
21874            if (count($max) > 0) {
21875                uksort($max, 'version_compare');
21876            }
21877            if (count($min)) {
21878                // get the highest minimum
21879                $min = array_pop($a = array_flip($min));
21880            } else {
21881                $min = false;
21882            }
21883            if (count($max)) {
21884                // get the lowest maximum
21885                $max = array_shift($a = array_flip($max));
21886            } else {
21887                $max = false;
21888            }
21889            if ($min) {
21890                $php['min'] = $min;
21891            }
21892            if ($max) {
21893                $php['max'] = $max;
21894            }
21895            $exclude = array();
21896            foreach ($test as $dep) {
21897                if (!isset($dep['exclude'])) {
21898                    continue;
21899                }
21900                $exclude[] = $dep['exclude'];
21901            }
21902            if (count($exclude)) {
21903                $php['exclude'] = $exclude;
21904            }
21905            $ret[] = $php;
21906        }
21907        return $ret;
21908    }
21909}
21910?>PEAR-1.9.4/PEAR/PackageFile/Generator/v2.php0000644000076500000240000010136011605156614017105 0ustar  helgistaff<?php
21911/**
21912 * package.xml generation class, package.xml version 2.0
21913 *
21914 * PHP versions 4 and 5
21915 *
21916 * @category   pear
21917 * @package    PEAR
21918 * @author     Greg Beaver <cellog@php.net>
21919 * @author     Stephan Schmidt (original XML_Serializer code)
21920 * @copyright  1997-2009 The Authors
21921 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
21922 * @version    CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
21923 * @link       http://pear.php.net/package/PEAR
21924 * @since      File available since Release 1.4.0a1
21925 */
21926/**
21927 * file/dir manipulation routines
21928 */
21929require_once 'System.php';
21930require_once 'XML/Util.php';
21931
21932/**
21933 * This class converts a PEAR_PackageFile_v2 object into any output format.
21934 *
21935 * Supported output formats include array, XML string (using S. Schmidt's
21936 * XML_Serializer, slightly customized)
21937 * @category   pear
21938 * @package    PEAR
21939 * @author     Greg Beaver <cellog@php.net>
21940 * @author     Stephan Schmidt (original XML_Serializer code)
21941 * @copyright  1997-2009 The Authors
21942 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
21943 * @version    Release: 1.9.4
21944 * @link       http://pear.php.net/package/PEAR
21945 * @since      Class available since Release 1.4.0a1
21946 */
21947class PEAR_PackageFile_Generator_v2
21948{
21949   /**
21950    * default options for the serialization
21951    * @access private
21952    * @var array $_defaultOptions
21953    */
21954    var $_defaultOptions = array(
21955        'indent'             => ' ',                    // string used for indentation
21956        'linebreak'          => "\n",                  // string used for newlines
21957        'typeHints'          => false,                 // automatically add type hin attributes
21958        'addDecl'            => true,                 // add an XML declaration
21959        'defaultTagName'     => 'XML_Serializer_Tag',  // tag used for indexed arrays or invalid names
21960        'classAsTagName'     => false,                 // use classname for objects in indexed arrays
21961        'keyAttribute'       => '_originalKey',        // attribute where original key is stored
21962        'typeAttribute'      => '_type',               // attribute for type (only if typeHints => true)
21963        'classAttribute'     => '_class',              // attribute for class of objects (only if typeHints => true)
21964        'scalarAsAttributes' => false,                 // scalar values (strings, ints,..) will be serialized as attribute
21965        'prependAttributes'  => '',                    // prepend string for attributes
21966        'indentAttributes'   => false,                 // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
21967        'mode'               => 'simplexml',             // use 'simplexml' to use parent name as tagname if transforming an indexed array
21968        'addDoctype'         => false,                 // add a doctype declaration
21969        'doctype'            => null,                  // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
21970        'rootName'           => 'package',                  // name of the root tag
21971        'rootAttributes'     => array(
21972            'version' => '2.0',
21973            'xmlns' => 'http://pear.php.net/dtd/package-2.0',
21974            'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
21975            'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
21976            'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
21977http://pear.php.net/dtd/tasks-1.0.xsd
21978http://pear.php.net/dtd/package-2.0
21979http://pear.php.net/dtd/package-2.0.xsd',
21980        ),               // attributes of the root tag
21981        'attributesArray'    => 'attribs',                  // all values in this key will be treated as attributes
21982        'contentName'        => '_content',                   // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
21983        'beautifyFilelist'   => false,
21984        'encoding' => 'UTF-8',
21985    );
21986
21987   /**
21988    * options for the serialization
21989    * @access private
21990    * @var array $options
21991    */
21992    var $options = array();
21993
21994   /**
21995    * current tag depth
21996    * @var integer $_tagDepth
21997    */
21998    var $_tagDepth = 0;
21999
22000   /**
22001    * serilialized representation of the data
22002    * @var string $_serializedData
22003    */
22004    var $_serializedData = null;
22005    /**
22006     * @var PEAR_PackageFile_v2
22007     */
22008    var $_packagefile;
22009    /**
22010     * @param PEAR_PackageFile_v2
22011     */
22012    function PEAR_PackageFile_Generator_v2(&$packagefile)
22013    {
22014        $this->_packagefile = &$packagefile;
22015        if (isset($this->_packagefile->encoding)) {
22016            $this->_defaultOptions['encoding'] = $this->_packagefile->encoding;
22017        }
22018    }
22019
22020    /**
22021     * @return string
22022     */
22023    function getPackagerVersion()
22024    {
22025        return '1.9.4';
22026    }
22027
22028    /**
22029     * @param PEAR_Packager
22030     * @param bool generate a .tgz or a .tar
22031     * @param string|null temporary directory to package in
22032     */
22033    function toTgz(&$packager, $compress = true, $where = null)
22034    {
22035        $a = null;
22036        return $this->toTgz2($packager, $a, $compress, $where);
22037    }
22038
22039    /**
22040     * Package up both a package.xml and package2.xml for the same release
22041     * @param PEAR_Packager
22042     * @param PEAR_PackageFile_v1
22043     * @param bool generate a .tgz or a .tar
22044     * @param string|null temporary directory to package in
22045     */
22046    function toTgz2(&$packager, &$pf1, $compress = true, $where = null)
22047    {
22048        require_once 'Archive/Tar.php';
22049        if (!$this->_packagefile->isEquivalent($pf1)) {
22050            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' .
22051                basename($pf1->getPackageFile()) .
22052                '" is not equivalent to "' . basename($this->_packagefile->getPackageFile())
22053                . '"');
22054        }
22055
22056        if ($where === null) {
22057            if (!($where = System::mktemp(array('-d')))) {
22058                return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed');
22059            }
22060        } elseif (!@System::mkDir(array('-p', $where))) {
22061            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' .
22062                ' not be created');
22063        }
22064
22065        $file = $where . DIRECTORY_SEPARATOR . 'package.xml';
22066        if (file_exists($file) && !is_file($file)) {
22067            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' .
22068                ' "' . $file  .'"');
22069        }
22070
22071        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
22072            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml');
22073        }
22074
22075        $ext = $compress ? '.tgz' : '.tar';
22076        $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion();
22077        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
22078        if (file_exists($dest_package) && !is_file($dest_package)) {
22079            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' .
22080                $dest_package . '"');
22081        }
22082
22083        $pkgfile = $this->_packagefile->getPackageFile();
22084        if (!$pkgfile) {
22085            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' .
22086                'be created from a real file');
22087        }
22088
22089        $pkgdir  = dirname(realpath($pkgfile));
22090        $pkgfile = basename($pkgfile);
22091
22092        // {{{ Create the package file list
22093        $filelist = array();
22094        $i = 0;
22095        $this->_packagefile->flattenFilelist();
22096        $contents = $this->_packagefile->getContents();
22097        if (isset($contents['bundledpackage'])) { // bundles of packages
22098            $contents = $contents['bundledpackage'];
22099            if (!isset($contents[0])) {
22100                $contents = array($contents);
22101            }
22102
22103            $packageDir = $where;
22104            foreach ($contents as $i => $package) {
22105                $fname = $package;
22106                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
22107                if (!file_exists($file)) {
22108                    return $packager->raiseError("File does not exist: $fname");
22109                }
22110
22111                $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
22112                System::mkdir(array('-p', dirname($tfile)));
22113                copy($file, $tfile);
22114                $filelist[$i++] = $tfile;
22115                $packager->log(2, "Adding package $fname");
22116            }
22117        } else { // normal packages
22118            $contents = $contents['dir']['file'];
22119            if (!isset($contents[0])) {
22120                $contents = array($contents);
22121            }
22122
22123            $packageDir = $where;
22124            foreach ($contents as $i => $file) {
22125                $fname = $file['attribs']['name'];
22126                $atts = $file['attribs'];
22127                $orig = $file;
22128                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
22129                if (!file_exists($file)) {
22130                    return $packager->raiseError("File does not exist: $fname");
22131                }
22132
22133                $origperms = fileperms($file);
22134                $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
22135                unset($orig['attribs']);
22136                if (count($orig)) { // file with tasks
22137                    // run any package-time tasks
22138                    $contents = file_get_contents($file);
22139                    foreach ($orig as $tag => $raw) {
22140                        $tag = str_replace(
22141                            array($this->_packagefile->getTasksNs() . ':', '-'),
22142                            array('', '_'), $tag);
22143                        $task = "PEAR_Task_$tag";
22144                        $task = &new $task($this->_packagefile->_config,
22145                            $this->_packagefile->_logger,
22146                            PEAR_TASK_PACKAGE);
22147                        $task->init($raw, $atts, null);
22148                        $res = $task->startSession($this->_packagefile, $contents, $tfile);
22149                        if (!$res) {
22150                            continue; // skip this task
22151                        }
22152
22153                        if (PEAR::isError($res)) {
22154                            return $res;
22155                        }
22156
22157                        $contents = $res; // save changes
22158                        System::mkdir(array('-p', dirname($tfile)));
22159                        $wp = fopen($tfile, "wb");
22160                        fwrite($wp, $contents);
22161                        fclose($wp);
22162                    }
22163                }
22164
22165                if (!file_exists($tfile)) {
22166                    System::mkdir(array('-p', dirname($tfile)));
22167                    copy($file, $tfile);
22168                }
22169
22170                chmod($tfile, $origperms);
22171                $filelist[$i++] = $tfile;
22172                $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1);
22173                $packager->log(2, "Adding file $fname");
22174            }
22175        }
22176            // }}}
22177
22178        $name       = $pf1 !== null ? 'package2.xml' : 'package.xml';
22179        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name);
22180        if ($packagexml) {
22181            $tar =& new Archive_Tar($dest_package, $compress);
22182            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
22183            // ----- Creates with the package.xml file
22184            $ok = $tar->createModify(array($packagexml), '', $where);
22185            if (PEAR::isError($ok)) {
22186                return $packager->raiseError($ok);
22187            } elseif (!$ok) {
22188                return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name .
22189                    ' failed');
22190            }
22191
22192            // ----- Add the content of the package
22193            if (!$tar->addModify($filelist, $pkgver, $where)) {
22194                return $packager->raiseError(
22195                    'PEAR_Packagefile_v2::toTgz(): tarball creation failed');
22196            }
22197
22198            // add the package.xml version 1.0
22199            if ($pf1 !== null) {
22200                $pfgen = &$pf1->getDefaultGenerator();
22201                $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
22202                if (!$tar->addModify(array($packagexml1), '', $where)) {
22203                    return $packager->raiseError(
22204                        'PEAR_Packagefile_v2::toTgz(): adding package.xml failed');
22205                }
22206            }
22207
22208            return $dest_package;
22209        }
22210    }
22211
22212    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml')
22213    {
22214        if (!$this->_packagefile->validate($state)) {
22215            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml',
22216                null, null, null, $this->_packagefile->getValidationWarnings());
22217        }
22218
22219        if ($where === null) {
22220            if (!($where = System::mktemp(array('-d')))) {
22221                return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed');
22222            }
22223        } elseif (!@System::mkDir(array('-p', $where))) {
22224            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' .
22225                ' not be created');
22226        }
22227
22228        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
22229        $np = @fopen($newpkgfile, 'wb');
22230        if (!$np) {
22231            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' .
22232               "$name as $newpkgfile");
22233        }
22234        fwrite($np, $this->toXml($state));
22235        fclose($np);
22236        return $newpkgfile;
22237    }
22238
22239    function &toV2()
22240    {
22241        return $this->_packagefile;
22242    }
22243
22244    /**
22245     * Return an XML document based on the package info (as returned
22246     * by the PEAR_Common::infoFrom* methods).
22247     *
22248     * @return string XML data
22249     */
22250    function toXml($state = PEAR_VALIDATE_NORMAL, $options = array())
22251    {
22252        $this->_packagefile->setDate(date('Y-m-d'));
22253        $this->_packagefile->setTime(date('H:i:s'));
22254        if (!$this->_packagefile->validate($state)) {
22255            return false;
22256        }
22257
22258        if (is_array($options)) {
22259            $this->options = array_merge($this->_defaultOptions, $options);
22260        } else {
22261            $this->options = $this->_defaultOptions;
22262        }
22263
22264        $arr = $this->_packagefile->getArray();
22265        if (isset($arr['filelist'])) {
22266            unset($arr['filelist']);
22267        }
22268
22269        if (isset($arr['_lastversion'])) {
22270            unset($arr['_lastversion']);
22271        }
22272
22273        // Fix the notes a little bit
22274        if (isset($arr['notes'])) {
22275            // This trims out the indenting, needs fixing
22276            $arr['notes'] = "\n" . trim($arr['notes']) . "\n";
22277        }
22278
22279        if (isset($arr['changelog']) && !empty($arr['changelog'])) {
22280            // Fix for inconsistency how the array is filled depending on the changelog release amount
22281            if (!isset($arr['changelog']['release'][0])) {
22282                $release = $arr['changelog']['release'];
22283                unset($arr['changelog']['release']);
22284
22285                $arr['changelog']['release']    = array();
22286                $arr['changelog']['release'][0] = $release;
22287            }
22288
22289            foreach (array_keys($arr['changelog']['release']) as $key) {
22290                $c =& $arr['changelog']['release'][$key];
22291                if (isset($c['notes'])) {
22292                    // This trims out the indenting, needs fixing
22293                    $c['notes'] = "\n" . trim($c['notes']) . "\n";
22294                }
22295            }
22296        }
22297
22298        if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) {
22299            $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']);
22300            unset($arr['contents']['dir']['file']);
22301            if (isset($use['dir'])) {
22302                $arr['contents']['dir']['dir'] = $use['dir'];
22303            }
22304            if (isset($use['file'])) {
22305                $arr['contents']['dir']['file'] = $use['file'];
22306            }
22307            $this->options['beautifyFilelist'] = true;
22308        }
22309
22310        $arr['attribs']['packagerversion'] = '1.9.4';
22311        if ($this->serialize($arr, $options)) {
22312            return $this->_serializedData . "\n";
22313        }
22314
22315        return false;
22316    }
22317
22318
22319    function _recursiveXmlFilelist($list)
22320    {
22321        $dirs = array();
22322        if (isset($list['attribs'])) {
22323            $file = $list['attribs']['name'];
22324            unset($list['attribs']['name']);
22325            $attributes = $list['attribs'];
22326            $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes);
22327        } else {
22328            foreach ($list as $a) {
22329                $file = $a['attribs']['name'];
22330                $attributes = $a['attribs'];
22331                unset($a['attribs']);
22332                $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a);
22333            }
22334        }
22335        $this->_formatDir($dirs);
22336        $this->_deFormat($dirs);
22337        return $dirs;
22338    }
22339
22340    function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null)
22341    {
22342        if (!$tasks) {
22343            $tasks = array();
22344        }
22345        if ($dir == array() || $dir == array('.')) {
22346            $dirs['file'][basename($file)] = $tasks;
22347            $attributes['name'] = basename($file);
22348            $dirs['file'][basename($file)]['attribs'] = $attributes;
22349            return;
22350        }
22351        $curdir = array_shift($dir);
22352        if (!isset($dirs['dir'][$curdir])) {
22353            $dirs['dir'][$curdir] = array();
22354        }
22355        $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks);
22356    }
22357
22358    function _formatDir(&$dirs)
22359    {
22360        if (!count($dirs)) {
22361            return array();
22362        }
22363        $newdirs = array();
22364        if (isset($dirs['dir'])) {
22365            $newdirs['dir'] = $dirs['dir'];
22366        }
22367        if (isset($dirs['file'])) {
22368            $newdirs['file'] = $dirs['file'];
22369        }
22370        $dirs = $newdirs;
22371        if (isset($dirs['dir'])) {
22372            uksort($dirs['dir'], 'strnatcasecmp');
22373            foreach ($dirs['dir'] as $dir => $contents) {
22374                $this->_formatDir($dirs['dir'][$dir]);
22375            }
22376        }
22377        if (isset($dirs['file'])) {
22378            uksort($dirs['file'], 'strnatcasecmp');
22379        };
22380    }
22381
22382    function _deFormat(&$dirs)
22383    {
22384        if (!count($dirs)) {
22385            return array();
22386        }
22387        $newdirs = array();
22388        if (isset($dirs['dir'])) {
22389            foreach ($dirs['dir'] as $dir => $contents) {
22390                $newdir = array();
22391                $newdir['attribs']['name'] = $dir;
22392                $this->_deFormat($contents);
22393                foreach ($contents as $tag => $val) {
22394                    $newdir[$tag] = $val;
22395                }
22396                $newdirs['dir'][] = $newdir;
22397            }
22398            if (count($newdirs['dir']) == 1) {
22399                $newdirs['dir'] = $newdirs['dir'][0];
22400            }
22401        }
22402        if (isset($dirs['file'])) {
22403            foreach ($dirs['file'] as $name => $file) {
22404                $newdirs['file'][] = $file;
22405            }
22406            if (count($newdirs['file']) == 1) {
22407                $newdirs['file'] = $newdirs['file'][0];
22408            }
22409        }
22410        $dirs = $newdirs;
22411    }
22412
22413    /**
22414    * reset all options to default options
22415    *
22416    * @access   public
22417    * @see      setOption(), XML_Unserializer()
22418    */
22419    function resetOptions()
22420    {
22421        $this->options = $this->_defaultOptions;
22422    }
22423
22424   /**
22425    * set an option
22426    *
22427    * You can use this method if you do not want to set all options in the constructor
22428    *
22429    * @access   public
22430    * @see      resetOption(), XML_Serializer()
22431    */
22432    function setOption($name, $value)
22433    {
22434        $this->options[$name] = $value;
22435    }
22436
22437   /**
22438    * sets several options at once
22439    *
22440    * You can use this method if you do not want to set all options in the constructor
22441    *
22442    * @access   public
22443    * @see      resetOption(), XML_Unserializer(), setOption()
22444    */
22445    function setOptions($options)
22446    {
22447        $this->options = array_merge($this->options, $options);
22448    }
22449
22450   /**
22451    * serialize data
22452    *
22453    * @access   public
22454    * @param    mixed    $data data to serialize
22455    * @return   boolean  true on success, pear error on failure
22456    */
22457    function serialize($data, $options = null)
22458    {
22459        // if options have been specified, use them instead
22460        // of the previously defined ones
22461        if (is_array($options)) {
22462            $optionsBak = $this->options;
22463            if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
22464                $this->options = array_merge($this->_defaultOptions, $options);
22465            } else {
22466                $this->options = array_merge($this->options, $options);
22467            }
22468        } else {
22469            $optionsBak = null;
22470        }
22471
22472        //  start depth is zero
22473        $this->_tagDepth = 0;
22474        $this->_serializedData = '';
22475        // serialize an array
22476        if (is_array($data)) {
22477            $tagName = isset($this->options['rootName']) ? $this->options['rootName'] : 'array';
22478            $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']);
22479        }
22480
22481        // add doctype declaration
22482        if ($this->options['addDoctype'] === true) {
22483            $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype'])
22484                                   . $this->options['linebreak']
22485                                   . $this->_serializedData;
22486        }
22487
22488        //  build xml declaration
22489        if ($this->options['addDecl']) {
22490            $atts = array();
22491            $encoding = isset($this->options['encoding']) ? $this->options['encoding'] : null;
22492            $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $encoding)
22493                                   . $this->options['linebreak']
22494                                   . $this->_serializedData;
22495        }
22496
22497
22498        if ($optionsBak !== null) {
22499            $this->options = $optionsBak;
22500        }
22501
22502        return  true;
22503    }
22504
22505   /**
22506    * get the result of the serialization
22507    *
22508    * @access public
22509    * @return string serialized XML
22510    */
22511    function getSerializedData()
22512    {
22513        if ($this->_serializedData === null) {
22514            return  $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
22515        }
22516        return $this->_serializedData;
22517    }
22518
22519   /**
22520    * serialize any value
22521    *
22522    * This method checks for the type of the value and calls the appropriate method
22523    *
22524    * @access private
22525    * @param  mixed     $value
22526    * @param  string    $tagName
22527    * @param  array     $attributes
22528    * @return string
22529    */
22530    function _serializeValue($value, $tagName = null, $attributes = array())
22531    {
22532        if (is_array($value)) {
22533            $xml = $this->_serializeArray($value, $tagName, $attributes);
22534        } elseif (is_object($value)) {
22535            $xml = $this->_serializeObject($value, $tagName);
22536        } else {
22537            $tag = array(
22538                          'qname'      => $tagName,
22539                          'attributes' => $attributes,
22540                          'content'    => $value
22541                        );
22542            $xml = $this->_createXMLTag($tag);
22543        }
22544        return $xml;
22545    }
22546
22547   /**
22548    * serialize an array
22549    *
22550    * @access   private
22551    * @param    array   $array       array to serialize
22552    * @param    string  $tagName     name of the root tag
22553    * @param    array   $attributes  attributes for the root tag
22554    * @return   string  $string      serialized data
22555    * @uses     XML_Util::isValidName() to check, whether key has to be substituted
22556    */
22557    function _serializeArray(&$array, $tagName = null, $attributes = array())
22558    {
22559        $_content = null;
22560
22561        /**
22562         * check for special attributes
22563         */
22564        if ($this->options['attributesArray'] !== null) {
22565            if (isset($array[$this->options['attributesArray']])) {
22566                $attributes = $array[$this->options['attributesArray']];
22567                unset($array[$this->options['attributesArray']]);
22568            }
22569            /**
22570             * check for special content
22571             */
22572            if ($this->options['contentName'] !== null) {
22573                if (isset($array[$this->options['contentName']])) {
22574                    $_content = $array[$this->options['contentName']];
22575                    unset($array[$this->options['contentName']]);
22576                }
22577            }
22578        }
22579
22580        /*
22581        * if mode is set to simpleXML, check whether
22582        * the array is associative or indexed
22583        */
22584        if (is_array($array) && $this->options['mode'] == 'simplexml') {
22585            $indexed = true;
22586            if (!count($array)) {
22587                $indexed = false;
22588            }
22589            foreach ($array as $key => $val) {
22590                if (!is_int($key)) {
22591                    $indexed = false;
22592                    break;
22593                }
22594            }
22595
22596            if ($indexed && $this->options['mode'] == 'simplexml') {
22597                $string = '';
22598                foreach ($array as $key => $val) {
22599                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
22600                        if (!isset($this->_curdir)) {
22601                            $this->_curdir = '';
22602                        }
22603                        $savedir = $this->_curdir;
22604                        if (isset($val['attribs'])) {
22605                            if ($val['attribs']['name'] == '/') {
22606                                $this->_curdir = '/';
22607                            } else {
22608                                if ($this->_curdir == '/') {
22609                                    $this->_curdir = '';
22610                                }
22611                                $this->_curdir .= '/' . $val['attribs']['name'];
22612                            }
22613                        }
22614                    }
22615                    $string .= $this->_serializeValue( $val, $tagName, $attributes);
22616                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
22617                        $string .= ' <!-- ' . $this->_curdir . ' -->';
22618                        if (empty($savedir)) {
22619                            unset($this->_curdir);
22620                        } else {
22621                            $this->_curdir = $savedir;
22622                        }
22623                    }
22624
22625                    $string .= $this->options['linebreak'];
22626                    // do indentation
22627                    if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
22628                        $string .= str_repeat($this->options['indent'], $this->_tagDepth);
22629                    }
22630                }
22631                return rtrim($string);
22632            }
22633        }
22634
22635        if ($this->options['scalarAsAttributes'] === true) {
22636            foreach ($array as $key => $value) {
22637                if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
22638                    unset($array[$key]);
22639                    $attributes[$this->options['prependAttributes'].$key] = $value;
22640                }
22641            }
22642        }
22643
22644        // check for empty array => create empty tag
22645        if (empty($array)) {
22646            $tag = array(
22647                            'qname'      => $tagName,
22648                            'content'    => $_content,
22649                            'attributes' => $attributes
22650                        );
22651
22652        } else {
22653            $this->_tagDepth++;
22654            $tmp = $this->options['linebreak'];
22655            foreach ($array as $key => $value) {
22656                // do indentation
22657                if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
22658                    $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
22659                }
22660
22661                // copy key
22662                $origKey = $key;
22663                // key cannot be used as tagname => use default tag
22664                $valid = XML_Util::isValidName($key);
22665                if (PEAR::isError($valid)) {
22666                    if ($this->options['classAsTagName'] && is_object($value)) {
22667                        $key = get_class($value);
22668                    } else {
22669                        $key = $this->options['defaultTagName'];
22670                    }
22671                }
22672                $atts = array();
22673                if ($this->options['typeHints'] === true) {
22674                    $atts[$this->options['typeAttribute']] = gettype($value);
22675                    if ($key !== $origKey) {
22676                        $atts[$this->options['keyAttribute']] = (string)$origKey;
22677                    }
22678
22679                }
22680                if ($this->options['beautifyFilelist'] && $key == 'dir') {
22681                    if (!isset($this->_curdir)) {
22682                        $this->_curdir = '';
22683                    }
22684                    $savedir = $this->_curdir;
22685                    if (isset($value['attribs'])) {
22686                        if ($value['attribs']['name'] == '/') {
22687                            $this->_curdir = '/';
22688                        } else {
22689                            $this->_curdir .= '/' . $value['attribs']['name'];
22690                        }
22691                    }
22692                }
22693
22694                if (is_string($value) && $value && ($value{strlen($value) - 1} == "\n")) {
22695                    $value .= str_repeat($this->options['indent'], $this->_tagDepth);
22696                }
22697                $tmp .= $this->_createXMLTag(array(
22698                                                    'qname'      => $key,
22699                                                    'attributes' => $atts,
22700                                                    'content'    => $value )
22701                                            );
22702                if ($this->options['beautifyFilelist'] && $key == 'dir') {
22703                    if (isset($value['attribs'])) {
22704                        $tmp .= ' <!-- ' . $this->_curdir . ' -->';
22705                        if (empty($savedir)) {
22706                            unset($this->_curdir);
22707                        } else {
22708                            $this->_curdir = $savedir;
22709                        }
22710                    }
22711                }
22712                $tmp .= $this->options['linebreak'];
22713            }
22714
22715            $this->_tagDepth--;
22716            if ($this->options['indent']!==null && $this->_tagDepth>0) {
22717                $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
22718            }
22719
22720            if (trim($tmp) === '') {
22721                $tmp = null;
22722            }
22723
22724            $tag = array(
22725                'qname'      => $tagName,
22726                'content'    => $tmp,
22727                'attributes' => $attributes
22728            );
22729        }
22730        if ($this->options['typeHints'] === true) {
22731            if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
22732                $tag['attributes'][$this->options['typeAttribute']] = 'array';
22733            }
22734        }
22735
22736        $string = $this->_createXMLTag($tag, false);
22737        return $string;
22738    }
22739
22740   /**
22741    * create a tag from an array
22742    * this method awaits an array in the following format
22743    * array(
22744    *       'qname'        => $tagName,
22745    *       'attributes'   => array(),
22746    *       'content'      => $content,      // optional
22747    *       'namespace'    => $namespace     // optional
22748    *       'namespaceUri' => $namespaceUri  // optional
22749    *   )
22750    *
22751    * @access   private
22752    * @param    array   $tag tag definition
22753    * @param    boolean $replaceEntities whether to replace XML entities in content or not
22754    * @return   string  $string XML tag
22755    */
22756    function _createXMLTag($tag, $replaceEntities = true)
22757    {
22758        if ($this->options['indentAttributes'] !== false) {
22759            $multiline = true;
22760            $indent    = str_repeat($this->options['indent'], $this->_tagDepth);
22761
22762            if ($this->options['indentAttributes'] == '_auto') {
22763                $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
22764
22765            } else {
22766                $indent .= $this->options['indentAttributes'];
22767            }
22768        } else {
22769            $indent = $multiline = false;
22770        }
22771
22772        if (is_array($tag['content'])) {
22773            if (empty($tag['content'])) {
22774                $tag['content'] = '';
22775            }
22776        } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
22777            $tag['content'] = '';
22778        }
22779
22780        if (is_scalar($tag['content']) || is_null($tag['content'])) {
22781            if ($this->options['encoding'] == 'UTF-8' &&
22782                  version_compare(phpversion(), '5.0.0', 'lt')
22783            ) {
22784                $tag['content'] = utf8_encode($tag['content']);
22785            }
22786
22787            if ($replaceEntities === true) {
22788                $replaceEntities = XML_UTIL_ENTITIES_XML;
22789            }
22790
22791            $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak']);
22792        } elseif (is_array($tag['content'])) {
22793            $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
22794        } elseif (is_object($tag['content'])) {
22795            $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
22796        } elseif (is_resource($tag['content'])) {
22797            settype($tag['content'], 'string');
22798            $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
22799        }
22800        return  $tag;
22801    }
22802}PEAR-1.9.4/PEAR/PackageFile/Parser/v1.php0000644000076500000240000004031411605156614016413 0ustar  helgistaff<?php
22803/**
22804 * package.xml parsing class, package.xml version 1.0
22805 *
22806 * PHP versions 4 and 5
22807 *
22808 * @category   pear
22809 * @package    PEAR
22810 * @author     Greg Beaver <cellog@php.net>
22811 * @copyright  1997-2009 The Authors
22812 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
22813 * @version    CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $
22814 * @link       http://pear.php.net/package/PEAR
22815 * @since      File available since Release 1.4.0a1
22816 */
22817/**
22818 * package.xml abstraction class
22819 */
22820require_once 'PEAR/PackageFile/v1.php';
22821/**
22822 * Parser for package.xml version 1.0
22823 * @category   pear
22824 * @package    PEAR
22825 * @author     Greg Beaver <cellog@php.net>
22826 * @copyright  1997-2009 The Authors
22827 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
22828 * @version    Release: @PEAR-VER@
22829 * @link       http://pear.php.net/package/PEAR
22830 * @since      Class available since Release 1.4.0a1
22831 */
22832class PEAR_PackageFile_Parser_v1
22833{
22834    var $_registry;
22835    var $_config;
22836    var $_logger;
22837    /**
22838     * BC hack to allow PEAR_Common::infoFromString() to sort of
22839     * work with the version 2.0 format - there's no filelist though
22840     * @param PEAR_PackageFile_v2
22841     */
22842    function fromV2($packagefile)
22843    {
22844        $info = $packagefile->getArray(true);
22845        $ret = new PEAR_PackageFile_v1;
22846        $ret->fromArray($info['old']);
22847    }
22848
22849    function setConfig(&$c)
22850    {
22851        $this->_config = &$c;
22852        $this->_registry = &$c->getRegistry();
22853    }
22854
22855    function setLogger(&$l)
22856    {
22857        $this->_logger = &$l;
22858    }
22859
22860    /**
22861     * @param string contents of package.xml file, version 1.0
22862     * @return bool success of parsing
22863     */
22864    function &parse($data, $file, $archive = false)
22865    {
22866        if (!extension_loaded('xml')) {
22867            return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension');
22868        }
22869        $xp = xml_parser_create();
22870        if (!$xp) {
22871            $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml');
22872            return $a;
22873        }
22874        xml_set_object($xp, $this);
22875        xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0');
22876        xml_set_character_data_handler($xp, '_pkginfo_cdata_1_0');
22877        xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
22878
22879        $this->element_stack = array();
22880        $this->_packageInfo = array('provides' => array());
22881        $this->current_element = false;
22882        unset($this->dir_install);
22883        $this->_packageInfo['filelist'] = array();
22884        $this->filelist =& $this->_packageInfo['filelist'];
22885        $this->dir_names = array();
22886        $this->in_changelog = false;
22887        $this->d_i = 0;
22888        $this->cdata = '';
22889        $this->_isValid = true;
22890
22891        if (!xml_parse($xp, $data, 1)) {
22892            $code = xml_get_error_code($xp);
22893            $line = xml_get_current_line_number($xp);
22894            xml_parser_free($xp);
22895            $a = &PEAR::raiseError(sprintf("XML error: %s at line %d",
22896                           $str = xml_error_string($code), $line), 2);
22897            return $a;
22898        }
22899
22900        xml_parser_free($xp);
22901
22902        $pf = new PEAR_PackageFile_v1;
22903        $pf->setConfig($this->_config);
22904        if (isset($this->_logger)) {
22905            $pf->setLogger($this->_logger);
22906        }
22907        $pf->setPackagefile($file, $archive);
22908        $pf->fromArray($this->_packageInfo);
22909        return $pf;
22910    }
22911    // {{{ _unIndent()
22912
22913    /**
22914     * Unindent given string
22915     *
22916     * @param string $str The string that has to be unindented.
22917     * @return string
22918     * @access private
22919     */
22920    function _unIndent($str)
22921    {
22922        // remove leading newlines
22923        $str = preg_replace('/^[\r\n]+/', '', $str);
22924        // find whitespace at the beginning of the first line
22925        $indent_len = strspn($str, " \t");
22926        $indent = substr($str, 0, $indent_len);
22927        $data = '';
22928        // remove the same amount of whitespace from following lines
22929        foreach (explode("\n", $str) as $line) {
22930            if (substr($line, 0, $indent_len) == $indent) {
22931                $data .= substr($line, $indent_len) . "\n";
22932            } elseif (trim(substr($line, 0, $indent_len))) {
22933                $data .= ltrim($line);
22934            }
22935        }
22936        return $data;
22937    }
22938
22939    // Support for package DTD v1.0:
22940    // {{{ _element_start_1_0()
22941
22942    /**
22943     * XML parser callback for ending elements.  Used for version 1.0
22944     * packages.
22945     *
22946     * @param resource  $xp    XML parser resource
22947     * @param string    $name  name of ending element
22948     *
22949     * @return void
22950     *
22951     * @access private
22952     */
22953    function _element_start_1_0($xp, $name, $attribs)
22954    {
22955        array_push($this->element_stack, $name);
22956        $this->current_element = $name;
22957        $spos = sizeof($this->element_stack) - 2;
22958        $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
22959        $this->current_attributes = $attribs;
22960        $this->cdata = '';
22961        switch ($name) {
22962            case 'dir':
22963                if ($this->in_changelog) {
22964                    break;
22965                }
22966                if (array_key_exists('name', $attribs) && $attribs['name'] != '/') {
22967                    $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
22968                        $attribs['name']);
22969                    if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) {
22970                        $attribs['name'] = substr($attribs['name'], 0,
22971                            strlen($attribs['name']) - 1);
22972                    }
22973                    if (strpos($attribs['name'], '/') === 0) {
22974                        $attribs['name'] = substr($attribs['name'], 1);
22975                    }
22976                    $this->dir_names[] = $attribs['name'];
22977                }
22978                if (isset($attribs['baseinstalldir'])) {
22979                    $this->dir_install = $attribs['baseinstalldir'];
22980                }
22981                if (isset($attribs['role'])) {
22982                    $this->dir_role = $attribs['role'];
22983                }
22984                break;
22985            case 'file':
22986                if ($this->in_changelog) {
22987                    break;
22988                }
22989                if (isset($attribs['name'])) {
22990                    $path = '';
22991                    if (count($this->dir_names)) {
22992                        foreach ($this->dir_names as $dir) {
22993                            $path .= $dir . '/';
22994                        }
22995                    }
22996                    $path .= preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
22997                        $attribs['name']);
22998                    unset($attribs['name']);
22999                    $this->current_path = $path;
23000                    $this->filelist[$path] = $attribs;
23001                    // Set the baseinstalldir only if the file don't have this attrib
23002                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
23003                        isset($this->dir_install))
23004                    {
23005                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
23006                    }
23007                    // Set the Role
23008                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
23009                        $this->filelist[$path]['role'] = $this->dir_role;
23010                    }
23011                }
23012                break;
23013            case 'replace':
23014                if (!$this->in_changelog) {
23015                    $this->filelist[$this->current_path]['replacements'][] = $attribs;
23016                }
23017                break;
23018            case 'maintainers':
23019                $this->_packageInfo['maintainers'] = array();
23020                $this->m_i = 0; // maintainers array index
23021                break;
23022            case 'maintainer':
23023                // compatibility check
23024                if (!isset($this->_packageInfo['maintainers'])) {
23025                    $this->_packageInfo['maintainers'] = array();
23026                    $this->m_i = 0;
23027                }
23028                $this->_packageInfo['maintainers'][$this->m_i] = array();
23029                $this->current_maintainer =& $this->_packageInfo['maintainers'][$this->m_i];
23030                break;
23031            case 'changelog':
23032                $this->_packageInfo['changelog'] = array();
23033                $this->c_i = 0; // changelog array index
23034                $this->in_changelog = true;
23035                break;
23036            case 'release':
23037                if ($this->in_changelog) {
23038                    $this->_packageInfo['changelog'][$this->c_i] = array();
23039                    $this->current_release = &$this->_packageInfo['changelog'][$this->c_i];
23040                } else {
23041                    $this->current_release = &$this->_packageInfo;
23042                }
23043                break;
23044            case 'deps':
23045                if (!$this->in_changelog) {
23046                    $this->_packageInfo['release_deps'] = array();
23047                }
23048                break;
23049            case 'dep':
23050                // dependencies array index
23051                if (!$this->in_changelog) {
23052                    $this->d_i++;
23053                    isset($attribs['type']) ? ($attribs['type'] = strtolower($attribs['type'])) : false;
23054                    $this->_packageInfo['release_deps'][$this->d_i] = $attribs;
23055                }
23056                break;
23057            case 'configureoptions':
23058                if (!$this->in_changelog) {
23059                    $this->_packageInfo['configure_options'] = array();
23060                }
23061                break;
23062            case 'configureoption':
23063                if (!$this->in_changelog) {
23064                    $this->_packageInfo['configure_options'][] = $attribs;
23065                }
23066                break;
23067            case 'provides':
23068                if (empty($attribs['type']) || empty($attribs['name'])) {
23069                    break;
23070                }
23071                $attribs['explicit'] = true;
23072                $this->_packageInfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
23073                break;
23074            case 'package' :
23075                if (isset($attribs['version'])) {
23076                    $this->_packageInfo['xsdversion'] = trim($attribs['version']);
23077                } else {
23078                    $this->_packageInfo['xsdversion'] = '1.0';
23079                }
23080                if (isset($attribs['packagerversion'])) {
23081                    $this->_packageInfo['packagerversion'] = $attribs['packagerversion'];
23082                }
23083                break;
23084        }
23085    }
23086
23087    // }}}
23088    // {{{ _element_end_1_0()
23089
23090    /**
23091     * XML parser callback for ending elements.  Used for version 1.0
23092     * packages.
23093     *
23094     * @param resource  $xp    XML parser resource
23095     * @param string    $name  name of ending element
23096     *
23097     * @return void
23098     *
23099     * @access private
23100     */
23101    function _element_end_1_0($xp, $name)
23102    {
23103        $data = trim($this->cdata);
23104        switch ($name) {
23105            case 'name':
23106                switch ($this->prev_element) {
23107                    case 'package':
23108                        $this->_packageInfo['package'] = $data;
23109                        break;
23110                    case 'maintainer':
23111                        $this->current_maintainer['name'] = $data;
23112                        break;
23113                }
23114                break;
23115            case 'extends' :
23116                $this->_packageInfo['extends'] = $data;
23117                break;
23118            case 'summary':
23119                $this->_packageInfo['summary'] = $data;
23120                break;
23121            case 'description':
23122                $data = $this->_unIndent($this->cdata);
23123                $this->_packageInfo['description'] = $data;
23124                break;
23125            case 'user':
23126                $this->current_maintainer['handle'] = $data;
23127                break;
23128            case 'email':
23129                $this->current_maintainer['email'] = $data;
23130                break;
23131            case 'role':
23132                $this->current_maintainer['role'] = $data;
23133                break;
23134            case 'version':
23135                if ($this->in_changelog) {
23136                    $this->current_release['version'] = $data;
23137                } else {
23138                    $this->_packageInfo['version'] = $data;
23139                }
23140                break;
23141            case 'date':
23142                if ($this->in_changelog) {
23143                    $this->current_release['release_date'] = $data;
23144                } else {
23145                    $this->_packageInfo['release_date'] = $data;
23146                }
23147                break;
23148            case 'notes':
23149                // try to "de-indent" release notes in case someone
23150                // has been over-indenting their xml ;-)
23151                // Trim only on the right side
23152                $data = rtrim($this->_unIndent($this->cdata));
23153                if ($this->in_changelog) {
23154                    $this->current_release['release_notes'] = $data;
23155                } else {
23156                    $this->_packageInfo['release_notes'] = $data;
23157                }
23158                break;
23159            case 'warnings':
23160                if ($this->in_changelog) {
23161                    $this->current_release['release_warnings'] = $data;
23162                } else {
23163                    $this->_packageInfo['release_warnings'] = $data;
23164                }
23165                break;
23166            case 'state':
23167                if ($this->in_changelog) {
23168                    $this->current_release['release_state'] = $data;
23169                } else {
23170                    $this->_packageInfo['release_state'] = $data;
23171                }
23172                break;
23173            case 'license':
23174                if ($this->in_changelog) {
23175                    $this->current_release['release_license'] = $data;
23176                } else {
23177                    $this->_packageInfo['release_license'] = $data;
23178                }
23179                break;
23180            case 'dep':
23181                if ($data && !$this->in_changelog) {
23182                    $this->_packageInfo['release_deps'][$this->d_i]['name'] = $data;
23183                }
23184                break;
23185            case 'dir':
23186                if ($this->in_changelog) {
23187                    break;
23188                }
23189                array_pop($this->dir_names);
23190                break;
23191            case 'file':
23192                if ($this->in_changelog) {
23193                    break;
23194                }
23195                if ($data) {
23196                    $path = '';
23197                    if (count($this->dir_names)) {
23198                        foreach ($this->dir_names as $dir) {
23199                            $path .= $dir . '/';
23200                        }
23201                    }
23202                    $path .= $data;
23203                    $this->filelist[$path] = $this->current_attributes;
23204                    // Set the baseinstalldir only if the file don't have this attrib
23205                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
23206                        isset($this->dir_install))
23207                    {
23208                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
23209                    }
23210                    // Set the Role
23211                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
23212                        $this->filelist[$path]['role'] = $this->dir_role;
23213                    }
23214                }
23215                break;
23216            case 'maintainer':
23217                if (empty($this->_packageInfo['maintainers'][$this->m_i]['role'])) {
23218                    $this->_packageInfo['maintainers'][$this->m_i]['role'] = 'lead';
23219                }
23220                $this->m_i++;
23221                break;
23222            case 'release':
23223                if ($this->in_changelog) {
23224                    $this->c_i++;
23225                }
23226                break;
23227            case 'changelog':
23228                $this->in_changelog = false;
23229                break;
23230        }
23231        array_pop($this->element_stack);
23232        $spos = sizeof($this->element_stack) - 1;
23233        $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
23234        $this->cdata = '';
23235    }
23236
23237    // }}}
23238    // {{{ _pkginfo_cdata_1_0()
23239
23240    /**
23241     * XML parser callback for character data.  Used for version 1.0
23242     * packages.
23243     *
23244     * @param resource  $xp    XML parser resource
23245     * @param string    $name  character data
23246     *
23247     * @return void
23248     *
23249     * @access private
23250     */
23251    function _pkginfo_cdata_1_0($xp, $data)
23252    {
23253        if (isset($this->cdata)) {
23254            $this->cdata .= $data;
23255        }
23256    }
23257
23258    // }}}
23259}
23260?>PEAR-1.9.4/PEAR/PackageFile/Parser/v2.php0000644000076500000240000000621511605156614016416 0ustar  helgistaff<?php
23261/**
23262 * package.xml parsing class, package.xml version 2.0
23263 *
23264 * PHP versions 4 and 5
23265 *
23266 * @category   pear
23267 * @package    PEAR
23268 * @author     Greg Beaver <cellog@php.net>
23269 * @copyright  1997-2009 The Authors
23270 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
23271 * @version    CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
23272 * @link       http://pear.php.net/package/PEAR
23273 * @since      File available since Release 1.4.0a1
23274 */
23275/**
23276 * base xml parser class
23277 */
23278require_once 'PEAR/XMLParser.php';
23279require_once 'PEAR/PackageFile/v2.php';
23280/**
23281 * Parser for package.xml version 2.0
23282 * @category   pear
23283 * @package    PEAR
23284 * @author     Greg Beaver <cellog@php.net>
23285 * @copyright  1997-2009 The Authors
23286 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
23287 * @version    Release: @PEAR-VER@
23288 * @link       http://pear.php.net/package/PEAR
23289 * @since      Class available since Release 1.4.0a1
23290 */
23291class PEAR_PackageFile_Parser_v2 extends PEAR_XMLParser
23292{
23293    var $_config;
23294    var $_logger;
23295    var $_registry;
23296
23297    function setConfig(&$c)
23298    {
23299        $this->_config = &$c;
23300        $this->_registry = &$c->getRegistry();
23301    }
23302
23303    function setLogger(&$l)
23304    {
23305        $this->_logger = &$l;
23306    }
23307    /**
23308     * Unindent given string
23309     *
23310     * @param string $str The string that has to be unindented.
23311     * @return string
23312     * @access private
23313     */
23314    function _unIndent($str)
23315    {
23316        // remove leading newlines
23317        $str = preg_replace('/^[\r\n]+/', '', $str);
23318        // find whitespace at the beginning of the first line
23319        $indent_len = strspn($str, " \t");
23320        $indent = substr($str, 0, $indent_len);
23321        $data = '';
23322        // remove the same amount of whitespace from following lines
23323        foreach (explode("\n", $str) as $line) {
23324            if (substr($line, 0, $indent_len) == $indent) {
23325                $data .= substr($line, $indent_len) . "\n";
23326            } else {
23327                $data .= $line . "\n";
23328            }
23329        }
23330        return $data;
23331    }
23332
23333    /**
23334     * post-process data
23335     *
23336     * @param string $data
23337     * @param string $element element name
23338     */
23339    function postProcess($data, $element)
23340    {
23341        if ($element == 'notes') {
23342            return trim($this->_unIndent($data));
23343        }
23344        return trim($data);
23345    }
23346
23347    /**
23348     * @param string
23349     * @param string file name of the package.xml
23350     * @param string|false name of the archive this package.xml came from, if any
23351     * @param string class name to instantiate and return.  This must be PEAR_PackageFile_v2 or
23352     *               a subclass
23353     * @return PEAR_PackageFile_v2
23354     */
23355    function &parse($data, $file, $archive = false, $class = 'PEAR_PackageFile_v2')
23356    {
23357        if (PEAR::isError($err = parent::parse($data, $file))) {
23358            return $err;
23359        }
23360
23361        $ret = new $class;
23362        $ret->encoding = $this->encoding;
23363        $ret->setConfig($this->_config);
23364        if (isset($this->_logger)) {
23365            $ret->setLogger($this->_logger);
23366        }
23367
23368        $ret->fromArray($this->_unserializedData);
23369        $ret->setPackagefile($file, $archive);
23370        return $ret;
23371    }
23372}PEAR-1.9.4/PEAR/PackageFile/v2/rw.php0000644000076500000240000017321011605156614015612 0ustar  helgistaff<?php
23373/**
23374 * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
23375 *
23376 * PHP versions 4 and 5
23377 *
23378 * @category   pear
23379 * @package    PEAR
23380 * @author     Greg Beaver <cellog@php.net>
23381 * @copyright  1997-2009 The Authors
23382 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
23383 * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
23384 * @link       http://pear.php.net/package/PEAR
23385 * @since      File available since Release 1.4.0a8
23386 */
23387/**
23388 * For base class
23389 */
23390require_once 'PEAR/PackageFile/v2.php';
23391/**
23392 * @category   pear
23393 * @package    PEAR
23394 * @author     Greg Beaver <cellog@php.net>
23395 * @copyright  1997-2009 The Authors
23396 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
23397 * @version    Release: 1.9.4
23398 * @link       http://pear.php.net/package/PEAR
23399 * @since      Class available since Release 1.4.0a8
23400 */
23401class PEAR_PackageFile_v2_rw extends PEAR_PackageFile_v2
23402{
23403    /**
23404     * @param string Extension name
23405     * @return bool success of operation
23406     */
23407    function setProvidesExtension($extension)
23408    {
23409        if (in_array($this->getPackageType(),
23410              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
23411            if (!isset($this->_packageInfo['providesextension'])) {
23412                // ensure that the channel tag is set up in the right location
23413                $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
23414                    array('usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
23415                    'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
23416                    'bundle', 'changelog'),
23417                    $extension, 'providesextension');
23418            }
23419            $this->_packageInfo['providesextension'] = $extension;
23420            return true;
23421        }
23422        return false;
23423    }
23424
23425    function setPackage($package)
23426    {
23427        $this->_isValid = 0;
23428        if (!isset($this->_packageInfo['attribs'])) {
23429            $this->_packageInfo = array_merge(array('attribs' => array(
23430                                 'version' => '2.0',
23431                                 'xmlns' => 'http://pear.php.net/dtd/package-2.0',
23432                                 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
23433                                 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
23434                                 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
23435    http://pear.php.net/dtd/tasks-1.0.xsd
23436    http://pear.php.net/dtd/package-2.0
23437    http://pear.php.net/dtd/package-2.0.xsd',
23438                             )), $this->_packageInfo);
23439        }
23440        if (!isset($this->_packageInfo['name'])) {
23441            return $this->_packageInfo = array_merge(array('name' => $package),
23442                $this->_packageInfo);
23443        }
23444        $this->_packageInfo['name'] = $package;
23445    }
23446
23447    /**
23448     * set this as a package.xml version 2.1
23449     * @access private
23450     */
23451    function _setPackageVersion2_1()
23452    {
23453        $info = array(
23454                                 'version' => '2.1',
23455                                 'xmlns' => 'http://pear.php.net/dtd/package-2.1',
23456                                 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
23457                                 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
23458                                 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
23459    http://pear.php.net/dtd/tasks-1.0.xsd
23460    http://pear.php.net/dtd/package-2.1
23461    http://pear.php.net/dtd/package-2.1.xsd',
23462                             );
23463        if (!isset($this->_packageInfo['attribs'])) {
23464            $this->_packageInfo = array_merge(array('attribs' => $info), $this->_packageInfo);
23465        } else {
23466            $this->_packageInfo['attribs'] = $info;
23467        }
23468    }
23469
23470    function setUri($uri)
23471    {
23472        unset($this->_packageInfo['channel']);
23473        $this->_isValid = 0;
23474        if (!isset($this->_packageInfo['uri'])) {
23475            // ensure that the uri tag is set up in the right location
23476            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
23477                array('extends', 'summary', 'description', 'lead',
23478                'developer', 'contributor', 'helper', 'date', 'time', 'version',
23479                'stability', 'license', 'notes', 'contents', 'compatible',
23480                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
23481                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
23482                'extbinrelease', 'bundle', 'changelog'), $uri, 'uri');
23483        }
23484        $this->_packageInfo['uri'] = $uri;
23485    }
23486
23487    function setChannel($channel)
23488    {
23489        unset($this->_packageInfo['uri']);
23490        $this->_isValid = 0;
23491        if (!isset($this->_packageInfo['channel'])) {
23492            // ensure that the channel tag is set up in the right location
23493            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
23494                array('extends', 'summary', 'description', 'lead',
23495                'developer', 'contributor', 'helper', 'date', 'time', 'version',
23496                'stability', 'license', 'notes', 'contents', 'compatible',
23497                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
23498                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
23499                'extbinrelease', 'bundle', 'changelog'), $channel, 'channel');
23500        }
23501        $this->_packageInfo['channel'] = $channel;
23502    }
23503
23504    function setExtends($extends)
23505    {
23506        $this->_isValid = 0;
23507        if (!isset($this->_packageInfo['extends'])) {
23508            // ensure that the extends tag is set up in the right location
23509            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
23510                array('summary', 'description', 'lead',
23511                'developer', 'contributor', 'helper', 'date', 'time', 'version',
23512                'stability', 'license', 'notes', 'contents', 'compatible',
23513                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
23514                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
23515                'extbinrelease', 'bundle', 'changelog'), $extends, 'extends');
23516        }
23517        $this->_packageInfo['extends'] = $extends;
23518    }
23519
23520    function setSummary($summary)
23521    {
23522        $this->_isValid = 0;
23523        if (!isset($this->_packageInfo['summary'])) {
23524            // ensure that the summary tag is set up in the right location
23525            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
23526                array('description', 'lead',
23527                'developer', 'contributor', 'helper', 'date', 'time', 'version',
23528                'stability', 'license', 'notes', 'contents', 'compatible',
23529                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
23530                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
23531                'extbinrelease', 'bundle', 'changelog'), $summary, 'summary');
23532        }
23533        $this->_packageInfo['summary'] = $summary;
23534    }
23535
23536    function setDescription($desc)
23537    {
23538        $this->_isValid = 0;
23539        if (!isset($this->_packageInfo['description'])) {
23540            // ensure that the description tag is set up in the right location
23541            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
23542                array('lead',
23543                'developer', 'contributor', 'helper', 'date', 'time', 'version',
23544                'stability', 'license', 'notes', 'contents', 'compatible',
23545                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
23546                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
23547                'extbinrelease', 'bundle', 'changelog'), $desc, 'description');
23548        }
23549        $this->_packageInfo['description'] = $desc;
23550    }
23551
23552    /**
23553     * Adds a new maintainer - no checking of duplicates is performed, use
23554     * updatemaintainer for that purpose.
23555     */
23556    function addMaintainer($role, $handle, $name, $email, $active = 'yes')
23557    {
23558        if (!in_array($role, array('lead', 'developer', 'contributor', 'helper'))) {
23559            return false;
23560        }
23561        if (isset($this->_packageInfo[$role])) {
23562            if (!isset($this->_packageInfo[$role][0])) {
23563                $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
23564            }
23565            $this->_packageInfo[$role][] =
23566                array(
23567                    'name' => $name,
23568                    'user' => $handle,
23569                    'email' => $email,
23570                    'active' => $active,
23571                );
23572        } else {
23573            $testarr = array('lead',
23574                    'developer', 'contributor', 'helper', 'date', 'time', 'version',
23575                    'stability', 'license', 'notes', 'contents', 'compatible',
23576                    'dependencies', 'providesextension', 'usesrole', 'usestask',
23577                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
23578                    'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog');
23579            foreach (array('lead', 'developer', 'contributor', 'helper') as $testrole) {
23580                array_shift($testarr);
23581                if ($role == $testrole) {
23582                    break;
23583                }
23584            }
23585            if (!isset($this->_packageInfo[$role])) {
23586                // ensure that the extends tag is set up in the right location
23587                $this->_packageInfo = $this->_insertBefore($this->_packageInfo, $testarr,
23588                    array(), $role);
23589            }
23590            $this->_packageInfo[$role] =
23591                array(
23592                    'name' => $name,
23593                    'user' => $handle,
23594                    'email' => $email,
23595                    'active' => $active,
23596                );
23597        }
23598        $this->_isValid = 0;
23599    }
23600
23601    function updateMaintainer($newrole, $handle, $name, $email, $active = 'yes')
23602    {
23603        $found = false;
23604        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
23605            if (!isset($this->_packageInfo[$role])) {
23606                continue;
23607            }
23608            $info = $this->_packageInfo[$role];
23609            if (!isset($info[0])) {
23610                if ($info['user'] == $handle) {
23611                    $found = true;
23612                    break;
23613                }
23614            }
23615            foreach ($info as $i => $maintainer) {
23616                if ($maintainer['user'] == $handle) {
23617                    $found = $i;
23618                    break 2;
23619                }
23620            }
23621        }
23622        if ($found === false) {
23623            return $this->addMaintainer($newrole, $handle, $name, $email, $active);
23624        }
23625        if ($found !== false) {
23626            if ($found === true) {
23627                unset($this->_packageInfo[$role]);
23628            } else {
23629                unset($this->_packageInfo[$role][$found]);
23630                $this->_packageInfo[$role] = array_values($this->_packageInfo[$role]);
23631            }
23632        }
23633        $this->addMaintainer($newrole, $handle, $name, $email, $active);
23634        $this->_isValid = 0;
23635    }
23636
23637    function deleteMaintainer($handle)
23638    {
23639        $found = false;
23640        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
23641            if (!isset($this->_packageInfo[$role])) {
23642                continue;
23643            }
23644            if (!isset($this->_packageInfo[$role][0])) {
23645                $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
23646            }
23647            foreach ($this->_packageInfo[$role] as $i => $maintainer) {
23648                if ($maintainer['user'] == $handle) {
23649                    $found = $i;
23650                    break;
23651                }
23652            }
23653            if ($found !== false) {
23654                unset($this->_packageInfo[$role][$found]);
23655                if (!count($this->_packageInfo[$role]) && $role == 'lead') {
23656                    $this->_isValid = 0;
23657                }
23658                if (!count($this->_packageInfo[$role])) {
23659                    unset($this->_packageInfo[$role]);
23660                    return true;
23661                }
23662                $this->_packageInfo[$role] =
23663                    array_values($this->_packageInfo[$role]);
23664                if (count($this->_packageInfo[$role]) == 1) {
23665                    $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
23666                }
23667                return true;
23668            }
23669            if (count($this->_packageInfo[$role]) == 1) {
23670                $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
23671            }
23672        }
23673        return false;
23674    }
23675
23676    function setReleaseVersion($version)
23677    {
23678        if (isset($this->_packageInfo['version']) &&
23679              isset($this->_packageInfo['version']['release'])) {
23680            unset($this->_packageInfo['version']['release']);
23681        }
23682        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
23683            'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
23684                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
23685                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
23686                'extbinrelease', 'bundle', 'changelog'),
23687            'release' => array('api')));
23688        $this->_isValid = 0;
23689    }
23690
23691    function setAPIVersion($version)
23692    {
23693        if (isset($this->_packageInfo['version']) &&
23694              isset($this->_packageInfo['version']['api'])) {
23695            unset($this->_packageInfo['version']['api']);
23696        }
23697        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
23698            'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
23699                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
23700                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
23701                'extbinrelease', 'bundle', 'changelog'),
23702            'api' => array()));
23703        $this->_isValid = 0;
23704    }
23705
23706    /**
23707     * snapshot|devel|alpha|beta|stable
23708     */
23709    function setReleaseStability($state)
23710    {
23711        if (isset($this->_packageInfo['stability']) &&
23712              isset($this->_packageInfo['stability']['release'])) {
23713            unset($this->_packageInfo['stability']['release']);
23714        }
23715        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
23716            'stability' => array('license', 'notes', 'contents', 'compatible',
23717                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
23718                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
23719                'extbinrelease', 'bundle', 'changelog'),
23720            'release' => array('api')));
23721        $this->_isValid = 0;
23722    }
23723
23724    /**
23725     * @param devel|alpha|beta|stable
23726     */
23727    function setAPIStability($state)
23728    {
23729        if (isset($this->_packageInfo['stability']) &&
23730              isset($this->_packageInfo['stability']['api'])) {
23731            unset($this->_packageInfo['stability']['api']);
23732        }
23733        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
23734            'stability' => array('license', 'notes', 'contents', 'compatible',
23735                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
23736                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
23737                'extbinrelease', 'bundle', 'changelog'),
23738            'api' => array()));
23739        $this->_isValid = 0;
23740    }
23741
23742    function setLicense($license, $uri = false, $filesource = false)
23743    {
23744        if (!isset($this->_packageInfo['license'])) {
23745            // ensure that the license tag is set up in the right location
23746            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
23747                array('notes', 'contents', 'compatible',
23748                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
23749                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
23750                'extbinrelease', 'bundle', 'changelog'), 0, 'license');
23751        }
23752        if ($uri || $filesource) {
23753            $attribs = array();
23754            if ($uri) {
23755                $attribs['uri'] = $uri;
23756            }
23757            $uri = true; // for test below
23758            if ($filesource) {
23759                $attribs['filesource'] = $filesource;
23760            }
23761        }
23762        $license = $uri ? array('attribs' => $attribs, '_content' => $license) : $license;
23763        $this->_packageInfo['license'] = $license;
23764        $this->_isValid = 0;
23765    }
23766
23767    function setNotes($notes)
23768    {
23769        $this->_isValid = 0;
23770        if (!isset($this->_packageInfo['notes'])) {
23771            // ensure that the notes tag is set up in the right location
23772            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
23773                array('contents', 'compatible',
23774                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
23775                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
23776                'extbinrelease', 'bundle', 'changelog'), $notes, 'notes');
23777        }
23778        $this->_packageInfo['notes'] = $notes;
23779    }
23780
23781    /**
23782     * This is only used at install-time, after all serialization
23783     * is over.
23784     * @param string file name
23785     * @param string installed path
23786     */
23787    function setInstalledAs($file, $path)
23788    {
23789        if ($path) {
23790            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
23791        }
23792        unset($this->_packageInfo['filelist'][$file]['installed_as']);
23793    }
23794
23795    /**
23796     * This is only used at install-time, after all serialization
23797     * is over.
23798     */
23799    function installedFile($file, $atts)
23800    {
23801        if (isset($this->_packageInfo['filelist'][$file])) {
23802            $this->_packageInfo['filelist'][$file] =
23803                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
23804        } else {
23805            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
23806        }
23807    }
23808
23809    /**
23810     * Reset the listing of package contents
23811     * @param string base installation dir for the whole package, if any
23812     */
23813    function clearContents($baseinstall = false)
23814    {
23815        $this->_filesValid = false;
23816        $this->_isValid = 0;
23817        if (!isset($this->_packageInfo['contents'])) {
23818            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
23819                array('compatible',
23820                    'dependencies', 'providesextension', 'usesrole', 'usestask',
23821                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
23822                    'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
23823                    'bundle', 'changelog'), array(), 'contents');
23824        }
23825        if ($this->getPackageType() != 'bundle') {
23826            $this->_packageInfo['contents'] =
23827                array('dir' => array('attribs' => array('name' => '/')));
23828            if ($baseinstall) {
23829                $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'] = $baseinstall;
23830            }
23831        } else {
23832            $this->_packageInfo['contents'] = array('bundledpackage' => array());
23833        }
23834    }
23835
23836    /**
23837     * @param string relative path of the bundled package.
23838     */
23839    function addBundledPackage($path)
23840    {
23841        if ($this->getPackageType() != 'bundle') {
23842            return false;
23843        }
23844        $this->_filesValid = false;
23845        $this->_isValid = 0;
23846        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $path, array(
23847                'contents' => array('compatible', 'dependencies', 'providesextension',
23848                'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
23849                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
23850                'bundle', 'changelog'),
23851                'bundledpackage' => array()));
23852    }
23853
23854    /**
23855     * @param string file name
23856     * @param PEAR_Task_Common a read/write task
23857     */
23858    function addTaskToFile($filename, $task)
23859    {
23860        if (!method_exists($task, 'getXml')) {
23861            return false;
23862        }
23863        if (!method_exists($task, 'getName')) {
23864            return false;
23865        }
23866        if (!method_exists($task, 'validate')) {
23867            return false;
23868        }
23869        if (!$task->validate()) {
23870            return false;
23871        }
23872        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
23873            return false;
23874        }
23875        $this->getTasksNs(); // discover the tasks namespace if not done already
23876        $files = $this->_packageInfo['contents']['dir']['file'];
23877        if (!isset($files[0])) {
23878            $files = array($files);
23879            $ind = false;
23880        } else {
23881            $ind = true;
23882        }
23883        foreach ($files as $i => $file) {
23884            if (isset($file['attribs'])) {
23885                if ($file['attribs']['name'] == $filename) {
23886                    if ($ind) {
23887                        $t = isset($this->_packageInfo['contents']['dir']['file'][$i]
23888                              ['attribs'][$this->_tasksNs .
23889                              ':' . $task->getName()]) ?
23890                              $this->_packageInfo['contents']['dir']['file'][$i]
23891                              ['attribs'][$this->_tasksNs .
23892                              ':' . $task->getName()] : false;
23893                        if ($t && !isset($t[0])) {
23894                            $this->_packageInfo['contents']['dir']['file'][$i]
23895                                [$this->_tasksNs . ':' . $task->getName()] = array($t);
23896                        }
23897                        $this->_packageInfo['contents']['dir']['file'][$i][$this->_tasksNs .
23898                            ':' . $task->getName()][] = $task->getXml();
23899                    } else {
23900                        $t = isset($this->_packageInfo['contents']['dir']['file']
23901                              ['attribs'][$this->_tasksNs .
23902                              ':' . $task->getName()]) ? $this->_packageInfo['contents']['dir']['file']
23903                              ['attribs'][$this->_tasksNs .
23904                              ':' . $task->getName()] : false;
23905                        if ($t && !isset($t[0])) {
23906                            $this->_packageInfo['contents']['dir']['file']
23907                                [$this->_tasksNs . ':' . $task->getName()] = array($t);
23908                        }
23909                        $this->_packageInfo['contents']['dir']['file'][$this->_tasksNs .
23910                            ':' . $task->getName()][] = $task->getXml();
23911                    }
23912                    return true;
23913                }
23914            }
23915        }
23916        return false;
23917    }
23918
23919    /**
23920     * @param string path to the file
23921     * @param string filename
23922     * @param array extra attributes
23923     */
23924    function addFile($dir, $file, $attrs)
23925    {
23926        if ($this->getPackageType() == 'bundle') {
23927            return false;
23928        }
23929        $this->_filesValid = false;
23930        $this->_isValid = 0;
23931        $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
23932        if ($dir == '/' || $dir == '') {
23933            $dir = '';
23934        } else {
23935            $dir .= '/';
23936        }
23937        $attrs['name'] = $dir . $file;
23938        if (!isset($this->_packageInfo['contents'])) {
23939            // ensure that the contents tag is set up
23940            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
23941                array('compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask',
23942                'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
23943                'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
23944                'bundle', 'changelog'), array(), 'contents');
23945        }
23946        if (isset($this->_packageInfo['contents']['dir']['file'])) {
23947            if (!isset($this->_packageInfo['contents']['dir']['file'][0])) {
23948                $this->_packageInfo['contents']['dir']['file'] =
23949                    array($this->_packageInfo['contents']['dir']['file']);
23950            }
23951            $this->_packageInfo['contents']['dir']['file'][]['attribs'] = $attrs;
23952        } else {
23953            $this->_packageInfo['contents']['dir']['file']['attribs'] = $attrs;
23954        }
23955    }
23956
23957    /**
23958     * @param string Dependent package name
23959     * @param string Dependent package's channel name
23960     * @param string minimum version of specified package that this release is guaranteed to be
23961     *               compatible with
23962     * @param string maximum version of specified package that this release is guaranteed to be
23963     *               compatible with
23964     * @param string versions of specified package that this release is not compatible with
23965     */
23966    function addCompatiblePackage($name, $channel, $min, $max, $exclude = false)
23967    {
23968        $this->_isValid = 0;
23969        $set = array(
23970            'name' => $name,
23971            'channel' => $channel,
23972            'min' => $min,
23973            'max' => $max,
23974        );
23975        if ($exclude) {
23976            $set['exclude'] = $exclude;
23977        }
23978        $this->_isValid = 0;
23979        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
23980                'compatible' => array('dependencies', 'providesextension', 'usesrole', 'usestask',
23981                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
23982                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
23983            ));
23984    }
23985
23986    /**
23987     * Removes the <usesrole> tag entirely
23988     */
23989    function resetUsesrole()
23990    {
23991        if (isset($this->_packageInfo['usesrole'])) {
23992            unset($this->_packageInfo['usesrole']);
23993        }
23994    }
23995
23996    /**
23997     * @param string
23998     * @param string package name or uri
23999     * @param string channel name if non-uri
24000     */
24001    function addUsesrole($role, $packageOrUri, $channel = false) {
24002        $set = array('role' => $role);
24003        if ($channel) {
24004            $set['package'] = $packageOrUri;
24005            $set['channel'] = $channel;
24006        } else {
24007            $set['uri'] = $packageOrUri;
24008        }
24009        $this->_isValid = 0;
24010        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
24011                'usesrole' => array('usestask', 'srcpackage', 'srcuri',
24012                    'phprelease', 'extsrcrelease', 'extbinrelease',
24013                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
24014            ));
24015    }
24016
24017    /**
24018     * Removes the <usestask> tag entirely
24019     */
24020    function resetUsestask()
24021    {
24022        if (isset($this->_packageInfo['usestask'])) {
24023            unset($this->_packageInfo['usestask']);
24024        }
24025    }
24026
24027
24028    /**
24029     * @param string
24030     * @param string package name or uri
24031     * @param string channel name if non-uri
24032     */
24033    function addUsestask($task, $packageOrUri, $channel = false) {
24034        $set = array('task' => $task);
24035        if ($channel) {
24036            $set['package'] = $packageOrUri;
24037            $set['channel'] = $channel;
24038        } else {
24039            $set['uri'] = $packageOrUri;
24040        }
24041        $this->_isValid = 0;
24042        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
24043                'usestask' => array('srcpackage', 'srcuri',
24044                    'phprelease', 'extsrcrelease', 'extbinrelease',
24045                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
24046            ));
24047    }
24048
24049    /**
24050     * Remove all compatible tags
24051     */
24052    function clearCompatible()
24053    {
24054        unset($this->_packageInfo['compatible']);
24055    }
24056
24057    /**
24058     * Reset dependencies prior to adding new ones
24059     */
24060    function clearDeps()
24061    {
24062        if (!isset($this->_packageInfo['dependencies'])) {
24063            $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
24064                array(
24065                    'dependencies' => array('providesextension', 'usesrole', 'usestask',
24066                        'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24067                        'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')));
24068        }
24069        $this->_packageInfo['dependencies'] = array();
24070    }
24071
24072    /**
24073     * @param string minimum PHP version allowed
24074     * @param string maximum PHP version allowed
24075     * @param array $exclude incompatible PHP versions
24076     */
24077    function setPhpDep($min, $max = false, $exclude = false)
24078    {
24079        $this->_isValid = 0;
24080        $dep =
24081            array(
24082                'min' => $min,
24083            );
24084        if ($max) {
24085            $dep['max'] = $max;
24086        }
24087        if ($exclude) {
24088            if (count($exclude) == 1) {
24089                $exclude = $exclude[0];
24090            }
24091            $dep['exclude'] = $exclude;
24092        }
24093        if (isset($this->_packageInfo['dependencies']['required']['php'])) {
24094            $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
24095            $this->_packageInfo['dependencies']['required']['php']),
24096                'warning: PHP dependency already exists, overwriting');
24097            unset($this->_packageInfo['dependencies']['required']['php']);
24098        }
24099        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
24100            array(
24101                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24102                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24103                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24104                'required' => array('optional', 'group'),
24105                'php' => array('pearinstaller', 'package', 'subpackage', 'extension', 'os', 'arch')
24106            ));
24107        return true;
24108    }
24109
24110    /**
24111     * @param string minimum allowed PEAR installer version
24112     * @param string maximum allowed PEAR installer version
24113     * @param string recommended PEAR installer version
24114     * @param array incompatible version of the PEAR installer
24115     */
24116    function setPearinstallerDep($min, $max = false, $recommended = false, $exclude = false)
24117    {
24118        $this->_isValid = 0;
24119        $dep =
24120            array(
24121                'min' => $min,
24122            );
24123        if ($max) {
24124            $dep['max'] = $max;
24125        }
24126        if ($recommended) {
24127            $dep['recommended'] = $recommended;
24128        }
24129        if ($exclude) {
24130            if (count($exclude) == 1) {
24131                $exclude = $exclude[0];
24132            }
24133            $dep['exclude'] = $exclude;
24134        }
24135        if (isset($this->_packageInfo['dependencies']['required']['pearinstaller'])) {
24136            $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
24137            $this->_packageInfo['dependencies']['required']['pearinstaller']),
24138                'warning: PEAR Installer dependency already exists, overwriting');
24139            unset($this->_packageInfo['dependencies']['required']['pearinstaller']);
24140        }
24141        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
24142            array(
24143                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24144                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24145                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24146                'required' => array('optional', 'group'),
24147                'pearinstaller' => array('package', 'subpackage', 'extension', 'os', 'arch')
24148            ));
24149    }
24150
24151    /**
24152     * Mark a package as conflicting with this package
24153     * @param string package name
24154     * @param string package channel
24155     * @param string extension this package provides, if any
24156     * @param string|false minimum version required
24157     * @param string|false maximum version allowed
24158     * @param array|false versions to exclude from installation
24159     */
24160    function addConflictingPackageDepWithChannel($name, $channel,
24161                $providesextension = false, $min = false, $max = false, $exclude = false)
24162    {
24163        $this->_isValid = 0;
24164        $dep = $this->_constructDep($name, $channel, false, $min, $max, false,
24165            $exclude, $providesextension, false, true);
24166        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
24167            array(
24168                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24169                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24170                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24171                'required' => array('optional', 'group'),
24172                'package' => array('subpackage', 'extension', 'os', 'arch')
24173            ));
24174    }
24175
24176    /**
24177     * Mark a package as conflicting with this package
24178     * @param string package name
24179     * @param string package channel
24180     * @param string extension this package provides, if any
24181     */
24182    function addConflictingPackageDepWithUri($name, $uri, $providesextension = false)
24183    {
24184        $this->_isValid = 0;
24185        $dep =
24186            array(
24187                'name' => $name,
24188                'uri' => $uri,
24189                'conflicts' => '',
24190            );
24191        if ($providesextension) {
24192            $dep['providesextension'] = $providesextension;
24193        }
24194        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
24195            array(
24196                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24197                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24198                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24199                'required' => array('optional', 'group'),
24200                'package' => array('subpackage', 'extension', 'os', 'arch')
24201            ));
24202    }
24203
24204    function addDependencyGroup($name, $hint)
24205    {
24206        $this->_isValid = 0;
24207        $this->_packageInfo = $this->_mergeTag($this->_packageInfo,
24208            array('attribs' => array('name' => $name, 'hint' => $hint)),
24209            array(
24210                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24211                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24212                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24213                'group' => array(),
24214            ));
24215    }
24216
24217    /**
24218     * @param string package name
24219     * @param string|false channel name, false if this is a uri
24220     * @param string|false uri name, false if this is a channel
24221     * @param string|false minimum version required
24222     * @param string|false maximum version allowed
24223     * @param string|false recommended installation version
24224     * @param array|false versions to exclude from installation
24225     * @param string extension this package provides, if any
24226     * @param bool if true, tells the installer to ignore the default optional dependency group
24227     *             when installing this package
24228     * @param bool if true, tells the installer to negate this dependency (conflicts)
24229     * @return array
24230     * @access private
24231     */
24232    function _constructDep($name, $channel, $uri, $min, $max, $recommended, $exclude,
24233                           $providesextension = false, $nodefault = false,
24234                           $conflicts = false)
24235    {
24236        $dep =
24237            array(
24238                'name' => $name,
24239            );
24240        if ($channel) {
24241            $dep['channel'] = $channel;
24242        } elseif ($uri) {
24243            $dep['uri'] = $uri;
24244        }
24245        if ($min) {
24246            $dep['min'] = $min;
24247        }
24248        if ($max) {
24249            $dep['max'] = $max;
24250        }
24251        if ($recommended) {
24252            $dep['recommended'] = $recommended;
24253        }
24254        if ($exclude) {
24255            if (is_array($exclude) && count($exclude) == 1) {
24256                $exclude = $exclude[0];
24257            }
24258            $dep['exclude'] = $exclude;
24259        }
24260        if ($conflicts) {
24261            $dep['conflicts'] = '';
24262        }
24263        if ($nodefault) {
24264            $dep['nodefault'] = '';
24265        }
24266        if ($providesextension) {
24267            $dep['providesextension'] = $providesextension;
24268        }
24269        return $dep;
24270    }
24271
24272    /**
24273     * @param package|subpackage
24274     * @param string group name
24275     * @param string package name
24276     * @param string package channel
24277     * @param string minimum version
24278     * @param string maximum version
24279     * @param string recommended version
24280     * @param array|false optional excluded versions
24281     * @param string extension this package provides, if any
24282     * @param bool if true, tells the installer to ignore the default optional dependency group
24283     *             when installing this package
24284     * @return bool false if the dependency group has not been initialized with
24285     *              {@link addDependencyGroup()}, or a subpackage is added with
24286     *              a providesextension
24287     */
24288    function addGroupPackageDepWithChannel($type, $groupname, $name, $channel, $min = false,
24289                                      $max = false, $recommended = false, $exclude = false,
24290                                      $providesextension = false, $nodefault = false)
24291    {
24292        if ($type == 'subpackage' && $providesextension) {
24293            return false; // subpackages must be php packages
24294        }
24295        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
24296            $providesextension, $nodefault);
24297        return $this->_addGroupDependency($type, $dep, $groupname);
24298    }
24299
24300    /**
24301     * @param package|subpackage
24302     * @param string group name
24303     * @param string package name
24304     * @param string package uri
24305     * @param string extension this package provides, if any
24306     * @param bool if true, tells the installer to ignore the default optional dependency group
24307     *             when installing this package
24308     * @return bool false if the dependency group has not been initialized with
24309     *              {@link addDependencyGroup()}
24310     */
24311    function addGroupPackageDepWithURI($type, $groupname, $name, $uri, $providesextension = false,
24312                                       $nodefault = false)
24313    {
24314        if ($type == 'subpackage' && $providesextension) {
24315            return false; // subpackages must be php packages
24316        }
24317        $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
24318            $providesextension, $nodefault);
24319        return $this->_addGroupDependency($type, $dep, $groupname);
24320    }
24321
24322    /**
24323     * @param string group name (must be pre-existing)
24324     * @param string extension name
24325     * @param string minimum version allowed
24326     * @param string maximum version allowed
24327     * @param string recommended version
24328     * @param array incompatible versions
24329     */
24330    function addGroupExtensionDep($groupname, $name, $min = false, $max = false,
24331                                         $recommended = false, $exclude = false)
24332    {
24333        $this->_isValid = 0;
24334        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
24335        return $this->_addGroupDependency('extension', $dep, $groupname);
24336    }
24337
24338    /**
24339     * @param package|subpackage|extension
24340     * @param array dependency contents
24341     * @param string name of the dependency group to add this to
24342     * @return boolean
24343     * @access private
24344     */
24345    function _addGroupDependency($type, $dep, $groupname)
24346    {
24347        $arr = array('subpackage', 'extension');
24348        if ($type != 'package') {
24349            array_shift($arr);
24350        }
24351        if ($type == 'extension') {
24352            array_shift($arr);
24353        }
24354        if (!isset($this->_packageInfo['dependencies']['group'])) {
24355            return false;
24356        } else {
24357            if (!isset($this->_packageInfo['dependencies']['group'][0])) {
24358                if ($this->_packageInfo['dependencies']['group']['attribs']['name'] == $groupname) {
24359                    $this->_packageInfo['dependencies']['group'] = $this->_mergeTag(
24360                        $this->_packageInfo['dependencies']['group'], $dep,
24361                        array(
24362                            $type => $arr
24363                        ));
24364                    $this->_isValid = 0;
24365                    return true;
24366                } else {
24367                    return false;
24368                }
24369            } else {
24370                foreach ($this->_packageInfo['dependencies']['group'] as $i => $group) {
24371                    if ($group['attribs']['name'] == $groupname) {
24372                    $this->_packageInfo['dependencies']['group'][$i] = $this->_mergeTag(
24373                        $this->_packageInfo['dependencies']['group'][$i], $dep,
24374                        array(
24375                            $type => $arr
24376                        ));
24377                        $this->_isValid = 0;
24378                        return true;
24379                    }
24380                }
24381                return false;
24382            }
24383        }
24384    }
24385
24386    /**
24387     * @param optional|required
24388     * @param string package name
24389     * @param string package channel
24390     * @param string minimum version
24391     * @param string maximum version
24392     * @param string recommended version
24393     * @param string extension this package provides, if any
24394     * @param bool if true, tells the installer to ignore the default optional dependency group
24395     *             when installing this package
24396     * @param array|false optional excluded versions
24397     */
24398    function addPackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
24399                                      $recommended = false, $exclude = false,
24400                                      $providesextension = false, $nodefault = false)
24401    {
24402        if (!in_array($type, array('optional', 'required'), true)) {
24403            $type = 'required';
24404        }
24405        $this->_isValid = 0;
24406        $arr = array('optional', 'group');
24407        if ($type != 'required') {
24408            array_shift($arr);
24409        }
24410        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
24411            $providesextension, $nodefault);
24412        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
24413            array(
24414                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24415                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24416                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24417                $type => $arr,
24418                'package' => array('subpackage', 'extension', 'os', 'arch')
24419            ));
24420    }
24421
24422    /**
24423     * @param optional|required
24424     * @param string name of the package
24425     * @param string uri of the package
24426     * @param string extension this package provides, if any
24427     * @param bool if true, tells the installer to ignore the default optional dependency group
24428     *             when installing this package
24429     */
24430    function addPackageDepWithUri($type, $name, $uri, $providesextension = false,
24431                                  $nodefault = false)
24432    {
24433        $this->_isValid = 0;
24434        $arr = array('optional', 'group');
24435        if ($type != 'required') {
24436            array_shift($arr);
24437        }
24438        $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
24439            $providesextension, $nodefault);
24440        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
24441            array(
24442                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24443                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24444                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24445                $type => $arr,
24446                'package' => array('subpackage', 'extension', 'os', 'arch')
24447            ));
24448    }
24449
24450    /**
24451     * @param optional|required optional, required
24452     * @param string package name
24453     * @param string package channel
24454     * @param string minimum version
24455     * @param string maximum version
24456     * @param string recommended version
24457     * @param array incompatible versions
24458     * @param bool if true, tells the installer to ignore the default optional dependency group
24459     *             when installing this package
24460     */
24461    function addSubpackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
24462                                         $recommended = false, $exclude = false,
24463                                         $nodefault = false)
24464    {
24465        $this->_isValid = 0;
24466        $arr = array('optional', 'group');
24467        if ($type != 'required') {
24468            array_shift($arr);
24469        }
24470        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
24471            $nodefault);
24472        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
24473            array(
24474                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24475                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24476                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24477                $type => $arr,
24478                'subpackage' => array('extension', 'os', 'arch')
24479            ));
24480    }
24481
24482    /**
24483     * @param optional|required optional, required
24484     * @param string package name
24485     * @param string package uri for download
24486     * @param bool if true, tells the installer to ignore the default optional dependency group
24487     *             when installing this package
24488     */
24489    function addSubpackageDepWithUri($type, $name, $uri, $nodefault = false)
24490    {
24491        $this->_isValid = 0;
24492        $arr = array('optional', 'group');
24493        if ($type != 'required') {
24494            array_shift($arr);
24495        }
24496        $dep = $this->_constructDep($name, false, $uri, false, false, false, false, $nodefault);
24497        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
24498            array(
24499                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24500                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24501                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24502                $type => $arr,
24503                'subpackage' => array('extension', 'os', 'arch')
24504            ));
24505    }
24506
24507    /**
24508     * @param optional|required optional, required
24509     * @param string extension name
24510     * @param string minimum version
24511     * @param string maximum version
24512     * @param string recommended version
24513     * @param array incompatible versions
24514     */
24515    function addExtensionDep($type, $name, $min = false, $max = false, $recommended = false,
24516                             $exclude = false)
24517    {
24518        $this->_isValid = 0;
24519        $arr = array('optional', 'group');
24520        if ($type != 'required') {
24521            array_shift($arr);
24522        }
24523        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
24524        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
24525            array(
24526                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24527                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24528                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24529                $type => $arr,
24530                'extension' => array('os', 'arch')
24531            ));
24532    }
24533
24534    /**
24535     * @param string Operating system name
24536     * @param boolean true if this package cannot be installed on this OS
24537     */
24538    function addOsDep($name, $conflicts = false)
24539    {
24540        $this->_isValid = 0;
24541        $dep = array('name' => $name);
24542        if ($conflicts) {
24543            $dep['conflicts'] = '';
24544        }
24545        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
24546            array(
24547                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24548                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24549                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24550                'required' => array('optional', 'group'),
24551                'os' => array('arch')
24552            ));
24553    }
24554
24555    /**
24556     * @param string Architecture matching pattern
24557     * @param boolean true if this package cannot be installed on this architecture
24558     */
24559    function addArchDep($pattern, $conflicts = false)
24560    {
24561        $this->_isValid = 0;
24562        $dep = array('pattern' => $pattern);
24563        if ($conflicts) {
24564            $dep['conflicts'] = '';
24565        }
24566        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
24567            array(
24568                'dependencies' => array('providesextension', 'usesrole', 'usestask',
24569                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
24570                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
24571                'required' => array('optional', 'group'),
24572                'arch' => array()
24573            ));
24574    }
24575
24576    /**
24577     * Set the kind of package, and erase all release tags
24578     *
24579     * - a php package is a PEAR-style package
24580     * - an extbin package is a PECL-style extension binary
24581     * - an extsrc package is a PECL-style source for a binary
24582     * - an zendextbin package is a PECL-style zend extension binary
24583     * - an zendextsrc package is a PECL-style source for a zend extension binary
24584     * - a bundle package is a collection of other pre-packaged packages
24585     * @param php|extbin|extsrc|zendextsrc|zendextbin|bundle
24586     * @return bool success
24587     */
24588    function setPackageType($type)
24589    {
24590        $this->_isValid = 0;
24591        if (!in_array($type, array('php', 'extbin', 'extsrc', 'zendextsrc',
24592                                   'zendextbin', 'bundle'))) {
24593            return false;
24594        }
24595
24596        if (in_array($type, array('zendextsrc', 'zendextbin'))) {
24597            $this->_setPackageVersion2_1();
24598        }
24599
24600        if ($type != 'bundle') {
24601            $type .= 'release';
24602        }
24603
24604        foreach (array('phprelease', 'extbinrelease', 'extsrcrelease',
24605                       'zendextsrcrelease', 'zendextbinrelease', 'bundle') as $test) {
24606            unset($this->_packageInfo[$test]);
24607        }
24608
24609        if (!isset($this->_packageInfo[$type])) {
24610            // ensure that the release tag is set up
24611            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('changelog'),
24612                array(), $type);
24613        }
24614
24615        $this->_packageInfo[$type] = array();
24616        return true;
24617    }
24618
24619    /**
24620     * @return bool true if package type is set up
24621     */
24622    function addRelease()
24623    {
24624        if ($type = $this->getPackageType()) {
24625            if ($type != 'bundle') {
24626                $type .= 'release';
24627            }
24628            $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
24629                array($type => array('changelog')));
24630            return true;
24631        }
24632        return false;
24633    }
24634
24635    /**
24636     * Get the current release tag in order to add to it
24637     * @param bool returns only releases that have installcondition if true
24638     * @return array|null
24639     */
24640    function &_getCurrentRelease($strict = true)
24641    {
24642        if ($p = $this->getPackageType()) {
24643            if ($strict) {
24644                if ($p == 'extsrc' || $p == 'zendextsrc') {
24645                    $a = null;
24646                    return $a;
24647                }
24648            }
24649            if ($p != 'bundle') {
24650                $p .= 'release';
24651            }
24652            if (isset($this->_packageInfo[$p][0])) {
24653                return $this->_packageInfo[$p][count($this->_packageInfo[$p]) - 1];
24654            } else {
24655                return $this->_packageInfo[$p];
24656            }
24657        } else {
24658            $a = null;
24659            return $a;
24660        }
24661    }
24662
24663    /**
24664     * Add a file to the current release that should be installed under a different name
24665     * @param string <contents> path to file
24666     * @param string name the file should be installed as
24667     */
24668    function addInstallAs($path, $as)
24669    {
24670        $r = &$this->_getCurrentRelease();
24671        if ($r === null) {
24672            return false;
24673        }
24674        $this->_isValid = 0;
24675        $r = $this->_mergeTag($r, array('attribs' => array('name' => $path, 'as' => $as)),
24676            array(
24677                'filelist' => array(),
24678                'install' => array('ignore')
24679            ));
24680    }
24681
24682    /**
24683     * Add a file to the current release that should be ignored
24684     * @param string <contents> path to file
24685     * @return bool success of operation
24686     */
24687    function addIgnore($path)
24688    {
24689        $r = &$this->_getCurrentRelease();
24690        if ($r === null) {
24691            return false;
24692        }
24693        $this->_isValid = 0;
24694        $r = $this->_mergeTag($r, array('attribs' => array('name' => $path)),
24695            array(
24696                'filelist' => array(),
24697                'ignore' => array()
24698            ));
24699    }
24700
24701    /**
24702     * Add an extension binary package for this extension source code release
24703     *
24704     * Note that the package must be from the same channel as the extension source package
24705     * @param string
24706     */
24707    function addBinarypackage($package)
24708    {
24709        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
24710            return false;
24711        }
24712        $r = &$this->_getCurrentRelease(false);
24713        if ($r === null) {
24714            return false;
24715        }
24716        $this->_isValid = 0;
24717        $r = $this->_mergeTag($r, $package,
24718            array(
24719                'binarypackage' => array('filelist'),
24720            ));
24721    }
24722
24723    /**
24724     * Add a configureoption to an extension source package
24725     * @param string
24726     * @param string
24727     * @param string
24728     */
24729    function addConfigureOption($name, $prompt, $default = null)
24730    {
24731        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
24732            return false;
24733        }
24734
24735        $r = &$this->_getCurrentRelease(false);
24736        if ($r === null) {
24737            return false;
24738        }
24739
24740        $opt = array('attribs' => array('name' => $name, 'prompt' => $prompt));
24741        if ($default !== null) {
24742            $opt['attribs']['default'] = $default;
24743        }
24744
24745        $this->_isValid = 0;
24746        $r = $this->_mergeTag($r, $opt,
24747            array(
24748                'configureoption' => array('binarypackage', 'filelist'),
24749            ));
24750    }
24751
24752    /**
24753     * Set an installation condition based on php version for the current release set
24754     * @param string minimum version
24755     * @param string maximum version
24756     * @param false|array incompatible versions of PHP
24757     */
24758    function setPhpInstallCondition($min, $max, $exclude = false)
24759    {
24760        $r = &$this->_getCurrentRelease();
24761        if ($r === null) {
24762            return false;
24763        }
24764        $this->_isValid = 0;
24765        if (isset($r['installconditions']['php'])) {
24766            unset($r['installconditions']['php']);
24767        }
24768        $dep = array('min' => $min, 'max' => $max);
24769        if ($exclude) {
24770            if (is_array($exclude) && count($exclude) == 1) {
24771                $exclude = $exclude[0];
24772            }
24773            $dep['exclude'] = $exclude;
24774        }
24775        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
24776            $r = $this->_mergeTag($r, $dep,
24777                array(
24778                    'installconditions' => array('configureoption', 'binarypackage',
24779                        'filelist'),
24780                    'php' => array('extension', 'os', 'arch')
24781                ));
24782        } else {
24783            $r = $this->_mergeTag($r, $dep,
24784                array(
24785                    'installconditions' => array('filelist'),
24786                    'php' => array('extension', 'os', 'arch')
24787                ));
24788        }
24789    }
24790
24791    /**
24792     * @param optional|required optional, required
24793     * @param string extension name
24794     * @param string minimum version
24795     * @param string maximum version
24796     * @param string recommended version
24797     * @param array incompatible versions
24798     */
24799    function addExtensionInstallCondition($name, $min = false, $max = false, $recommended = false,
24800                                          $exclude = false)
24801    {
24802        $r = &$this->_getCurrentRelease();
24803        if ($r === null) {
24804            return false;
24805        }
24806        $this->_isValid = 0;
24807        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
24808        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
24809            $r = $this->_mergeTag($r, $dep,
24810                array(
24811                    'installconditions' => array('configureoption', 'binarypackage',
24812                        'filelist'),
24813                    'extension' => array('os', 'arch')
24814                ));
24815        } else {
24816            $r = $this->_mergeTag($r, $dep,
24817                array(
24818                    'installconditions' => array('filelist'),
24819                    'extension' => array('os', 'arch')
24820                ));
24821        }
24822    }
24823
24824    /**
24825     * Set an installation condition based on operating system for the current release set
24826     * @param string OS name
24827     * @param bool whether this OS is incompatible with the current release
24828     */
24829    function setOsInstallCondition($name, $conflicts = false)
24830    {
24831        $r = &$this->_getCurrentRelease();
24832        if ($r === null) {
24833            return false;
24834        }
24835        $this->_isValid = 0;
24836        if (isset($r['installconditions']['os'])) {
24837            unset($r['installconditions']['os']);
24838        }
24839        $dep = array('name' => $name);
24840        if ($conflicts) {
24841            $dep['conflicts'] = '';
24842        }
24843        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
24844            $r = $this->_mergeTag($r, $dep,
24845                array(
24846                    'installconditions' => array('configureoption', 'binarypackage',
24847                        'filelist'),
24848                    'os' => array('arch')
24849                ));
24850        } else {
24851            $r = $this->_mergeTag($r, $dep,
24852                array(
24853                    'installconditions' => array('filelist'),
24854                    'os' => array('arch')
24855                ));
24856        }
24857    }
24858
24859    /**
24860     * Set an installation condition based on architecture for the current release set
24861     * @param string architecture pattern
24862     * @param bool whether this arch is incompatible with the current release
24863     */
24864    function setArchInstallCondition($pattern, $conflicts = false)
24865    {
24866        $r = &$this->_getCurrentRelease();
24867        if ($r === null) {
24868            return false;
24869        }
24870        $this->_isValid = 0;
24871        if (isset($r['installconditions']['arch'])) {
24872            unset($r['installconditions']['arch']);
24873        }
24874        $dep = array('pattern' => $pattern);
24875        if ($conflicts) {
24876            $dep['conflicts'] = '';
24877        }
24878        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
24879            $r = $this->_mergeTag($r, $dep,
24880                array(
24881                    'installconditions' => array('configureoption', 'binarypackage',
24882                        'filelist'),
24883                    'arch' => array()
24884                ));
24885        } else {
24886            $r = $this->_mergeTag($r, $dep,
24887                array(
24888                    'installconditions' => array('filelist'),
24889                    'arch' => array()
24890                ));
24891        }
24892    }
24893
24894    /**
24895     * For extension binary releases, this is used to specify either the
24896     * static URI to a source package, or the package name and channel of the extsrc/zendextsrc
24897     * package it is based on.
24898     * @param string Package name, or full URI to source package (extsrc/zendextsrc type)
24899     */
24900    function setSourcePackage($packageOrUri)
24901    {
24902        $this->_isValid = 0;
24903        if (isset($this->_packageInfo['channel'])) {
24904            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
24905                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
24906                'bundle', 'changelog'),
24907                $packageOrUri, 'srcpackage');
24908        } else {
24909            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
24910                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
24911                'bundle', 'changelog'), $packageOrUri, 'srcuri');
24912        }
24913    }
24914
24915    /**
24916     * Generate a valid change log entry from the current package.xml
24917     * @param string|false
24918     */
24919    function generateChangeLogEntry($notes = false)
24920    {
24921        return array(
24922            'version' =>
24923                array(
24924                    'release' => $this->getVersion('release'),
24925                    'api' => $this->getVersion('api'),
24926                    ),
24927            'stability' =>
24928                $this->getStability(),
24929            'date' => $this->getDate(),
24930            'license' => $this->getLicense(true),
24931            'notes' => $notes ? $notes : $this->getNotes()
24932            );
24933    }
24934
24935    /**
24936     * @param string release version to set change log notes for
24937     * @param array output of {@link generateChangeLogEntry()}
24938     */
24939    function setChangelogEntry($releaseversion, $contents)
24940    {
24941        if (!isset($this->_packageInfo['changelog'])) {
24942            $this->_packageInfo['changelog']['release'] = $contents;
24943            return;
24944        }
24945        if (!isset($this->_packageInfo['changelog']['release'][0])) {
24946            if ($this->_packageInfo['changelog']['release']['version']['release'] == $releaseversion) {
24947                $this->_packageInfo['changelog']['release'] = array(
24948                    $this->_packageInfo['changelog']['release']);
24949            } else {
24950                $this->_packageInfo['changelog']['release'] = array(
24951                    $this->_packageInfo['changelog']['release']);
24952                return $this->_packageInfo['changelog']['release'][] = $contents;
24953            }
24954        }
24955        foreach($this->_packageInfo['changelog']['release'] as $index => $changelog) {
24956            if (isset($changelog['version']) &&
24957                  strnatcasecmp($changelog['version']['release'], $releaseversion) == 0) {
24958                $curlog = $index;
24959            }
24960        }
24961        if (isset($curlog)) {
24962            $this->_packageInfo['changelog']['release'][$curlog] = $contents;
24963        } else {
24964            $this->_packageInfo['changelog']['release'][] = $contents;
24965        }
24966    }
24967
24968    /**
24969     * Remove the changelog entirely
24970     */
24971    function clearChangeLog()
24972    {
24973        unset($this->_packageInfo['changelog']);
24974    }
24975}PEAR-1.9.4/PEAR/PackageFile/v2/Validator.php0000644000076500000240000025015111605156614017107 0ustar  helgistaff<?php
24976/**
24977 * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
24978 *
24979 * PHP versions 4 and 5
24980 *
24981 * @category   pear
24982 * @package    PEAR
24983 * @author     Greg Beaver <cellog@php.net>
24984 * @copyright  1997-2009 The Authors
24985 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
24986 * @version    CVS: $Id: Validator.php 313023 2011-07-06 19:17:11Z dufuz $
24987 * @link       http://pear.php.net/package/PEAR
24988 * @since      File available since Release 1.4.0a8
24989 */
24990/**
24991 * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its
24992 * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller
24993 * @category   pear
24994 * @package    PEAR
24995 * @author     Greg Beaver <cellog@php.net>
24996 * @copyright  1997-2009 The Authors
24997 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
24998 * @version    Release: 1.9.4
24999 * @link       http://pear.php.net/package/PEAR
25000 * @since      Class available since Release 1.4.0a8
25001 * @access private
25002 */
25003class PEAR_PackageFile_v2_Validator
25004{
25005    /**
25006     * @var array
25007     */
25008    var $_packageInfo;
25009    /**
25010     * @var PEAR_PackageFile_v2
25011     */
25012    var $_pf;
25013    /**
25014     * @var PEAR_ErrorStack
25015     */
25016    var $_stack;
25017    /**
25018     * @var int
25019     */
25020    var $_isValid = 0;
25021    /**
25022     * @var int
25023     */
25024    var $_filesValid = 0;
25025    /**
25026     * @var int
25027     */
25028    var $_curState = 0;
25029    /**
25030     * @param PEAR_PackageFile_v2
25031     * @param int
25032     */
25033    function validate(&$pf, $state = PEAR_VALIDATE_NORMAL)
25034    {
25035        $this->_pf = &$pf;
25036        $this->_curState = $state;
25037        $this->_packageInfo = $this->_pf->getArray();
25038        $this->_isValid = $this->_pf->_isValid;
25039        $this->_filesValid = $this->_pf->_filesValid;
25040        $this->_stack = &$pf->_stack;
25041        $this->_stack->getErrors(true);
25042        if (($this->_isValid & $state) == $state) {
25043            return true;
25044        }
25045        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
25046            return false;
25047        }
25048        if (!isset($this->_packageInfo['attribs']['version']) ||
25049              ($this->_packageInfo['attribs']['version'] != '2.0' &&
25050               $this->_packageInfo['attribs']['version'] != '2.1')
25051        ) {
25052            $this->_noPackageVersion();
25053        }
25054        $structure =
25055        array(
25056            'name',
25057            'channel|uri',
25058            '*extends', // can't be multiple, but this works fine
25059            'summary',
25060            'description',
25061            '+lead', // these all need content checks
25062            '*developer',
25063            '*contributor',
25064            '*helper',
25065            'date',
25066            '*time',
25067            'version',
25068            'stability',
25069            'license->?uri->?filesource',
25070            'notes',
25071            'contents', //special validation needed
25072            '*compatible',
25073            'dependencies', //special validation needed
25074            '*usesrole',
25075            '*usestask', // reserve these for 1.4.0a1 to implement
25076                         // this will allow a package.xml to gracefully say it
25077                         // needs a certain package installed in order to implement a role or task
25078            '*providesextension',
25079            '*srcpackage|*srcuri',
25080            '+phprelease|+extsrcrelease|+extbinrelease|' .
25081                '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed
25082            '*changelog',
25083        );
25084        $test = $this->_packageInfo;
25085        if (isset($test['dependencies']) &&
25086              isset($test['dependencies']['required']) &&
25087              isset($test['dependencies']['required']['pearinstaller']) &&
25088              isset($test['dependencies']['required']['pearinstaller']['min']) &&
25089              version_compare('1.9.4',
25090                $test['dependencies']['required']['pearinstaller']['min'], '<')
25091        ) {
25092            $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']);
25093            return false;
25094        }
25095        // ignore post-installation array fields
25096        if (array_key_exists('filelist', $test)) {
25097            unset($test['filelist']);
25098        }
25099        if (array_key_exists('_lastmodified', $test)) {
25100            unset($test['_lastmodified']);
25101        }
25102        if (array_key_exists('#binarypackage', $test)) {
25103            unset($test['#binarypackage']);
25104        }
25105        if (array_key_exists('old', $test)) {
25106            unset($test['old']);
25107        }
25108        if (array_key_exists('_lastversion', $test)) {
25109            unset($test['_lastversion']);
25110        }
25111        if (!$this->_stupidSchemaValidate($structure, $test, '<package>')) {
25112            return false;
25113        }
25114        if (empty($this->_packageInfo['name'])) {
25115            $this->_tagCannotBeEmpty('name');
25116        }
25117        $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel';
25118        if (empty($this->_packageInfo[$test])) {
25119            $this->_tagCannotBeEmpty($test);
25120        }
25121        if (is_array($this->_packageInfo['license']) &&
25122              (!isset($this->_packageInfo['license']['_content']) ||
25123              empty($this->_packageInfo['license']['_content']))) {
25124            $this->_tagCannotBeEmpty('license');
25125        } elseif (empty($this->_packageInfo['license'])) {
25126            $this->_tagCannotBeEmpty('license');
25127        }
25128        if (empty($this->_packageInfo['summary'])) {
25129            $this->_tagCannotBeEmpty('summary');
25130        }
25131        if (empty($this->_packageInfo['description'])) {
25132            $this->_tagCannotBeEmpty('description');
25133        }
25134        if (empty($this->_packageInfo['date'])) {
25135            $this->_tagCannotBeEmpty('date');
25136        }
25137        if (empty($this->_packageInfo['notes'])) {
25138            $this->_tagCannotBeEmpty('notes');
25139        }
25140        if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) {
25141            $this->_tagCannotBeEmpty('time');
25142        }
25143        if (isset($this->_packageInfo['dependencies'])) {
25144            $this->_validateDependencies();
25145        }
25146        if (isset($this->_packageInfo['compatible'])) {
25147            $this->_validateCompatible();
25148        }
25149        if (!isset($this->_packageInfo['bundle'])) {
25150            if (empty($this->_packageInfo['contents'])) {
25151                $this->_tagCannotBeEmpty('contents');
25152            }
25153            if (!isset($this->_packageInfo['contents']['dir'])) {
25154                $this->_filelistMustContainDir('contents');
25155                return false;
25156            }
25157            if (isset($this->_packageInfo['contents']['file'])) {
25158                $this->_filelistCannotContainFile('contents');
25159                return false;
25160            }
25161        }
25162        $this->_validateMaintainers();
25163        $this->_validateStabilityVersion();
25164        $fail = false;
25165        if (array_key_exists('usesrole', $this->_packageInfo)) {
25166            $roles = $this->_packageInfo['usesrole'];
25167            if (!is_array($roles) || !isset($roles[0])) {
25168                $roles = array($roles);
25169            }
25170            foreach ($roles as $role) {
25171                if (!isset($role['role'])) {
25172                    $this->_usesroletaskMustHaveRoleTask('usesrole', 'role');
25173                    $fail = true;
25174                } else {
25175                    if (!isset($role['channel'])) {
25176                        if (!isset($role['uri'])) {
25177                            $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole');
25178                            $fail = true;
25179                        }
25180                    } elseif (!isset($role['package'])) {
25181                        $this->_usesroletaskMustHavePackage($role['role'], 'usesrole');
25182                        $fail = true;
25183                    }
25184                }
25185            }
25186        }
25187        if (array_key_exists('usestask', $this->_packageInfo)) {
25188            $roles = $this->_packageInfo['usestask'];
25189            if (!is_array($roles) || !isset($roles[0])) {
25190                $roles = array($roles);
25191            }
25192            foreach ($roles as $role) {
25193                if (!isset($role['task'])) {
25194                    $this->_usesroletaskMustHaveRoleTask('usestask', 'task');
25195                    $fail = true;
25196                } else {
25197                    if (!isset($role['channel'])) {
25198                        if (!isset($role['uri'])) {
25199                            $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask');
25200                            $fail = true;
25201                        }
25202                    } elseif (!isset($role['package'])) {
25203                        $this->_usesroletaskMustHavePackage($role['task'], 'usestask');
25204                        $fail = true;
25205                    }
25206                }
25207            }
25208        }
25209
25210        if ($fail) {
25211            return false;
25212        }
25213
25214        $list = $this->_packageInfo['contents'];
25215        if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) {
25216            $this->_multipleToplevelDirNotAllowed();
25217            return $this->_isValid = 0;
25218        }
25219
25220        $this->_validateFilelist();
25221        $this->_validateRelease();
25222        if (!$this->_stack->hasErrors()) {
25223            $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true);
25224            if (PEAR::isError($chan)) {
25225                $this->_unknownChannel($this->_pf->getChannel());
25226            } else {
25227                $valpack = $chan->getValidationPackage();
25228                // for channel validator packages, always use the default PEAR validator.
25229                // otherwise, they can't be installed or packaged
25230                $validator = $chan->getValidationObject($this->_pf->getPackage());
25231                if (!$validator) {
25232                    $this->_stack->push(__FUNCTION__, 'error',
25233                        array('channel' => $chan->getName(),
25234                              'package' => $this->_pf->getPackage(),
25235                              'name'    => $valpack['_content'],
25236                              'version' => $valpack['attribs']['version']),
25237                        'package "%channel%/%package%" cannot be properly validated without ' .
25238                        'validation package "%channel%/%name%-%version%"');
25239                    return $this->_isValid = 0;
25240                }
25241                $validator->setPackageFile($this->_pf);
25242                $validator->validate($state);
25243                $failures = $validator->getFailures();
25244                foreach ($failures['errors'] as $error) {
25245                    $this->_stack->push(__FUNCTION__, 'error', $error,
25246                        'Channel validator error: field "%field%" - %reason%');
25247                }
25248                foreach ($failures['warnings'] as $warning) {
25249                    $this->_stack->push(__FUNCTION__, 'warning', $warning,
25250                        'Channel validator warning: field "%field%" - %reason%');
25251                }
25252            }
25253        }
25254
25255        $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error');
25256        if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) {
25257            if ($this->_pf->getPackageType() == 'bundle') {
25258                if ($this->_analyzeBundledPackages()) {
25259                    $this->_filesValid = $this->_pf->_filesValid = true;
25260                } else {
25261                    $this->_pf->_isValid = $this->_isValid = 0;
25262                }
25263            } else {
25264                if (!$this->_analyzePhpFiles()) {
25265                    $this->_pf->_isValid = $this->_isValid = 0;
25266                } else {
25267                    $this->_filesValid = $this->_pf->_filesValid = true;
25268                }
25269            }
25270        }
25271
25272        if ($this->_isValid) {
25273            return $this->_pf->_isValid = $this->_isValid = $state;
25274        }
25275
25276        return $this->_pf->_isValid = $this->_isValid = 0;
25277    }
25278
25279    function _stupidSchemaValidate($structure, $xml, $root)
25280    {
25281        if (!is_array($xml)) {
25282            $xml = array();
25283        }
25284        $keys = array_keys($xml);
25285        reset($keys);
25286        $key = current($keys);
25287        while ($key == 'attribs' || $key == '_contents') {
25288            $key = next($keys);
25289        }
25290        $unfoundtags = $optionaltags = array();
25291        $ret = true;
25292        $mismatch = false;
25293        foreach ($structure as $struc) {
25294            if ($key) {
25295                $tag = $xml[$key];
25296            }
25297            $test = $this->_processStructure($struc);
25298            if (isset($test['choices'])) {
25299                $loose = true;
25300                foreach ($test['choices'] as $choice) {
25301                    if ($key == $choice['tag']) {
25302                        $key = next($keys);
25303                        while ($key == 'attribs' || $key == '_contents') {
25304                            $key = next($keys);
25305                        }
25306                        $unfoundtags = $optionaltags = array();
25307                        $mismatch = false;
25308                        if ($key && $key != $choice['tag'] && isset($choice['multiple'])) {
25309                            $unfoundtags[] = $choice['tag'];
25310                            $optionaltags[] = $choice['tag'];
25311                            if ($key) {
25312                                $mismatch = true;
25313                            }
25314                        }
25315                        $ret &= $this->_processAttribs($choice, $tag, $root);
25316                        continue 2;
25317                    } else {
25318                        $unfoundtags[] = $choice['tag'];
25319                        $mismatch = true;
25320                    }
25321                    if (!isset($choice['multiple']) || $choice['multiple'] != '*') {
25322                        $loose = false;
25323                    } else {
25324                        $optionaltags[] = $choice['tag'];
25325                    }
25326                }
25327                if (!$loose) {
25328                    $this->_invalidTagOrder($unfoundtags, $key, $root);
25329                    return false;
25330                }
25331            } else {
25332                if ($key != $test['tag']) {
25333                    if (isset($test['multiple']) && $test['multiple'] != '*') {
25334                        $unfoundtags[] = $test['tag'];
25335                        $this->_invalidTagOrder($unfoundtags, $key, $root);
25336                        return false;
25337                    } else {
25338                        if ($key) {
25339                            $mismatch = true;
25340                        }
25341                        $unfoundtags[] = $test['tag'];
25342                        $optionaltags[] = $test['tag'];
25343                    }
25344                    if (!isset($test['multiple'])) {
25345                        $this->_invalidTagOrder($unfoundtags, $key, $root);
25346                        return false;
25347                    }
25348                    continue;
25349                } else {
25350                    $unfoundtags = $optionaltags = array();
25351                    $mismatch = false;
25352                }
25353                $key = next($keys);
25354                while ($key == 'attribs' || $key == '_contents') {
25355                    $key = next($keys);
25356                }
25357                if ($key && $key != $test['tag'] && isset($test['multiple'])) {
25358                    $unfoundtags[] = $test['tag'];
25359                    $optionaltags[] = $test['tag'];
25360                    $mismatch = true;
25361                }
25362                $ret &= $this->_processAttribs($test, $tag, $root);
25363                continue;
25364            }
25365        }
25366        if (!$mismatch && count($optionaltags)) {
25367            // don't error out on any optional tags
25368            $unfoundtags = array_diff($unfoundtags, $optionaltags);
25369        }
25370        if (count($unfoundtags)) {
25371            $this->_invalidTagOrder($unfoundtags, $key, $root);
25372        } elseif ($key) {
25373            // unknown tags
25374            $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
25375            while ($key = next($keys)) {
25376                $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
25377            }
25378        }
25379        return $ret;
25380    }
25381
25382    function _processAttribs($choice, $tag, $context)
25383    {
25384        if (isset($choice['attribs'])) {
25385            if (!is_array($tag)) {
25386                $tag = array($tag);
25387            }
25388            $tags = $tag;
25389            if (!isset($tags[0])) {
25390                $tags = array($tags);
25391            }
25392            $ret = true;
25393            foreach ($tags as $i => $tag) {
25394                if (!is_array($tag) || !isset($tag['attribs'])) {
25395                    foreach ($choice['attribs'] as $attrib) {
25396                        if ($attrib{0} != '?') {
25397                            $ret &= $this->_tagHasNoAttribs($choice['tag'],
25398                                $context);
25399                            continue 2;
25400                        }
25401                    }
25402                }
25403                foreach ($choice['attribs'] as $attrib) {
25404                    if ($attrib{0} != '?') {
25405                        if (!isset($tag['attribs'][$attrib])) {
25406                            $ret &= $this->_tagMissingAttribute($choice['tag'],
25407                                $attrib, $context);
25408                        }
25409                    }
25410                }
25411            }
25412            return $ret;
25413        }
25414        return true;
25415    }
25416
25417    function _processStructure($key)
25418    {
25419        $ret = array();
25420        if (count($pieces = explode('|', $key)) > 1) {
25421            $ret['choices'] = array();
25422            foreach ($pieces as $piece) {
25423                $ret['choices'][] = $this->_processStructure($piece);
25424            }
25425            return $ret;
25426        }
25427        $multi = $key{0};
25428        if ($multi == '+' || $multi == '*') {
25429            $ret['multiple'] = $key{0};
25430            $key = substr($key, 1);
25431        }
25432        if (count($attrs = explode('->', $key)) > 1) {
25433            $ret['tag'] = array_shift($attrs);
25434            $ret['attribs'] = $attrs;
25435        } else {
25436            $ret['tag'] = $key;
25437        }
25438        return $ret;
25439    }
25440
25441    function _validateStabilityVersion()
25442    {
25443        $structure = array('release', 'api');
25444        $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], '<version>');
25445        $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], '<stability>');
25446        if ($a) {
25447            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25448                  $this->_packageInfo['version']['release'])) {
25449                $this->_invalidVersion('release', $this->_packageInfo['version']['release']);
25450            }
25451            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25452                  $this->_packageInfo['version']['api'])) {
25453                $this->_invalidVersion('api', $this->_packageInfo['version']['api']);
25454            }
25455            if (!in_array($this->_packageInfo['stability']['release'],
25456                  array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) {
25457                $this->_invalidState('release', $this->_packageInfo['stability']['release']);
25458            }
25459            if (!in_array($this->_packageInfo['stability']['api'],
25460                  array('devel', 'alpha', 'beta', 'stable'))) {
25461                $this->_invalidState('api', $this->_packageInfo['stability']['api']);
25462            }
25463        }
25464    }
25465
25466    function _validateMaintainers()
25467    {
25468        $structure =
25469            array(
25470                'name',
25471                'user',
25472                'email',
25473                'active',
25474            );
25475        foreach (array('lead', 'developer', 'contributor', 'helper') as $type) {
25476            if (!isset($this->_packageInfo[$type])) {
25477                continue;
25478            }
25479            if (isset($this->_packageInfo[$type][0])) {
25480                foreach ($this->_packageInfo[$type] as $lead) {
25481                    $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>');
25482                }
25483            } else {
25484                $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type],
25485                    '<' . $type . '>');
25486            }
25487        }
25488    }
25489
25490    function _validatePhpDep($dep, $installcondition = false)
25491    {
25492        $structure = array(
25493            'min',
25494            '*max',
25495            '*exclude',
25496        );
25497        $type = $installcondition ? '<installcondition><php>' : '<dependencies><required><php>';
25498        $this->_stupidSchemaValidate($structure, $dep, $type);
25499        if (isset($dep['min'])) {
25500            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
25501                  $dep['min'])) {
25502                $this->_invalidVersion($type . '<min>', $dep['min']);
25503            }
25504        }
25505        if (isset($dep['max'])) {
25506            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
25507                  $dep['max'])) {
25508                $this->_invalidVersion($type . '<max>', $dep['max']);
25509            }
25510        }
25511        if (isset($dep['exclude'])) {
25512            if (!is_array($dep['exclude'])) {
25513                $dep['exclude'] = array($dep['exclude']);
25514            }
25515            foreach ($dep['exclude'] as $exclude) {
25516                if (!preg_match(
25517                     '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
25518                     $exclude)) {
25519                    $this->_invalidVersion($type . '<exclude>', $exclude);
25520                }
25521            }
25522        }
25523    }
25524
25525    function _validatePearinstallerDep($dep)
25526    {
25527        $structure = array(
25528            'min',
25529            '*max',
25530            '*recommended',
25531            '*exclude',
25532        );
25533        $this->_stupidSchemaValidate($structure, $dep, '<dependencies><required><pearinstaller>');
25534        if (isset($dep['min'])) {
25535            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25536                  $dep['min'])) {
25537                $this->_invalidVersion('<dependencies><required><pearinstaller><min>',
25538                    $dep['min']);
25539            }
25540        }
25541        if (isset($dep['max'])) {
25542            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25543                  $dep['max'])) {
25544                $this->_invalidVersion('<dependencies><required><pearinstaller><max>',
25545                    $dep['max']);
25546            }
25547        }
25548        if (isset($dep['recommended'])) {
25549            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25550                  $dep['recommended'])) {
25551                $this->_invalidVersion('<dependencies><required><pearinstaller><recommended>',
25552                    $dep['recommended']);
25553            }
25554        }
25555        if (isset($dep['exclude'])) {
25556            if (!is_array($dep['exclude'])) {
25557                $dep['exclude'] = array($dep['exclude']);
25558            }
25559            foreach ($dep['exclude'] as $exclude) {
25560                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25561                      $exclude)) {
25562                    $this->_invalidVersion('<dependencies><required><pearinstaller><exclude>',
25563                        $exclude);
25564                }
25565            }
25566        }
25567    }
25568
25569    function _validatePackageDep($dep, $group, $type = '<package>')
25570    {
25571        if (isset($dep['uri'])) {
25572            if (isset($dep['conflicts'])) {
25573                $structure = array(
25574                    'name',
25575                    'uri',
25576                    'conflicts',
25577                    '*providesextension',
25578                );
25579            } else {
25580                $structure = array(
25581                    'name',
25582                    'uri',
25583                    '*providesextension',
25584                );
25585            }
25586        } else {
25587            if (isset($dep['conflicts'])) {
25588                $structure = array(
25589                    'name',
25590                    'channel',
25591                    '*min',
25592                    '*max',
25593                    '*exclude',
25594                    'conflicts',
25595                    '*providesextension',
25596                );
25597            } else {
25598                $structure = array(
25599                    'name',
25600                    'channel',
25601                    '*min',
25602                    '*max',
25603                    '*recommended',
25604                    '*exclude',
25605                    '*nodefault',
25606                    '*providesextension',
25607                );
25608            }
25609        }
25610        if (isset($dep['name'])) {
25611            $type .= '<name>' . $dep['name'] . '</name>';
25612        }
25613        $this->_stupidSchemaValidate($structure, $dep, '<dependencies>' . $group . $type);
25614        if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) ||
25615              isset($dep['recommended']) || isset($dep['exclude']))) {
25616            $this->_uriDepsCannotHaveVersioning('<dependencies>' . $group . $type);
25617        }
25618        if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') {
25619            $this->_DepchannelCannotBeUri('<dependencies>' . $group . $type);
25620        }
25621        if (isset($dep['min'])) {
25622            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25623                  $dep['min'])) {
25624                $this->_invalidVersion('<dependencies>' . $group . $type . '<min>', $dep['min']);
25625            }
25626        }
25627        if (isset($dep['max'])) {
25628            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25629                  $dep['max'])) {
25630                $this->_invalidVersion('<dependencies>' . $group . $type . '<max>', $dep['max']);
25631            }
25632        }
25633        if (isset($dep['recommended'])) {
25634            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25635                  $dep['recommended'])) {
25636                $this->_invalidVersion('<dependencies>' . $group . $type . '<recommended>',
25637                    $dep['recommended']);
25638            }
25639        }
25640        if (isset($dep['exclude'])) {
25641            if (!is_array($dep['exclude'])) {
25642                $dep['exclude'] = array($dep['exclude']);
25643            }
25644            foreach ($dep['exclude'] as $exclude) {
25645                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25646                      $exclude)) {
25647                    $this->_invalidVersion('<dependencies>' . $group . $type . '<exclude>',
25648                        $exclude);
25649                }
25650            }
25651        }
25652    }
25653
25654    function _validateSubpackageDep($dep, $group)
25655    {
25656        $this->_validatePackageDep($dep, $group, '<subpackage>');
25657        if (isset($dep['providesextension'])) {
25658            $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : '');
25659        }
25660        if (isset($dep['conflicts'])) {
25661            $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : '');
25662        }
25663    }
25664
25665    function _validateExtensionDep($dep, $group = false, $installcondition = false)
25666    {
25667        if (isset($dep['conflicts'])) {
25668            $structure = array(
25669                'name',
25670                '*min',
25671                '*max',
25672                '*exclude',
25673                'conflicts',
25674            );
25675        } else {
25676            $structure = array(
25677                'name',
25678                '*min',
25679                '*max',
25680                '*recommended',
25681                '*exclude',
25682            );
25683        }
25684        if ($installcondition) {
25685            $type = '<installcondition><extension>';
25686        } else {
25687            $type = '<dependencies>' . $group . '<extension>';
25688        }
25689        if (isset($dep['name'])) {
25690            $type .= '<name>' . $dep['name'] . '</name>';
25691        }
25692        $this->_stupidSchemaValidate($structure, $dep, $type);
25693        if (isset($dep['min'])) {
25694            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25695                  $dep['min'])) {
25696                $this->_invalidVersion(substr($type, 1) . '<min', $dep['min']);
25697            }
25698        }
25699        if (isset($dep['max'])) {
25700            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25701                  $dep['max'])) {
25702                $this->_invalidVersion(substr($type, 1) . '<max', $dep['max']);
25703            }
25704        }
25705        if (isset($dep['recommended'])) {
25706            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25707                  $dep['recommended'])) {
25708                $this->_invalidVersion(substr($type, 1) . '<recommended', $dep['recommended']);
25709            }
25710        }
25711        if (isset($dep['exclude'])) {
25712            if (!is_array($dep['exclude'])) {
25713                $dep['exclude'] = array($dep['exclude']);
25714            }
25715            foreach ($dep['exclude'] as $exclude) {
25716                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25717                      $exclude)) {
25718                    $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
25719                }
25720            }
25721        }
25722    }
25723
25724    function _validateOsDep($dep, $installcondition = false)
25725    {
25726        $structure = array(
25727            'name',
25728            '*conflicts',
25729        );
25730        $type = $installcondition ? '<installcondition><os>' : '<dependencies><required><os>';
25731        if ($this->_stupidSchemaValidate($structure, $dep, $type)) {
25732            if ($dep['name'] == '*') {
25733                if (array_key_exists('conflicts', $dep)) {
25734                    $this->_cannotConflictWithAllOs($type);
25735                }
25736            }
25737        }
25738    }
25739
25740    function _validateArchDep($dep, $installcondition = false)
25741    {
25742        $structure = array(
25743            'pattern',
25744            '*conflicts',
25745        );
25746        $type = $installcondition ? '<installcondition><arch>' : '<dependencies><required><arch>';
25747        $this->_stupidSchemaValidate($structure, $dep, $type);
25748    }
25749
25750    function _validateInstallConditions($cond, $release)
25751    {
25752        $structure = array(
25753            '*php',
25754            '*extension',
25755            '*os',
25756            '*arch',
25757        );
25758        if (!$this->_stupidSchemaValidate($structure,
25759              $cond, $release)) {
25760            return false;
25761        }
25762        foreach (array('php', 'extension', 'os', 'arch') as $type) {
25763            if (isset($cond[$type])) {
25764                $iter = $cond[$type];
25765                if (!is_array($iter) || !isset($iter[0])) {
25766                    $iter = array($iter);
25767                }
25768                foreach ($iter as $package) {
25769                    if ($type == 'extension') {
25770                        $this->{"_validate{$type}Dep"}($package, false, true);
25771                    } else {
25772                        $this->{"_validate{$type}Dep"}($package, true);
25773                    }
25774                }
25775            }
25776        }
25777    }
25778
25779    function _validateDependencies()
25780    {
25781        $structure = array(
25782            'required',
25783            '*optional',
25784            '*group->name->hint'
25785        );
25786        if (!$this->_stupidSchemaValidate($structure,
25787              $this->_packageInfo['dependencies'], '<dependencies>')) {
25788            return false;
25789        }
25790        foreach (array('required', 'optional') as $simpledep) {
25791            if (isset($this->_packageInfo['dependencies'][$simpledep])) {
25792                if ($simpledep == 'optional') {
25793                    $structure = array(
25794                        '*package',
25795                        '*subpackage',
25796                        '*extension',
25797                    );
25798                } else {
25799                    $structure = array(
25800                        'php',
25801                        'pearinstaller',
25802                        '*package',
25803                        '*subpackage',
25804                        '*extension',
25805                        '*os',
25806                        '*arch',
25807                    );
25808                }
25809                if ($this->_stupidSchemaValidate($structure,
25810                      $this->_packageInfo['dependencies'][$simpledep],
25811                      "<dependencies><$simpledep>")) {
25812                    foreach (array('package', 'subpackage', 'extension') as $type) {
25813                        if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
25814                            $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
25815                            if (!isset($iter[0])) {
25816                                $iter = array($iter);
25817                            }
25818                            foreach ($iter as $package) {
25819                                if ($type != 'extension') {
25820                                    if (isset($package['uri'])) {
25821                                        if (isset($package['channel'])) {
25822                                            $this->_UrlOrChannel($type,
25823                                                $package['name']);
25824                                        }
25825                                    } else {
25826                                        if (!isset($package['channel'])) {
25827                                            $this->_NoChannel($type, $package['name']);
25828                                        }
25829                                    }
25830                                }
25831                                $this->{"_validate{$type}Dep"}($package, "<$simpledep>");
25832                            }
25833                        }
25834                    }
25835                    if ($simpledep == 'optional') {
25836                        continue;
25837                    }
25838                    foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) {
25839                        if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
25840                            $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
25841                            if (!isset($iter[0])) {
25842                                $iter = array($iter);
25843                            }
25844                            foreach ($iter as $package) {
25845                                $this->{"_validate{$type}Dep"}($package);
25846                            }
25847                        }
25848                    }
25849                }
25850            }
25851        }
25852        if (isset($this->_packageInfo['dependencies']['group'])) {
25853            $groups = $this->_packageInfo['dependencies']['group'];
25854            if (!isset($groups[0])) {
25855                $groups = array($groups);
25856            }
25857            $structure = array(
25858                '*package',
25859                '*subpackage',
25860                '*extension',
25861            );
25862            foreach ($groups as $group) {
25863                if ($this->_stupidSchemaValidate($structure, $group, '<group>')) {
25864                    if (!PEAR_Validate::validGroupName($group['attribs']['name'])) {
25865                        $this->_invalidDepGroupName($group['attribs']['name']);
25866                    }
25867                    foreach (array('package', 'subpackage', 'extension') as $type) {
25868                        if (isset($group[$type])) {
25869                            $iter = $group[$type];
25870                            if (!isset($iter[0])) {
25871                                $iter = array($iter);
25872                            }
25873                            foreach ($iter as $package) {
25874                                if ($type != 'extension') {
25875                                    if (isset($package['uri'])) {
25876                                        if (isset($package['channel'])) {
25877                                            $this->_UrlOrChannelGroup($type,
25878                                                $package['name'],
25879                                                $group['name']);
25880                                        }
25881                                    } else {
25882                                        if (!isset($package['channel'])) {
25883                                            $this->_NoChannelGroup($type,
25884                                                $package['name'],
25885                                                $group['name']);
25886                                        }
25887                                    }
25888                                }
25889                                $this->{"_validate{$type}Dep"}($package, '<group name="' .
25890                                    $group['attribs']['name'] . '">');
25891                            }
25892                        }
25893                    }
25894                }
25895            }
25896        }
25897    }
25898
25899    function _validateCompatible()
25900    {
25901        $compat = $this->_packageInfo['compatible'];
25902        if (!isset($compat[0])) {
25903            $compat = array($compat);
25904        }
25905        $required = array('name', 'channel', 'min', 'max', '*exclude');
25906        foreach ($compat as $package) {
25907            $type = '<compatible>';
25908            if (is_array($package) && array_key_exists('name', $package)) {
25909                $type .= '<name>' . $package['name'] . '</name>';
25910            }
25911            $this->_stupidSchemaValidate($required, $package, $type);
25912            if (is_array($package) && array_key_exists('min', $package)) {
25913                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25914                      $package['min'])) {
25915                    $this->_invalidVersion(substr($type, 1) . '<min', $package['min']);
25916                }
25917            }
25918            if (is_array($package) && array_key_exists('max', $package)) {
25919                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25920                      $package['max'])) {
25921                    $this->_invalidVersion(substr($type, 1) . '<max', $package['max']);
25922                }
25923            }
25924            if (is_array($package) && array_key_exists('exclude', $package)) {
25925                if (!is_array($package['exclude'])) {
25926                    $package['exclude'] = array($package['exclude']);
25927                }
25928                foreach ($package['exclude'] as $exclude) {
25929                    if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
25930                          $exclude)) {
25931                        $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
25932                    }
25933                }
25934            }
25935        }
25936    }
25937
25938    function _validateBundle($list)
25939    {
25940        if (!is_array($list) || !isset($list['bundledpackage'])) {
25941            return $this->_NoBundledPackages();
25942        }
25943        if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) {
25944            return $this->_AtLeast2BundledPackages();
25945        }
25946        foreach ($list['bundledpackage'] as $package) {
25947            if (!is_string($package)) {
25948                $this->_bundledPackagesMustBeFilename();
25949            }
25950        }
25951    }
25952
25953    function _validateFilelist($list = false, $allowignore = false, $dirs = '')
25954    {
25955        $iscontents = false;
25956        if (!$list) {
25957            $iscontents = true;
25958            $list = $this->_packageInfo['contents'];
25959            if (isset($this->_packageInfo['bundle'])) {
25960                return $this->_validateBundle($list);
25961            }
25962        }
25963        if ($allowignore) {
25964            $struc = array(
25965                '*install->name->as',
25966                '*ignore->name'
25967            );
25968        } else {
25969            $struc = array(
25970                '*dir->name->?baseinstalldir',
25971                '*file->name->role->?baseinstalldir->?md5sum'
25972            );
25973            if (isset($list['dir']) && isset($list['file'])) {
25974                // stave off validation errors without requiring a set order.
25975                $_old = $list;
25976                if (isset($list['attribs'])) {
25977                    $list = array('attribs' => $_old['attribs']);
25978                }
25979                $list['dir'] = $_old['dir'];
25980                $list['file'] = $_old['file'];
25981            }
25982        }
25983        if (!isset($list['attribs']) || !isset($list['attribs']['name'])) {
25984            $unknown = $allowignore ? '<filelist>' : '<dir name="*unknown*">';
25985            $dirname = $iscontents ? '<contents>' : $unknown;
25986        } else {
25987            $dirname = '<dir name="' . $list['attribs']['name'] . '">';
25988            if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
25989                          str_replace('\\', '/', $list['attribs']['name']))) {
25990                // file contains .. parent directory or . cur directory
25991                $this->_invalidDirName($list['attribs']['name']);
25992            }
25993        }
25994        $res = $this->_stupidSchemaValidate($struc, $list, $dirname);
25995        if ($allowignore && $res) {
25996            $ignored_or_installed = array();
25997            $this->_pf->getFilelist();
25998            $fcontents = $this->_pf->getContents();
25999            $filelist = array();
26000            if (!isset($fcontents['dir']['file'][0])) {
26001                $fcontents['dir']['file'] = array($fcontents['dir']['file']);
26002            }
26003            foreach ($fcontents['dir']['file'] as $file) {
26004                $filelist[$file['attribs']['name']] = true;
26005            }
26006            if (isset($list['install'])) {
26007                if (!isset($list['install'][0])) {
26008                    $list['install'] = array($list['install']);
26009                }
26010                foreach ($list['install'] as $file) {
26011                    if (!isset($filelist[$file['attribs']['name']])) {
26012                        $this->_notInContents($file['attribs']['name'], 'install');
26013                        continue;
26014                    }
26015                    if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
26016                        $this->_multipleInstallAs($file['attribs']['name']);
26017                    }
26018                    if (!isset($ignored_or_installed[$file['attribs']['name']])) {
26019                        $ignored_or_installed[$file['attribs']['name']] = array();
26020                    }
26021                    $ignored_or_installed[$file['attribs']['name']][] = 1;
26022                    if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
26023                                  str_replace('\\', '/', $file['attribs']['as']))) {
26024                        // file contains .. parent directory or . cur directory references
26025                        $this->_invalidFileInstallAs($file['attribs']['name'],
26026                            $file['attribs']['as']);
26027                    }
26028                }
26029            }
26030            if (isset($list['ignore'])) {
26031                if (!isset($list['ignore'][0])) {
26032                    $list['ignore'] = array($list['ignore']);
26033                }
26034                foreach ($list['ignore'] as $file) {
26035                    if (!isset($filelist[$file['attribs']['name']])) {
26036                        $this->_notInContents($file['attribs']['name'], 'ignore');
26037                        continue;
26038                    }
26039                    if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
26040                        $this->_ignoreAndInstallAs($file['attribs']['name']);
26041                    }
26042                }
26043            }
26044        }
26045        if (!$allowignore && isset($list['file'])) {
26046            if (is_string($list['file'])) {
26047                $this->_oldStyleFileNotAllowed();
26048                return false;
26049            }
26050            if (!isset($list['file'][0])) {
26051                // single file
26052                $list['file'] = array($list['file']);
26053            }
26054            foreach ($list['file'] as $i => $file)
26055            {
26056                if (isset($file['attribs']) && isset($file['attribs']['name'])) {
26057                    if ($file['attribs']['name']{0} == '.' &&
26058                          $file['attribs']['name']{1} == '/') {
26059                        // name is something like "./doc/whatever.txt"
26060                        $this->_invalidFileName($file['attribs']['name'], $dirname);
26061                    }
26062                    if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
26063                                  str_replace('\\', '/', $file['attribs']['name']))) {
26064                        // file contains .. parent directory or . cur directory
26065                        $this->_invalidFileName($file['attribs']['name'], $dirname);
26066                    }
26067                }
26068                if (isset($file['attribs']) && isset($file['attribs']['role'])) {
26069                    if (!$this->_validateRole($file['attribs']['role'])) {
26070                        if (isset($this->_packageInfo['usesrole'])) {
26071                            $roles = $this->_packageInfo['usesrole'];
26072                            if (!isset($roles[0])) {
26073                                $roles = array($roles);
26074                            }
26075                            foreach ($roles as $role) {
26076                                if ($role['role'] = $file['attribs']['role']) {
26077                                    $msg = 'This package contains role "%role%" and requires ' .
26078                                        'package "%package%" to be used';
26079                                    if (isset($role['uri'])) {
26080                                        $params = array('role' => $role['role'],
26081                                            'package' => $role['uri']);
26082                                    } else {
26083                                        $params = array('role' => $role['role'],
26084                                            'package' => $this->_pf->_registry->
26085                                            parsedPackageNameToString(array('package' =>
26086                                                $role['package'], 'channel' => $role['channel']),
26087                                                true));
26088                                    }
26089                                    $this->_stack->push('_mustInstallRole', 'error', $params, $msg);
26090                                }
26091                            }
26092                        }
26093                        $this->_invalidFileRole($file['attribs']['name'],
26094                            $dirname, $file['attribs']['role']);
26095                    }
26096                }
26097                if (!isset($file['attribs'])) {
26098                    continue;
26099                }
26100                $save = $file['attribs'];
26101                if ($dirs) {
26102                    $save['name'] = $dirs . '/' . $save['name'];
26103                }
26104                unset($file['attribs']);
26105                if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks
26106                    foreach ($file as $task => $value) {
26107                        if ($tagClass = $this->_pf->getTask($task)) {
26108                            if (!is_array($value) || !isset($value[0])) {
26109                                $value = array($value);
26110                            }
26111                            foreach ($value as $v) {
26112                                $ret = call_user_func(array($tagClass, 'validateXml'),
26113                                    $this->_pf, $v, $this->_pf->_config, $save);
26114                                if (is_array($ret)) {
26115                                    $this->_invalidTask($task, $ret, isset($save['name']) ?
26116                                        $save['name'] : '');
26117                                }
26118                            }
26119                        } else {
26120                            if (isset($this->_packageInfo['usestask'])) {
26121                                $roles = $this->_packageInfo['usestask'];
26122                                if (!isset($roles[0])) {
26123                                    $roles = array($roles);
26124                                }
26125                                foreach ($roles as $role) {
26126                                    if ($role['task'] = $task) {
26127                                        $msg = 'This package contains task "%task%" and requires ' .
26128                                            'package "%package%" to be used';
26129                                        if (isset($role['uri'])) {
26130                                            $params = array('task' => $role['task'],
26131                                                'package' => $role['uri']);
26132                                        } else {
26133                                            $params = array('task' => $role['task'],
26134                                                'package' => $this->_pf->_registry->
26135                                                parsedPackageNameToString(array('package' =>
26136                                                    $role['package'], 'channel' => $role['channel']),
26137                                                    true));
26138                                        }
26139                                        $this->_stack->push('_mustInstallTask', 'error',
26140                                            $params, $msg);
26141                                    }
26142                                }
26143                            }
26144                            $this->_unknownTask($task, $save['name']);
26145                        }
26146                    }
26147                }
26148            }
26149        }
26150        if (isset($list['ignore'])) {
26151            if (!$allowignore) {
26152                $this->_ignoreNotAllowed('ignore');
26153            }
26154        }
26155        if (isset($list['install'])) {
26156            if (!$allowignore) {
26157                $this->_ignoreNotAllowed('install');
26158            }
26159        }
26160        if (isset($list['file'])) {
26161            if ($allowignore) {
26162                $this->_fileNotAllowed('file');
26163            }
26164        }
26165        if (isset($list['dir'])) {
26166            if ($allowignore) {
26167                $this->_fileNotAllowed('dir');
26168            } else {
26169                if (!isset($list['dir'][0])) {
26170                    $list['dir'] = array($list['dir']);
26171                }
26172                foreach ($list['dir'] as $dir) {
26173                    if (isset($dir['attribs']) && isset($dir['attribs']['name'])) {
26174                        if ($dir['attribs']['name'] == '/' ||
26175                              !isset($this->_packageInfo['contents']['dir']['dir'])) {
26176                            // always use nothing if the filelist has already been flattened
26177                            $newdirs = '';
26178                        } elseif ($dirs == '') {
26179                            $newdirs = $dir['attribs']['name'];
26180                        } else {
26181                            $newdirs = $dirs . '/' . $dir['attribs']['name'];
26182                        }
26183                    } else {
26184                        $newdirs = $dirs;
26185                    }
26186                    $this->_validateFilelist($dir, $allowignore, $newdirs);
26187                }
26188            }
26189        }
26190    }
26191
26192    function _validateRelease()
26193    {
26194        if (isset($this->_packageInfo['phprelease'])) {
26195            $release = 'phprelease';
26196            if (isset($this->_packageInfo['providesextension'])) {
26197                $this->_cannotProvideExtension($release);
26198            }
26199            if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
26200                $this->_cannotHaveSrcpackage($release);
26201            }
26202            $releases = $this->_packageInfo['phprelease'];
26203            if (!is_array($releases)) {
26204                return true;
26205            }
26206            if (!isset($releases[0])) {
26207                $releases = array($releases);
26208            }
26209            foreach ($releases as $rel) {
26210                $this->_stupidSchemaValidate(array(
26211                    '*installconditions',
26212                    '*filelist',
26213                ), $rel, '<phprelease>');
26214            }
26215        }
26216        foreach (array('', 'zend') as $prefix) {
26217            $releasetype = $prefix . 'extsrcrelease';
26218            if (isset($this->_packageInfo[$releasetype])) {
26219                $release = $releasetype;
26220                if (!isset($this->_packageInfo['providesextension'])) {
26221                    $this->_mustProvideExtension($release);
26222                }
26223                if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
26224                    $this->_cannotHaveSrcpackage($release);
26225                }
26226                $releases = $this->_packageInfo[$releasetype];
26227                if (!is_array($releases)) {
26228                    return true;
26229                }
26230                if (!isset($releases[0])) {
26231                    $releases = array($releases);
26232                }
26233                foreach ($releases as $rel) {
26234                    $this->_stupidSchemaValidate(array(
26235                        '*installconditions',
26236                        '*configureoption->name->prompt->?default',
26237                        '*binarypackage',
26238                        '*filelist',
26239                    ), $rel, '<' . $releasetype . '>');
26240                    if (isset($rel['binarypackage'])) {
26241                        if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) {
26242                            $rel['binarypackage'] = array($rel['binarypackage']);
26243                        }
26244                        foreach ($rel['binarypackage'] as $bin) {
26245                            if (!is_string($bin)) {
26246                                $this->_binaryPackageMustBePackagename();
26247                            }
26248                        }
26249                    }
26250                }
26251            }
26252            $releasetype = 'extbinrelease';
26253            if (isset($this->_packageInfo[$releasetype])) {
26254                $release = $releasetype;
26255                if (!isset($this->_packageInfo['providesextension'])) {
26256                    $this->_mustProvideExtension($release);
26257                }
26258                if (isset($this->_packageInfo['channel']) &&
26259                      !isset($this->_packageInfo['srcpackage'])) {
26260                    $this->_mustSrcPackage($release);
26261                }
26262                if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) {
26263                    $this->_mustSrcuri($release);
26264                }
26265                $releases = $this->_packageInfo[$releasetype];
26266                if (!is_array($releases)) {
26267                    return true;
26268                }
26269                if (!isset($releases[0])) {
26270                    $releases = array($releases);
26271                }
26272                foreach ($releases as $rel) {
26273                    $this->_stupidSchemaValidate(array(
26274                        '*installconditions',
26275                        '*filelist',
26276                    ), $rel, '<' . $releasetype . '>');
26277                }
26278            }
26279        }
26280        if (isset($this->_packageInfo['bundle'])) {
26281            $release = 'bundle';
26282            if (isset($this->_packageInfo['providesextension'])) {
26283                $this->_cannotProvideExtension($release);
26284            }
26285            if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
26286                $this->_cannotHaveSrcpackage($release);
26287            }
26288            $releases = $this->_packageInfo['bundle'];
26289            if (!is_array($releases) || !isset($releases[0])) {
26290                $releases = array($releases);
26291            }
26292            foreach ($releases as $rel) {
26293                $this->_stupidSchemaValidate(array(
26294                    '*installconditions',
26295                    '*filelist',
26296                ), $rel, '<bundle>');
26297            }
26298        }
26299        foreach ($releases as $rel) {
26300            if (is_array($rel) && array_key_exists('installconditions', $rel)) {
26301                $this->_validateInstallConditions($rel['installconditions'],
26302                    "<$release><installconditions>");
26303            }
26304            if (is_array($rel) && array_key_exists('filelist', $rel)) {
26305                if ($rel['filelist']) {
26306
26307                    $this->_validateFilelist($rel['filelist'], true);
26308                }
26309            }
26310        }
26311    }
26312
26313    /**
26314     * This is here to allow role extension through plugins
26315     * @param string
26316     */
26317    function _validateRole($role)
26318    {
26319        return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType()));
26320    }
26321
26322    function _pearVersionTooLow($version)
26323    {
26324        $this->_stack->push(__FUNCTION__, 'error',
26325            array('version' => $version),
26326            'This package.xml requires PEAR version %version% to parse properly, we are ' .
26327            'version 1.9.4');
26328    }
26329
26330    function _invalidTagOrder($oktags, $actual, $root)
26331    {
26332        $this->_stack->push(__FUNCTION__, 'error',
26333            array('oktags' => $oktags, 'actual' => $actual, 'root' => $root),
26334            'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"');
26335    }
26336
26337    function _ignoreNotAllowed($type)
26338    {
26339        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
26340            '<%type%> is not allowed inside global <contents>, only inside ' .
26341            '<phprelease>/<extbinrelease>/<zendextbinrelease>, use <dir> and <file> only');
26342    }
26343
26344    function _fileNotAllowed($type)
26345    {
26346        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
26347            '<%type%> is not allowed inside release <filelist>, only inside ' .
26348            '<contents>, use <ignore> and <install> only');
26349    }
26350
26351    function _oldStyleFileNotAllowed()
26352    {
26353        $this->_stack->push(__FUNCTION__, 'error', array(),
26354            'Old-style <file>name</file> is not allowed.  Use' .
26355            '<file name="name" role="role"/>');
26356    }
26357
26358    function _tagMissingAttribute($tag, $attr, $context)
26359    {
26360        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
26361            'attribute' => $attr, 'context' => $context),
26362            'tag <%tag%> in context "%context%" has no attribute "%attribute%"');
26363    }
26364
26365    function _tagHasNoAttribs($tag, $context)
26366    {
26367        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
26368            'context' => $context),
26369            'tag <%tag%> has no attributes in context "%context%"');
26370    }
26371
26372    function _invalidInternalStructure()
26373    {
26374        $this->_stack->push(__FUNCTION__, 'exception', array(),
26375            'internal array was not generated by compatible parser, or extreme parser error, cannot continue');
26376    }
26377
26378    function _invalidFileRole($file, $dir, $role)
26379    {
26380        $this->_stack->push(__FUNCTION__, 'error', array(
26381            'file' => $file, 'dir' => $dir, 'role' => $role,
26382            'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())),
26383            'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%');
26384    }
26385
26386    function _invalidFileName($file, $dir)
26387    {
26388        $this->_stack->push(__FUNCTION__, 'error', array(
26389            'file' => $file),
26390            'File "%file%" in directory "%dir%" cannot begin with "./" or contain ".."');
26391    }
26392
26393    function _invalidFileInstallAs($file, $as)
26394    {
26395        $this->_stack->push(__FUNCTION__, 'error', array(
26396            'file' => $file, 'as' => $as),
26397            'File "%file%" <install as="%as%"/> cannot contain "./" or contain ".."');
26398    }
26399
26400    function _invalidDirName($dir)
26401    {
26402        $this->_stack->push(__FUNCTION__, 'error', array(
26403            'dir' => $file),
26404            'Directory "%dir%" cannot begin with "./" or contain ".."');
26405    }
26406
26407    function _filelistCannotContainFile($filelist)
26408    {
26409        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
26410            '<%tag%> can only contain <dir>, contains <file>.  Use ' .
26411            '<dir name="/"> as the first dir element');
26412    }
26413
26414    function _filelistMustContainDir($filelist)
26415    {
26416        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
26417            '<%tag%> must contain <dir>.  Use <dir name="/"> as the ' .
26418            'first dir element');
26419    }
26420
26421    function _tagCannotBeEmpty($tag)
26422    {
26423        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
26424            '<%tag%> cannot be empty (<%tag%/>)');
26425    }
26426
26427    function _UrlOrChannel($type, $name)
26428    {
26429        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
26430            'name' => $name),
26431            'Required dependency <%type%> "%name%" can have either url OR ' .
26432            'channel attributes, and not both');
26433    }
26434
26435    function _NoChannel($type, $name)
26436    {
26437        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
26438            'name' => $name),
26439            'Required dependency <%type%> "%name%" must have either url OR ' .
26440            'channel attributes');
26441    }
26442
26443    function _UrlOrChannelGroup($type, $name, $group)
26444    {
26445        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
26446            'name' => $name, 'group' => $group),
26447            'Group "%group%" dependency <%type%> "%name%" can have either url OR ' .
26448            'channel attributes, and not both');
26449    }
26450
26451    function _NoChannelGroup($type, $name, $group)
26452    {
26453        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
26454            'name' => $name, 'group' => $group),
26455            'Group "%group%" dependency <%type%> "%name%" must have either url OR ' .
26456            'channel attributes');
26457    }
26458
26459    function _unknownChannel($channel)
26460    {
26461        $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel),
26462            'Unknown channel "%channel%"');
26463    }
26464
26465    function _noPackageVersion()
26466    {
26467        $this->_stack->push(__FUNCTION__, 'error', array(),
26468            'package.xml <package> tag has no version attribute, or version is not 2.0');
26469    }
26470
26471    function _NoBundledPackages()
26472    {
26473        $this->_stack->push(__FUNCTION__, 'error', array(),
26474            'No <bundledpackage> tag was found in <contents>, required for bundle packages');
26475    }
26476
26477    function _AtLeast2BundledPackages()
26478    {
26479        $this->_stack->push(__FUNCTION__, 'error', array(),
26480            'At least 2 packages must be bundled in a bundle package');
26481    }
26482
26483    function _ChannelOrUri($name)
26484    {
26485        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
26486            'Bundled package "%name%" can have either a uri or a channel, not both');
26487    }
26488
26489    function _noChildTag($child, $tag)
26490    {
26491        $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag),
26492            'Tag <%tag%> is missing child tag <%child%>');
26493    }
26494
26495    function _invalidVersion($type, $value)
26496    {
26497        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value),
26498            'Version type <%type%> is not a valid version (%value%)');
26499    }
26500
26501    function _invalidState($type, $value)
26502    {
26503        $states = array('stable', 'beta', 'alpha', 'devel');
26504        if ($type != 'api') {
26505            $states[] = 'snapshot';
26506        }
26507        if (strtolower($value) == 'rc') {
26508            $this->_stack->push(__FUNCTION__, 'error',
26509                array('version' => $this->_packageInfo['version']['release']),
26510                'RC is not a state, it is a version postfix, try %version%RC1, stability beta');
26511        }
26512        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value,
26513            'types' => $states),
26514            'Stability type <%type%> is not a valid stability (%value%), must be one of ' .
26515            '%types%');
26516    }
26517
26518    function _invalidTask($task, $ret, $file)
26519    {
26520        switch ($ret[0]) {
26521            case PEAR_TASK_ERROR_MISSING_ATTRIB :
26522                $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file);
26523                $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%';
26524            break;
26525            case PEAR_TASK_ERROR_NOATTRIBS :
26526                $info = array('task' => $task, 'file' => $file);
26527                $msg = 'task <%task%> has no attributes in file %file%';
26528            break;
26529            case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE :
26530                $info = array('attrib' => $ret[1], 'values' => $ret[3],
26531                    'was' => $ret[2], 'task' => $task, 'file' => $file);
26532                $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '.
26533                    'in file %file%, expecting one of "%values%"';
26534            break;
26535            case PEAR_TASK_ERROR_INVALID :
26536                $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file);
26537                $msg = 'task <%task%> in file %file% is invalid because of "%reason%"';
26538            break;
26539        }
26540        $this->_stack->push(__FUNCTION__, 'error', $info, $msg);
26541    }
26542
26543    function _unknownTask($task, $file)
26544    {
26545        $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file),
26546            'Unknown task "%task%" passed in file <file name="%file%">');
26547    }
26548
26549    function _subpackageCannotProvideExtension($name)
26550    {
26551        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
26552            'Subpackage dependency "%name%" cannot use <providesextension>, ' .
26553            'only package dependencies can use this tag');
26554    }
26555
26556    function _subpackagesCannotConflict($name)
26557    {
26558        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
26559            'Subpackage dependency "%name%" cannot use <conflicts/>, ' .
26560            'only package dependencies can use this tag');
26561    }
26562
26563    function _cannotProvideExtension($release)
26564    {
26565        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
26566            '<%release%> packages cannot use <providesextension>, only extbinrelease, extsrcrelease, zendextsrcrelease, and zendextbinrelease can provide a PHP extension');
26567    }
26568
26569    function _mustProvideExtension($release)
26570    {
26571        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
26572            '<%release%> packages must use <providesextension> to indicate which PHP extension is provided');
26573    }
26574
26575    function _cannotHaveSrcpackage($release)
26576    {
26577        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
26578            '<%release%> packages cannot specify a source code package, only extension binaries may use the <srcpackage> tag');
26579    }
26580
26581    function _mustSrcPackage($release)
26582    {
26583        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
26584            '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcpackage>');
26585    }
26586
26587    function _mustSrcuri($release)
26588    {
26589        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
26590            '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcuri>');
26591    }
26592
26593    function _uriDepsCannotHaveVersioning($type)
26594    {
26595        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
26596            '%type%: dependencies with a <uri> tag cannot have any versioning information');
26597    }
26598
26599    function _conflictingDepsCannotHaveVersioning($type)
26600    {
26601        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
26602            '%type%: conflicting dependencies cannot have versioning info, use <exclude> to ' .
26603            'exclude specific versions of a dependency');
26604    }
26605
26606    function _DepchannelCannotBeUri($type)
26607    {
26608        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
26609            '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' .
26610            'dependencies only');
26611    }
26612
26613    function _bundledPackagesMustBeFilename()
26614    {
26615        $this->_stack->push(__FUNCTION__, 'error', array(),
26616            '<bundledpackage> tags must contain only the filename of a package release ' .
26617            'in the bundle');
26618    }
26619
26620    function _binaryPackageMustBePackagename()
26621    {
26622        $this->_stack->push(__FUNCTION__, 'error', array(),
26623            '<binarypackage> tags must contain the name of a package that is ' .
26624            'a compiled version of this extsrc/zendextsrc package');
26625    }
26626
26627    function _fileNotFound($file)
26628    {
26629        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
26630            'File "%file%" in package.xml does not exist');
26631    }
26632
26633    function _notInContents($file, $tag)
26634    {
26635        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag),
26636            '<%tag% name="%file%"> is invalid, file is not in <contents>');
26637    }
26638
26639    function _cannotValidateNoPathSet()
26640    {
26641        $this->_stack->push(__FUNCTION__, 'error', array(),
26642            'Cannot validate files, no path to package file is set (use setPackageFile())');
26643    }
26644
26645    function _usesroletaskMustHaveChannelOrUri($role, $tag)
26646    {
26647        $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
26648            '<%tag%> for role "%role%" must contain either <uri>, or <channel> and <package>');
26649    }
26650
26651    function _usesroletaskMustHavePackage($role, $tag)
26652    {
26653        $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
26654            '<%tag%> for role "%role%" must contain <package>');
26655    }
26656
26657    function _usesroletaskMustHaveRoleTask($tag, $type)
26658    {
26659        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type),
26660            '<%tag%> must contain <%type%> defining the %type% to be used');
26661    }
26662
26663    function _cannotConflictWithAllOs($type)
26664    {
26665        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
26666            '%tag% cannot conflict with all OSes');
26667    }
26668
26669    function _invalidDepGroupName($name)
26670    {
26671        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
26672            'Invalid dependency group name "%name%"');
26673    }
26674
26675    function _multipleToplevelDirNotAllowed()
26676    {
26677        $this->_stack->push(__FUNCTION__, 'error', array(),
26678            'Multiple top-level <dir> tags are not allowed.  Enclose them ' .
26679                'in a <dir name="/">');
26680    }
26681
26682    function _multipleInstallAs($file)
26683    {
26684        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
26685            'Only one <install> tag is allowed for file "%file%"');
26686    }
26687
26688    function _ignoreAndInstallAs($file)
26689    {
26690        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
26691            'Cannot have both <ignore> and <install> tags for file "%file%"');
26692    }
26693
26694    function _analyzeBundledPackages()
26695    {
26696        if (!$this->_isValid) {
26697            return false;
26698        }
26699        if (!$this->_pf->getPackageType() == 'bundle') {
26700            return false;
26701        }
26702        if (!isset($this->_pf->_packageFile)) {
26703            return false;
26704        }
26705        $dir_prefix = dirname($this->_pf->_packageFile);
26706        $common = new PEAR_Common;
26707        $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
26708            array($common, 'log');
26709        $info = $this->_pf->getContents();
26710        $info = $info['bundledpackage'];
26711        if (!is_array($info)) {
26712            $info = array($info);
26713        }
26714        $pkg = &new PEAR_PackageFile($this->_pf->_config);
26715        foreach ($info as $package) {
26716            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) {
26717                $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package);
26718                $this->_isValid = 0;
26719                continue;
26720            }
26721            call_user_func_array($log, array(1, "Analyzing bundled package $package"));
26722            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
26723            $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package,
26724                PEAR_VALIDATE_NORMAL);
26725            PEAR::popErrorHandling();
26726            if (PEAR::isError($ret)) {
26727                call_user_func_array($log, array(0, "ERROR: package $package is not a valid " .
26728                    'package'));
26729                $inf = $ret->getUserInfo();
26730                if (is_array($inf)) {
26731                    foreach ($inf as $err) {
26732                        call_user_func_array($log, array(1, $err['message']));
26733                    }
26734                }
26735                return false;
26736            }
26737        }
26738        return true;
26739    }
26740
26741    function _analyzePhpFiles()
26742    {
26743        if (!$this->_isValid) {
26744            return false;
26745        }
26746        if (!isset($this->_pf->_packageFile)) {
26747            $this->_cannotValidateNoPathSet();
26748            return false;
26749        }
26750        $dir_prefix = dirname($this->_pf->_packageFile);
26751        $common = new PEAR_Common;
26752        $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
26753            array(&$common, 'log');
26754        $info = $this->_pf->getContents();
26755        if (!$info || !isset($info['dir']['file'])) {
26756            $this->_tagCannotBeEmpty('contents><dir');
26757            return false;
26758        }
26759        $info = $info['dir']['file'];
26760        if (isset($info['attribs'])) {
26761            $info = array($info);
26762        }
26763        $provides = array();
26764        foreach ($info as $fa) {
26765            $fa = $fa['attribs'];
26766            $file = $fa['name'];
26767            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
26768                $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file);
26769                $this->_isValid = 0;
26770                continue;
26771            }
26772            if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) {
26773                call_user_func_array($log, array(1, "Analyzing $file"));
26774                $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
26775                if ($srcinfo) {
26776                    $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo));
26777                }
26778            }
26779        }
26780        $this->_packageName = $pn = $this->_pf->getPackage();
26781        $pnl = strlen($pn);
26782        foreach ($provides as $key => $what) {
26783            if (isset($what['explicit']) || !$what) {
26784                // skip conformance checks if the provides entry is
26785                // specified in the package.xml file
26786                continue;
26787            }
26788            extract($what);
26789            if ($type == 'class') {
26790                if (!strncasecmp($name, $pn, $pnl)) {
26791                    continue;
26792                }
26793                $this->_stack->push(__FUNCTION__, 'warning',
26794                    array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
26795                    'in %file%: %type% "%name%" not prefixed with package name "%package%"');
26796            } elseif ($type == 'function') {
26797                if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
26798                    continue;
26799                }
26800                $this->_stack->push(__FUNCTION__, 'warning',
26801                    array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
26802                    'in %file%: %type% "%name%" not prefixed with package name "%package%"');
26803            }
26804        }
26805        return $this->_isValid;
26806    }
26807
26808    /**
26809     * Analyze the source code of the given PHP file
26810     *
26811     * @param  string Filename of the PHP file
26812     * @param  boolean whether to analyze $file as the file contents
26813     * @return mixed
26814     */
26815    function analyzeSourceCode($file, $string = false)
26816    {
26817        if (!function_exists("token_get_all")) {
26818            $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
26819                'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer');
26820            return false;
26821        }
26822
26823        if (!defined('T_DOC_COMMENT')) {
26824            define('T_DOC_COMMENT', T_COMMENT);
26825        }
26826
26827        if (!defined('T_INTERFACE')) {
26828            define('T_INTERFACE', -1);
26829        }
26830
26831        if (!defined('T_IMPLEMENTS')) {
26832            define('T_IMPLEMENTS', -1);
26833        }
26834
26835        if ($string) {
26836            $contents = $file;
26837        } else {
26838            if (!$fp = @fopen($file, "r")) {
26839                return false;
26840            }
26841            fclose($fp);
26842            $contents = file_get_contents($file);
26843        }
26844
26845        // Silence this function so we can catch PHP Warnings and show our own custom message
26846        $tokens = @token_get_all($contents);
26847        if (isset($php_errormsg)) {
26848            if (isset($this->_stack)) {
26849                $pn = $this->_pf->getPackage();
26850                $this->_stack->push(__FUNCTION__, 'warning',
26851                        array('file' => $file, 'package' => $pn),
26852                        'in %file%: Could not process file for unkown reasons,' .
26853                        ' possibly a PHP parse error in %file% from %package%');
26854            }
26855        }
26856/*
26857        for ($i = 0; $i < sizeof($tokens); $i++) {
26858            @list($token, $data) = $tokens[$i];
26859            if (is_string($token)) {
26860                var_dump($token);
26861            } else {
26862                print token_name($token) . ' ';
26863                var_dump(rtrim($data));
26864            }
26865        }
26866*/
26867        $look_for = 0;
26868        $paren_level = 0;
26869        $bracket_level = 0;
26870        $brace_level = 0;
26871        $lastphpdoc = '';
26872        $current_class = '';
26873        $current_interface = '';
26874        $current_class_level = -1;
26875        $current_function = '';
26876        $current_function_level = -1;
26877        $declared_classes = array();
26878        $declared_interfaces = array();
26879        $declared_functions = array();
26880        $declared_methods = array();
26881        $used_classes = array();
26882        $used_functions = array();
26883        $extends = array();
26884        $implements = array();
26885        $nodeps = array();
26886        $inquote = false;
26887        $interface = false;
26888        for ($i = 0; $i < sizeof($tokens); $i++) {
26889            if (is_array($tokens[$i])) {
26890                list($token, $data) = $tokens[$i];
26891            } else {
26892                $token = $tokens[$i];
26893                $data = '';
26894            }
26895
26896            if ($inquote) {
26897                if ($token != '"' && $token != T_END_HEREDOC) {
26898                    continue;
26899                } else {
26900                    $inquote = false;
26901                    continue;
26902                }
26903            }
26904
26905            switch ($token) {
26906                case T_WHITESPACE :
26907                    continue;
26908                case ';':
26909                    if ($interface) {
26910                        $current_function = '';
26911                        $current_function_level = -1;
26912                    }
26913                    break;
26914                case '"':
26915                case T_START_HEREDOC:
26916                    $inquote = true;
26917                    break;
26918                case T_CURLY_OPEN:
26919                case T_DOLLAR_OPEN_CURLY_BRACES:
26920                case '{': $brace_level++; continue 2;
26921                case '}':
26922                    $brace_level--;
26923                    if ($current_class_level == $brace_level) {
26924                        $current_class = '';
26925                        $current_class_level = -1;
26926                    }
26927                    if ($current_function_level == $brace_level) {
26928                        $current_function = '';
26929                        $current_function_level = -1;
26930                    }
26931                    continue 2;
26932                case '[': $bracket_level++; continue 2;
26933                case ']': $bracket_level--; continue 2;
26934                case '(': $paren_level++;   continue 2;
26935                case ')': $paren_level--;   continue 2;
26936                case T_INTERFACE:
26937                    $interface = true;
26938                case T_CLASS:
26939                    if (($current_class_level != -1) || ($current_function_level != -1)) {
26940                        if (isset($this->_stack)) {
26941                            $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
26942                            'Parser error: invalid PHP found in file "%file%"');
26943                        } else {
26944                            PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
26945                                PEAR_COMMON_ERROR_INVALIDPHP);
26946                        }
26947
26948                        return false;
26949                    }
26950                case T_FUNCTION:
26951                case T_NEW:
26952                case T_EXTENDS:
26953                case T_IMPLEMENTS:
26954                    $look_for = $token;
26955                    continue 2;
26956                case T_STRING:
26957                    if (version_compare(zend_version(), '2.0', '<')) {
26958                        if (in_array(strtolower($data),
26959                            array('public', 'private', 'protected', 'abstract',
26960                                  'interface', 'implements', 'throw')
26961                                 )
26962                        ) {
26963                            if (isset($this->_stack)) {
26964                                $this->_stack->push(__FUNCTION__, 'warning', array(
26965                                    'file' => $file),
26966                                    'Error, PHP5 token encountered in %file%,' .
26967                                    ' analysis should be in PHP5');
26968                            } else {
26969                                PEAR::raiseError('Error: PHP5 token encountered in ' . $file .
26970                                    'packaging should be done in PHP 5');
26971                                return false;
26972                            }
26973                        }
26974                    }
26975
26976                    if ($look_for == T_CLASS) {
26977                        $current_class = $data;
26978                        $current_class_level = $brace_level;
26979                        $declared_classes[] = $current_class;
26980                    } elseif ($look_for == T_INTERFACE) {
26981                        $current_interface = $data;
26982                        $current_class_level = $brace_level;
26983                        $declared_interfaces[] = $current_interface;
26984                    } elseif ($look_for == T_IMPLEMENTS) {
26985                        $implements[$current_class] = $data;
26986                    } elseif ($look_for == T_EXTENDS) {
26987                        $extends[$current_class] = $data;
26988                    } elseif ($look_for == T_FUNCTION) {
26989                        if ($current_class) {
26990                            $current_function = "$current_class::$data";
26991                            $declared_methods[$current_class][] = $data;
26992                        } elseif ($current_interface) {
26993                            $current_function = "$current_interface::$data";
26994                            $declared_methods[$current_interface][] = $data;
26995                        } else {
26996                            $current_function = $data;
26997                            $declared_functions[] = $current_function;
26998                        }
26999
27000                        $current_function_level = $brace_level;
27001                        $m = array();
27002                    } elseif ($look_for == T_NEW) {
27003                        $used_classes[$data] = true;
27004                    }
27005
27006                    $look_for = 0;
27007                    continue 2;
27008                case T_VARIABLE:
27009                    $look_for = 0;
27010                    continue 2;
27011                case T_DOC_COMMENT:
27012                case T_COMMENT:
27013                    if (preg_match('!^/\*\*\s!', $data)) {
27014                        $lastphpdoc = $data;
27015                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
27016                            $nodeps = array_merge($nodeps, $m[1]);
27017                        }
27018                    }
27019                    continue 2;
27020                case T_DOUBLE_COLON:
27021                    $token = $tokens[$i - 1][0];
27022                    if (!($token == T_WHITESPACE || $token == T_STRING || $token == T_STATIC)) {
27023                        if (isset($this->_stack)) {
27024                            $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file),
27025                                'Parser error: invalid PHP found in file "%file%"');
27026                        } else {
27027                            PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
27028                                PEAR_COMMON_ERROR_INVALIDPHP);
27029                        }
27030
27031                        return false;
27032                    }
27033
27034                    $class = $tokens[$i - 1][1];
27035                    if (strtolower($class) != 'parent') {
27036                        $used_classes[$class] = true;
27037                    }
27038
27039                    continue 2;
27040            }
27041        }
27042
27043        return array(
27044            "source_file" => $file,
27045            "declared_classes" => $declared_classes,
27046            "declared_interfaces" => $declared_interfaces,
27047            "declared_methods" => $declared_methods,
27048            "declared_functions" => $declared_functions,
27049            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
27050            "inheritance" => $extends,
27051            "implements" => $implements,
27052        );
27053    }
27054
27055    /**
27056     * Build a "provides" array from data returned by
27057     * analyzeSourceCode().  The format of the built array is like
27058     * this:
27059     *
27060     *  array(
27061     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
27062     *    ...
27063     *  )
27064     *
27065     *
27066     * @param array $srcinfo array with information about a source file
27067     * as returned by the analyzeSourceCode() method.
27068     *
27069     * @return void
27070     *
27071     * @access private
27072     *
27073     */
27074    function _buildProvidesArray($srcinfo)
27075    {
27076        if (!$this->_isValid) {
27077            return array();
27078        }
27079
27080        $providesret = array();
27081        $file        = basename($srcinfo['source_file']);
27082        $pn          = isset($this->_pf) ? $this->_pf->getPackage() : '';
27083        $pnl         = strlen($pn);
27084        foreach ($srcinfo['declared_classes'] as $class) {
27085            $key = "class;$class";
27086            if (isset($providesret[$key])) {
27087                continue;
27088            }
27089
27090            $providesret[$key] =
27091                array('file'=> $file, 'type' => 'class', 'name' => $class);
27092            if (isset($srcinfo['inheritance'][$class])) {
27093                $providesret[$key]['extends'] =
27094                    $srcinfo['inheritance'][$class];
27095            }
27096        }
27097
27098        foreach ($srcinfo['declared_methods'] as $class => $methods) {
27099            foreach ($methods as $method) {
27100                $function = "$class::$method";
27101                $key = "function;$function";
27102                if ($method{0} == '_' || !strcasecmp($method, $class) ||
27103                    isset($providesret[$key])) {
27104                    continue;
27105                }
27106
27107                $providesret[$key] =
27108                    array('file'=> $file, 'type' => 'function', 'name' => $function);
27109            }
27110        }
27111
27112        foreach ($srcinfo['declared_functions'] as $function) {
27113            $key = "function;$function";
27114            if ($function{0} == '_' || isset($providesret[$key])) {
27115                continue;
27116            }
27117
27118            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
27119                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
27120            }
27121
27122            $providesret[$key] =
27123                array('file'=> $file, 'type' => 'function', 'name' => $function);
27124        }
27125
27126        return $providesret;
27127    }
27128}PEAR-1.9.4/PEAR/PackageFile/v1.php0000644000076500000240000014376011605156614015170 0ustar  helgistaff<?php
27129/**
27130 * PEAR_PackageFile_v1, package.xml version 1.0
27131 *
27132 * PHP versions 4 and 5
27133 *
27134 * @category   pear
27135 * @package    PEAR
27136 * @author     Greg Beaver <cellog@php.net>
27137 * @copyright  1997-2009 The Authors
27138 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
27139 * @version    CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $
27140 * @link       http://pear.php.net/package/PEAR
27141 * @since      File available since Release 1.4.0a1
27142 */
27143/**
27144 * For error handling
27145 */
27146require_once 'PEAR/ErrorStack.php';
27147
27148/**
27149 * Error code if parsing is attempted with no xml extension
27150 */
27151define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3);
27152
27153/**
27154 * Error code if creating the xml parser resource fails
27155 */
27156define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4);
27157
27158/**
27159 * Error code used for all sax xml parsing errors
27160 */
27161define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5);
27162
27163/**
27164 * Error code used when there is no name
27165 */
27166define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6);
27167
27168/**
27169 * Error code when a package name is not valid
27170 */
27171define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7);
27172
27173/**
27174 * Error code used when no summary is parsed
27175 */
27176define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8);
27177
27178/**
27179 * Error code for summaries that are more than 1 line
27180 */
27181define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9);
27182
27183/**
27184 * Error code used when no description is present
27185 */
27186define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10);
27187
27188/**
27189 * Error code used when no license is present
27190 */
27191define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11);
27192
27193/**
27194 * Error code used when a <version> version number is not present
27195 */
27196define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12);
27197
27198/**
27199 * Error code used when a <version> version number is invalid
27200 */
27201define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13);
27202
27203/**
27204 * Error code when release state is missing
27205 */
27206define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14);
27207
27208/**
27209 * Error code when release state is invalid
27210 */
27211define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15);
27212
27213/**
27214 * Error code when release state is missing
27215 */
27216define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16);
27217
27218/**
27219 * Error code when release state is invalid
27220 */
27221define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17);
27222
27223/**
27224 * Error code when no release notes are found
27225 */
27226define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18);
27227
27228/**
27229 * Error code when no maintainers are found
27230 */
27231define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19);
27232
27233/**
27234 * Error code when a maintainer has no handle
27235 */
27236define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20);
27237
27238/**
27239 * Error code when a maintainer has no handle
27240 */
27241define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21);
27242
27243/**
27244 * Error code when a maintainer has no name
27245 */
27246define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22);
27247
27248/**
27249 * Error code when a maintainer has no email
27250 */
27251define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23);
27252
27253/**
27254 * Error code when a maintainer has no handle
27255 */
27256define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24);
27257
27258/**
27259 * Error code when a dependency is not a PHP dependency, but has no name
27260 */
27261define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25);
27262
27263/**
27264 * Error code when a dependency has no type (pkg, php, etc.)
27265 */
27266define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26);
27267
27268/**
27269 * Error code when a dependency has no relation (lt, ge, has, etc.)
27270 */
27271define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27);
27272
27273/**
27274 * Error code when a dependency is not a 'has' relation, but has no version
27275 */
27276define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28);
27277
27278/**
27279 * Error code when a dependency has an invalid relation
27280 */
27281define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29);
27282
27283/**
27284 * Error code when a dependency has an invalid type
27285 */
27286define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30);
27287
27288/**
27289 * Error code when a dependency has an invalid optional option
27290 */
27291define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31);
27292
27293/**
27294 * Error code when a dependency is a pkg dependency, and has an invalid package name
27295 */
27296define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32);
27297
27298/**
27299 * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel
27300 */
27301define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33);
27302
27303/**
27304 * Error code when rel="has" and version attribute is present.
27305 */
27306define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34);
27307
27308/**
27309 * Error code when type="php" and dependency name is present
27310 */
27311define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35);
27312
27313/**
27314 * Error code when a configure option has no name
27315 */
27316define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36);
27317
27318/**
27319 * Error code when a configure option has no name
27320 */
27321define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37);
27322
27323/**
27324 * Error code when a file in the filelist has an invalid role
27325 */
27326define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38);
27327
27328/**
27329 * Error code when a file in the filelist has no role
27330 */
27331define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39);
27332
27333/**
27334 * Error code when analyzing a php source file that has parse errors
27335 */
27336define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40);
27337
27338/**
27339 * Error code when analyzing a php source file reveals a source element
27340 * without a package name prefix
27341 */
27342define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41);
27343
27344/**
27345 * Error code when an unknown channel is specified
27346 */
27347define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42);
27348
27349/**
27350 * Error code when no files are found in the filelist
27351 */
27352define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43);
27353
27354/**
27355 * Error code when a file is not valid php according to _analyzeSourceCode()
27356 */
27357define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44);
27358
27359/**
27360 * Error code when the channel validator returns an error or warning
27361 */
27362define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45);
27363
27364/**
27365 * Error code when a php5 package is packaged in php4 (analysis doesn't work)
27366 */
27367define('PEAR_PACKAGEFILE_ERROR_PHP5', 46);
27368
27369/**
27370 * Error code when a file is listed in package.xml but does not exist
27371 */
27372define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47);
27373
27374/**
27375 * Error code when a <dep type="php" rel="not"... is encountered (use rel="ne")
27376 */
27377define('PEAR_PACKAGEFILE_PHP_NO_NOT', 48);
27378
27379/**
27380 * Error code when a package.xml contains non-ISO-8859-1 characters
27381 */
27382define('PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS', 49);
27383
27384/**
27385 * Error code when a dependency is not a 'has' relation, but has no version
27386 */
27387define('PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION', 50);
27388
27389/**
27390 * Error code when a package has no lead developer
27391 */
27392define('PEAR_PACKAGEFILE_ERROR_NO_LEAD', 51);
27393
27394/**
27395 * Error code when a filename begins with "."
27396 */
27397define('PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME', 52);
27398/**
27399 * package.xml encapsulator
27400 * @category   pear
27401 * @package    PEAR
27402 * @author     Greg Beaver <cellog@php.net>
27403 * @copyright  1997-2009 The Authors
27404 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
27405 * @version    Release: 1.9.4
27406 * @link       http://pear.php.net/package/PEAR
27407 * @since      Class available since Release 1.4.0a1
27408 */
27409class PEAR_PackageFile_v1
27410{
27411    /**
27412     * @access private
27413     * @var PEAR_ErrorStack
27414     * @access private
27415     */
27416    var $_stack;
27417
27418    /**
27419     * A registry object, used to access the package name validation regex for non-standard channels
27420     * @var PEAR_Registry
27421     * @access private
27422     */
27423    var $_registry;
27424
27425    /**
27426     * An object that contains a log method that matches PEAR_Common::log's signature
27427     * @var object
27428     * @access private
27429     */
27430    var $_logger;
27431
27432    /**
27433     * Parsed package information
27434     * @var array
27435     * @access private
27436     */
27437    var $_packageInfo;
27438
27439    /**
27440     * path to package.xml
27441     * @var string
27442     * @access private
27443     */
27444    var $_packageFile;
27445
27446    /**
27447     * path to package .tgz or false if this is a local/extracted package.xml
27448     * @var string
27449     * @access private
27450     */
27451    var $_archiveFile;
27452
27453    /**
27454     * @var int
27455     * @access private
27456     */
27457    var $_isValid = 0;
27458
27459    /**
27460     * Determines whether this packagefile was initialized only with partial package info
27461     *
27462     * If this package file was constructed via parsing REST, it will only contain
27463     *
27464     * - package name
27465     * - channel name
27466     * - dependencies 
27467     * @var boolean
27468     * @access private
27469     */
27470    var $_incomplete = true;
27471
27472    /**
27473     * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack
27474     * @param string Name of Error Stack class to use.
27475     */
27476    function PEAR_PackageFile_v1()
27477    {
27478        $this->_stack = &new PEAR_ErrorStack('PEAR_PackageFile_v1');
27479        $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
27480        $this->_isValid = 0;
27481    }
27482
27483    function installBinary($installer)
27484    {
27485        return false;
27486    }
27487
27488    function isExtension($name)
27489    {
27490        return false;
27491    }
27492
27493    function setConfig(&$config)
27494    {
27495        $this->_config = &$config;
27496        $this->_registry = &$config->getRegistry();
27497    }
27498
27499    function setRequestedGroup()
27500    {
27501        // placeholder
27502    }
27503
27504    /**
27505     * For saving in the registry.
27506     *
27507     * Set the last version that was installed
27508     * @param string
27509     */
27510    function setLastInstalledVersion($version)
27511    {
27512        $this->_packageInfo['_lastversion'] = $version;
27513    }
27514
27515    /**
27516     * @return string|false
27517     */
27518    function getLastInstalledVersion()
27519    {
27520        if (isset($this->_packageInfo['_lastversion'])) {
27521            return $this->_packageInfo['_lastversion'];
27522        }
27523        return false;
27524    }
27525
27526    function getInstalledBinary()
27527    {
27528        return false;
27529    }
27530
27531    function listPostinstallScripts()
27532    {
27533        return false;
27534    }
27535
27536    function initPostinstallScripts()
27537    {
27538        return false;
27539    }
27540
27541    function setLogger(&$logger)
27542    {
27543        if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) {
27544            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
27545        }
27546        $this->_logger = &$logger;
27547    }
27548
27549    function setPackagefile($file, $archive = false)
27550    {
27551        $this->_packageFile = $file;
27552        $this->_archiveFile = $archive ? $archive : $file;
27553    }
27554
27555    function getPackageFile()
27556    {
27557        return isset($this->_packageFile) ? $this->_packageFile : false;
27558    }
27559
27560    function getPackageType()
27561    {
27562        return 'php';
27563    }
27564
27565    function getArchiveFile()
27566    {
27567        return $this->_archiveFile;
27568    }
27569
27570    function packageInfo($field)
27571    {
27572        if (!is_string($field) || empty($field) ||
27573            !isset($this->_packageInfo[$field])) {
27574            return false;
27575        }
27576        return $this->_packageInfo[$field];
27577    }
27578
27579    function setDirtree($path)
27580    {
27581        if (!isset($this->_packageInfo['dirtree'])) {
27582            $this->_packageInfo['dirtree'] = array();
27583        }
27584        $this->_packageInfo['dirtree'][$path] = true;
27585    }
27586
27587    function getDirtree()
27588    {
27589        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
27590            return $this->_packageInfo['dirtree'];
27591        }
27592        return false;
27593    }
27594
27595    function resetDirtree()
27596    {
27597        unset($this->_packageInfo['dirtree']);
27598    }
27599
27600    function fromArray($pinfo)
27601    {
27602        $this->_incomplete = false;
27603        $this->_packageInfo = $pinfo;
27604    }
27605
27606    function isIncomplete()
27607    {
27608        return $this->_incomplete;
27609    }
27610
27611    function getChannel()
27612    {
27613        return 'pear.php.net';
27614    }
27615
27616    function getUri()
27617    {
27618        return false;
27619    }
27620
27621    function getTime()
27622    {
27623        return false;
27624    }
27625
27626    function getExtends()
27627    {
27628        if (isset($this->_packageInfo['extends'])) {
27629            return $this->_packageInfo['extends'];
27630        }
27631        return false;
27632    }
27633
27634    /**
27635     * @return array
27636     */
27637    function toArray()
27638    {
27639        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
27640            return false;
27641        }
27642        return $this->getArray();
27643    }
27644
27645    function getArray()
27646    {
27647        return $this->_packageInfo;
27648    }
27649
27650    function getName()
27651    {
27652        return $this->getPackage();
27653    }
27654
27655    function getPackage()
27656    {
27657        if (isset($this->_packageInfo['package'])) {
27658            return $this->_packageInfo['package'];
27659        }
27660        return false;
27661    }
27662
27663    /**
27664     * WARNING - don't use this unless you know what you are doing
27665     */
27666    function setRawPackage($package)
27667    {
27668        $this->_packageInfo['package'] = $package;
27669    }
27670
27671    function setPackage($package)
27672    {
27673        $this->_packageInfo['package'] = $package;
27674        $this->_isValid = false;
27675    }
27676
27677    function getVersion()
27678    {
27679        if (isset($this->_packageInfo['version'])) {
27680            return $this->_packageInfo['version'];
27681        }
27682        return false;
27683    }
27684
27685    function setVersion($version)
27686    {
27687        $this->_packageInfo['version'] = $version;
27688        $this->_isValid = false;
27689    }
27690
27691    function clearMaintainers()
27692    {
27693        unset($this->_packageInfo['maintainers']);
27694    }
27695
27696    function getMaintainers()
27697    {
27698        if (isset($this->_packageInfo['maintainers'])) {
27699            return $this->_packageInfo['maintainers'];
27700        }
27701        return false;
27702    }
27703
27704    /**
27705     * Adds a new maintainer - no checking of duplicates is performed, use
27706     * updatemaintainer for that purpose.
27707     */
27708    function addMaintainer($role, $handle, $name, $email)
27709    {
27710        $this->_packageInfo['maintainers'][] =
27711            array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name);
27712        $this->_isValid = false;
27713    }
27714
27715    function updateMaintainer($role, $handle, $name, $email)
27716    {
27717        $found = false;
27718        if (!isset($this->_packageInfo['maintainers']) ||
27719              !is_array($this->_packageInfo['maintainers'])) {
27720            return $this->addMaintainer($role, $handle, $name, $email);
27721        }
27722        foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
27723            if ($maintainer['handle'] == $handle) {
27724                $found = $i;
27725                break;
27726            }
27727        }
27728        if ($found !== false) {
27729            unset($this->_packageInfo['maintainers'][$found]);
27730            $this->_packageInfo['maintainers'] =
27731                array_values($this->_packageInfo['maintainers']);
27732        }
27733        $this->addMaintainer($role, $handle, $name, $email);
27734    }
27735
27736    function deleteMaintainer($handle)
27737    {
27738        $found = false;
27739        foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
27740            if ($maintainer['handle'] == $handle) {
27741                $found = $i;
27742                break;
27743            }
27744        }
27745        if ($found !== false) {
27746            unset($this->_packageInfo['maintainers'][$found]);
27747            $this->_packageInfo['maintainers'] =
27748                array_values($this->_packageInfo['maintainers']);
27749            return true;
27750        }
27751        return false;
27752    }
27753
27754    function getState()
27755    {
27756        if (isset($this->_packageInfo['release_state'])) {
27757            return $this->_packageInfo['release_state'];
27758        }
27759        return false;
27760    }
27761
27762    function setRawState($state)
27763    {
27764        $this->_packageInfo['release_state'] = $state;
27765    }
27766
27767    function setState($state)
27768    {
27769        $this->_packageInfo['release_state'] = $state;
27770        $this->_isValid = false;
27771    }
27772
27773    function getDate()
27774    {
27775        if (isset($this->_packageInfo['release_date'])) {
27776            return $this->_packageInfo['release_date'];
27777        }
27778        return false;
27779    }
27780
27781    function setDate($date)
27782    {
27783        $this->_packageInfo['release_date'] = $date;
27784        $this->_isValid = false;
27785    }
27786
27787    function getLicense()
27788    {
27789        if (isset($this->_packageInfo['release_license'])) {
27790            return $this->_packageInfo['release_license'];
27791        }
27792        return false;
27793    }
27794
27795    function setLicense($date)
27796    {
27797        $this->_packageInfo['release_license'] = $date;
27798        $this->_isValid = false;
27799    }
27800
27801    function getSummary()
27802    {
27803        if (isset($this->_packageInfo['summary'])) {
27804            return $this->_packageInfo['summary'];
27805        }
27806        return false;
27807    }
27808
27809    function setSummary($summary)
27810    {
27811        $this->_packageInfo['summary'] = $summary;
27812        $this->_isValid = false;
27813    }
27814
27815    function getDescription()
27816    {
27817        if (isset($this->_packageInfo['description'])) {
27818            return $this->_packageInfo['description'];
27819        }
27820        return false;
27821    }
27822
27823    function setDescription($desc)
27824    {
27825        $this->_packageInfo['description'] = $desc;
27826        $this->_isValid = false;
27827    }
27828
27829    function getNotes()
27830    {
27831        if (isset($this->_packageInfo['release_notes'])) {
27832            return $this->_packageInfo['release_notes'];
27833        }
27834        return false;
27835    }
27836
27837    function setNotes($notes)
27838    {
27839        $this->_packageInfo['release_notes'] = $notes;
27840        $this->_isValid = false;
27841    }
27842
27843    function getDeps()
27844    {
27845        if (isset($this->_packageInfo['release_deps'])) {
27846            return $this->_packageInfo['release_deps'];
27847        }
27848        return false;
27849    }
27850
27851    /**
27852     * Reset dependencies prior to adding new ones
27853     */
27854    function clearDeps()
27855    {
27856        unset($this->_packageInfo['release_deps']);
27857    }
27858
27859    function addPhpDep($version, $rel)
27860    {
27861        $this->_isValid = false;
27862        $this->_packageInfo['release_deps'][] =
27863            array('type' => 'php',
27864                  'rel' => $rel,
27865                  'version' => $version);
27866    }
27867
27868    function addPackageDep($name, $version, $rel, $optional = 'no')
27869    {
27870        $this->_isValid = false;
27871        $dep =
27872            array('type' => 'pkg',
27873                  'name' => $name,
27874                  'rel' => $rel,
27875                  'optional' => $optional);
27876        if ($rel != 'has' && $rel != 'not') {
27877            $dep['version'] = $version;
27878        }
27879        $this->_packageInfo['release_deps'][] = $dep;
27880    }
27881
27882    function addExtensionDep($name, $version, $rel, $optional = 'no')
27883    {
27884        $this->_isValid = false;
27885        $this->_packageInfo['release_deps'][] =
27886            array('type' => 'ext',
27887                  'name' => $name,
27888                  'rel' => $rel,
27889                  'version' => $version,
27890                  'optional' => $optional);
27891    }
27892
27893    /**
27894     * WARNING - do not use this function directly unless you know what you're doing
27895     */
27896    function setDeps($deps)
27897    {
27898        $this->_packageInfo['release_deps'] = $deps;
27899    }
27900
27901    function hasDeps()
27902    {
27903        return isset($this->_packageInfo['release_deps']) &&
27904            count($this->_packageInfo['release_deps']);
27905    }
27906
27907    function getDependencyGroup($group)
27908    {
27909        return false;
27910    }
27911
27912    function isCompatible($pf)
27913    {
27914        return false;
27915    }
27916
27917    function isSubpackageOf($p)
27918    {
27919        return $p->isSubpackage($this);
27920    }
27921
27922    function isSubpackage($p)
27923    {
27924        return false;
27925    }
27926
27927    function dependsOn($package, $channel)
27928    {
27929        if (strtolower($channel) != 'pear.php.net') {
27930            return false;
27931        }
27932        if (!($deps = $this->getDeps())) {
27933            return false;
27934        }
27935        foreach ($deps as $dep) {
27936            if ($dep['type'] != 'pkg') {
27937                continue;
27938            }
27939            if (strtolower($dep['name']) == strtolower($package)) {
27940                return true;
27941            }
27942        }
27943        return false;
27944    }
27945
27946    function getConfigureOptions()
27947    {
27948        if (isset($this->_packageInfo['configure_options'])) {
27949            return $this->_packageInfo['configure_options'];
27950        }
27951        return false;
27952    }
27953
27954    function hasConfigureOptions()
27955    {
27956        return isset($this->_packageInfo['configure_options']) &&
27957            count($this->_packageInfo['configure_options']);
27958    }
27959
27960    function addConfigureOption($name, $prompt, $default = false)
27961    {
27962        $o = array('name' => $name, 'prompt' => $prompt);
27963        if ($default !== false) {
27964            $o['default'] = $default;
27965        }
27966        if (!isset($this->_packageInfo['configure_options'])) {
27967            $this->_packageInfo['configure_options'] = array();
27968        }
27969        $this->_packageInfo['configure_options'][] = $o;
27970    }
27971
27972    function clearConfigureOptions()
27973    {
27974        unset($this->_packageInfo['configure_options']);
27975    }
27976
27977    function getProvides()
27978    {
27979        if (isset($this->_packageInfo['provides'])) {
27980            return $this->_packageInfo['provides'];
27981        }
27982        return false;
27983    }
27984
27985    function getProvidesExtension()
27986    {
27987        return false;
27988    }
27989
27990    function addFile($dir, $file, $attrs)
27991    {
27992        $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
27993        if ($dir == '/' || $dir == '') {
27994            $dir = '';
27995        } else {
27996            $dir .= '/';
27997        }
27998        $file = $dir . $file;
27999        $file = preg_replace('![\\/]+!', '/', $file);
28000        $this->_packageInfo['filelist'][$file] = $attrs;
28001    }
28002
28003    function getInstallationFilelist()
28004    {
28005        return $this->getFilelist();
28006    }
28007
28008    function getFilelist()
28009    {
28010        if (isset($this->_packageInfo['filelist'])) {
28011            return $this->_packageInfo['filelist'];
28012        }
28013        return false;
28014    }
28015
28016    function setFileAttribute($file, $attr, $value)
28017    {
28018        $this->_packageInfo['filelist'][$file][$attr] = $value;
28019    }
28020
28021    function resetFilelist()
28022    {
28023        $this->_packageInfo['filelist'] = array();
28024    }
28025
28026    function setInstalledAs($file, $path)
28027    {
28028        if ($path) {
28029            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
28030        }
28031        unset($this->_packageInfo['filelist'][$file]['installed_as']);
28032    }
28033
28034    function installedFile($file, $atts)
28035    {
28036        if (isset($this->_packageInfo['filelist'][$file])) {
28037            $this->_packageInfo['filelist'][$file] =
28038                array_merge($this->_packageInfo['filelist'][$file], $atts);
28039        } else {
28040            $this->_packageInfo['filelist'][$file] = $atts;
28041        }
28042    }
28043
28044    function getChangelog()
28045    {
28046        if (isset($this->_packageInfo['changelog'])) {
28047            return $this->_packageInfo['changelog'];
28048        }
28049        return false;
28050    }
28051
28052    function getPackagexmlVersion()
28053    {
28054        return '1.0';
28055    }
28056
28057    /**
28058     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
28059     * @param boolean determines whether to purge the error stack after retrieving
28060     * @return array
28061     */
28062    function getValidationWarnings($purge = true)
28063    {
28064        return $this->_stack->getErrors($purge);
28065    }
28066
28067    // }}}
28068    /**
28069     * Validation error.  Also marks the object contents as invalid
28070     * @param error code
28071     * @param array error information
28072     * @access private
28073     */
28074    function _validateError($code, $params = array())
28075    {
28076        $this->_stack->push($code, 'error', $params, false, false, debug_backtrace());
28077        $this->_isValid = false;
28078    }
28079
28080    /**
28081     * Validation warning.  Does not mark the object contents invalid.
28082     * @param error code
28083     * @param array error information
28084     * @access private
28085     */
28086    function _validateWarning($code, $params = array())
28087    {
28088        $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace());
28089    }
28090
28091    /**
28092     * @param integer error code
28093     * @access protected
28094     */
28095    function _getErrorMessage()
28096    {
28097        return array(
28098                PEAR_PACKAGEFILE_ERROR_NO_NAME =>
28099                    'Missing Package Name',
28100                PEAR_PACKAGEFILE_ERROR_NO_SUMMARY =>
28101                    'No summary found',
28102                PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY =>
28103                    'Summary should be on one line',
28104                PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION =>
28105                    'Missing description',
28106                PEAR_PACKAGEFILE_ERROR_NO_LICENSE =>
28107                    'Missing license',
28108                PEAR_PACKAGEFILE_ERROR_NO_VERSION =>
28109                    'No release version found',
28110                PEAR_PACKAGEFILE_ERROR_NO_STATE =>
28111                    'No release state found',
28112                PEAR_PACKAGEFILE_ERROR_NO_DATE =>
28113                    'No release date found',
28114                PEAR_PACKAGEFILE_ERROR_NO_NOTES =>
28115                    'No release notes found',
28116                PEAR_PACKAGEFILE_ERROR_NO_LEAD =>
28117                    'Package must have at least one lead maintainer',
28118                PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS =>
28119                    'No maintainers found, at least one must be defined',
28120                PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE =>
28121                    'Maintainer %index% has no handle (user ID at channel server)',
28122                PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE =>
28123                    'Maintainer %index% has no role',
28124                PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME =>
28125                    'Maintainer %index% has no name',
28126                PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL =>
28127                    'Maintainer %index% has no email',
28128                PEAR_PACKAGEFILE_ERROR_NO_DEPNAME =>
28129                    'Dependency %index% is not a php dependency, and has no name',
28130                PEAR_PACKAGEFILE_ERROR_NO_DEPREL =>
28131                    'Dependency %index% has no relation (rel)',
28132                PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE =>
28133                    'Dependency %index% has no type',
28134                PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED =>
28135                    'PHP Dependency %index% has a name attribute of "%name%" which will be' .
28136                        ' ignored!',
28137                PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION =>
28138                    'Dependency %index% is not a rel="has" or rel="not" dependency, ' .
28139                        'and has no version',
28140                PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION =>
28141                    'Dependency %index% is a type="php" dependency, ' .
28142                        'and has no version',
28143                PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED =>
28144                    'Dependency %index% is a rel="%rel%" dependency, versioning is ignored',
28145                PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL =>
28146                    'Dependency %index% has invalid optional value "%opt%", should be yes or no',
28147                PEAR_PACKAGEFILE_PHP_NO_NOT =>
28148                    'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' .
28149                        ' to exclude specific versions',
28150                PEAR_PACKAGEFILE_ERROR_NO_CONFNAME =>
28151                    'Configure Option %index% has no name',
28152                PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT =>
28153                    'Configure Option %index% has no prompt',
28154                PEAR_PACKAGEFILE_ERROR_NO_FILES =>
28155                    'No files in <filelist> section of package.xml',
28156                PEAR_PACKAGEFILE_ERROR_NO_FILEROLE =>
28157                    'File "%file%" has no role, expecting one of "%roles%"',
28158                PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE =>
28159                    'File "%file%" has invalid role "%role%", expecting one of "%roles%"',
28160                PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME =>
28161                    'File "%file%" cannot start with ".", cannot package or install',
28162                PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE =>
28163                    'Parser error: invalid PHP found in file "%file%"',
28164                PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX =>
28165                    'in %file%: %type% "%name%" not prefixed with package name "%package%"',
28166                PEAR_PACKAGEFILE_ERROR_INVALID_FILE =>
28167                    'Parser error: invalid PHP file "%file%"',
28168                PEAR_PACKAGEFILE_ERROR_CHANNELVAL =>
28169                    'Channel validator error: field "%field%" - %reason%',
28170                PEAR_PACKAGEFILE_ERROR_PHP5 =>
28171                    'Error, PHP5 token encountered in %file%, analysis should be in PHP5',
28172                PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND =>
28173                    'File "%file%" in package.xml does not exist',
28174                PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS =>
28175                    'Package.xml contains non-ISO-8859-1 characters, and may not validate',
28176            );
28177    }
28178
28179    /**
28180     * Validate XML package definition file.
28181     *
28182     * @access public
28183     * @return boolean
28184     */
28185    function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false)
28186    {
28187        if (($this->_isValid & $state) == $state) {
28188            return true;
28189        }
28190        $this->_isValid = true;
28191        $info = $this->_packageInfo;
28192        if (empty($info['package'])) {
28193            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME);
28194            $this->_packageName = $pn = 'unknown';
28195        } else {
28196            $this->_packageName = $pn = $info['package'];
28197        }
28198
28199        if (empty($info['summary'])) {
28200            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY);
28201        } elseif (strpos(trim($info['summary']), "\n") !== false) {
28202            $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY,
28203                array('summary' => $info['summary']));
28204        }
28205        if (empty($info['description'])) {
28206            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION);
28207        }
28208        if (empty($info['release_license'])) {
28209            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE);
28210        }
28211        if (empty($info['version'])) {
28212            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION);
28213        }
28214        if (empty($info['release_state'])) {
28215            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE);
28216        }
28217        if (empty($info['release_date'])) {
28218            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE);
28219        }
28220        if (empty($info['release_notes'])) {
28221            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES);
28222        }
28223        if (empty($info['maintainers'])) {
28224            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS);
28225        } else {
28226            $haslead = false;
28227            $i = 1;
28228            foreach ($info['maintainers'] as $m) {
28229                if (empty($m['handle'])) {
28230                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE,
28231                        array('index' => $i));
28232                }
28233                if (empty($m['role'])) {
28234                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE,
28235                        array('index' => $i, 'roles' => PEAR_Common::getUserRoles()));
28236                } elseif ($m['role'] == 'lead') {
28237                    $haslead = true;
28238                }
28239                if (empty($m['name'])) {
28240                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME,
28241                        array('index' => $i));
28242                }
28243                if (empty($m['email'])) {
28244                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL,
28245                        array('index' => $i));
28246                }
28247                $i++;
28248            }
28249            if (!$haslead) {
28250                $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD);
28251            }
28252        }
28253        if (!empty($info['release_deps'])) {
28254            $i = 1;
28255            foreach ($info['release_deps'] as $d) {
28256                if (!isset($d['type']) || empty($d['type'])) {
28257                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE,
28258                        array('index' => $i, 'types' => PEAR_Common::getDependencyTypes()));
28259                    continue;
28260                }
28261                if (!isset($d['rel']) || empty($d['rel'])) {
28262                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL,
28263                        array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations()));
28264                    continue;
28265                }
28266                if (!empty($d['optional'])) {
28267                    if (!in_array($d['optional'], array('yes', 'no'))) {
28268                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL,
28269                            array('index' => $i, 'opt' => $d['optional']));
28270                    }
28271                }
28272                if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) {
28273                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION,
28274                        array('index' => $i));
28275                } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) {
28276                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED,
28277                        array('index' => $i, 'rel' => $d['rel']));
28278                }
28279                if ($d['type'] == 'php' && !empty($d['name'])) {
28280                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED,
28281                        array('index' => $i, 'name' => $d['name']));
28282                } elseif ($d['type'] != 'php' && empty($d['name'])) {
28283                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME,
28284                        array('index' => $i));
28285                }
28286                if ($d['type'] == 'php' && empty($d['version'])) {
28287                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION,
28288                        array('index' => $i));
28289                }
28290                if (($d['rel'] == 'not') && ($d['type'] == 'php')) {
28291                    $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT,
28292                        array('index' => $i));
28293                }
28294                $i++;
28295            }
28296        }
28297        if (!empty($info['configure_options'])) {
28298            $i = 1;
28299            foreach ($info['configure_options'] as $c) {
28300                if (empty($c['name'])) {
28301                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME,
28302                        array('index' => $i));
28303                }
28304                if (empty($c['prompt'])) {
28305                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT,
28306                        array('index' => $i));
28307                }
28308                $i++;
28309            }
28310        }
28311        if (empty($info['filelist'])) {
28312            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES);
28313            $errors[] = 'no files';
28314        } else {
28315            foreach ($info['filelist'] as $file => $fa) {
28316                if (empty($fa['role'])) {
28317                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE,
28318                        array('file' => $file, 'roles' => PEAR_Common::getFileRoles()));
28319                    continue;
28320                } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
28321                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE,
28322                        array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles()));
28323                }
28324                if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) {
28325                    // file contains .. parent directory or . cur directory references
28326                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
28327                        array('file' => $file));
28328                }
28329                if (isset($fa['install-as']) &&
28330                      preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
28331                                 str_replace('\\', '/', $fa['install-as']))) {
28332                    // install-as contains .. parent directory or . cur directory references
28333                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
28334                        array('file' => $file . ' [installed as ' . $fa['install-as'] . ']'));
28335                }
28336                if (isset($fa['baseinstalldir']) &&
28337                      preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
28338                                 str_replace('\\', '/', $fa['baseinstalldir']))) {
28339                    // install-as contains .. parent directory or . cur directory references
28340                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
28341                        array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']'));
28342                }
28343            }
28344        }
28345        if (isset($this->_registry) && $this->_isValid) {
28346            $chan = $this->_registry->getChannel('pear.php.net');
28347            if (PEAR::isError($chan)) {
28348                $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage());
28349                return $this->_isValid = 0;
28350            }
28351            $validator = $chan->getValidationObject();
28352            $validator->setPackageFile($this);
28353            $validator->validate($state);
28354            $failures = $validator->getFailures();
28355            foreach ($failures['errors'] as $error) {
28356                $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error);
28357            }
28358            foreach ($failures['warnings'] as $warning) {
28359                $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning);
28360            }
28361        }
28362        if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) {
28363            if ($this->_analyzePhpFiles()) {
28364                $this->_isValid = true;
28365            }
28366        }
28367        if ($this->_isValid) {
28368            return $this->_isValid = $state;
28369        }
28370        return $this->_isValid = 0;
28371    }
28372
28373    function _analyzePhpFiles()
28374    {
28375        if (!$this->_isValid) {
28376            return false;
28377        }
28378        if (!isset($this->_packageFile)) {
28379            return false;
28380        }
28381        $dir_prefix = dirname($this->_packageFile);
28382        $common = new PEAR_Common;
28383        $log = isset($this->_logger) ? array(&$this->_logger, 'log') :
28384            array($common, 'log');
28385        $info = $this->getFilelist();
28386        foreach ($info as $file => $fa) {
28387            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
28388                $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND,
28389                    array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file));
28390                continue;
28391            }
28392            if ($fa['role'] == 'php' && $dir_prefix) {
28393                call_user_func_array($log, array(1, "Analyzing $file"));
28394                $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
28395                if ($srcinfo) {
28396                    $this->_buildProvidesArray($srcinfo);
28397                }
28398            }
28399        }
28400        $this->_packageName = $pn = $this->getPackage();
28401        $pnl = strlen($pn);
28402        if (isset($this->_packageInfo['provides'])) {
28403            foreach ((array) $this->_packageInfo['provides'] as $key => $what) {
28404                if (isset($what['explicit'])) {
28405                    // skip conformance checks if the provides entry is
28406                    // specified in the package.xml file
28407                    continue;
28408                }
28409                extract($what);
28410                if ($type == 'class') {
28411                    if (!strncasecmp($name, $pn, $pnl)) {
28412                        continue;
28413                    }
28414                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
28415                        array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
28416                } elseif ($type == 'function') {
28417                    if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
28418                        continue;
28419                    }
28420                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
28421                        array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
28422                }
28423            }
28424        }
28425        return $this->_isValid;
28426    }
28427
28428    /**
28429     * Get the default xml generator object
28430     *
28431     * @return PEAR_PackageFile_Generator_v1
28432     */
28433    function &getDefaultGenerator()
28434    {
28435        if (!class_exists('PEAR_PackageFile_Generator_v1')) {
28436            require_once 'PEAR/PackageFile/Generator/v1.php';
28437        }
28438        $a = &new PEAR_PackageFile_Generator_v1($this);
28439        return $a;
28440    }
28441
28442    /**
28443     * Get the contents of a file listed within the package.xml
28444     * @param string
28445     * @return string
28446     */
28447    function getFileContents($file)
28448    {
28449        if ($this->_archiveFile == $this->_packageFile) { // unpacked
28450            $dir = dirname($this->_packageFile);
28451            $file = $dir . DIRECTORY_SEPARATOR . $file;
28452            $file = str_replace(array('/', '\\'),
28453                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
28454            if (file_exists($file) && is_readable($file)) {
28455                return implode('', file($file));
28456            }
28457        } else { // tgz
28458            if (!class_exists('Archive_Tar')) {
28459                require_once 'Archive/Tar.php';
28460            }
28461            $tar = &new Archive_Tar($this->_archiveFile);
28462            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
28463            if ($file != 'package.xml' && $file != 'package2.xml') {
28464                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
28465            }
28466            $file = $tar->extractInString($file);
28467            $tar->popErrorHandling();
28468            if (PEAR::isError($file)) {
28469                return PEAR::raiseError("Cannot locate file '$file' in archive");
28470            }
28471            return $file;
28472        }
28473    }
28474
28475    // {{{ analyzeSourceCode()
28476    /**
28477     * Analyze the source code of the given PHP file
28478     *
28479     * @param  string Filename of the PHP file
28480     * @return mixed
28481     * @access private
28482     */
28483    function _analyzeSourceCode($file)
28484    {
28485        if (!function_exists("token_get_all")) {
28486            return false;
28487        }
28488        if (!defined('T_DOC_COMMENT')) {
28489            define('T_DOC_COMMENT', T_COMMENT);
28490        }
28491        if (!defined('T_INTERFACE')) {
28492            define('T_INTERFACE', -1);
28493        }
28494        if (!defined('T_IMPLEMENTS')) {
28495            define('T_IMPLEMENTS', -1);
28496        }
28497        if (!$fp = @fopen($file, "r")) {
28498            return false;
28499        }
28500        fclose($fp);
28501        $contents = file_get_contents($file);
28502        $tokens = token_get_all($contents);
28503/*
28504        for ($i = 0; $i < sizeof($tokens); $i++) {
28505            @list($token, $data) = $tokens[$i];
28506            if (is_string($token)) {
28507                var_dump($token);
28508            } else {
28509                print token_name($token) . ' ';
28510                var_dump(rtrim($data));
28511            }
28512        }
28513*/
28514        $look_for = 0;
28515        $paren_level = 0;
28516        $bracket_level = 0;
28517        $brace_level = 0;
28518        $lastphpdoc = '';
28519        $current_class = '';
28520        $current_interface = '';
28521        $current_class_level = -1;
28522        $current_function = '';
28523        $current_function_level = -1;
28524        $declared_classes = array();
28525        $declared_interfaces = array();
28526        $declared_functions = array();
28527        $declared_methods = array();
28528        $used_classes = array();
28529        $used_functions = array();
28530        $extends = array();
28531        $implements = array();
28532        $nodeps = array();
28533        $inquote = false;
28534        $interface = false;
28535        for ($i = 0; $i < sizeof($tokens); $i++) {
28536            if (is_array($tokens[$i])) {
28537                list($token, $data) = $tokens[$i];
28538            } else {
28539                $token = $tokens[$i];
28540                $data = '';
28541            }
28542            if ($inquote) {
28543                if ($token != '"' && $token != T_END_HEREDOC) {
28544                    continue;
28545                } else {
28546                    $inquote = false;
28547                    continue;
28548                }
28549            }
28550            switch ($token) {
28551                case T_WHITESPACE :
28552                    continue;
28553                case ';':
28554                    if ($interface) {
28555                        $current_function = '';
28556                        $current_function_level = -1;
28557                    }
28558                    break;
28559                case '"':
28560                case T_START_HEREDOC:
28561                    $inquote = true;
28562                    break;
28563                case T_CURLY_OPEN:
28564                case T_DOLLAR_OPEN_CURLY_BRACES:
28565                case '{': $brace_level++; continue 2;
28566                case '}':
28567                    $brace_level--;
28568                    if ($current_class_level == $brace_level) {
28569                        $current_class = '';
28570                        $current_class_level = -1;
28571                    }
28572                    if ($current_function_level == $brace_level) {
28573                        $current_function = '';
28574                        $current_function_level = -1;
28575                    }
28576                    continue 2;
28577                case '[': $bracket_level++; continue 2;
28578                case ']': $bracket_level--; continue 2;
28579                case '(': $paren_level++;   continue 2;
28580                case ')': $paren_level--;   continue 2;
28581                case T_INTERFACE:
28582                    $interface = true;
28583                case T_CLASS:
28584                    if (($current_class_level != -1) || ($current_function_level != -1)) {
28585                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
28586                            array('file' => $file));
28587                        return false;
28588                    }
28589                case T_FUNCTION:
28590                case T_NEW:
28591                case T_EXTENDS:
28592                case T_IMPLEMENTS:
28593                    $look_for = $token;
28594                    continue 2;
28595                case T_STRING:
28596                    if (version_compare(zend_version(), '2.0', '<')) {
28597                        if (in_array(strtolower($data),
28598                            array('public', 'private', 'protected', 'abstract',
28599                                  'interface', 'implements', 'throw') 
28600                                 )) {
28601                            $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_PHP5,
28602                                array($file));
28603                        }
28604                    }
28605                    if ($look_for == T_CLASS) {
28606                        $current_class = $data;
28607                        $current_class_level = $brace_level;
28608                        $declared_classes[] = $current_class;
28609                    } elseif ($look_for == T_INTERFACE) {
28610                        $current_interface = $data;
28611                        $current_class_level = $brace_level;
28612                        $declared_interfaces[] = $current_interface;
28613                    } elseif ($look_for == T_IMPLEMENTS) {
28614                        $implements[$current_class] = $data;
28615                    } elseif ($look_for == T_EXTENDS) {
28616                        $extends[$current_class] = $data;
28617                    } elseif ($look_for == T_FUNCTION) {
28618                        if ($current_class) {
28619                            $current_function = "$current_class::$data";
28620                            $declared_methods[$current_class][] = $data;
28621                        } elseif ($current_interface) {
28622                            $current_function = "$current_interface::$data";
28623                            $declared_methods[$current_interface][] = $data;
28624                        } else {
28625                            $current_function = $data;
28626                            $declared_functions[] = $current_function;
28627                        }
28628                        $current_function_level = $brace_level;
28629                        $m = array();
28630                    } elseif ($look_for == T_NEW) {
28631                        $used_classes[$data] = true;
28632                    }
28633                    $look_for = 0;
28634                    continue 2;
28635                case T_VARIABLE:
28636                    $look_for = 0;
28637                    continue 2;
28638                case T_DOC_COMMENT:
28639                case T_COMMENT:
28640                    if (preg_match('!^/\*\*\s!', $data)) {
28641                        $lastphpdoc = $data;
28642                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
28643                            $nodeps = array_merge($nodeps, $m[1]);
28644                        }
28645                    }
28646                    continue 2;
28647                case T_DOUBLE_COLON:
28648                    if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
28649                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
28650                            array('file' => $file));
28651                        return false;
28652                    }
28653                    $class = $tokens[$i - 1][1];
28654                    if (strtolower($class) != 'parent') {
28655                        $used_classes[$class] = true;
28656                    }
28657                    continue 2;
28658            }
28659        }
28660        return array(
28661            "source_file" => $file,
28662            "declared_classes" => $declared_classes,
28663            "declared_interfaces" => $declared_interfaces,
28664            "declared_methods" => $declared_methods,
28665            "declared_functions" => $declared_functions,
28666            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
28667            "inheritance" => $extends,
28668            "implements" => $implements,
28669            );
28670    }
28671
28672    /**
28673     * Build a "provides" array from data returned by
28674     * analyzeSourceCode().  The format of the built array is like
28675     * this:
28676     *
28677     *  array(
28678     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
28679     *    ...
28680     *  )
28681     *
28682     *
28683     * @param array $srcinfo array with information about a source file
28684     * as returned by the analyzeSourceCode() method.
28685     *
28686     * @return void
28687     *
28688     * @access private
28689     *
28690     */
28691    function _buildProvidesArray($srcinfo)
28692    {
28693        if (!$this->_isValid) {
28694            return false;
28695        }
28696        $file = basename($srcinfo['source_file']);
28697        $pn = $this->getPackage();
28698        $pnl = strlen($pn);
28699        foreach ($srcinfo['declared_classes'] as $class) {
28700            $key = "class;$class";
28701            if (isset($this->_packageInfo['provides'][$key])) {
28702                continue;
28703            }
28704            $this->_packageInfo['provides'][$key] =
28705                array('file'=> $file, 'type' => 'class', 'name' => $class);
28706            if (isset($srcinfo['inheritance'][$class])) {
28707                $this->_packageInfo['provides'][$key]['extends'] =
28708                    $srcinfo['inheritance'][$class];
28709            }
28710        }
28711        foreach ($srcinfo['declared_methods'] as $class => $methods) {
28712            foreach ($methods as $method) {
28713                $function = "$class::$method";
28714                $key = "function;$function";
28715                if ($method{0} == '_' || !strcasecmp($method, $class) ||
28716                    isset($this->_packageInfo['provides'][$key])) {
28717                    continue;
28718                }
28719                $this->_packageInfo['provides'][$key] =
28720                    array('file'=> $file, 'type' => 'function', 'name' => $function);
28721            }
28722        }
28723
28724        foreach ($srcinfo['declared_functions'] as $function) {
28725            $key = "function;$function";
28726            if ($function{0} == '_' || isset($this->_packageInfo['provides'][$key])) {
28727                continue;
28728            }
28729            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
28730                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
28731            }
28732            $this->_packageInfo['provides'][$key] =
28733                array('file'=> $file, 'type' => 'function', 'name' => $function);
28734        }
28735    }
28736
28737    // }}}
28738}
28739?>
28740PEAR-1.9.4/PEAR/PackageFile/v2.php0000644000076500000240000021011311605156614015154 0ustar  helgistaff<?php
28741/**
28742 * PEAR_PackageFile_v2, package.xml version 2.0
28743 *
28744 * PHP versions 4 and 5
28745 *
28746 * @category   pear
28747 * @package    PEAR
28748 * @author     Greg Beaver <cellog@php.net>
28749 * @copyright  1997-2009 The Authors
28750 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
28751 * @version    CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
28752 * @link       http://pear.php.net/package/PEAR
28753 * @since      File available since Release 1.4.0a1
28754 */
28755/**
28756 * For error handling
28757 */
28758require_once 'PEAR/ErrorStack.php';
28759/**
28760 * @category   pear
28761 * @package    PEAR
28762 * @author     Greg Beaver <cellog@php.net>
28763 * @copyright  1997-2009 The Authors
28764 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
28765 * @version    Release: 1.9.4
28766 * @link       http://pear.php.net/package/PEAR
28767 * @since      Class available since Release 1.4.0a1
28768 */
28769class PEAR_PackageFile_v2
28770{
28771
28772    /**
28773     * Parsed package information
28774     * @var array
28775     * @access private
28776     */
28777    var $_packageInfo = array();
28778
28779    /**
28780     * path to package .tgz or false if this is a local/extracted package.xml
28781     * @var string|false
28782     * @access private
28783     */
28784    var $_archiveFile;
28785
28786    /**
28787     * path to package .xml or false if this is an abstract parsed-from-string xml
28788     * @var string|false
28789     * @access private
28790     */
28791    var $_packageFile;
28792
28793    /**
28794     * This is used by file analysis routines to log progress information
28795     * @var PEAR_Common
28796     * @access protected
28797     */
28798    var $_logger;
28799
28800    /**
28801     * This is set to the highest validation level that has been validated
28802     *
28803     * If the package.xml is invalid or unknown, this is set to 0.  If
28804     * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL.  If
28805     * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING
28806     * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING.  This allows validation
28807     * "caching" to occur, which is particularly important for package validation, so
28808     * that PHP files are not validated twice
28809     * @var int
28810     * @access private
28811     */
28812    var $_isValid = 0;
28813
28814    /**
28815     * True if the filelist has been validated
28816     * @param bool
28817     */
28818    var $_filesValid = false;
28819
28820    /**
28821     * @var PEAR_Registry
28822     * @access protected
28823     */
28824    var $_registry;
28825
28826    /**
28827     * @var PEAR_Config
28828     * @access protected
28829     */
28830    var $_config;
28831
28832    /**
28833     * Optional Dependency group requested for installation
28834     * @var string
28835     * @access private
28836     */
28837    var $_requestedGroup = false;
28838
28839    /**
28840     * @var PEAR_ErrorStack
28841     * @access protected
28842     */
28843    var $_stack;
28844
28845    /**
28846     * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible
28847     */
28848    var $_tasksNs;
28849
28850    /**
28851     * Determines whether this packagefile was initialized only with partial package info
28852     *
28853     * If this package file was constructed via parsing REST, it will only contain
28854     *
28855     * - package name
28856     * - channel name
28857     * - dependencies
28858     * @var boolean
28859     * @access private
28860     */
28861    var $_incomplete = true;
28862
28863    /**
28864     * @var PEAR_PackageFile_v2_Validator
28865     */
28866    var $_v2Validator;
28867
28868    /**
28869     * The constructor merely sets up the private error stack
28870     */
28871    function PEAR_PackageFile_v2()
28872    {
28873        $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null);
28874        $this->_isValid = false;
28875    }
28876
28877    /**
28878     * To make unit-testing easier
28879     * @param PEAR_Frontend_*
28880     * @param array options
28881     * @param PEAR_Config
28882     * @return PEAR_Downloader
28883     * @access protected
28884     */
28885    function &getPEARDownloader(&$i, $o, &$c)
28886    {
28887        $z = &new PEAR_Downloader($i, $o, $c);
28888        return $z;
28889    }
28890
28891    /**
28892     * To make unit-testing easier
28893     * @param PEAR_Config
28894     * @param array options
28895     * @param array package name as returned from {@link PEAR_Registry::parsePackageName()}
28896     * @param int PEAR_VALIDATE_* constant
28897     * @return PEAR_Dependency2
28898     * @access protected
28899     */
28900    function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING)
28901    {
28902        if (!class_exists('PEAR_Dependency2')) {
28903            require_once 'PEAR/Dependency2.php';
28904        }
28905        $z = &new PEAR_Dependency2($c, $o, $p, $s);
28906        return $z;
28907    }
28908
28909    function getInstalledBinary()
28910    {
28911        return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] :
28912            false;
28913    }
28914
28915    /**
28916     * Installation of source package has failed, attempt to download and install the
28917     * binary version of this package.
28918     * @param PEAR_Installer
28919     * @return array|false
28920     */
28921    function installBinary(&$installer)
28922    {
28923        if (!OS_WINDOWS) {
28924            $a = false;
28925            return $a;
28926        }
28927        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
28928            $releasetype = $this->getPackageType() . 'release';
28929            if (!is_array($installer->getInstallPackages())) {
28930                $a = false;
28931                return $a;
28932            }
28933            foreach ($installer->getInstallPackages() as $p) {
28934                if ($p->isExtension($this->_packageInfo['providesextension'])) {
28935                    if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') {
28936                        $a = false;
28937                        return $a; // the user probably downloaded it separately
28938                    }
28939                }
28940            }
28941            if (isset($this->_packageInfo[$releasetype]['binarypackage'])) {
28942                $installer->log(0, 'Attempting to download binary version of extension "' .
28943                    $this->_packageInfo['providesextension'] . '"');
28944                $params = $this->_packageInfo[$releasetype]['binarypackage'];
28945                if (!is_array($params) || !isset($params[0])) {
28946                    $params = array($params);
28947                }
28948                if (isset($this->_packageInfo['channel'])) {
28949                    foreach ($params as $i => $param) {
28950                        $params[$i] = array('channel' => $this->_packageInfo['channel'],
28951                            'package' => $param, 'version' => $this->getVersion());
28952                    }
28953                }
28954                $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(),
28955                    $installer->config);
28956                $verbose = $dl->config->get('verbose');
28957                $dl->config->set('verbose', -1);
28958                foreach ($params as $param) {
28959                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
28960                    $ret = $dl->download(array($param));
28961                    PEAR::popErrorHandling();
28962                    if (is_array($ret) && count($ret)) {
28963                        break;
28964                    }
28965                }
28966                $dl->config->set('verbose', $verbose);
28967                if (is_array($ret)) {
28968                    if (count($ret) == 1) {
28969                        $pf = $ret[0]->getPackageFile();
28970                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
28971                        $err = $installer->install($ret[0]);
28972                        PEAR::popErrorHandling();
28973                        if (is_array($err)) {
28974                            $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage();
28975                            // "install" self, so all dependencies will work transparently
28976                            $this->_registry->addPackage2($this);
28977                            $installer->log(0, 'Download and install of binary extension "' .
28978                                $this->_registry->parsedPackageNameToString(
28979                                    array('channel' => $pf->getChannel(),
28980                                          'package' => $pf->getPackage()), true) . '" successful');
28981                            $a = array($ret[0], $err);
28982                            return $a;
28983                        }
28984                        $installer->log(0, 'Download and install of binary extension "' .
28985                            $this->_registry->parsedPackageNameToString(
28986                                    array('channel' => $pf->getChannel(),
28987                                          'package' => $pf->getPackage()), true) . '" failed');
28988                    }
28989                }
28990            }
28991        }
28992        $a = false;
28993        return $a;
28994    }
28995
28996    /**
28997     * @return string|false Extension name
28998     */
28999    function getProvidesExtension()
29000    {
29001        if (in_array($this->getPackageType(),
29002              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
29003            if (isset($this->_packageInfo['providesextension'])) {
29004                return $this->_packageInfo['providesextension'];
29005            }
29006        }
29007        return false;
29008    }
29009
29010    /**
29011     * @param string Extension name
29012     * @return bool
29013     */
29014    function isExtension($extension)
29015    {
29016        if (in_array($this->getPackageType(),
29017              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
29018            return $this->_packageInfo['providesextension'] == $extension;
29019        }
29020        return false;
29021    }
29022
29023    /**
29024     * Tests whether every part of the package.xml 1.0 is represented in
29025     * this package.xml 2.0
29026     * @param PEAR_PackageFile_v1
29027     * @return bool
29028     */
29029    function isEquivalent($pf1)
29030    {
29031        if (!$pf1) {
29032            return true;
29033        }
29034        if ($this->getPackageType() == 'bundle') {
29035            return false;
29036        }
29037        $this->_stack->getErrors(true);
29038        if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) {
29039            return false;
29040        }
29041        $pass = true;
29042        if ($pf1->getPackage() != $this->getPackage()) {
29043            $this->_differentPackage($pf1->getPackage());
29044            $pass = false;
29045        }
29046        if ($pf1->getVersion() != $this->getVersion()) {
29047            $this->_differentVersion($pf1->getVersion());
29048            $pass = false;
29049        }
29050        if (trim($pf1->getSummary()) != $this->getSummary()) {
29051            $this->_differentSummary($pf1->getSummary());
29052            $pass = false;
29053        }
29054        if (preg_replace('/\s+/', '', $pf1->getDescription()) !=
29055              preg_replace('/\s+/', '', $this->getDescription())) {
29056            $this->_differentDescription($pf1->getDescription());
29057            $pass = false;
29058        }
29059        if ($pf1->getState() != $this->getState()) {
29060            $this->_differentState($pf1->getState());
29061            $pass = false;
29062        }
29063        if (!strstr(preg_replace('/\s+/', '', $this->getNotes()),
29064              preg_replace('/\s+/', '', $pf1->getNotes()))) {
29065            $this->_differentNotes($pf1->getNotes());
29066            $pass = false;
29067        }
29068        $mymaintainers = $this->getMaintainers();
29069        $yourmaintainers = $pf1->getMaintainers();
29070        for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) {
29071            $reset = false;
29072            for ($i2 = 0; $i2 < count($mymaintainers); $i2++) {
29073                if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) {
29074                    if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) {
29075                        $this->_differentRole($mymaintainers[$i2]['handle'],
29076                            $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']);
29077                        $pass = false;
29078                    }
29079                    if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
29080                        $this->_differentEmail($mymaintainers[$i2]['handle'],
29081                            $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
29082                        $pass = false;
29083                    }
29084                    if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
29085                        $this->_differentName($mymaintainers[$i2]['handle'],
29086                            $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
29087                        $pass = false;
29088                    }
29089                    unset($mymaintainers[$i2]);
29090                    $mymaintainers = array_values($mymaintainers);
29091                    unset($yourmaintainers[$i1]);
29092                    $yourmaintainers = array_values($yourmaintainers);
29093                    $reset = true;
29094                    break;
29095                }
29096            }
29097            if ($reset) {
29098                $i1 = -1;
29099            }
29100        }
29101        $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers);
29102        $filelist = $this->getFilelist();
29103        foreach ($pf1->getFilelist() as $file => $atts) {
29104            if (!isset($filelist[$file])) {
29105                $this->_missingFile($file);
29106                $pass = false;
29107            }
29108        }
29109        return $pass;
29110    }
29111
29112    function _differentPackage($package)
29113    {
29114        $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
29115            'self' => $this->getPackage()),
29116            'package.xml 1.0 package "%package%" does not match "%self%"');
29117    }
29118
29119    function _differentVersion($version)
29120    {
29121        $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
29122            'self' => $this->getVersion()),
29123            'package.xml 1.0 version "%version%" does not match "%self%"');
29124    }
29125
29126    function _differentState($state)
29127    {
29128        $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
29129            'self' => $this->getState()),
29130            'package.xml 1.0 state "%state%" does not match "%self%"');
29131    }
29132
29133    function _differentRole($handle, $role, $selfrole)
29134    {
29135        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
29136            'role' => $role, 'self' => $selfrole),
29137            'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"');
29138    }
29139
29140    function _differentEmail($handle, $email, $selfemail)
29141    {
29142        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
29143            'email' => $email, 'self' => $selfemail),
29144            'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"');
29145    }
29146
29147    function _differentName($handle, $name, $selfname)
29148    {
29149        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
29150            'name' => $name, 'self' => $selfname),
29151            'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"');
29152    }
29153
29154    function _unmatchedMaintainers($my, $yours)
29155    {
29156        if ($my) {
29157            array_walk($my, create_function('&$i, $k', '$i = $i["handle"];'));
29158            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my),
29159                'package.xml 2.0 has unmatched extra maintainers "%handles%"');
29160        }
29161        if ($yours) {
29162            array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];'));
29163            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours),
29164                'package.xml 1.0 has unmatched extra maintainers "%handles%"');
29165        }
29166    }
29167
29168    function _differentNotes($notes)
29169    {
29170        $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...';
29171        $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() :
29172            substr($this->getNotes(), 0, 24) . '...';
29173        $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes,
29174            'self' => $truncmynotes),
29175            'package.xml 1.0 release notes "%notes%" do not match "%self%"');
29176    }
29177
29178    function _differentSummary($summary)
29179    {
29180        $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...';
29181        $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() :
29182            substr($this->getsummary(), 0, 24) . '...';
29183        $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary,
29184            'self' => $truncmysummary),
29185            'package.xml 1.0 summary "%summary%" does not match "%self%"');
29186    }
29187
29188    function _differentDescription($description)
29189    {
29190        $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...');
29191        $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() :
29192            substr($this->getdescription(), 0, 24) . '...');
29193        $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription,
29194            'self' => $truncmydescription),
29195            'package.xml 1.0 description "%description%" does not match "%self%"');
29196    }
29197
29198    function _missingFile($file)
29199    {
29200        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
29201            'package.xml 1.0 file "%file%" is not present in <contents>');
29202    }
29203
29204    /**
29205     * WARNING - do not use this function unless you know what you're doing
29206     */
29207    function setRawState($state)
29208    {
29209        if (!isset($this->_packageInfo['stability'])) {
29210            $this->_packageInfo['stability'] = array();
29211        }
29212        $this->_packageInfo['stability']['release'] = $state;
29213    }
29214
29215    /**
29216     * WARNING - do not use this function unless you know what you're doing
29217     */
29218    function setRawCompatible($compatible)
29219    {
29220        $this->_packageInfo['compatible'] = $compatible;
29221    }
29222
29223    /**
29224     * WARNING - do not use this function unless you know what you're doing
29225     */
29226    function setRawPackage($package)
29227    {
29228        $this->_packageInfo['name'] = $package;
29229    }
29230
29231    /**
29232     * WARNING - do not use this function unless you know what you're doing
29233     */
29234    function setRawChannel($channel)
29235    {
29236        $this->_packageInfo['channel'] = $channel;
29237    }
29238
29239    function setRequestedGroup($group)
29240    {
29241        $this->_requestedGroup = $group;
29242    }
29243
29244    function getRequestedGroup()
29245    {
29246        if (isset($this->_requestedGroup)) {
29247            return $this->_requestedGroup;
29248        }
29249        return false;
29250    }
29251
29252    /**
29253     * For saving in the registry.
29254     *
29255     * Set the last version that was installed
29256     * @param string
29257     */
29258    function setLastInstalledVersion($version)
29259    {
29260        $this->_packageInfo['_lastversion'] = $version;
29261    }
29262
29263    /**
29264     * @return string|false
29265     */
29266    function getLastInstalledVersion()
29267    {
29268        if (isset($this->_packageInfo['_lastversion'])) {
29269            return $this->_packageInfo['_lastversion'];
29270        }
29271        return false;
29272    }
29273
29274    /**
29275     * Determines whether this package.xml has post-install scripts or not
29276     * @return array|false
29277     */
29278    function listPostinstallScripts()
29279    {
29280        $filelist = $this->getFilelist();
29281        $contents = $this->getContents();
29282        $contents = $contents['dir']['file'];
29283        if (!is_array($contents) || !isset($contents[0])) {
29284            $contents = array($contents);
29285        }
29286        $taskfiles = array();
29287        foreach ($contents as $file) {
29288            $atts = $file['attribs'];
29289            unset($file['attribs']);
29290            if (count($file)) {
29291                $taskfiles[$atts['name']] = $file;
29292            }
29293        }
29294        $common = new PEAR_Common;
29295        $common->debug = $this->_config->get('verbose');
29296        $this->_scripts = array();
29297        $ret = array();
29298        foreach ($taskfiles as $name => $tasks) {
29299            if (!isset($filelist[$name])) {
29300                // ignored files will not be in the filelist
29301                continue;
29302            }
29303            $atts = $filelist[$name];
29304            foreach ($tasks as $tag => $raw) {
29305                $task = $this->getTask($tag);
29306                $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL);
29307                if ($task->isScript()) {
29308                    $ret[] = $filelist[$name]['installed_as'];
29309                }
29310            }
29311        }
29312        if (count($ret)) {
29313            return $ret;
29314        }
29315        return false;
29316    }
29317
29318    /**
29319     * Initialize post-install scripts for running
29320     *
29321     * This method can be used to detect post-install scripts, as the return value
29322     * indicates whether any exist
29323     * @return bool
29324     */
29325    function initPostinstallScripts()
29326    {
29327        $filelist = $this->getFilelist();
29328        $contents = $this->getContents();
29329        $contents = $contents['dir']['file'];
29330        if (!is_array($contents) || !isset($contents[0])) {
29331            $contents = array($contents);
29332        }
29333        $taskfiles = array();
29334        foreach ($contents as $file) {
29335            $atts = $file['attribs'];
29336            unset($file['attribs']);
29337            if (count($file)) {
29338                $taskfiles[$atts['name']] = $file;
29339            }
29340        }
29341        $common = new PEAR_Common;
29342        $common->debug = $this->_config->get('verbose');
29343        $this->_scripts = array();
29344        foreach ($taskfiles as $name => $tasks) {
29345            if (!isset($filelist[$name])) {
29346                // file was not installed due to installconditions
29347                continue;
29348            }
29349            $atts = $filelist[$name];
29350            foreach ($tasks as $tag => $raw) {
29351                $taskname = $this->getTask($tag);
29352                $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL);
29353                if (!$task->isScript()) {
29354                    continue; // scripts are only handled after installation
29355                }
29356                $lastversion = isset($this->_packageInfo['_lastversion']) ?
29357                    $this->_packageInfo['_lastversion'] : null;
29358                $task->init($raw, $atts, $lastversion);
29359                $res = $task->startSession($this, $atts['installed_as']);
29360                if (!$res) {
29361                    continue; // skip this file
29362                }
29363                if (PEAR::isError($res)) {
29364                    return $res;
29365                }
29366                $assign = &$task;
29367                $this->_scripts[] = &$assign;
29368            }
29369        }
29370        if (count($this->_scripts)) {
29371            return true;
29372        }
29373        return false;
29374    }
29375
29376    function runPostinstallScripts()
29377    {
29378        if ($this->initPostinstallScripts()) {
29379            $ui = &PEAR_Frontend::singleton();
29380            if ($ui) {
29381                $ui->runPostinstallScripts($this->_scripts, $this);
29382            }
29383        }
29384    }
29385
29386
29387    /**
29388     * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
29389     * <file> tags.
29390     */
29391    function flattenFilelist()
29392    {
29393        if (isset($this->_packageInfo['bundle'])) {
29394            return;
29395        }
29396        $filelist = array();
29397        if (isset($this->_packageInfo['contents']['dir']['dir'])) {
29398            $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']);
29399            if (!isset($filelist[1])) {
29400                $filelist = $filelist[0];
29401            }
29402            $this->_packageInfo['contents']['dir']['file'] = $filelist;
29403            unset($this->_packageInfo['contents']['dir']['dir']);
29404        } else {
29405            // else already flattened but check for baseinstalldir propagation
29406            if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) {
29407                if (isset($this->_packageInfo['contents']['dir']['file'][0])) {
29408                    foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) {
29409                        if (isset($file['attribs']['baseinstalldir'])) {
29410                            continue;
29411                        }
29412                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
29413                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
29414                    }
29415                } else {
29416                    if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) {
29417                       $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir']
29418                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
29419                    }
29420                }
29421            }
29422        }
29423    }
29424
29425    /**
29426     * @param array the final flattened file list
29427     * @param array the current directory being processed
29428     * @param string|false any recursively inherited baeinstalldir attribute
29429     * @param string private recursion variable
29430     * @return array
29431     * @access protected
29432     */
29433    function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
29434    {
29435        if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
29436            $baseinstall = $dir['attribs']['baseinstalldir'];
29437        }
29438        if (isset($dir['dir'])) {
29439            if (!isset($dir['dir'][0])) {
29440                $dir['dir'] = array($dir['dir']);
29441            }
29442            foreach ($dir['dir'] as $subdir) {
29443                if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
29444                    $name = '*unknown*';
29445                } else {
29446                    $name = $subdir['attribs']['name'];
29447                }
29448                $newpath = empty($path) ? $name :
29449                    $path . '/' . $name;
29450                $this->_getFlattenedFilelist($files, $subdir,
29451                    $baseinstall, $newpath);
29452            }
29453        }
29454        if (isset($dir['file'])) {
29455            if (!isset($dir['file'][0])) {
29456                $dir['file'] = array($dir['file']);
29457            }
29458            foreach ($dir['file'] as $file) {
29459                $attrs = $file['attribs'];
29460                $name = $attrs['name'];
29461                if ($baseinstall && !isset($attrs['baseinstalldir'])) {
29462                    $attrs['baseinstalldir'] = $baseinstall;
29463                }
29464                $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
29465                $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
29466                    $attrs['name']);
29467                $file['attribs'] = $attrs;
29468                $files[] = $file;
29469            }
29470        }
29471    }
29472
29473    function setConfig(&$config)
29474    {
29475        $this->_config = &$config;
29476        $this->_registry = &$config->getRegistry();
29477    }
29478
29479    function setLogger(&$logger)
29480    {
29481        if (!is_object($logger) || !method_exists($logger, 'log')) {
29482            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
29483        }
29484        $this->_logger = &$logger;
29485    }
29486
29487    /**
29488     * WARNING - do not use this function directly unless you know what you're doing
29489     */
29490    function setDeps($deps)
29491    {
29492        $this->_packageInfo['dependencies'] = $deps;
29493    }
29494
29495    /**
29496     * WARNING - do not use this function directly unless you know what you're doing
29497     */
29498    function setCompatible($compat)
29499    {
29500        $this->_packageInfo['compatible'] = $compat;
29501    }
29502
29503    function setPackagefile($file, $archive = false)
29504    {
29505        $this->_packageFile = $file;
29506        $this->_archiveFile = $archive ? $archive : $file;
29507    }
29508
29509    /**
29510     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
29511     * @param boolean determines whether to purge the error stack after retrieving
29512     * @return array
29513     */
29514    function getValidationWarnings($purge = true)
29515    {
29516        return $this->_stack->getErrors($purge);
29517    }
29518
29519    function getPackageFile()
29520    {
29521        return $this->_packageFile;
29522    }
29523
29524    function getArchiveFile()
29525    {
29526        return $this->_archiveFile;
29527    }
29528
29529
29530    /**
29531     * Directly set the array that defines this packagefile
29532     *
29533     * WARNING: no validation.  This should only be performed by internal methods
29534     * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2
29535     * @param array
29536     */
29537    function fromArray($pinfo)
29538    {
29539        unset($pinfo['old']);
29540        unset($pinfo['xsdversion']);
29541        // If the changelog isn't an array then it was passed in as an empty tag
29542        if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) {
29543          unset($pinfo['changelog']);
29544        }
29545        $this->_incomplete = false;
29546        $this->_packageInfo = $pinfo;
29547    }
29548
29549    function isIncomplete()
29550    {
29551        return $this->_incomplete;
29552    }
29553
29554    /**
29555     * @return array
29556     */
29557    function toArray($forreg = false)
29558    {
29559        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
29560            return false;
29561        }
29562        return $this->getArray($forreg);
29563    }
29564
29565    function getArray($forReg = false)
29566    {
29567        if ($forReg) {
29568            $arr = $this->_packageInfo;
29569            $arr['old'] = array();
29570            $arr['old']['version'] = $this->getVersion();
29571            $arr['old']['release_date'] = $this->getDate();
29572            $arr['old']['release_state'] = $this->getState();
29573            $arr['old']['release_license'] = $this->getLicense();
29574            $arr['old']['release_notes'] = $this->getNotes();
29575            $arr['old']['release_deps'] = $this->getDeps();
29576            $arr['old']['maintainers'] = $this->getMaintainers();
29577            $arr['xsdversion'] = '2.0';
29578            return $arr;
29579        } else {
29580            $info = $this->_packageInfo;
29581            unset($info['dirtree']);
29582            if (isset($info['_lastversion'])) {
29583                unset($info['_lastversion']);
29584            }
29585            if (isset($info['#binarypackage'])) {
29586                unset($info['#binarypackage']);
29587            }
29588            return $info;
29589        }
29590    }
29591
29592    function packageInfo($field)
29593    {
29594        $arr = $this->getArray(true);
29595        if ($field == 'state') {
29596            return $arr['stability']['release'];
29597        }
29598        if ($field == 'api-version') {
29599            return $arr['version']['api'];
29600        }
29601        if ($field == 'api-state') {
29602            return $arr['stability']['api'];
29603        }
29604        if (isset($arr['old'][$field])) {
29605            if (!is_string($arr['old'][$field])) {
29606                return null;
29607            }
29608            return $arr['old'][$field];
29609        }
29610        if (isset($arr[$field])) {
29611            if (!is_string($arr[$field])) {
29612                return null;
29613            }
29614            return $arr[$field];
29615        }
29616        return null;
29617    }
29618
29619    function getName()
29620    {
29621        return $this->getPackage();
29622    }
29623
29624    function getPackage()
29625    {
29626        if (isset($this->_packageInfo['name'])) {
29627            return $this->_packageInfo['name'];
29628        }
29629        return false;
29630    }
29631
29632    function getChannel()
29633    {
29634        if (isset($this->_packageInfo['uri'])) {
29635            return '__uri';
29636        }
29637        if (isset($this->_packageInfo['channel'])) {
29638            return strtolower($this->_packageInfo['channel']);
29639        }
29640        return false;
29641    }
29642
29643    function getUri()
29644    {
29645        if (isset($this->_packageInfo['uri'])) {
29646            return $this->_packageInfo['uri'];
29647        }
29648        return false;
29649    }
29650
29651    function getExtends()
29652    {
29653        if (isset($this->_packageInfo['extends'])) {
29654            return $this->_packageInfo['extends'];
29655        }
29656        return false;
29657    }
29658
29659    function getSummary()
29660    {
29661        if (isset($this->_packageInfo['summary'])) {
29662            return $this->_packageInfo['summary'];
29663        }
29664        return false;
29665    }
29666
29667    function getDescription()
29668    {
29669        if (isset($this->_packageInfo['description'])) {
29670            return $this->_packageInfo['description'];
29671        }
29672        return false;
29673    }
29674
29675    function getMaintainers($raw = false)
29676    {
29677        if (!isset($this->_packageInfo['lead'])) {
29678            return false;
29679        }
29680        if ($raw) {
29681            $ret = array('lead' => $this->_packageInfo['lead']);
29682            (isset($this->_packageInfo['developer'])) ?
29683                $ret['developer'] = $this->_packageInfo['developer'] :null;
29684            (isset($this->_packageInfo['contributor'])) ?
29685                $ret['contributor'] = $this->_packageInfo['contributor'] :null;
29686            (isset($this->_packageInfo['helper'])) ?
29687                $ret['helper'] = $this->_packageInfo['helper'] :null;
29688            return $ret;
29689        } else {
29690            $ret = array();
29691            $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
29692                array($this->_packageInfo['lead']);
29693            foreach ($leads as $lead) {
29694                $s = $lead;
29695                $s['handle'] = $s['user'];
29696                unset($s['user']);
29697                $s['role'] = 'lead';
29698                $ret[] = $s;
29699            }
29700            if (isset($this->_packageInfo['developer'])) {
29701                $leads = isset($this->_packageInfo['developer'][0]) ?
29702                    $this->_packageInfo['developer'] :
29703                    array($this->_packageInfo['developer']);
29704                foreach ($leads as $maintainer) {
29705                    $s = $maintainer;
29706                    $s['handle'] = $s['user'];
29707                    unset($s['user']);
29708                    $s['role'] = 'developer';
29709                    $ret[] = $s;
29710                }
29711            }
29712            if (isset($this->_packageInfo['contributor'])) {
29713                $leads = isset($this->_packageInfo['contributor'][0]) ?
29714                    $this->_packageInfo['contributor'] :
29715                    array($this->_packageInfo['contributor']);
29716                foreach ($leads as $maintainer) {
29717                    $s = $maintainer;
29718                    $s['handle'] = $s['user'];
29719                    unset($s['user']);
29720                    $s['role'] = 'contributor';
29721                    $ret[] = $s;
29722                }
29723            }
29724            if (isset($this->_packageInfo['helper'])) {
29725                $leads = isset($this->_packageInfo['helper'][0]) ?
29726                    $this->_packageInfo['helper'] :
29727                    array($this->_packageInfo['helper']);
29728                foreach ($leads as $maintainer) {
29729                    $s = $maintainer;
29730                    $s['handle'] = $s['user'];
29731                    unset($s['user']);
29732                    $s['role'] = 'helper';
29733                    $ret[] = $s;
29734                }
29735            }
29736            return $ret;
29737        }
29738        return false;
29739    }
29740
29741    function getLeads()
29742    {
29743        if (isset($this->_packageInfo['lead'])) {
29744            return $this->_packageInfo['lead'];
29745        }
29746        return false;
29747    }
29748
29749    function getDevelopers()
29750    {
29751        if (isset($this->_packageInfo['developer'])) {
29752            return $this->_packageInfo['developer'];
29753        }
29754        return false;
29755    }
29756
29757    function getContributors()
29758    {
29759        if (isset($this->_packageInfo['contributor'])) {
29760            return $this->_packageInfo['contributor'];
29761        }
29762        return false;
29763    }
29764
29765    function getHelpers()
29766    {
29767        if (isset($this->_packageInfo['helper'])) {
29768            return $this->_packageInfo['helper'];
29769        }
29770        return false;
29771    }
29772
29773    function setDate($date)
29774    {
29775        if (!isset($this->_packageInfo['date'])) {
29776            // ensure that the extends tag is set up in the right location
29777            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
29778                array('time', 'version',
29779                    'stability', 'license', 'notes', 'contents', 'compatible',
29780                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
29781                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
29782                    'zendextbinrelease', 'bundle', 'changelog'), array(), 'date');
29783        }
29784        $this->_packageInfo['date'] = $date;
29785        $this->_isValid = 0;
29786    }
29787
29788    function setTime($time)
29789    {
29790        $this->_isValid = 0;
29791        if (!isset($this->_packageInfo['time'])) {
29792            // ensure that the time tag is set up in the right location
29793            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
29794                    array('version',
29795                    'stability', 'license', 'notes', 'contents', 'compatible',
29796                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
29797                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
29798                    'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
29799        }
29800        $this->_packageInfo['time'] = $time;
29801    }
29802
29803    function getDate()
29804    {
29805        if (isset($this->_packageInfo['date'])) {
29806            return $this->_packageInfo['date'];
29807        }
29808        return false;
29809    }
29810
29811    function getTime()
29812    {
29813        if (isset($this->_packageInfo['time'])) {
29814            return $this->_packageInfo['time'];
29815        }
29816        return false;
29817    }
29818
29819    /**
29820     * @param package|api version category to return
29821     */
29822    function getVersion($key = 'release')
29823    {
29824        if (isset($this->_packageInfo['version'][$key])) {
29825            return $this->_packageInfo['version'][$key];
29826        }
29827        return false;
29828    }
29829
29830    function getStability()
29831    {
29832        if (isset($this->_packageInfo['stability'])) {
29833            return $this->_packageInfo['stability'];
29834        }
29835        return false;
29836    }
29837
29838    function getState($key = 'release')
29839    {
29840        if (isset($this->_packageInfo['stability'][$key])) {
29841            return $this->_packageInfo['stability'][$key];
29842        }
29843        return false;
29844    }
29845
29846    function getLicense($raw = false)
29847    {
29848        if (isset($this->_packageInfo['license'])) {
29849            if ($raw) {
29850                return $this->_packageInfo['license'];
29851            }
29852            if (is_array($this->_packageInfo['license'])) {
29853                return $this->_packageInfo['license']['_content'];
29854            } else {
29855                return $this->_packageInfo['license'];
29856            }
29857        }
29858        return false;
29859    }
29860
29861    function getLicenseLocation()
29862    {
29863        if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
29864            return false;
29865        }
29866        return $this->_packageInfo['license']['attribs'];
29867    }
29868
29869    function getNotes()
29870    {
29871        if (isset($this->_packageInfo['notes'])) {
29872            return $this->_packageInfo['notes'];
29873        }
29874        return false;
29875    }
29876
29877    /**
29878     * Return the <usesrole> tag contents, if any
29879     * @return array|false
29880     */
29881    function getUsesrole()
29882    {
29883        if (isset($this->_packageInfo['usesrole'])) {
29884            return $this->_packageInfo['usesrole'];
29885        }
29886        return false;
29887    }
29888
29889    /**
29890     * Return the <usestask> tag contents, if any
29891     * @return array|false
29892     */
29893    function getUsestask()
29894    {
29895        if (isset($this->_packageInfo['usestask'])) {
29896            return $this->_packageInfo['usestask'];
29897        }
29898        return false;
29899    }
29900
29901    /**
29902     * This should only be used to retrieve filenames and install attributes
29903     */
29904    function getFilelist($preserve = false)
29905    {
29906        if (isset($this->_packageInfo['filelist']) && !$preserve) {
29907            return $this->_packageInfo['filelist'];
29908        }
29909        $this->flattenFilelist();
29910        if ($contents = $this->getContents()) {
29911            $ret = array();
29912            if (!isset($contents['dir'])) {
29913                return false;
29914            }
29915            if (!isset($contents['dir']['file'][0])) {
29916                $contents['dir']['file'] = array($contents['dir']['file']);
29917            }
29918            foreach ($contents['dir']['file'] as $file) {
29919                $name = $file['attribs']['name'];
29920                if (!$preserve) {
29921                    $file = $file['attribs'];
29922                }
29923                $ret[$name] = $file;
29924            }
29925            if (!$preserve) {
29926                $this->_packageInfo['filelist'] = $ret;
29927            }
29928            return $ret;
29929        }
29930        return false;
29931    }
29932
29933    /**
29934     * Return configure options array, if any
29935     *
29936     * @return array|false
29937     */
29938    function getConfigureOptions()
29939    {
29940        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
29941            return false;
29942        }
29943
29944        $releases = $this->getReleases();
29945        if (isset($releases[0])) {
29946            $releases = $releases[0];
29947        }
29948
29949        if (isset($releases['configureoption'])) {
29950            if (!isset($releases['configureoption'][0])) {
29951                $releases['configureoption'] = array($releases['configureoption']);
29952            }
29953
29954            for ($i = 0; $i < count($releases['configureoption']); $i++) {
29955                $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
29956            }
29957
29958            return $releases['configureoption'];
29959        }
29960
29961        return false;
29962    }
29963
29964    /**
29965     * This is only used at install-time, after all serialization
29966     * is over.
29967     */
29968    function resetFilelist()
29969    {
29970        $this->_packageInfo['filelist'] = array();
29971    }
29972
29973    /**
29974     * Retrieve a list of files that should be installed on this computer
29975     * @return array
29976     */
29977    function getInstallationFilelist($forfilecheck = false)
29978    {
29979        $contents = $this->getFilelist(true);
29980        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
29981            $base = $contents['dir']['attribs']['baseinstalldir'];
29982        }
29983        if (isset($this->_packageInfo['bundle'])) {
29984            return PEAR::raiseError(
29985                'Exception: bundles should be handled in download code only');
29986        }
29987        $release = $this->getReleases();
29988        if ($release) {
29989            if (!isset($release[0])) {
29990                if (!isset($release['installconditions']) && !isset($release['filelist'])) {
29991                    if ($forfilecheck) {
29992                        return $this->getFilelist();
29993                    }
29994                    return $contents;
29995                }
29996                $release = array($release);
29997            }
29998            $depchecker = &$this->getPEARDependency2($this->_config, array(),
29999                array('channel' => $this->getChannel(), 'package' => $this->getPackage()),
30000                PEAR_VALIDATE_INSTALLING);
30001            foreach ($release as $instance) {
30002                if (isset($instance['installconditions'])) {
30003                    $installconditions = $instance['installconditions'];
30004                    if (is_array($installconditions)) {
30005                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
30006                        foreach ($installconditions as $type => $conditions) {
30007                            if (!isset($conditions[0])) {
30008                                $conditions = array($conditions);
30009                            }
30010                            foreach ($conditions as $condition) {
30011                                $ret = $depchecker->{"validate{$type}Dependency"}($condition);
30012                                if (PEAR::isError($ret)) {
30013                                    PEAR::popErrorHandling();
30014                                    continue 3; // skip this release
30015                                }
30016                            }
30017                        }
30018                        PEAR::popErrorHandling();
30019                    }
30020                }
30021                // this is the release to use
30022                if (isset($instance['filelist'])) {
30023                    // ignore files
30024                    if (isset($instance['filelist']['ignore'])) {
30025                        $ignore = isset($instance['filelist']['ignore'][0]) ?
30026                            $instance['filelist']['ignore'] :
30027                            array($instance['filelist']['ignore']);
30028                        foreach ($ignore as $ig) {
30029                            unset ($contents[$ig['attribs']['name']]);
30030                        }
30031                    }
30032                    // install files as this name
30033                    if (isset($instance['filelist']['install'])) {
30034                        $installas = isset($instance['filelist']['install'][0]) ?
30035                            $instance['filelist']['install'] :
30036                            array($instance['filelist']['install']);
30037                        foreach ($installas as $as) {
30038                            $contents[$as['attribs']['name']]['attribs']['install-as'] =
30039                                $as['attribs']['as'];
30040                        }
30041                    }
30042                }
30043                if ($forfilecheck) {
30044                    foreach ($contents as $file => $attrs) {
30045                        $contents[$file] = $attrs['attribs'];
30046                    }
30047                }
30048                return $contents;
30049            }
30050        } else { // simple release - no installconditions or install-as
30051            if ($forfilecheck) {
30052                return $this->getFilelist();
30053            }
30054            return $contents;
30055        }
30056        // no releases matched
30057        return PEAR::raiseError('No releases in package.xml matched the existing operating ' .
30058            'system, extensions installed, or architecture, cannot install');
30059    }
30060
30061    /**
30062     * This is only used at install-time, after all serialization
30063     * is over.
30064     * @param string file name
30065     * @param string installed path
30066     */
30067    function setInstalledAs($file, $path)
30068    {
30069        if ($path) {
30070            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
30071        }
30072        unset($this->_packageInfo['filelist'][$file]['installed_as']);
30073    }
30074
30075    function getInstalledLocation($file)
30076    {
30077        if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
30078            return $this->_packageInfo['filelist'][$file]['installed_as'];
30079        }
30080        return false;
30081    }
30082
30083    /**
30084     * This is only used at install-time, after all serialization
30085     * is over.
30086     */
30087    function installedFile($file, $atts)
30088    {
30089        if (isset($this->_packageInfo['filelist'][$file])) {
30090            $this->_packageInfo['filelist'][$file] =
30091                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
30092        } else {
30093            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
30094        }
30095    }
30096
30097    /**
30098     * Retrieve the contents tag
30099     */
30100    function getContents()
30101    {
30102        if (isset($this->_packageInfo['contents'])) {
30103            return $this->_packageInfo['contents'];
30104        }
30105        return false;
30106    }
30107
30108    /**
30109     * @param string full path to file
30110     * @param string attribute name
30111     * @param string attribute value
30112     * @param int risky but fast - use this to choose a file based on its position in the list
30113     *            of files.  Index is zero-based like PHP arrays.
30114     * @return bool success of operation
30115     */
30116    function setFileAttribute($filename, $attr, $value, $index = false)
30117    {
30118        $this->_isValid = 0;
30119        if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
30120            $this->_filesValid = false;
30121        }
30122        if ($index !== false &&
30123              isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
30124            $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
30125            return true;
30126        }
30127        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
30128            return false;
30129        }
30130        $files = $this->_packageInfo['contents']['dir']['file'];
30131        if (!isset($files[0])) {
30132            $files = array($files);
30133            $ind = false;
30134        } else {
30135            $ind = true;
30136        }
30137        foreach ($files as $i => $file) {
30138            if (isset($file['attribs'])) {
30139                if ($file['attribs']['name'] == $filename) {
30140                    if ($ind) {
30141                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
30142                    } else {
30143                        $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
30144                    }
30145                    return true;
30146                }
30147            }
30148        }
30149        return false;
30150    }
30151
30152    function setDirtree($path)
30153    {
30154        if (!isset($this->_packageInfo['dirtree'])) {
30155            $this->_packageInfo['dirtree'] = array();
30156        }
30157        $this->_packageInfo['dirtree'][$path] = true;
30158    }
30159
30160    function getDirtree()
30161    {
30162        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
30163            return $this->_packageInfo['dirtree'];
30164        }
30165        return false;
30166    }
30167
30168    function resetDirtree()
30169    {
30170        unset($this->_packageInfo['dirtree']);
30171    }
30172
30173    /**
30174     * Determines whether this package claims it is compatible with the version of
30175     * the package that has a recommended version dependency
30176     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package
30177     * @return boolean
30178     */
30179    function isCompatible($pf)
30180    {
30181        if (!isset($this->_packageInfo['compatible'])) {
30182            return false;
30183        }
30184        if (!isset($this->_packageInfo['channel'])) {
30185            return false;
30186        }
30187        $me = $pf->getVersion();
30188        $compatible = $this->_packageInfo['compatible'];
30189        if (!isset($compatible[0])) {
30190            $compatible = array($compatible);
30191        }
30192        $found = false;
30193        foreach ($compatible as $info) {
30194            if (strtolower($info['name']) == strtolower($pf->getPackage())) {
30195                if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
30196                    $found = true;
30197                    break;
30198                }
30199            }
30200        }
30201        if (!$found) {
30202            return false;
30203        }
30204        if (isset($info['exclude'])) {
30205            if (!isset($info['exclude'][0])) {
30206                $info['exclude'] = array($info['exclude']);
30207            }
30208            foreach ($info['exclude'] as $exclude) {
30209                if (version_compare($me, $exclude, '==')) {
30210                    return false;
30211                }
30212            }
30213        }
30214        if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
30215            return true;
30216        }
30217        return false;
30218    }
30219
30220    /**
30221     * @return array|false
30222     */
30223    function getCompatible()
30224    {
30225        if (isset($this->_packageInfo['compatible'])) {
30226            return $this->_packageInfo['compatible'];
30227        }
30228        return false;
30229    }
30230
30231    function getDependencies()
30232    {
30233        if (isset($this->_packageInfo['dependencies'])) {
30234            return $this->_packageInfo['dependencies'];
30235        }
30236        return false;
30237    }
30238
30239    function isSubpackageOf($p)
30240    {
30241        return $p->isSubpackage($this);
30242    }
30243
30244    /**
30245     * Determines whether the passed in package is a subpackage of this package.
30246     *
30247     * No version checking is done, only name verification.
30248     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
30249     * @return bool
30250     */
30251    function isSubpackage($p)
30252    {
30253        $sub = array();
30254        if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
30255            $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
30256            if (!isset($sub[0])) {
30257                $sub = array($sub);
30258            }
30259        }
30260        if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
30261            $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
30262            if (!isset($sub1[0])) {
30263                $sub1 = array($sub1);
30264            }
30265            $sub = array_merge($sub, $sub1);
30266        }
30267        if (isset($this->_packageInfo['dependencies']['group'])) {
30268            $group = $this->_packageInfo['dependencies']['group'];
30269            if (!isset($group[0])) {
30270                $group = array($group);
30271            }
30272            foreach ($group as $deps) {
30273                if (isset($deps['subpackage'])) {
30274                    $sub2 = $deps['subpackage'];
30275                    if (!isset($sub2[0])) {
30276                        $sub2 = array($sub2);
30277                    }
30278                    $sub = array_merge($sub, $sub2);
30279                }
30280            }
30281        }
30282        foreach ($sub as $dep) {
30283            if (strtolower($dep['name']) == strtolower($p->getPackage())) {
30284                if (isset($dep['channel'])) {
30285                    if (strtolower($dep['channel']) == strtolower($p->getChannel())) {
30286                        return true;
30287                    }
30288                } else {
30289                    if ($dep['uri'] == $p->getURI()) {
30290                        return true;
30291                    }
30292                }
30293            }
30294        }
30295        return false;
30296    }
30297
30298    function dependsOn($package, $channel)
30299    {
30300        if (!($deps = $this->getDependencies())) {
30301            return false;
30302        }
30303        foreach (array('package', 'subpackage') as $type) {
30304            foreach (array('required', 'optional') as $needed) {
30305                if (isset($deps[$needed][$type])) {
30306                    if (!isset($deps[$needed][$type][0])) {
30307                        $deps[$needed][$type] = array($deps[$needed][$type]);
30308                    }
30309                    foreach ($deps[$needed][$type] as $dep) {
30310                        $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
30311                        if (strtolower($dep['name']) == strtolower($package) &&
30312                              $depchannel == $channel) {
30313                            return true;
30314                        }
30315                    }
30316                }
30317            }
30318            if (isset($deps['group'])) {
30319                if (!isset($deps['group'][0])) {
30320                    $dep['group'] = array($deps['group']);
30321                }
30322                foreach ($deps['group'] as $group) {
30323                    if (isset($group[$type])) {
30324                        if (!is_array($group[$type])) {
30325                            $group[$type] = array($group[$type]);
30326                        }
30327                        foreach ($group[$type] as $dep) {
30328                            $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
30329                            if (strtolower($dep['name']) == strtolower($package) &&
30330                                  $depchannel == $channel) {
30331                                return true;
30332                            }
30333                        }
30334                    }
30335                }
30336            }
30337        }
30338        return false;
30339    }
30340
30341    /**
30342     * Get the contents of a dependency group
30343     * @param string
30344     * @return array|false
30345     */
30346    function getDependencyGroup($name)
30347    {
30348        $name = strtolower($name);
30349        if (!isset($this->_packageInfo['dependencies']['group'])) {
30350            return false;
30351        }
30352        $groups = $this->_packageInfo['dependencies']['group'];
30353        if (!isset($groups[0])) {
30354            $groups = array($groups);
30355        }
30356        foreach ($groups as $group) {
30357            if (strtolower($group['attribs']['name']) == $name) {
30358                return $group;
30359            }
30360        }
30361        return false;
30362    }
30363
30364    /**
30365     * Retrieve a partial package.xml 1.0 representation of dependencies
30366     *
30367     * a very limited representation of dependencies is returned by this method.
30368     * The <exclude> tag for excluding certain versions of a dependency is
30369     * completely ignored.  In addition, dependency groups are ignored, with the
30370     * assumption that all dependencies in dependency groups are also listed in
30371     * the optional group that work with all dependency groups
30372     * @param boolean return package.xml 2.0 <dependencies> tag
30373     * @return array|false
30374     */
30375    function getDeps($raw = false, $nopearinstaller = false)
30376    {
30377        if (isset($this->_packageInfo['dependencies'])) {
30378            if ($raw) {
30379                return $this->_packageInfo['dependencies'];
30380            }
30381            $ret = array();
30382            $map = array(
30383                'php' => 'php',
30384                'package' => 'pkg',
30385                'subpackage' => 'pkg',
30386                'extension' => 'ext',
30387                'os' => 'os',
30388                'pearinstaller' => 'pkg',
30389                );
30390            foreach (array('required', 'optional') as $type) {
30391                $optional = ($type == 'optional') ? 'yes' : 'no';
30392                if (!isset($this->_packageInfo['dependencies'][$type])
30393                    || empty($this->_packageInfo['dependencies'][$type])) {
30394                    continue;
30395                }
30396                foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
30397                    if ($dtype == 'pearinstaller' && $nopearinstaller) {
30398                        continue;
30399                    }
30400                    if (!isset($deps[0])) {
30401                        $deps = array($deps);
30402                    }
30403                    foreach ($deps as $dep) {
30404                        if (!isset($map[$dtype])) {
30405                            // no support for arch type
30406                            continue;
30407                        }
30408                        if ($dtype == 'pearinstaller') {
30409                            $dep['name'] = 'PEAR';
30410                            $dep['channel'] = 'pear.php.net';
30411                        }
30412                        $s = array('type' => $map[$dtype]);
30413                        if (isset($dep['channel'])) {
30414                            $s['channel'] = $dep['channel'];
30415                        }
30416                        if (isset($dep['uri'])) {
30417                            $s['uri'] = $dep['uri'];
30418                        }
30419                        if (isset($dep['name'])) {
30420                            $s['name'] = $dep['name'];
30421                        }
30422                        if (isset($dep['conflicts'])) {
30423                            $s['rel'] = 'not';
30424                        } else {
30425                            if (!isset($dep['min']) &&
30426                                  !isset($dep['max'])) {
30427                                $s['rel'] = 'has';
30428                                $s['optional'] = $optional;
30429                            } elseif (isset($dep['min']) &&
30430                                  isset($dep['max'])) {
30431                                $s['rel'] = 'ge';
30432                                $s1 = $s;
30433                                $s1['rel'] = 'le';
30434                                $s['version'] = $dep['min'];
30435                                $s1['version'] = $dep['max'];
30436                                if (isset($dep['channel'])) {
30437                                    $s1['channel'] = $dep['channel'];
30438                                }
30439                                if ($dtype != 'php') {
30440                                    $s['name'] = $dep['name'];
30441                                    $s1['name'] = $dep['name'];
30442                                }
30443                                $s['optional'] = $optional;
30444                                $s1['optional'] = $optional;
30445                                $ret[] = $s1;
30446                            } elseif (isset($dep['min'])) {
30447                                if (isset($dep['exclude']) &&
30448                                      $dep['exclude'] == $dep['min']) {
30449                                    $s['rel'] = 'gt';
30450                                } else {
30451                                    $s['rel'] = 'ge';
30452                                }
30453                                $s['version'] = $dep['min'];
30454                                $s['optional'] = $optional;
30455                                if ($dtype != 'php') {
30456                                    $s['name'] = $dep['name'];
30457                                }
30458                            } elseif (isset($dep['max'])) {
30459                                if (isset($dep['exclude']) &&
30460                                      $dep['exclude'] == $dep['max']) {
30461                                    $s['rel'] = 'lt';
30462                                } else {
30463                                    $s['rel'] = 'le';
30464                                }
30465                                $s['version'] = $dep['max'];
30466                                $s['optional'] = $optional;
30467                                if ($dtype != 'php') {
30468                                    $s['name'] = $dep['name'];
30469                                }
30470                            }
30471                        }
30472                        $ret[] = $s;
30473                    }
30474                }
30475            }
30476            if (count($ret)) {
30477                return $ret;
30478            }
30479        }
30480        return false;
30481    }
30482
30483    /**
30484     * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
30485     */
30486    function getPackageType()
30487    {
30488        if (isset($this->_packageInfo['phprelease'])) {
30489            return 'php';
30490        }
30491        if (isset($this->_packageInfo['extsrcrelease'])) {
30492            return 'extsrc';
30493        }
30494        if (isset($this->_packageInfo['extbinrelease'])) {
30495            return 'extbin';
30496        }
30497        if (isset($this->_packageInfo['zendextsrcrelease'])) {
30498            return 'zendextsrc';
30499        }
30500        if (isset($this->_packageInfo['zendextbinrelease'])) {
30501            return 'zendextbin';
30502        }
30503        if (isset($this->_packageInfo['bundle'])) {
30504            return 'bundle';
30505        }
30506        return false;
30507    }
30508
30509    /**
30510     * @return array|false
30511     */
30512    function getReleases()
30513    {
30514        $type = $this->getPackageType();
30515        if ($type != 'bundle') {
30516            $type .= 'release';
30517        }
30518        if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
30519            return $this->_packageInfo[$type];
30520        }
30521        return false;
30522    }
30523
30524    /**
30525     * @return array
30526     */
30527    function getChangelog()
30528    {
30529        if (isset($this->_packageInfo['changelog'])) {
30530            return $this->_packageInfo['changelog'];
30531        }
30532        return false;
30533    }
30534
30535    function hasDeps()
30536    {
30537        return isset($this->_packageInfo['dependencies']);
30538    }
30539
30540    function getPackagexmlVersion()
30541    {
30542        if (isset($this->_packageInfo['zendextsrcrelease'])) {
30543            return '2.1';
30544        }
30545        if (isset($this->_packageInfo['zendextbinrelease'])) {
30546            return '2.1';
30547        }
30548        return '2.0';
30549    }
30550
30551    /**
30552     * @return array|false
30553     */
30554    function getSourcePackage()
30555    {
30556        if (isset($this->_packageInfo['extbinrelease']) ||
30557              isset($this->_packageInfo['zendextbinrelease'])) {
30558            return array('channel' => $this->_packageInfo['srcchannel'],
30559                         'package' => $this->_packageInfo['srcpackage']);
30560        }
30561        return false;
30562    }
30563
30564    function getBundledPackages()
30565    {
30566        if (isset($this->_packageInfo['bundle'])) {
30567            return $this->_packageInfo['contents']['bundledpackage'];
30568        }
30569        return false;
30570    }
30571
30572    function getLastModified()
30573    {
30574        if (isset($this->_packageInfo['_lastmodified'])) {
30575            return $this->_packageInfo['_lastmodified'];
30576        }
30577        return false;
30578    }
30579
30580    /**
30581     * Get the contents of a file listed within the package.xml
30582     * @param string
30583     * @return string
30584     */
30585    function getFileContents($file)
30586    {
30587        if ($this->_archiveFile == $this->_packageFile) { // unpacked
30588            $dir = dirname($this->_packageFile);
30589            $file = $dir . DIRECTORY_SEPARATOR . $file;
30590            $file = str_replace(array('/', '\\'),
30591                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
30592            if (file_exists($file) && is_readable($file)) {
30593                return implode('', file($file));
30594            }
30595        } else { // tgz
30596            $tar = &new Archive_Tar($this->_archiveFile);
30597            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
30598            if ($file != 'package.xml' && $file != 'package2.xml') {
30599                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
30600            }
30601            $file = $tar->extractInString($file);
30602            $tar->popErrorHandling();
30603            if (PEAR::isError($file)) {
30604                return PEAR::raiseError("Cannot locate file '$file' in archive");
30605            }
30606            return $file;
30607        }
30608    }
30609
30610    function &getRW()
30611    {
30612        if (!class_exists('PEAR_PackageFile_v2_rw')) {
30613            require_once 'PEAR/PackageFile/v2/rw.php';
30614        }
30615        $a = new PEAR_PackageFile_v2_rw;
30616        foreach (get_object_vars($this) as $name => $unused) {
30617            if (!isset($this->$name)) {
30618                continue;
30619            }
30620            if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
30621                  $name == '_stack') {
30622                $a->$name = &$this->$name;
30623            } else {
30624                $a->$name = $this->$name;
30625            }
30626        }
30627        return $a;
30628    }
30629
30630    function &getDefaultGenerator()
30631    {
30632        if (!class_exists('PEAR_PackageFile_Generator_v2')) {
30633            require_once 'PEAR/PackageFile/Generator/v2.php';
30634        }
30635        $a = &new PEAR_PackageFile_Generator_v2($this);
30636        return $a;
30637    }
30638
30639    function analyzeSourceCode($file, $string = false)
30640    {
30641        if (!isset($this->_v2Validator) ||
30642              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
30643            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
30644                require_once 'PEAR/PackageFile/v2/Validator.php';
30645            }
30646            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
30647        }
30648        return $this->_v2Validator->analyzeSourceCode($file, $string);
30649    }
30650
30651    function validate($state = PEAR_VALIDATE_NORMAL)
30652    {
30653        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
30654            return false;
30655        }
30656        if (!isset($this->_v2Validator) ||
30657              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
30658            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
30659                require_once 'PEAR/PackageFile/v2/Validator.php';
30660            }
30661            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
30662        }
30663        if (isset($this->_packageInfo['xsdversion'])) {
30664            unset($this->_packageInfo['xsdversion']);
30665        }
30666        return $this->_v2Validator->validate($this, $state);
30667    }
30668
30669    function getTasksNs()
30670    {
30671        if (!isset($this->_tasksNs)) {
30672            if (isset($this->_packageInfo['attribs'])) {
30673                foreach ($this->_packageInfo['attribs'] as $name => $value) {
30674                    if ($value == 'http://pear.php.net/dtd/tasks-1.0') {
30675                        $this->_tasksNs = str_replace('xmlns:', '', $name);
30676                        break;
30677                    }
30678                }
30679            }
30680        }
30681        return $this->_tasksNs;
30682    }
30683
30684    /**
30685     * Determine whether a task name is a valid task.  Custom tasks may be defined
30686     * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task>
30687     *
30688     * Note that this method will auto-load the task class file and test for the existence
30689     * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class
30690     * PEAR_Task_mycustom_task
30691     * @param string
30692     * @return boolean
30693     */
30694    function getTask($task)
30695    {
30696        $this->getTasksNs();
30697        // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace
30698        $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task);
30699        $taskfile = str_replace(' ', '/', ucwords($task));
30700        $task = str_replace(array(' ', '/'), '_', ucwords($task));
30701        if (class_exists("PEAR_Task_$task")) {
30702            return "PEAR_Task_$task";
30703        }
30704        $fp = @fopen("PEAR/Task/$taskfile.php", 'r', true);
30705        if ($fp) {
30706            fclose($fp);
30707            require_once "PEAR/Task/$taskfile.php";
30708            return "PEAR_Task_$task";
30709        }
30710        return false;
30711    }
30712
30713    /**
30714     * Key-friendly array_splice
30715     * @param tagname to splice a value in before
30716     * @param mixed the value to splice in
30717     * @param string the new tag name
30718     */
30719    function _ksplice($array, $key, $value, $newkey)
30720    {
30721        $offset = array_search($key, array_keys($array));
30722        $after = array_slice($array, $offset);
30723        $before = array_slice($array, 0, $offset);
30724        $before[$newkey] = $value;
30725        return array_merge($before, $after);
30726    }
30727
30728    /**
30729     * @param array a list of possible keys, in the order they may occur
30730     * @param mixed contents of the new package.xml tag
30731     * @param string tag name
30732     * @access private
30733     */
30734    function _insertBefore($array, $keys, $contents, $newkey)
30735    {
30736        foreach ($keys as $key) {
30737            if (isset($array[$key])) {
30738                return $array = $this->_ksplice($array, $key, $contents, $newkey);
30739            }
30740        }
30741        $array[$newkey] = $contents;
30742        return $array;
30743    }
30744
30745    /**
30746     * @param subsection of {@link $_packageInfo}
30747     * @param array|string tag contents
30748     * @param array format:
30749     * <pre>
30750     * array(
30751     *   tagname => array(list of tag names that follow this one),
30752     *   childtagname => array(list of child tag names that follow this one),
30753     * )
30754     * </pre>
30755     *
30756     * This allows construction of nested tags
30757     * @access private
30758     */
30759    function _mergeTag($manip, $contents, $order)
30760    {
30761        if (count($order)) {
30762            foreach ($order as $tag => $curorder) {
30763                if (!isset($manip[$tag])) {
30764                    // ensure that the tag is set up
30765                    $manip = $this->_insertBefore($manip, $curorder, array(), $tag);
30766                }
30767                if (count($order) > 1) {
30768                    $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
30769                    return $manip;
30770                }
30771            }
30772        } else {
30773            return $manip;
30774        }
30775        if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
30776            $manip[$tag][] = $contents;
30777        } else {
30778            if (!count($manip[$tag])) {
30779                $manip[$tag] = $contents;
30780            } else {
30781                $manip[$tag] = array($manip[$tag]);
30782                $manip[$tag][] = $contents;
30783            }
30784        }
30785        return $manip;
30786    }
30787}
30788?>
30789PEAR-1.9.4/PEAR/REST/10.php0000644000076500000240000007772411605156614013512 0ustar  helgistaff<?php
30790/**
30791 * PEAR_REST_10
30792 *
30793 * PHP versions 4 and 5
30794 *
30795 * @category   pear
30796 * @package    PEAR
30797 * @author     Greg Beaver <cellog@php.net>
30798 * @copyright  1997-2009 The Authors
30799 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
30800 * @version    CVS: $Id: 10.php 313023 2011-07-06 19:17:11Z dufuz $
30801 * @link       http://pear.php.net/package/PEAR
30802 * @since      File available since Release 1.4.0a12
30803 */
30804
30805/**
30806 * For downloading REST xml/txt files
30807 */
30808require_once 'PEAR/REST.php';
30809
30810/**
30811 * Implement REST 1.0
30812 *
30813 * @category   pear
30814 * @package    PEAR
30815 * @author     Greg Beaver <cellog@php.net>
30816 * @copyright  1997-2009 The Authors
30817 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
30818 * @version    Release: 1.9.4
30819 * @link       http://pear.php.net/package/PEAR
30820 * @since      Class available since Release 1.4.0a12
30821 */
30822class PEAR_REST_10
30823{
30824    /**
30825     * @var PEAR_REST
30826     */
30827    var $_rest;
30828    function PEAR_REST_10($config, $options = array())
30829    {
30830        $this->_rest = &new PEAR_REST($config, $options);
30831    }
30832
30833    /**
30834     * Retrieve information about a remote package to be downloaded from a REST server
30835     *
30836     * @param string $base The uri to prepend to all REST calls
30837     * @param array $packageinfo an array of format:
30838     * <pre>
30839     *  array(
30840     *   'package' => 'packagename',
30841     *   'channel' => 'channelname',
30842     *  ['state' => 'alpha' (or valid state),]
30843     *  -or-
30844     *  ['version' => '1.whatever']
30845     * </pre>
30846     * @param string $prefstate Current preferred_state config variable value
30847     * @param bool $installed the installed version of this package to compare against
30848     * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
30849     */
30850    function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
30851    {
30852        $states = $this->betterStates($prefstate, true);
30853        if (!$states) {
30854            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
30855        }
30856
30857        $channel  = $packageinfo['channel'];
30858        $package  = $packageinfo['package'];
30859        $state    = isset($packageinfo['state'])   ? $packageinfo['state']   : null;
30860        $version  = isset($packageinfo['version']) ? $packageinfo['version'] : null;
30861        $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';
30862
30863        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
30864        if (PEAR::isError($info)) {
30865            return PEAR::raiseError('No releases available for package "' .
30866                $channel . '/' . $package . '"');
30867        }
30868
30869        if (!isset($info['r'])) {
30870            return false;
30871        }
30872
30873        $release = $found = false;
30874        if (!is_array($info['r']) || !isset($info['r'][0])) {
30875            $info['r'] = array($info['r']);
30876        }
30877
30878        foreach ($info['r'] as $release) {
30879            if (!isset($this->_rest->_options['force']) && ($installed &&
30880                  version_compare($release['v'], $installed, '<'))) {
30881                continue;
30882            }
30883
30884            if (isset($state)) {
30885                // try our preferred state first
30886                if ($release['s'] == $state) {
30887                    $found = true;
30888                    break;
30889                }
30890                // see if there is something newer and more stable
30891                // bug #7221
30892                if (in_array($release['s'], $this->betterStates($state), true)) {
30893                    $found = true;
30894                    break;
30895                }
30896            } elseif (isset($version)) {
30897                if ($release['v'] == $version) {
30898                    $found = true;
30899                    break;
30900                }
30901            } else {
30902                if (in_array($release['s'], $states)) {
30903                    $found = true;
30904                    break;
30905                }
30906            }
30907        }
30908
30909        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
30910    }
30911
30912    function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
30913                               $prefstate = 'stable', $installed = false, $channel = false)
30914    {
30915        $states = $this->betterStates($prefstate, true);
30916        if (!$states) {
30917            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
30918        }
30919
30920        $channel  = $dependency['channel'];
30921        $package  = $dependency['name'];
30922        $state    = isset($dependency['state'])   ? $dependency['state']   : null;
30923        $version  = isset($dependency['version']) ? $dependency['version'] : null;
30924        $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';
30925
30926        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
30927        if (PEAR::isError($info)) {
30928            return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
30929                . '" dependency "' . $channel . '/' . $package . '" has no releases');
30930        }
30931
30932        if (!is_array($info) || !isset($info['r'])) {
30933            return false;
30934        }
30935
30936        $exclude = array();
30937        $min = $max = $recommended = false;
30938        if ($xsdversion == '1.0') {
30939            switch ($dependency['rel']) {
30940                case 'ge' :
30941                    $min = $dependency['version'];
30942                break;
30943                case 'gt' :
30944                    $min = $dependency['version'];
30945                    $exclude = array($dependency['version']);
30946                break;
30947                case 'eq' :
30948                    $recommended = $dependency['version'];
30949                break;
30950                case 'lt' :
30951                    $max = $dependency['version'];
30952                    $exclude = array($dependency['version']);
30953                break;
30954                case 'le' :
30955                    $max = $dependency['version'];
30956                break;
30957                case 'ne' :
30958                    $exclude = array($dependency['version']);
30959                break;
30960            }
30961        } else {
30962            $min = isset($dependency['min']) ? $dependency['min'] : false;
30963            $max = isset($dependency['max']) ? $dependency['max'] : false;
30964            $recommended = isset($dependency['recommended']) ?
30965                $dependency['recommended'] : false;
30966            if (isset($dependency['exclude'])) {
30967                if (!isset($dependency['exclude'][0])) {
30968                    $exclude = array($dependency['exclude']);
30969                }
30970            }
30971        }
30972        $release = $found = false;
30973        if (!is_array($info['r']) || !isset($info['r'][0])) {
30974            $info['r'] = array($info['r']);
30975        }
30976        foreach ($info['r'] as $release) {
30977            if (!isset($this->_rest->_options['force']) && ($installed &&
30978                  version_compare($release['v'], $installed, '<'))) {
30979                continue;
30980            }
30981            if (in_array($release['v'], $exclude)) { // skip excluded versions
30982                continue;
30983            }
30984            // allow newer releases to say "I'm OK with the dependent package"
30985            if ($xsdversion == '2.0' && isset($release['co'])) {
30986                if (!is_array($release['co']) || !isset($release['co'][0])) {
30987                    $release['co'] = array($release['co']);
30988                }
30989                foreach ($release['co'] as $entry) {
30990                    if (isset($entry['x']) && !is_array($entry['x'])) {
30991                        $entry['x'] = array($entry['x']);
30992                    } elseif (!isset($entry['x'])) {
30993                        $entry['x'] = array();
30994                    }
30995                    if ($entry['c'] == $deppackage['channel'] &&
30996                          strtolower($entry['p']) == strtolower($deppackage['package']) &&
30997                          version_compare($deppackage['version'], $entry['min'], '>=') &&
30998                          version_compare($deppackage['version'], $entry['max'], '<=') &&
30999                          !in_array($release['v'], $entry['x'])) {
31000                        $recommended = $release['v'];
31001                        break;
31002                    }
31003                }
31004            }
31005            if ($recommended) {
31006                if ($release['v'] != $recommended) { // if we want a specific
31007                    // version, then skip all others
31008                    continue;
31009                } else {
31010                    if (!in_array($release['s'], $states)) {
31011                        // the stability is too low, but we must return the
31012                        // recommended version if possible
31013                        return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
31014                    }
31015                }
31016            }
31017            if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
31018                continue;
31019            }
31020            if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
31021                continue;
31022            }
31023            if ($installed && version_compare($release['v'], $installed, '<')) {
31024                continue;
31025            }
31026            if (in_array($release['s'], $states)) { // if in the preferred state...
31027                $found = true; // ... then use it
31028                break;
31029            }
31030        }
31031        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
31032    }
31033
31034    /**
31035     * Take raw data and return the array needed for processing a download URL
31036     *
31037     * @param string $base REST base uri
31038     * @param string $package Package name
31039     * @param array $release an array of format array('v' => version, 's' => state)
31040     *                       describing the release to download
31041     * @param array $info list of all releases as defined by allreleases.xml
31042     * @param bool|null $found determines whether the release was found or this is the next
31043     *                    best alternative.  If null, then versions were skipped because
31044     *                    of PHP dependency
31045     * @return array|PEAR_Error
31046     * @access private
31047     */
31048    function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false)
31049    {
31050        if (!$found) {
31051            $release = $info['r'][0];
31052        }
31053
31054        $packageLower = strtolower($package);
31055        $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . $packageLower . '/' .
31056            'info.xml', false, false, $channel);
31057        if (PEAR::isError($pinfo)) {
31058            return PEAR::raiseError('Package "' . $package .
31059                '" does not have REST info xml available');
31060        }
31061
31062        $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
31063            $release['v'] . '.xml', false, false, $channel);
31064        if (PEAR::isError($releaseinfo)) {
31065            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
31066                '" does not have REST xml available');
31067        }
31068
31069        $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
31070            'deps.' . $release['v'] . '.txt', false, true, $channel);
31071        if (PEAR::isError($packagexml)) {
31072            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
31073                '" does not have REST dependency information available');
31074        }
31075
31076        $packagexml = unserialize($packagexml);
31077        if (!$packagexml) {
31078            $packagexml = array();
31079        }
31080
31081        $allinfo = $this->_rest->retrieveData($base . 'r/' . $packageLower .
31082            '/allreleases.xml', false, false, $channel);
31083        if (PEAR::isError($allinfo)) {
31084            return $allinfo;
31085        }
31086
31087        if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) {
31088            $allinfo['r'] = array($allinfo['r']);
31089        }
31090
31091        $compatible = false;
31092        foreach ($allinfo['r'] as $release) {
31093            if ($release['v'] != $releaseinfo['v']) {
31094                continue;
31095            }
31096
31097            if (!isset($release['co'])) {
31098                break;
31099            }
31100
31101            $compatible = array();
31102            if (!is_array($release['co']) || !isset($release['co'][0])) {
31103                $release['co'] = array($release['co']);
31104            }
31105
31106            foreach ($release['co'] as $entry) {
31107                $comp = array();
31108                $comp['name']    = $entry['p'];
31109                $comp['channel'] = $entry['c'];
31110                $comp['min']     = $entry['min'];
31111                $comp['max']     = $entry['max'];
31112                if (isset($entry['x']) && !is_array($entry['x'])) {
31113                    $comp['exclude'] = $entry['x'];
31114                }
31115
31116                $compatible[] = $comp;
31117            }
31118
31119            if (count($compatible) == 1) {
31120                $compatible = $compatible[0];
31121            }
31122
31123            break;
31124        }
31125
31126        $deprecated = false;
31127        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
31128            if (is_array($pinfo['dp'])) {
31129                $deprecated = array('channel' => (string) $pinfo['dc'],
31130                                    'package' => trim($pinfo['dp']['_content']));
31131            } else {
31132                $deprecated = array('channel' => (string) $pinfo['dc'],
31133                                    'package' => trim($pinfo['dp']));
31134            }
31135        }
31136
31137        $return = array(
31138            'version'    => $releaseinfo['v'],
31139            'info'       => $packagexml,
31140            'package'    => $releaseinfo['p']['_content'],
31141            'stability'  => $releaseinfo['st'],
31142            'compatible' => $compatible,
31143            'deprecated' => $deprecated,
31144        );
31145
31146        if ($found) {
31147            $return['url'] = $releaseinfo['g'];
31148            return $return;
31149        }
31150
31151        $return['php'] = $phpversion;
31152        return $return;
31153    }
31154
31155    function listPackages($base, $channel = false)
31156    {
31157        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
31158        if (PEAR::isError($packagelist)) {
31159            return $packagelist;
31160        }
31161
31162        if (!is_array($packagelist) || !isset($packagelist['p'])) {
31163            return array();
31164        }
31165
31166        if (!is_array($packagelist['p'])) {
31167            $packagelist['p'] = array($packagelist['p']);
31168        }
31169
31170        return $packagelist['p'];
31171    }
31172
31173    /**
31174     * List all categories of a REST server
31175     *
31176     * @param string $base base URL of the server
31177     * @return array of categorynames
31178     */
31179    function listCategories($base, $channel = false)
31180    {
31181        $categories = array();
31182
31183        // c/categories.xml does not exist;
31184        // check for every package its category manually
31185        // This is SLOOOWWWW : ///
31186        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
31187        if (PEAR::isError($packagelist)) {
31188            return $packagelist;
31189        }
31190
31191        if (!is_array($packagelist) || !isset($packagelist['p'])) {
31192            $ret = array();
31193            return $ret;
31194        }
31195
31196        if (!is_array($packagelist['p'])) {
31197            $packagelist['p'] = array($packagelist['p']);
31198        }
31199
31200        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
31201        foreach ($packagelist['p'] as $package) {
31202                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
31203                if (PEAR::isError($inf)) {
31204                    PEAR::popErrorHandling();
31205                    return $inf;
31206                }
31207                $cat = $inf['ca']['_content'];
31208                if (!isset($categories[$cat])) {
31209                    $categories[$cat] = $inf['ca'];
31210                }
31211        }
31212
31213        return array_values($categories);
31214    }
31215
31216    /**
31217     * List a category of a REST server
31218     *
31219     * @param string $base base URL of the server
31220     * @param string $category name of the category
31221     * @param boolean $info also download full package info
31222     * @return array of packagenames
31223     */
31224    function listCategory($base, $category, $info = false, $channel = false)
31225    {
31226        // gives '404 Not Found' error when category doesn't exist
31227        $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel);
31228        if (PEAR::isError($packagelist)) {
31229            return $packagelist;
31230        }
31231
31232        if (!is_array($packagelist) || !isset($packagelist['p'])) {
31233            return array();
31234        }
31235
31236        if (!is_array($packagelist['p']) ||
31237            !isset($packagelist['p'][0])) { // only 1 pkg
31238            $packagelist = array($packagelist['p']);
31239        } else {
31240            $packagelist = $packagelist['p'];
31241        }
31242
31243        if ($info == true) {
31244            // get individual package info
31245            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
31246            foreach ($packagelist as $i => $packageitem) {
31247                $url = sprintf('%s'.'r/%s/latest.txt',
31248                        $base,
31249                        strtolower($packageitem['_content']));
31250                $version = $this->_rest->retrieveData($url, false, false, $channel);
31251                if (PEAR::isError($version)) {
31252                    break; // skipit
31253                }
31254                $url = sprintf('%s'.'r/%s/%s.xml',
31255                        $base,
31256                        strtolower($packageitem['_content']),
31257                        $version);
31258                $info = $this->_rest->retrieveData($url, false, false, $channel);
31259                if (PEAR::isError($info)) {
31260                    break; // skipit
31261                }
31262                $packagelist[$i]['info'] = $info;
31263            }
31264            PEAR::popErrorHandling();
31265        }
31266
31267        return $packagelist;
31268    }
31269
31270
31271    function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
31272    {
31273        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
31274        if (PEAR::isError($packagelist)) {
31275            return $packagelist;
31276        }
31277        if ($this->_rest->config->get('verbose') > 0) {
31278            $ui = &PEAR_Frontend::singleton();
31279            $ui->log('Retrieving data...0%', true);
31280        }
31281        $ret = array();
31282        if (!is_array($packagelist) || !isset($packagelist['p'])) {
31283            return $ret;
31284        }
31285        if (!is_array($packagelist['p'])) {
31286            $packagelist['p'] = array($packagelist['p']);
31287        }
31288
31289        // only search-packagename = quicksearch !
31290        if ($searchpackage && (!$searchsummary || empty($searchpackage))) {
31291            $newpackagelist = array();
31292            foreach ($packagelist['p'] as $package) {
31293                if (!empty($searchpackage) && stristr($package, $searchpackage) !== false) {
31294                    $newpackagelist[] = $package;
31295                }
31296            }
31297            $packagelist['p'] = $newpackagelist;
31298        }
31299        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
31300        $next = .1;
31301        foreach ($packagelist['p'] as $progress => $package) {
31302            if ($this->_rest->config->get('verbose') > 0) {
31303                if ($progress / count($packagelist['p']) >= $next) {
31304                    if ($next == .5) {
31305                        $ui->log('50%', false);
31306                    } else {
31307                        $ui->log('.', false);
31308                    }
31309                    $next += .1;
31310                }
31311            }
31312
31313            if ($basic) { // remote-list command
31314                if ($dostable) {
31315                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
31316                        '/stable.txt', false, false, $channel);
31317                } else {
31318                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
31319                        '/latest.txt', false, false, $channel);
31320                }
31321                if (PEAR::isError($latest)) {
31322                    $latest = false;
31323                }
31324                $info = array('stable' => $latest);
31325            } else { // list-all command
31326                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
31327                if (PEAR::isError($inf)) {
31328                    PEAR::popErrorHandling();
31329                    return $inf;
31330                }
31331                if ($searchpackage) {
31332                    $found = (!empty($searchpackage) && stristr($package, $searchpackage) !== false);
31333                    if (!$found && !(isset($searchsummary) && !empty($searchsummary)
31334                        && (stristr($inf['s'], $searchsummary) !== false
31335                            || stristr($inf['d'], $searchsummary) !== false)))
31336                    {
31337                        continue;
31338                    };
31339                }
31340                $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
31341                    '/allreleases.xml', false, false, $channel);
31342                if (PEAR::isError($releases)) {
31343                    continue;
31344                }
31345                if (!isset($releases['r'][0])) {
31346                    $releases['r'] = array($releases['r']);
31347                }
31348                unset($latest);
31349                unset($unstable);
31350                unset($stable);
31351                unset($state);
31352                foreach ($releases['r'] as $release) {
31353                    if (!isset($latest)) {
31354                        if ($dostable && $release['s'] == 'stable') {
31355                            $latest = $release['v'];
31356                            $state = 'stable';
31357                        }
31358                        if (!$dostable) {
31359                            $latest = $release['v'];
31360                            $state = $release['s'];
31361                        }
31362                    }
31363                    if (!isset($stable) && $release['s'] == 'stable') {
31364                        $stable = $release['v'];
31365                        if (!isset($unstable)) {
31366                            $unstable = $stable;
31367                        }
31368                    }
31369                    if (!isset($unstable) && $release['s'] != 'stable') {
31370                        $latest = $unstable = $release['v'];
31371                        $state = $release['s'];
31372                    }
31373                    if (isset($latest) && !isset($state)) {
31374                        $state = $release['s'];
31375                    }
31376                    if (isset($latest) && isset($stable) && isset($unstable)) {
31377                        break;
31378                    }
31379                }
31380                $deps = array();
31381                if (!isset($unstable)) {
31382                    $unstable = false;
31383                    $state = 'stable';
31384                    if (isset($stable)) {
31385                        $latest = $unstable = $stable;
31386                    }
31387                } else {
31388                    $latest = $unstable;
31389                }
31390                if (!isset($latest)) {
31391                    $latest = false;
31392                }
31393                if ($latest) {
31394                    $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
31395                        $latest . '.txt', false, false, $channel);
31396                    if (!PEAR::isError($d)) {
31397                        $d = unserialize($d);
31398                        if ($d) {
31399                            if (isset($d['required'])) {
31400                                if (!class_exists('PEAR_PackageFile_v2')) {
31401                                    require_once 'PEAR/PackageFile/v2.php';
31402                                }
31403                                if (!isset($pf)) {
31404                                    $pf = new PEAR_PackageFile_v2;
31405                                }
31406                                $pf->setDeps($d);
31407                                $tdeps = $pf->getDeps();
31408                            } else {
31409                                $tdeps = $d;
31410                            }
31411                            foreach ($tdeps as $dep) {
31412                                if ($dep['type'] !== 'pkg') {
31413                                    continue;
31414                                }
31415                                $deps[] = $dep;
31416                            }
31417                        }
31418                    }
31419                }
31420                if (!isset($stable)) {
31421                    $stable = '-n/a-';
31422                }
31423                if (!$searchpackage) {
31424                    $info = array('stable' => $latest, 'summary' => $inf['s'], 'description' =>
31425                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
31426                        'unstable' => $unstable, 'state' => $state);
31427                } else {
31428                    $info = array('stable' => $stable, 'summary' => $inf['s'], 'description' =>
31429                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
31430                        'unstable' => $unstable, 'state' => $state);
31431                }
31432            }
31433            $ret[$package] = $info;
31434        }
31435        PEAR::popErrorHandling();
31436        return $ret;
31437    }
31438
31439    function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg)
31440    {
31441        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
31442        if (PEAR::isError($packagelist)) {
31443            return $packagelist;
31444        }
31445
31446        $ret = array();
31447        if (!is_array($packagelist) || !isset($packagelist['p'])) {
31448            return $ret;
31449        }
31450
31451        if (!is_array($packagelist['p'])) {
31452            $packagelist['p'] = array($packagelist['p']);
31453        }
31454
31455        foreach ($packagelist['p'] as $package) {
31456            if (!isset($installed[strtolower($package)])) {
31457                continue;
31458            }
31459
31460            $inst_version = $reg->packageInfo($package, 'version', $channel);
31461            $inst_state   = $reg->packageInfo($package, 'release_state', $channel);
31462            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
31463            $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
31464                '/allreleases.xml', false, false, $channel);
31465            PEAR::popErrorHandling();
31466            if (PEAR::isError($info)) {
31467                continue; // no remote releases
31468            }
31469
31470            if (!isset($info['r'])) {
31471                continue;
31472            }
31473
31474            $release = $found = false;
31475            if (!is_array($info['r']) || !isset($info['r'][0])) {
31476                $info['r'] = array($info['r']);
31477            }
31478
31479            // $info['r'] is sorted by version number
31480            usort($info['r'], array($this, '_sortReleasesByVersionNumber'));
31481            foreach ($info['r'] as $release) {
31482                if ($inst_version && version_compare($release['v'], $inst_version, '<=')) {
31483                    // not newer than the one installed
31484                    break;
31485                }
31486
31487                // new version > installed version
31488                if (!$pref_state) {
31489                    // every state is a good state
31490                    $found = true;
31491                    break;
31492                } else {
31493                    $new_state = $release['s'];
31494                    // if new state >= installed state: go
31495                    if (in_array($new_state, $this->betterStates($inst_state, true))) {
31496                        $found = true;
31497                        break;
31498                    } else {
31499                        // only allow to lower the state of package,
31500                        // if new state >= preferred state: go
31501                        if (in_array($new_state, $this->betterStates($pref_state, true))) {
31502                            $found = true;
31503                            break;
31504                        }
31505                    }
31506                }
31507            }
31508
31509            if (!$found) {
31510                continue;
31511            }
31512
31513            $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
31514                $release['v'] . '.xml', false, false, $channel);
31515            if (PEAR::isError($relinfo)) {
31516                return $relinfo;
31517            }
31518
31519            $ret[$package] = array(
31520                'version'  => $release['v'],
31521                'state'    => $release['s'],
31522                'filesize' => $relinfo['f'],
31523            );
31524        }
31525
31526        return $ret;
31527    }
31528
31529    function packageInfo($base, $package, $channel = false)
31530    {
31531        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
31532        $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
31533        if (PEAR::isError($pinfo)) {
31534            PEAR::popErrorHandling();
31535            return PEAR::raiseError('Unknown package: "' . $package . '" in channel "' . $channel . '"' . "\n". 'Debug: ' .
31536                $pinfo->getMessage());
31537        }
31538
31539        $releases = array();
31540        $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
31541            '/allreleases.xml', false, false, $channel);
31542        if (!PEAR::isError($allreleases)) {
31543            if (!class_exists('PEAR_PackageFile_v2')) {
31544                require_once 'PEAR/PackageFile/v2.php';
31545            }
31546
31547            if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) {
31548                $allreleases['r'] = array($allreleases['r']);
31549            }
31550
31551            $pf = new PEAR_PackageFile_v2;
31552            foreach ($allreleases['r'] as $release) {
31553                $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
31554                    $release['v'] . '.txt', false, false, $channel);
31555                if (PEAR::isError($ds)) {
31556                    continue;
31557                }
31558
31559                if (!isset($latest)) {
31560                    $latest = $release['v'];
31561                }
31562
31563                $pf->setDeps(unserialize($ds));
31564                $ds = $pf->getDeps();
31565                $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package)
31566                    . '/' . $release['v'] . '.xml', false, false, $channel);
31567
31568                if (PEAR::isError($info)) {
31569                    continue;
31570                }
31571
31572                $releases[$release['v']] = array(
31573                    'doneby' => $info['m'],
31574                    'license' => $info['l'],
31575                    'summary' => $info['s'],
31576                    'description' => $info['d'],
31577                    'releasedate' => $info['da'],
31578                    'releasenotes' => $info['n'],
31579                    'state' => $release['s'],
31580                    'deps' => $ds ? $ds : array(),
31581                );
31582            }
31583        } else {
31584            $latest = '';
31585        }
31586
31587        PEAR::popErrorHandling();
31588        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
31589            if (is_array($pinfo['dp'])) {
31590                $deprecated = array('channel' => (string) $pinfo['dc'],
31591                                    'package' => trim($pinfo['dp']['_content']));
31592            } else {
31593                $deprecated = array('channel' => (string) $pinfo['dc'],
31594                                    'package' => trim($pinfo['dp']));
31595            }
31596        } else {
31597            $deprecated = false;
31598        }
31599
31600        if (!isset($latest)) {
31601            $latest = '';
31602        }
31603
31604        return array(
31605            'name' => $pinfo['n'],
31606            'channel' => $pinfo['c'],
31607            'category' => $pinfo['ca']['_content'],
31608            'stable' => $latest,
31609            'license' => $pinfo['l'],
31610            'summary' => $pinfo['s'],
31611            'description' => $pinfo['d'],
31612            'releases' => $releases,
31613            'deprecated' => $deprecated,
31614            );
31615    }
31616
31617    /**
31618     * Return an array containing all of the states that are more stable than
31619     * or equal to the passed in state
31620     *
31621     * @param string Release state
31622     * @param boolean Determines whether to include $state in the list
31623     * @return false|array False if $state is not a valid release state
31624     */
31625    function betterStates($state, $include = false)
31626    {
31627        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
31628        $i = array_search($state, $states);
31629        if ($i === false) {
31630            return false;
31631        }
31632
31633        if ($include) {
31634            $i--;
31635        }
31636
31637        return array_slice($states, $i + 1);
31638    }
31639
31640    /**
31641     * Sort releases by version number
31642     *
31643     * @access private
31644     */
31645    function _sortReleasesByVersionNumber($a, $b)
31646    {
31647        if (version_compare($a['v'], $b['v'], '=')) {
31648            return 0;
31649        }
31650
31651        if (version_compare($a['v'], $b['v'], '>')) {
31652            return -1;
31653        }
31654
31655        if (version_compare($a['v'], $b['v'], '<')) {
31656            return 1;
31657        }
31658    }
31659}PEAR-1.9.4/PEAR/REST/11.php0000644000076500000240000002605411605156614013501 0ustar  helgistaff<?php
31660/**
31661 * PEAR_REST_11 - implement faster list-all/remote-list command
31662 *
31663 * PHP versions 4 and 5
31664 *
31665 * @category   pear
31666 * @package    PEAR
31667 * @author     Greg Beaver <cellog@php.net>
31668 * @copyright  1997-2009 The Authors
31669 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
31670 * @version    CVS: $Id: 11.php 313023 2011-07-06 19:17:11Z dufuz $
31671 * @link       http://pear.php.net/package/PEAR
31672 * @since      File available since Release 1.4.3
31673 */
31674
31675/**
31676 * For downloading REST xml/txt files
31677 */
31678require_once 'PEAR/REST.php';
31679
31680/**
31681 * Implement REST 1.1
31682 *
31683 * @category   pear
31684 * @package    PEAR
31685 * @author     Greg Beaver <cellog@php.net>
31686 * @copyright  1997-2009 The Authors
31687 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
31688 * @version    Release: 1.9.4
31689 * @link       http://pear.php.net/package/PEAR
31690 * @since      Class available since Release 1.4.3
31691 */
31692class PEAR_REST_11
31693{
31694    /**
31695     * @var PEAR_REST
31696     */
31697    var $_rest;
31698
31699    function PEAR_REST_11($config, $options = array())
31700    {
31701        $this->_rest = &new PEAR_REST($config, $options);
31702    }
31703
31704    function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
31705    {
31706        $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel);
31707        if (PEAR::isError($categorylist)) {
31708            return $categorylist;
31709        }
31710
31711        $ret = array();
31712        if (!is_array($categorylist['c']) || !isset($categorylist['c'][0])) {
31713            $categorylist['c'] = array($categorylist['c']);
31714        }
31715
31716        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
31717
31718        foreach ($categorylist['c'] as $progress => $category) {
31719            $category = $category['_content'];
31720            $packagesinfo = $this->_rest->retrieveData($base .
31721                'c/' . urlencode($category) . '/packagesinfo.xml', false, false, $channel);
31722
31723            if (PEAR::isError($packagesinfo)) {
31724                continue;
31725            }
31726
31727            if (!is_array($packagesinfo) || !isset($packagesinfo['pi'])) {
31728                continue;
31729            }
31730
31731            if (!is_array($packagesinfo['pi']) || !isset($packagesinfo['pi'][0])) {
31732                $packagesinfo['pi'] = array($packagesinfo['pi']);
31733            }
31734
31735            foreach ($packagesinfo['pi'] as $packageinfo) {
31736                if (empty($packageinfo)) {
31737                    continue;
31738                }
31739
31740                $info     = $packageinfo['p'];
31741                $package  = $info['n'];
31742                $releases = isset($packageinfo['a']) ? $packageinfo['a'] : false;
31743                unset($latest);
31744                unset($unstable);
31745                unset($stable);
31746                unset($state);
31747
31748                if ($releases) {
31749                    if (!isset($releases['r'][0])) {
31750                        $releases['r'] = array($releases['r']);
31751                    }
31752
31753                    foreach ($releases['r'] as $release) {
31754                        if (!isset($latest)) {
31755                            if ($dostable && $release['s'] == 'stable') {
31756                                $latest = $release['v'];
31757                                $state = 'stable';
31758                            }
31759                            if (!$dostable) {
31760                                $latest = $release['v'];
31761                                $state = $release['s'];
31762                            }
31763                        }
31764
31765                        if (!isset($stable) && $release['s'] == 'stable') {
31766                            $stable = $release['v'];
31767                            if (!isset($unstable)) {
31768                                $unstable = $stable;
31769                            }
31770                        }
31771
31772                        if (!isset($unstable) && $release['s'] != 'stable') {
31773                            $unstable = $release['v'];
31774                            $state = $release['s'];
31775                        }
31776
31777                        if (isset($latest) && !isset($state)) {
31778                            $state = $release['s'];
31779                        }
31780
31781                        if (isset($latest) && isset($stable) && isset($unstable)) {
31782                            break;
31783                        }
31784                    }
31785                }
31786
31787                if ($basic) { // remote-list command
31788                    if (!isset($latest)) {
31789                        $latest = false;
31790                    }
31791
31792                    if ($dostable) {
31793                        // $state is not set if there are no releases
31794                        if (isset($state) && $state == 'stable') {
31795                            $ret[$package] = array('stable' => $latest);
31796                        } else {
31797                            $ret[$package] = array('stable' => '-n/a-');
31798                        }
31799                    } else {
31800                        $ret[$package] = array('stable' => $latest);
31801                    }
31802
31803                    continue;
31804                }
31805
31806                // list-all command
31807                if (!isset($unstable)) {
31808                    $unstable = false;
31809                    $state = 'stable';
31810                    if (isset($stable)) {
31811                        $latest = $unstable = $stable;
31812                    }
31813                } else {
31814                    $latest = $unstable;
31815                }
31816
31817                if (!isset($latest)) {
31818                    $latest = false;
31819                }
31820
31821                $deps = array();
31822                if ($latest && isset($packageinfo['deps'])) {
31823                    if (!is_array($packageinfo['deps']) ||
31824                          !isset($packageinfo['deps'][0])
31825                    ) {
31826                        $packageinfo['deps'] = array($packageinfo['deps']);
31827                    }
31828
31829                    $d = false;
31830                    foreach ($packageinfo['deps'] as $dep) {
31831                        if ($dep['v'] == $latest) {
31832                            $d = unserialize($dep['d']);
31833                        }
31834                    }
31835
31836                    if ($d) {
31837                        if (isset($d['required'])) {
31838                            if (!class_exists('PEAR_PackageFile_v2')) {
31839                                require_once 'PEAR/PackageFile/v2.php';
31840                            }
31841
31842                            if (!isset($pf)) {
31843                                $pf = new PEAR_PackageFile_v2;
31844                            }
31845
31846                            $pf->setDeps($d);
31847                            $tdeps = $pf->getDeps();
31848                        } else {
31849                            $tdeps = $d;
31850                        }
31851
31852                        foreach ($tdeps as $dep) {
31853                            if ($dep['type'] !== 'pkg') {
31854                                continue;
31855                            }
31856
31857                            $deps[] = $dep;
31858                        }
31859                    }
31860                }
31861
31862                $info = array(
31863                    'stable'      => $latest,
31864                    'summary'     => $info['s'],
31865                    'description' => $info['d'],
31866                    'deps'        => $deps,
31867                    'category'    => $info['ca']['_content'],
31868                    'unstable'    => $unstable,
31869                    'state'       => $state
31870                );
31871                $ret[$package] = $info;
31872            }
31873        }
31874
31875        PEAR::popErrorHandling();
31876        return $ret;
31877    }
31878
31879    /**
31880     * List all categories of a REST server
31881     *
31882     * @param string $base base URL of the server
31883     * @return array of categorynames
31884     */
31885    function listCategories($base, $channel = false)
31886    {
31887        $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel);
31888        if (PEAR::isError($categorylist)) {
31889            return $categorylist;
31890        }
31891
31892        if (!is_array($categorylist) || !isset($categorylist['c'])) {
31893            return array();
31894        }
31895
31896        if (isset($categorylist['c']['_content'])) {
31897            // only 1 category
31898            $categorylist['c'] = array($categorylist['c']);
31899        }
31900
31901        return $categorylist['c'];
31902    }
31903
31904    /**
31905     * List packages in a category of a REST server
31906     *
31907     * @param string $base base URL of the server
31908     * @param string $category name of the category
31909     * @param boolean $info also download full package info
31910     * @return array of packagenames
31911     */
31912    function listCategory($base, $category, $info = false, $channel = false)
31913    {
31914        if ($info == false) {
31915            $url = '%s'.'c/%s/packages.xml';
31916        } else {
31917            $url = '%s'.'c/%s/packagesinfo.xml';
31918        }
31919        $url = sprintf($url,
31920                    $base,
31921                    urlencode($category));
31922
31923        // gives '404 Not Found' error when category doesn't exist
31924        $packagelist = $this->_rest->retrieveData($url, false, false, $channel);
31925        if (PEAR::isError($packagelist)) {
31926            return $packagelist;
31927        }
31928        if (!is_array($packagelist)) {
31929            return array();
31930        }
31931
31932        if ($info == false) {
31933            if (!isset($packagelist['p'])) {
31934                return array();
31935            }
31936            if (!is_array($packagelist['p']) ||
31937                !isset($packagelist['p'][0])) { // only 1 pkg
31938                $packagelist = array($packagelist['p']);
31939            } else {
31940                $packagelist = $packagelist['p'];
31941            }
31942            return $packagelist;
31943        }
31944
31945        // info == true
31946        if (!isset($packagelist['pi'])) {
31947            return array();
31948        }
31949
31950        if (!is_array($packagelist['pi']) ||
31951            !isset($packagelist['pi'][0])) { // only 1 pkg
31952            $packagelist_pre = array($packagelist['pi']);
31953        } else {
31954            $packagelist_pre = $packagelist['pi'];
31955        }
31956
31957        $packagelist = array();
31958        foreach ($packagelist_pre as $i => $item) {
31959            // compatibility with r/<latest.txt>.xml
31960            if (isset($item['a']['r'][0])) {
31961                // multiple releases
31962                $item['p']['v'] = $item['a']['r'][0]['v'];
31963                $item['p']['st'] = $item['a']['r'][0]['s'];
31964            } elseif (isset($item['a'])) {
31965                // first and only release
31966                $item['p']['v'] = $item['a']['r']['v'];
31967                $item['p']['st'] = $item['a']['r']['s'];
31968            }
31969
31970            $packagelist[$i] = array('attribs' => $item['p']['r'],
31971                                     '_content' => $item['p']['n'],
31972                                     'info' => $item['p']);
31973        }
31974
31975        return $packagelist;
31976    }
31977
31978    /**
31979     * Return an array containing all of the states that are more stable than
31980     * or equal to the passed in state
31981     *
31982     * @param string Release state
31983     * @param boolean Determines whether to include $state in the list
31984     * @return false|array False if $state is not a valid release state
31985     */
31986    function betterStates($state, $include = false)
31987    {
31988        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
31989        $i = array_search($state, $states);
31990        if ($i === false) {
31991            return false;
31992        }
31993        if ($include) {
31994            $i--;
31995        }
31996        return array_slice($states, $i + 1);
31997    }
31998}
31999?>PEAR-1.9.4/PEAR/REST/13.php0000644000076500000240000002654411605156614013507 0ustar  helgistaff<?php
32000/**
32001 * PEAR_REST_13
32002 *
32003 * PHP versions 4 and 5
32004 *
32005 * @category   pear
32006 * @package    PEAR
32007 * @author     Greg Beaver <cellog@php.net>
32008 * @copyright  1997-2009 The Authors
32009 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32010 * @version    CVS: $Id: 13.php 313023 2011-07-06 19:17:11Z dufuz $
32011 * @link       http://pear.php.net/package/PEAR
32012 * @since      File available since Release 1.4.0a12
32013 */
32014
32015/**
32016 * For downloading REST xml/txt files
32017 */
32018require_once 'PEAR/REST.php';
32019require_once 'PEAR/REST/10.php';
32020
32021/**
32022 * Implement REST 1.3
32023 *
32024 * @category   pear
32025 * @package    PEAR
32026 * @author     Greg Beaver <cellog@php.net>
32027 * @copyright  1997-2009 The Authors
32028 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32029 * @version    Release: 1.9.4
32030 * @link       http://pear.php.net/package/PEAR
32031 * @since      Class available since Release 1.4.0a12
32032 */
32033class PEAR_REST_13 extends PEAR_REST_10
32034{
32035    /**
32036     * Retrieve information about a remote package to be downloaded from a REST server
32037     *
32038     * This is smart enough to resolve the minimum PHP version dependency prior to download
32039     * @param string $base The uri to prepend to all REST calls
32040     * @param array $packageinfo an array of format:
32041     * <pre>
32042     *  array(
32043     *   'package' => 'packagename',
32044     *   'channel' => 'channelname',
32045     *  ['state' => 'alpha' (or valid state),]
32046     *  -or-
32047     *  ['version' => '1.whatever']
32048     * </pre>
32049     * @param string $prefstate Current preferred_state config variable value
32050     * @param bool $installed the installed version of this package to compare against
32051     * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
32052     */
32053    function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
32054    {
32055        $states = $this->betterStates($prefstate, true);
32056        if (!$states) {
32057            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
32058        }
32059
32060        $channel  = $packageinfo['channel'];
32061        $package  = $packageinfo['package'];
32062        $state    = isset($packageinfo['state'])   ? $packageinfo['state']   : null;
32063        $version  = isset($packageinfo['version']) ? $packageinfo['version'] : null;
32064        $restFile = $base . 'r/' . strtolower($package) . '/allreleases2.xml';
32065
32066        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
32067        if (PEAR::isError($info)) {
32068            return PEAR::raiseError('No releases available for package "' .
32069                $channel . '/' . $package . '"');
32070        }
32071
32072        if (!isset($info['r'])) {
32073            return false;
32074        }
32075
32076        $release = $found = false;
32077        if (!is_array($info['r']) || !isset($info['r'][0])) {
32078            $info['r'] = array($info['r']);
32079        }
32080
32081        $skippedphp = false;
32082        foreach ($info['r'] as $release) {
32083            if (!isset($this->_rest->_options['force']) && ($installed &&
32084                  version_compare($release['v'], $installed, '<'))) {
32085                continue;
32086            }
32087
32088            if (isset($state)) {
32089                // try our preferred state first
32090                if ($release['s'] == $state) {
32091                    if (!isset($version) && version_compare($release['m'], phpversion(), '>')) {
32092                        // skip releases that require a PHP version newer than our PHP version
32093                        $skippedphp = $release;
32094                        continue;
32095                    }
32096                    $found = true;
32097                    break;
32098                }
32099
32100                // see if there is something newer and more stable
32101                // bug #7221
32102                if (in_array($release['s'], $this->betterStates($state), true)) {
32103                    if (!isset($version) && version_compare($release['m'], phpversion(), '>')) {
32104                        // skip releases that require a PHP version newer than our PHP version
32105                        $skippedphp = $release;
32106                        continue;
32107                    }
32108                    $found = true;
32109                    break;
32110                }
32111            } elseif (isset($version)) {
32112                if ($release['v'] == $version) {
32113                    if (!isset($this->_rest->_options['force']) &&
32114                          !isset($version) &&
32115                          version_compare($release['m'], phpversion(), '>')) {
32116                        // skip releases that require a PHP version newer than our PHP version
32117                        $skippedphp = $release;
32118                        continue;
32119                    }
32120                    $found = true;
32121                    break;
32122                }
32123            } else {
32124                if (in_array($release['s'], $states)) {
32125                    if (version_compare($release['m'], phpversion(), '>')) {
32126                        // skip releases that require a PHP version newer than our PHP version
32127                        $skippedphp = $release;
32128                        continue;
32129                    }
32130                    $found = true;
32131                    break;
32132                }
32133            }
32134        }
32135
32136        if (!$found && $skippedphp) {
32137            $found = null;
32138        }
32139
32140        return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel);
32141    }
32142
32143    function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
32144                               $prefstate = 'stable', $installed = false, $channel = false)
32145    {
32146        $states = $this->betterStates($prefstate, true);
32147        if (!$states) {
32148            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
32149        }
32150
32151        $channel  = $dependency['channel'];
32152        $package  = $dependency['name'];
32153        $state    = isset($dependency['state'])   ? $dependency['state']   : null;
32154        $version  = isset($dependency['version']) ? $dependency['version'] : null;
32155        $restFile = $base . 'r/' . strtolower($package) .'/allreleases2.xml';
32156
32157        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
32158        if (PEAR::isError($info)) {
32159            return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
32160                . '" dependency "' . $channel . '/' . $package . '" has no releases');
32161        }
32162
32163        if (!is_array($info) || !isset($info['r'])) {
32164            return false;
32165        }
32166
32167        $exclude = array();
32168        $min = $max = $recommended = false;
32169        if ($xsdversion == '1.0') {
32170            $pinfo['package'] = $dependency['name'];
32171            $pinfo['channel'] = 'pear.php.net'; // this is always true - don't change this
32172            switch ($dependency['rel']) {
32173                case 'ge' :
32174                    $min = $dependency['version'];
32175                break;
32176                case 'gt' :
32177                    $min = $dependency['version'];
32178                    $exclude = array($dependency['version']);
32179                break;
32180                case 'eq' :
32181                    $recommended = $dependency['version'];
32182                break;
32183                case 'lt' :
32184                    $max = $dependency['version'];
32185                    $exclude = array($dependency['version']);
32186                break;
32187                case 'le' :
32188                    $max = $dependency['version'];
32189                break;
32190                case 'ne' :
32191                    $exclude = array($dependency['version']);
32192                break;
32193            }
32194        } else {
32195            $pinfo['package'] = $dependency['name'];
32196            $min = isset($dependency['min']) ? $dependency['min'] : false;
32197            $max = isset($dependency['max']) ? $dependency['max'] : false;
32198            $recommended = isset($dependency['recommended']) ?
32199                $dependency['recommended'] : false;
32200            if (isset($dependency['exclude'])) {
32201                if (!isset($dependency['exclude'][0])) {
32202                    $exclude = array($dependency['exclude']);
32203                }
32204            }
32205        }
32206
32207        $skippedphp = $found = $release = false;
32208        if (!is_array($info['r']) || !isset($info['r'][0])) {
32209            $info['r'] = array($info['r']);
32210        }
32211
32212        foreach ($info['r'] as $release) {
32213            if (!isset($this->_rest->_options['force']) && ($installed &&
32214                  version_compare($release['v'], $installed, '<'))) {
32215                continue;
32216            }
32217
32218            if (in_array($release['v'], $exclude)) { // skip excluded versions
32219                continue;
32220            }
32221
32222            // allow newer releases to say "I'm OK with the dependent package"
32223            if ($xsdversion == '2.0' && isset($release['co'])) {
32224                if (!is_array($release['co']) || !isset($release['co'][0])) {
32225                    $release['co'] = array($release['co']);
32226                }
32227
32228                foreach ($release['co'] as $entry) {
32229                    if (isset($entry['x']) && !is_array($entry['x'])) {
32230                        $entry['x'] = array($entry['x']);
32231                    } elseif (!isset($entry['x'])) {
32232                        $entry['x'] = array();
32233                    }
32234
32235                    if ($entry['c'] == $deppackage['channel'] &&
32236                          strtolower($entry['p']) == strtolower($deppackage['package']) &&
32237                          version_compare($deppackage['version'], $entry['min'], '>=') &&
32238                          version_compare($deppackage['version'], $entry['max'], '<=') &&
32239                          !in_array($release['v'], $entry['x'])) {
32240                        if (version_compare($release['m'], phpversion(), '>')) {
32241                            // skip dependency releases that require a PHP version
32242                            // newer than our PHP version
32243                            $skippedphp = $release;
32244                            continue;
32245                        }
32246
32247                        $recommended = $release['v'];
32248                        break;
32249                    }
32250                }
32251            }
32252
32253            if ($recommended) {
32254                if ($release['v'] != $recommended) { // if we want a specific
32255                    // version, then skip all others
32256                    continue;
32257                }
32258
32259                if (!in_array($release['s'], $states)) {
32260                    // the stability is too low, but we must return the
32261                    // recommended version if possible
32262                    return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
32263                }
32264            }
32265
32266            if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
32267                continue;
32268            }
32269
32270            if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
32271                continue;
32272            }
32273
32274            if ($installed && version_compare($release['v'], $installed, '<')) {
32275                continue;
32276            }
32277
32278            if (in_array($release['s'], $states)) { // if in the preferred state...
32279                if (version_compare($release['m'], phpversion(), '>')) {
32280                    // skip dependency releases that require a PHP version
32281                    // newer than our PHP version
32282                    $skippedphp = $release;
32283                    continue;
32284                }
32285
32286                $found = true; // ... then use it
32287                break;
32288            }
32289        }
32290
32291        if (!$found && $skippedphp) {
32292            $found = null;
32293        }
32294
32295        return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel);
32296    }
32297}PEAR-1.9.4/PEAR/Task/Postinstallscript/rw.php0000644000076500000240000001331111605156614017566 0ustar  helgistaff<?php
32298/**
32299 * <tasks:postinstallscript> - read/write version
32300 *
32301 * PHP versions 4 and 5
32302 *
32303 * @category   pear
32304 * @package    PEAR
32305 * @author     Greg Beaver <cellog@php.net>
32306 * @copyright  1997-2009 The Authors
32307 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32308 * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
32309 * @link       http://pear.php.net/package/PEAR
32310 * @since      File available since Release 1.4.0a10
32311 */
32312/**
32313 * Base class
32314 */
32315require_once 'PEAR/Task/Postinstallscript.php';
32316/**
32317 * Abstracts the postinstallscript file task xml.
32318 * @category   pear
32319 * @package    PEAR
32320 * @author     Greg Beaver <cellog@php.net>
32321 * @copyright  1997-2009 The Authors
32322 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32323 * @version    Release: 1.9.4
32324 * @link       http://pear.php.net/package/PEAR
32325 * @since      Class available since Release 1.4.0a10
32326 */
32327class PEAR_Task_Postinstallscript_rw extends PEAR_Task_Postinstallscript
32328{
32329    /**
32330     * parent package file object
32331     *
32332     * @var PEAR_PackageFile_v2_rw
32333     */
32334    var $_pkg;
32335    /**
32336     * Enter description here...
32337     *
32338     * @param PEAR_PackageFile_v2_rw $pkg
32339     * @param PEAR_Config $config
32340     * @param PEAR_Frontend $logger
32341     * @param array $fileXml
32342     * @return PEAR_Task_Postinstallscript_rw
32343     */
32344    function PEAR_Task_Postinstallscript_rw(&$pkg, &$config, &$logger, $fileXml)
32345    {
32346        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
32347        $this->_contents = $fileXml;
32348        $this->_pkg = &$pkg;
32349        $this->_params = array();
32350    }
32351
32352    function validate()
32353    {
32354        return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
32355    }
32356
32357    function getName()
32358    {
32359        return 'postinstallscript';
32360    }
32361
32362    /**
32363     * add a simple <paramgroup> to the post-install script
32364     *
32365     * Order is significant, so call this method in the same
32366     * sequence the users should see the paramgroups.  The $params
32367     * parameter should either be the result of a call to {@link getParam()}
32368     * or an array of calls to getParam().
32369     *
32370     * Use {@link addConditionTypeGroup()} to add a <paramgroup> containing
32371     * a <conditiontype> tag
32372     * @param string $id <paramgroup> id as seen by the script
32373     * @param array|false $params array of getParam() calls, or false for no params
32374     * @param string|false $instructions
32375     */
32376    function addParamGroup($id, $params = false, $instructions = false)
32377    {
32378        if ($params && isset($params[0]) && !isset($params[1])) {
32379            $params = $params[0];
32380        }
32381        $stuff =
32382            array(
32383                $this->_pkg->getTasksNs() . ':id' => $id,
32384            );
32385        if ($instructions) {
32386            $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions;
32387        }
32388        if ($params) {
32389            $stuff[$this->_pkg->getTasksNs() . ':param'] = $params;
32390        }
32391        $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff;
32392    }
32393
32394    /**
32395     * add a complex <paramgroup> to the post-install script with conditions
32396     *
32397     * This inserts a <paramgroup> with
32398     *
32399     * Order is significant, so call this method in the same
32400     * sequence the users should see the paramgroups.  The $params
32401     * parameter should either be the result of a call to {@link getParam()}
32402     * or an array of calls to getParam().
32403     *
32404     * Use {@link addParamGroup()} to add a simple <paramgroup>
32405     *
32406     * @param string $id <paramgroup> id as seen by the script
32407     * @param string $oldgroup <paramgroup> id of the section referenced by
32408     *                         <conditiontype>
32409     * @param string $param name of the <param> from the older section referenced
32410     *                      by <contitiontype>
32411     * @param string $value value to match of the parameter
32412     * @param string $conditiontype one of '=', '!=', 'preg_match'
32413     * @param array|false $params array of getParam() calls, or false for no params
32414     * @param string|false $instructions
32415     */
32416    function addConditionTypeGroup($id, $oldgroup, $param, $value, $conditiontype = '=',
32417                                   $params = false, $instructions = false)
32418    {
32419        if ($params && isset($params[0]) && !isset($params[1])) {
32420            $params = $params[0];
32421        }
32422        $stuff = array(
32423            $this->_pkg->getTasksNs() . ':id' => $id,
32424        );
32425        if ($instructions) {
32426            $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions;
32427        }
32428        $stuff[$this->_pkg->getTasksNs() . ':name'] = $oldgroup . '::' . $param;
32429        $stuff[$this->_pkg->getTasksNs() . ':conditiontype'] = $conditiontype;
32430        $stuff[$this->_pkg->getTasksNs() . ':value'] = $value;
32431        if ($params) {
32432            $stuff[$this->_pkg->getTasksNs() . ':param'] = $params;
32433        }
32434        $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff;
32435    }
32436
32437    function getXml()
32438    {
32439        return $this->_params;
32440    }
32441
32442    /**
32443     * Use to set up a param tag for use in creating a paramgroup
32444     * @static
32445     */
32446    function getParam($name, $prompt, $type = 'string', $default = null)
32447    {
32448        if ($default !== null) {
32449            return
32450            array(
32451                $this->_pkg->getTasksNs() . ':name' => $name,
32452                $this->_pkg->getTasksNs() . ':prompt' => $prompt,
32453                $this->_pkg->getTasksNs() . ':type' => $type,
32454                $this->_pkg->getTasksNs() . ':default' => $default
32455            );
32456        }
32457        return
32458            array(
32459                $this->_pkg->getTasksNs() . ':name' => $name,
32460                $this->_pkg->getTasksNs() . ':prompt' => $prompt,
32461                $this->_pkg->getTasksNs() . ':type' => $type,
32462            );
32463    }
32464}
32465?>PEAR-1.9.4/PEAR/Task/Replace/rw.php0000644000076500000240000000311511605156614015401 0ustar  helgistaff<?php
32466/**
32467 * <tasks:replace> - read/write version
32468 *
32469 * PHP versions 4 and 5
32470 *
32471 * @category   pear
32472 * @package    PEAR
32473 * @author     Greg Beaver <cellog@php.net>
32474 * @copyright  1997-2009 The Authors
32475 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32476 * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
32477 * @link       http://pear.php.net/package/PEAR
32478 * @since      File available since Release 1.4.0a10
32479 */
32480/**
32481 * Base class
32482 */
32483require_once 'PEAR/Task/Replace.php';
32484/**
32485 * Abstracts the replace task xml.
32486 * @category   pear
32487 * @package    PEAR
32488 * @author     Greg Beaver <cellog@php.net>
32489 * @copyright  1997-2009 The Authors
32490 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32491 * @version    Release: 1.9.4
32492 * @link       http://pear.php.net/package/PEAR
32493 * @since      Class available since Release 1.4.0a10
32494 */
32495class PEAR_Task_Replace_rw extends PEAR_Task_Replace
32496{
32497    function PEAR_Task_Replace_rw(&$pkg, &$config, &$logger, $fileXml)
32498    {
32499        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
32500        $this->_contents = $fileXml;
32501        $this->_pkg = &$pkg;
32502        $this->_params = array();
32503    }
32504
32505    function validate()
32506    {
32507        return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
32508    }
32509
32510    function setInfo($from, $to, $type)
32511    {
32512        $this->_params = array('attribs' => array('from' => $from, 'to' => $to, 'type' => $type));
32513    }
32514
32515    function getName()
32516    {
32517        return 'replace';
32518    }
32519
32520    function getXml()
32521    {
32522        return $this->_params;
32523    }
32524}
32525?>PEAR-1.9.4/PEAR/Task/Unixeol/rw.php0000644000076500000240000000253511605156614015456 0ustar  helgistaff<?php
32526/**
32527 * <tasks:unixeol> - read/write version
32528 *
32529 * PHP versions 4 and 5
32530 *
32531 * @category   pear
32532 * @package    PEAR
32533 * @author     Greg Beaver <cellog@php.net>
32534 * @copyright  1997-2009 The Authors
32535 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32536 * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
32537 * @link       http://pear.php.net/package/PEAR
32538 * @since      File available since Release 1.4.0a10
32539 */
32540/**
32541 * Base class
32542 */
32543require_once 'PEAR/Task/Unixeol.php';
32544/**
32545 * Abstracts the unixeol task xml.
32546 * @category   pear
32547 * @package    PEAR
32548 * @author     Greg Beaver <cellog@php.net>
32549 * @copyright  1997-2009 The Authors
32550 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32551 * @version    Release: 1.9.4
32552 * @link       http://pear.php.net/package/PEAR
32553 * @since      Class available since Release 1.4.0a10
32554 */
32555class PEAR_Task_Unixeol_rw extends PEAR_Task_Unixeol
32556{
32557    function PEAR_Task_Unixeol_rw(&$pkg, &$config, &$logger, $fileXml)
32558    {
32559        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
32560        $this->_contents = $fileXml;
32561        $this->_pkg = &$pkg;
32562        $this->_params = array();
32563    }
32564
32565    function validate()
32566    {
32567        return true;
32568    }
32569
32570    function getName()
32571    {
32572        return 'unixeol';
32573    }
32574
32575    function getXml()
32576    {
32577        return '';
32578    }
32579}
32580?>PEAR-1.9.4/PEAR/Task/Windowseol/rw.php0000644000076500000240000000256211605156614016165 0ustar  helgistaff<?php
32581/**
32582 * <tasks:windowseol> - read/write version
32583 *
32584 * PHP versions 4 and 5
32585 *
32586 * @category   pear
32587 * @package    PEAR
32588 * @author     Greg Beaver <cellog@php.net>
32589 * @copyright  1997-2009 The Authors
32590 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32591 * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
32592 * @link       http://pear.php.net/package/PEAR
32593 * @since      File available since Release 1.4.0a10
32594 */
32595/**
32596 * Base class
32597 */
32598require_once 'PEAR/Task/Windowseol.php';
32599/**
32600 * Abstracts the windowseol task xml.
32601 * @category   pear
32602 * @package    PEAR
32603 * @author     Greg Beaver <cellog@php.net>
32604 * @copyright  1997-2009 The Authors
32605 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32606 * @version    Release: 1.9.4
32607 * @link       http://pear.php.net/package/PEAR
32608 * @since      Class available since Release 1.4.0a10
32609 */
32610class PEAR_Task_Windowseol_rw extends PEAR_Task_Windowseol
32611{
32612    function PEAR_Task_Windowseol_rw(&$pkg, &$config, &$logger, $fileXml)
32613    {
32614        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
32615        $this->_contents = $fileXml;
32616        $this->_pkg = &$pkg;
32617        $this->_params = array();
32618    }
32619
32620    function validate()
32621    {
32622        return true;
32623    }
32624
32625    function getName()
32626    {
32627        return 'windowseol';
32628    }
32629
32630    function getXml()
32631    {
32632        return '';
32633    }
32634}
32635?>PEAR-1.9.4/PEAR/Task/Common.php0000644000076500000240000001373611605156614014640 0ustar  helgistaff<?php
32636/**
32637 * PEAR_Task_Common, base class for installer tasks
32638 *
32639 * PHP versions 4 and 5
32640 *
32641 * @category   pear
32642 * @package    PEAR
32643 * @author     Greg Beaver <cellog@php.net>
32644 * @copyright  1997-2009 The Authors
32645 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32646 * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
32647 * @link       http://pear.php.net/package/PEAR
32648 * @since      File available since Release 1.4.0a1
32649 */
32650/**#@+
32651 * Error codes for task validation routines
32652 */
32653define('PEAR_TASK_ERROR_NOATTRIBS', 1);
32654define('PEAR_TASK_ERROR_MISSING_ATTRIB', 2);
32655define('PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE', 3);
32656define('PEAR_TASK_ERROR_INVALID', 4);
32657/**#@-*/
32658define('PEAR_TASK_PACKAGE', 1);
32659define('PEAR_TASK_INSTALL', 2);
32660define('PEAR_TASK_PACKAGEANDINSTALL', 3);
32661/**
32662 * A task is an operation that manipulates the contents of a file.
32663 *
32664 * Simple tasks operate on 1 file.  Multiple tasks are executed after all files have been
32665 * processed and installed, and are designed to operate on all files containing the task.
32666 * The Post-install script task simply takes advantage of the fact that it will be run
32667 * after installation, replace is a simple task.
32668 *
32669 * Combining tasks is possible, but ordering is significant.
32670 *
32671 * <file name="test.php" role="php">
32672 *  <tasks:replace from="@data-dir@" to="data_dir" type="pear-config"/>
32673 *  <tasks:postinstallscript/>
32674 * </file>
32675 *
32676 * This will first replace any instance of @data-dir@ in the test.php file
32677 * with the path to the current data directory.  Then, it will include the
32678 * test.php file and run the script it contains to configure the package post-installation.
32679 * @category   pear
32680 * @package    PEAR
32681 * @author     Greg Beaver <cellog@php.net>
32682 * @copyright  1997-2009 The Authors
32683 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32684 * @version    Release: 1.9.4
32685 * @link       http://pear.php.net/package/PEAR
32686 * @since      Class available since Release 1.4.0a1
32687 * @abstract
32688 */
32689class PEAR_Task_Common
32690{
32691    /**
32692     * Valid types for this version are 'simple' and 'multiple'
32693     *
32694     * - simple tasks operate on the contents of a file and write out changes to disk
32695     * - multiple tasks operate on the contents of many files and write out the
32696     *   changes directly to disk
32697     *
32698     * Child task classes must override this property.
32699     * @access protected
32700     */
32701    var $type = 'simple';
32702    /**
32703     * Determines which install phase this task is executed under
32704     */
32705    var $phase = PEAR_TASK_INSTALL;
32706    /**
32707     * @access protected
32708     */
32709    var $config;
32710    /**
32711     * @access protected
32712     */
32713    var $registry;
32714    /**
32715     * @access protected
32716     */
32717    var $logger;
32718    /**
32719     * @access protected
32720     */
32721    var $installphase;
32722    /**
32723     * @param PEAR_Config
32724     * @param PEAR_Common
32725     */
32726    function PEAR_Task_Common(&$config, &$logger, $phase)
32727    {
32728        $this->config = &$config;
32729        $this->registry = &$config->getRegistry();
32730        $this->logger = &$logger;
32731        $this->installphase = $phase;
32732        if ($this->type == 'multiple') {
32733            $GLOBALS['_PEAR_TASK_POSTINSTANCES'][get_class($this)][] = &$this;
32734        }
32735    }
32736
32737    /**
32738     * Validate the basic contents of a task tag.
32739     * @param PEAR_PackageFile_v2
32740     * @param array
32741     * @param PEAR_Config
32742     * @param array the entire parsed <file> tag
32743     * @return true|array On error, return an array in format:
32744     *    array(PEAR_TASK_ERROR_???[, param1][, param2][, ...])
32745     *
32746     *    For PEAR_TASK_ERROR_MISSING_ATTRIB, pass the attribute name in
32747     *    For PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, pass the attribute name and an array
32748     *    of legal values in
32749     * @static
32750     * @abstract
32751     */
32752    function validateXml($pkg, $xml, $config, $fileXml)
32753    {
32754    }
32755
32756    /**
32757     * Initialize a task instance with the parameters
32758     * @param array raw, parsed xml
32759     * @param array attributes from the <file> tag containing this task
32760     * @param string|null last installed version of this package
32761     * @abstract
32762     */
32763    function init($xml, $fileAttributes, $lastVersion)
32764    {
32765    }
32766
32767    /**
32768     * Begin a task processing session.  All multiple tasks will be processed after each file
32769     * has been successfully installed, all simple tasks should perform their task here and
32770     * return any errors using the custom throwError() method to allow forward compatibility
32771     *
32772     * This method MUST NOT write out any changes to disk
32773     * @param PEAR_PackageFile_v2
32774     * @param string file contents
32775     * @param string the eventual final file location (informational only)
32776     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
32777     *         (use $this->throwError), otherwise return the new contents
32778     * @abstract
32779     */
32780    function startSession($pkg, $contents, $dest)
32781    {
32782    }
32783
32784    /**
32785     * This method is used to process each of the tasks for a particular multiple class
32786     * type.  Simple tasks need not implement this method.
32787     * @param array an array of tasks
32788     * @access protected
32789     * @static
32790     * @abstract
32791     */
32792    function run($tasks)
32793    {
32794    }
32795
32796    /**
32797     * @static
32798     * @final
32799     */
32800    function hasPostinstallTasks()
32801    {
32802        return isset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
32803    }
32804
32805    /**
32806     * @static
32807     * @final
32808     */
32809     function runPostinstallTasks()
32810     {
32811         foreach ($GLOBALS['_PEAR_TASK_POSTINSTANCES'] as $class => $tasks) {
32812             $err = call_user_func(array($class, 'run'),
32813                  $GLOBALS['_PEAR_TASK_POSTINSTANCES'][$class]);
32814             if ($err) {
32815                 return PEAR_Task_Common::throwError($err);
32816             }
32817         }
32818         unset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
32819    }
32820
32821    /**
32822     * Determines whether a role is a script
32823     * @return bool
32824     */
32825    function isScript()
32826    {
32827        return $this->type == 'script';
32828    }
32829
32830    function throwError($msg, $code = -1)
32831    {
32832        include_once 'PEAR.php';
32833        return PEAR::raiseError($msg, $code);
32834    }
32835}
32836?>PEAR-1.9.4/PEAR/Task/Postinstallscript.php0000644000076500000240000003412211605156614017141 0ustar  helgistaff<?php
32837/**
32838 * <tasks:postinstallscript>
32839 *
32840 * PHP versions 4 and 5
32841 *
32842 * @category   pear
32843 * @package    PEAR
32844 * @author     Greg Beaver <cellog@php.net>
32845 * @copyright  1997-2009 The Authors
32846 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32847 * @version    CVS: $Id: Postinstallscript.php 313023 2011-07-06 19:17:11Z dufuz $
32848 * @link       http://pear.php.net/package/PEAR
32849 * @since      File available since Release 1.4.0a1
32850 */
32851/**
32852 * Base class
32853 */
32854require_once 'PEAR/Task/Common.php';
32855/**
32856 * Implements the postinstallscript file task.
32857 *
32858 * Note that post-install scripts are handled separately from installation, by the
32859 * "pear run-scripts" command
32860 * @category   pear
32861 * @package    PEAR
32862 * @author     Greg Beaver <cellog@php.net>
32863 * @copyright  1997-2009 The Authors
32864 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
32865 * @version    Release: 1.9.4
32866 * @link       http://pear.php.net/package/PEAR
32867 * @since      Class available since Release 1.4.0a1
32868 */
32869class PEAR_Task_Postinstallscript extends PEAR_Task_Common
32870{
32871    var $type = 'script';
32872    var $_class;
32873    var $_params;
32874    var $_obj;
32875    /**
32876     *
32877     * @var PEAR_PackageFile_v2
32878     */
32879    var $_pkg;
32880    var $_contents;
32881    var $phase = PEAR_TASK_INSTALL;
32882
32883    /**
32884     * Validate the raw xml at parsing-time.
32885     *
32886     * This also attempts to validate the script to make sure it meets the criteria
32887     * for a post-install script
32888     * @param PEAR_PackageFile_v2
32889     * @param array The XML contents of the <postinstallscript> tag
32890     * @param PEAR_Config
32891     * @param array the entire parsed <file> tag
32892     * @static
32893     */
32894    function validateXml($pkg, $xml, $config, $fileXml)
32895    {
32896        if ($fileXml['role'] != 'php') {
32897            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32898            $fileXml['name'] . '" must be role="php"');
32899        }
32900        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
32901        $file = $pkg->getFileContents($fileXml['name']);
32902        if (PEAR::isError($file)) {
32903            PEAR::popErrorHandling();
32904            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32905                $fileXml['name'] . '" is not valid: ' .
32906                $file->getMessage());
32907        } elseif ($file === null) {
32908            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32909                $fileXml['name'] . '" could not be retrieved for processing!');
32910        } else {
32911            $analysis = $pkg->analyzeSourceCode($file, true);
32912            if (!$analysis) {
32913                PEAR::popErrorHandling();
32914                $warnings = '';
32915                foreach ($pkg->getValidationWarnings() as $warn) {
32916                    $warnings .= $warn['message'] . "\n";
32917                }
32918                return array(PEAR_TASK_ERROR_INVALID, 'Analysis of post-install script "' .
32919                    $fileXml['name'] . '" failed: ' . $warnings);
32920            }
32921            if (count($analysis['declared_classes']) != 1) {
32922                PEAR::popErrorHandling();
32923                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32924                    $fileXml['name'] . '" must declare exactly 1 class');
32925            }
32926            $class = $analysis['declared_classes'][0];
32927            if ($class != str_replace(array('/', '.php'), array('_', ''),
32928                  $fileXml['name']) . '_postinstall') {
32929                PEAR::popErrorHandling();
32930                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32931                    $fileXml['name'] . '" class "' . $class . '" must be named "' .
32932                    str_replace(array('/', '.php'), array('_', ''),
32933                    $fileXml['name']) . '_postinstall"');
32934            }
32935            if (!isset($analysis['declared_methods'][$class])) {
32936                PEAR::popErrorHandling();
32937                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32938                    $fileXml['name'] . '" must declare methods init() and run()');
32939            }
32940            $methods = array('init' => 0, 'run' => 1);
32941            foreach ($analysis['declared_methods'][$class] as $method) {
32942                if (isset($methods[$method])) {
32943                    unset($methods[$method]);
32944                }
32945            }
32946            if (count($methods)) {
32947                PEAR::popErrorHandling();
32948                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32949                    $fileXml['name'] . '" must declare methods init() and run()');
32950            }
32951        }
32952        PEAR::popErrorHandling();
32953        $definedparams = array();
32954        $tasksNamespace = $pkg->getTasksNs() . ':';
32955        if (!isset($xml[$tasksNamespace . 'paramgroup']) && isset($xml['paramgroup'])) {
32956            // in order to support the older betas, which did not expect internal tags
32957            // to also use the namespace
32958            $tasksNamespace = '';
32959        }
32960        if (isset($xml[$tasksNamespace . 'paramgroup'])) {
32961            $params = $xml[$tasksNamespace . 'paramgroup'];
32962            if (!is_array($params) || !isset($params[0])) {
32963                $params = array($params);
32964            }
32965            foreach ($params as $param) {
32966                if (!isset($param[$tasksNamespace . 'id'])) {
32967                    return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32968                        $fileXml['name'] . '" <paramgroup> must have ' .
32969                        'an ' . $tasksNamespace . 'id> tag');
32970                }
32971                if (isset($param[$tasksNamespace . 'name'])) {
32972                    if (!in_array($param[$tasksNamespace . 'name'], $definedparams)) {
32973                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32974                            $fileXml['name'] . '" ' . $tasksNamespace .
32975                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
32976                            '" parameter "' . $param[$tasksNamespace . 'name'] .
32977                            '" has not been previously defined');
32978                    }
32979                    if (!isset($param[$tasksNamespace . 'conditiontype'])) {
32980                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32981                            $fileXml['name'] . '" ' . $tasksNamespace .
32982                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
32983                            '" must have a ' . $tasksNamespace .
32984                            'conditiontype> tag containing either "=", ' .
32985                            '"!=", or "preg_match"');
32986                    }
32987                    if (!in_array($param[$tasksNamespace . 'conditiontype'],
32988                          array('=', '!=', 'preg_match'))) {
32989                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32990                            $fileXml['name'] . '" ' . $tasksNamespace .
32991                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
32992                            '" must have a ' . $tasksNamespace .
32993                            'conditiontype> tag containing either "=", ' .
32994                            '"!=", or "preg_match"');
32995                    }
32996                    if (!isset($param[$tasksNamespace . 'value'])) {
32997                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
32998                            $fileXml['name'] . '" ' . $tasksNamespace .
32999                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
33000                            '" must have a ' . $tasksNamespace .
33001                            'value> tag containing expected parameter value');
33002                    }
33003                }
33004                if (isset($param[$tasksNamespace . 'instructions'])) {
33005                    if (!is_string($param[$tasksNamespace . 'instructions'])) {
33006                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
33007                            $fileXml['name'] . '" ' . $tasksNamespace .
33008                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
33009                            '" ' . $tasksNamespace . 'instructions> must be simple text');
33010                    }
33011                }
33012                if (!isset($param[$tasksNamespace . 'param'])) {
33013                    continue; // <param> is no longer required
33014                }
33015                $subparams = $param[$tasksNamespace . 'param'];
33016                if (!is_array($subparams) || !isset($subparams[0])) {
33017                    $subparams = array($subparams);
33018                }
33019                foreach ($subparams as $subparam) {
33020                    if (!isset($subparam[$tasksNamespace . 'name'])) {
33021                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
33022                            $fileXml['name'] . '" parameter for ' .
33023                            $tasksNamespace . 'paramgroup> id "' .
33024                            $param[$tasksNamespace . 'id'] . '" must have ' .
33025                            'a ' . $tasksNamespace . 'name> tag');
33026                    }
33027                    if (!preg_match('/[a-zA-Z0-9]+/',
33028                          $subparam[$tasksNamespace . 'name'])) {
33029                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
33030                            $fileXml['name'] . '" parameter "' .
33031                            $subparam[$tasksNamespace . 'name'] .
33032                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
33033                            $param[$tasksNamespace . 'id'] .
33034                            '" is not a valid name.  Must contain only alphanumeric characters');
33035                    }
33036                    if (!isset($subparam[$tasksNamespace . 'prompt'])) {
33037                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
33038                            $fileXml['name'] . '" parameter "' .
33039                            $subparam[$tasksNamespace . 'name'] .
33040                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
33041                            $param[$tasksNamespace . 'id'] .
33042                            '" must have a ' . $tasksNamespace . 'prompt> tag');
33043                    }
33044                    if (!isset($subparam[$tasksNamespace . 'type'])) {
33045                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
33046                            $fileXml['name'] . '" parameter "' .
33047                            $subparam[$tasksNamespace . 'name'] .
33048                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
33049                            $param[$tasksNamespace . 'id'] .
33050                            '" must have a ' . $tasksNamespace . 'type> tag');
33051                    }
33052                    $definedparams[] = $param[$tasksNamespace . 'id'] . '::' .
33053                    $subparam[$tasksNamespace . 'name'];
33054                }
33055            }
33056        }
33057        return true;
33058    }
33059
33060    /**
33061     * Initialize a task instance with the parameters
33062     * @param array raw, parsed xml
33063     * @param array attributes from the <file> tag containing this task
33064     * @param string|null last installed version of this package, if any (useful for upgrades)
33065     */
33066    function init($xml, $fileattribs, $lastversion)
33067    {
33068        $this->_class = str_replace('/', '_', $fileattribs['name']);
33069        $this->_filename = $fileattribs['name'];
33070        $this->_class = str_replace ('.php', '', $this->_class) . '_postinstall';
33071        $this->_params = $xml;
33072        $this->_lastversion = $lastversion;
33073    }
33074
33075    /**
33076     * Strip the tasks: namespace from internal params
33077     *
33078     * @access private
33079     */
33080    function _stripNamespace($params = null)
33081    {
33082        if ($params === null) {
33083            $params = array();
33084            if (!is_array($this->_params)) {
33085                return;
33086            }
33087            foreach ($this->_params as $i => $param) {
33088                if (is_array($param)) {
33089                    $param = $this->_stripNamespace($param);
33090                }
33091                $params[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param;
33092            }
33093            $this->_params = $params;
33094        } else {
33095            $newparams = array();
33096            foreach ($params as $i => $param) {
33097                if (is_array($param)) {
33098                    $param = $this->_stripNamespace($param);
33099                }
33100                $newparams[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param;
33101            }
33102            return $newparams;
33103        }
33104    }
33105
33106    /**
33107     * Unlike other tasks, the installed file name is passed in instead of the file contents,
33108     * because this task is handled post-installation
33109     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
33110     * @param string file name
33111     * @return bool|PEAR_Error false to skip this file, PEAR_Error to fail
33112     *         (use $this->throwError)
33113     */
33114    function startSession($pkg, $contents)
33115    {
33116        if ($this->installphase != PEAR_TASK_INSTALL) {
33117            return false;
33118        }
33119        // remove the tasks: namespace if present
33120        $this->_pkg = $pkg;
33121        $this->_stripNamespace();
33122        $this->logger->log(0, 'Including external post-installation script "' .
33123            $contents . '" - any errors are in this script');
33124        include_once $contents;
33125        if (class_exists($this->_class)) {
33126            $this->logger->log(0, 'Inclusion succeeded');
33127        } else {
33128            return $this->throwError('init of post-install script class "' . $this->_class
33129                . '" failed');
33130        }
33131        $this->_obj = new $this->_class;
33132        $this->logger->log(1, 'running post-install script "' . $this->_class . '->init()"');
33133        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
33134        $res = $this->_obj->init($this->config, $pkg, $this->_lastversion);
33135        PEAR::popErrorHandling();
33136        if ($res) {
33137            $this->logger->log(0, 'init succeeded');
33138        } else {
33139            return $this->throwError('init of post-install script "' . $this->_class .
33140                '->init()" failed');
33141        }
33142        $this->_contents = $contents;
33143        return true;
33144    }
33145
33146    /**
33147     * No longer used
33148     * @see PEAR_PackageFile_v2::runPostinstallScripts()
33149     * @param array an array of tasks
33150     * @param string install or upgrade
33151     * @access protected
33152     * @static
33153     */
33154    function run()
33155    {
33156    }
33157}
33158?>PEAR-1.9.4/PEAR/Task/Replace.php0000644000076500000240000001523211605156614014754 0ustar  helgistaff<?php
33159/**
33160 * <tasks:replace>
33161 *
33162 * PHP versions 4 and 5
33163 *
33164 * @category   pear
33165 * @package    PEAR
33166 * @author     Greg Beaver <cellog@php.net>
33167 * @copyright  1997-2009 The Authors
33168 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33169 * @version    CVS: $Id: Replace.php 313023 2011-07-06 19:17:11Z dufuz $
33170 * @link       http://pear.php.net/package/PEAR
33171 * @since      File available since Release 1.4.0a1
33172 */
33173/**
33174 * Base class
33175 */
33176require_once 'PEAR/Task/Common.php';
33177/**
33178 * Implements the replace file task.
33179 * @category   pear
33180 * @package    PEAR
33181 * @author     Greg Beaver <cellog@php.net>
33182 * @copyright  1997-2009 The Authors
33183 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33184 * @version    Release: 1.9.4
33185 * @link       http://pear.php.net/package/PEAR
33186 * @since      Class available since Release 1.4.0a1
33187 */
33188class PEAR_Task_Replace extends PEAR_Task_Common
33189{
33190    var $type = 'simple';
33191    var $phase = PEAR_TASK_PACKAGEANDINSTALL;
33192    var $_replacements;
33193
33194    /**
33195     * Validate the raw xml at parsing-time.
33196     * @param PEAR_PackageFile_v2
33197     * @param array raw, parsed xml
33198     * @param PEAR_Config
33199     * @static
33200     */
33201    function validateXml($pkg, $xml, $config, $fileXml)
33202    {
33203        if (!isset($xml['attribs'])) {
33204            return array(PEAR_TASK_ERROR_NOATTRIBS);
33205        }
33206        if (!isset($xml['attribs']['type'])) {
33207            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'type');
33208        }
33209        if (!isset($xml['attribs']['to'])) {
33210            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'to');
33211        }
33212        if (!isset($xml['attribs']['from'])) {
33213            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'from');
33214        }
33215        if ($xml['attribs']['type'] == 'pear-config') {
33216            if (!in_array($xml['attribs']['to'], $config->getKeys())) {
33217                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
33218                    $config->getKeys());
33219            }
33220        } elseif ($xml['attribs']['type'] == 'php-const') {
33221            if (defined($xml['attribs']['to'])) {
33222                return true;
33223            } else {
33224                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
33225                    array('valid PHP constant'));
33226            }
33227        } elseif ($xml['attribs']['type'] == 'package-info') {
33228            if (in_array($xml['attribs']['to'],
33229                array('name', 'summary', 'channel', 'notes', 'extends', 'description',
33230                    'release_notes', 'license', 'release-license', 'license-uri',
33231                    'version', 'api-version', 'state', 'api-state', 'release_date',
33232                    'date', 'time'))) {
33233                return true;
33234            } else {
33235                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
33236                    array('name', 'summary', 'channel', 'notes', 'extends', 'description',
33237                    'release_notes', 'license', 'release-license', 'license-uri',
33238                    'version', 'api-version', 'state', 'api-state', 'release_date',
33239                    'date', 'time'));
33240            }
33241        } else {
33242            return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'type', $xml['attribs']['type'],
33243                array('pear-config', 'package-info', 'php-const'));
33244        }
33245        return true;
33246    }
33247
33248    /**
33249     * Initialize a task instance with the parameters
33250     * @param array raw, parsed xml
33251     * @param unused
33252     */
33253    function init($xml, $attribs)
33254    {
33255        $this->_replacements = isset($xml['attribs']) ? array($xml) : $xml;
33256    }
33257
33258    /**
33259     * Do a package.xml 1.0 replacement, with additional package-info fields available
33260     *
33261     * See validateXml() source for the complete list of allowed fields
33262     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
33263     * @param string file contents
33264     * @param string the eventual final file location (informational only)
33265     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
33266     *         (use $this->throwError), otherwise return the new contents
33267     */
33268    function startSession($pkg, $contents, $dest)
33269    {
33270        $subst_from = $subst_to = array();
33271        foreach ($this->_replacements as $a) {
33272            $a = $a['attribs'];
33273            $to = '';
33274            if ($a['type'] == 'pear-config') {
33275                if ($this->installphase == PEAR_TASK_PACKAGE) {
33276                    return false;
33277                }
33278                if ($a['to'] == 'master_server') {
33279                    $chan = $this->registry->getChannel($pkg->getChannel());
33280                    if (!PEAR::isError($chan)) {
33281                        $to = $chan->getServer();
33282                    } else {
33283                        $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
33284                        return false;
33285                    }
33286                } else {
33287                    if ($this->config->isDefinedLayer('ftp')) {
33288                        // try the remote config file first
33289                        $to = $this->config->get($a['to'], 'ftp', $pkg->getChannel());
33290                        if (is_null($to)) {
33291                            // then default to local
33292                            $to = $this->config->get($a['to'], null, $pkg->getChannel());
33293                        }
33294                    } else {
33295                        $to = $this->config->get($a['to'], null, $pkg->getChannel());
33296                    }
33297                }
33298                if (is_null($to)) {
33299                    $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
33300                    return false;
33301                }
33302            } elseif ($a['type'] == 'php-const') {
33303                if ($this->installphase == PEAR_TASK_PACKAGE) {
33304                    return false;
33305                }
33306                if (defined($a['to'])) {
33307                    $to = constant($a['to']);
33308                } else {
33309                    $this->logger->log(0, "$dest: invalid php-const replacement: $a[to]");
33310                    return false;
33311                }
33312            } else {
33313                if ($t = $pkg->packageInfo($a['to'])) {
33314                    $to = $t;
33315                } else {
33316                    $this->logger->log(0, "$dest: invalid package-info replacement: $a[to]");
33317                    return false;
33318                }
33319            }
33320            if (!is_null($to)) {
33321                $subst_from[] = $a['from'];
33322                $subst_to[] = $to;
33323            }
33324        }
33325        $this->logger->log(3, "doing " . sizeof($subst_from) .
33326            " substitution(s) for $dest");
33327        if (sizeof($subst_from)) {
33328            $contents = str_replace($subst_from, $subst_to, $contents);
33329        }
33330        return $contents;
33331    }
33332}
33333?>PEAR-1.9.4/PEAR/Task/Unixeol.php0000644000076500000240000000434111605156614015023 0ustar  helgistaff<?php
33334/**
33335 * <tasks:unixeol>
33336 *
33337 * PHP versions 4 and 5
33338 *
33339 * @category   pear
33340 * @package    PEAR
33341 * @author     Greg Beaver <cellog@php.net>
33342 * @copyright  1997-2009 The Authors
33343 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33344 * @version    CVS: $Id: Unixeol.php 313023 2011-07-06 19:17:11Z dufuz $
33345 * @link       http://pear.php.net/package/PEAR
33346 * @since      File available since Release 1.4.0a1
33347 */
33348/**
33349 * Base class
33350 */
33351require_once 'PEAR/Task/Common.php';
33352/**
33353 * Implements the unix line endings file task.
33354 * @category   pear
33355 * @package    PEAR
33356 * @author     Greg Beaver <cellog@php.net>
33357 * @copyright  1997-2009 The Authors
33358 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33359 * @version    Release: 1.9.4
33360 * @link       http://pear.php.net/package/PEAR
33361 * @since      Class available since Release 1.4.0a1
33362 */
33363class PEAR_Task_Unixeol extends PEAR_Task_Common
33364{
33365    var $type = 'simple';
33366    var $phase = PEAR_TASK_PACKAGE;
33367    var $_replacements;
33368
33369    /**
33370     * Validate the raw xml at parsing-time.
33371     * @param PEAR_PackageFile_v2
33372     * @param array raw, parsed xml
33373     * @param PEAR_Config
33374     * @static
33375     */
33376    function validateXml($pkg, $xml, $config, $fileXml)
33377    {
33378        if ($xml != '') {
33379            return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
33380        }
33381        return true;
33382    }
33383
33384    /**
33385     * Initialize a task instance with the parameters
33386     * @param array raw, parsed xml
33387     * @param unused
33388     */
33389    function init($xml, $attribs)
33390    {
33391    }
33392
33393    /**
33394     * Replace all line endings with line endings customized for the current OS
33395     *
33396     * See validateXml() source for the complete list of allowed fields
33397     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
33398     * @param string file contents
33399     * @param string the eventual final file location (informational only)
33400     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
33401     *         (use $this->throwError), otherwise return the new contents
33402     */
33403    function startSession($pkg, $contents, $dest)
33404    {
33405        $this->logger->log(3, "replacing all line endings with \\n in $dest");
33406        return preg_replace("/\r\n|\n\r|\r|\n/", "\n", $contents);
33407    }
33408}
33409?>PEAR-1.9.4/PEAR/Task/Windowseol.php0000644000076500000240000000433511605156614015535 0ustar  helgistaff<?php
33410/**
33411 * <tasks:windowseol>
33412 *
33413 * PHP versions 4 and 5
33414 *
33415 * @category   pear
33416 * @package    PEAR
33417 * @author     Greg Beaver <cellog@php.net>
33418 * @copyright  1997-2009 The Authors
33419 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33420 * @version    CVS: $Id: Windowseol.php 313023 2011-07-06 19:17:11Z dufuz $
33421 * @link       http://pear.php.net/package/PEAR
33422 * @since      File available since Release 1.4.0a1
33423 */
33424/**
33425 * Base class
33426 */
33427require_once 'PEAR/Task/Common.php';
33428/**
33429 * Implements the windows line endsings file task.
33430 * @category   pear
33431 * @package    PEAR
33432 * @author     Greg Beaver <cellog@php.net>
33433 * @copyright  1997-2009 The Authors
33434 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33435 * @version    Release: 1.9.4
33436 * @link       http://pear.php.net/package/PEAR
33437 * @since      Class available since Release 1.4.0a1
33438 */
33439class PEAR_Task_Windowseol extends PEAR_Task_Common
33440{
33441    var $type = 'simple';
33442    var $phase = PEAR_TASK_PACKAGE;
33443    var $_replacements;
33444
33445    /**
33446     * Validate the raw xml at parsing-time.
33447     * @param PEAR_PackageFile_v2
33448     * @param array raw, parsed xml
33449     * @param PEAR_Config
33450     * @static
33451     */
33452    function validateXml($pkg, $xml, $config, $fileXml)
33453    {
33454        if ($xml != '') {
33455            return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
33456        }
33457        return true;
33458    }
33459
33460    /**
33461     * Initialize a task instance with the parameters
33462     * @param array raw, parsed xml
33463     * @param unused
33464     */
33465    function init($xml, $attribs)
33466    {
33467    }
33468
33469    /**
33470     * Replace all line endings with windows line endings
33471     *
33472     * See validateXml() source for the complete list of allowed fields
33473     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
33474     * @param string file contents
33475     * @param string the eventual final file location (informational only)
33476     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
33477     *         (use $this->throwError), otherwise return the new contents
33478     */
33479    function startSession($pkg, $contents, $dest)
33480    {
33481        $this->logger->log(3, "replacing all line endings with \\r\\n in $dest");
33482        return preg_replace("/\r\n|\n\r|\r|\n/", "\r\n", $contents);
33483    }
33484}
33485?>PEAR-1.9.4/PEAR/Validator/PECL.php0000644000076500000240000000417511605156614015153 0ustar  helgistaff<?php
33486/**
33487 * Channel Validator for the pecl.php.net channel
33488 *
33489 * PHP 4 and PHP 5
33490 *
33491 * @category   pear
33492 * @package    PEAR
33493 * @author     Greg Beaver <cellog@php.net>
33494 * @copyright  1997-2006 The PHP Group
33495 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33496 * @version    CVS: $Id: PECL.php 313023 2011-07-06 19:17:11Z dufuz $
33497 * @link       http://pear.php.net/package/PEAR
33498 * @since      File available since Release 1.4.0a5
33499 */
33500/**
33501 * This is the parent class for all validators
33502 */
33503require_once 'PEAR/Validate.php';
33504/**
33505 * Channel Validator for the pecl.php.net channel
33506 * @category   pear
33507 * @package    PEAR
33508 * @author     Greg Beaver <cellog@php.net>
33509 * @copyright  1997-2009 The Authors
33510 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33511 * @version    Release: 1.9.4
33512 * @link       http://pear.php.net/package/PEAR
33513 * @since      Class available since Release 1.4.0a5
33514 */
33515class PEAR_Validator_PECL extends PEAR_Validate
33516{
33517    function validateVersion()
33518    {
33519        if ($this->_state == PEAR_VALIDATE_PACKAGING) {
33520            $version = $this->_packagexml->getVersion();
33521            $versioncomponents = explode('.', $version);
33522            $last = array_pop($versioncomponents);
33523            if (substr($last, 1, 2) == 'rc') {
33524                $this->_addFailure('version', 'Release Candidate versions must have ' .
33525                'upper-case RC, not lower-case rc');
33526                return false;
33527            }
33528        }
33529        return true;
33530    }
33531
33532    function validatePackageName()
33533    {
33534        $ret = parent::validatePackageName();
33535        if ($this->_packagexml->getPackageType() == 'extsrc' ||
33536              $this->_packagexml->getPackageType() == 'zendextsrc') {
33537            if (strtolower($this->_packagexml->getPackage()) !=
33538                  strtolower($this->_packagexml->getProvidesExtension())) {
33539                $this->_addWarning('providesextension', 'package name "' .
33540                    $this->_packagexml->getPackage() . '" is different from extension name "' .
33541                    $this->_packagexml->getProvidesExtension() . '"');
33542            }
33543        }
33544        return $ret;
33545    }
33546}
33547?>PEAR-1.9.4/PEAR/Autoloader.php0000644000076500000240000001464511605156614014605 0ustar  helgistaff<?php
33548/**
33549 * Class auto-loader
33550 *
33551 * PHP versions 4
33552
33553 *
33554 * @category   pear
33555 * @package    PEAR
33556 * @author     Stig Bakken <ssb@php.net>
33557 * @copyright  1997-2009 The Authors
33558 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33559 * @version    CVS: $Id: Autoloader.php 313023 2011-07-06 19:17:11Z dufuz $
33560 * @link       http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader
33561 * @since      File available since Release 0.1
33562 * @deprecated File deprecated in Release 1.4.0a1
33563 */
33564
33565// /* vim: set expandtab tabstop=4 shiftwidth=4: */
33566
33567if (!extension_loaded("overload")) {
33568    // die hard without ext/overload
33569    die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader");
33570}
33571
33572/**
33573 * Include for PEAR_Error and PEAR classes
33574 */
33575require_once "PEAR.php";
33576
33577/**
33578 * This class is for objects where you want to separate the code for
33579 * some methods into separate classes.  This is useful if you have a
33580 * class with not-frequently-used methods that contain lots of code
33581 * that you would like to avoid always parsing.
33582 *
33583 * The PEAR_Autoloader class provides autoloading and aggregation.
33584 * The autoloading lets you set up in which classes the separated
33585 * methods are found.  Aggregation is the technique used to import new
33586 * methods, an instance of each class providing separated methods is
33587 * stored and called every time the aggregated method is called.
33588 *
33589 * @category   pear
33590 * @package    PEAR
33591 * @author Stig Bakken <ssb@php.net>
33592 * @copyright  1997-2009 The Authors
33593 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33594 * @version    Release: 1.9.4
33595 * @link       http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader
33596 * @since      File available since Release 0.1
33597 * @deprecated File deprecated in Release 1.4.0a1
33598 */
33599class PEAR_Autoloader extends PEAR
33600{
33601    // {{{ properties
33602
33603    /**
33604     * Map of methods and classes where they are defined
33605     *
33606     * @var array
33607     *
33608     * @access private
33609     */
33610    var $_autoload_map = array();
33611
33612    /**
33613     * Map of methods and aggregate objects
33614     *
33615     * @var array
33616     *
33617     * @access private
33618     */
33619    var $_method_map = array();
33620
33621    // }}}
33622    // {{{ addAutoload()
33623
33624    /**
33625     * Add one or more autoload entries.
33626     *
33627     * @param string $method     which method to autoload
33628     *
33629     * @param string $classname  (optional) which class to find the method in.
33630     *                           If the $method parameter is an array, this
33631     *                           parameter may be omitted (and will be ignored
33632     *                           if not), and the $method parameter will be
33633     *                           treated as an associative array with method
33634     *                           names as keys and class names as values.
33635     *
33636     * @return void
33637     *
33638     * @access public
33639     */
33640    function addAutoload($method, $classname = null)
33641    {
33642        if (is_array($method)) {
33643            array_walk($method, create_function('$a,&$b', '$b = strtolower($b);'));
33644            $this->_autoload_map = array_merge($this->_autoload_map, $method);
33645        } else {
33646            $this->_autoload_map[strtolower($method)] = $classname;
33647        }
33648    }
33649
33650    // }}}
33651    // {{{ removeAutoload()
33652
33653    /**
33654     * Remove an autoload entry.
33655     *
33656     * @param string $method  which method to remove the autoload entry for
33657     *
33658     * @return bool TRUE if an entry was removed, FALSE if not
33659     *
33660     * @access public
33661     */
33662    function removeAutoload($method)
33663    {
33664        $method = strtolower($method);
33665        $ok = isset($this->_autoload_map[$method]);
33666        unset($this->_autoload_map[$method]);
33667        return $ok;
33668    }
33669
33670    // }}}
33671    // {{{ addAggregateObject()
33672
33673    /**
33674     * Add an aggregate object to this object.  If the specified class
33675     * is not defined, loading it will be attempted following PEAR's
33676     * file naming scheme.  All the methods in the class will be
33677     * aggregated, except private ones (name starting with an
33678     * underscore) and constructors.
33679     *
33680     * @param string $classname  what class to instantiate for the object.
33681     *
33682     * @return void
33683     *
33684     * @access public
33685     */
33686    function addAggregateObject($classname)
33687    {
33688        $classname = strtolower($classname);
33689        if (!class_exists($classname)) {
33690            $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname);
33691            include_once $include_file;
33692        }
33693        $obj =& new $classname;
33694        $methods = get_class_methods($classname);
33695        foreach ($methods as $method) {
33696            // don't import priviate methods and constructors
33697            if ($method{0} != '_' && $method != $classname) {
33698                $this->_method_map[$method] = $obj;
33699            }
33700        }
33701    }
33702
33703    // }}}
33704    // {{{ removeAggregateObject()
33705
33706    /**
33707     * Remove an aggregate object.
33708     *
33709     * @param string $classname  the class of the object to remove
33710     *
33711     * @return bool  TRUE if an object was removed, FALSE if not
33712     *
33713     * @access public
33714     */
33715    function removeAggregateObject($classname)
33716    {
33717        $ok = false;
33718        $classname = strtolower($classname);
33719        reset($this->_method_map);
33720        while (list($method, $obj) = each($this->_method_map)) {
33721            if (is_a($obj, $classname)) {
33722                unset($this->_method_map[$method]);
33723                $ok = true;
33724            }
33725        }
33726        return $ok;
33727    }
33728
33729    // }}}
33730    // {{{ __call()
33731
33732    /**
33733     * Overloaded object call handler, called each time an
33734     * undefined/aggregated method is invoked.  This method repeats
33735     * the call in the right aggregate object and passes on the return
33736     * value.
33737     *
33738     * @param string $method  which method that was called
33739     *
33740     * @param string $args    An array of the parameters passed in the
33741     *                        original call
33742     *
33743     * @return mixed  The return value from the aggregated method, or a PEAR
33744     *                error if the called method was unknown.
33745     */
33746    function __call($method, $args, &$retval)
33747    {
33748        $method = strtolower($method);
33749        if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) {
33750            $this->addAggregateObject($this->_autoload_map[$method]);
33751        }
33752        if (isset($this->_method_map[$method])) {
33753            $retval = call_user_func_array(array($this->_method_map[$method], $method), $args);
33754            return true;
33755        }
33756        return false;
33757    }
33758
33759    // }}}
33760}
33761
33762overload("PEAR_Autoloader");
33763
33764?>
33765PEAR-1.9.4/PEAR/Builder.php0000644000076500000240000004065411605156614014073 0ustar  helgistaff<?php
33766/**
33767 * PEAR_Builder for building PHP extensions (PECL packages)
33768 *
33769 * PHP versions 4 and 5
33770 *
33771 * @category   pear
33772 * @package    PEAR
33773 * @author     Stig Bakken <ssb@php.net>
33774 * @author     Greg Beaver <cellog@php.net>
33775 * @copyright  1997-2009 The Authors
33776 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33777 * @version    CVS: $Id: Builder.php 313024 2011-07-06 19:51:24Z dufuz $
33778 * @link       http://pear.php.net/package/PEAR
33779 * @since      File available since Release 0.1
33780 *
33781 * TODO: log output parameters in PECL command line
33782 * TODO: msdev path in configuration
33783 */
33784
33785/**
33786 * Needed for extending PEAR_Builder
33787 */
33788require_once 'PEAR/Common.php';
33789require_once 'PEAR/PackageFile.php';
33790
33791/**
33792 * Class to handle building (compiling) extensions.
33793 *
33794 * @category   pear
33795 * @package    PEAR
33796 * @author     Stig Bakken <ssb@php.net>
33797 * @author     Greg Beaver <cellog@php.net>
33798 * @copyright  1997-2009 The Authors
33799 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33800 * @version    Release: 1.9.4
33801 * @link       http://pear.php.net/package/PEAR
33802 * @since      Class available since PHP 4.0.2
33803 * @see        http://pear.php.net/manual/en/core.ppm.pear-builder.php
33804 */
33805class PEAR_Builder extends PEAR_Common
33806{
33807    var $php_api_version = 0;
33808    var $zend_module_api_no = 0;
33809    var $zend_extension_api_no = 0;
33810
33811    var $extensions_built = array();
33812
33813    /**
33814     * @var string Used for reporting when it is not possible to pass function
33815     *             via extra parameter, e.g. log, msdevCallback
33816     */
33817    var $current_callback = null;
33818
33819    // used for msdev builds
33820    var $_lastline = null;
33821    var $_firstline = null;
33822
33823    /**
33824     * PEAR_Builder constructor.
33825     *
33826     * @param object $ui user interface object (instance of PEAR_Frontend_*)
33827     *
33828     * @access public
33829     */
33830    function PEAR_Builder(&$ui)
33831    {
33832        parent::PEAR_Common();
33833        $this->setFrontendObject($ui);
33834    }
33835
33836    /**
33837     * Build an extension from source on windows.
33838     * requires msdev
33839     */
33840    function _build_win32($descfile, $callback = null)
33841    {
33842        if (is_object($descfile)) {
33843            $pkg = $descfile;
33844            $descfile = $pkg->getPackageFile();
33845        } else {
33846            $pf = &new PEAR_PackageFile($this->config, $this->debug);
33847            $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
33848            if (PEAR::isError($pkg)) {
33849                return $pkg;
33850            }
33851        }
33852        $dir = dirname($descfile);
33853        $old_cwd = getcwd();
33854
33855        if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
33856            return $this->raiseError("could not chdir to $dir");
33857        }
33858
33859        // packages that were in a .tar have the packagefile in this directory
33860        $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
33861        if (file_exists($dir) && is_dir($vdir)) {
33862            if (!chdir($vdir)) {
33863                return $this->raiseError("could not chdir to " . realpath($vdir));
33864            }
33865
33866            $dir = getcwd();
33867        }
33868
33869        $this->log(2, "building in $dir");
33870
33871        $dsp = $pkg->getPackage().'.dsp';
33872        if (!file_exists("$dir/$dsp")) {
33873            return $this->raiseError("The DSP $dsp does not exist.");
33874        }
33875        // XXX TODO: make release build type configurable
33876        $command = 'msdev '.$dsp.' /MAKE "'.$pkg->getPackage(). ' - Release"';
33877
33878        $err = $this->_runCommand($command, array(&$this, 'msdevCallback'));
33879        if (PEAR::isError($err)) {
33880            return $err;
33881        }
33882
33883        // figure out the build platform and type
33884        $platform = 'Win32';
33885        $buildtype = 'Release';
33886        if (preg_match('/.*?'.$pkg->getPackage().'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) {
33887            $platform = $matches[1];
33888            $buildtype = $matches[2];
33889        }
33890
33891        if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/', $this->_lastline, $matches)) {
33892            if ($matches[2]) {
33893                // there were errors in the build
33894                return $this->raiseError("There were errors during compilation.");
33895            }
33896            $out = $matches[1];
33897        } else {
33898            return $this->raiseError("Did not understand the completion status returned from msdev.exe.");
33899        }
33900
33901        // msdev doesn't tell us the output directory :/
33902        // open the dsp, find /out and use that directory
33903        $dsptext = join(file($dsp),'');
33904
33905        // this regex depends on the build platform and type having been
33906        // correctly identified above.
33907        $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'.
33908                    $pkg->getPackage().'\s-\s'.
33909                    $platform.'\s'.
33910                    $buildtype.'").*?'.
33911                    '\/out:"(.*?)"/is';
33912
33913        if ($dsptext && preg_match($regex, $dsptext, $matches)) {
33914            // what we get back is a relative path to the output file itself.
33915            $outfile = realpath($matches[2]);
33916        } else {
33917            return $this->raiseError("Could not retrieve output information from $dsp.");
33918        }
33919        // realpath returns false if the file doesn't exist
33920        if ($outfile && copy($outfile, "$dir/$out")) {
33921            $outfile = "$dir/$out";
33922        }
33923
33924        $built_files[] = array(
33925            'file' => "$outfile",
33926            'php_api' => $this->php_api_version,
33927            'zend_mod_api' => $this->zend_module_api_no,
33928            'zend_ext_api' => $this->zend_extension_api_no,
33929            );
33930
33931        return $built_files;
33932    }
33933    // }}}
33934
33935    // {{{ msdevCallback()
33936    function msdevCallback($what, $data)
33937    {
33938        if (!$this->_firstline)
33939            $this->_firstline = $data;
33940        $this->_lastline = $data;
33941        call_user_func($this->current_callback, $what, $data);
33942    }
33943
33944    /**
33945     * @param string
33946     * @param string
33947     * @param array
33948     * @access private
33949     */
33950    function _harvestInstDir($dest_prefix, $dirname, &$built_files)
33951    {
33952        $d = opendir($dirname);
33953        if (!$d)
33954            return false;
33955
33956        $ret = true;
33957        while (($ent = readdir($d)) !== false) {
33958            if ($ent{0} == '.')
33959                continue;
33960
33961            $full = $dirname . DIRECTORY_SEPARATOR . $ent;
33962            if (is_dir($full)) {
33963                if (!$this->_harvestInstDir(
33964                        $dest_prefix . DIRECTORY_SEPARATOR . $ent,
33965                        $full, $built_files)) {
33966                    $ret = false;
33967                    break;
33968                }
33969            } else {
33970                $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent;
33971                $built_files[] = array(
33972                        'file' => $full,
33973                        'dest' => $dest,
33974                        'php_api' => $this->php_api_version,
33975                        'zend_mod_api' => $this->zend_module_api_no,
33976                        'zend_ext_api' => $this->zend_extension_api_no,
33977                        );
33978            }
33979        }
33980        closedir($d);
33981        return $ret;
33982    }
33983
33984    /**
33985     * Build an extension from source.  Runs "phpize" in the source
33986     * directory, but compiles in a temporary directory
33987     * (TMPDIR/pear-build-USER/PACKAGE-VERSION).
33988     *
33989     * @param string|PEAR_PackageFile_v* $descfile path to XML package description file, or
33990     *               a PEAR_PackageFile object
33991     *
33992     * @param mixed $callback callback function used to report output,
33993     * see PEAR_Builder::_runCommand for details
33994     *
33995     * @return array an array of associative arrays with built files,
33996     * format:
33997     * array( array( 'file' => '/path/to/ext.so',
33998     *               'php_api' => YYYYMMDD,
33999     *               'zend_mod_api' => YYYYMMDD,
34000     *               'zend_ext_api' => YYYYMMDD ),
34001     *        ... )
34002     *
34003     * @access public
34004     *
34005     * @see PEAR_Builder::_runCommand
34006     */
34007    function build($descfile, $callback = null)
34008    {
34009        if (preg_match('/(\\/|\\\\|^)([^\\/\\\\]+)?php(.+)?$/',
34010                       $this->config->get('php_bin'), $matches)) {
34011            if (isset($matches[2]) && strlen($matches[2]) &&
34012                trim($matches[2]) != trim($this->config->get('php_prefix'))) {
34013                $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') .
34014                           ' appears to have a prefix ' . $matches[2] . ', but' .
34015                           ' config variable php_prefix does not match');
34016            }
34017
34018            if (isset($matches[3]) && strlen($matches[3]) &&
34019                trim($matches[3]) != trim($this->config->get('php_suffix'))) {
34020                $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') .
34021                           ' appears to have a suffix ' . $matches[3] . ', but' .
34022                           ' config variable php_suffix does not match');
34023            }
34024        }
34025
34026        $this->current_callback = $callback;
34027        if (PEAR_OS == "Windows") {
34028            return $this->_build_win32($descfile, $callback);
34029        }
34030
34031        if (PEAR_OS != 'Unix') {
34032            return $this->raiseError("building extensions not supported on this platform");
34033        }
34034
34035        if (is_object($descfile)) {
34036            $pkg = $descfile;
34037            $descfile = $pkg->getPackageFile();
34038            if (is_a($pkg, 'PEAR_PackageFile_v1')) {
34039                $dir = dirname($descfile);
34040            } else {
34041                $dir = $pkg->_config->get('temp_dir') . '/' . $pkg->getName();
34042                // automatically delete at session end
34043                $this->addTempFile($dir);
34044            }
34045        } else {
34046            $pf = &new PEAR_PackageFile($this->config);
34047            $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
34048            if (PEAR::isError($pkg)) {
34049                return $pkg;
34050            }
34051            $dir = dirname($descfile);
34052        }
34053
34054        // Find config. outside of normal path - e.g. config.m4
34055        foreach (array_keys($pkg->getInstallationFileList()) as $item) {
34056          if (stristr(basename($item), 'config.m4') && dirname($item) != '.') {
34057            $dir .= DIRECTORY_SEPARATOR . dirname($item);
34058            break;
34059          }
34060        }
34061
34062        $old_cwd = getcwd();
34063        if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
34064            return $this->raiseError("could not chdir to $dir");
34065        }
34066
34067        $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
34068        if (is_dir($vdir)) {
34069            chdir($vdir);
34070        }
34071
34072        $dir = getcwd();
34073        $this->log(2, "building in $dir");
34074        putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH'));
34075        $err = $this->_runCommand($this->config->get('php_prefix')
34076                                . "phpize" .
34077                                $this->config->get('php_suffix'),
34078                                array(&$this, 'phpizeCallback'));
34079        if (PEAR::isError($err)) {
34080            return $err;
34081        }
34082
34083        if (!$err) {
34084            return $this->raiseError("`phpize' failed");
34085        }
34086
34087        // {{{ start of interactive part
34088        $configure_command = "$dir/configure";
34089        $configure_options = $pkg->getConfigureOptions();
34090        if ($configure_options) {
34091            foreach ($configure_options as $o) {
34092                $default = array_key_exists('default', $o) ? $o['default'] : null;
34093                list($r) = $this->ui->userDialog('build',
34094                                                 array($o['prompt']),
34095                                                 array('text'),
34096                                                 array($default));
34097                if (substr($o['name'], 0, 5) == 'with-' &&
34098                    ($r == 'yes' || $r == 'autodetect')) {
34099                    $configure_command .= " --$o[name]";
34100                } else {
34101                    $configure_command .= " --$o[name]=".trim($r);
34102                }
34103            }
34104        }
34105        // }}} end of interactive part
34106
34107        // FIXME make configurable
34108        if (!$user=getenv('USER')) {
34109            $user='defaultuser';
34110        }
34111
34112        $tmpdir = $this->config->get('temp_dir');
34113        $build_basedir = System::mktemp(' -t "' . $tmpdir . '" -d "pear-build-' . $user . '"');
34114        $build_dir = "$build_basedir/$vdir";
34115        $inst_dir = "$build_basedir/install-$vdir";
34116        $this->log(1, "building in $build_dir");
34117        if (is_dir($build_dir)) {
34118            System::rm(array('-rf', $build_dir));
34119        }
34120
34121        if (!System::mkDir(array('-p', $build_dir))) {
34122            return $this->raiseError("could not create build dir: $build_dir");
34123        }
34124
34125        $this->addTempFile($build_dir);
34126        if (!System::mkDir(array('-p', $inst_dir))) {
34127            return $this->raiseError("could not create temporary install dir: $inst_dir");
34128        }
34129        $this->addTempFile($inst_dir);
34130
34131        $make_command = getenv('MAKE') ? getenv('MAKE') : 'make';
34132
34133        $to_run = array(
34134            $configure_command,
34135            $make_command,
34136            "$make_command INSTALL_ROOT=\"$inst_dir\" install",
34137            "find \"$inst_dir\" | xargs ls -dils"
34138            );
34139        if (!file_exists($build_dir) || !is_dir($build_dir) || !chdir($build_dir)) {
34140            return $this->raiseError("could not chdir to $build_dir");
34141        }
34142        putenv('PHP_PEAR_VERSION=1.9.4');
34143        foreach ($to_run as $cmd) {
34144            $err = $this->_runCommand($cmd, $callback);
34145            if (PEAR::isError($err)) {
34146                chdir($old_cwd);
34147                return $err;
34148            }
34149            if (!$err) {
34150                chdir($old_cwd);
34151                return $this->raiseError("`$cmd' failed");
34152            }
34153        }
34154        if (!($dp = opendir("modules"))) {
34155            chdir($old_cwd);
34156            return $this->raiseError("no `modules' directory found");
34157        }
34158        $built_files = array();
34159        $prefix = exec($this->config->get('php_prefix')
34160                        . "php-config" .
34161                       $this->config->get('php_suffix') . " --prefix");
34162        $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files);
34163        chdir($old_cwd);
34164        return $built_files;
34165    }
34166
34167    /**
34168     * Message callback function used when running the "phpize"
34169     * program.  Extracts the API numbers used.  Ignores other message
34170     * types than "cmdoutput".
34171     *
34172     * @param string $what the type of message
34173     * @param mixed $data the message
34174     *
34175     * @return void
34176     *
34177     * @access public
34178     */
34179    function phpizeCallback($what, $data)
34180    {
34181        if ($what != 'cmdoutput') {
34182            return;
34183        }
34184        $this->log(1, rtrim($data));
34185        if (preg_match('/You should update your .aclocal.m4/', $data)) {
34186            return;
34187        }
34188        $matches = array();
34189        if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) {
34190            $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1]));
34191            $apino = (int)$matches[2];
34192            if (isset($this->$member)) {
34193                $this->$member = $apino;
34194                //$msg = sprintf("%-22s : %d", $matches[1], $apino);
34195                //$this->log(1, $msg);
34196            }
34197        }
34198    }
34199
34200    /**
34201     * Run an external command, using a message callback to report
34202     * output.  The command will be run through popen and output is
34203     * reported for every line with a "cmdoutput" message with the
34204     * line string, including newlines, as payload.
34205     *
34206     * @param string $command the command to run
34207     *
34208     * @param mixed $callback (optional) function to use as message
34209     * callback
34210     *
34211     * @return bool whether the command was successful (exit code 0
34212     * means success, any other means failure)
34213     *
34214     * @access private
34215     */
34216    function _runCommand($command, $callback = null)
34217    {
34218        $this->log(1, "running: $command");
34219        $pp = popen("$command 2>&1", "r");
34220        if (!$pp) {
34221            return $this->raiseError("failed to run `$command'");
34222        }
34223        if ($callback && $callback[0]->debug == 1) {
34224            $olddbg = $callback[0]->debug;
34225            $callback[0]->debug = 2;
34226        }
34227
34228        while ($line = fgets($pp, 1024)) {
34229            if ($callback) {
34230                call_user_func($callback, 'cmdoutput', $line);
34231            } else {
34232                $this->log(2, rtrim($line));
34233            }
34234        }
34235        if ($callback && isset($olddbg)) {
34236            $callback[0]->debug = $olddbg;
34237        }
34238
34239        $exitcode = is_resource($pp) ? pclose($pp) : -1;
34240        return ($exitcode == 0);
34241    }
34242
34243    function log($level, $msg)
34244    {
34245        if ($this->current_callback) {
34246            if ($this->debug >= $level) {
34247                call_user_func($this->current_callback, 'output', $msg);
34248            }
34249            return;
34250        }
34251        return PEAR_Common::log($level, $msg);
34252    }
34253}PEAR-1.9.4/PEAR/ChannelFile.php0000644000076500000240000014335311605156614014655 0ustar  helgistaff<?php
34254/**
34255 * PEAR_ChannelFile, the channel handling class
34256 *
34257 * PHP versions 4 and 5
34258 *
34259 * @category   pear
34260 * @package    PEAR
34261 * @author     Greg Beaver <cellog@php.net>
34262 * @copyright  1997-2009 The Authors
34263 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
34264 * @version    CVS: $Id: ChannelFile.php 313023 2011-07-06 19:17:11Z dufuz $
34265 * @link       http://pear.php.net/package/PEAR
34266 * @since      File available since Release 1.4.0a1
34267 */
34268
34269/**
34270 * Needed for error handling
34271 */
34272require_once 'PEAR/ErrorStack.php';
34273require_once 'PEAR/XMLParser.php';
34274require_once 'PEAR/Common.php';
34275
34276/**
34277 * Error code if the channel.xml <channel> tag does not contain a valid version
34278 */
34279define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1);
34280/**
34281 * Error code if the channel.xml <channel> tag version is not supported (version 1.0 is the only supported version,
34282 * currently
34283 */
34284define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2);
34285
34286/**
34287 * Error code if parsing is attempted with no xml extension
34288 */
34289define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3);
34290
34291/**
34292 * Error code if creating the xml parser resource fails
34293 */
34294define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4);
34295
34296/**
34297 * Error code used for all sax xml parsing errors
34298 */
34299define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5);
34300
34301/**#@+
34302 * Validation errors
34303 */
34304/**
34305 * Error code when channel name is missing
34306 */
34307define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6);
34308/**
34309 * Error code when channel name is invalid
34310 */
34311define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7);
34312/**
34313 * Error code when channel summary is missing
34314 */
34315define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8);
34316/**
34317 * Error code when channel summary is multi-line
34318 */
34319define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9);
34320/**
34321 * Error code when channel server is missing for protocol
34322 */
34323define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10);
34324/**
34325 * Error code when channel server is invalid for protocol
34326 */
34327define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11);
34328/**
34329 * Error code when a mirror name is invalid
34330 */
34331define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21);
34332/**
34333 * Error code when a mirror type is invalid
34334 */
34335define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22);
34336/**
34337 * Error code when an attempt is made to generate xml, but the parsed content is invalid
34338 */
34339define('PEAR_CHANNELFILE_ERROR_INVALID', 23);
34340/**
34341 * Error code when an empty package name validate regex is passed in
34342 */
34343define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24);
34344/**
34345 * Error code when a <function> tag has no version
34346 */
34347define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25);
34348/**
34349 * Error code when a <function> tag has no name
34350 */
34351define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26);
34352/**
34353 * Error code when a <validatepackage> tag has no name
34354 */
34355define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27);
34356/**
34357 * Error code when a <validatepackage> tag has no version attribute
34358 */
34359define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28);
34360/**
34361 * Error code when a mirror does not exist but is called for in one of the set*
34362 * methods.
34363 */
34364define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32);
34365/**
34366 * Error code when a server port is not numeric
34367 */
34368define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33);
34369/**
34370 * Error code when <static> contains no version attribute
34371 */
34372define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34);
34373/**
34374 * Error code when <baseurl> contains no type attribute in a <rest> protocol definition
34375 */
34376define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35);
34377/**
34378 * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel
34379 */
34380define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36);
34381/**
34382 * Error code when ssl attribute is present and is not "yes"
34383 */
34384define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37);
34385/**#@-*/
34386
34387/**
34388 * Mirror types allowed.  Currently only internet servers are recognized.
34389 */
34390$GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] =  array('server');
34391
34392
34393/**
34394 * The Channel handling class
34395 *
34396 * @category   pear
34397 * @package    PEAR
34398 * @author     Greg Beaver <cellog@php.net>
34399 * @copyright  1997-2009 The Authors
34400 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
34401 * @version    Release: 1.9.4
34402 * @link       http://pear.php.net/package/PEAR
34403 * @since      Class available since Release 1.4.0a1
34404 */
34405class PEAR_ChannelFile
34406{
34407    /**
34408     * @access private
34409     * @var PEAR_ErrorStack
34410     * @access private
34411     */
34412    var $_stack;
34413
34414    /**
34415     * Supported channel.xml versions, for parsing
34416     * @var array
34417     * @access private
34418     */
34419    var $_supportedVersions = array('1.0');
34420
34421    /**
34422     * Parsed channel information
34423     * @var array
34424     * @access private
34425     */
34426    var $_channelInfo;
34427
34428    /**
34429     * index into the subchannels array, used for parsing xml
34430     * @var int
34431     * @access private
34432     */
34433    var $_subchannelIndex;
34434
34435    /**
34436     * index into the mirrors array, used for parsing xml
34437     * @var int
34438     * @access private
34439     */
34440    var $_mirrorIndex;
34441
34442    /**
34443     * Flag used to determine the validity of parsed content
34444     * @var boolean
34445     * @access private
34446     */
34447    var $_isValid = false;
34448
34449    function PEAR_ChannelFile()
34450    {
34451        $this->_stack = &new PEAR_ErrorStack('PEAR_ChannelFile');
34452        $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
34453        $this->_isValid = false;
34454    }
34455
34456    /**
34457     * @return array
34458     * @access protected
34459     */
34460    function _getErrorMessage()
34461    {
34462        return
34463            array(
34464                PEAR_CHANNELFILE_ERROR_INVALID_VERSION =>
34465                    'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%',
34466                PEAR_CHANNELFILE_ERROR_NO_VERSION =>
34467                    'No version number found in <channel> tag',
34468                PEAR_CHANNELFILE_ERROR_NO_XML_EXT =>
34469                    '%error%',
34470                PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER =>
34471                    'Unable to create XML parser',
34472                PEAR_CHANNELFILE_ERROR_PARSER_ERROR =>
34473                    '%error%',
34474                PEAR_CHANNELFILE_ERROR_NO_NAME =>
34475                    'Missing channel name',
34476                PEAR_CHANNELFILE_ERROR_INVALID_NAME =>
34477                    'Invalid channel %tag% "%name%"',
34478                PEAR_CHANNELFILE_ERROR_NO_SUMMARY =>
34479                    'Missing channel summary',
34480                PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY =>
34481                    'Channel summary should be on one line, but is multi-line',
34482                PEAR_CHANNELFILE_ERROR_NO_HOST =>
34483                    'Missing channel server for %type% server',
34484                PEAR_CHANNELFILE_ERROR_INVALID_HOST =>
34485                    'Server name "%server%" is invalid for %type% server',
34486                PEAR_CHANNELFILE_ERROR_INVALID_MIRROR =>
34487                    'Invalid mirror name "%name%", mirror type %type%',
34488                PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE =>
34489                    'Invalid mirror type "%type%"',
34490                PEAR_CHANNELFILE_ERROR_INVALID =>
34491                    'Cannot generate xml, contents are invalid',
34492                PEAR_CHANNELFILE_ERROR_EMPTY_REGEX =>
34493                    'packagenameregex cannot be empty',
34494                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION =>
34495                    '%parent% %protocol% function has no version',
34496                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME =>
34497                    '%parent% %protocol% function has no name',
34498                PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE =>
34499                    '%parent% rest baseurl has no type',
34500                PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME =>
34501                    'Validation package has no name in <validatepackage> tag',
34502                PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION =>
34503                    'Validation package "%package%" has no version',
34504                PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND =>
34505                    'Mirror "%mirror%" does not exist',
34506                PEAR_CHANNELFILE_ERROR_INVALID_PORT =>
34507                    'Port "%port%" must be numeric',
34508                PEAR_CHANNELFILE_ERROR_NO_STATICVERSION =>
34509                    '<static> tag must contain version attribute',
34510                PEAR_CHANNELFILE_URI_CANT_MIRROR =>
34511                    'The __uri pseudo-channel cannot have mirrors',
34512                PEAR_CHANNELFILE_ERROR_INVALID_SSL =>
34513                    '%server% has invalid ssl attribute "%ssl%" can only be yes or not present',
34514            );
34515    }
34516
34517    /**
34518     * @param string contents of package.xml file
34519     * @return bool success of parsing
34520     */
34521    function fromXmlString($data)
34522    {
34523        if (preg_match('/<channel\s+version="([0-9]+\.[0-9]+)"/', $data, $channelversion)) {
34524            if (!in_array($channelversion[1], $this->_supportedVersions)) {
34525                $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error',
34526                    array('version' => $channelversion[1]));
34527                return false;
34528            }
34529            $parser = new PEAR_XMLParser;
34530            $result = $parser->parse($data);
34531            if ($result !== true) {
34532                if ($result->getCode() == 1) {
34533                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error',
34534                        array('error' => $result->getMessage()));
34535                } else {
34536                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error');
34537                }
34538                return false;
34539            }
34540            $this->_channelInfo = $parser->getData();
34541            return true;
34542        } else {
34543            $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data));
34544            return false;
34545        }
34546    }
34547
34548    /**
34549     * @return array
34550     */
34551    function toArray()
34552    {
34553        if (!$this->_isValid && !$this->validate()) {
34554            return false;
34555        }
34556        return $this->_channelInfo;
34557    }
34558
34559    /**
34560     * @param array
34561     * @static
34562     * @return PEAR_ChannelFile|false false if invalid
34563     */
34564    function &fromArray($data, $compatibility = false, $stackClass = 'PEAR_ErrorStack')
34565    {
34566        $a = new PEAR_ChannelFile($compatibility, $stackClass);
34567        $a->_fromArray($data);
34568        if (!$a->validate()) {
34569            $a = false;
34570            return $a;
34571        }
34572        return $a;
34573    }
34574
34575    /**
34576     * Unlike {@link fromArray()} this does not do any validation
34577     * @param array
34578     * @static
34579     * @return PEAR_ChannelFile
34580     */
34581    function &fromArrayWithErrors($data, $compatibility = false,
34582                                  $stackClass = 'PEAR_ErrorStack')
34583    {
34584        $a = new PEAR_ChannelFile($compatibility, $stackClass);
34585        $a->_fromArray($data);
34586        return $a;
34587    }
34588
34589    /**
34590     * @param array
34591     * @access private
34592     */
34593    function _fromArray($data)
34594    {
34595        $this->_channelInfo = $data;
34596    }
34597
34598    /**
34599     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
34600     * @param boolean determines whether to purge the error stack after retrieving
34601     * @return array
34602     */
34603    function getErrors($purge = false)
34604    {
34605        return $this->_stack->getErrors($purge);
34606    }
34607
34608    /**
34609     * Unindent given string (?)
34610     *
34611     * @param string $str The string that has to be unindented.
34612     * @return string
34613     * @access private
34614     */
34615    function _unIndent($str)
34616    {
34617        // remove leading newlines
34618        $str = preg_replace('/^[\r\n]+/', '', $str);
34619        // find whitespace at the beginning of the first line
34620        $indent_len = strspn($str, " \t");
34621        $indent = substr($str, 0, $indent_len);
34622        $data = '';
34623        // remove the same amount of whitespace from following lines
34624        foreach (explode("\n", $str) as $line) {
34625            if (substr($line, 0, $indent_len) == $indent) {
34626                $data .= substr($line, $indent_len) . "\n";
34627            }
34628        }
34629        return $data;
34630    }
34631
34632    /**
34633     * Parse a channel.xml file.  Expects the name of
34634     * a channel xml file as input.
34635     *
34636     * @param string  $descfile  name of channel xml file
34637     * @return bool success of parsing
34638     */
34639    function fromXmlFile($descfile)
34640    {
34641        if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) ||
34642             (!$fp = fopen($descfile, 'r'))) {
34643            require_once 'PEAR.php';
34644            return PEAR::raiseError("Unable to open $descfile");
34645        }
34646
34647        // read the whole thing so we only get one cdata callback
34648        // for each block of cdata
34649        fclose($fp);
34650        $data = file_get_contents($descfile);
34651        return $this->fromXmlString($data);
34652    }
34653
34654    /**
34655     * Parse channel information from different sources
34656     *
34657     * This method is able to extract information about a channel
34658     * from an .xml file or a string
34659     *
34660     * @access public
34661     * @param  string Filename of the source or the source itself
34662     * @return bool
34663     */
34664    function fromAny($info)
34665    {
34666        if (is_string($info) && file_exists($info) && strlen($info) < 255) {
34667            $tmp = substr($info, -4);
34668            if ($tmp == '.xml') {
34669                $info = $this->fromXmlFile($info);
34670            } else {
34671                $fp = fopen($info, "r");
34672                $test = fread($fp, 5);
34673                fclose($fp);
34674                if ($test == "<?xml") {
34675                    $info = $this->fromXmlFile($info);
34676                }
34677            }
34678            if (PEAR::isError($info)) {
34679                require_once 'PEAR.php';
34680                return PEAR::raiseError($info);
34681            }
34682        }
34683        if (is_string($info)) {
34684            $info = $this->fromXmlString($info);
34685        }
34686        return $info;
34687    }
34688
34689    /**
34690     * Return an XML document based on previous parsing and modifications
34691     *
34692     * @return string XML data
34693     *
34694     * @access public
34695     */
34696    function toXml()
34697    {
34698        if (!$this->_isValid && !$this->validate()) {
34699            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID);
34700            return false;
34701        }
34702        if (!isset($this->_channelInfo['attribs']['version'])) {
34703            $this->_channelInfo['attribs']['version'] = '1.0';
34704        }
34705        $channelInfo = $this->_channelInfo;
34706        $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
34707        $ret .= "<channel version=\"" .
34708            $channelInfo['attribs']['version'] . "\" xmlns=\"http://pear.php.net/channel-1.0\"
34709  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
34710  xsi:schemaLocation=\"http://pear.php.net/dtd/channel-"
34711            . $channelInfo['attribs']['version'] . " http://pear.php.net/dtd/channel-" .
34712            $channelInfo['attribs']['version'] . ".xsd\">
34713 <name>$channelInfo[name]</name>
34714 <summary>" . htmlspecialchars($channelInfo['summary'])."</summary>
34715";
34716        if (isset($channelInfo['suggestedalias'])) {
34717            $ret .= ' <suggestedalias>' . $channelInfo['suggestedalias'] . "</suggestedalias>\n";
34718        }
34719        if (isset($channelInfo['validatepackage'])) {
34720            $ret .= ' <validatepackage version="' .
34721                $channelInfo['validatepackage']['attribs']['version']. '">' .
34722                htmlspecialchars($channelInfo['validatepackage']['_content']) .
34723                "</validatepackage>\n";
34724        }
34725        $ret .= " <servers>\n";
34726        $ret .= '  <primary';
34727        if (isset($channelInfo['servers']['primary']['attribs']['ssl'])) {
34728            $ret .= ' ssl="' . $channelInfo['servers']['primary']['attribs']['ssl'] . '"';
34729        }
34730        if (isset($channelInfo['servers']['primary']['attribs']['port'])) {
34731            $ret .= ' port="' . $channelInfo['servers']['primary']['attribs']['port'] . '"';
34732        }
34733        $ret .= ">\n";
34734        if (isset($channelInfo['servers']['primary']['rest'])) {
34735            $ret .= $this->_makeRestXml($channelInfo['servers']['primary']['rest'], '   ');
34736        }
34737        $ret .= "  </primary>\n";
34738        if (isset($channelInfo['servers']['mirror'])) {
34739            $ret .= $this->_makeMirrorsXml($channelInfo);
34740        }
34741        $ret .= " </servers>\n";
34742        $ret .= "</channel>";
34743        return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret));
34744    }
34745
34746    /**
34747     * Generate the <rest> tag
34748     * @access private
34749     */
34750    function _makeRestXml($info, $indent)
34751    {
34752        $ret = $indent . "<rest>\n";
34753        if (isset($info['baseurl']) && !isset($info['baseurl'][0])) {
34754            $info['baseurl'] = array($info['baseurl']);
34755        }
34756
34757        if (isset($info['baseurl'])) {
34758            foreach ($info['baseurl'] as $url) {
34759                $ret .= "$indent <baseurl type=\"" . $url['attribs']['type'] . "\"";
34760                $ret .= ">" . $url['_content'] . "</baseurl>\n";
34761            }
34762        }
34763        $ret .= $indent . "</rest>\n";
34764        return $ret;
34765    }
34766
34767    /**
34768     * Generate the <mirrors> tag
34769     * @access private
34770     */
34771    function _makeMirrorsXml($channelInfo)
34772    {
34773        $ret = "";
34774        if (!isset($channelInfo['servers']['mirror'][0])) {
34775            $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']);
34776        }
34777        foreach ($channelInfo['servers']['mirror'] as $mirror) {
34778            $ret .= '  <mirror host="' . $mirror['attribs']['host'] . '"';
34779            if (isset($mirror['attribs']['port'])) {
34780                $ret .= ' port="' . $mirror['attribs']['port'] . '"';
34781            }
34782            if (isset($mirror['attribs']['ssl'])) {
34783                $ret .= ' ssl="' . $mirror['attribs']['ssl'] . '"';
34784            }
34785            $ret .= ">\n";
34786            if (isset($mirror['rest'])) {
34787                if (isset($mirror['rest'])) {
34788                    $ret .= $this->_makeRestXml($mirror['rest'], '   ');
34789                }
34790                $ret .= "  </mirror>\n";
34791            } else {
34792                $ret .= "/>\n";
34793            }
34794        }
34795        return $ret;
34796    }
34797
34798    /**
34799     * Generate the <functions> tag
34800     * @access private
34801     */
34802    function _makeFunctionsXml($functions, $indent, $rest = false)
34803    {
34804        $ret = '';
34805        if (!isset($functions[0])) {
34806            $functions = array($functions);
34807        }
34808        foreach ($functions as $function) {
34809            $ret .= "$indent<function version=\"" . $function['attribs']['version'] . "\"";
34810            if ($rest) {
34811                $ret .= ' uri="' . $function['attribs']['uri'] . '"';
34812            }
34813            $ret .= ">" . $function['_content'] . "</function>\n";
34814        }
34815        return $ret;
34816    }
34817
34818    /**
34819     * Validation error.  Also marks the object contents as invalid
34820     * @param error code
34821     * @param array error information
34822     * @access private
34823     */
34824    function _validateError($code, $params = array())
34825    {
34826        $this->_stack->push($code, 'error', $params);
34827        $this->_isValid = false;
34828    }
34829
34830    /**
34831     * Validation warning.  Does not mark the object contents invalid.
34832     * @param error code
34833     * @param array error information
34834     * @access private
34835     */
34836    function _validateWarning($code, $params = array())
34837    {
34838        $this->_stack->push($code, 'warning', $params);
34839    }
34840
34841    /**
34842     * Validate parsed file.
34843     *
34844     * @access public
34845     * @return boolean
34846     */
34847    function validate()
34848    {
34849        $this->_isValid = true;
34850        $info = $this->_channelInfo;
34851        if (empty($info['name'])) {
34852            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME);
34853        } elseif (!$this->validChannelServer($info['name'])) {
34854            if ($info['name'] != '__uri') {
34855                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name',
34856                    'name' => $info['name']));
34857            }
34858        }
34859        if (empty($info['summary'])) {
34860            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
34861        } elseif (strpos(trim($info['summary']), "\n") !== false) {
34862            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
34863                array('summary' => $info['summary']));
34864        }
34865        if (isset($info['suggestedalias'])) {
34866            if (!$this->validChannelServer($info['suggestedalias'])) {
34867                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
34868                    array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias']));
34869            }
34870        }
34871        if (isset($info['localalias'])) {
34872            if (!$this->validChannelServer($info['localalias'])) {
34873                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
34874                    array('tag' => 'localalias', 'name' =>$info['localalias']));
34875            }
34876        }
34877        if (isset($info['validatepackage'])) {
34878            if (!isset($info['validatepackage']['_content'])) {
34879                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME);
34880            }
34881            if (!isset($info['validatepackage']['attribs']['version'])) {
34882                $content = isset($info['validatepackage']['_content']) ?
34883                    $info['validatepackage']['_content'] :
34884                    null;
34885                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION,
34886                    array('package' => $content));
34887            }
34888        }
34889
34890        if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['port']) &&
34891              !is_numeric($info['servers']['primary']['attribs']['port'])) {
34892            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT,
34893                array('port' => $info['servers']['primary']['attribs']['port']));
34894        }
34895
34896        if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['ssl']) &&
34897              $info['servers']['primary']['attribs']['ssl'] != 'yes') {
34898            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
34899                array('ssl' => $info['servers']['primary']['attribs']['ssl'],
34900                    'server' => $info['name']));
34901        }
34902
34903        if (isset($info['servers']['primary']['rest']) &&
34904              isset($info['servers']['primary']['rest']['baseurl'])) {
34905            $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']);
34906        }
34907        if (isset($info['servers']['mirror'])) {
34908            if ($this->_channelInfo['name'] == '__uri') {
34909                $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR);
34910            }
34911            if (!isset($info['servers']['mirror'][0])) {
34912                $info['servers']['mirror'] = array($info['servers']['mirror']);
34913            }
34914            foreach ($info['servers']['mirror'] as $mirror) {
34915                if (!isset($mirror['attribs']['host'])) {
34916                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST,
34917                      array('type' => 'mirror'));
34918                } elseif (!$this->validChannelServer($mirror['attribs']['host'])) {
34919                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST,
34920                        array('server' => $mirror['attribs']['host'], 'type' => 'mirror'));
34921                }
34922                if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') {
34923                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
34924                        array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host']));
34925                }
34926                if (isset($mirror['rest'])) {
34927                    $this->_validateFunctions('rest', $mirror['rest']['baseurl'],
34928                        $mirror['attribs']['host']);
34929                }
34930            }
34931        }
34932        return $this->_isValid;
34933    }
34934
34935    /**
34936     * @param string  rest - protocol name this function applies to
34937     * @param array the functions
34938     * @param string the name of the parent element (mirror name, for instance)
34939     */
34940    function _validateFunctions($protocol, $functions, $parent = '')
34941    {
34942        if (!isset($functions[0])) {
34943            $functions = array($functions);
34944        }
34945
34946        foreach ($functions as $function) {
34947            if (!isset($function['_content']) || empty($function['_content'])) {
34948                $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME,
34949                    array('parent' => $parent, 'protocol' => $protocol));
34950            }
34951
34952            if ($protocol == 'rest') {
34953                if (!isset($function['attribs']['type']) ||
34954                      empty($function['attribs']['type'])) {
34955                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE,
34956                        array('parent' => $parent, 'protocol' => $protocol));
34957                }
34958            } else {
34959                if (!isset($function['attribs']['version']) ||
34960                      empty($function['attribs']['version'])) {
34961                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION,
34962                        array('parent' => $parent, 'protocol' => $protocol));
34963                }
34964            }
34965        }
34966    }
34967
34968    /**
34969     * Test whether a string contains a valid channel server.
34970     * @param string $ver the package version to test
34971     * @return bool
34972     */
34973    function validChannelServer($server)
34974    {
34975        if ($server == '__uri') {
34976            return true;
34977        }
34978        return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server);
34979    }
34980
34981    /**
34982     * @return string|false
34983     */
34984    function getName()
34985    {
34986        if (isset($this->_channelInfo['name'])) {
34987            return $this->_channelInfo['name'];
34988        }
34989
34990        return false;
34991    }
34992
34993    /**
34994     * @return string|false
34995     */
34996    function getServer()
34997    {
34998        if (isset($this->_channelInfo['name'])) {
34999            return $this->_channelInfo['name'];
35000        }
35001
35002        return false;
35003    }
35004
35005    /**
35006     * @return int|80 port number to connect to
35007     */
35008    function getPort($mirror = false)
35009    {
35010        if ($mirror) {
35011            if ($mir = $this->getMirror($mirror)) {
35012                if (isset($mir['attribs']['port'])) {
35013                    return $mir['attribs']['port'];
35014                }
35015
35016                if ($this->getSSL($mirror)) {
35017                    return 443;
35018                }
35019
35020                return 80;
35021            }
35022
35023            return false;
35024        }
35025
35026        if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) {
35027            return $this->_channelInfo['servers']['primary']['attribs']['port'];
35028        }
35029
35030        if ($this->getSSL()) {
35031            return 443;
35032        }
35033
35034        return 80;
35035    }
35036
35037    /**
35038     * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel
35039     */
35040    function getSSL($mirror = false)
35041    {
35042        if ($mirror) {
35043            if ($mir = $this->getMirror($mirror)) {
35044                if (isset($mir['attribs']['ssl'])) {
35045                    return true;
35046                }
35047
35048                return false;
35049            }
35050
35051            return false;
35052        }
35053
35054        if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
35055            return true;
35056        }
35057
35058        return false;
35059    }
35060
35061    /**
35062     * @return string|false
35063     */
35064    function getSummary()
35065    {
35066        if (isset($this->_channelInfo['summary'])) {
35067            return $this->_channelInfo['summary'];
35068        }
35069
35070        return false;
35071    }
35072
35073    /**
35074     * @param string protocol type
35075     * @param string Mirror name
35076     * @return array|false
35077     */
35078    function getFunctions($protocol, $mirror = false)
35079    {
35080        if ($this->getName() == '__uri') {
35081            return false;
35082        }
35083
35084        $function = $protocol == 'rest' ? 'baseurl' : 'function';
35085        if ($mirror) {
35086            if ($mir = $this->getMirror($mirror)) {
35087                if (isset($mir[$protocol][$function])) {
35088                    return $mir[$protocol][$function];
35089                }
35090            }
35091
35092            return false;
35093        }
35094
35095        if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) {
35096            return $this->_channelInfo['servers']['primary'][$protocol][$function];
35097        }
35098
35099        return false;
35100    }
35101
35102    /**
35103     * @param string Protocol type
35104     * @param string Function name (null to return the
35105     *               first protocol of the type requested)
35106     * @param string Mirror name, if any
35107     * @return array
35108     */
35109     function getFunction($type, $name = null, $mirror = false)
35110     {
35111        $protocols = $this->getFunctions($type, $mirror);
35112        if (!$protocols) {
35113            return false;
35114        }
35115
35116        foreach ($protocols as $protocol) {
35117            if ($name === null) {
35118                return $protocol;
35119            }
35120
35121            if ($protocol['_content'] != $name) {
35122                continue;
35123            }
35124
35125            return $protocol;
35126        }
35127
35128        return false;
35129     }
35130
35131    /**
35132     * @param string protocol type
35133     * @param string protocol name
35134     * @param string version
35135     * @param string mirror name
35136     * @return boolean
35137     */
35138    function supports($type, $name = null, $mirror = false, $version = '1.0')
35139    {
35140        $protocols = $this->getFunctions($type, $mirror);
35141        if (!$protocols) {
35142            return false;
35143        }
35144
35145        foreach ($protocols as $protocol) {
35146            if ($protocol['attribs']['version'] != $version) {
35147                continue;
35148            }
35149
35150            if ($name === null) {
35151                return true;
35152            }
35153
35154            if ($protocol['_content'] != $name) {
35155                continue;
35156            }
35157
35158            return true;
35159        }
35160
35161        return false;
35162    }
35163
35164    /**
35165     * Determines whether a channel supports Representational State Transfer (REST) protocols
35166     * for retrieving channel information
35167     * @param string
35168     * @return bool
35169     */
35170    function supportsREST($mirror = false)
35171    {
35172        if ($mirror == $this->_channelInfo['name']) {
35173            $mirror = false;
35174        }
35175
35176        if ($mirror) {
35177            if ($mir = $this->getMirror($mirror)) {
35178                return isset($mir['rest']);
35179            }
35180
35181            return false;
35182        }
35183
35184        return isset($this->_channelInfo['servers']['primary']['rest']);
35185    }
35186
35187    /**
35188     * Get the URL to access a base resource.
35189     *
35190     * Hyperlinks in the returned xml will be used to retrieve the proper information
35191     * needed.  This allows extreme extensibility and flexibility in implementation
35192     * @param string Resource Type to retrieve
35193     */
35194    function getBaseURL($resourceType, $mirror = false)
35195    {
35196        if ($mirror == $this->_channelInfo['name']) {
35197            $mirror = false;
35198        }
35199
35200        if ($mirror) {
35201            $mir = $this->getMirror($mirror);
35202            if (!$mir) {
35203                return false;
35204            }
35205
35206            $rest = $mir['rest'];
35207        } else {
35208            $rest = $this->_channelInfo['servers']['primary']['rest'];
35209        }
35210
35211        if (!isset($rest['baseurl'][0])) {
35212            $rest['baseurl'] = array($rest['baseurl']);
35213        }
35214
35215        foreach ($rest['baseurl'] as $baseurl) {
35216            if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) {
35217                return $baseurl['_content'];
35218            }
35219        }
35220
35221        return false;
35222    }
35223
35224    /**
35225     * Since REST does not implement RPC, provide this as a logical wrapper around
35226     * resetFunctions for REST
35227     * @param string|false mirror name, if any
35228     */
35229    function resetREST($mirror = false)
35230    {
35231        return $this->resetFunctions('rest', $mirror);
35232    }
35233
35234    /**
35235     * Empty all protocol definitions
35236     * @param string protocol type
35237     * @param string|false mirror name, if any
35238     */
35239    function resetFunctions($type, $mirror = false)
35240    {
35241        if ($mirror) {
35242            if (isset($this->_channelInfo['servers']['mirror'])) {
35243                $mirrors = $this->_channelInfo['servers']['mirror'];
35244                if (!isset($mirrors[0])) {
35245                    $mirrors = array($mirrors);
35246                }
35247
35248                foreach ($mirrors as $i => $mir) {
35249                    if ($mir['attribs']['host'] == $mirror) {
35250                        if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) {
35251                            unset($this->_channelInfo['servers']['mirror'][$i][$type]);
35252                        }
35253
35254                        return true;
35255                    }
35256                }
35257
35258                return false;
35259            }
35260
35261            return false;
35262        }
35263
35264        if (isset($this->_channelInfo['servers']['primary'][$type])) {
35265            unset($this->_channelInfo['servers']['primary'][$type]);
35266        }
35267
35268        return true;
35269    }
35270
35271    /**
35272     * Set a channel's protocols to the protocols supported by pearweb
35273     */
35274    function setDefaultPEARProtocols($version = '1.0', $mirror = false)
35275    {
35276        switch ($version) {
35277            case '1.0' :
35278                $this->resetREST($mirror);
35279
35280                if (!isset($this->_channelInfo['servers'])) {
35281                    $this->_channelInfo['servers'] = array('primary' =>
35282                        array('rest' => array()));
35283                } elseif (!isset($this->_channelInfo['servers']['primary'])) {
35284                    $this->_channelInfo['servers']['primary'] = array('rest' => array());
35285                }
35286
35287                return true;
35288            break;
35289            default :
35290                return false;
35291            break;
35292        }
35293    }
35294
35295    /**
35296     * @return array
35297     */
35298    function getMirrors()
35299    {
35300        if (isset($this->_channelInfo['servers']['mirror'])) {
35301            $mirrors = $this->_channelInfo['servers']['mirror'];
35302            if (!isset($mirrors[0])) {
35303                $mirrors = array($mirrors);
35304            }
35305
35306            return $mirrors;
35307        }
35308
35309        return array();
35310    }
35311
35312    /**
35313     * Get the unserialized XML representing a mirror
35314     * @return array|false
35315     */
35316    function getMirror($server)
35317    {
35318        foreach ($this->getMirrors() as $mirror) {
35319            if ($mirror['attribs']['host'] == $server) {
35320                return $mirror;
35321            }
35322        }
35323
35324        return false;
35325    }
35326
35327    /**
35328     * @param string
35329     * @return string|false
35330     * @error PEAR_CHANNELFILE_ERROR_NO_NAME
35331     * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME
35332     */
35333    function setName($name)
35334    {
35335        return $this->setServer($name);
35336    }
35337
35338    /**
35339     * Set the socket number (port) that is used to connect to this channel
35340     * @param integer
35341     * @param string|false name of the mirror server, or false for the primary
35342     */
35343    function setPort($port, $mirror = false)
35344    {
35345        if ($mirror) {
35346            if (!isset($this->_channelInfo['servers']['mirror'])) {
35347                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
35348                    array('mirror' => $mirror));
35349                return false;
35350            }
35351
35352            if (isset($this->_channelInfo['servers']['mirror'][0])) {
35353                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
35354                    if ($mirror == $mir['attribs']['host']) {
35355                        $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port;
35356                        return true;
35357                    }
35358                }
35359
35360                return false;
35361            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
35362                $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port;
35363                $this->_isValid = false;
35364                return true;
35365            }
35366        }
35367
35368        $this->_channelInfo['servers']['primary']['attribs']['port'] = $port;
35369        $this->_isValid = false;
35370        return true;
35371    }
35372
35373    /**
35374     * Set the socket number (port) that is used to connect to this channel
35375     * @param bool Determines whether to turn on SSL support or turn it off
35376     * @param string|false name of the mirror server, or false for the primary
35377     */
35378    function setSSL($ssl = true, $mirror = false)
35379    {
35380        if ($mirror) {
35381            if (!isset($this->_channelInfo['servers']['mirror'])) {
35382                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
35383                    array('mirror' => $mirror));
35384                return false;
35385            }
35386
35387            if (isset($this->_channelInfo['servers']['mirror'][0])) {
35388                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
35389                    if ($mirror == $mir['attribs']['host']) {
35390                        if (!$ssl) {
35391                            if (isset($this->_channelInfo['servers']['mirror'][$i]
35392                                  ['attribs']['ssl'])) {
35393                                unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']);
35394                            }
35395                        } else {
35396                            $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes';
35397                        }
35398
35399                        return true;
35400                    }
35401                }
35402
35403                return false;
35404            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
35405                if (!$ssl) {
35406                    if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) {
35407                        unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']);
35408                    }
35409                } else {
35410                    $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes';
35411                }
35412
35413                $this->_isValid = false;
35414                return true;
35415            }
35416        }
35417
35418        if ($ssl) {
35419            $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes';
35420        } else {
35421            if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
35422                unset($this->_channelInfo['servers']['primary']['attribs']['ssl']);
35423            }
35424        }
35425
35426        $this->_isValid = false;
35427        return true;
35428    }
35429
35430    /**
35431     * @param string
35432     * @return string|false
35433     * @error PEAR_CHANNELFILE_ERROR_NO_SERVER
35434     * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER
35435     */
35436    function setServer($server, $mirror = false)
35437    {
35438        if (empty($server)) {
35439            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER);
35440            return false;
35441        } elseif (!$this->validChannelServer($server)) {
35442            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
35443                array('tag' => 'name', 'name' => $server));
35444            return false;
35445        }
35446
35447        if ($mirror) {
35448            $found = false;
35449            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
35450                if ($mirror == $mir['attribs']['host']) {
35451                    $found = true;
35452                    break;
35453                }
35454            }
35455
35456            if (!$found) {
35457                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
35458                    array('mirror' => $mirror));
35459                return false;
35460            }
35461
35462            $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server;
35463            return true;
35464        }
35465
35466        $this->_channelInfo['name'] = $server;
35467        return true;
35468    }
35469
35470    /**
35471     * @param string
35472     * @return boolean success
35473     * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY
35474     * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY
35475     */
35476    function setSummary($summary)
35477    {
35478        if (empty($summary)) {
35479            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
35480            return false;
35481        } elseif (strpos(trim($summary), "\n") !== false) {
35482            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
35483                array('summary' => $summary));
35484        }
35485
35486        $this->_channelInfo['summary'] = $summary;
35487        return true;
35488    }
35489
35490    /**
35491     * @param string
35492     * @param boolean determines whether the alias is in channel.xml or local
35493     * @return boolean success
35494     */
35495    function setAlias($alias, $local = false)
35496    {
35497        if (!$this->validChannelServer($alias)) {
35498            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
35499                array('tag' => 'suggestedalias', 'name' => $alias));
35500            return false;
35501        }
35502
35503        if ($local) {
35504            $this->_channelInfo['localalias'] = $alias;
35505        } else {
35506            $this->_channelInfo['suggestedalias'] = $alias;
35507        }
35508
35509        return true;
35510    }
35511
35512    /**
35513     * @return string
35514     */
35515    function getAlias()
35516    {
35517        if (isset($this->_channelInfo['localalias'])) {
35518            return $this->_channelInfo['localalias'];
35519        }
35520        if (isset($this->_channelInfo['suggestedalias'])) {
35521            return $this->_channelInfo['suggestedalias'];
35522        }
35523        if (isset($this->_channelInfo['name'])) {
35524            return $this->_channelInfo['name'];
35525        }
35526        return '';
35527    }
35528
35529    /**
35530     * Set the package validation object if it differs from PEAR's default
35531     * The class must be includeable via changing _ in the classname to path separator,
35532     * but no checking of this is made.
35533     * @param string|false pass in false to reset to the default packagename regex
35534     * @return boolean success
35535     */
35536    function setValidationPackage($validateclass, $version)
35537    {
35538        if (empty($validateclass)) {
35539            unset($this->_channelInfo['validatepackage']);
35540        }
35541        $this->_channelInfo['validatepackage'] = array('_content' => $validateclass);
35542        $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version);
35543    }
35544
35545    /**
35546     * Add a protocol to the provides section
35547     * @param string protocol type
35548     * @param string protocol version
35549     * @param string protocol name, if any
35550     * @param string mirror name, if this is a mirror's protocol
35551     * @return bool
35552     */
35553    function addFunction($type, $version, $name = '', $mirror = false)
35554    {
35555        if ($mirror) {
35556            return $this->addMirrorFunction($mirror, $type, $version, $name);
35557        }
35558
35559        $set = array('attribs' => array('version' => $version), '_content' => $name);
35560        if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) {
35561            if (!isset($this->_channelInfo['servers'])) {
35562                $this->_channelInfo['servers'] = array('primary' =>
35563                    array($type => array()));
35564            } elseif (!isset($this->_channelInfo['servers']['primary'])) {
35565                $this->_channelInfo['servers']['primary'] = array($type => array());
35566            }
35567
35568            $this->_channelInfo['servers']['primary'][$type]['function'] = $set;
35569            $this->_isValid = false;
35570            return true;
35571        } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) {
35572            $this->_channelInfo['servers']['primary'][$type]['function'] = array(
35573                $this->_channelInfo['servers']['primary'][$type]['function']);
35574        }
35575
35576        $this->_channelInfo['servers']['primary'][$type]['function'][] = $set;
35577        return true;
35578    }
35579    /**
35580     * Add a protocol to a mirror's provides section
35581     * @param string mirror name (server)
35582     * @param string protocol type
35583     * @param string protocol version
35584     * @param string protocol name, if any
35585     */
35586    function addMirrorFunction($mirror, $type, $version, $name = '')
35587    {
35588        if (!isset($this->_channelInfo['servers']['mirror'])) {
35589            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
35590                array('mirror' => $mirror));
35591            return false;
35592        }
35593
35594        $setmirror = false;
35595        if (isset($this->_channelInfo['servers']['mirror'][0])) {
35596            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
35597                if ($mirror == $mir['attribs']['host']) {
35598                    $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
35599                    break;
35600                }
35601            }
35602        } else {
35603            if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
35604                $setmirror = &$this->_channelInfo['servers']['mirror'];
35605            }
35606        }
35607
35608        if (!$setmirror) {
35609            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
35610                array('mirror' => $mirror));
35611            return false;
35612        }
35613
35614        $set = array('attribs' => array('version' => $version), '_content' => $name);
35615        if (!isset($setmirror[$type]['function'])) {
35616            $setmirror[$type]['function'] = $set;
35617            $this->_isValid = false;
35618            return true;
35619        } elseif (!isset($setmirror[$type]['function'][0])) {
35620            $setmirror[$type]['function'] = array($setmirror[$type]['function']);
35621        }
35622
35623        $setmirror[$type]['function'][] = $set;
35624        $this->_isValid = false;
35625        return true;
35626    }
35627
35628    /**
35629     * @param string Resource Type this url links to
35630     * @param string URL
35631     * @param string|false mirror name, if this is not a primary server REST base URL
35632     */
35633    function setBaseURL($resourceType, $url, $mirror = false)
35634    {
35635        if ($mirror) {
35636            if (!isset($this->_channelInfo['servers']['mirror'])) {
35637                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
35638                    array('mirror' => $mirror));
35639                return false;
35640            }
35641
35642            $setmirror = false;
35643            if (isset($this->_channelInfo['servers']['mirror'][0])) {
35644                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
35645                    if ($mirror == $mir['attribs']['host']) {
35646                        $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
35647                        break;
35648                    }
35649                }
35650            } else {
35651                if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
35652                    $setmirror = &$this->_channelInfo['servers']['mirror'];
35653                }
35654            }
35655        } else {
35656            $setmirror = &$this->_channelInfo['servers']['primary'];
35657        }
35658
35659        $set = array('attribs' => array('type' => $resourceType), '_content' => $url);
35660        if (!isset($setmirror['rest'])) {
35661            $setmirror['rest'] = array();
35662        }
35663
35664        if (!isset($setmirror['rest']['baseurl'])) {
35665            $setmirror['rest']['baseurl'] = $set;
35666            $this->_isValid = false;
35667            return true;
35668        } elseif (!isset($setmirror['rest']['baseurl'][0])) {
35669            $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']);
35670        }
35671
35672        foreach ($setmirror['rest']['baseurl'] as $i => $url) {
35673            if ($url['attribs']['type'] == $resourceType) {
35674                $this->_isValid = false;
35675                $setmirror['rest']['baseurl'][$i] = $set;
35676                return true;
35677            }
35678        }
35679
35680        $setmirror['rest']['baseurl'][] = $set;
35681        $this->_isValid = false;
35682        return true;
35683    }
35684
35685    /**
35686     * @param string mirror server
35687     * @param int mirror http port
35688     * @return boolean
35689     */
35690    function addMirror($server, $port = null)
35691    {
35692        if ($this->_channelInfo['name'] == '__uri') {
35693            return false; // the __uri channel cannot have mirrors by definition
35694        }
35695
35696        $set = array('attribs' => array('host' => $server));
35697        if (is_numeric($port)) {
35698            $set['attribs']['port'] = $port;
35699        }
35700
35701        if (!isset($this->_channelInfo['servers']['mirror'])) {
35702            $this->_channelInfo['servers']['mirror'] = $set;
35703            return true;
35704        }
35705
35706        if (!isset($this->_channelInfo['servers']['mirror'][0])) {
35707            $this->_channelInfo['servers']['mirror'] =
35708                array($this->_channelInfo['servers']['mirror']);
35709        }
35710
35711        $this->_channelInfo['servers']['mirror'][] = $set;
35712        return true;
35713    }
35714
35715    /**
35716     * Retrieve the name of the validation package for this channel
35717     * @return string|false
35718     */
35719    function getValidationPackage()
35720    {
35721        if (!$this->_isValid && !$this->validate()) {
35722            return false;
35723        }
35724
35725        if (!isset($this->_channelInfo['validatepackage'])) {
35726            return array('attribs' => array('version' => 'default'),
35727                '_content' => 'PEAR_Validate');
35728        }
35729
35730        return $this->_channelInfo['validatepackage'];
35731    }
35732
35733    /**
35734     * Retrieve the object that can be used for custom validation
35735     * @param string|false the name of the package to validate.  If the package is
35736     *                     the channel validation package, PEAR_Validate is returned
35737     * @return PEAR_Validate|false false is returned if the validation package
35738     *         cannot be located
35739     */
35740    function &getValidationObject($package = false)
35741    {
35742        if (!class_exists('PEAR_Validate')) {
35743            require_once 'PEAR/Validate.php';
35744        }
35745
35746        if (!$this->_isValid) {
35747            if (!$this->validate()) {
35748                $a = false;
35749                return $a;
35750            }
35751        }
35752
35753        if (isset($this->_channelInfo['validatepackage'])) {
35754            if ($package == $this->_channelInfo['validatepackage']) {
35755                // channel validation packages are always validated by PEAR_Validate
35756                $val = &new PEAR_Validate;
35757                return $val;
35758            }
35759
35760            if (!class_exists(str_replace('.', '_',
35761                  $this->_channelInfo['validatepackage']['_content']))) {
35762                if ($this->isIncludeable(str_replace('_', '/',
35763                      $this->_channelInfo['validatepackage']['_content']) . '.php')) {
35764                    include_once str_replace('_', '/',
35765                        $this->_channelInfo['validatepackage']['_content']) . '.php';
35766                    $vclass = str_replace('.', '_',
35767                        $this->_channelInfo['validatepackage']['_content']);
35768                    $val = &new $vclass;
35769                } else {
35770                    $a = false;
35771                    return $a;
35772                }
35773            } else {
35774                $vclass = str_replace('.', '_',
35775                    $this->_channelInfo['validatepackage']['_content']);
35776                $val = &new $vclass;
35777            }
35778        } else {
35779            $val = &new PEAR_Validate;
35780        }
35781
35782        return $val;
35783    }
35784
35785    function isIncludeable($path)
35786    {
35787        $possibilities = explode(PATH_SEPARATOR, ini_get('include_path'));
35788        foreach ($possibilities as $dir) {
35789            if (file_exists($dir . DIRECTORY_SEPARATOR . $path)
35790                  && is_readable($dir . DIRECTORY_SEPARATOR . $path)) {
35791                return true;
35792            }
35793        }
35794
35795        return false;
35796    }
35797
35798    /**
35799     * This function is used by the channel updater and retrieves a value set by
35800     * the registry, or the current time if it has not been set
35801     * @return string
35802     */
35803    function lastModified()
35804    {
35805        if (isset($this->_channelInfo['_lastmodified'])) {
35806            return $this->_channelInfo['_lastmodified'];
35807        }
35808
35809        return time();
35810    }
35811}PEAR-1.9.4/PEAR/Command.php0000644000076500000240000003063311605156614014057 0ustar  helgistaff<?php
35812/**
35813 * PEAR_Command, command pattern class
35814 *
35815 * PHP versions 4 and 5
35816 *
35817 * @category   pear
35818 * @package    PEAR
35819 * @author     Stig Bakken <ssb@php.net>
35820 * @author     Greg Beaver <cellog@php.net>
35821 * @copyright  1997-2009 The Authors
35822 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
35823 * @version    CVS: $Id: Command.php 313023 2011-07-06 19:17:11Z dufuz $
35824 * @link       http://pear.php.net/package/PEAR
35825 * @since      File available since Release 0.1
35826 */
35827
35828/**
35829 * Needed for error handling
35830 */
35831require_once 'PEAR.php';
35832require_once 'PEAR/Frontend.php';
35833require_once 'PEAR/XMLParser.php';
35834
35835/**
35836 * List of commands and what classes they are implemented in.
35837 * @var array command => implementing class
35838 */
35839$GLOBALS['_PEAR_Command_commandlist'] = array();
35840
35841/**
35842 * List of commands and their descriptions
35843 * @var array command => description
35844 */
35845$GLOBALS['_PEAR_Command_commanddesc'] = array();
35846
35847/**
35848 * List of shortcuts to common commands.
35849 * @var array shortcut => command
35850 */
35851$GLOBALS['_PEAR_Command_shortcuts'] = array();
35852
35853/**
35854 * Array of command objects
35855 * @var array class => object
35856 */
35857$GLOBALS['_PEAR_Command_objects'] = array();
35858
35859/**
35860 * PEAR command class, a simple factory class for administrative
35861 * commands.
35862 *
35863 * How to implement command classes:
35864 *
35865 * - The class must be called PEAR_Command_Nnn, installed in the
35866 *   "PEAR/Common" subdir, with a method called getCommands() that
35867 *   returns an array of the commands implemented by the class (see
35868 *   PEAR/Command/Install.php for an example).
35869 *
35870 * - The class must implement a run() function that is called with three
35871 *   params:
35872 *
35873 *    (string) command name
35874 *    (array)  assoc array with options, freely defined by each
35875 *             command, for example:
35876 *             array('force' => true)
35877 *    (array)  list of the other parameters
35878 *
35879 *   The run() function returns a PEAR_CommandResponse object.  Use
35880 *   these methods to get information:
35881 *
35882 *    int getStatus()   Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL)
35883 *                      *_PARTIAL means that you need to issue at least
35884 *                      one more command to complete the operation
35885 *                      (used for example for validation steps).
35886 *
35887 *    string getMessage()  Returns a message for the user.  Remember,
35888 *                         no HTML or other interface-specific markup.
35889 *
35890 *   If something unexpected happens, run() returns a PEAR error.
35891 *
35892 * - DON'T OUTPUT ANYTHING! Return text for output instead.
35893 *
35894 * - DON'T USE HTML! The text you return will be used from both Gtk,
35895 *   web and command-line interfaces, so for now, keep everything to
35896 *   plain text.
35897 *
35898 * - DON'T USE EXIT OR DIE! Always use pear errors.  From static
35899 *   classes do PEAR::raiseError(), from other classes do
35900 *   $this->raiseError().
35901 * @category   pear
35902 * @package    PEAR
35903 * @author     Stig Bakken <ssb@php.net>
35904 * @author     Greg Beaver <cellog@php.net>
35905 * @copyright  1997-2009 The Authors
35906 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
35907 * @version    Release: 1.9.4
35908 * @link       http://pear.php.net/package/PEAR
35909 * @since      Class available since Release 0.1
35910 */
35911class PEAR_Command
35912{
35913    // {{{ factory()
35914
35915    /**
35916     * Get the right object for executing a command.
35917     *
35918     * @param string $command The name of the command
35919     * @param object $config  Instance of PEAR_Config object
35920     *
35921     * @return object the command object or a PEAR error
35922     *
35923     * @access public
35924     * @static
35925     */
35926    function &factory($command, &$config)
35927    {
35928        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
35929            PEAR_Command::registerCommands();
35930        }
35931        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
35932            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
35933        }
35934        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
35935            $a = PEAR::raiseError("unknown command `$command'");
35936            return $a;
35937        }
35938        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
35939        if (!class_exists($class)) {
35940            require_once $GLOBALS['_PEAR_Command_objects'][$class];
35941        }
35942        if (!class_exists($class)) {
35943            $a = PEAR::raiseError("unknown command `$command'");
35944            return $a;
35945        }
35946        $ui =& PEAR_Command::getFrontendObject();
35947        $obj = &new $class($ui, $config);
35948        return $obj;
35949    }
35950
35951    // }}}
35952    // {{{ & getObject()
35953    function &getObject($command)
35954    {
35955        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
35956        if (!class_exists($class)) {
35957            require_once $GLOBALS['_PEAR_Command_objects'][$class];
35958        }
35959        if (!class_exists($class)) {
35960            return PEAR::raiseError("unknown command `$command'");
35961        }
35962        $ui =& PEAR_Command::getFrontendObject();
35963        $config = &PEAR_Config::singleton();
35964        $obj = &new $class($ui, $config);
35965        return $obj;
35966    }
35967
35968    // }}}
35969    // {{{ & getFrontendObject()
35970
35971    /**
35972     * Get instance of frontend object.
35973     *
35974     * @return object|PEAR_Error
35975     * @static
35976     */
35977    function &getFrontendObject()
35978    {
35979        $a = &PEAR_Frontend::singleton();
35980        return $a;
35981    }
35982
35983    // }}}
35984    // {{{ & setFrontendClass()
35985
35986    /**
35987     * Load current frontend class.
35988     *
35989     * @param string $uiclass Name of class implementing the frontend
35990     *
35991     * @return object the frontend object, or a PEAR error
35992     * @static
35993     */
35994    function &setFrontendClass($uiclass)
35995    {
35996        $a = &PEAR_Frontend::setFrontendClass($uiclass);
35997        return $a;
35998    }
35999
36000    // }}}
36001    // {{{ setFrontendType()
36002
36003    /**
36004     * Set current frontend.
36005     *
36006     * @param string $uitype Name of the frontend type (for example "CLI")
36007     *
36008     * @return object the frontend object, or a PEAR error
36009     * @static
36010     */
36011    function setFrontendType($uitype)
36012    {
36013        $uiclass = 'PEAR_Frontend_' . $uitype;
36014        return PEAR_Command::setFrontendClass($uiclass);
36015    }
36016
36017    // }}}
36018    // {{{ registerCommands()
36019
36020    /**
36021     * Scan through the Command directory looking for classes
36022     * and see what commands they implement.
36023     *
36024     * @param bool   (optional) if FALSE (default), the new list of
36025     *               commands should replace the current one.  If TRUE,
36026     *               new entries will be merged with old.
36027     *
36028     * @param string (optional) where (what directory) to look for
36029     *               classes, defaults to the Command subdirectory of
36030     *               the directory from where this file (__FILE__) is
36031     *               included.
36032     *
36033     * @return bool TRUE on success, a PEAR error on failure
36034     *
36035     * @access public
36036     * @static
36037     */
36038    function registerCommands($merge = false, $dir = null)
36039    {
36040        $parser = new PEAR_XMLParser;
36041        if ($dir === null) {
36042            $dir = dirname(__FILE__) . '/Command';
36043        }
36044        if (!is_dir($dir)) {
36045            return PEAR::raiseError("registerCommands: opendir($dir) '$dir' does not exist or is not a directory");
36046        }
36047        $dp = @opendir($dir);
36048        if (empty($dp)) {
36049            return PEAR::raiseError("registerCommands: opendir($dir) failed");
36050        }
36051        if (!$merge) {
36052            $GLOBALS['_PEAR_Command_commandlist'] = array();
36053        }
36054
36055        while ($file = readdir($dp)) {
36056            if ($file{0} == '.' || substr($file, -4) != '.xml') {
36057                continue;
36058            }
36059
36060            $f = substr($file, 0, -4);
36061            $class = "PEAR_Command_" . $f;
36062            // List of commands
36063            if (empty($GLOBALS['_PEAR_Command_objects'][$class])) {
36064                $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . $f . '.php';
36065            }
36066
36067            $parser->parse(file_get_contents("$dir/$file"));
36068            $implements = $parser->getData();
36069            foreach ($implements as $command => $desc) {
36070                if ($command == 'attribs') {
36071                    continue;
36072                }
36073
36074                if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
36075                    return PEAR::raiseError('Command "' . $command . '" already registered in ' .
36076                        'class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
36077                }
36078
36079                $GLOBALS['_PEAR_Command_commandlist'][$command] = $class;
36080                $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc['summary'];
36081                if (isset($desc['shortcut'])) {
36082                    $shortcut = $desc['shortcut'];
36083                    if (isset($GLOBALS['_PEAR_Command_shortcuts'][$shortcut])) {
36084                        return PEAR::raiseError('Command shortcut "' . $shortcut . '" already ' .
36085                            'registered to command "' . $command . '" in class "' .
36086                            $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
36087                    }
36088                    $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command;
36089                }
36090
36091                if (isset($desc['options']) && $desc['options']) {
36092                    foreach ($desc['options'] as $oname => $option) {
36093                        if (isset($option['shortopt']) && strlen($option['shortopt']) > 1) {
36094                            return PEAR::raiseError('Option "' . $oname . '" short option "' .
36095                                $option['shortopt'] . '" must be ' .
36096                                'only 1 character in Command "' . $command . '" in class "' .
36097                                $class . '"');
36098                        }
36099                    }
36100                }
36101            }
36102        }
36103
36104        ksort($GLOBALS['_PEAR_Command_shortcuts']);
36105        ksort($GLOBALS['_PEAR_Command_commandlist']);
36106        @closedir($dp);
36107        return true;
36108    }
36109
36110    // }}}
36111    // {{{ getCommands()
36112
36113    /**
36114     * Get the list of currently supported commands, and what
36115     * classes implement them.
36116     *
36117     * @return array command => implementing class
36118     *
36119     * @access public
36120     * @static
36121     */
36122    function getCommands()
36123    {
36124        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
36125            PEAR_Command::registerCommands();
36126        }
36127        return $GLOBALS['_PEAR_Command_commandlist'];
36128    }
36129
36130    // }}}
36131    // {{{ getShortcuts()
36132
36133    /**
36134     * Get the list of command shortcuts.
36135     *
36136     * @return array shortcut => command
36137     *
36138     * @access public
36139     * @static
36140     */
36141    function getShortcuts()
36142    {
36143        if (empty($GLOBALS['_PEAR_Command_shortcuts'])) {
36144            PEAR_Command::registerCommands();
36145        }
36146        return $GLOBALS['_PEAR_Command_shortcuts'];
36147    }
36148
36149    // }}}
36150    // {{{ getGetoptArgs()
36151
36152    /**
36153     * Compiles arguments for getopt.
36154     *
36155     * @param string $command     command to get optstring for
36156     * @param string $short_args  (reference) short getopt format
36157     * @param array  $long_args   (reference) long getopt format
36158     *
36159     * @return void
36160     *
36161     * @access public
36162     * @static
36163     */
36164    function getGetoptArgs($command, &$short_args, &$long_args)
36165    {
36166        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
36167            PEAR_Command::registerCommands();
36168        }
36169        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
36170            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
36171        }
36172        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
36173            return null;
36174        }
36175        $obj = &PEAR_Command::getObject($command);
36176        return $obj->getGetoptArgs($command, $short_args, $long_args);
36177    }
36178
36179    // }}}
36180    // {{{ getDescription()
36181
36182    /**
36183     * Get description for a command.
36184     *
36185     * @param  string $command Name of the command
36186     *
36187     * @return string command description
36188     *
36189     * @access public
36190     * @static
36191     */
36192    function getDescription($command)
36193    {
36194        if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) {
36195            return null;
36196        }
36197        return $GLOBALS['_PEAR_Command_commanddesc'][$command];
36198    }
36199
36200    // }}}
36201    // {{{ getHelp()
36202
36203    /**
36204     * Get help for command.
36205     *
36206     * @param string $command Name of the command to return help for
36207     *
36208     * @access public
36209     * @static
36210     */
36211    function getHelp($command)
36212    {
36213        $cmds = PEAR_Command::getCommands();
36214        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
36215            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
36216        }
36217        if (isset($cmds[$command])) {
36218            $obj = &PEAR_Command::getObject($command);
36219            return $obj->getHelp($command);
36220        }
36221        return false;
36222    }
36223    // }}}
36224}PEAR-1.9.4/PEAR/Common.php0000644000076500000240000006225511605156614013736 0ustar  helgistaff<?php
36225/**
36226 * PEAR_Common, the base class for the PEAR Installer
36227 *
36228 * PHP versions 4 and 5
36229 *
36230 * @category   pear
36231 * @package    PEAR
36232 * @author     Stig Bakken <ssb@php.net>
36233 * @author     Tomas V. V. Cox <cox@idecnet.com>
36234 * @author     Greg Beaver <cellog@php.net>
36235 * @copyright  1997-2009 The Authors
36236 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
36237 * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
36238 * @link       http://pear.php.net/package/PEAR
36239 * @since      File available since Release 0.1.0
36240 * @deprecated File deprecated since Release 1.4.0a1
36241 */
36242
36243/**
36244 * Include error handling
36245 */
36246require_once 'PEAR.php';
36247
36248/**
36249 * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
36250 */
36251define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
36252define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
36253define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/');
36254
36255// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
36256define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
36257define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '\\z/i');
36258
36259// XXX far from perfect :-)
36260define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG .
36261    ')(-([.0-9a-zA-Z]+))?');
36262define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG .
36263    '\\z/');
36264
36265define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+');
36266define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '\\z/');
36267
36268// this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED
36269define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*');
36270define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '\\z/i');
36271
36272define('_PEAR_CHANNELS_PACKAGE_PREG',  '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/('
36273         . _PEAR_COMMON_PACKAGE_NAME_PREG . ')');
36274define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '\\z/i');
36275
36276define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::('
36277    . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?');
36278define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '\\z/');
36279
36280/**
36281 * List of temporary files and directories registered by
36282 * PEAR_Common::addTempFile().
36283 * @var array
36284 */
36285$GLOBALS['_PEAR_Common_tempfiles'] = array();
36286
36287/**
36288 * Valid maintainer roles
36289 * @var array
36290 */
36291$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
36292
36293/**
36294 * Valid release states
36295 * @var array
36296 */
36297$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
36298
36299/**
36300 * Valid dependency types
36301 * @var array
36302 */
36303$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
36304
36305/**
36306 * Valid dependency relations
36307 * @var array
36308 */
36309$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');
36310
36311/**
36312 * Valid file roles
36313 * @var array
36314 */
36315$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
36316
36317/**
36318 * Valid replacement types
36319 * @var array
36320 */
36321$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
36322
36323/**
36324 * Valid "provide" types
36325 * @var array
36326 */
36327$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
36328
36329/**
36330 * Valid "provide" types
36331 * @var array
36332 */
36333$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');
36334
36335/**
36336 * Class providing common functionality for PEAR administration classes.
36337 * @category   pear
36338 * @package    PEAR
36339 * @author     Stig Bakken <ssb@php.net>
36340 * @author     Tomas V. V. Cox <cox@idecnet.com>
36341 * @author     Greg Beaver <cellog@php.net>
36342 * @copyright  1997-2009 The Authors
36343 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
36344 * @version    Release: 1.9.4
36345 * @link       http://pear.php.net/package/PEAR
36346 * @since      Class available since Release 1.4.0a1
36347 * @deprecated This class will disappear, and its components will be spread
36348 *             into smaller classes, like the AT&T breakup, as of Release 1.4.0a1
36349 */
36350class PEAR_Common extends PEAR
36351{
36352    /**
36353     * User Interface object (PEAR_Frontend_* class).  If null,
36354     * the log() method uses print.
36355     * @var object
36356     */
36357    var $ui = null;
36358
36359    /**
36360     * Configuration object (PEAR_Config).
36361     * @var PEAR_Config
36362     */
36363    var $config = null;
36364
36365    /** stack of elements, gives some sort of XML context */
36366    var $element_stack = array();
36367
36368    /** name of currently parsed XML element */
36369    var $current_element;
36370
36371    /** array of attributes of the currently parsed XML element */
36372    var $current_attributes = array();
36373
36374    /** assoc with information about a package */
36375    var $pkginfo = array();
36376
36377    var $current_path = null;
36378
36379    /**
36380     * Flag variable used to mark a valid package file
36381     * @var boolean
36382     * @access private
36383     */
36384    var $_validPackageFile;
36385
36386    /**
36387     * PEAR_Common constructor
36388     *
36389     * @access public
36390     */
36391    function PEAR_Common()
36392    {
36393        parent::PEAR();
36394        $this->config = &PEAR_Config::singleton();
36395        $this->debug = $this->config->get('verbose');
36396    }
36397
36398    /**
36399     * PEAR_Common destructor
36400     *
36401     * @access private
36402     */
36403    function _PEAR_Common()
36404    {
36405        // doesn't work due to bug #14744
36406        //$tempfiles = $this->_tempfiles;
36407        $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
36408        while ($file = array_shift($tempfiles)) {
36409            if (@is_dir($file)) {
36410                if (!class_exists('System')) {
36411                    require_once 'System.php';
36412                }
36413
36414                System::rm(array('-rf', $file));
36415            } elseif (file_exists($file)) {
36416                unlink($file);
36417            }
36418        }
36419    }
36420
36421    /**
36422     * Register a temporary file or directory.  When the destructor is
36423     * executed, all registered temporary files and directories are
36424     * removed.
36425     *
36426     * @param string  $file  name of file or directory
36427     *
36428     * @return void
36429     *
36430     * @access public
36431     */
36432    function addTempFile($file)
36433    {
36434        if (!class_exists('PEAR_Frontend')) {
36435            require_once 'PEAR/Frontend.php';
36436        }
36437        PEAR_Frontend::addTempFile($file);
36438    }
36439
36440    /**
36441     * Wrapper to System::mkDir(), creates a directory as well as
36442     * any necessary parent directories.
36443     *
36444     * @param string  $dir  directory name
36445     *
36446     * @return bool TRUE on success, or a PEAR error
36447     *
36448     * @access public
36449     */
36450    function mkDirHier($dir)
36451    {
36452        // Only used in Installer - move it there ?
36453        $this->log(2, "+ create dir $dir");
36454        if (!class_exists('System')) {
36455            require_once 'System.php';
36456        }
36457        return System::mkDir(array('-p', $dir));
36458    }
36459
36460    /**
36461     * Logging method.
36462     *
36463     * @param int    $level  log level (0 is quiet, higher is noisier)
36464     * @param string $msg    message to write to the log
36465     *
36466     * @return void
36467     *
36468     * @access public
36469     * @static
36470     */
36471    function log($level, $msg, $append_crlf = true)
36472    {
36473        if ($this->debug >= $level) {
36474            if (!class_exists('PEAR_Frontend')) {
36475                require_once 'PEAR/Frontend.php';
36476            }
36477
36478            $ui = &PEAR_Frontend::singleton();
36479            if (is_a($ui, 'PEAR_Frontend')) {
36480                $ui->log($msg, $append_crlf);
36481            } else {
36482                print "$msg\n";
36483            }
36484        }
36485    }
36486
36487    /**
36488     * Create and register a temporary directory.
36489     *
36490     * @param string $tmpdir (optional) Directory to use as tmpdir.
36491     *                       Will use system defaults (for example
36492     *                       /tmp or c:\windows\temp) if not specified
36493     *
36494     * @return string name of created directory
36495     *
36496     * @access public
36497     */
36498    function mkTempDir($tmpdir = '')
36499    {
36500        $topt = $tmpdir ? array('-t', $tmpdir) : array();
36501        $topt = array_merge($topt, array('-d', 'pear'));
36502        if (!class_exists('System')) {
36503            require_once 'System.php';
36504        }
36505
36506        if (!$tmpdir = System::mktemp($topt)) {
36507            return false;
36508        }
36509
36510        $this->addTempFile($tmpdir);
36511        return $tmpdir;
36512    }
36513
36514    /**
36515     * Set object that represents the frontend to be used.
36516     *
36517     * @param  object Reference of the frontend object
36518     * @return void
36519     * @access public
36520     */
36521    function setFrontendObject(&$ui)
36522    {
36523        $this->ui = &$ui;
36524    }
36525
36526    /**
36527     * Return an array containing all of the states that are more stable than
36528     * or equal to the passed in state
36529     *
36530     * @param string Release state
36531     * @param boolean Determines whether to include $state in the list
36532     * @return false|array False if $state is not a valid release state
36533     */
36534    function betterStates($state, $include = false)
36535    {
36536        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
36537        $i = array_search($state, $states);
36538        if ($i === false) {
36539            return false;
36540        }
36541        if ($include) {
36542            $i--;
36543        }
36544        return array_slice($states, $i + 1);
36545    }
36546
36547    /**
36548     * Get the valid roles for a PEAR package maintainer
36549     *
36550     * @return array
36551     * @static
36552     */
36553    function getUserRoles()
36554    {
36555        return $GLOBALS['_PEAR_Common_maintainer_roles'];
36556    }
36557
36558    /**
36559     * Get the valid package release states of packages
36560     *
36561     * @return array
36562     * @static
36563     */
36564    function getReleaseStates()
36565    {
36566        return $GLOBALS['_PEAR_Common_release_states'];
36567    }
36568
36569    /**
36570     * Get the implemented dependency types (php, ext, pkg etc.)
36571     *
36572     * @return array
36573     * @static
36574     */
36575    function getDependencyTypes()
36576    {
36577        return $GLOBALS['_PEAR_Common_dependency_types'];
36578    }
36579
36580    /**
36581     * Get the implemented dependency relations (has, lt, ge etc.)
36582     *
36583     * @return array
36584     * @static
36585     */
36586    function getDependencyRelations()
36587    {
36588        return $GLOBALS['_PEAR_Common_dependency_relations'];
36589    }
36590
36591    /**
36592     * Get the implemented file roles
36593     *
36594     * @return array
36595     * @static
36596     */
36597    function getFileRoles()
36598    {
36599        return $GLOBALS['_PEAR_Common_file_roles'];
36600    }
36601
36602    /**
36603     * Get the implemented file replacement types in
36604     *
36605     * @return array
36606     * @static
36607     */
36608    function getReplacementTypes()
36609    {
36610        return $GLOBALS['_PEAR_Common_replacement_types'];
36611    }
36612
36613    /**
36614     * Get the implemented file replacement types in
36615     *
36616     * @return array
36617     * @static
36618     */
36619    function getProvideTypes()
36620    {
36621        return $GLOBALS['_PEAR_Common_provide_types'];
36622    }
36623
36624    /**
36625     * Get the implemented file replacement types in
36626     *
36627     * @return array
36628     * @static
36629     */
36630    function getScriptPhases()
36631    {
36632        return $GLOBALS['_PEAR_Common_script_phases'];
36633    }
36634
36635    /**
36636     * Test whether a string contains a valid package name.
36637     *
36638     * @param string $name the package name to test
36639     *
36640     * @return bool
36641     *
36642     * @access public
36643     */
36644    function validPackageName($name)
36645    {
36646        return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
36647    }
36648
36649    /**
36650     * Test whether a string contains a valid package version.
36651     *
36652     * @param string $ver the package version to test
36653     *
36654     * @return bool
36655     *
36656     * @access public
36657     */
36658    function validPackageVersion($ver)
36659    {
36660        return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
36661    }
36662
36663    /**
36664     * @param string $path relative or absolute include path
36665     * @return boolean
36666     * @static
36667     */
36668    function isIncludeable($path)
36669    {
36670        if (file_exists($path) && is_readable($path)) {
36671            return true;
36672        }
36673
36674        $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
36675        foreach ($ipath as $include) {
36676            $test = realpath($include . DIRECTORY_SEPARATOR . $path);
36677            if (file_exists($test) && is_readable($test)) {
36678                return true;
36679            }
36680        }
36681
36682        return false;
36683    }
36684
36685    function _postProcessChecks($pf)
36686    {
36687        if (!PEAR::isError($pf)) {
36688            return $this->_postProcessValidPackagexml($pf);
36689        }
36690
36691        $errs = $pf->getUserinfo();
36692        if (is_array($errs)) {
36693            foreach ($errs as $error) {
36694                $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
36695            }
36696        }
36697
36698        return $pf;
36699    }
36700
36701    /**
36702     * Returns information about a package file.  Expects the name of
36703     * a gzipped tar file as input.
36704     *
36705     * @param string  $file  name of .tgz file
36706     *
36707     * @return array  array with package information
36708     *
36709     * @access public
36710     * @deprecated use PEAR_PackageFile->fromTgzFile() instead
36711     *
36712     */
36713    function infoFromTgzFile($file)
36714    {
36715        $packagefile = &new PEAR_PackageFile($this->config);
36716        $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL);
36717        return $this->_postProcessChecks($pf);
36718    }
36719
36720    /**
36721     * Returns information about a package file.  Expects the name of
36722     * a package xml file as input.
36723     *
36724     * @param string  $descfile  name of package xml file
36725     *
36726     * @return array  array with package information
36727     *
36728     * @access public
36729     * @deprecated use PEAR_PackageFile->fromPackageFile() instead
36730     *
36731     */
36732    function infoFromDescriptionFile($descfile)
36733    {
36734        $packagefile = &new PEAR_PackageFile($this->config);
36735        $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
36736        return $this->_postProcessChecks($pf);
36737    }
36738
36739    /**
36740     * Returns information about a package file.  Expects the contents
36741     * of a package xml file as input.
36742     *
36743     * @param string  $data  contents of package.xml file
36744     *
36745     * @return array   array with package information
36746     *
36747     * @access public
36748     * @deprecated use PEAR_PackageFile->fromXmlstring() instead
36749     *
36750     */
36751    function infoFromString($data)
36752    {
36753        $packagefile = &new PEAR_PackageFile($this->config);
36754        $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false);
36755        return $this->_postProcessChecks($pf);
36756    }
36757
36758    /**
36759     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
36760     * @return array
36761     */
36762    function _postProcessValidPackagexml(&$pf)
36763    {
36764        if (!is_a($pf, 'PEAR_PackageFile_v2')) {
36765            $this->pkginfo = $pf->toArray();
36766            return $this->pkginfo;
36767        }
36768
36769        // sort of make this into a package.xml 1.0-style array
36770        // changelog is not converted to old format.
36771        $arr = $pf->toArray(true);
36772        $arr = array_merge($arr, $arr['old']);
36773        unset($arr['old'], $arr['xsdversion'], $arr['contents'], $arr['compatible'],
36774              $arr['channel'], $arr['uri'], $arr['dependencies'], $arr['phprelease'],
36775              $arr['extsrcrelease'], $arr['zendextsrcrelease'], $arr['extbinrelease'],
36776              $arr['zendextbinrelease'], $arr['bundle'], $arr['lead'], $arr['developer'],
36777              $arr['helper'], $arr['contributor']);
36778        $arr['filelist'] = $pf->getFilelist();
36779        $this->pkginfo = $arr;
36780        return $arr;
36781    }
36782
36783    /**
36784     * Returns package information from different sources
36785     *
36786     * This method is able to extract information about a package
36787     * from a .tgz archive or from a XML package definition file.
36788     *
36789     * @access public
36790     * @param  string Filename of the source ('package.xml', '<package>.tgz')
36791     * @return string
36792     * @deprecated use PEAR_PackageFile->fromAnyFile() instead
36793     */
36794    function infoFromAny($info)
36795    {
36796        if (is_string($info) && file_exists($info)) {
36797            $packagefile = &new PEAR_PackageFile($this->config);
36798            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
36799            if (PEAR::isError($pf)) {
36800                $errs = $pf->getUserinfo();
36801                if (is_array($errs)) {
36802                    foreach ($errs as $error) {
36803                        $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
36804                    }
36805                }
36806
36807                return $pf;
36808            }
36809
36810            return $this->_postProcessValidPackagexml($pf);
36811        }
36812
36813        return $info;
36814    }
36815
36816    /**
36817     * Return an XML document based on the package info (as returned
36818     * by the PEAR_Common::infoFrom* methods).
36819     *
36820     * @param array  $pkginfo  package info
36821     *
36822     * @return string XML data
36823     *
36824     * @access public
36825     * @deprecated use a PEAR_PackageFile_v* object's generator instead
36826     */
36827    function xmlFromInfo($pkginfo)
36828    {
36829        $config      = &PEAR_Config::singleton();
36830        $packagefile = &new PEAR_PackageFile($config);
36831        $pf = &$packagefile->fromArray($pkginfo);
36832        $gen = &$pf->getDefaultGenerator();
36833        return $gen->toXml(PEAR_VALIDATE_PACKAGING);
36834    }
36835
36836    /**
36837     * Validate XML package definition file.
36838     *
36839     * @param  string $info Filename of the package archive or of the
36840     *                package definition file
36841     * @param  array $errors Array that will contain the errors
36842     * @param  array $warnings Array that will contain the warnings
36843     * @param  string $dir_prefix (optional) directory where source files
36844     *                may be found, or empty if they are not available
36845     * @access public
36846     * @return boolean
36847     * @deprecated use the validation of PEAR_PackageFile objects
36848     */
36849    function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
36850    {
36851        $config      = &PEAR_Config::singleton();
36852        $packagefile = &new PEAR_PackageFile($config);
36853        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
36854        if (strpos($info, '<?xml') !== false) {
36855            $pf = &$packagefile->fromXmlString($info, PEAR_VALIDATE_NORMAL, '');
36856        } else {
36857            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
36858        }
36859
36860        PEAR::staticPopErrorHandling();
36861        if (PEAR::isError($pf)) {
36862            $errs = $pf->getUserinfo();
36863            if (is_array($errs)) {
36864                foreach ($errs as $error) {
36865                    if ($error['level'] == 'error') {
36866                        $errors[] = $error['message'];
36867                    } else {
36868                        $warnings[] = $error['message'];
36869                    }
36870                }
36871            }
36872
36873            return false;
36874        }
36875
36876        return true;
36877    }
36878
36879    /**
36880     * Build a "provides" array from data returned by
36881     * analyzeSourceCode().  The format of the built array is like
36882     * this:
36883     *
36884     *  array(
36885     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
36886     *    ...
36887     *  )
36888     *
36889     *
36890     * @param array $srcinfo array with information about a source file
36891     * as returned by the analyzeSourceCode() method.
36892     *
36893     * @return void
36894     *
36895     * @access public
36896     *
36897     */
36898    function buildProvidesArray($srcinfo)
36899    {
36900        $file = basename($srcinfo['source_file']);
36901        $pn = '';
36902        if (isset($this->_packageName)) {
36903            $pn = $this->_packageName;
36904        }
36905
36906        $pnl = strlen($pn);
36907        foreach ($srcinfo['declared_classes'] as $class) {
36908            $key = "class;$class";
36909            if (isset($this->pkginfo['provides'][$key])) {
36910                continue;
36911            }
36912
36913            $this->pkginfo['provides'][$key] =
36914                array('file'=> $file, 'type' => 'class', 'name' => $class);
36915            if (isset($srcinfo['inheritance'][$class])) {
36916                $this->pkginfo['provides'][$key]['extends'] =
36917                    $srcinfo['inheritance'][$class];
36918            }
36919        }
36920
36921        foreach ($srcinfo['declared_methods'] as $class => $methods) {
36922            foreach ($methods as $method) {
36923                $function = "$class::$method";
36924                $key = "function;$function";
36925                if ($method{0} == '_' || !strcasecmp($method, $class) ||
36926                    isset($this->pkginfo['provides'][$key])) {
36927                    continue;
36928                }
36929
36930                $this->pkginfo['provides'][$key] =
36931                    array('file'=> $file, 'type' => 'function', 'name' => $function);
36932            }
36933        }
36934
36935        foreach ($srcinfo['declared_functions'] as $function) {
36936            $key = "function;$function";
36937            if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
36938                continue;
36939            }
36940
36941            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
36942                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
36943            }
36944
36945            $this->pkginfo['provides'][$key] =
36946                array('file'=> $file, 'type' => 'function', 'name' => $function);
36947        }
36948    }
36949
36950    /**
36951     * Analyze the source code of the given PHP file
36952     *
36953     * @param  string Filename of the PHP file
36954     * @return mixed
36955     * @access public
36956     */
36957    function analyzeSourceCode($file)
36958    {
36959        if (!class_exists('PEAR_PackageFile_v2_Validator')) {
36960            require_once 'PEAR/PackageFile/v2/Validator.php';
36961        }
36962
36963        $a = new PEAR_PackageFile_v2_Validator;
36964        return $a->analyzeSourceCode($file);
36965    }
36966
36967    function detectDependencies($any, $status_callback = null)
36968    {
36969        if (!function_exists("token_get_all")) {
36970            return false;
36971        }
36972
36973        if (PEAR::isError($info = $this->infoFromAny($any))) {
36974            return $this->raiseError($info);
36975        }
36976
36977        if (!is_array($info)) {
36978            return false;
36979        }
36980
36981        $deps = array();
36982        $used_c = $decl_c = $decl_f = $decl_m = array();
36983        foreach ($info['filelist'] as $file => $fa) {
36984            $tmp = $this->analyzeSourceCode($file);
36985            $used_c = @array_merge($used_c, $tmp['used_classes']);
36986            $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
36987            $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
36988            $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
36989            $inheri = @array_merge($inheri, $tmp['inheritance']);
36990        }
36991
36992        $used_c = array_unique($used_c);
36993        $decl_c = array_unique($decl_c);
36994        $undecl_c = array_diff($used_c, $decl_c);
36995
36996        return array('used_classes' => $used_c,
36997                     'declared_classes' => $decl_c,
36998                     'declared_methods' => $decl_m,
36999                     'declared_functions' => $decl_f,
37000                     'undeclared_classes' => $undecl_c,
37001                     'inheritance' => $inheri,
37002                     );
37003    }
37004
37005    /**
37006     * Download a file through HTTP.  Considers suggested file name in
37007     * Content-disposition: header and can run a callback function for
37008     * different events.  The callback will be called with two
37009     * parameters: the callback type, and parameters.  The implemented
37010     * callback types are:
37011     *
37012     *  'setup'       called at the very beginning, parameter is a UI object
37013     *                that should be used for all output
37014     *  'message'     the parameter is a string with an informational message
37015     *  'saveas'      may be used to save with a different file name, the
37016     *                parameter is the filename that is about to be used.
37017     *                If a 'saveas' callback returns a non-empty string,
37018     *                that file name will be used as the filename instead.
37019     *                Note that $save_dir will not be affected by this, only
37020     *                the basename of the file.
37021     *  'start'       download is starting, parameter is number of bytes
37022     *                that are expected, or -1 if unknown
37023     *  'bytesread'   parameter is the number of bytes read so far
37024     *  'done'        download is complete, parameter is the total number
37025     *                of bytes read
37026     *  'connfailed'  if the TCP connection fails, this callback is called
37027     *                with array(host,port,errno,errmsg)
37028     *  'writefailed' if writing to disk fails, this callback is called
37029     *                with array(destfile,errmsg)
37030     *
37031     * If an HTTP proxy has been configured (http_proxy PEAR_Config
37032     * setting), the proxy will be used.
37033     *
37034     * @param string  $url       the URL to download
37035     * @param object  $ui        PEAR_Frontend_* instance
37036     * @param object  $config    PEAR_Config instance
37037     * @param string  $save_dir  (optional) directory to save file in
37038     * @param mixed   $callback  (optional) function/method to call for status
37039     *                           updates
37040     *
37041     * @return string  Returns the full path of the downloaded file or a PEAR
37042     *                 error on failure.  If the error is caused by
37043     *                 socket-related errors, the error object will
37044     *                 have the fsockopen error code available through
37045     *                 getCode().
37046     *
37047     * @access public
37048     * @deprecated in favor of PEAR_Downloader::downloadHttp()
37049     */
37050    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
37051    {
37052        if (!class_exists('PEAR_Downloader')) {
37053            require_once 'PEAR/Downloader.php';
37054        }
37055        return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback);
37056    }
37057}
37058
37059require_once 'PEAR/Config.php';
37060require_once 'PEAR/PackageFile.php';PEAR-1.9.4/PEAR/Config.php0000644000076500000240000020445211605156614013710 0ustar  helgistaff<?php
37061/**
37062 * PEAR_Config, customized configuration handling for the PEAR Installer
37063 *
37064 * PHP versions 4 and 5
37065 *
37066 * @category   pear
37067 * @package    PEAR
37068 * @author     Stig Bakken <ssb@php.net>
37069 * @author     Greg Beaver <cellog@php.net>
37070 * @copyright  1997-2009 The Authors
37071 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
37072 * @version    CVS: $Id: Config.php 313023 2011-07-06 19:17:11Z dufuz $
37073 * @link       http://pear.php.net/package/PEAR
37074 * @since      File available since Release 0.1
37075 */
37076
37077/**
37078 * Required for error handling
37079 */
37080require_once 'PEAR.php';
37081require_once 'PEAR/Registry.php';
37082require_once 'PEAR/Installer/Role.php';
37083require_once 'System.php';
37084
37085/**
37086 * Last created PEAR_Config instance.
37087 * @var object
37088 */
37089$GLOBALS['_PEAR_Config_instance'] = null;
37090if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) {
37091    $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear';
37092} else {
37093    $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR;
37094}
37095
37096// Below we define constants with default values for all configuration
37097// parameters except username/password.  All of them can have their
37098// defaults set through environment variables.  The reason we use the
37099// PHP_ prefix is for some security, PHP protects environment
37100// variables starting with PHP_*.
37101
37102// default channel and preferred mirror is based on whether we are invoked through
37103// the "pear" or the "pecl" command
37104if (!defined('PEAR_RUNTYPE')) {
37105    define('PEAR_RUNTYPE', 'pear');
37106}
37107
37108if (PEAR_RUNTYPE == 'pear') {
37109    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net');
37110} else {
37111    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net');
37112}
37113
37114if (getenv('PHP_PEAR_SYSCONF_DIR')) {
37115    define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR'));
37116} elseif (getenv('SystemRoot')) {
37117    define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot'));
37118} else {
37119    define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR);
37120}
37121
37122// Default for master_server
37123if (getenv('PHP_PEAR_MASTER_SERVER')) {
37124    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER'));
37125} else {
37126    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net');
37127}
37128
37129// Default for http_proxy
37130if (getenv('PHP_PEAR_HTTP_PROXY')) {
37131    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY'));
37132} elseif (getenv('http_proxy')) {
37133    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy'));
37134} else {
37135    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', '');
37136}
37137
37138// Default for php_dir
37139if (getenv('PHP_PEAR_INSTALL_DIR')) {
37140    define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR'));
37141} else {
37142    if (@file_exists($PEAR_INSTALL_DIR) && is_dir($PEAR_INSTALL_DIR)) {
37143        define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
37144    } else {
37145        define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
37146    }
37147}
37148
37149// Default for ext_dir
37150if (getenv('PHP_PEAR_EXTENSION_DIR')) {
37151    define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR'));
37152} else {
37153    if (ini_get('extension_dir')) {
37154        define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir'));
37155    } elseif (defined('PEAR_EXTENSION_DIR') &&
37156              file_exists(PEAR_EXTENSION_DIR) && is_dir(PEAR_EXTENSION_DIR)) {
37157        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR);
37158    } elseif (defined('PHP_EXTENSION_DIR')) {
37159        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR);
37160    } else {
37161        define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.');
37162    }
37163}
37164
37165// Default for doc_dir
37166if (getenv('PHP_PEAR_DOC_DIR')) {
37167    define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR'));
37168} else {
37169    define('PEAR_CONFIG_DEFAULT_DOC_DIR',
37170           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs');
37171}
37172
37173// Default for bin_dir
37174if (getenv('PHP_PEAR_BIN_DIR')) {
37175    define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR'));
37176} else {
37177    define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR);
37178}
37179
37180// Default for data_dir
37181if (getenv('PHP_PEAR_DATA_DIR')) {
37182    define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR'));
37183} else {
37184    define('PEAR_CONFIG_DEFAULT_DATA_DIR',
37185           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data');
37186}
37187
37188// Default for cfg_dir
37189if (getenv('PHP_PEAR_CFG_DIR')) {
37190    define('PEAR_CONFIG_DEFAULT_CFG_DIR', getenv('PHP_PEAR_CFG_DIR'));
37191} else {
37192    define('PEAR_CONFIG_DEFAULT_CFG_DIR',
37193           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'cfg');
37194}
37195
37196// Default for www_dir
37197if (getenv('PHP_PEAR_WWW_DIR')) {
37198    define('PEAR_CONFIG_DEFAULT_WWW_DIR', getenv('PHP_PEAR_WWW_DIR'));
37199} else {
37200    define('PEAR_CONFIG_DEFAULT_WWW_DIR',
37201           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'www');
37202}
37203
37204// Default for test_dir
37205if (getenv('PHP_PEAR_TEST_DIR')) {
37206    define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR'));
37207} else {
37208    define('PEAR_CONFIG_DEFAULT_TEST_DIR',
37209           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests');
37210}
37211
37212// Default for temp_dir
37213if (getenv('PHP_PEAR_TEMP_DIR')) {
37214    define('PEAR_CONFIG_DEFAULT_TEMP_DIR', getenv('PHP_PEAR_TEMP_DIR'));
37215} else {
37216    define('PEAR_CONFIG_DEFAULT_TEMP_DIR',
37217           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
37218           DIRECTORY_SEPARATOR . 'temp');
37219}
37220
37221// Default for cache_dir
37222if (getenv('PHP_PEAR_CACHE_DIR')) {
37223    define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR'));
37224} else {
37225    define('PEAR_CONFIG_DEFAULT_CACHE_DIR',
37226           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
37227           DIRECTORY_SEPARATOR . 'cache');
37228}
37229
37230// Default for download_dir
37231if (getenv('PHP_PEAR_DOWNLOAD_DIR')) {
37232    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', getenv('PHP_PEAR_DOWNLOAD_DIR'));
37233} else {
37234    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR',
37235           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
37236           DIRECTORY_SEPARATOR . 'download');
37237}
37238
37239// Default for php_bin
37240if (getenv('PHP_PEAR_PHP_BIN')) {
37241    define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN'));
37242} else {
37243    define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR.
37244           DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : ''));
37245}
37246
37247// Default for verbose
37248if (getenv('PHP_PEAR_VERBOSE')) {
37249    define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE'));
37250} else {
37251    define('PEAR_CONFIG_DEFAULT_VERBOSE', 1);
37252}
37253
37254// Default for preferred_state
37255if (getenv('PHP_PEAR_PREFERRED_STATE')) {
37256    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE'));
37257} else {
37258    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable');
37259}
37260
37261// Default for umask
37262if (getenv('PHP_PEAR_UMASK')) {
37263    define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK'));
37264} else {
37265    define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask()));
37266}
37267
37268// Default for cache_ttl
37269if (getenv('PHP_PEAR_CACHE_TTL')) {
37270    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL'));
37271} else {
37272    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600);
37273}
37274
37275// Default for sig_type
37276if (getenv('PHP_PEAR_SIG_TYPE')) {
37277    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE'));
37278} else {
37279    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg');
37280}
37281
37282// Default for sig_bin
37283if (getenv('PHP_PEAR_SIG_BIN')) {
37284    define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN'));
37285} else {
37286    define('PEAR_CONFIG_DEFAULT_SIG_BIN',
37287           System::which(
37288               'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg'));
37289}
37290
37291// Default for sig_keydir
37292if (getenv('PHP_PEAR_SIG_KEYDIR')) {
37293    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR'));
37294} else {
37295    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR',
37296           PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys');
37297}
37298
37299/**
37300 * This is a class for storing configuration data, keeping track of
37301 * which are system-defined, user-defined or defaulted.
37302 * @category   pear
37303 * @package    PEAR
37304 * @author     Stig Bakken <ssb@php.net>
37305 * @author     Greg Beaver <cellog@php.net>
37306 * @copyright  1997-2009 The Authors
37307 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
37308 * @version    Release: 1.9.4
37309 * @link       http://pear.php.net/package/PEAR
37310 * @since      Class available since Release 0.1
37311 */
37312class PEAR_Config extends PEAR
37313{
37314    /**
37315     * Array of config files used.
37316     *
37317     * @var array layer => config file
37318     */
37319    var $files = array(
37320        'system' => '',
37321        'user' => '',
37322        );
37323
37324    var $layers = array();
37325
37326    /**
37327     * Configuration data, two-dimensional array where the first
37328     * dimension is the config layer ('user', 'system' and 'default'),
37329     * and the second dimension is keyname => value.
37330     *
37331     * The order in the first dimension is important!  Earlier
37332     * layers will shadow later ones when a config value is
37333     * requested (if a 'user' value exists, it will be returned first,
37334     * then 'system' and finally 'default').
37335     *
37336     * @var array layer => array(keyname => value, ...)
37337     */
37338    var $configuration = array(
37339        'user' => array(),
37340        'system' => array(),
37341        'default' => array(),
37342        );
37343
37344    /**
37345     * Configuration values that can be set for a channel
37346     *
37347     * All other configuration values can only have a global value
37348     * @var array
37349     * @access private
37350     */
37351    var $_channelConfigInfo = array(
37352        'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'cfg_dir',
37353        'test_dir', 'www_dir', 'php_bin', 'php_prefix', 'php_suffix', 'username',
37354        'password', 'verbose', 'preferred_state', 'umask', 'preferred_mirror', 'php_ini'
37355        );
37356
37357    /**
37358     * Channels that can be accessed
37359     * @see setChannels()
37360     * @var array
37361     * @access private
37362     */
37363    var $_channels = array('pear.php.net', 'pecl.php.net', '__uri');
37364
37365    /**
37366     * This variable is used to control the directory values returned
37367     * @see setInstallRoot();
37368     * @var string|false
37369     * @access private
37370     */
37371    var $_installRoot = false;
37372
37373    /**
37374     * If requested, this will always refer to the registry
37375     * contained in php_dir
37376     * @var PEAR_Registry
37377     */
37378    var $_registry = array();
37379
37380    /**
37381     * @var array
37382     * @access private
37383     */
37384    var $_regInitialized = array();
37385
37386    /**
37387     * @var bool
37388     * @access private
37389     */
37390    var $_noRegistry = false;
37391
37392    /**
37393     * amount of errors found while parsing config
37394     * @var integer
37395     * @access private
37396     */
37397    var $_errorsFound = 0;
37398    var $_lastError = null;
37399
37400    /**
37401     * Information about the configuration data.  Stores the type,
37402     * default value and a documentation string for each configuration
37403     * value.
37404     *
37405     * @var array layer => array(infotype => value, ...)
37406     */
37407    var $configuration_info = array(
37408        // Channels/Internet Access
37409        'default_channel' => array(
37410            'type' => 'string',
37411            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
37412            'doc' => 'the default channel to use for all non explicit commands',
37413            'prompt' => 'Default Channel',
37414            'group' => 'Internet Access',
37415            ),
37416        'preferred_mirror' => array(
37417            'type' => 'string',
37418            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
37419            'doc' => 'the default server or mirror to use for channel actions',
37420            'prompt' => 'Default Channel Mirror',
37421            'group' => 'Internet Access',
37422            ),
37423        'remote_config' => array(
37424            'type' => 'password',
37425            'default' => '',
37426            'doc' => 'ftp url of remote configuration file to use for synchronized install',
37427            'prompt' => 'Remote Configuration File',
37428            'group' => 'Internet Access',
37429            ),
37430        'auto_discover' => array(
37431            'type' => 'integer',
37432            'default' => 0,
37433            'doc' => 'whether to automatically discover new channels',
37434            'prompt' => 'Auto-discover new Channels',
37435            'group' => 'Internet Access',
37436            ),
37437        // Internet Access
37438        'master_server' => array(
37439            'type' => 'string',
37440            'default' => 'pear.php.net',
37441            'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]',
37442            'prompt' => 'PEAR server [DEPRECATED]',
37443            'group' => 'Internet Access',
37444            ),
37445        'http_proxy' => array(
37446            'type' => 'string',
37447            'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY,
37448            'doc' => 'HTTP proxy (host:port) to use when downloading packages',
37449            'prompt' => 'HTTP Proxy Server Address',
37450            'group' => 'Internet Access',
37451            ),
37452        // File Locations
37453        'php_dir' => array(
37454            'type' => 'directory',
37455            'default' => PEAR_CONFIG_DEFAULT_PHP_DIR,
37456            'doc' => 'directory where .php files are installed',
37457            'prompt' => 'PEAR directory',
37458            'group' => 'File Locations',
37459            ),
37460        'ext_dir' => array(
37461            'type' => 'directory',
37462            'default' => PEAR_CONFIG_DEFAULT_EXT_DIR,
37463            'doc' => 'directory where loadable extensions are installed',
37464            'prompt' => 'PHP extension directory',
37465            'group' => 'File Locations',
37466            ),
37467        'doc_dir' => array(
37468            'type' => 'directory',
37469            'default' => PEAR_CONFIG_DEFAULT_DOC_DIR,
37470            'doc' => 'directory where documentation is installed',
37471            'prompt' => 'PEAR documentation directory',
37472            'group' => 'File Locations',
37473            ),
37474        'bin_dir' => array(
37475            'type' => 'directory',
37476            'default' => PEAR_CONFIG_DEFAULT_BIN_DIR,
37477            'doc' => 'directory where executables are installed',
37478            'prompt' => 'PEAR executables directory',
37479            'group' => 'File Locations',
37480            ),
37481        'data_dir' => array(
37482            'type' => 'directory',
37483            'default' => PEAR_CONFIG_DEFAULT_DATA_DIR,
37484            'doc' => 'directory where data files are installed',
37485            'prompt' => 'PEAR data directory',
37486            'group' => 'File Locations (Advanced)',
37487            ),
37488        'cfg_dir' => array(
37489            'type' => 'directory',
37490            'default' => PEAR_CONFIG_DEFAULT_CFG_DIR,
37491            'doc' => 'directory where modifiable configuration files are installed',
37492            'prompt' => 'PEAR configuration file directory',
37493            'group' => 'File Locations (Advanced)',
37494            ),
37495        'www_dir' => array(
37496            'type' => 'directory',
37497            'default' => PEAR_CONFIG_DEFAULT_WWW_DIR,
37498            'doc' => 'directory where www frontend files (html/js) are installed',
37499            'prompt' => 'PEAR www files directory',
37500            'group' => 'File Locations (Advanced)',
37501            ),
37502        'test_dir' => array(
37503            'type' => 'directory',
37504            'default' => PEAR_CONFIG_DEFAULT_TEST_DIR,
37505            'doc' => 'directory where regression tests are installed',
37506            'prompt' => 'PEAR test directory',
37507            'group' => 'File Locations (Advanced)',
37508            ),
37509        'cache_dir' => array(
37510            'type' => 'directory',
37511            'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
37512            'doc' => 'directory which is used for web service cache',
37513            'prompt' => 'PEAR Installer cache directory',
37514            'group' => 'File Locations (Advanced)',
37515            ),
37516        'temp_dir' => array(
37517            'type' => 'directory',
37518            'default' => PEAR_CONFIG_DEFAULT_TEMP_DIR,
37519            'doc' => 'directory which is used for all temp files',
37520            'prompt' => 'PEAR Installer temp directory',
37521            'group' => 'File Locations (Advanced)',
37522            ),
37523        'download_dir' => array(
37524            'type' => 'directory',
37525            'default' => PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR,
37526            'doc' => 'directory which is used for all downloaded files',
37527            'prompt' => 'PEAR Installer download directory',
37528            'group' => 'File Locations (Advanced)',
37529            ),
37530        'php_bin' => array(
37531            'type' => 'file',
37532            'default' => PEAR_CONFIG_DEFAULT_PHP_BIN,
37533            'doc' => 'PHP CLI/CGI binary for executing scripts',
37534            'prompt' => 'PHP CLI/CGI binary',
37535            'group' => 'File Locations (Advanced)',
37536            ),
37537        'php_prefix' => array(
37538            'type' => 'string',
37539            'default' => '',
37540            'doc' => '--program-prefix for php_bin\'s ./configure, used for pecl installs',
37541            'prompt' => '--program-prefix passed to PHP\'s ./configure',
37542            'group' => 'File Locations (Advanced)',
37543            ),
37544        'php_suffix' => array(
37545            'type' => 'string',
37546            'default' => '',
37547            'doc' => '--program-suffix for php_bin\'s ./configure, used for pecl installs',
37548            'prompt' => '--program-suffix passed to PHP\'s ./configure',
37549            'group' => 'File Locations (Advanced)',
37550            ),
37551        'php_ini' => array(
37552            'type' => 'file',
37553            'default' => '',
37554            'doc' => 'location of php.ini in which to enable PECL extensions on install',
37555            'prompt' => 'php.ini location',
37556            'group' => 'File Locations (Advanced)',
37557            ),
37558        // Maintainers
37559        'username' => array(
37560            'type' => 'string',
37561            'default' => '',
37562            'doc' => '(maintainers) your PEAR account name',
37563            'prompt' => 'PEAR username (for maintainers)',
37564            'group' => 'Maintainers',
37565            ),
37566        'password' => array(
37567            'type' => 'password',
37568            'default' => '',
37569            'doc' => '(maintainers) your PEAR account password',
37570            'prompt' => 'PEAR password (for maintainers)',
37571            'group' => 'Maintainers',
37572            ),
37573        // Advanced
37574        'verbose' => array(
37575            'type' => 'integer',
37576            'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
37577            'doc' => 'verbosity level
375780: really quiet
375791: somewhat quiet
375802: verbose
375813: debug',
37582            'prompt' => 'Debug Log Level',
37583            'group' => 'Advanced',
37584            ),
37585        'preferred_state' => array(
37586            'type' => 'set',
37587            'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
37588            'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
37589            'valid_set' => array(
37590                'stable', 'beta', 'alpha', 'devel', 'snapshot'),
37591            'prompt' => 'Preferred Package State',
37592            'group' => 'Advanced',
37593            ),
37594        'umask' => array(
37595            'type' => 'mask',
37596            'default' => PEAR_CONFIG_DEFAULT_UMASK,
37597            'doc' => 'umask used when creating files (Unix-like systems only)',
37598            'prompt' => 'Unix file mask',
37599            'group' => 'Advanced',
37600            ),
37601        'cache_ttl' => array(
37602            'type' => 'integer',
37603            'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL,
37604            'doc' => 'amount of secs where the local cache is used and not updated',
37605            'prompt' => 'Cache TimeToLive',
37606            'group' => 'Advanced',
37607            ),
37608        'sig_type' => array(
37609            'type' => 'set',
37610            'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE,
37611            'doc' => 'which package signature mechanism to use',
37612            'valid_set' => array('gpg'),
37613            'prompt' => 'Package Signature Type',
37614            'group' => 'Maintainers',
37615            ),
37616        'sig_bin' => array(
37617            'type' => 'string',
37618            'default' => PEAR_CONFIG_DEFAULT_SIG_BIN,
37619            'doc' => 'which package signature mechanism to use',
37620            'prompt' => 'Signature Handling Program',
37621            'group' => 'Maintainers',
37622            ),
37623        'sig_keyid' => array(
37624            'type' => 'string',
37625            'default' => '',
37626            'doc' => 'which key to use for signing with',
37627            'prompt' => 'Signature Key Id',
37628            'group' => 'Maintainers',
37629            ),
37630        'sig_keydir' => array(
37631            'type' => 'directory',
37632            'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR,
37633            'doc' => 'directory where signature keys are located',
37634            'prompt' => 'Signature Key Directory',
37635            'group' => 'Maintainers',
37636            ),
37637        // __channels is reserved - used for channel-specific configuration
37638        );
37639
37640    /**
37641     * Constructor.
37642     *
37643     * @param string file to read user-defined options from
37644     * @param string file to read system-wide defaults from
37645     * @param bool   determines whether a registry object "follows"
37646     *               the value of php_dir (is automatically created
37647     *               and moved when php_dir is changed)
37648     * @param bool   if true, fails if configuration files cannot be loaded
37649     *
37650     * @access public
37651     *
37652     * @see PEAR_Config::singleton
37653     */
37654    function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false,
37655                         $strict = true)
37656    {
37657        $this->PEAR();
37658        PEAR_Installer_Role::initializeConfig($this);
37659        $sl = DIRECTORY_SEPARATOR;
37660        if (empty($user_file)) {
37661            if (OS_WINDOWS) {
37662                $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini';
37663            } else {
37664                $user_file = getenv('HOME') . $sl . '.pearrc';
37665            }
37666        }
37667
37668        if (empty($system_file)) {
37669            $system_file = PEAR_CONFIG_SYSCONFDIR . $sl;
37670            if (OS_WINDOWS) {
37671                $system_file .= 'pearsys.ini';
37672            } else {
37673                $system_file .= 'pear.conf';
37674            }
37675        }
37676
37677        $this->layers = array_keys($this->configuration);
37678        $this->files['user']   = $user_file;
37679        $this->files['system'] = $system_file;
37680        if ($user_file && file_exists($user_file)) {
37681            $this->pushErrorHandling(PEAR_ERROR_RETURN);
37682            $this->readConfigFile($user_file, 'user', $strict);
37683            $this->popErrorHandling();
37684            if ($this->_errorsFound > 0) {
37685                return;
37686            }
37687        }
37688
37689        if ($system_file && @file_exists($system_file)) {
37690            $this->mergeConfigFile($system_file, false, 'system', $strict);
37691            if ($this->_errorsFound > 0) {
37692                return;
37693            }
37694
37695        }
37696
37697        if (!$ftp_file) {
37698            $ftp_file = $this->get('remote_config');
37699        }
37700
37701        if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) {
37702            $this->readFTPConfigFile($ftp_file);
37703        }
37704
37705        foreach ($this->configuration_info as $key => $info) {
37706            $this->configuration['default'][$key] = $info['default'];
37707        }
37708
37709        $this->_registry['default'] = &new PEAR_Registry($this->configuration['default']['php_dir']);
37710        $this->_registry['default']->setConfig($this, false);
37711        $this->_regInitialized['default'] = false;
37712        //$GLOBALS['_PEAR_Config_instance'] = &$this;
37713    }
37714
37715    /**
37716     * Return the default locations of user and system configuration files
37717     * @static
37718     */
37719    function getDefaultConfigFiles()
37720    {
37721        $sl = DIRECTORY_SEPARATOR;
37722        if (OS_WINDOWS) {
37723            return array(
37724                'user'   => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini',
37725                'system' =>  PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini'
37726            );
37727        }
37728
37729        return array(
37730            'user'   => getenv('HOME') . $sl . '.pearrc',
37731            'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf'
37732        );
37733    }
37734
37735    /**
37736     * Static singleton method.  If you want to keep only one instance
37737     * of this class in use, this method will give you a reference to
37738     * the last created PEAR_Config object if one exists, or create a
37739     * new object.
37740     *
37741     * @param string (optional) file to read user-defined options from
37742     * @param string (optional) file to read system-wide defaults from
37743     *
37744     * @return object an existing or new PEAR_Config instance
37745     *
37746     * @access public
37747     *
37748     * @see PEAR_Config::PEAR_Config
37749     */
37750    function &singleton($user_file = '', $system_file = '', $strict = true)
37751    {
37752        if (is_object($GLOBALS['_PEAR_Config_instance'])) {
37753            return $GLOBALS['_PEAR_Config_instance'];
37754        }
37755
37756        $t_conf = &new PEAR_Config($user_file, $system_file, false, $strict);
37757        if ($t_conf->_errorsFound > 0) {
37758             return $t_conf->lastError;
37759        }
37760
37761        $GLOBALS['_PEAR_Config_instance'] = &$t_conf;
37762        return $GLOBALS['_PEAR_Config_instance'];
37763    }
37764
37765    /**
37766     * Determine whether any configuration files have been detected, and whether a
37767     * registry object can be retrieved from this configuration.
37768     * @return bool
37769     * @since PEAR 1.4.0a1
37770     */
37771    function validConfiguration()
37772    {
37773        if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) {
37774            return true;
37775        }
37776
37777        return false;
37778    }
37779
37780    /**
37781     * Reads configuration data from a file.  All existing values in
37782     * the config layer are discarded and replaced with data from the
37783     * file.
37784     * @param string file to read from, if NULL or not specified, the
37785     *               last-used file for the same layer (second param) is used
37786     * @param string config layer to insert data into ('user' or 'system')
37787     * @return bool TRUE on success or a PEAR error on failure
37788     */
37789    function readConfigFile($file = null, $layer = 'user', $strict = true)
37790    {
37791        if (empty($this->files[$layer])) {
37792            return $this->raiseError("unknown config layer `$layer'");
37793        }
37794
37795        if ($file === null) {
37796            $file = $this->files[$layer];
37797        }
37798
37799        $data = $this->_readConfigDataFrom($file);
37800        if (PEAR::isError($data)) {
37801            if (!$strict) {
37802                return true;
37803            }
37804
37805            $this->_errorsFound++;
37806            $this->lastError = $data;
37807
37808            return $data;
37809        }
37810
37811        $this->files[$layer] = $file;
37812        $this->_decodeInput($data);
37813        $this->configuration[$layer] = $data;
37814        $this->_setupChannels();
37815        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
37816            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
37817            $this->_registry[$layer]->setConfig($this, false);
37818            $this->_regInitialized[$layer] = false;
37819        } else {
37820            unset($this->_registry[$layer]);
37821        }
37822        return true;
37823    }
37824
37825    /**
37826     * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini
37827     * @return true|PEAR_Error
37828     */
37829    function readFTPConfigFile($path)
37830    {
37831        do { // poor man's try
37832            if (!class_exists('PEAR_FTP')) {
37833                if (!class_exists('PEAR_Common')) {
37834                    require_once 'PEAR/Common.php';
37835                }
37836                if (PEAR_Common::isIncludeable('PEAR/FTP.php')) {
37837                    require_once 'PEAR/FTP.php';
37838                }
37839            }
37840
37841            if (!class_exists('PEAR_FTP')) {
37842                return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config');
37843            }
37844
37845            $this->_ftp = &new PEAR_FTP;
37846            $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN);
37847            $e = $this->_ftp->init($path);
37848            if (PEAR::isError($e)) {
37849                $this->_ftp->popErrorHandling();
37850                return $e;
37851            }
37852
37853            $tmp = System::mktemp('-d');
37854            PEAR_Common::addTempFile($tmp);
37855            $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR .
37856                'pear.ini', false, FTP_BINARY);
37857            if (PEAR::isError($e)) {
37858                $this->_ftp->popErrorHandling();
37859                return $e;
37860            }
37861
37862            PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini');
37863            $this->_ftp->disconnect();
37864            $this->_ftp->popErrorHandling();
37865            $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini';
37866            $e = $this->readConfigFile(null, 'ftp');
37867            if (PEAR::isError($e)) {
37868                return $e;
37869            }
37870
37871            $fail = array();
37872            foreach ($this->configuration_info as $key => $val) {
37873                if (in_array($this->getGroup($key),
37874                      array('File Locations', 'File Locations (Advanced)')) &&
37875                      $this->getType($key) == 'directory') {
37876                    // any directory configs must be set for this to work
37877                    if (!isset($this->configuration['ftp'][$key])) {
37878                        $fail[] = $key;
37879                    }
37880                }
37881            }
37882
37883            if (!count($fail)) {
37884                return true;
37885            }
37886
37887            $fail = '"' . implode('", "', $fail) . '"';
37888            unset($this->files['ftp']);
37889            unset($this->configuration['ftp']);
37890            return PEAR::raiseError('ERROR: Ftp configuration file must set all ' .
37891                'directory configuration variables.  These variables were not set: ' .
37892                $fail);
37893        } while (false); // poor man's catch
37894        unset($this->files['ftp']);
37895        return PEAR::raiseError('no remote host specified');
37896    }
37897
37898    /**
37899     * Reads the existing configurations and creates the _channels array from it
37900     */
37901    function _setupChannels()
37902    {
37903        $set = array_flip(array_values($this->_channels));
37904        foreach ($this->configuration as $layer => $data) {
37905            $i = 1000;
37906            if (isset($data['__channels']) && is_array($data['__channels'])) {
37907                foreach ($data['__channels'] as $channel => $info) {
37908                    $set[$channel] = $i++;
37909                }
37910            }
37911        }
37912        $this->_channels = array_values(array_flip($set));
37913        $this->setChannels($this->_channels);
37914    }
37915
37916    function deleteChannel($channel)
37917    {
37918        $ch = strtolower($channel);
37919        foreach ($this->configuration as $layer => $data) {
37920            if (isset($data['__channels']) && isset($data['__channels'][$ch])) {
37921                unset($this->configuration[$layer]['__channels'][$ch]);
37922            }
37923        }
37924
37925        $this->_channels = array_flip($this->_channels);
37926        unset($this->_channels[$ch]);
37927        $this->_channels = array_flip($this->_channels);
37928    }
37929
37930    /**
37931     * Merges data into a config layer from a file.  Does the same
37932     * thing as readConfigFile, except it does not replace all
37933     * existing values in the config layer.
37934     * @param string file to read from
37935     * @param bool whether to overwrite existing data (default TRUE)
37936     * @param string config layer to insert data into ('user' or 'system')
37937     * @param string if true, errors are returned if file opening fails
37938     * @return bool TRUE on success or a PEAR error on failure
37939     */
37940    function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true)
37941    {
37942        if (empty($this->files[$layer])) {
37943            return $this->raiseError("unknown config layer `$layer'");
37944        }
37945
37946        if ($file === null) {
37947            $file = $this->files[$layer];
37948        }
37949
37950        $data = $this->_readConfigDataFrom($file);
37951        if (PEAR::isError($data)) {
37952            if (!$strict) {
37953                return true;
37954            }
37955
37956            $this->_errorsFound++;
37957            $this->lastError = $data;
37958
37959            return $data;
37960        }
37961
37962        $this->_decodeInput($data);
37963        if ($override) {
37964            $this->configuration[$layer] =
37965                PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data);
37966        } else {
37967            $this->configuration[$layer] =
37968                PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]);
37969        }
37970
37971        $this->_setupChannels();
37972        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
37973            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
37974            $this->_registry[$layer]->setConfig($this, false);
37975            $this->_regInitialized[$layer] = false;
37976        } else {
37977            unset($this->_registry[$layer]);
37978        }
37979        return true;
37980    }
37981
37982    /**
37983     * @param array
37984     * @param array
37985     * @return array
37986     * @static
37987     */
37988    function arrayMergeRecursive($arr2, $arr1)
37989    {
37990        $ret = array();
37991        foreach ($arr2 as $key => $data) {
37992            if (!isset($arr1[$key])) {
37993                $ret[$key] = $data;
37994                unset($arr1[$key]);
37995                continue;
37996            }
37997            if (is_array($data)) {
37998                if (!is_array($arr1[$key])) {
37999                    $ret[$key] = $arr1[$key];
38000                    unset($arr1[$key]);
38001                    continue;
38002                }
38003                $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]);
38004                unset($arr1[$key]);
38005            }
38006        }
38007
38008        return array_merge($ret, $arr1);
38009    }
38010
38011    /**
38012     * Writes data into a config layer from a file.
38013     *
38014     * @param string|null file to read from, or null for default
38015     * @param string config layer to insert data into ('user' or
38016     *               'system')
38017     * @param string|null data to write to config file or null for internal data [DEPRECATED]
38018     * @return bool TRUE on success or a PEAR error on failure
38019     */
38020    function writeConfigFile($file = null, $layer = 'user', $data = null)
38021    {
38022        $this->_lazyChannelSetup($layer);
38023        if ($layer == 'both' || $layer == 'all') {
38024            foreach ($this->files as $type => $file) {
38025                $err = $this->writeConfigFile($file, $type, $data);
38026                if (PEAR::isError($err)) {
38027                    return $err;
38028                }
38029            }
38030            return true;
38031        }
38032
38033        if (empty($this->files[$layer])) {
38034            return $this->raiseError("unknown config file type `$layer'");
38035        }
38036
38037        if ($file === null) {
38038            $file = $this->files[$layer];
38039        }
38040
38041        $data = ($data === null) ? $this->configuration[$layer] : $data;
38042        $this->_encodeOutput($data);
38043        $opt = array('-p', dirname($file));
38044        if (!@System::mkDir($opt)) {
38045            return $this->raiseError("could not create directory: " . dirname($file));
38046        }
38047
38048        if (file_exists($file) && is_file($file) && !is_writeable($file)) {
38049            return $this->raiseError("no write access to $file!");
38050        }
38051
38052        $fp = @fopen($file, "w");
38053        if (!$fp) {
38054            return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed ($php_errormsg)");
38055        }
38056
38057        $contents = "#PEAR_Config 0.9\n" . serialize($data);
38058        if (!@fwrite($fp, $contents)) {
38059            return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed ($php_errormsg)");
38060        }
38061        return true;
38062    }
38063
38064    /**
38065     * Reads configuration data from a file and returns the parsed data
38066     * in an array.
38067     *
38068     * @param string file to read from
38069     * @return array configuration data or a PEAR error on failure
38070     * @access private
38071     */
38072    function _readConfigDataFrom($file)
38073    {
38074        $fp = false;
38075        if (file_exists($file)) {
38076            $fp = @fopen($file, "r");
38077        }
38078
38079        if (!$fp) {
38080            return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed");
38081        }
38082
38083        $size = filesize($file);
38084        $rt = get_magic_quotes_runtime();
38085        set_magic_quotes_runtime(0);
38086        fclose($fp);
38087        $contents = file_get_contents($file);
38088        if (empty($contents)) {
38089            return $this->raiseError('Configuration file "' . $file . '" is empty');
38090        }
38091
38092        set_magic_quotes_runtime($rt);
38093
38094        $version = false;
38095        if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) {
38096            $version = $matches[1];
38097            $contents = substr($contents, strlen($matches[0]));
38098        } else {
38099            // Museum config file
38100            if (substr($contents,0,2) == 'a:') {
38101                $version = '0.1';
38102            }
38103        }
38104
38105        if ($version && version_compare("$version", '1', '<')) {
38106            // no '@', it is possible that unserialize
38107            // raises a notice but it seems to block IO to
38108            // STDOUT if a '@' is used and a notice is raise
38109            $data = unserialize($contents);
38110
38111            if (!is_array($data) && !$data) {
38112                if ($contents == serialize(false)) {
38113                    $data = array();
38114                } else {
38115                    $err = $this->raiseError("PEAR_Config: bad data in $file");
38116                    return $err;
38117                }
38118            }
38119            if (!is_array($data)) {
38120                if (strlen(trim($contents)) > 0) {
38121                    $error = "PEAR_Config: bad data in $file";
38122                    $err = $this->raiseError($error);
38123                    return $err;
38124                }
38125
38126                $data = array();
38127            }
38128        // add parsing of newer formats here...
38129        } else {
38130            $err = $this->raiseError("$file: unknown version `$version'");
38131            return $err;
38132        }
38133
38134        return $data;
38135    }
38136
38137    /**
38138    * Gets the file used for storing the config for a layer
38139    *
38140    * @param string $layer 'user' or 'system'
38141    */
38142    function getConfFile($layer)
38143    {
38144        return $this->files[$layer];
38145    }
38146
38147    /**
38148     * @param string Configuration class name, used for detecting duplicate calls
38149     * @param array information on a role as parsed from its xml file
38150     * @return true|PEAR_Error
38151     * @access private
38152     */
38153    function _addConfigVars($class, $vars)
38154    {
38155        static $called = array();
38156        if (isset($called[$class])) {
38157            return;
38158        }
38159
38160        $called[$class] = 1;
38161        if (count($vars) > 3) {
38162            return $this->raiseError('Roles can only define 3 new config variables or less');
38163        }
38164
38165        foreach ($vars as $name => $var) {
38166            if (!is_array($var)) {
38167                return $this->raiseError('Configuration information must be an array');
38168            }
38169
38170            if (!isset($var['type'])) {
38171                return $this->raiseError('Configuration information must contain a type');
38172            } elseif (!in_array($var['type'],
38173                    array('string', 'mask', 'password', 'directory', 'file', 'set'))) {
38174                  return $this->raiseError(
38175                      'Configuration type must be one of directory, file, string, ' .
38176                      'mask, set, or password');
38177            }
38178            if (!isset($var['default'])) {
38179                return $this->raiseError(
38180                    'Configuration information must contain a default value ("default" index)');
38181            }
38182
38183            if (is_array($var['default'])) {
38184                $real_default = '';
38185                foreach ($var['default'] as $config_var => $val) {
38186                    if (strpos($config_var, 'text') === 0) {
38187                        $real_default .= $val;
38188                    } elseif (strpos($config_var, 'constant') === 0) {
38189                        if (!defined($val)) {
38190                            return $this->raiseError(
38191                                'Unknown constant "' . $val . '" requested in ' .
38192                                'default value for configuration variable "' .
38193                                $name . '"');
38194                        }
38195
38196                        $real_default .= constant($val);
38197                    } elseif (isset($this->configuration_info[$config_var])) {
38198                        $real_default .=
38199                            $this->configuration_info[$config_var]['default'];
38200                    } else {
38201                        return $this->raiseError(
38202                            'Unknown request for "' . $config_var . '" value in ' .
38203                            'default value for configuration variable "' .
38204                            $name . '"');
38205                    }
38206                }
38207                $var['default'] = $real_default;
38208            }
38209
38210            if ($var['type'] == 'integer') {
38211                $var['default'] = (integer) $var['default'];
38212            }
38213
38214            if (!isset($var['doc'])) {
38215                return $this->raiseError(
38216                    'Configuration information must contain a summary ("doc" index)');
38217            }
38218
38219            if (!isset($var['prompt'])) {
38220                return $this->raiseError(
38221                    'Configuration information must contain a simple prompt ("prompt" index)');
38222            }
38223
38224            if (!isset($var['group'])) {
38225                return $this->raiseError(
38226                    'Configuration information must contain a simple group ("group" index)');
38227            }
38228
38229            if (isset($this->configuration_info[$name])) {
38230                return $this->raiseError('Configuration variable "' . $name .
38231                    '" already exists');
38232            }
38233
38234            $this->configuration_info[$name] = $var;
38235            // fix bug #7351: setting custom config variable in a channel fails
38236            $this->_channelConfigInfo[] = $name;
38237        }
38238
38239        return true;
38240    }
38241
38242    /**
38243     * Encodes/scrambles configuration data before writing to files.
38244     * Currently, 'password' values will be base64-encoded as to avoid
38245     * that people spot cleartext passwords by accident.
38246     *
38247     * @param array (reference) array to encode values in
38248     * @return bool TRUE on success
38249     * @access private
38250     */
38251    function _encodeOutput(&$data)
38252    {
38253        foreach ($data as $key => $value) {
38254            if ($key == '__channels') {
38255                foreach ($data['__channels'] as $channel => $blah) {
38256                    $this->_encodeOutput($data['__channels'][$channel]);
38257                }
38258            }
38259
38260            if (!isset($this->configuration_info[$key])) {
38261                continue;
38262            }
38263
38264            $type = $this->configuration_info[$key]['type'];
38265            switch ($type) {
38266                // we base64-encode passwords so they are at least
38267                // not shown in plain by accident
38268                case 'password': {
38269                    $data[$key] = base64_encode($data[$key]);
38270                    break;
38271                }
38272                case 'mask': {
38273                    $data[$key] = octdec($data[$key]);
38274                    break;
38275                }
38276            }
38277        }
38278
38279        return true;
38280    }
38281
38282    /**
38283     * Decodes/unscrambles configuration data after reading from files.
38284     *
38285     * @param array (reference) array to encode values in
38286     * @return bool TRUE on success
38287     * @access private
38288     *
38289     * @see PEAR_Config::_encodeOutput
38290     */
38291    function _decodeInput(&$data)
38292    {
38293        if (!is_array($data)) {
38294            return true;
38295        }
38296
38297        foreach ($data as $key => $value) {
38298            if ($key == '__channels') {
38299                foreach ($data['__channels'] as $channel => $blah) {
38300                    $this->_decodeInput($data['__channels'][$channel]);
38301                }
38302            }
38303
38304            if (!isset($this->configuration_info[$key])) {
38305                continue;
38306            }
38307
38308            $type = $this->configuration_info[$key]['type'];
38309            switch ($type) {
38310                case 'password': {
38311                    $data[$key] = base64_decode($data[$key]);
38312                    break;
38313                }
38314                case 'mask': {
38315                    $data[$key] = decoct($data[$key]);
38316                    break;
38317                }
38318            }
38319        }
38320
38321        return true;
38322    }
38323
38324    /**
38325     * Retrieve the default channel.
38326     *
38327     * On startup, channels are not initialized, so if the default channel is not
38328     * pear.php.net, then initialize the config.
38329     * @param string registry layer
38330     * @return string|false
38331     */
38332    function getDefaultChannel($layer = null)
38333    {
38334        $ret = false;
38335        if ($layer === null) {
38336            foreach ($this->layers as $layer) {
38337                if (isset($this->configuration[$layer]['default_channel'])) {
38338                    $ret = $this->configuration[$layer]['default_channel'];
38339                    break;
38340                }
38341            }
38342        } elseif (isset($this->configuration[$layer]['default_channel'])) {
38343            $ret = $this->configuration[$layer]['default_channel'];
38344        }
38345
38346        if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') {
38347            $ret = 'pecl.php.net';
38348        }
38349
38350        if ($ret) {
38351            if ($ret != 'pear.php.net') {
38352                $this->_lazyChannelSetup();
38353            }
38354
38355            return $ret;
38356        }
38357
38358        return PEAR_CONFIG_DEFAULT_CHANNEL;
38359    }
38360
38361    /**
38362     * Returns a configuration value, prioritizing layers as per the
38363     * layers property.
38364     *
38365     * @param string config key
38366     * @return mixed the config value, or NULL if not found
38367     * @access public
38368     */
38369    function get($key, $layer = null, $channel = false)
38370    {
38371        if (!isset($this->configuration_info[$key])) {
38372            return null;
38373        }
38374
38375        if ($key == '__channels') {
38376            return null;
38377        }
38378
38379        if ($key == 'default_channel') {
38380            return $this->getDefaultChannel($layer);
38381        }
38382
38383        if (!$channel) {
38384            $channel = $this->getDefaultChannel();
38385        } elseif ($channel != 'pear.php.net') {
38386            $this->_lazyChannelSetup();
38387        }
38388        $channel = strtolower($channel);
38389
38390        $test = (in_array($key, $this->_channelConfigInfo)) ?
38391            $this->_getChannelValue($key, $layer, $channel) :
38392            null;
38393        if ($test !== null) {
38394            if ($this->_installRoot) {
38395                if (in_array($this->getGroup($key),
38396                      array('File Locations', 'File Locations (Advanced)')) &&
38397                      $this->getType($key) == 'directory') {
38398                    return $this->_prependPath($test, $this->_installRoot);
38399                }
38400            }
38401            return $test;
38402        }
38403
38404        if ($layer === null) {
38405            foreach ($this->layers as $layer) {
38406                if (isset($this->configuration[$layer][$key])) {
38407                    $test = $this->configuration[$layer][$key];
38408                    if ($this->_installRoot) {
38409                        if (in_array($this->getGroup($key),
38410                              array('File Locations', 'File Locations (Advanced)')) &&
38411                              $this->getType($key) == 'directory') {
38412                            return $this->_prependPath($test, $this->_installRoot);
38413                        }
38414                    }
38415
38416                    if ($key == 'preferred_mirror') {
38417                        $reg = &$this->getRegistry();
38418                        if (is_object($reg)) {
38419                            $chan = &$reg->getChannel($channel);
38420                            if (PEAR::isError($chan)) {
38421                                return $channel;
38422                            }
38423
38424                            if (!$chan->getMirror($test) && $chan->getName() != $test) {
38425                                return $channel; // mirror does not exist
38426                            }
38427                        }
38428                    }
38429                    return $test;
38430                }
38431            }
38432        } elseif (isset($this->configuration[$layer][$key])) {
38433            $test = $this->configuration[$layer][$key];
38434            if ($this->_installRoot) {
38435                if (in_array($this->getGroup($key),
38436                      array('File Locations', 'File Locations (Advanced)')) &&
38437                      $this->getType($key) == 'directory') {
38438                    return $this->_prependPath($test, $this->_installRoot);
38439                }
38440            }
38441
38442            if ($key == 'preferred_mirror') {
38443                $reg = &$this->getRegistry();
38444                if (is_object($reg)) {
38445                    $chan = &$reg->getChannel($channel);
38446                    if (PEAR::isError($chan)) {
38447                        return $channel;
38448                    }
38449
38450                    if (!$chan->getMirror($test) && $chan->getName() != $test) {
38451                        return $channel; // mirror does not exist
38452                    }
38453                }
38454            }
38455
38456            return $test;
38457        }
38458
38459        return null;
38460    }
38461
38462    /**
38463     * Returns a channel-specific configuration value, prioritizing layers as per the
38464     * layers property.
38465     *
38466     * @param string config key
38467     * @return mixed the config value, or NULL if not found
38468     * @access private
38469     */
38470    function _getChannelValue($key, $layer, $channel)
38471    {
38472        if ($key == '__channels' || $channel == 'pear.php.net') {
38473            return null;
38474        }
38475
38476        $ret = null;
38477        if ($layer === null) {
38478            foreach ($this->layers as $ilayer) {
38479                if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) {
38480                    $ret = $this->configuration[$ilayer]['__channels'][$channel][$key];
38481                    break;
38482                }
38483            }
38484        } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
38485            $ret = $this->configuration[$layer]['__channels'][$channel][$key];
38486        }
38487
38488        if ($key != 'preferred_mirror') {
38489            return $ret;
38490        }
38491
38492
38493        if ($ret !== null) {
38494            $reg = &$this->getRegistry($layer);
38495            if (is_object($reg)) {
38496                $chan = &$reg->getChannel($channel);
38497                if (PEAR::isError($chan)) {
38498                    return $channel;
38499                }
38500
38501                if (!$chan->getMirror($ret) && $chan->getName() != $ret) {
38502                    return $channel; // mirror does not exist
38503                }
38504            }
38505
38506            return $ret;
38507        }
38508
38509        if ($channel != $this->getDefaultChannel($layer)) {
38510            return $channel; // we must use the channel name as the preferred mirror
38511                             // if the user has not chosen an alternate
38512        }
38513
38514        return $this->getDefaultChannel($layer);
38515    }
38516
38517    /**
38518     * Set a config value in a specific layer (defaults to 'user').
38519     * Enforces the types defined in the configuration_info array.  An
38520     * integer config variable will be cast to int, and a set config
38521     * variable will be validated against its legal values.
38522     *
38523     * @param string config key
38524     * @param string config value
38525     * @param string (optional) config layer
38526     * @param string channel to set this value for, or null for global value
38527     * @return bool TRUE on success, FALSE on failure
38528     */
38529    function set($key, $value, $layer = 'user', $channel = false)
38530    {
38531        if ($key == '__channels') {
38532            return false;
38533        }
38534
38535        if (!isset($this->configuration[$layer])) {
38536            return false;
38537        }
38538
38539        if ($key == 'default_channel') {
38540            // can only set this value globally
38541            $channel = 'pear.php.net';
38542            if ($value != 'pear.php.net') {
38543                $this->_lazyChannelSetup($layer);
38544            }
38545        }
38546
38547        if ($key == 'preferred_mirror') {
38548            if ($channel == '__uri') {
38549                return false; // can't set the __uri pseudo-channel's mirror
38550            }
38551
38552            $reg = &$this->getRegistry($layer);
38553            if (is_object($reg)) {
38554                $chan = &$reg->getChannel($channel ? $channel : 'pear.php.net');
38555                if (PEAR::isError($chan)) {
38556                    return false;
38557                }
38558
38559                if (!$chan->getMirror($value) && $chan->getName() != $value) {
38560                    return false; // mirror does not exist
38561                }
38562            }
38563        }
38564
38565        if (!isset($this->configuration_info[$key])) {
38566            return false;
38567        }
38568
38569        extract($this->configuration_info[$key]);
38570        switch ($type) {
38571            case 'integer':
38572                $value = (int)$value;
38573                break;
38574            case 'set': {
38575                // If a valid_set is specified, require the value to
38576                // be in the set.  If there is no valid_set, accept
38577                // any value.
38578                if ($valid_set) {
38579                    reset($valid_set);
38580                    if ((key($valid_set) === 0 && !in_array($value, $valid_set)) ||
38581                        (key($valid_set) !== 0 && empty($valid_set[$value])))
38582                    {
38583                        return false;
38584                    }
38585                }
38586                break;
38587            }
38588        }
38589
38590        if (!$channel) {
38591            $channel = $this->get('default_channel', null, 'pear.php.net');
38592        }
38593
38594        if (!in_array($channel, $this->_channels)) {
38595            $this->_lazyChannelSetup($layer);
38596            $reg = &$this->getRegistry($layer);
38597            if ($reg) {
38598                $channel = $reg->channelName($channel);
38599            }
38600
38601            if (!in_array($channel, $this->_channels)) {
38602                return false;
38603            }
38604        }
38605
38606        if ($channel != 'pear.php.net') {
38607            if (in_array($key, $this->_channelConfigInfo)) {
38608                $this->configuration[$layer]['__channels'][$channel][$key] = $value;
38609                return true;
38610            }
38611
38612            return false;
38613        }
38614
38615        if ($key == 'default_channel') {
38616            if (!isset($reg)) {
38617                $reg = &$this->getRegistry($layer);
38618                if (!$reg) {
38619                    $reg = &$this->getRegistry();
38620                }
38621            }
38622
38623            if ($reg) {
38624                $value = $reg->channelName($value);
38625            }
38626
38627            if (!$value) {
38628                return false;
38629            }
38630        }
38631
38632        $this->configuration[$layer][$key] = $value;
38633        if ($key == 'php_dir' && !$this->_noRegistry) {
38634            if (!isset($this->_registry[$layer]) ||
38635                  $value != $this->_registry[$layer]->install_dir) {
38636                $this->_registry[$layer] = &new PEAR_Registry($value);
38637                $this->_regInitialized[$layer] = false;
38638                $this->_registry[$layer]->setConfig($this, false);
38639            }
38640        }
38641
38642        return true;
38643    }
38644
38645    function _lazyChannelSetup($uselayer = false)
38646    {
38647        if ($this->_noRegistry) {
38648            return;
38649        }
38650
38651        $merge = false;
38652        foreach ($this->_registry as $layer => $p) {
38653            if ($uselayer && $uselayer != $layer) {
38654                continue;
38655            }
38656
38657            if (!$this->_regInitialized[$layer]) {
38658                if ($layer == 'default' && isset($this->_registry['user']) ||
38659                      isset($this->_registry['system'])) {
38660                    // only use the default registry if there are no alternatives
38661                    continue;
38662                }
38663
38664                if (!is_object($this->_registry[$layer])) {
38665                    if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) {
38666                        $this->_registry[$layer] = &new PEAR_Registry($phpdir);
38667                        $this->_registry[$layer]->setConfig($this, false);
38668                        $this->_regInitialized[$layer] = false;
38669                    } else {
38670                        unset($this->_registry[$layer]);
38671                        return;
38672                    }
38673                }
38674
38675                $this->setChannels($this->_registry[$layer]->listChannels(), $merge);
38676                $this->_regInitialized[$layer] = true;
38677                $merge = true;
38678            }
38679        }
38680    }
38681
38682    /**
38683     * Set the list of channels.
38684     *
38685     * This should be set via a call to {@link PEAR_Registry::listChannels()}
38686     * @param array
38687     * @param bool
38688     * @return bool success of operation
38689     */
38690    function setChannels($channels, $merge = false)
38691    {
38692        if (!is_array($channels)) {
38693            return false;
38694        }
38695
38696        if ($merge) {
38697            $this->_channels = array_merge($this->_channels, $channels);
38698        } else {
38699            $this->_channels = $channels;
38700        }
38701
38702        foreach ($channels as $channel) {
38703            $channel = strtolower($channel);
38704            if ($channel == 'pear.php.net') {
38705                continue;
38706            }
38707
38708            foreach ($this->layers as $layer) {
38709                if (!isset($this->configuration[$layer]['__channels'])) {
38710                    $this->configuration[$layer]['__channels'] = array();
38711                }
38712                if (!isset($this->configuration[$layer]['__channels'][$channel])
38713                      || !is_array($this->configuration[$layer]['__channels'][$channel])) {
38714                    $this->configuration[$layer]['__channels'][$channel] = array();
38715                }
38716            }
38717        }
38718
38719        return true;
38720    }
38721
38722    /**
38723     * Get the type of a config value.
38724     *
38725     * @param string  config key
38726     *
38727     * @return string type, one of "string", "integer", "file",
38728     * "directory", "set" or "password".
38729     *
38730     * @access public
38731     *
38732     */
38733    function getType($key)
38734    {
38735        if (isset($this->configuration_info[$key])) {
38736            return $this->configuration_info[$key]['type'];
38737        }
38738        return false;
38739    }
38740
38741    /**
38742     * Get the documentation for a config value.
38743     *
38744     * @param string  config key
38745     * @return string documentation string
38746     *
38747     * @access public
38748     *
38749     */
38750    function getDocs($key)
38751    {
38752        if (isset($this->configuration_info[$key])) {
38753            return $this->configuration_info[$key]['doc'];
38754        }
38755
38756        return false;
38757    }
38758
38759    /**
38760     * Get the short documentation for a config value.
38761     *
38762     * @param string  config key
38763     * @return string short documentation string
38764     *
38765     * @access public
38766     *
38767     */
38768    function getPrompt($key)
38769    {
38770        if (isset($this->configuration_info[$key])) {
38771            return $this->configuration_info[$key]['prompt'];
38772        }
38773
38774        return false;
38775    }
38776
38777    /**
38778     * Get the parameter group for a config key.
38779     *
38780     * @param string  config key
38781     * @return string parameter group
38782     *
38783     * @access public
38784     *
38785     */
38786    function getGroup($key)
38787    {
38788        if (isset($this->configuration_info[$key])) {
38789            return $this->configuration_info[$key]['group'];
38790        }
38791
38792        return false;
38793    }
38794
38795    /**
38796     * Get the list of parameter groups.
38797     *
38798     * @return array list of parameter groups
38799     *
38800     * @access public
38801     *
38802     */
38803    function getGroups()
38804    {
38805        $tmp = array();
38806        foreach ($this->configuration_info as $key => $info) {
38807            $tmp[$info['group']] = 1;
38808        }
38809
38810        return array_keys($tmp);
38811    }
38812
38813    /**
38814     * Get the list of the parameters in a group.
38815     *
38816     * @param string $group parameter group
38817     * @return array list of parameters in $group
38818     *
38819     * @access public
38820     *
38821     */
38822    function getGroupKeys($group)
38823    {
38824        $keys = array();
38825        foreach ($this->configuration_info as $key => $info) {
38826            if ($info['group'] == $group) {
38827                $keys[] = $key;
38828            }
38829        }
38830
38831        return $keys;
38832    }
38833
38834    /**
38835     * Get the list of allowed set values for a config value.  Returns
38836     * NULL for config values that are not sets.
38837     *
38838     * @param string  config key
38839     * @return array enumerated array of set values, or NULL if the
38840     *               config key is unknown or not a set
38841     *
38842     * @access public
38843     *
38844     */
38845    function getSetValues($key)
38846    {
38847        if (isset($this->configuration_info[$key]) &&
38848            isset($this->configuration_info[$key]['type']) &&
38849            $this->configuration_info[$key]['type'] == 'set')
38850        {
38851            $valid_set = $this->configuration_info[$key]['valid_set'];
38852            reset($valid_set);
38853            if (key($valid_set) === 0) {
38854                return $valid_set;
38855            }
38856
38857            return array_keys($valid_set);
38858        }
38859
38860        return null;
38861    }
38862
38863    /**
38864     * Get all the current config keys.
38865     *
38866     * @return array simple array of config keys
38867     *
38868     * @access public
38869     */
38870    function getKeys()
38871    {
38872        $keys = array();
38873        foreach ($this->layers as $layer) {
38874            $test = $this->configuration[$layer];
38875            if (isset($test['__channels'])) {
38876                foreach ($test['__channels'] as $channel => $configs) {
38877                    $keys = array_merge($keys, $configs);
38878                }
38879            }
38880
38881            unset($test['__channels']);
38882            $keys = array_merge($keys, $test);
38883
38884        }
38885        return array_keys($keys);
38886    }
38887
38888    /**
38889     * Remove the a config key from a specific config layer.
38890     *
38891     * @param string config key
38892     * @param string (optional) config layer
38893     * @param string (optional) channel (defaults to default channel)
38894     * @return bool TRUE on success, FALSE on failure
38895     *
38896     * @access public
38897     */
38898    function remove($key, $layer = 'user', $channel = null)
38899    {
38900        if ($channel === null) {
38901            $channel = $this->getDefaultChannel();
38902        }
38903
38904        if ($channel !== 'pear.php.net') {
38905            if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
38906                unset($this->configuration[$layer]['__channels'][$channel][$key]);
38907                return true;
38908            }
38909        }
38910
38911        if (isset($this->configuration[$layer][$key])) {
38912            unset($this->configuration[$layer][$key]);
38913            return true;
38914        }
38915
38916        return false;
38917    }
38918
38919    /**
38920     * Temporarily remove an entire config layer.  USE WITH CARE!
38921     *
38922     * @param string config key
38923     * @param string (optional) config layer
38924     * @return bool TRUE on success, FALSE on failure
38925     *
38926     * @access public
38927     */
38928    function removeLayer($layer)
38929    {
38930        if (isset($this->configuration[$layer])) {
38931            $this->configuration[$layer] = array();
38932            return true;
38933        }
38934
38935        return false;
38936    }
38937
38938    /**
38939     * Stores configuration data in a layer.
38940     *
38941     * @param string config layer to store
38942     * @return bool TRUE on success, or PEAR error on failure
38943     *
38944     * @access public
38945     */
38946    function store($layer = 'user', $data = null)
38947    {
38948        return $this->writeConfigFile(null, $layer, $data);
38949    }
38950
38951    /**
38952     * Tells what config layer that gets to define a key.
38953     *
38954     * @param string config key
38955     * @param boolean return the defining channel
38956     *
38957     * @return string|array the config layer, or an empty string if not found.
38958     *
38959     *         if $returnchannel, the return is an array array('layer' => layername,
38960     *         'channel' => channelname), or an empty string if not found
38961     *
38962     * @access public
38963     */
38964    function definedBy($key, $returnchannel = false)
38965    {
38966        foreach ($this->layers as $layer) {
38967            $channel = $this->getDefaultChannel();
38968            if ($channel !== 'pear.php.net') {
38969                if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
38970                    if ($returnchannel) {
38971                        return array('layer' => $layer, 'channel' => $channel);
38972                    }
38973                    return $layer;
38974                }
38975            }
38976
38977            if (isset($this->configuration[$layer][$key])) {
38978                if ($returnchannel) {
38979                    return array('layer' => $layer, 'channel' => 'pear.php.net');
38980                }
38981                return $layer;
38982            }
38983        }
38984
38985        return '';
38986    }
38987
38988    /**
38989     * Tells whether a given key exists as a config value.
38990     *
38991     * @param string config key
38992     * @return bool whether <config key> exists in this object
38993     *
38994     * @access public
38995     */
38996    function isDefined($key)
38997    {
38998        foreach ($this->layers as $layer) {
38999            if (isset($this->configuration[$layer][$key])) {
39000                return true;
39001            }
39002        }
39003
39004        return false;
39005    }
39006
39007    /**
39008     * Tells whether a given config layer exists.
39009     *
39010     * @param string config layer
39011     * @return bool whether <config layer> exists in this object
39012     *
39013     * @access public
39014     */
39015    function isDefinedLayer($layer)
39016    {
39017        return isset($this->configuration[$layer]);
39018    }
39019
39020    /**
39021     * Returns the layers defined (except the 'default' one)
39022     *
39023     * @return array of the defined layers
39024     */
39025    function getLayers()
39026    {
39027        $cf = $this->configuration;
39028        unset($cf['default']);
39029        return array_keys($cf);
39030    }
39031
39032    function apiVersion()
39033    {
39034        return '1.1';
39035    }
39036
39037    /**
39038     * @return PEAR_Registry
39039     */
39040    function &getRegistry($use = null)
39041    {
39042        $layer = $use === null ? 'user' : $use;
39043        if (isset($this->_registry[$layer])) {
39044            return $this->_registry[$layer];
39045        } elseif ($use === null && isset($this->_registry['system'])) {
39046            return $this->_registry['system'];
39047        } elseif ($use === null && isset($this->_registry['default'])) {
39048            return $this->_registry['default'];
39049        } elseif ($use) {
39050            $a = false;
39051            return $a;
39052        }
39053
39054        // only go here if null was passed in
39055        echo "CRITICAL ERROR: Registry could not be initialized from any value";
39056        exit(1);
39057    }
39058
39059    /**
39060     * This is to allow customization like the use of installroot
39061     * @param PEAR_Registry
39062     * @return bool
39063     */
39064    function setRegistry(&$reg, $layer = 'user')
39065    {
39066        if ($this->_noRegistry) {
39067            return false;
39068        }
39069
39070        if (!in_array($layer, array('user', 'system'))) {
39071            return false;
39072        }
39073
39074        $this->_registry[$layer] = &$reg;
39075        if (is_object($reg)) {
39076            $this->_registry[$layer]->setConfig($this, false);
39077        }
39078
39079        return true;
39080    }
39081
39082    function noRegistry()
39083    {
39084        $this->_noRegistry = true;
39085    }
39086
39087    /**
39088     * @return PEAR_REST
39089     */
39090    function &getREST($version, $options = array())
39091    {
39092        $version = str_replace('.', '', $version);
39093        if (!class_exists($class = 'PEAR_REST_' . $version)) {
39094            require_once 'PEAR/REST/' . $version . '.php';
39095        }
39096
39097        $remote = &new $class($this, $options);
39098        return $remote;
39099    }
39100
39101    /**
39102     * The ftp server is set in {@link readFTPConfigFile()}.  It exists only if a
39103     * remote configuration file has been specified
39104     * @return PEAR_FTP|false
39105     */
39106    function &getFTP()
39107    {
39108        if (isset($this->_ftp)) {
39109            return $this->_ftp;
39110        }
39111
39112        $a = false;
39113        return $a;
39114    }
39115
39116    function _prependPath($path, $prepend)
39117    {
39118        if (strlen($prepend) > 0) {
39119            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
39120                if (preg_match('/^[a-z]:/i', $prepend)) {
39121                    $prepend = substr($prepend, 2);
39122                } elseif ($prepend{0} != '\\') {
39123                    $prepend = "\\$prepend";
39124                }
39125                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
39126            } else {
39127                $path = $prepend . $path;
39128            }
39129        }
39130        return $path;
39131    }
39132
39133    /**
39134     * @param string|false installation directory to prepend to all _dir variables, or false to
39135     *                     disable
39136     */
39137    function setInstallRoot($root)
39138    {
39139        if (substr($root, -1) == DIRECTORY_SEPARATOR) {
39140            $root = substr($root, 0, -1);
39141        }
39142        $old = $this->_installRoot;
39143        $this->_installRoot = $root;
39144        if (($old != $root) && !$this->_noRegistry) {
39145            foreach (array_keys($this->_registry) as $layer) {
39146                if ($layer == 'ftp' || !isset($this->_registry[$layer])) {
39147                    continue;
39148                }
39149                $this->_registry[$layer] =
39150                    &new PEAR_Registry($this->get('php_dir', $layer, 'pear.php.net'));
39151                $this->_registry[$layer]->setConfig($this, false);
39152                $this->_regInitialized[$layer] = false;
39153            }
39154        }
39155    }
39156}
39157PEAR-1.9.4/PEAR/DependencyDB.php0000644000076500000240000005725611605156614014777 0ustar  helgistaff<?php
39158/**
39159 * PEAR_DependencyDB, advanced installed packages dependency database
39160 *
39161 * PHP versions 4 and 5
39162 *
39163 * @category   pear
39164 * @package    PEAR
39165 * @author     Tomas V. V. Cox <cox@idecnet.com>
39166 * @author     Greg Beaver <cellog@php.net>
39167 * @copyright  1997-2009 The Authors
39168 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
39169 * @version    CVS: $Id: DependencyDB.php 313023 2011-07-06 19:17:11Z dufuz $
39170 * @link       http://pear.php.net/package/PEAR
39171 * @since      File available since Release 1.4.0a1
39172 */
39173
39174/**
39175 * Needed for error handling
39176 */
39177require_once 'PEAR.php';
39178require_once 'PEAR/Config.php';
39179
39180$GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array();
39181/**
39182 * Track dependency relationships between installed packages
39183 * @category   pear
39184 * @package    PEAR
39185 * @author     Greg Beaver <cellog@php.net>
39186 * @author     Tomas V.V.Cox <cox@idec.net.com>
39187 * @copyright  1997-2009 The Authors
39188 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
39189 * @version    Release: 1.9.4
39190 * @link       http://pear.php.net/package/PEAR
39191 * @since      Class available since Release 1.4.0a1
39192 */
39193class PEAR_DependencyDB
39194{
39195    // {{{ properties
39196
39197    /**
39198     * This is initialized by {@link setConfig()}
39199     * @var PEAR_Config
39200     * @access private
39201     */
39202    var $_config;
39203    /**
39204     * This is initialized by {@link setConfig()}
39205     * @var PEAR_Registry
39206     * @access private
39207     */
39208    var $_registry;
39209    /**
39210     * Filename of the dependency DB (usually .depdb)
39211     * @var string
39212     * @access private
39213     */
39214    var $_depdb = false;
39215    /**
39216     * File name of the lockfile (usually .depdblock)
39217     * @var string
39218     * @access private
39219     */
39220    var $_lockfile = false;
39221    /**
39222     * Open file resource for locking the lockfile
39223     * @var resource|false
39224     * @access private
39225     */
39226    var $_lockFp = false;
39227    /**
39228     * API version of this class, used to validate a file on-disk
39229     * @var string
39230     * @access private
39231     */
39232    var $_version = '1.0';
39233    /**
39234     * Cached dependency database file
39235     * @var array|null
39236     * @access private
39237     */
39238    var $_cache;
39239
39240    // }}}
39241    // {{{ & singleton()
39242
39243    /**
39244     * Get a raw dependency database.  Calls setConfig() and assertDepsDB()
39245     * @param PEAR_Config
39246     * @param string|false full path to the dependency database, or false to use default
39247     * @return PEAR_DependencyDB|PEAR_Error
39248     * @static
39249     */
39250    function &singleton(&$config, $depdb = false)
39251    {
39252        $phpdir = $config->get('php_dir', null, 'pear.php.net');
39253        if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir])) {
39254            $a = new PEAR_DependencyDB;
39255            $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir] = &$a;
39256            $a->setConfig($config, $depdb);
39257            $e = $a->assertDepsDB();
39258            if (PEAR::isError($e)) {
39259                return $e;
39260            }
39261        }
39262
39263        return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir];
39264    }
39265
39266    /**
39267     * Set up the registry/location of dependency DB
39268     * @param PEAR_Config|false
39269     * @param string|false full path to the dependency database, or false to use default
39270     */
39271    function setConfig(&$config, $depdb = false)
39272    {
39273        if (!$config) {
39274            $this->_config = &PEAR_Config::singleton();
39275        } else {
39276            $this->_config = &$config;
39277        }
39278
39279        $this->_registry = &$this->_config->getRegistry();
39280        if (!$depdb) {
39281            $this->_depdb = $this->_config->get('php_dir', null, 'pear.php.net') .
39282                DIRECTORY_SEPARATOR . '.depdb';
39283        } else {
39284            $this->_depdb = $depdb;
39285        }
39286
39287        $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock';
39288    }
39289    // }}}
39290
39291    function hasWriteAccess()
39292    {
39293        if (!file_exists($this->_depdb)) {
39294            $dir = $this->_depdb;
39295            while ($dir && $dir != '.') {
39296                $dir = dirname($dir); // cd ..
39297                if ($dir != '.' && file_exists($dir)) {
39298                    if (is_writeable($dir)) {
39299                        return true;
39300                    }
39301
39302                    return false;
39303                }
39304            }
39305
39306            return false;
39307        }
39308
39309        return is_writeable($this->_depdb);
39310    }
39311
39312    // {{{ assertDepsDB()
39313
39314    /**
39315     * Create the dependency database, if it doesn't exist.  Error if the database is
39316     * newer than the code reading it.
39317     * @return void|PEAR_Error
39318     */
39319    function assertDepsDB()
39320    {
39321        if (!is_file($this->_depdb)) {
39322            $this->rebuildDB();
39323            return;
39324        }
39325
39326        $depdb = $this->_getDepDB();
39327        // Datatype format has been changed, rebuild the Deps DB
39328        if ($depdb['_version'] < $this->_version) {
39329            $this->rebuildDB();
39330        }
39331
39332        if ($depdb['_version']{0} > $this->_version{0}) {
39333            return PEAR::raiseError('Dependency database is version ' .
39334                $depdb['_version'] . ', and we are version ' .
39335                $this->_version . ', cannot continue');
39336        }
39337    }
39338
39339    /**
39340     * Get a list of installed packages that depend on this package
39341     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
39342     * @return array|false
39343     */
39344    function getDependentPackages(&$pkg)
39345    {
39346        $data = $this->_getDepDB();
39347        if (is_object($pkg)) {
39348            $channel = strtolower($pkg->getChannel());
39349            $package = strtolower($pkg->getPackage());
39350        } else {
39351            $channel = strtolower($pkg['channel']);
39352            $package = strtolower($pkg['package']);
39353        }
39354
39355        if (isset($data['packages'][$channel][$package])) {
39356            return $data['packages'][$channel][$package];
39357        }
39358
39359        return false;
39360    }
39361
39362    /**
39363     * Get a list of the actual dependencies of installed packages that depend on
39364     * a package.
39365     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
39366     * @return array|false
39367     */
39368    function getDependentPackageDependencies(&$pkg)
39369    {
39370        $data = $this->_getDepDB();
39371        if (is_object($pkg)) {
39372            $channel = strtolower($pkg->getChannel());
39373            $package = strtolower($pkg->getPackage());
39374        } else {
39375            $channel = strtolower($pkg['channel']);
39376            $package = strtolower($pkg['package']);
39377        }
39378
39379        $depend = $this->getDependentPackages($pkg);
39380        if (!$depend) {
39381            return false;
39382        }
39383
39384        $dependencies = array();
39385        foreach ($depend as $info) {
39386            $temp = $this->getDependencies($info);
39387            foreach ($temp as $dep) {
39388                if (
39389                    isset($dep['dep'], $dep['dep']['channel'], $dep['dep']['name']) &&
39390                    strtolower($dep['dep']['channel']) == $channel &&
39391                    strtolower($dep['dep']['name']) == $package
39392                ) {
39393                    if (!isset($dependencies[$info['channel']])) {
39394                        $dependencies[$info['channel']] = array();
39395                    }
39396
39397                    if (!isset($dependencies[$info['channel']][$info['package']])) {
39398                        $dependencies[$info['channel']][$info['package']] = array();
39399                    }
39400                    $dependencies[$info['channel']][$info['package']][] = $dep;
39401                }
39402            }
39403        }
39404
39405        return $dependencies;
39406    }
39407
39408    /**
39409     * Get a list of dependencies of this installed package
39410     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
39411     * @return array|false
39412     */
39413    function getDependencies(&$pkg)
39414    {
39415        if (is_object($pkg)) {
39416            $channel = strtolower($pkg->getChannel());
39417            $package = strtolower($pkg->getPackage());
39418        } else {
39419            $channel = strtolower($pkg['channel']);
39420            $package = strtolower($pkg['package']);
39421        }
39422
39423        $data = $this->_getDepDB();
39424        if (isset($data['dependencies'][$channel][$package])) {
39425            return $data['dependencies'][$channel][$package];
39426        }
39427
39428        return false;
39429    }
39430
39431    /**
39432     * Determine whether $parent depends on $child, near or deep
39433     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
39434     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
39435     */
39436    function dependsOn($parent, $child)
39437    {
39438        $c = array();
39439        $this->_getDepDB();
39440        return $this->_dependsOn($parent, $child, $c);
39441    }
39442
39443    function _dependsOn($parent, $child, &$checked)
39444    {
39445        if (is_object($parent)) {
39446            $channel = strtolower($parent->getChannel());
39447            $package = strtolower($parent->getPackage());
39448        } else {
39449            $channel = strtolower($parent['channel']);
39450            $package = strtolower($parent['package']);
39451        }
39452
39453        if (is_object($child)) {
39454            $depchannel = strtolower($child->getChannel());
39455            $deppackage = strtolower($child->getPackage());
39456        } else {
39457            $depchannel = strtolower($child['channel']);
39458            $deppackage = strtolower($child['package']);
39459        }
39460
39461        if (isset($checked[$channel][$package][$depchannel][$deppackage])) {
39462            return false; // avoid endless recursion
39463        }
39464
39465        $checked[$channel][$package][$depchannel][$deppackage] = true;
39466        if (!isset($this->_cache['dependencies'][$channel][$package])) {
39467            return false;
39468        }
39469
39470        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
39471            if (isset($info['dep']['uri'])) {
39472                if (is_object($child)) {
39473                    if ($info['dep']['uri'] == $child->getURI()) {
39474                        return true;
39475                    }
39476                } elseif (isset($child['uri'])) {
39477                    if ($info['dep']['uri'] == $child['uri']) {
39478                        return true;
39479                    }
39480                }
39481                return false;
39482            }
39483
39484            if (strtolower($info['dep']['channel']) == $depchannel &&
39485                  strtolower($info['dep']['name']) == $deppackage) {
39486                return true;
39487            }
39488        }
39489
39490        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
39491            if (isset($info['dep']['uri'])) {
39492                if ($this->_dependsOn(array(
39493                        'uri' => $info['dep']['uri'],
39494                        'package' => $info['dep']['name']), $child, $checked)) {
39495                    return true;
39496                }
39497            } else {
39498                if ($this->_dependsOn(array(
39499                        'channel' => $info['dep']['channel'],
39500                        'package' => $info['dep']['name']), $child, $checked)) {
39501                    return true;
39502                }
39503            }
39504        }
39505
39506        return false;
39507    }
39508
39509    /**
39510     * Register dependencies of a package that is being installed or upgraded
39511     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2
39512     */
39513    function installPackage(&$package)
39514    {
39515        $data = $this->_getDepDB();
39516        unset($this->_cache);
39517        $this->_setPackageDeps($data, $package);
39518        $this->_writeDepDB($data);
39519    }
39520
39521    /**
39522     * Remove dependencies of a package that is being uninstalled, or upgraded.
39523     *
39524     * Upgraded packages first uninstall, then install
39525     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have
39526     *        indices 'channel' and 'package'
39527     */
39528    function uninstallPackage(&$pkg)
39529    {
39530        $data = $this->_getDepDB();
39531        unset($this->_cache);
39532        if (is_object($pkg)) {
39533            $channel = strtolower($pkg->getChannel());
39534            $package = strtolower($pkg->getPackage());
39535        } else {
39536            $channel = strtolower($pkg['channel']);
39537            $package = strtolower($pkg['package']);
39538        }
39539
39540        if (!isset($data['dependencies'][$channel][$package])) {
39541            return true;
39542        }
39543
39544        foreach ($data['dependencies'][$channel][$package] as $dep) {
39545            $found      = false;
39546            $depchannel = isset($dep['dep']['uri']) ? '__uri' : strtolower($dep['dep']['channel']);
39547            $depname    = strtolower($dep['dep']['name']);
39548            if (isset($data['packages'][$depchannel][$depname])) {
39549                foreach ($data['packages'][$depchannel][$depname] as $i => $info) {
39550                    if ($info['channel'] == $channel && $info['package'] == $package) {
39551                        $found = true;
39552                        break;
39553                    }
39554                }
39555            }
39556
39557            if ($found) {
39558                unset($data['packages'][$depchannel][$depname][$i]);
39559                if (!count($data['packages'][$depchannel][$depname])) {
39560                    unset($data['packages'][$depchannel][$depname]);
39561                    if (!count($data['packages'][$depchannel])) {
39562                        unset($data['packages'][$depchannel]);
39563                    }
39564                } else {
39565                    $data['packages'][$depchannel][$depname] =
39566                        array_values($data['packages'][$depchannel][$depname]);
39567                }
39568            }
39569        }
39570
39571        unset($data['dependencies'][$channel][$package]);
39572        if (!count($data['dependencies'][$channel])) {
39573            unset($data['dependencies'][$channel]);
39574        }
39575
39576        if (!count($data['dependencies'])) {
39577            unset($data['dependencies']);
39578        }
39579
39580        if (!count($data['packages'])) {
39581            unset($data['packages']);
39582        }
39583
39584        $this->_writeDepDB($data);
39585    }
39586
39587    /**
39588     * Rebuild the dependency DB by reading registry entries.
39589     * @return true|PEAR_Error
39590     */
39591    function rebuildDB()
39592    {
39593        $depdb = array('_version' => $this->_version);
39594        if (!$this->hasWriteAccess()) {
39595            // allow startup for read-only with older Registry
39596            return $depdb;
39597        }
39598
39599        $packages = $this->_registry->listAllPackages();
39600        if (PEAR::isError($packages)) {
39601            return $packages;
39602        }
39603
39604        foreach ($packages as $channel => $ps) {
39605            foreach ($ps as $package) {
39606                $package = $this->_registry->getPackage($package, $channel);
39607                if (PEAR::isError($package)) {
39608                    return $package;
39609                }
39610                $this->_setPackageDeps($depdb, $package);
39611            }
39612        }
39613
39614        $error = $this->_writeDepDB($depdb);
39615        if (PEAR::isError($error)) {
39616            return $error;
39617        }
39618
39619        $this->_cache = $depdb;
39620        return true;
39621    }
39622
39623    /**
39624     * Register usage of the dependency DB to prevent race conditions
39625     * @param int one of the LOCK_* constants
39626     * @return true|PEAR_Error
39627     * @access private
39628     */
39629    function _lock($mode = LOCK_EX)
39630    {
39631        if (stristr(php_uname(), 'Windows 9')) {
39632            return true;
39633        }
39634
39635        if ($mode != LOCK_UN && is_resource($this->_lockFp)) {
39636            // XXX does not check type of lock (LOCK_SH/LOCK_EX)
39637            return true;
39638        }
39639
39640        $open_mode = 'w';
39641        // XXX People reported problems with LOCK_SH and 'w'
39642        if ($mode === LOCK_SH) {
39643            if (!file_exists($this->_lockfile)) {
39644                touch($this->_lockfile);
39645            } elseif (!is_file($this->_lockfile)) {
39646                return PEAR::raiseError('could not create Dependency lock file, ' .
39647                    'it exists and is not a regular file');
39648            }
39649            $open_mode = 'r';
39650        }
39651
39652        if (!is_resource($this->_lockFp)) {
39653            $this->_lockFp = @fopen($this->_lockfile, $open_mode);
39654        }
39655
39656        if (!is_resource($this->_lockFp)) {
39657            return PEAR::raiseError("could not create Dependency lock file" .
39658                                     (isset($php_errormsg) ? ": " . $php_errormsg : ""));
39659        }
39660
39661        if (!(int)flock($this->_lockFp, $mode)) {
39662            switch ($mode) {
39663                case LOCK_SH: $str = 'shared';    break;
39664                case LOCK_EX: $str = 'exclusive'; break;
39665                case LOCK_UN: $str = 'unlock';    break;
39666                default:      $str = 'unknown';   break;
39667            }
39668
39669            return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)");
39670        }
39671
39672        return true;
39673    }
39674
39675    /**
39676     * Release usage of dependency DB
39677     * @return true|PEAR_Error
39678     * @access private
39679     */
39680    function _unlock()
39681    {
39682        $ret = $this->_lock(LOCK_UN);
39683        if (is_resource($this->_lockFp)) {
39684            fclose($this->_lockFp);
39685        }
39686        $this->_lockFp = null;
39687        return $ret;
39688    }
39689
39690    /**
39691     * Load the dependency database from disk, or return the cache
39692     * @return array|PEAR_Error
39693     */
39694    function _getDepDB()
39695    {
39696        if (!$this->hasWriteAccess()) {
39697            return array('_version' => $this->_version);
39698        }
39699
39700        if (isset($this->_cache)) {
39701            return $this->_cache;
39702        }
39703
39704        if (!$fp = fopen($this->_depdb, 'r')) {
39705            $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'");
39706            return $err;
39707        }
39708
39709        $rt = get_magic_quotes_runtime();
39710        set_magic_quotes_runtime(0);
39711        clearstatcache();
39712        fclose($fp);
39713        $data = unserialize(file_get_contents($this->_depdb));
39714        set_magic_quotes_runtime($rt);
39715        $this->_cache = $data;
39716        return $data;
39717    }
39718
39719    /**
39720     * Write out the dependency database to disk
39721     * @param array the database
39722     * @return true|PEAR_Error
39723     * @access private
39724     */
39725    function _writeDepDB(&$deps)
39726    {
39727        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
39728            return $e;
39729        }
39730
39731        if (!$fp = fopen($this->_depdb, 'wb')) {
39732            $this->_unlock();
39733            return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing");
39734        }
39735
39736        $rt = get_magic_quotes_runtime();
39737        set_magic_quotes_runtime(0);
39738        fwrite($fp, serialize($deps));
39739        set_magic_quotes_runtime($rt);
39740        fclose($fp);
39741        $this->_unlock();
39742        $this->_cache = $deps;
39743        return true;
39744    }
39745
39746    /**
39747     * Register all dependencies from a package in the dependencies database, in essence
39748     * "installing" the package's dependency information
39749     * @param array the database
39750     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
39751     * @access private
39752     */
39753    function _setPackageDeps(&$data, &$pkg)
39754    {
39755        $pkg->setConfig($this->_config);
39756        if ($pkg->getPackagexmlVersion() == '1.0') {
39757            $gen = &$pkg->getDefaultGenerator();
39758            $deps = $gen->dependenciesToV2();
39759        } else {
39760            $deps = $pkg->getDeps(true);
39761        }
39762
39763        if (!$deps) {
39764            return;
39765        }
39766
39767        if (!is_array($data)) {
39768            $data = array();
39769        }
39770
39771        if (!isset($data['dependencies'])) {
39772            $data['dependencies'] = array();
39773        }
39774
39775        $channel = strtolower($pkg->getChannel());
39776        $package = strtolower($pkg->getPackage());
39777
39778        if (!isset($data['dependencies'][$channel])) {
39779            $data['dependencies'][$channel] = array();
39780        }
39781
39782        $data['dependencies'][$channel][$package] = array();
39783        if (isset($deps['required']['package'])) {
39784            if (!isset($deps['required']['package'][0])) {
39785                $deps['required']['package'] = array($deps['required']['package']);
39786            }
39787
39788            foreach ($deps['required']['package'] as $dep) {
39789                $this->_registerDep($data, $pkg, $dep, 'required');
39790            }
39791        }
39792
39793        if (isset($deps['optional']['package'])) {
39794            if (!isset($deps['optional']['package'][0])) {
39795                $deps['optional']['package'] = array($deps['optional']['package']);
39796            }
39797
39798            foreach ($deps['optional']['package'] as $dep) {
39799                $this->_registerDep($data, $pkg, $dep, 'optional');
39800            }
39801        }
39802
39803        if (isset($deps['required']['subpackage'])) {
39804            if (!isset($deps['required']['subpackage'][0])) {
39805                $deps['required']['subpackage'] = array($deps['required']['subpackage']);
39806            }
39807
39808            foreach ($deps['required']['subpackage'] as $dep) {
39809                $this->_registerDep($data, $pkg, $dep, 'required');
39810            }
39811        }
39812
39813        if (isset($deps['optional']['subpackage'])) {
39814            if (!isset($deps['optional']['subpackage'][0])) {
39815                $deps['optional']['subpackage'] = array($deps['optional']['subpackage']);
39816            }
39817
39818            foreach ($deps['optional']['subpackage'] as $dep) {
39819                $this->_registerDep($data, $pkg, $dep, 'optional');
39820            }
39821        }
39822
39823        if (isset($deps['group'])) {
39824            if (!isset($deps['group'][0])) {
39825                $deps['group'] = array($deps['group']);
39826            }
39827
39828            foreach ($deps['group'] as $group) {
39829                if (isset($group['package'])) {
39830                    if (!isset($group['package'][0])) {
39831                        $group['package'] = array($group['package']);
39832                    }
39833
39834                    foreach ($group['package'] as $dep) {
39835                        $this->_registerDep($data, $pkg, $dep, 'optional',
39836                            $group['attribs']['name']);
39837                    }
39838                }
39839
39840                if (isset($group['subpackage'])) {
39841                    if (!isset($group['subpackage'][0])) {
39842                        $group['subpackage'] = array($group['subpackage']);
39843                    }
39844
39845                    foreach ($group['subpackage'] as $dep) {
39846                        $this->_registerDep($data, $pkg, $dep, 'optional',
39847                            $group['attribs']['name']);
39848                    }
39849                }
39850            }
39851        }
39852
39853        if ($data['dependencies'][$channel][$package] == array()) {
39854            unset($data['dependencies'][$channel][$package]);
39855            if (!count($data['dependencies'][$channel])) {
39856                unset($data['dependencies'][$channel]);
39857            }
39858        }
39859    }
39860
39861    /**
39862     * @param array the database
39863     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
39864     * @param array the specific dependency
39865     * @param required|optional whether this is a required or an optional dep
39866     * @param string|false dependency group this dependency is from, or false for ordinary dep
39867     */
39868    function _registerDep(&$data, &$pkg, $dep, $type, $group = false)
39869    {
39870        $info = array(
39871            'dep'   => $dep,
39872            'type'  => $type,
39873            'group' => $group
39874        );
39875
39876        $dep  = array_map('strtolower', $dep);
39877        $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
39878        if (!isset($data['dependencies'])) {
39879            $data['dependencies'] = array();
39880        }
39881
39882        $channel = strtolower($pkg->getChannel());
39883        $package = strtolower($pkg->getPackage());
39884
39885        if (!isset($data['dependencies'][$channel])) {
39886            $data['dependencies'][$channel] = array();
39887        }
39888
39889        if (!isset($data['dependencies'][$channel][$package])) {
39890            $data['dependencies'][$channel][$package] = array();
39891        }
39892
39893        $data['dependencies'][$channel][$package][] = $info;
39894        if (isset($data['packages'][$depchannel][$dep['name']])) {
39895            $found = false;
39896            foreach ($data['packages'][$depchannel][$dep['name']] as $i => $p) {
39897                if ($p['channel'] == $channel && $p['package'] == $package) {
39898                    $found = true;
39899                    break;
39900                }
39901            }
39902        } else {
39903            if (!isset($data['packages'])) {
39904                $data['packages'] = array();
39905            }
39906
39907            if (!isset($data['packages'][$depchannel])) {
39908                $data['packages'][$depchannel] = array();
39909            }
39910
39911            if (!isset($data['packages'][$depchannel][$dep['name']])) {
39912                $data['packages'][$depchannel][$dep['name']] = array();
39913            }
39914
39915            $found = false;
39916        }
39917
39918        if (!$found) {
39919            $data['packages'][$depchannel][$dep['name']][] = array(
39920                'channel' => $channel,
39921                'package' => $package
39922            );
39923        }
39924    }
39925}PEAR-1.9.4/PEAR/Dependency2.php0000644000076500000240000014251711605156614014646 0ustar  helgistaff<?php
39926/**
39927 * PEAR_Dependency2, advanced dependency validation
39928 *
39929 * PHP versions 4 and 5
39930 *
39931 * @category   pear
39932 * @package    PEAR
39933 * @author     Greg Beaver <cellog@php.net>
39934 * @copyright  1997-2009 The Authors
39935 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
39936 * @version    CVS: $Id: Dependency2.php 313023 2011-07-06 19:17:11Z dufuz $
39937 * @link       http://pear.php.net/package/PEAR
39938 * @since      File available since Release 1.4.0a1
39939 */
39940
39941/**
39942 * Required for the PEAR_VALIDATE_* constants
39943 */
39944require_once 'PEAR/Validate.php';
39945
39946/**
39947 * Dependency check for PEAR packages
39948 *
39949 * This class handles both version 1.0 and 2.0 dependencies
39950 * WARNING: *any* changes to this class must be duplicated in the
39951 * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc,
39952 * or unit tests will not actually validate the changes
39953 * @category   pear
39954 * @package    PEAR
39955 * @author     Greg Beaver <cellog@php.net>
39956 * @copyright  1997-2009 The Authors
39957 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
39958 * @version    Release: 1.9.4
39959 * @link       http://pear.php.net/package/PEAR
39960 * @since      Class available since Release 1.4.0a1
39961 */
39962class PEAR_Dependency2
39963{
39964    /**
39965     * One of the PEAR_VALIDATE_* states
39966     * @see PEAR_VALIDATE_NORMAL
39967     * @var integer
39968     */
39969    var $_state;
39970
39971    /**
39972     * Command-line options to install/upgrade/uninstall commands
39973     * @param array
39974     */
39975    var $_options;
39976
39977    /**
39978     * @var OS_Guess
39979     */
39980    var $_os;
39981
39982    /**
39983     * @var PEAR_Registry
39984     */
39985    var $_registry;
39986
39987    /**
39988     * @var PEAR_Config
39989     */
39990    var $_config;
39991
39992    /**
39993     * @var PEAR_DependencyDB
39994     */
39995    var $_dependencydb;
39996
39997    /**
39998     * Output of PEAR_Registry::parsedPackageName()
39999     * @var array
40000     */
40001    var $_currentPackage;
40002
40003    /**
40004     * @param PEAR_Config
40005     * @param array installation options
40006     * @param array format of PEAR_Registry::parsedPackageName()
40007     * @param int installation state (one of PEAR_VALIDATE_*)
40008     */
40009    function PEAR_Dependency2(&$config, $installoptions, $package,
40010                              $state = PEAR_VALIDATE_INSTALLING)
40011    {
40012        $this->_config = &$config;
40013        if (!class_exists('PEAR_DependencyDB')) {
40014            require_once 'PEAR/DependencyDB.php';
40015        }
40016
40017        if (isset($installoptions['packagingroot'])) {
40018            // make sure depdb is in the right location
40019            $config->setInstallRoot($installoptions['packagingroot']);
40020        }
40021
40022        $this->_registry = &$config->getRegistry();
40023        $this->_dependencydb = &PEAR_DependencyDB::singleton($config);
40024        if (isset($installoptions['packagingroot'])) {
40025            $config->setInstallRoot(false);
40026        }
40027
40028        $this->_options = $installoptions;
40029        $this->_state = $state;
40030        if (!class_exists('OS_Guess')) {
40031            require_once 'OS/Guess.php';
40032        }
40033
40034        $this->_os = new OS_Guess;
40035        $this->_currentPackage = $package;
40036    }
40037
40038    function _getExtraString($dep)
40039    {
40040        $extra = ' (';
40041        if (isset($dep['uri'])) {
40042            return '';
40043        }
40044
40045        if (isset($dep['recommended'])) {
40046            $extra .= 'recommended version ' . $dep['recommended'];
40047        } else {
40048            if (isset($dep['min'])) {
40049                $extra .= 'version >= ' . $dep['min'];
40050            }
40051
40052            if (isset($dep['max'])) {
40053                if ($extra != ' (') {
40054                    $extra .= ', ';
40055                }
40056                $extra .= 'version <= ' . $dep['max'];
40057            }
40058
40059            if (isset($dep['exclude'])) {
40060                if (!is_array($dep['exclude'])) {
40061                    $dep['exclude'] = array($dep['exclude']);
40062                }
40063
40064                if ($extra != ' (') {
40065                    $extra .= ', ';
40066                }
40067
40068                $extra .= 'excluded versions: ';
40069                foreach ($dep['exclude'] as $i => $exclude) {
40070                    if ($i) {
40071                        $extra .= ', ';
40072                    }
40073                    $extra .= $exclude;
40074                }
40075            }
40076        }
40077
40078        $extra .= ')';
40079        if ($extra == ' ()') {
40080            $extra = '';
40081        }
40082
40083        return $extra;
40084    }
40085
40086    /**
40087     * This makes unit-testing a heck of a lot easier
40088     */
40089    function getPHP_OS()
40090    {
40091        return PHP_OS;
40092    }
40093
40094    /**
40095     * This makes unit-testing a heck of a lot easier
40096     */
40097    function getsysname()
40098    {
40099        return $this->_os->getSysname();
40100    }
40101
40102    /**
40103     * Specify a dependency on an OS.  Use arch for detailed os/processor information
40104     *
40105     * There are two generic OS dependencies that will be the most common, unix and windows.
40106     * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix
40107     */
40108    function validateOsDependency($dep)
40109    {
40110        if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) {
40111            return true;
40112        }
40113
40114        if ($dep['name'] == '*') {
40115            return true;
40116        }
40117
40118        $not = isset($dep['conflicts']) ? true : false;
40119        switch (strtolower($dep['name'])) {
40120            case 'windows' :
40121                if ($not) {
40122                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') {
40123                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40124                            return $this->raiseError("Cannot install %s on Windows");
40125                        }
40126
40127                        return $this->warning("warning: Cannot install %s on Windows");
40128                    }
40129                } else {
40130                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') {
40131                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40132                            return $this->raiseError("Can only install %s on Windows");
40133                        }
40134
40135                        return $this->warning("warning: Can only install %s on Windows");
40136                    }
40137                }
40138            break;
40139            case 'unix' :
40140                $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix');
40141                if ($not) {
40142                    if (in_array($this->getSysname(), $unices)) {
40143                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40144                            return $this->raiseError("Cannot install %s on any Unix system");
40145                        }
40146
40147                        return $this->warning( "warning: Cannot install %s on any Unix system");
40148                    }
40149                } else {
40150                    if (!in_array($this->getSysname(), $unices)) {
40151                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40152                            return $this->raiseError("Can only install %s on a Unix system");
40153                        }
40154
40155                        return $this->warning("warning: Can only install %s on a Unix system");
40156                    }
40157                }
40158            break;
40159            default :
40160                if ($not) {
40161                    if (strtolower($dep['name']) == strtolower($this->getSysname())) {
40162                        if (!isset($this->_options['nodeps']) &&
40163                              !isset($this->_options['force'])) {
40164                            return $this->raiseError('Cannot install %s on ' . $dep['name'] .
40165                                ' operating system');
40166                        }
40167
40168                        return $this->warning('warning: Cannot install %s on ' .
40169                            $dep['name'] . ' operating system');
40170                    }
40171                } else {
40172                    if (strtolower($dep['name']) != strtolower($this->getSysname())) {
40173                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40174                            return $this->raiseError('Cannot install %s on ' .
40175                                $this->getSysname() .
40176                                ' operating system, can only install on ' . $dep['name']);
40177                        }
40178
40179                        return $this->warning('warning: Cannot install %s on ' .
40180                            $this->getSysname() .
40181                            ' operating system, can only install on ' . $dep['name']);
40182                    }
40183                }
40184        }
40185        return true;
40186    }
40187
40188    /**
40189     * This makes unit-testing a heck of a lot easier
40190     */
40191    function matchSignature($pattern)
40192    {
40193        return $this->_os->matchSignature($pattern);
40194    }
40195
40196    /**
40197     * Specify a complex dependency on an OS/processor/kernel version,
40198     * Use OS for simple operating system dependency.
40199     *
40200     * This is the only dependency that accepts an eregable pattern.  The pattern
40201     * will be matched against the php_uname() output parsed by OS_Guess
40202     */
40203    function validateArchDependency($dep)
40204    {
40205        if ($this->_state != PEAR_VALIDATE_INSTALLING) {
40206            return true;
40207        }
40208
40209        $not = isset($dep['conflicts']) ? true : false;
40210        if (!$this->matchSignature($dep['pattern'])) {
40211            if (!$not) {
40212                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40213                    return $this->raiseError('%s Architecture dependency failed, does not ' .
40214                        'match "' . $dep['pattern'] . '"');
40215                }
40216
40217                return $this->warning('warning: %s Architecture dependency failed, does ' .
40218                    'not match "' . $dep['pattern'] . '"');
40219            }
40220
40221            return true;
40222        }
40223
40224        if ($not) {
40225            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40226                return $this->raiseError('%s Architecture dependency failed, required "' .
40227                    $dep['pattern'] . '"');
40228            }
40229
40230            return $this->warning('warning: %s Architecture dependency failed, ' .
40231                'required "' . $dep['pattern'] . '"');
40232        }
40233
40234        return true;
40235    }
40236
40237    /**
40238     * This makes unit-testing a heck of a lot easier
40239     */
40240    function extension_loaded($name)
40241    {
40242        return extension_loaded($name);
40243    }
40244
40245    /**
40246     * This makes unit-testing a heck of a lot easier
40247     */
40248    function phpversion($name = null)
40249    {
40250        if ($name !== null) {
40251            return phpversion($name);
40252        }
40253
40254        return phpversion();
40255    }
40256
40257    function validateExtensionDependency($dep, $required = true)
40258    {
40259        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
40260              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
40261            return true;
40262        }
40263
40264        $loaded = $this->extension_loaded($dep['name']);
40265        $extra  = $this->_getExtraString($dep);
40266        if (isset($dep['exclude'])) {
40267            if (!is_array($dep['exclude'])) {
40268                $dep['exclude'] = array($dep['exclude']);
40269            }
40270        }
40271
40272        if (!isset($dep['min']) && !isset($dep['max']) &&
40273            !isset($dep['recommended']) && !isset($dep['exclude'])
40274        ) {
40275            if ($loaded) {
40276                if (isset($dep['conflicts'])) {
40277                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40278                        return $this->raiseError('%s conflicts with PHP extension "' .
40279                            $dep['name'] . '"' . $extra);
40280                    }
40281
40282                    return $this->warning('warning: %s conflicts with PHP extension "' .
40283                        $dep['name'] . '"' . $extra);
40284                }
40285
40286                return true;
40287            }
40288
40289            if (isset($dep['conflicts'])) {
40290                return true;
40291            }
40292
40293            if ($required) {
40294                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40295                    return $this->raiseError('%s requires PHP extension "' .
40296                        $dep['name'] . '"' . $extra);
40297                }
40298
40299                return $this->warning('warning: %s requires PHP extension "' .
40300                    $dep['name'] . '"' . $extra);
40301            }
40302
40303            return $this->warning('%s can optionally use PHP extension "' .
40304                $dep['name'] . '"' . $extra);
40305        }
40306
40307        if (!$loaded) {
40308            if (isset($dep['conflicts'])) {
40309                return true;
40310            }
40311
40312            if (!$required) {
40313                return $this->warning('%s can optionally use PHP extension "' .
40314                    $dep['name'] . '"' . $extra);
40315            }
40316
40317            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40318                return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
40319                    '"' . $extra);
40320            }
40321
40322            return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
40323                    '"' . $extra);
40324        }
40325
40326        $version = (string) $this->phpversion($dep['name']);
40327        if (empty($version)) {
40328            $version = '0';
40329        }
40330
40331        $fail = false;
40332        if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) {
40333            $fail = true;
40334        }
40335
40336        if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) {
40337            $fail = true;
40338        }
40339
40340        if ($fail && !isset($dep['conflicts'])) {
40341            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40342                return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
40343                    '"' . $extra . ', installed version is ' . $version);
40344            }
40345
40346            return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
40347                '"' . $extra . ', installed version is ' . $version);
40348        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) {
40349            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40350                return $this->raiseError('%s conflicts with PHP extension "' .
40351                    $dep['name'] . '"' . $extra . ', installed version is ' . $version);
40352            }
40353
40354            return $this->warning('warning: %s conflicts with PHP extension "' .
40355                $dep['name'] . '"' . $extra . ', installed version is ' . $version);
40356        }
40357
40358        if (isset($dep['exclude'])) {
40359            foreach ($dep['exclude'] as $exclude) {
40360                if (version_compare($version, $exclude, '==')) {
40361                    if (isset($dep['conflicts'])) {
40362                        continue;
40363                    }
40364
40365                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40366                        return $this->raiseError('%s is not compatible with PHP extension "' .
40367                            $dep['name'] . '" version ' .
40368                            $exclude);
40369                    }
40370
40371                    return $this->warning('warning: %s is not compatible with PHP extension "' .
40372                        $dep['name'] . '" version ' .
40373                        $exclude);
40374                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
40375                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40376                        return $this->raiseError('%s conflicts with PHP extension "' .
40377                            $dep['name'] . '"' . $extra . ', installed version is ' . $version);
40378                    }
40379
40380                    return $this->warning('warning: %s conflicts with PHP extension "' .
40381                        $dep['name'] . '"' . $extra . ', installed version is ' . $version);
40382                }
40383            }
40384        }
40385
40386        if (isset($dep['recommended'])) {
40387            if (version_compare($version, $dep['recommended'], '==')) {
40388                return true;
40389            }
40390
40391            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40392                return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] .
40393                    ' version "' . $version . '"' .
40394                    ' is not the recommended version "' . $dep['recommended'] .
40395                    '", but may be compatible, use --force to install');
40396            }
40397
40398            return $this->warning('warning: %s dependency: PHP extension ' .
40399                $dep['name'] . ' version "' . $version . '"' .
40400                ' is not the recommended version "' . $dep['recommended'].'"');
40401        }
40402
40403        return true;
40404    }
40405
40406    function validatePhpDependency($dep)
40407    {
40408        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
40409              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
40410            return true;
40411        }
40412
40413        $version = $this->phpversion();
40414        $extra   = $this->_getExtraString($dep);
40415        if (isset($dep['exclude'])) {
40416            if (!is_array($dep['exclude'])) {
40417                $dep['exclude'] = array($dep['exclude']);
40418            }
40419        }
40420
40421        if (isset($dep['min'])) {
40422            if (!version_compare($version, $dep['min'], '>=')) {
40423                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40424                    return $this->raiseError('%s requires PHP' .
40425                        $extra . ', installed version is ' . $version);
40426                }
40427
40428                return $this->warning('warning: %s requires PHP' .
40429                    $extra . ', installed version is ' . $version);
40430            }
40431        }
40432
40433        if (isset($dep['max'])) {
40434            if (!version_compare($version, $dep['max'], '<=')) {
40435                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40436                    return $this->raiseError('%s requires PHP' .
40437                        $extra . ', installed version is ' . $version);
40438                }
40439
40440                return $this->warning('warning: %s requires PHP' .
40441                    $extra . ', installed version is ' . $version);
40442            }
40443        }
40444
40445        if (isset($dep['exclude'])) {
40446            foreach ($dep['exclude'] as $exclude) {
40447                if (version_compare($version, $exclude, '==')) {
40448                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40449                        return $this->raiseError('%s is not compatible with PHP version ' .
40450                            $exclude);
40451                    }
40452
40453                    return $this->warning(
40454                        'warning: %s is not compatible with PHP version ' .
40455                        $exclude);
40456                }
40457            }
40458        }
40459
40460        return true;
40461    }
40462
40463    /**
40464     * This makes unit-testing a heck of a lot easier
40465     */
40466    function getPEARVersion()
40467    {
40468        return '1.9.4';
40469    }
40470
40471    function validatePearinstallerDependency($dep)
40472    {
40473        $pearversion = $this->getPEARVersion();
40474        $extra = $this->_getExtraString($dep);
40475        if (isset($dep['exclude'])) {
40476            if (!is_array($dep['exclude'])) {
40477                $dep['exclude'] = array($dep['exclude']);
40478            }
40479        }
40480
40481        if (version_compare($pearversion, $dep['min'], '<')) {
40482            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40483                return $this->raiseError('%s requires PEAR Installer' . $extra .
40484                    ', installed version is ' . $pearversion);
40485            }
40486
40487            return $this->warning('warning: %s requires PEAR Installer' . $extra .
40488                ', installed version is ' . $pearversion);
40489        }
40490
40491        if (isset($dep['max'])) {
40492            if (version_compare($pearversion, $dep['max'], '>')) {
40493                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40494                    return $this->raiseError('%s requires PEAR Installer' . $extra .
40495                        ', installed version is ' . $pearversion);
40496                }
40497
40498                return $this->warning('warning: %s requires PEAR Installer' . $extra .
40499                    ', installed version is ' . $pearversion);
40500            }
40501        }
40502
40503        if (isset($dep['exclude'])) {
40504            if (!isset($dep['exclude'][0])) {
40505                $dep['exclude'] = array($dep['exclude']);
40506            }
40507
40508            foreach ($dep['exclude'] as $exclude) {
40509                if (version_compare($exclude, $pearversion, '==')) {
40510                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40511                        return $this->raiseError('%s is not compatible with PEAR Installer ' .
40512                            'version ' . $exclude);
40513                    }
40514
40515                    return $this->warning('warning: %s is not compatible with PEAR ' .
40516                        'Installer version ' . $exclude);
40517                }
40518            }
40519        }
40520
40521        return true;
40522    }
40523
40524    function validateSubpackageDependency($dep, $required, $params)
40525    {
40526        return $this->validatePackageDependency($dep, $required, $params);
40527    }
40528
40529    /**
40530     * @param array dependency information (2.0 format)
40531     * @param boolean whether this is a required dependency
40532     * @param array a list of downloaded packages to be installed, if any
40533     * @param boolean if true, then deps on pear.php.net that fail will also check
40534     *                against pecl.php.net packages to accomodate extensions that have
40535     *                moved to pecl.php.net from pear.php.net
40536     */
40537    function validatePackageDependency($dep, $required, $params, $depv1 = false)
40538    {
40539        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
40540              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
40541            return true;
40542        }
40543
40544        if (isset($dep['providesextension'])) {
40545            if ($this->extension_loaded($dep['providesextension'])) {
40546                $save = $dep;
40547                $subdep = $dep;
40548                $subdep['name'] = $subdep['providesextension'];
40549                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
40550                $ret = $this->validateExtensionDependency($subdep, $required);
40551                PEAR::popErrorHandling();
40552                if (!PEAR::isError($ret)) {
40553                    return true;
40554                }
40555            }
40556        }
40557
40558        if ($this->_state == PEAR_VALIDATE_INSTALLING) {
40559            return $this->_validatePackageInstall($dep, $required, $depv1);
40560        }
40561
40562        if ($this->_state == PEAR_VALIDATE_DOWNLOADING) {
40563            return $this->_validatePackageDownload($dep, $required, $params, $depv1);
40564        }
40565    }
40566
40567    function _validatePackageDownload($dep, $required, $params, $depv1 = false)
40568    {
40569        $dep['package'] = $dep['name'];
40570        if (isset($dep['uri'])) {
40571            $dep['channel'] = '__uri';
40572        }
40573
40574        $depname = $this->_registry->parsedPackageNameToString($dep, true);
40575        $found = false;
40576        foreach ($params as $param) {
40577            if ($param->isEqual(
40578                  array('package' => $dep['name'],
40579                        'channel' => $dep['channel']))) {
40580                $found = true;
40581                break;
40582            }
40583
40584            if ($depv1 && $dep['channel'] == 'pear.php.net') {
40585                if ($param->isEqual(
40586                  array('package' => $dep['name'],
40587                        'channel' => 'pecl.php.net'))) {
40588                    $found = true;
40589                    break;
40590                }
40591            }
40592        }
40593
40594        if (!$found && isset($dep['providesextension'])) {
40595            foreach ($params as $param) {
40596                if ($param->isExtension($dep['providesextension'])) {
40597                    $found = true;
40598                    break;
40599                }
40600            }
40601        }
40602
40603        if ($found) {
40604            $version = $param->getVersion();
40605            $installed = false;
40606            $downloaded = true;
40607        } else {
40608            if ($this->_registry->packageExists($dep['name'], $dep['channel'])) {
40609                $installed = true;
40610                $downloaded = false;
40611                $version = $this->_registry->packageinfo($dep['name'], 'version',
40612                    $dep['channel']);
40613            } else {
40614                if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'],
40615                      'pear.php.net')) {
40616                    $installed = true;
40617                    $downloaded = false;
40618                    $version = $this->_registry->packageinfo($dep['name'], 'version',
40619                        'pear.php.net');
40620                } else {
40621                    $version = 'not installed or downloaded';
40622                    $installed = false;
40623                    $downloaded = false;
40624                }
40625            }
40626        }
40627
40628        $extra = $this->_getExtraString($dep);
40629        if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
40630            $dep['exclude'] = array($dep['exclude']);
40631        }
40632
40633        if (!isset($dep['min']) && !isset($dep['max']) &&
40634              !isset($dep['recommended']) && !isset($dep['exclude'])
40635        ) {
40636            if ($installed || $downloaded) {
40637                $installed = $installed ? 'installed' : 'downloaded';
40638                if (isset($dep['conflicts'])) {
40639                    $rest = '';
40640                    if ($version) {
40641                        $rest = ", $installed version is " . $version;
40642                    }
40643
40644                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40645                        return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest);
40646                    }
40647
40648                    return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest);
40649                }
40650
40651                return true;
40652            }
40653
40654            if (isset($dep['conflicts'])) {
40655                return true;
40656            }
40657
40658            if ($required) {
40659                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40660                    return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
40661                }
40662
40663                return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
40664            }
40665
40666            return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
40667        }
40668
40669        if (!$installed && !$downloaded) {
40670            if (isset($dep['conflicts'])) {
40671                return true;
40672            }
40673
40674            if ($required) {
40675                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40676                    return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
40677                }
40678
40679                return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
40680            }
40681
40682            return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
40683        }
40684
40685        $fail = false;
40686        if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) {
40687            $fail = true;
40688        }
40689
40690        if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) {
40691            $fail = true;
40692        }
40693
40694        if ($fail && !isset($dep['conflicts'])) {
40695            $installed = $installed ? 'installed' : 'downloaded';
40696            $dep['package'] = $dep['name'];
40697            $dep = $this->_registry->parsedPackageNameToString($dep, true);
40698            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40699                return $this->raiseError('%s requires package "' . $depname . '"' .
40700                    $extra . ", $installed version is " . $version);
40701            }
40702
40703            return $this->warning('warning: %s requires package "' . $depname . '"' .
40704                $extra . ", $installed version is " . $version);
40705        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail &&
40706              isset($dep['conflicts']) && !isset($dep['exclude'])) {
40707            $installed = $installed ? 'installed' : 'downloaded';
40708            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40709                return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra .
40710                    ", $installed version is " . $version);
40711            }
40712
40713            return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
40714                $extra . ", $installed version is " . $version);
40715        }
40716
40717        if (isset($dep['exclude'])) {
40718            $installed = $installed ? 'installed' : 'downloaded';
40719            foreach ($dep['exclude'] as $exclude) {
40720                if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) {
40721                    if (!isset($this->_options['nodeps']) &&
40722                          !isset($this->_options['force'])
40723                    ) {
40724                        return $this->raiseError('%s is not compatible with ' .
40725                            $installed . ' package "' .
40726                            $depname . '" version ' .
40727                            $exclude);
40728                    }
40729
40730                    return $this->warning('warning: %s is not compatible with ' .
40731                        $installed . ' package "' .
40732                        $depname . '" version ' .
40733                        $exclude);
40734                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
40735                    $installed = $installed ? 'installed' : 'downloaded';
40736                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
40737                        return $this->raiseError('%s conflicts with package "' . $depname . '"' .
40738                            $extra . ", $installed version is " . $version);
40739                    }
40740
40741                    return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
40742                        $extra . ", $installed version is " . $version);
40743                }
40744            }
40745        }
40746
40747        if (isset($dep['recommended'])) {
40748            $installed = $installed ? 'installed' : 'downloaded';
40749            if (version_compare($version, $dep['recommended'], '==')) {
40750                return true;
40751            }
40752
40753            if (!$found && $installed) {
40754                $param = $this->_registry->getPackage($dep['name'], $dep['channel']);
40755            }
40756
40757            if ($param) {
40758                $found = false;
40759                foreach ($params as $parent) {
40760                    if ($parent->isEqual($this->_currentPackage)) {
40761                        $found = true;
40762                        break;
40763                    }
40764                }
40765
40766                if ($found) {
40767                    if ($param->isCompatible($parent)) {
40768                        return true;
40769                    }
40770                } else { // this is for validPackage() calls
40771                    $parent = $this->_registry->getPackage($this->_currentPackage['package'],
40772                        $this->_currentPackage['channel']);
40773                    if ($parent !== null && $param->isCompatible($parent)) {
40774                        return true;
40775                    }
40776                }
40777            }
40778
40779            if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) &&
40780                  !isset($this->_options['loose'])
40781            ) {
40782                return $this->raiseError('%s dependency package "' . $depname .
40783                    '" ' . $installed . ' version ' . $version .
40784                    ' is not the recommended version ' . $dep['recommended'] .
40785                    ', but may be compatible, use --force to install');
40786            }
40787
40788            return $this->warning('warning: %s dependency package "' . $depname .
40789                '" ' . $installed . ' version ' . $version .
40790                ' is not the recommended version ' . $dep['recommended']);
40791        }
40792
40793        return true;
40794    }
40795
40796    function _validatePackageInstall($dep, $required, $depv1 = false)
40797    {
40798        return $this->_validatePackageDownload($dep, $required, array(), $depv1);
40799    }
40800
40801    /**
40802     * Verify that uninstalling packages passed in to command line is OK.
40803     *
40804     * @param PEAR_Installer $dl
40805     * @return PEAR_Error|true
40806     */
40807    function validatePackageUninstall(&$dl)
40808    {
40809        if (PEAR::isError($this->_dependencydb)) {
40810            return $this->_dependencydb;
40811        }
40812
40813        $params = array();
40814        // construct an array of "downloaded" packages to fool the package dependency checker
40815        // into using these to validate uninstalls of circular dependencies
40816        $downloaded = &$dl->getUninstallPackages();
40817        foreach ($downloaded as $i => $pf) {
40818            if (!class_exists('PEAR_Downloader_Package')) {
40819                require_once 'PEAR/Downloader/Package.php';
40820            }
40821            $dp = &new PEAR_Downloader_Package($dl);
40822            $dp->setPackageFile($downloaded[$i]);
40823            $params[$i] = &$dp;
40824        }
40825
40826        // check cache
40827        $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' .
40828            strtolower($this->_currentPackage['package']);
40829        if (isset($dl->___uninstall_package_cache)) {
40830            $badpackages = $dl->___uninstall_package_cache;
40831            if (isset($badpackages[$memyselfandI]['warnings'])) {
40832                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
40833                    $dl->log(0, $warning[0]);
40834                }
40835            }
40836
40837            if (isset($badpackages[$memyselfandI]['errors'])) {
40838                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
40839                    if (is_array($error)) {
40840                        $dl->log(0, $error[0]);
40841                    } else {
40842                        $dl->log(0, $error->getMessage());
40843                    }
40844                }
40845
40846                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
40847                    return $this->warning(
40848                        'warning: %s should not be uninstalled, other installed packages depend ' .
40849                        'on this package');
40850                }
40851
40852                return $this->raiseError(
40853                    '%s cannot be uninstalled, other installed packages depend on this package');
40854            }
40855
40856            return true;
40857        }
40858
40859        // first, list the immediate parents of each package to be uninstalled
40860        $perpackagelist = array();
40861        $allparents = array();
40862        foreach ($params as $i => $param) {
40863            $a = array(
40864                'channel' => strtolower($param->getChannel()),
40865                'package' => strtolower($param->getPackage())
40866            );
40867
40868            $deps = $this->_dependencydb->getDependentPackages($a);
40869            if ($deps) {
40870                foreach ($deps as $d) {
40871                    $pardeps = $this->_dependencydb->getDependencies($d);
40872                    foreach ($pardeps as $dep) {
40873                        if (strtolower($dep['dep']['channel']) == $a['channel'] &&
40874                              strtolower($dep['dep']['name']) == $a['package']) {
40875                            if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) {
40876                                $perpackagelist[$a['channel'] . '/' . $a['package']] = array();
40877                            }
40878                            $perpackagelist[$a['channel'] . '/' . $a['package']][]
40879                                = array($d['channel'] . '/' . $d['package'], $dep);
40880                            if (!isset($allparents[$d['channel'] . '/' . $d['package']])) {
40881                                $allparents[$d['channel'] . '/' . $d['package']] = array();
40882                            }
40883                            if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) {
40884                                $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array();
40885                            }
40886                            $allparents[$d['channel'] . '/' . $d['package']]
40887                                       [$a['channel'] . '/' . $a['package']][]
40888                                = array($d, $dep);
40889                        }
40890                    }
40891                }
40892            }
40893        }
40894
40895        // next, remove any packages from the parents list that are not installed
40896        $remove = array();
40897        foreach ($allparents as $parent => $d1) {
40898            foreach ($d1 as $d) {
40899                if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) {
40900                    continue;
40901                }
40902                $remove[$parent] = true;
40903            }
40904        }
40905
40906        // next remove any packages from the parents list that are not passed in for
40907        // uninstallation
40908        foreach ($allparents as $parent => $d1) {
40909            foreach ($d1 as $d) {
40910                foreach ($params as $param) {
40911                    if (strtolower($param->getChannel()) == $d[0][0]['channel'] &&
40912                          strtolower($param->getPackage()) == $d[0][0]['package']) {
40913                        // found it
40914                        continue 3;
40915                    }
40916                }
40917                $remove[$parent] = true;
40918            }
40919        }
40920
40921        // remove all packages whose dependencies fail
40922        // save which ones failed for error reporting
40923        $badchildren = array();
40924        do {
40925            $fail = false;
40926            foreach ($remove as $package => $unused) {
40927                if (!isset($allparents[$package])) {
40928                    continue;
40929                }
40930
40931                foreach ($allparents[$package] as $kid => $d1) {
40932                    foreach ($d1 as $depinfo) {
40933                        if ($depinfo[1]['type'] != 'optional') {
40934                            if (isset($badchildren[$kid])) {
40935                                continue;
40936                            }
40937                            $badchildren[$kid] = true;
40938                            $remove[$kid] = true;
40939                            $fail = true;
40940                            continue 2;
40941                        }
40942                    }
40943                }
40944                if ($fail) {
40945                    // start over, we removed some children
40946                    continue 2;
40947                }
40948            }
40949        } while ($fail);
40950
40951        // next, construct the list of packages that can't be uninstalled
40952        $badpackages = array();
40953        $save = $this->_currentPackage;
40954        foreach ($perpackagelist as $package => $packagedeps) {
40955            foreach ($packagedeps as $parent) {
40956                if (!isset($remove[$parent[0]])) {
40957                    continue;
40958                }
40959
40960                $packagename = $this->_registry->parsePackageName($parent[0]);
40961                $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']);
40962                $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']);
40963                $packagename['package'] = $pa->getPackage();
40964                $this->_currentPackage = $packagename;
40965                // parent is not present in uninstall list, make sure we can actually
40966                // uninstall it (parent dep is optional)
40967                $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']);
40968                $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']);
40969                $parentname['package'] = $pa->getPackage();
40970                $parent[1]['dep']['package'] = $parentname['package'];
40971                $parent[1]['dep']['channel'] = $parentname['channel'];
40972                if ($parent[1]['type'] == 'optional') {
40973                    $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl);
40974                    if ($test !== true) {
40975                        $badpackages[$package]['warnings'][] = $test;
40976                    }
40977                } else {
40978                    $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl);
40979                    if ($test !== true) {
40980                        $badpackages[$package]['errors'][] = $test;
40981                    }
40982                }
40983            }
40984        }
40985
40986        $this->_currentPackage          = $save;
40987        $dl->___uninstall_package_cache = $badpackages;
40988        if (isset($badpackages[$memyselfandI])) {
40989            if (isset($badpackages[$memyselfandI]['warnings'])) {
40990                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
40991                    $dl->log(0, $warning[0]);
40992                }
40993            }
40994
40995            if (isset($badpackages[$memyselfandI]['errors'])) {
40996                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
40997                    if (is_array($error)) {
40998                        $dl->log(0, $error[0]);
40999                    } else {
41000                        $dl->log(0, $error->getMessage());
41001                    }
41002                }
41003
41004                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
41005                    return $this->warning(
41006                        'warning: %s should not be uninstalled, other installed packages depend ' .
41007                        'on this package');
41008                }
41009
41010                return $this->raiseError(
41011                    '%s cannot be uninstalled, other installed packages depend on this package');
41012            }
41013        }
41014
41015        return true;
41016    }
41017
41018    function _validatePackageUninstall($dep, $required, $dl)
41019    {
41020        $depname = $this->_registry->parsedPackageNameToString($dep, true);
41021        $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']);
41022        if (!$version) {
41023            return true;
41024        }
41025
41026        $extra = $this->_getExtraString($dep);
41027        if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
41028            $dep['exclude'] = array($dep['exclude']);
41029        }
41030
41031        if (isset($dep['conflicts'])) {
41032            return true; // uninstall OK - these packages conflict (probably installed with --force)
41033        }
41034
41035        if (!isset($dep['min']) && !isset($dep['max'])) {
41036            if (!$required) {
41037                return $this->warning('"' . $depname . '" can be optionally used by ' .
41038                        'installed package %s' . $extra);
41039            }
41040
41041            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
41042                return $this->raiseError('"' . $depname . '" is required by ' .
41043                    'installed package %s' . $extra);
41044            }
41045
41046            return $this->warning('warning: "' . $depname . '" is required by ' .
41047                'installed package %s' . $extra);
41048        }
41049
41050        $fail = false;
41051        if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) {
41052            $fail = true;
41053        }
41054
41055        if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) {
41056            $fail = true;
41057        }
41058
41059        // we re-use this variable, preserve the original value
41060        $saverequired = $required;
41061        if (!$required) {
41062            return $this->warning($depname . $extra . ' can be optionally used by installed package' .
41063                    ' "%s"');
41064        }
41065
41066        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
41067            return $this->raiseError($depname . $extra . ' is required by installed package' .
41068                ' "%s"');
41069        }
41070
41071        return $this->raiseError('warning: ' . $depname . $extra .
41072            ' is required by installed package "%s"');
41073    }
41074
41075    /**
41076     * validate a downloaded package against installed packages
41077     *
41078     * As of PEAR 1.4.3, this will only validate
41079     *
41080     * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2
41081     *              $pkg package identifier (either
41082     *                   array('package' => blah, 'channel' => blah) or an array with
41083     *                   index 'info' referencing an object)
41084     * @param PEAR_Downloader $dl
41085     * @param array $params full list of packages to install
41086     * @return true|PEAR_Error
41087     */
41088    function validatePackage($pkg, &$dl, $params = array())
41089    {
41090        if (is_array($pkg) && isset($pkg['info'])) {
41091            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']);
41092        } else {
41093            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg);
41094        }
41095
41096        $fail = false;
41097        if ($deps) {
41098            if (!class_exists('PEAR_Downloader_Package')) {
41099                require_once 'PEAR/Downloader/Package.php';
41100            }
41101
41102            $dp = &new PEAR_Downloader_Package($dl);
41103            if (is_object($pkg)) {
41104                $dp->setPackageFile($pkg);
41105            } else {
41106                $dp->setDownloadURL($pkg);
41107            }
41108
41109            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
41110            foreach ($deps as $channel => $info) {
41111                foreach ($info as $package => $ds) {
41112                    foreach ($params as $packd) {
41113                        if (strtolower($packd->getPackage()) == strtolower($package) &&
41114                              $packd->getChannel() == $channel) {
41115                            $dl->log(3, 'skipping installed package check of "' .
41116                                        $this->_registry->parsedPackageNameToString(
41117                                            array('channel' => $channel, 'package' => $package),
41118                                            true) .
41119                                        '", version "' . $packd->getVersion() . '" will be ' .
41120                                        'downloaded and installed');
41121                            continue 2; // jump to next package
41122                        }
41123                    }
41124
41125                    foreach ($ds as $d) {
41126                        $checker = &new PEAR_Dependency2($this->_config, $this->_options,
41127                            array('channel' => $channel, 'package' => $package), $this->_state);
41128                        $dep = $d['dep'];
41129                        $required = $d['type'] == 'required';
41130                        $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp));
41131                        if (is_array($ret)) {
41132                            $dl->log(0, $ret[0]);
41133                        } elseif (PEAR::isError($ret)) {
41134                            $dl->log(0, $ret->getMessage());
41135                            $fail = true;
41136                        }
41137                    }
41138                }
41139            }
41140            PEAR::popErrorHandling();
41141        }
41142
41143        if ($fail) {
41144            return $this->raiseError(
41145                '%s cannot be installed, conflicts with installed packages');
41146        }
41147
41148        return true;
41149    }
41150
41151    /**
41152     * validate a package.xml 1.0 dependency
41153     */
41154    function validateDependency1($dep, $params = array())
41155    {
41156        if (!isset($dep['optional'])) {
41157            $dep['optional'] = 'no';
41158        }
41159
41160        list($newdep, $type) = $this->normalizeDep($dep);
41161        if (!$newdep) {
41162            return $this->raiseError("Invalid Dependency");
41163        }
41164
41165        if (method_exists($this, "validate{$type}Dependency")) {
41166            return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no',
41167                $params, true);
41168        }
41169    }
41170
41171    /**
41172     * Convert a 1.0 dep into a 2.0 dep
41173     */
41174    function normalizeDep($dep)
41175    {
41176        $types = array(
41177            'pkg' => 'Package',
41178            'ext' => 'Extension',
41179            'os' => 'Os',
41180            'php' => 'Php'
41181        );
41182
41183        if (!isset($types[$dep['type']])) {
41184            return array(false, false);
41185        }
41186
41187        $type = $types[$dep['type']];
41188
41189        $newdep = array();
41190        switch ($type) {
41191            case 'Package' :
41192                $newdep['channel'] = 'pear.php.net';
41193            case 'Extension' :
41194            case 'Os' :
41195                $newdep['name'] = $dep['name'];
41196            break;
41197        }
41198
41199        $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']);
41200        switch ($dep['rel']) {
41201            case 'has' :
41202                return array($newdep, $type);
41203            break;
41204            case 'not' :
41205                $newdep['conflicts'] = true;
41206            break;
41207            case '>=' :
41208            case '>' :
41209                $newdep['min'] = $dep['version'];
41210                if ($dep['rel'] == '>') {
41211                    $newdep['exclude'] = $dep['version'];
41212                }
41213            break;
41214            case '<=' :
41215            case '<' :
41216                $newdep['max'] = $dep['version'];
41217                if ($dep['rel'] == '<') {
41218                    $newdep['exclude'] = $dep['version'];
41219                }
41220            break;
41221            case 'ne' :
41222            case '!=' :
41223                $newdep['min'] = '0';
41224                $newdep['max'] = '100000';
41225                $newdep['exclude'] = $dep['version'];
41226            break;
41227            case '==' :
41228                $newdep['min'] = $dep['version'];
41229                $newdep['max'] = $dep['version'];
41230            break;
41231        }
41232        if ($type == 'Php') {
41233            if (!isset($newdep['min'])) {
41234                $newdep['min'] = '4.4.0';
41235            }
41236
41237            if (!isset($newdep['max'])) {
41238                $newdep['max'] = '6.0.0';
41239            }
41240        }
41241        return array($newdep, $type);
41242    }
41243
41244    /**
41245     * Converts text comparing operators to them sign equivalents
41246     *
41247     * Example: 'ge' to '>='
41248     *
41249     * @access public
41250     * @param  string Operator
41251     * @return string Sign equivalent
41252     */
41253    function signOperator($operator)
41254    {
41255        switch($operator) {
41256            case 'lt': return '<';
41257            case 'le': return '<=';
41258            case 'gt': return '>';
41259            case 'ge': return '>=';
41260            case 'eq': return '==';
41261            case 'ne': return '!=';
41262            default:
41263                return $operator;
41264        }
41265    }
41266
41267    function raiseError($msg)
41268    {
41269        if (isset($this->_options['ignore-errors'])) {
41270            return $this->warning($msg);
41271        }
41272
41273        return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString(
41274            $this->_currentPackage, true)));
41275    }
41276
41277    function warning($msg)
41278    {
41279        return array(sprintf($msg, $this->_registry->parsedPackageNameToString(
41280            $this->_currentPackage, true)));
41281    }
41282}PEAR-1.9.4/PEAR/Downloader.php0000644000076500000240000020203111605156614014570 0ustar  helgistaff<?php
41283/**
41284 * PEAR_Downloader, the PEAR Installer's download utility class
41285 *
41286 * PHP versions 4 and 5
41287 *
41288 * @category   pear
41289 * @package    PEAR
41290 * @author     Greg Beaver <cellog@php.net>
41291 * @author     Stig Bakken <ssb@php.net>
41292 * @author     Tomas V. V. Cox <cox@idecnet.com>
41293 * @author     Martin Jansen <mj@php.net>
41294 * @copyright  1997-2009 The Authors
41295 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
41296 * @version    CVS: $Id: Downloader.php 313024 2011-07-06 19:51:24Z dufuz $
41297 * @link       http://pear.php.net/package/PEAR
41298 * @since      File available since Release 1.3.0
41299 */
41300
41301/**
41302 * Needed for constants, extending
41303 */
41304require_once 'PEAR/Common.php';
41305
41306define('PEAR_INSTALLER_OK',       1);
41307define('PEAR_INSTALLER_FAILED',   0);
41308define('PEAR_INSTALLER_SKIPPED', -1);
41309define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
41310
41311/**
41312 * Administration class used to download anything from the internet (PEAR Packages,
41313 * static URLs, xml files)
41314 *
41315 * @category   pear
41316 * @package    PEAR
41317 * @author     Greg Beaver <cellog@php.net>
41318 * @author     Stig Bakken <ssb@php.net>
41319 * @author     Tomas V. V. Cox <cox@idecnet.com>
41320 * @author     Martin Jansen <mj@php.net>
41321 * @copyright  1997-2009 The Authors
41322 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
41323 * @version    Release: 1.9.4
41324 * @link       http://pear.php.net/package/PEAR
41325 * @since      Class available since Release 1.3.0
41326 */
41327class PEAR_Downloader extends PEAR_Common
41328{
41329    /**
41330     * @var PEAR_Registry
41331     * @access private
41332     */
41333    var $_registry;
41334
41335    /**
41336     * Preferred Installation State (snapshot, devel, alpha, beta, stable)
41337     * @var string|null
41338     * @access private
41339     */
41340    var $_preferredState;
41341
41342    /**
41343     * Options from command-line passed to Install.
41344     *
41345     * Recognized options:<br />
41346     *  - onlyreqdeps   : install all required dependencies as well
41347     *  - alldeps       : install all dependencies, including optional
41348     *  - installroot   : base relative path to install files in
41349     *  - force         : force a download even if warnings would prevent it
41350     *  - nocompress    : download uncompressed tarballs
41351     * @see PEAR_Command_Install
41352     * @access private
41353     * @var array
41354     */
41355    var $_options;
41356
41357    /**
41358     * Downloaded Packages after a call to download().
41359     *
41360     * Format of each entry:
41361     *
41362     * <code>
41363     * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
41364     *    'info' => array() // parsed package.xml
41365     * );
41366     * </code>
41367     * @access private
41368     * @var array
41369     */
41370    var $_downloadedPackages = array();
41371
41372    /**
41373     * Packages slated for download.
41374     *
41375     * This is used to prevent downloading a package more than once should it be a dependency
41376     * for two packages to be installed.
41377     * Format of each entry:
41378     *
41379     * <pre>
41380     * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
41381     * );
41382     * </pre>
41383     * @access private
41384     * @var array
41385     */
41386    var $_toDownload = array();
41387
41388    /**
41389     * Array of every package installed, with names lower-cased.
41390     *
41391     * Format:
41392     * <code>
41393     * array('package1' => 0, 'package2' => 1, );
41394     * </code>
41395     * @var array
41396     */
41397    var $_installed = array();
41398
41399    /**
41400     * @var array
41401     * @access private
41402     */
41403    var $_errorStack = array();
41404
41405    /**
41406     * @var boolean
41407     * @access private
41408     */
41409    var $_internalDownload = false;
41410
41411    /**
41412     * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()}
41413     * @var array
41414     * @access private
41415     */
41416    var $_packageSortTree;
41417
41418    /**
41419     * Temporary directory, or configuration value where downloads will occur
41420     * @var string
41421     */
41422    var $_downloadDir;
41423
41424    /**
41425     * @param PEAR_Frontend_*
41426     * @param array
41427     * @param PEAR_Config
41428     */
41429    function PEAR_Downloader(&$ui, $options, &$config)
41430    {
41431        parent::PEAR_Common();
41432        $this->_options = $options;
41433        $this->config = &$config;
41434        $this->_preferredState = $this->config->get('preferred_state');
41435        $this->ui = &$ui;
41436        if (!$this->_preferredState) {
41437            // don't inadvertantly use a non-set preferred_state
41438            $this->_preferredState = null;
41439        }
41440
41441        if (isset($this->_options['installroot'])) {
41442            $this->config->setInstallRoot($this->_options['installroot']);
41443        }
41444        $this->_registry = &$config->getRegistry();
41445
41446        if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
41447            $this->_installed = $this->_registry->listAllPackages();
41448            foreach ($this->_installed as $key => $unused) {
41449                if (!count($unused)) {
41450                    continue;
41451                }
41452                $strtolower = create_function('$a','return strtolower($a);');
41453                array_walk($this->_installed[$key], $strtolower);
41454            }
41455        }
41456    }
41457
41458    /**
41459     * Attempt to discover a channel's remote capabilities from
41460     * its server name
41461     * @param string
41462     * @return boolean
41463     */
41464    function discover($channel)
41465    {
41466        $this->log(1, 'Attempting to discover channel "' . $channel . '"...');
41467        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
41468        $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
41469        if (!class_exists('System')) {
41470            require_once 'System.php';
41471        }
41472
41473        $tmpdir = $this->config->get('temp_dir');
41474        $tmp = System::mktemp('-d -t "' . $tmpdir . '"');
41475        $a   = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
41476        PEAR::popErrorHandling();
41477        if (PEAR::isError($a)) {
41478            // Attempt to fallback to https automatically.
41479            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
41480            $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...');
41481            $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
41482            PEAR::popErrorHandling();
41483            if (PEAR::isError($a)) {
41484                return false;
41485            }
41486        }
41487
41488        list($a, $lastmodified) = $a;
41489        if (!class_exists('PEAR_ChannelFile')) {
41490            require_once 'PEAR/ChannelFile.php';
41491        }
41492
41493        $b = new PEAR_ChannelFile;
41494        if ($b->fromXmlFile($a)) {
41495            unlink($a);
41496            if ($this->config->get('auto_discover')) {
41497                $this->_registry->addChannel($b, $lastmodified);
41498                $alias = $b->getName();
41499                if ($b->getName() == $this->_registry->channelName($b->getAlias())) {
41500                    $alias = $b->getAlias();
41501                }
41502
41503                $this->log(1, 'Auto-discovered channel "' . $channel .
41504                    '", alias "' . $alias . '", adding to registry');
41505            }
41506
41507            return true;
41508        }
41509
41510        unlink($a);
41511        return false;
41512    }
41513
41514    /**
41515     * For simpler unit-testing
41516     * @param PEAR_Downloader
41517     * @return PEAR_Downloader_Package
41518     */
41519    function &newDownloaderPackage(&$t)
41520    {
41521        if (!class_exists('PEAR_Downloader_Package')) {
41522            require_once 'PEAR/Downloader/Package.php';
41523        }
41524        $a = &new PEAR_Downloader_Package($t);
41525        return $a;
41526    }
41527
41528    /**
41529     * For simpler unit-testing
41530     * @param PEAR_Config
41531     * @param array
41532     * @param array
41533     * @param int
41534     */
41535    function &getDependency2Object(&$c, $i, $p, $s)
41536    {
41537        if (!class_exists('PEAR_Dependency2')) {
41538            require_once 'PEAR/Dependency2.php';
41539        }
41540        $z = &new PEAR_Dependency2($c, $i, $p, $s);
41541        return $z;
41542    }
41543
41544    function &download($params)
41545    {
41546        if (!count($params)) {
41547            $a = array();
41548            return $a;
41549        }
41550
41551        if (!isset($this->_registry)) {
41552            $this->_registry = &$this->config->getRegistry();
41553        }
41554
41555        $channelschecked = array();
41556        // convert all parameters into PEAR_Downloader_Package objects
41557        foreach ($params as $i => $param) {
41558            $params[$i] = &$this->newDownloaderPackage($this);
41559            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
41560            $err = $params[$i]->initialize($param);
41561            PEAR::staticPopErrorHandling();
41562            if (!$err) {
41563                // skip parameters that were missed by preferred_state
41564                continue;
41565            }
41566
41567            if (PEAR::isError($err)) {
41568                if (!isset($this->_options['soft']) && $err->getMessage() !== '') {
41569                    $this->log(0, $err->getMessage());
41570                }
41571
41572                $params[$i] = false;
41573                if (is_object($param)) {
41574                    $param = $param->getChannel() . '/' . $param->getPackage();
41575                }
41576
41577                if (!isset($this->_options['soft'])) {
41578                    $this->log(2, 'Package "' . $param . '" is not valid');
41579                }
41580
41581                // Message logged above in a specific verbose mode, passing null to not show up on CLI
41582                $this->pushError(null, PEAR_INSTALLER_SKIPPED);
41583            } else {
41584                do {
41585                    if ($params[$i] && $params[$i]->getType() == 'local') {
41586                        // bug #7090 skip channel.xml check for local packages
41587                        break;
41588                    }
41589
41590                    if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) &&
41591                          !isset($this->_options['offline'])
41592                    ) {
41593                        $channelschecked[$params[$i]->getChannel()] = true;
41594                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
41595                        if (!class_exists('System')) {
41596                            require_once 'System.php';
41597                        }
41598
41599                        $curchannel = &$this->_registry->getChannel($params[$i]->getChannel());
41600                        if (PEAR::isError($curchannel)) {
41601                            PEAR::staticPopErrorHandling();
41602                            return $this->raiseError($curchannel);
41603                        }
41604
41605                        if (PEAR::isError($dir = $this->getDownloadDir())) {
41606                            PEAR::staticPopErrorHandling();
41607                            break;
41608                        }
41609
41610                        $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel());
41611                        $url    = 'http://' . $mirror . '/channel.xml';
41612                        $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified());
41613
41614                        PEAR::staticPopErrorHandling();
41615                        if (PEAR::isError($a) || !$a) {
41616                            // Attempt fallback to https automatically
41617                            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
41618                            $a = $this->downloadHttp('https://' . $mirror .
41619                                '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified());
41620
41621                            PEAR::staticPopErrorHandling();
41622                            if (PEAR::isError($a) || !$a) {
41623                                break;
41624                            }
41625                        }
41626                        $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' .
41627                            'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() .
41628                            '" to update');
41629                    }
41630                } while (false);
41631
41632                if ($params[$i] && !isset($this->_options['downloadonly'])) {
41633                    if (isset($this->_options['packagingroot'])) {
41634                        $checkdir = $this->_prependPath(
41635                            $this->config->get('php_dir', null, $params[$i]->getChannel()),
41636                            $this->_options['packagingroot']);
41637                    } else {
41638                        $checkdir = $this->config->get('php_dir',
41639                            null, $params[$i]->getChannel());
41640                    }
41641
41642                    while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) {
41643                        $checkdir = dirname($checkdir);
41644                    }
41645
41646                    if ($checkdir == '.') {
41647                        $checkdir = '/';
41648                    }
41649
41650                    if (!is_writeable($checkdir)) {
41651                        return PEAR::raiseError('Cannot install, php_dir for channel "' .
41652                            $params[$i]->getChannel() . '" is not writeable by the current user');
41653                    }
41654                }
41655            }
41656        }
41657
41658        unset($channelschecked);
41659        PEAR_Downloader_Package::removeDuplicates($params);
41660        if (!count($params)) {
41661            $a = array();
41662            return $a;
41663        }
41664
41665        if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) {
41666            $reverify = true;
41667            while ($reverify) {
41668                $reverify = false;
41669                foreach ($params as $i => $param) {
41670                    //PHP Bug 40768 / PEAR Bug #10944
41671                    //Nested foreaches fail in PHP 5.2.1
41672                    key($params);
41673                    $ret = $params[$i]->detectDependencies($params);
41674                    if (PEAR::isError($ret)) {
41675                        $reverify = true;
41676                        $params[$i] = false;
41677                        PEAR_Downloader_Package::removeDuplicates($params);
41678                        if (!isset($this->_options['soft'])) {
41679                            $this->log(0, $ret->getMessage());
41680                        }
41681                        continue 2;
41682                    }
41683                }
41684            }
41685        }
41686
41687        if (isset($this->_options['offline'])) {
41688            $this->log(3, 'Skipping dependency download check, --offline specified');
41689        }
41690
41691        if (!count($params)) {
41692            $a = array();
41693            return $a;
41694        }
41695
41696        while (PEAR_Downloader_Package::mergeDependencies($params));
41697        PEAR_Downloader_Package::removeDuplicates($params, true);
41698        $errorparams = array();
41699        if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) {
41700            if (count($errorparams)) {
41701                foreach ($errorparams as $param) {
41702                    $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage());
41703                    $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED);
41704                }
41705                $a = array();
41706                return $a;
41707            }
41708        }
41709
41710        PEAR_Downloader_Package::removeInstalled($params);
41711        if (!count($params)) {
41712            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
41713            $a = array();
41714            return $a;
41715        }
41716
41717        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
41718        $err = $this->analyzeDependencies($params);
41719        PEAR::popErrorHandling();
41720        if (!count($params)) {
41721            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
41722            $a = array();
41723            return $a;
41724        }
41725
41726        $ret = array();
41727        $newparams = array();
41728        if (isset($this->_options['pretend'])) {
41729            return $params;
41730        }
41731
41732        $somefailed = false;
41733        foreach ($params as $i => $package) {
41734            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
41735            $pf = &$params[$i]->download();
41736            PEAR::staticPopErrorHandling();
41737            if (PEAR::isError($pf)) {
41738                if (!isset($this->_options['soft'])) {
41739                    $this->log(1, $pf->getMessage());
41740                    $this->log(0, 'Error: cannot download "' .
41741                        $this->_registry->parsedPackageNameToString($package->getParsedPackage(),
41742                            true) .
41743                        '"');
41744                }
41745                $somefailed = true;
41746                continue;
41747            }
41748
41749            $newparams[] = &$params[$i];
41750            $ret[] = array(
41751                'file' => $pf->getArchiveFile(),
41752                'info' => &$pf,
41753                'pkg'  => $pf->getPackage()
41754            );
41755        }
41756
41757        if ($somefailed) {
41758            // remove params that did not download successfully
41759            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
41760            $err = $this->analyzeDependencies($newparams, true);
41761            PEAR::popErrorHandling();
41762            if (!count($newparams)) {
41763                $this->pushError('Download failed', PEAR_INSTALLER_FAILED);
41764                $a = array();
41765                return $a;
41766            }
41767        }
41768
41769        $this->_downloadedPackages = $ret;
41770        return $newparams;
41771    }
41772
41773    /**
41774     * @param array all packages to be installed
41775     */
41776    function analyzeDependencies(&$params, $force = false)
41777    {
41778        if (isset($this->_options['downloadonly'])) {
41779            return;
41780        }
41781
41782        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
41783        $redo  = true;
41784        $reset = $hasfailed = $failed = false;
41785        while ($redo) {
41786            $redo = false;
41787            foreach ($params as $i => $param) {
41788                $deps = $param->getDeps();
41789                if (!$deps) {
41790                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
41791                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
41792                    $send = $param->getPackageFile();
41793
41794                    $installcheck = $depchecker->validatePackage($send, $this, $params);
41795                    if (PEAR::isError($installcheck)) {
41796                        if (!isset($this->_options['soft'])) {
41797                            $this->log(0, $installcheck->getMessage());
41798                        }
41799                        $hasfailed  = true;
41800                        $params[$i] = false;
41801                        $reset      = true;
41802                        $redo       = true;
41803                        $failed     = false;
41804                        PEAR_Downloader_Package::removeDuplicates($params);
41805                        continue 2;
41806                    }
41807                    continue;
41808                }
41809
41810                if (!$reset && $param->alreadyValidated() && !$force) {
41811                    continue;
41812                }
41813
41814                if (count($deps)) {
41815                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
41816                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
41817                    $send = $param->getPackageFile();
41818                    if ($send === null) {
41819                        $send = $param->getDownloadURL();
41820                    }
41821
41822                    $installcheck = $depchecker->validatePackage($send, $this, $params);
41823                    if (PEAR::isError($installcheck)) {
41824                        if (!isset($this->_options['soft'])) {
41825                            $this->log(0, $installcheck->getMessage());
41826                        }
41827                        $hasfailed  = true;
41828                        $params[$i] = false;
41829                        $reset      = true;
41830                        $redo       = true;
41831                        $failed     = false;
41832                        PEAR_Downloader_Package::removeDuplicates($params);
41833                        continue 2;
41834                    }
41835
41836                    $failed = false;
41837                    if (isset($deps['required']) && is_array($deps['required'])) {
41838                        foreach ($deps['required'] as $type => $dep) {
41839                            // note: Dependency2 will never return a PEAR_Error if ignore-errors
41840                            // is specified, so soft is needed to turn off logging
41841                            if (!isset($dep[0])) {
41842                                if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep,
41843                                      true, $params))) {
41844                                    $failed = true;
41845                                    if (!isset($this->_options['soft'])) {
41846                                        $this->log(0, $e->getMessage());
41847                                    }
41848                                } elseif (is_array($e) && !$param->alreadyValidated()) {
41849                                    if (!isset($this->_options['soft'])) {
41850                                        $this->log(0, $e[0]);
41851                                    }
41852                                }
41853                            } else {
41854                                foreach ($dep as $d) {
41855                                    if (PEAR::isError($e =
41856                                          $depchecker->{"validate{$type}Dependency"}($d,
41857                                          true, $params))) {
41858                                        $failed = true;
41859                                        if (!isset($this->_options['soft'])) {
41860                                            $this->log(0, $e->getMessage());
41861                                        }
41862                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
41863                                        if (!isset($this->_options['soft'])) {
41864                                            $this->log(0, $e[0]);
41865                                        }
41866                                    }
41867                                }
41868                            }
41869                        }
41870
41871                        if (isset($deps['optional']) && is_array($deps['optional'])) {
41872                            foreach ($deps['optional'] as $type => $dep) {
41873                                if (!isset($dep[0])) {
41874                                    if (PEAR::isError($e =
41875                                          $depchecker->{"validate{$type}Dependency"}($dep,
41876                                          false, $params))) {
41877                                        $failed = true;
41878                                        if (!isset($this->_options['soft'])) {
41879                                            $this->log(0, $e->getMessage());
41880                                        }
41881                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
41882                                        if (!isset($this->_options['soft'])) {
41883                                            $this->log(0, $e[0]);
41884                                        }
41885                                    }
41886                                } else {
41887                                    foreach ($dep as $d) {
41888                                        if (PEAR::isError($e =
41889                                              $depchecker->{"validate{$type}Dependency"}($d,
41890                                              false, $params))) {
41891                                            $failed = true;
41892                                            if (!isset($this->_options['soft'])) {
41893                                                $this->log(0, $e->getMessage());
41894                                            }
41895                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
41896                                            if (!isset($this->_options['soft'])) {
41897                                                $this->log(0, $e[0]);
41898                                            }
41899                                        }
41900                                    }
41901                                }
41902                            }
41903                        }
41904
41905                        $groupname = $param->getGroup();
41906                        if (isset($deps['group']) && $groupname) {
41907                            if (!isset($deps['group'][0])) {
41908                                $deps['group'] = array($deps['group']);
41909                            }
41910
41911                            $found = false;
41912                            foreach ($deps['group'] as $group) {
41913                                if ($group['attribs']['name'] == $groupname) {
41914                                    $found = true;
41915                                    break;
41916                                }
41917                            }
41918
41919                            if ($found) {
41920                                unset($group['attribs']);
41921                                foreach ($group as $type => $dep) {
41922                                    if (!isset($dep[0])) {
41923                                        if (PEAR::isError($e =
41924                                              $depchecker->{"validate{$type}Dependency"}($dep,
41925                                              false, $params))) {
41926                                            $failed = true;
41927                                            if (!isset($this->_options['soft'])) {
41928                                                $this->log(0, $e->getMessage());
41929                                            }
41930                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
41931                                            if (!isset($this->_options['soft'])) {
41932                                                $this->log(0, $e[0]);
41933                                            }
41934                                        }
41935                                    } else {
41936                                        foreach ($dep as $d) {
41937                                            if (PEAR::isError($e =
41938                                                  $depchecker->{"validate{$type}Dependency"}($d,
41939                                                  false, $params))) {
41940                                                $failed = true;
41941                                                if (!isset($this->_options['soft'])) {
41942                                                    $this->log(0, $e->getMessage());
41943                                                }
41944                                            } elseif (is_array($e) && !$param->alreadyValidated()) {
41945                                                if (!isset($this->_options['soft'])) {
41946                                                    $this->log(0, $e[0]);
41947                                                }
41948                                            }
41949                                        }
41950                                    }
41951                                }
41952                            }
41953                        }
41954                    } else {
41955                        foreach ($deps as $dep) {
41956                            if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) {
41957                                $failed = true;
41958                                if (!isset($this->_options['soft'])) {
41959                                    $this->log(0, $e->getMessage());
41960                                }
41961                            } elseif (is_array($e) && !$param->alreadyValidated()) {
41962                                if (!isset($this->_options['soft'])) {
41963                                    $this->log(0, $e[0]);
41964                                }
41965                            }
41966                        }
41967                    }
41968                    $params[$i]->setValidated();
41969                }
41970
41971                if ($failed) {
41972                    $hasfailed  = true;
41973                    $params[$i] = false;
41974                    $reset      = true;
41975                    $redo       = true;
41976                    $failed     = false;
41977                    PEAR_Downloader_Package::removeDuplicates($params);
41978                    continue 2;
41979                }
41980            }
41981        }
41982
41983        PEAR::staticPopErrorHandling();
41984        if ($hasfailed && (isset($this->_options['ignore-errors']) ||
41985              isset($this->_options['nodeps']))) {
41986            // this is probably not needed, but just in case
41987            if (!isset($this->_options['soft'])) {
41988                $this->log(0, 'WARNING: dependencies failed');
41989            }
41990        }
41991    }
41992
41993    /**
41994     * Retrieve the directory that downloads will happen in
41995     * @access private
41996     * @return string
41997     */
41998    function getDownloadDir()
41999    {
42000        if (isset($this->_downloadDir)) {
42001            return $this->_downloadDir;
42002        }
42003
42004        $downloaddir = $this->config->get('download_dir');
42005        if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) {
42006            if  (is_dir($downloaddir) && !is_writable($downloaddir)) {
42007                $this->log(0, 'WARNING: configuration download directory "' . $downloaddir .
42008                    '" is not writeable.  Change download_dir config variable to ' .
42009                    'a writeable dir to avoid this warning');
42010            }
42011
42012            if (!class_exists('System')) {
42013                require_once 'System.php';
42014            }
42015
42016            if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
42017                return $downloaddir;
42018            }
42019            $this->log(3, '+ tmp dir created at ' . $downloaddir);
42020        }
42021
42022        if (!is_writable($downloaddir)) {
42023            if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) ||
42024                  !is_writable($downloaddir)) {
42025                return PEAR::raiseError('download directory "' . $downloaddir .
42026                    '" is not writeable.  Change download_dir config variable to ' .
42027                    'a writeable dir');
42028            }
42029        }
42030
42031        return $this->_downloadDir = $downloaddir;
42032    }
42033
42034    function setDownloadDir($dir)
42035    {
42036        if (!@is_writable($dir)) {
42037            if (PEAR::isError(System::mkdir(array('-p', $dir)))) {
42038                return PEAR::raiseError('download directory "' . $dir .
42039                    '" is not writeable.  Change download_dir config variable to ' .
42040                    'a writeable dir');
42041            }
42042        }
42043        $this->_downloadDir = $dir;
42044    }
42045
42046    function configSet($key, $value, $layer = 'user', $channel = false)
42047    {
42048        $this->config->set($key, $value, $layer, $channel);
42049        $this->_preferredState = $this->config->get('preferred_state', null, $channel);
42050        if (!$this->_preferredState) {
42051            // don't inadvertantly use a non-set preferred_state
42052            $this->_preferredState = null;
42053        }
42054    }
42055
42056    function setOptions($options)
42057    {
42058        $this->_options = $options;
42059    }
42060
42061    function getOptions()
42062    {
42063        return $this->_options;
42064    }
42065
42066
42067    /**
42068     * @param array output of {@link parsePackageName()}
42069     * @access private
42070     */
42071    function _getPackageDownloadUrl($parr)
42072    {
42073        $curchannel = $this->config->get('default_channel');
42074        $this->configSet('default_channel', $parr['channel']);
42075        // getDownloadURL returns an array.  On error, it only contains information
42076        // on the latest release as array(version, info).  On success it contains
42077        // array(version, info, download url string)
42078        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
42079        if (!$this->_registry->channelExists($parr['channel'])) {
42080            do {
42081                if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) {
42082                    break;
42083                }
42084
42085                $this->configSet('default_channel', $curchannel);
42086                return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']);
42087            } while (false);
42088        }
42089
42090        $chan = &$this->_registry->getChannel($parr['channel']);
42091        if (PEAR::isError($chan)) {
42092            return $chan;
42093        }
42094
42095        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
42096        $version   = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']);
42097        $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']);
42098        // package is installed - use the installed release stability level
42099        if (!isset($parr['state']) && $stability !== null) {
42100            $state = $stability['release'];
42101        }
42102        PEAR::staticPopErrorHandling();
42103        $base2 = false;
42104
42105        $preferred_mirror = $this->config->get('preferred_mirror');
42106        if (!$chan->supportsREST($preferred_mirror) ||
42107              (
42108               !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror))
42109               &&
42110               !($base = $chan->getBaseURL('REST1.0', $preferred_mirror))
42111              )
42112        ) {
42113            return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
42114        }
42115
42116        if ($base2) {
42117            $rest = &$this->config->getREST('1.3', $this->_options);
42118            $base = $base2;
42119        } else {
42120            $rest = &$this->config->getREST('1.0', $this->_options);
42121        }
42122
42123        $downloadVersion = false;
42124        if (!isset($parr['version']) && !isset($parr['state']) && $version
42125              && !PEAR::isError($version)
42126              && !isset($this->_options['downloadonly'])
42127        ) {
42128            $downloadVersion = $version;
42129        }
42130
42131        $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName());
42132        if (PEAR::isError($url)) {
42133            $this->configSet('default_channel', $curchannel);
42134            return $url;
42135        }
42136
42137        if ($parr['channel'] != $curchannel) {
42138            $this->configSet('default_channel', $curchannel);
42139        }
42140
42141        if (!is_array($url)) {
42142            return $url;
42143        }
42144
42145        $url['raw'] = false; // no checking is necessary for REST
42146        if (!is_array($url['info'])) {
42147            return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
42148                'this should never happen');
42149        }
42150
42151        if (!isset($this->_options['force']) &&
42152              !isset($this->_options['downloadonly']) &&
42153              $version &&
42154              !PEAR::isError($version) &&
42155              !isset($parr['group'])
42156        ) {
42157            if (version_compare($version, $url['version'], '=')) {
42158                return PEAR::raiseError($this->_registry->parsedPackageNameToString(
42159                    $parr, true) . ' is already installed and is the same as the ' .
42160                    'released version ' . $url['version'], -976);
42161            }
42162
42163            if (version_compare($version, $url['version'], '>')) {
42164                return PEAR::raiseError($this->_registry->parsedPackageNameToString(
42165                    $parr, true) . ' is already installed and is newer than detected ' .
42166                    'released version ' . $url['version'], -976);
42167            }
42168        }
42169
42170        if (isset($url['info']['required']) || $url['compatible']) {
42171            require_once 'PEAR/PackageFile/v2.php';
42172            $pf = new PEAR_PackageFile_v2;
42173            $pf->setRawChannel($parr['channel']);
42174            if ($url['compatible']) {
42175                $pf->setRawCompatible($url['compatible']);
42176            }
42177        } else {
42178            require_once 'PEAR/PackageFile/v1.php';
42179            $pf = new PEAR_PackageFile_v1;
42180        }
42181
42182        $pf->setRawPackage($url['package']);
42183        $pf->setDeps($url['info']);
42184        if ($url['compatible']) {
42185            $pf->setCompatible($url['compatible']);
42186        }
42187
42188        $pf->setRawState($url['stability']);
42189        $url['info'] = &$pf;
42190        if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
42191            $ext = '.tar';
42192        } else {
42193            $ext = '.tgz';
42194        }
42195
42196        if (is_array($url) && isset($url['url'])) {
42197            $url['url'] .= $ext;
42198        }
42199
42200        return $url;
42201    }
42202
42203    /**
42204     * @param array dependency array
42205     * @access private
42206     */
42207    function _getDepPackageDownloadUrl($dep, $parr)
42208    {
42209        $xsdversion = isset($dep['rel']) ? '1.0' : '2.0';
42210        $curchannel = $this->config->get('default_channel');
42211        if (isset($dep['uri'])) {
42212            $xsdversion = '2.0';
42213            $chan = &$this->_registry->getChannel('__uri');
42214            if (PEAR::isError($chan)) {
42215                return $chan;
42216            }
42217
42218            $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri');
42219            $this->configSet('default_channel', '__uri');
42220        } else {
42221            if (isset($dep['channel'])) {
42222                $remotechannel = $dep['channel'];
42223            } else {
42224                $remotechannel = 'pear.php.net';
42225            }
42226
42227            if (!$this->_registry->channelExists($remotechannel)) {
42228                do {
42229                    if ($this->config->get('auto_discover')) {
42230                        if ($this->discover($remotechannel)) {
42231                            break;
42232                        }
42233                    }
42234                    return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
42235                } while (false);
42236            }
42237
42238            $chan = &$this->_registry->getChannel($remotechannel);
42239            if (PEAR::isError($chan)) {
42240                return $chan;
42241            }
42242
42243            $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel);
42244            $this->configSet('default_channel', $remotechannel);
42245        }
42246
42247        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
42248        if (isset($parr['state']) && isset($parr['version'])) {
42249            unset($parr['state']);
42250        }
42251
42252        if (isset($dep['uri'])) {
42253            $info = &$this->newDownloaderPackage($this);
42254            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
42255            $err = $info->initialize($dep);
42256            PEAR::staticPopErrorHandling();
42257            if (!$err) {
42258                // skip parameters that were missed by preferred_state
42259                return PEAR::raiseError('Cannot initialize dependency');
42260            }
42261
42262            if (PEAR::isError($err)) {
42263                if (!isset($this->_options['soft'])) {
42264                    $this->log(0, $err->getMessage());
42265                }
42266
42267                if (is_object($info)) {
42268                    $param = $info->getChannel() . '/' . $info->getPackage();
42269                }
42270                return PEAR::raiseError('Package "' . $param . '" is not valid');
42271            }
42272            return $info;
42273        } elseif ($chan->supportsREST($this->config->get('preferred_mirror'))
42274              &&
42275                (
42276                  ($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror')))
42277                    ||
42278                  ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror')))
42279                )
42280        ) {
42281            if ($base2) {
42282                $base = $base2;
42283                $rest = &$this->config->getREST('1.3', $this->_options);
42284            } else {
42285                $rest = &$this->config->getREST('1.0', $this->_options);
42286            }
42287
42288            $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr,
42289                    $state, $version, $chan->getName());
42290            if (PEAR::isError($url)) {
42291                return $url;
42292            }
42293
42294            if ($parr['channel'] != $curchannel) {
42295                $this->configSet('default_channel', $curchannel);
42296            }
42297
42298            if (!is_array($url)) {
42299                return $url;
42300            }
42301
42302            $url['raw'] = false; // no checking is necessary for REST
42303            if (!is_array($url['info'])) {
42304                return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
42305                    'this should never happen');
42306            }
42307
42308            if (isset($url['info']['required'])) {
42309                if (!class_exists('PEAR_PackageFile_v2')) {
42310                    require_once 'PEAR/PackageFile/v2.php';
42311                }
42312                $pf = new PEAR_PackageFile_v2;
42313                $pf->setRawChannel($remotechannel);
42314            } else {
42315                if (!class_exists('PEAR_PackageFile_v1')) {
42316                    require_once 'PEAR/PackageFile/v1.php';
42317                }
42318                $pf = new PEAR_PackageFile_v1;
42319
42320            }
42321            $pf->setRawPackage($url['package']);
42322            $pf->setDeps($url['info']);
42323            if ($url['compatible']) {
42324                $pf->setCompatible($url['compatible']);
42325            }
42326
42327            $pf->setRawState($url['stability']);
42328            $url['info'] = &$pf;
42329            if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
42330                $ext = '.tar';
42331            } else {
42332                $ext = '.tgz';
42333            }
42334
42335            if (is_array($url) && isset($url['url'])) {
42336                $url['url'] .= $ext;
42337            }
42338
42339            return $url;
42340        }
42341
42342        return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
42343    }
42344
42345    /**
42346     * @deprecated in favor of _getPackageDownloadUrl
42347     */
42348    function getPackageDownloadUrl($package, $version = null, $channel = false)
42349    {
42350        if ($version) {
42351            $package .= "-$version";
42352        }
42353        if ($this === null || $this->_registry === null) {
42354            $package = "http://pear.php.net/get/$package";
42355        } else {
42356            $chan = $this->_registry->getChannel($channel);
42357            if (PEAR::isError($chan)) {
42358                return '';
42359            }
42360            $package = "http://" . $chan->getServer() . "/get/$package";
42361        }
42362        if (!extension_loaded("zlib")) {
42363            $package .= '?uncompress=yes';
42364        }
42365        return $package;
42366    }
42367
42368    /**
42369     * Retrieve a list of downloaded packages after a call to {@link download()}.
42370     *
42371     * Also resets the list of downloaded packages.
42372     * @return array
42373     */
42374    function getDownloadedPackages()
42375    {
42376        $ret = $this->_downloadedPackages;
42377        $this->_downloadedPackages = array();
42378        $this->_toDownload = array();
42379        return $ret;
42380    }
42381
42382    function _downloadCallback($msg, $params = null)
42383    {
42384        switch ($msg) {
42385            case 'saveas':
42386                $this->log(1, "downloading $params ...");
42387                break;
42388            case 'done':
42389                $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
42390                break;
42391            case 'bytesread':
42392                static $bytes;
42393                if (empty($bytes)) {
42394                    $bytes = 0;
42395                }
42396                if (!($bytes % 10240)) {
42397                    $this->log(1, '.', false);
42398                }
42399                $bytes += $params;
42400                break;
42401            case 'start':
42402                if($params[1] == -1) {
42403                    $length = "Unknown size";
42404                } else {
42405                    $length = number_format($params[1], 0, '', ',')." bytes";
42406                }
42407                $this->log(1, "Starting to download {$params[0]} ($length)");
42408                break;
42409        }
42410        if (method_exists($this->ui, '_downloadCallback'))
42411            $this->ui->_downloadCallback($msg, $params);
42412    }
42413
42414    function _prependPath($path, $prepend)
42415    {
42416        if (strlen($prepend) > 0) {
42417            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
42418                if (preg_match('/^[a-z]:/i', $prepend)) {
42419                    $prepend = substr($prepend, 2);
42420                } elseif ($prepend{0} != '\\') {
42421                    $prepend = "\\$prepend";
42422                }
42423                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
42424            } else {
42425                $path = $prepend . $path;
42426            }
42427        }
42428        return $path;
42429    }
42430
42431    /**
42432     * @param string
42433     * @param integer
42434     */
42435    function pushError($errmsg, $code = -1)
42436    {
42437        array_push($this->_errorStack, array($errmsg, $code));
42438    }
42439
42440    function getErrorMsgs()
42441    {
42442        $msgs = array();
42443        $errs = $this->_errorStack;
42444        foreach ($errs as $err) {
42445            $msgs[] = $err[0];
42446        }
42447        $this->_errorStack = array();
42448        return $msgs;
42449    }
42450
42451    /**
42452     * for BC
42453     *
42454     * @deprecated
42455     */
42456    function sortPkgDeps(&$packages, $uninstall = false)
42457    {
42458        $uninstall ?
42459            $this->sortPackagesForUninstall($packages) :
42460            $this->sortPackagesForInstall($packages);
42461    }
42462
42463    /**
42464     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
42465     *
42466     * This uses the topological sort method from graph theory, and the
42467     * Structures_Graph package to properly sort dependencies for installation.
42468     * @param array an array of downloaded PEAR_Downloader_Packages
42469     * @return array array of array(packagefilename, package.xml contents)
42470     */
42471    function sortPackagesForInstall(&$packages)
42472    {
42473        require_once 'Structures/Graph.php';
42474        require_once 'Structures/Graph/Node.php';
42475        require_once 'Structures/Graph/Manipulator/TopologicalSorter.php';
42476        $depgraph = new Structures_Graph(true);
42477        $nodes = array();
42478        $reg = &$this->config->getRegistry();
42479        foreach ($packages as $i => $package) {
42480            $pname = $reg->parsedPackageNameToString(
42481                array(
42482                    'channel' => $package->getChannel(),
42483                    'package' => strtolower($package->getPackage()),
42484                ));
42485            $nodes[$pname] = new Structures_Graph_Node;
42486            $nodes[$pname]->setData($packages[$i]);
42487            $depgraph->addNode($nodes[$pname]);
42488        }
42489
42490        $deplinks = array();
42491        foreach ($nodes as $package => $node) {
42492            $pf = &$node->getData();
42493            $pdeps = $pf->getDeps(true);
42494            if (!$pdeps) {
42495                continue;
42496            }
42497
42498            if ($pf->getPackagexmlVersion() == '1.0') {
42499                foreach ($pdeps as $dep) {
42500                    if ($dep['type'] != 'pkg' ||
42501                          (isset($dep['optional']) && $dep['optional'] == 'yes')) {
42502                        continue;
42503                    }
42504
42505                    $dname = $reg->parsedPackageNameToString(
42506                          array(
42507                              'channel' => 'pear.php.net',
42508                              'package' => strtolower($dep['name']),
42509                          ));
42510
42511                    if (isset($nodes[$dname])) {
42512                        if (!isset($deplinks[$dname])) {
42513                            $deplinks[$dname] = array();
42514                        }
42515
42516                        $deplinks[$dname][$package] = 1;
42517                        // dependency is in installed packages
42518                        continue;
42519                    }
42520
42521                    $dname = $reg->parsedPackageNameToString(
42522                          array(
42523                              'channel' => 'pecl.php.net',
42524                              'package' => strtolower($dep['name']),
42525                          ));
42526
42527                    if (isset($nodes[$dname])) {
42528                        if (!isset($deplinks[$dname])) {
42529                            $deplinks[$dname] = array();
42530                        }
42531
42532                        $deplinks[$dname][$package] = 1;
42533                        // dependency is in installed packages
42534                        continue;
42535                    }
42536                }
42537            } else {
42538                // the only ordering we care about is:
42539                // 1) subpackages must be installed before packages that depend on them
42540                // 2) required deps must be installed before packages that depend on them
42541                if (isset($pdeps['required']['subpackage'])) {
42542                    $t = $pdeps['required']['subpackage'];
42543                    if (!isset($t[0])) {
42544                        $t = array($t);
42545                    }
42546
42547                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
42548                }
42549
42550                if (isset($pdeps['group'])) {
42551                    if (!isset($pdeps['group'][0])) {
42552                        $pdeps['group'] = array($pdeps['group']);
42553                    }
42554
42555                    foreach ($pdeps['group'] as $group) {
42556                        if (isset($group['subpackage'])) {
42557                            $t = $group['subpackage'];
42558                            if (!isset($t[0])) {
42559                                $t = array($t);
42560                            }
42561
42562                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
42563                        }
42564                    }
42565                }
42566
42567                if (isset($pdeps['optional']['subpackage'])) {
42568                    $t = $pdeps['optional']['subpackage'];
42569                    if (!isset($t[0])) {
42570                        $t = array($t);
42571                    }
42572
42573                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
42574                }
42575
42576                if (isset($pdeps['required']['package'])) {
42577                    $t = $pdeps['required']['package'];
42578                    if (!isset($t[0])) {
42579                        $t = array($t);
42580                    }
42581
42582                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
42583                }
42584
42585                if (isset($pdeps['group'])) {
42586                    if (!isset($pdeps['group'][0])) {
42587                        $pdeps['group'] = array($pdeps['group']);
42588                    }
42589
42590                    foreach ($pdeps['group'] as $group) {
42591                        if (isset($group['package'])) {
42592                            $t = $group['package'];
42593                            if (!isset($t[0])) {
42594                                $t = array($t);
42595                            }
42596
42597                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
42598                        }
42599                    }
42600                }
42601            }
42602        }
42603
42604        $this->_detectDepCycle($deplinks);
42605        foreach ($deplinks as $dependent => $parents) {
42606            foreach ($parents as $parent => $unused) {
42607                $nodes[$dependent]->connectTo($nodes[$parent]);
42608            }
42609        }
42610
42611        $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph);
42612        $ret = array();
42613        for ($i = 0, $count = count($installOrder); $i < $count; $i++) {
42614            foreach ($installOrder[$i] as $index => $sortedpackage) {
42615                $data = &$installOrder[$i][$index]->getData();
42616                $ret[] = &$nodes[$reg->parsedPackageNameToString(
42617                          array(
42618                              'channel' => $data->getChannel(),
42619                              'package' => strtolower($data->getPackage()),
42620                          ))]->getData();
42621            }
42622        }
42623
42624        $packages = $ret;
42625        return;
42626    }
42627
42628    /**
42629     * Detect recursive links between dependencies and break the cycles
42630     *
42631     * @param array
42632     * @access private
42633     */
42634    function _detectDepCycle(&$deplinks)
42635    {
42636        do {
42637            $keepgoing = false;
42638            foreach ($deplinks as $dep => $parents) {
42639                foreach ($parents as $parent => $unused) {
42640                    // reset the parent cycle detector
42641                    $this->_testCycle(null, null, null);
42642                    if ($this->_testCycle($dep, $deplinks, $parent)) {
42643                        $keepgoing = true;
42644                        unset($deplinks[$dep][$parent]);
42645                        if (count($deplinks[$dep]) == 0) {
42646                            unset($deplinks[$dep]);
42647                        }
42648
42649                        continue 3;
42650                    }
42651                }
42652            }
42653        } while ($keepgoing);
42654    }
42655
42656    function _testCycle($test, $deplinks, $dep)
42657    {
42658        static $visited = array();
42659        if ($test === null) {
42660            $visited = array();
42661            return;
42662        }
42663
42664        // this happens when a parent has a dep cycle on another dependency
42665        // but the child is not part of the cycle
42666        if (isset($visited[$dep])) {
42667            return false;
42668        }
42669
42670        $visited[$dep] = 1;
42671        if ($test == $dep) {
42672            return true;
42673        }
42674
42675        if (isset($deplinks[$dep])) {
42676            if (in_array($test, array_keys($deplinks[$dep]), true)) {
42677                return true;
42678            }
42679
42680            foreach ($deplinks[$dep] as $parent => $unused) {
42681                if ($this->_testCycle($test, $deplinks, $parent)) {
42682                    return true;
42683                }
42684            }
42685        }
42686
42687        return false;
42688    }
42689
42690    /**
42691     * Set up the dependency for installation parsing
42692     *
42693     * @param array $t dependency information
42694     * @param PEAR_Registry $reg
42695     * @param array $deplinks list of dependency links already established
42696     * @param array $nodes all existing package nodes
42697     * @param string $package parent package name
42698     * @access private
42699     */
42700    function _setupGraph($t, $reg, &$deplinks, &$nodes, $package)
42701    {
42702        foreach ($t as $dep) {
42703            $depchannel = !isset($dep['channel']) ? '__uri': $dep['channel'];
42704            $dname = $reg->parsedPackageNameToString(
42705                  array(
42706                      'channel' => $depchannel,
42707                      'package' => strtolower($dep['name']),
42708                  ));
42709
42710            if (isset($nodes[$dname])) {
42711                if (!isset($deplinks[$dname])) {
42712                    $deplinks[$dname] = array();
42713                }
42714                $deplinks[$dname][$package] = 1;
42715            }
42716        }
42717    }
42718
42719    function _dependsOn($a, $b)
42720    {
42721        return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), $b);
42722    }
42723
42724    function _checkDepTree($channel, $package, $b, $checked = array())
42725    {
42726        $checked[$channel][$package] = true;
42727        if (!isset($this->_depTree[$channel][$package])) {
42728            return false;
42729        }
42730
42731        if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())]
42732              [strtolower($b->getPackage())])) {
42733            return true;
42734        }
42735
42736        foreach ($this->_depTree[$channel][$package] as $ch => $packages) {
42737            foreach ($packages as $pa => $true) {
42738                if ($this->_checkDepTree($ch, $pa, $b, $checked)) {
42739                    return true;
42740                }
42741            }
42742        }
42743
42744        return false;
42745    }
42746
42747    function _sortInstall($a, $b)
42748    {
42749        if (!$a->getDeps() && !$b->getDeps()) {
42750            return 0; // neither package has dependencies, order is insignificant
42751        }
42752        if ($a->getDeps() && !$b->getDeps()) {
42753            return 1; // $a must be installed after $b because $a has dependencies
42754        }
42755        if (!$a->getDeps() && $b->getDeps()) {
42756            return -1; // $b must be installed after $a because $b has dependencies
42757        }
42758        // both packages have dependencies
42759        if ($this->_dependsOn($a, $b)) {
42760            return 1;
42761        }
42762        if ($this->_dependsOn($b, $a)) {
42763            return -1;
42764        }
42765        return 0;
42766    }
42767
42768    /**
42769     * Download a file through HTTP.  Considers suggested file name in
42770     * Content-disposition: header and can run a callback function for
42771     * different events.  The callback will be called with two
42772     * parameters: the callback type, and parameters.  The implemented
42773     * callback types are:
42774     *
42775     *  'setup'       called at the very beginning, parameter is a UI object
42776     *                that should be used for all output
42777     *  'message'     the parameter is a string with an informational message
42778     *  'saveas'      may be used to save with a different file name, the
42779     *                parameter is the filename that is about to be used.
42780     *                If a 'saveas' callback returns a non-empty string,
42781     *                that file name will be used as the filename instead.
42782     *                Note that $save_dir will not be affected by this, only
42783     *                the basename of the file.
42784     *  'start'       download is starting, parameter is number of bytes
42785     *                that are expected, or -1 if unknown
42786     *  'bytesread'   parameter is the number of bytes read so far
42787     *  'done'        download is complete, parameter is the total number
42788     *                of bytes read
42789     *  'connfailed'  if the TCP/SSL connection fails, this callback is called
42790     *                with array(host,port,errno,errmsg)
42791     *  'writefailed' if writing to disk fails, this callback is called
42792     *                with array(destfile,errmsg)
42793     *
42794     * If an HTTP proxy has been configured (http_proxy PEAR_Config
42795     * setting), the proxy will be used.
42796     *
42797     * @param string  $url       the URL to download
42798     * @param object  $ui        PEAR_Frontend_* instance
42799     * @param object  $config    PEAR_Config instance
42800     * @param string  $save_dir  directory to save file in
42801     * @param mixed   $callback  function/method to call for status
42802     *                           updates
42803     * @param false|string|array $lastmodified header values to check against for caching
42804     *                           use false to return the header values from this download
42805     * @param false|array $accept Accept headers to send
42806     * @param false|string $channel Channel to use for retrieving authentication
42807     * @return string|array  Returns the full path of the downloaded file or a PEAR
42808     *                       error on failure.  If the error is caused by
42809     *                       socket-related errors, the error object will
42810     *                       have the fsockopen error code available through
42811     *                       getCode().  If caching is requested, then return the header
42812     *                       values.
42813     *
42814     * @access public
42815     */
42816    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null,
42817                          $accept = false, $channel = false)
42818    {
42819        static $redirect = 0;
42820        // always reset , so we are clean case of error
42821        $wasredirect = $redirect;
42822        $redirect = 0;
42823        if ($callback) {
42824            call_user_func($callback, 'setup', array(&$ui));
42825        }
42826
42827        $info = parse_url($url);
42828        if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
42829            return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
42830        }
42831
42832        if (!isset($info['host'])) {
42833            return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
42834        }
42835
42836        $host = isset($info['host']) ? $info['host'] : null;
42837        $port = isset($info['port']) ? $info['port'] : null;
42838        $path = isset($info['path']) ? $info['path'] : null;
42839
42840        if (isset($this)) {
42841            $config = &$this->config;
42842        } else {
42843            $config = &PEAR_Config::singleton();
42844        }
42845
42846        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
42847        if ($config->get('http_proxy') &&
42848              $proxy = parse_url($config->get('http_proxy'))) {
42849            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
42850            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
42851                $proxy_host = 'ssl://' . $proxy_host;
42852            }
42853            $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
42854            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
42855            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
42856
42857            if ($callback) {
42858                call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
42859            }
42860        }
42861
42862        if (empty($port)) {
42863            $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80;
42864        }
42865
42866        $scheme = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http';
42867
42868        if ($proxy_host != '') {
42869            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr);
42870            if (!$fp) {
42871                if ($callback) {
42872                    call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port,
42873                                                                  $errno, $errstr));
42874                }
42875                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno);
42876            }
42877
42878            if ($lastmodified === false || $lastmodified) {
42879                $request  = "GET $url HTTP/1.1\r\n";
42880                $request .= "Host: $host\r\n";
42881            } else {
42882                $request  = "GET $url HTTP/1.0\r\n";
42883                $request .= "Host: $host\r\n";
42884            }
42885        } else {
42886            $network_host = $host;
42887            if (isset($info['scheme']) && $info['scheme'] == 'https') {
42888                $network_host = 'ssl://' . $host;
42889            }
42890
42891            $fp = @fsockopen($network_host, $port, $errno, $errstr);
42892            if (!$fp) {
42893                if ($callback) {
42894                    call_user_func($callback, 'connfailed', array($host, $port,
42895                                                                  $errno, $errstr));
42896                }
42897                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
42898            }
42899
42900            if ($lastmodified === false || $lastmodified) {
42901                $request = "GET $path HTTP/1.1\r\n";
42902                $request .= "Host: $host\r\n";
42903            } else {
42904                $request = "GET $path HTTP/1.0\r\n";
42905                $request .= "Host: $host\r\n";
42906            }
42907        }
42908
42909        $ifmodifiedsince = '';
42910        if (is_array($lastmodified)) {
42911            if (isset($lastmodified['Last-Modified'])) {
42912                $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
42913            }
42914
42915            if (isset($lastmodified['ETag'])) {
42916                $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
42917            }
42918        } else {
42919            $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
42920        }
42921
42922        $request .= $ifmodifiedsince .
42923            "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n";
42924
42925        if (isset($this)) { // only pass in authentication for non-static calls
42926            $username = $config->get('username', null, $channel);
42927            $password = $config->get('password', null, $channel);
42928            if ($username && $password) {
42929                $tmp = base64_encode("$username:$password");
42930                $request .= "Authorization: Basic $tmp\r\n";
42931            }
42932        }
42933
42934        if ($proxy_host != '' && $proxy_user != '') {
42935            $request .= 'Proxy-Authorization: Basic ' .
42936                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
42937        }
42938
42939        if ($accept) {
42940            $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
42941        }
42942
42943        $request .= "Connection: close\r\n";
42944        $request .= "\r\n";
42945        fwrite($fp, $request);
42946        $headers = array();
42947        $reply = 0;
42948        while (trim($line = fgets($fp, 1024))) {
42949            if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
42950                $headers[strtolower($matches[1])] = trim($matches[2]);
42951            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
42952                $reply = (int)$matches[1];
42953                if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
42954                    return false;
42955                }
42956
42957                if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) {
42958                    return PEAR::raiseError("File $scheme://$host:$port$path not valid (received: $line)");
42959                }
42960            }
42961        }
42962
42963        if ($reply != 200) {
42964            if (!isset($headers['location'])) {
42965                return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirected but no location)");
42966            }
42967
42968            if ($wasredirect > 4) {
42969                return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirection looped more than 5 times)");
42970            }
42971
42972            $redirect = $wasredirect + 1;
42973            return $this->downloadHttp($headers['location'],
42974                    $ui, $save_dir, $callback, $lastmodified, $accept);
42975        }
42976
42977        if (isset($headers['content-disposition']) &&
42978            preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) {
42979            $save_as = basename($matches[1]);
42980        } else {
42981            $save_as = basename($url);
42982        }
42983
42984        if ($callback) {
42985            $tmp = call_user_func($callback, 'saveas', $save_as);
42986            if ($tmp) {
42987                $save_as = $tmp;
42988            }
42989        }
42990
42991        $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
42992        if (is_link($dest_file)) {
42993            return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $dest_file . ' as it is symlinked to ' . readlink($dest_file) . ' - Possible symlink attack');
42994        }
42995
42996        if (!$wp = @fopen($dest_file, 'wb')) {
42997            fclose($fp);
42998            if ($callback) {
42999                call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
43000            }
43001            return PEAR::raiseError("could not open $dest_file for writing");
43002        }
43003
43004        $length = isset($headers['content-length']) ? $headers['content-length'] : -1;
43005
43006        $bytes = 0;
43007        if ($callback) {
43008            call_user_func($callback, 'start', array(basename($dest_file), $length));
43009        }
43010
43011        while ($data = fread($fp, 1024)) {
43012            $bytes += strlen($data);
43013            if ($callback) {
43014                call_user_func($callback, 'bytesread', $bytes);
43015            }
43016            if (!@fwrite($wp, $data)) {
43017                fclose($fp);
43018                if ($callback) {
43019                    call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
43020                }
43021                return PEAR::raiseError("$dest_file: write failed ($php_errormsg)");
43022            }
43023        }
43024
43025        fclose($fp);
43026        fclose($wp);
43027        if ($callback) {
43028            call_user_func($callback, 'done', $bytes);
43029        }
43030
43031        if ($lastmodified === false || $lastmodified) {
43032            if (isset($headers['etag'])) {
43033                $lastmodified = array('ETag' => $headers['etag']);
43034            }
43035
43036            if (isset($headers['last-modified'])) {
43037                if (is_array($lastmodified)) {
43038                    $lastmodified['Last-Modified'] = $headers['last-modified'];
43039                } else {
43040                    $lastmodified = $headers['last-modified'];
43041                }
43042            }
43043            return array($dest_file, $lastmodified, $headers);
43044        }
43045        return $dest_file;
43046    }
43047}PEAR-1.9.4/PEAR/ErrorStack.php0000644000076500000240000010225111605156614014554 0ustar  helgistaff<?php
43048/**
43049 * Error Stack Implementation
43050 * 
43051 * This is an incredibly simple implementation of a very complex error handling
43052 * facility.  It contains the ability
43053 * to track multiple errors from multiple packages simultaneously.  In addition,
43054 * it can track errors of many levels, save data along with the error, context
43055 * information such as the exact file, line number, class and function that
43056 * generated the error, and if necessary, it can raise a traditional PEAR_Error.
43057 * It has built-in support for PEAR::Log, to log errors as they occur
43058 * 
43059 * Since version 0.2alpha, it is also possible to selectively ignore errors,
43060 * through the use of an error callback, see {@link pushCallback()}
43061 * 
43062 * Since version 0.3alpha, it is possible to specify the exception class
43063 * returned from {@link push()}
43064 *
43065 * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class.  This can
43066 * still be done quite handily in an error callback or by manipulating the returned array
43067 * @category   Debugging
43068 * @package    PEAR_ErrorStack
43069 * @author     Greg Beaver <cellog@php.net>
43070 * @copyright  2004-2008 Greg Beaver
43071 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
43072 * @version    CVS: $Id: ErrorStack.php 313023 2011-07-06 19:17:11Z dufuz $
43073 * @link       http://pear.php.net/package/PEAR_ErrorStack
43074 */
43075
43076/**
43077 * Singleton storage
43078 * 
43079 * Format:
43080 * <pre>
43081 * array(
43082 *  'package1' => PEAR_ErrorStack object,
43083 *  'package2' => PEAR_ErrorStack object,
43084 *  ...
43085 * )
43086 * </pre>
43087 * @access private
43088 * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
43089 */
43090$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
43091
43092/**
43093 * Global error callback (default)
43094 * 
43095 * This is only used if set to non-false.  * is the default callback for
43096 * all packages, whereas specific packages may set a default callback
43097 * for all instances, regardless of whether they are a singleton or not.
43098 *
43099 * To exclude non-singletons, only set the local callback for the singleton
43100 * @see PEAR_ErrorStack::setDefaultCallback()
43101 * @access private
43102 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
43103 */
43104$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
43105    '*' => false,
43106);
43107
43108/**
43109 * Global Log object (default)
43110 * 
43111 * This is only used if set to non-false.  Use to set a default log object for
43112 * all stacks, regardless of instantiation order or location
43113 * @see PEAR_ErrorStack::setDefaultLogger()
43114 * @access private
43115 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
43116 */
43117$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;
43118
43119/**
43120 * Global Overriding Callback
43121 * 
43122 * This callback will override any error callbacks that specific loggers have set.
43123 * Use with EXTREME caution
43124 * @see PEAR_ErrorStack::staticPushCallback()
43125 * @access private
43126 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
43127 */
43128$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
43129
43130/**#@+
43131 * One of four possible return values from the error Callback
43132 * @see PEAR_ErrorStack::_errorCallback()
43133 */
43134/**
43135 * If this is returned, then the error will be both pushed onto the stack
43136 * and logged.
43137 */
43138define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
43139/**
43140 * If this is returned, then the error will only be pushed onto the stack,
43141 * and not logged.
43142 */
43143define('PEAR_ERRORSTACK_PUSH', 2);
43144/**
43145 * If this is returned, then the error will only be logged, but not pushed
43146 * onto the error stack.
43147 */
43148define('PEAR_ERRORSTACK_LOG', 3);
43149/**
43150 * If this is returned, then the error is completely ignored.
43151 */
43152define('PEAR_ERRORSTACK_IGNORE', 4);
43153/**
43154 * If this is returned, then the error is logged and die() is called.
43155 */
43156define('PEAR_ERRORSTACK_DIE', 5);
43157/**#@-*/
43158
43159/**
43160 * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
43161 * the singleton method.
43162 */
43163define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);
43164
43165/**
43166 * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
43167 * that has no __toString() method
43168 */
43169define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
43170/**
43171 * Error Stack Implementation
43172 *
43173 * Usage:
43174 * <code>
43175 * // global error stack
43176 * $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
43177 * // local error stack
43178 * $local_stack = new PEAR_ErrorStack('MyPackage');
43179 * </code>
43180 * @author     Greg Beaver <cellog@php.net>
43181 * @version    1.9.4
43182 * @package    PEAR_ErrorStack
43183 * @category   Debugging
43184 * @copyright  2004-2008 Greg Beaver
43185 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
43186 * @version    CVS: $Id: ErrorStack.php 313023 2011-07-06 19:17:11Z dufuz $
43187 * @link       http://pear.php.net/package/PEAR_ErrorStack
43188 */
43189class PEAR_ErrorStack {
43190    /**
43191     * Errors are stored in the order that they are pushed on the stack.
43192     * @since 0.4alpha Errors are no longer organized by error level.
43193     * This renders pop() nearly unusable, and levels could be more easily
43194     * handled in a callback anyway
43195     * @var array
43196     * @access private
43197     */
43198    var $_errors = array();
43199
43200    /**
43201     * Storage of errors by level.
43202     *
43203     * Allows easy retrieval and deletion of only errors from a particular level
43204     * @since PEAR 1.4.0dev
43205     * @var array
43206     * @access private
43207     */
43208    var $_errorsByLevel = array();
43209
43210    /**
43211     * Package name this error stack represents
43212     * @var string
43213     * @access protected
43214     */
43215    var $_package;
43216    
43217    /**
43218     * Determines whether a PEAR_Error is thrown upon every error addition
43219     * @var boolean
43220     * @access private
43221     */
43222    var $_compat = false;
43223    
43224    /**
43225     * If set to a valid callback, this will be used to generate the error
43226     * message from the error code, otherwise the message passed in will be
43227     * used
43228     * @var false|string|array
43229     * @access private
43230     */
43231    var $_msgCallback = false;
43232    
43233    /**
43234     * If set to a valid callback, this will be used to generate the error
43235     * context for an error.  For PHP-related errors, this will be a file
43236     * and line number as retrieved from debug_backtrace(), but can be
43237     * customized for other purposes.  The error might actually be in a separate
43238     * configuration file, or in a database query.
43239     * @var false|string|array
43240     * @access protected
43241     */
43242    var $_contextCallback = false;
43243    
43244    /**
43245     * If set to a valid callback, this will be called every time an error
43246     * is pushed onto the stack.  The return value will be used to determine
43247     * whether to allow an error to be pushed or logged.
43248     * 
43249     * The return value must be one an PEAR_ERRORSTACK_* constant
43250     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
43251     * @var false|string|array
43252     * @access protected
43253     */
43254    var $_errorCallback = array();
43255    
43256    /**
43257     * PEAR::Log object for logging errors
43258     * @var false|Log
43259     * @access protected
43260     */
43261    var $_logger = false;
43262    
43263    /**
43264     * Error messages - designed to be overridden
43265     * @var array
43266     * @abstract
43267     */
43268    var $_errorMsgs = array();
43269    
43270    /**
43271     * Set up a new error stack
43272     * 
43273     * @param string   $package name of the package this error stack represents
43274     * @param callback $msgCallback callback used for error message generation
43275     * @param callback $contextCallback callback used for context generation,
43276     *                 defaults to {@link getFileLine()}
43277     * @param boolean  $throwPEAR_Error
43278     */
43279    function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false,
43280                         $throwPEAR_Error = false)
43281    {
43282        $this->_package = $package;
43283        $this->setMessageCallback($msgCallback);
43284        $this->setContextCallback($contextCallback);
43285        $this->_compat = $throwPEAR_Error;
43286    }
43287    
43288    /**
43289     * Return a single error stack for this package.
43290     * 
43291     * Note that all parameters are ignored if the stack for package $package
43292     * has already been instantiated
43293     * @param string   $package name of the package this error stack represents
43294     * @param callback $msgCallback callback used for error message generation
43295     * @param callback $contextCallback callback used for context generation,
43296     *                 defaults to {@link getFileLine()}
43297     * @param boolean  $throwPEAR_Error
43298     * @param string   $stackClass class to instantiate
43299     * @static
43300     * @return PEAR_ErrorStack
43301     */
43302    function &singleton($package, $msgCallback = false, $contextCallback = false,
43303                         $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack')
43304    {
43305        if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
43306            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
43307        }
43308        if (!class_exists($stackClass)) {
43309            if (function_exists('debug_backtrace')) {
43310                $trace = debug_backtrace();
43311            }
43312            PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
43313                'exception', array('stackclass' => $stackClass),
43314                'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
43315                false, $trace);
43316        }
43317        $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
43318            new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);
43319
43320        return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
43321    }
43322
43323    /**
43324     * Internal error handler for PEAR_ErrorStack class
43325     * 
43326     * Dies if the error is an exception (and would have died anyway)
43327     * @access private
43328     */
43329    function _handleError($err)
43330    {
43331        if ($err['level'] == 'exception') {
43332            $message = $err['message'];
43333            if (isset($_SERVER['REQUEST_URI'])) {
43334                echo '<br />';
43335            } else {
43336                echo "\n";
43337            }
43338            var_dump($err['context']);
43339            die($message);
43340        }
43341    }
43342    
43343    /**
43344     * Set up a PEAR::Log object for all error stacks that don't have one
43345     * @param Log $log 
43346     * @static
43347     */
43348    function setDefaultLogger(&$log)
43349    {
43350        if (is_object($log) && method_exists($log, 'log') ) {
43351            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
43352        } elseif (is_callable($log)) {
43353            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
43354	}
43355    }
43356    
43357    /**
43358     * Set up a PEAR::Log object for this error stack
43359     * @param Log $log 
43360     */
43361    function setLogger(&$log)
43362    {
43363        if (is_object($log) && method_exists($log, 'log') ) {
43364            $this->_logger = &$log;
43365        } elseif (is_callable($log)) {
43366            $this->_logger = &$log;
43367        }
43368    }
43369    
43370    /**
43371     * Set an error code => error message mapping callback
43372     * 
43373     * This method sets the callback that can be used to generate error
43374     * messages for any instance
43375     * @param array|string Callback function/method
43376     */
43377    function setMessageCallback($msgCallback)
43378    {
43379        if (!$msgCallback) {
43380            $this->_msgCallback = array(&$this, 'getErrorMessage');
43381        } else {
43382            if (is_callable($msgCallback)) {
43383                $this->_msgCallback = $msgCallback;
43384            }
43385        }
43386    }
43387    
43388    /**
43389     * Get an error code => error message mapping callback
43390     * 
43391     * This method returns the current callback that can be used to generate error
43392     * messages
43393     * @return array|string|false Callback function/method or false if none
43394     */
43395    function getMessageCallback()
43396    {
43397        return $this->_msgCallback;
43398    }
43399    
43400    /**
43401     * Sets a default callback to be used by all error stacks
43402     * 
43403     * This method sets the callback that can be used to generate error
43404     * messages for a singleton
43405     * @param array|string Callback function/method
43406     * @param string Package name, or false for all packages
43407     * @static
43408     */
43409    function setDefaultCallback($callback = false, $package = false)
43410    {
43411        if (!is_callable($callback)) {
43412            $callback = false;
43413        }
43414        $package = $package ? $package : '*';
43415        $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
43416    }
43417    
43418    /**
43419     * Set a callback that generates context information (location of error) for an error stack
43420     * 
43421     * This method sets the callback that can be used to generate context
43422     * information for an error.  Passing in NULL will disable context generation
43423     * and remove the expensive call to debug_backtrace()
43424     * @param array|string|null Callback function/method
43425     */
43426    function setContextCallback($contextCallback)
43427    {
43428        if ($contextCallback === null) {
43429            return $this->_contextCallback = false;
43430        }
43431        if (!$contextCallback) {
43432            $this->_contextCallback = array(&$this, 'getFileLine');
43433        } else {
43434            if (is_callable($contextCallback)) {
43435                $this->_contextCallback = $contextCallback;
43436            }
43437        }
43438    }
43439    
43440    /**
43441     * Set an error Callback
43442     * If set to a valid callback, this will be called every time an error
43443     * is pushed onto the stack.  The return value will be used to determine
43444     * whether to allow an error to be pushed or logged.
43445     * 
43446     * The return value must be one of the ERRORSTACK_* constants.
43447     * 
43448     * This functionality can be used to emulate PEAR's pushErrorHandling, and
43449     * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
43450     * the error stack or logging
43451     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
43452     * @see popCallback()
43453     * @param string|array $cb
43454     */
43455    function pushCallback($cb)
43456    {
43457        array_push($this->_errorCallback, $cb);
43458    }
43459    
43460    /**
43461     * Remove a callback from the error callback stack
43462     * @see pushCallback()
43463     * @return array|string|false
43464     */
43465    function popCallback()
43466    {
43467        if (!count($this->_errorCallback)) {
43468            return false;
43469        }
43470        return array_pop($this->_errorCallback);
43471    }
43472    
43473    /**
43474     * Set a temporary overriding error callback for every package error stack
43475     *
43476     * Use this to temporarily disable all existing callbacks (can be used
43477     * to emulate the @ operator, for instance)
43478     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
43479     * @see staticPopCallback(), pushCallback()
43480     * @param string|array $cb
43481     * @static
43482     */
43483    function staticPushCallback($cb)
43484    {
43485        array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
43486    }
43487    
43488    /**
43489     * Remove a temporary overriding error callback
43490     * @see staticPushCallback()
43491     * @return array|string|false
43492     * @static
43493     */
43494    function staticPopCallback()
43495    {
43496        $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
43497        if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
43498            $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
43499        }
43500        return $ret;
43501    }
43502    
43503    /**
43504     * Add an error to the stack
43505     * 
43506     * If the message generator exists, it is called with 2 parameters.
43507     *  - the current Error Stack object
43508     *  - an array that is in the same format as an error.  Available indices
43509     *    are 'code', 'package', 'time', 'params', 'level', and 'context'
43510     * 
43511     * Next, if the error should contain context information, this is
43512     * handled by the context grabbing method.
43513     * Finally, the error is pushed onto the proper error stack
43514     * @param int    $code      Package-specific error code
43515     * @param string $level     Error level.  This is NOT spell-checked
43516     * @param array  $params    associative array of error parameters
43517     * @param string $msg       Error message, or a portion of it if the message
43518     *                          is to be generated
43519     * @param array  $repackage If this error re-packages an error pushed by
43520     *                          another package, place the array returned from
43521     *                          {@link pop()} in this parameter
43522     * @param array  $backtrace Protected parameter: use this to pass in the
43523     *                          {@link debug_backtrace()} that should be used
43524     *                          to find error context
43525     * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
43526     * thrown.  If a PEAR_Error is returned, the userinfo
43527     * property is set to the following array:
43528     * 
43529     * <code>
43530     * array(
43531     *    'code' => $code,
43532     *    'params' => $params,
43533     *    'package' => $this->_package,
43534     *    'level' => $level,
43535     *    'time' => time(),
43536     *    'context' => $context,
43537     *    'message' => $msg,
43538     * //['repackage' => $err] repackaged error array/Exception class
43539     * );
43540     * </code>
43541     * 
43542     * Normally, the previous array is returned.
43543     */
43544    function push($code, $level = 'error', $params = array(), $msg = false,
43545                  $repackage = false, $backtrace = false)
43546    {
43547        $context = false;
43548        // grab error context
43549        if ($this->_contextCallback) {
43550            if (!$backtrace) {
43551                $backtrace = debug_backtrace();
43552            }
43553            $context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
43554        }
43555        
43556        // save error
43557        $time = explode(' ', microtime());
43558        $time = $time[1] + $time[0];
43559        $err = array(
43560                'code' => $code,
43561                'params' => $params,
43562                'package' => $this->_package,
43563                'level' => $level,
43564                'time' => $time,
43565                'context' => $context,
43566                'message' => $msg,
43567               );
43568
43569        if ($repackage) {
43570            $err['repackage'] = $repackage;
43571        }
43572
43573        // set up the error message, if necessary
43574        if ($this->_msgCallback) {
43575            $msg = call_user_func_array($this->_msgCallback,
43576                                        array(&$this, $err));
43577            $err['message'] = $msg;
43578        }        
43579        $push = $log = true;
43580        $die = false;
43581        // try the overriding callback first
43582        $callback = $this->staticPopCallback();
43583        if ($callback) {
43584            $this->staticPushCallback($callback);
43585        }
43586        if (!is_callable($callback)) {
43587            // try the local callback next
43588            $callback = $this->popCallback();
43589            if (is_callable($callback)) {
43590                $this->pushCallback($callback);
43591            } else {
43592                // try the default callback
43593                $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
43594                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
43595                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
43596            }
43597        }
43598        if (is_callable($callback)) {
43599            switch(call_user_func($callback, $err)){
43600            	case PEAR_ERRORSTACK_IGNORE: 
43601            		return $err;
43602        		break;
43603            	case PEAR_ERRORSTACK_PUSH: 
43604            		$log = false;
43605        		break;
43606            	case PEAR_ERRORSTACK_LOG: 
43607            		$push = false;
43608        		break;
43609            	case PEAR_ERRORSTACK_DIE: 
43610            		$die = true;
43611        		break;
43612                // anything else returned has the same effect as pushandlog
43613            }
43614        }
43615        if ($push) {
43616            array_unshift($this->_errors, $err);
43617            if (!isset($this->_errorsByLevel[$err['level']])) {
43618                $this->_errorsByLevel[$err['level']] = array();
43619            }
43620            $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
43621        }
43622        if ($log) {
43623            if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
43624                $this->_log($err);
43625            }
43626        }
43627        if ($die) {
43628            die();
43629        }
43630        if ($this->_compat && $push) {
43631            return $this->raiseError($msg, $code, null, null, $err);
43632        }
43633        return $err;
43634    }
43635    
43636    /**
43637     * Static version of {@link push()}
43638     * 
43639     * @param string $package   Package name this error belongs to
43640     * @param int    $code      Package-specific error code
43641     * @param string $level     Error level.  This is NOT spell-checked
43642     * @param array  $params    associative array of error parameters
43643     * @param string $msg       Error message, or a portion of it if the message
43644     *                          is to be generated
43645     * @param array  $repackage If this error re-packages an error pushed by
43646     *                          another package, place the array returned from
43647     *                          {@link pop()} in this parameter
43648     * @param array  $backtrace Protected parameter: use this to pass in the
43649     *                          {@link debug_backtrace()} that should be used
43650     *                          to find error context
43651     * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
43652     *                          thrown.  see docs for {@link push()}
43653     * @static
43654     */
43655    function staticPush($package, $code, $level = 'error', $params = array(),
43656                        $msg = false, $repackage = false, $backtrace = false)
43657    {
43658        $s = &PEAR_ErrorStack::singleton($package);
43659        if ($s->_contextCallback) {
43660            if (!$backtrace) {
43661                if (function_exists('debug_backtrace')) {
43662                    $backtrace = debug_backtrace();
43663                }
43664            }
43665        }
43666        return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
43667    }
43668    
43669    /**
43670     * Log an error using PEAR::Log
43671     * @param array $err Error array
43672     * @param array $levels Error level => Log constant map
43673     * @access protected
43674     */
43675    function _log($err)
43676    {
43677        if ($this->_logger) {
43678            $logger = &$this->_logger;
43679        } else {
43680            $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
43681        }
43682        if (is_a($logger, 'Log')) {
43683            $levels = array(
43684                'exception' => PEAR_LOG_CRIT,
43685                'alert' => PEAR_LOG_ALERT,
43686                'critical' => PEAR_LOG_CRIT,
43687                'error' => PEAR_LOG_ERR,
43688                'warning' => PEAR_LOG_WARNING,
43689                'notice' => PEAR_LOG_NOTICE,
43690                'info' => PEAR_LOG_INFO,
43691                'debug' => PEAR_LOG_DEBUG);
43692            if (isset($levels[$err['level']])) {
43693                $level = $levels[$err['level']];
43694            } else {
43695                $level = PEAR_LOG_INFO;
43696            }
43697            $logger->log($err['message'], $level, $err);
43698        } else { // support non-standard logs
43699            call_user_func($logger, $err);
43700        }
43701    }
43702
43703    
43704    /**
43705     * Pop an error off of the error stack
43706     * 
43707     * @return false|array
43708     * @since 0.4alpha it is no longer possible to specify a specific error
43709     * level to return - the last error pushed will be returned, instead
43710     */
43711    function pop()
43712    {
43713        $err = @array_shift($this->_errors);
43714        if (!is_null($err)) {
43715            @array_pop($this->_errorsByLevel[$err['level']]);
43716            if (!count($this->_errorsByLevel[$err['level']])) {
43717                unset($this->_errorsByLevel[$err['level']]);
43718            }
43719        }
43720        return $err;
43721    }
43722
43723    /**
43724     * Pop an error off of the error stack, static method
43725     *
43726     * @param string package name
43727     * @return boolean
43728     * @since PEAR1.5.0a1
43729     */
43730    function staticPop($package)
43731    {
43732        if ($package) {
43733            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
43734                return false;
43735            }
43736            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop();
43737        }
43738    }
43739
43740    /**
43741     * Determine whether there are any errors on the stack
43742     * @param string|array Level name.  Use to determine if any errors
43743     * of level (string), or levels (array) have been pushed
43744     * @return boolean
43745     */
43746    function hasErrors($level = false)
43747    {
43748        if ($level) {
43749            return isset($this->_errorsByLevel[$level]);
43750        }
43751        return count($this->_errors);
43752    }
43753    
43754    /**
43755     * Retrieve all errors since last purge
43756     * 
43757     * @param boolean set in order to empty the error stack
43758     * @param string level name, to return only errors of a particular severity
43759     * @return array
43760     */
43761    function getErrors($purge = false, $level = false)
43762    {
43763        if (!$purge) {
43764            if ($level) {
43765                if (!isset($this->_errorsByLevel[$level])) {
43766                    return array();
43767                } else {
43768                    return $this->_errorsByLevel[$level];
43769                }
43770            } else {
43771                return $this->_errors;
43772            }
43773        }
43774        if ($level) {
43775            $ret = $this->_errorsByLevel[$level];
43776            foreach ($this->_errorsByLevel[$level] as $i => $unused) {
43777                // entries are references to the $_errors array
43778                $this->_errorsByLevel[$level][$i] = false;
43779            }
43780            // array_filter removes all entries === false
43781            $this->_errors = array_filter($this->_errors);
43782            unset($this->_errorsByLevel[$level]);
43783            return $ret;
43784        }
43785        $ret = $this->_errors;
43786        $this->_errors = array();
43787        $this->_errorsByLevel = array();
43788        return $ret;
43789    }
43790    
43791    /**
43792     * Determine whether there are any errors on a single error stack, or on any error stack
43793     *
43794     * The optional parameter can be used to test the existence of any errors without the need of
43795     * singleton instantiation
43796     * @param string|false Package name to check for errors
43797     * @param string Level name to check for a particular severity
43798     * @return boolean
43799     * @static
43800     */
43801    function staticHasErrors($package = false, $level = false)
43802    {
43803        if ($package) {
43804            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
43805                return false;
43806            }
43807            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
43808        }
43809        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
43810            if ($obj->hasErrors($level)) {
43811                return true;
43812            }
43813        }
43814        return false;
43815    }
43816    
43817    /**
43818     * Get a list of all errors since last purge, organized by package
43819     * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
43820     * @param boolean $purge Set to purge the error stack of existing errors
43821     * @param string  $level Set to a level name in order to retrieve only errors of a particular level
43822     * @param boolean $merge Set to return a flat array, not organized by package
43823     * @param array   $sortfunc Function used to sort a merged array - default
43824     *        sorts by time, and should be good for most cases
43825     * @static
43826     * @return array 
43827     */
43828    function staticGetErrors($purge = false, $level = false, $merge = false,
43829                             $sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
43830    {
43831        $ret = array();
43832        if (!is_callable($sortfunc)) {
43833            $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
43834        }
43835        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
43836            $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
43837            if ($test) {
43838                if ($merge) {
43839                    $ret = array_merge($ret, $test);
43840                } else {
43841                    $ret[$package] = $test;
43842                }
43843            }
43844        }
43845        if ($merge) {
43846            usort($ret, $sortfunc);
43847        }
43848        return $ret;
43849    }
43850    
43851    /**
43852     * Error sorting function, sorts by time
43853     * @access private
43854     */
43855    function _sortErrors($a, $b)
43856    {
43857        if ($a['time'] == $b['time']) {
43858            return 0;
43859        }
43860        if ($a['time'] < $b['time']) {
43861            return 1;
43862        }
43863        return -1;
43864    }
43865
43866    /**
43867     * Standard file/line number/function/class context callback
43868     *
43869     * This function uses a backtrace generated from {@link debug_backtrace()}
43870     * and so will not work at all in PHP < 4.3.0.  The frame should
43871     * reference the frame that contains the source of the error.
43872     * @return array|false either array('file' => file, 'line' => line,
43873     *         'function' => function name, 'class' => class name) or
43874     *         if this doesn't work, then false
43875     * @param unused
43876     * @param integer backtrace frame.
43877     * @param array Results of debug_backtrace()
43878     * @static
43879     */
43880    function getFileLine($code, $params, $backtrace = null)
43881    {
43882        if ($backtrace === null) {
43883            return false;
43884        }
43885        $frame = 0;
43886        $functionframe = 1;
43887        if (!isset($backtrace[1])) {
43888            $functionframe = 0;
43889        } else {
43890            while (isset($backtrace[$functionframe]['function']) &&
43891                  $backtrace[$functionframe]['function'] == 'eval' &&
43892                  isset($backtrace[$functionframe + 1])) {
43893                $functionframe++;
43894            }
43895        }
43896        if (isset($backtrace[$frame])) {
43897            if (!isset($backtrace[$frame]['file'])) {
43898                $frame++;
43899            }
43900            $funcbacktrace = $backtrace[$functionframe];
43901            $filebacktrace = $backtrace[$frame];
43902            $ret = array('file' => $filebacktrace['file'],
43903                         'line' => $filebacktrace['line']);
43904            // rearrange for eval'd code or create function errors
43905            if (strpos($filebacktrace['file'], '(') && 
43906            	  preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'],
43907                  $matches)) {
43908                $ret['file'] = $matches[1];
43909                $ret['line'] = $matches[2] + 0;
43910            }
43911            if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
43912                if ($funcbacktrace['function'] != 'eval') {
43913                    if ($funcbacktrace['function'] == '__lambda_func') {
43914                        $ret['function'] = 'create_function() code';
43915                    } else {
43916                        $ret['function'] = $funcbacktrace['function'];
43917                    }
43918                }
43919            }
43920            if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
43921                $ret['class'] = $funcbacktrace['class'];
43922            }
43923            return $ret;
43924        }
43925        return false;
43926    }
43927    
43928    /**
43929     * Standard error message generation callback
43930     * 
43931     * This method may also be called by a custom error message generator
43932     * to fill in template values from the params array, simply
43933     * set the third parameter to the error message template string to use
43934     * 
43935     * The special variable %__msg% is reserved: use it only to specify
43936     * where a message passed in by the user should be placed in the template,
43937     * like so:
43938     * 
43939     * Error message: %msg% - internal error
43940     * 
43941     * If the message passed like so:
43942     * 
43943     * <code>
43944     * $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
43945     * </code>
43946     * 
43947     * The returned error message will be "Error message: server error 500 -
43948     * internal error"
43949     * @param PEAR_ErrorStack
43950     * @param array
43951     * @param string|false Pre-generated error message template
43952     * @static
43953     * @return string
43954     */
43955    function getErrorMessage(&$stack, $err, $template = false)
43956    {
43957        if ($template) {
43958            $mainmsg = $template;
43959        } else {
43960            $mainmsg = $stack->getErrorMessageTemplate($err['code']);
43961        }
43962        $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
43963        if (is_array($err['params']) && count($err['params'])) {
43964            foreach ($err['params'] as $name => $val) {
43965                if (is_array($val)) {
43966                    // @ is needed in case $val is a multi-dimensional array
43967                    $val = @implode(', ', $val);
43968                }
43969                if (is_object($val)) {
43970                    if (method_exists($val, '__toString')) {
43971                        $val = $val->__toString();
43972                    } else {
43973                        PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
43974                            'warning', array('obj' => get_class($val)),
43975                            'object %obj% passed into getErrorMessage, but has no __toString() method');
43976                        $val = 'Object';
43977                    }
43978                }
43979                $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
43980            }
43981        }
43982        return $mainmsg;
43983    }
43984    
43985    /**
43986     * Standard Error Message Template generator from code
43987     * @return string
43988     */
43989    function getErrorMessageTemplate($code)
43990    {
43991        if (!isset($this->_errorMsgs[$code])) {
43992            return '%__msg%';
43993        }
43994        return $this->_errorMsgs[$code];
43995    }
43996    
43997    /**
43998     * Set the Error Message Template array
43999     * 
44000     * The array format must be:
44001     * <pre>
44002     * array(error code => 'message template',...)
44003     * </pre>
44004     * 
44005     * Error message parameters passed into {@link push()} will be used as input
44006     * for the error message.  If the template is 'message %foo% was %bar%', and the
44007     * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
44008     * be 'message one was six'
44009     * @return string
44010     */
44011    function setErrorMessageTemplate($template)
44012    {
44013        $this->_errorMsgs = $template;
44014    }
44015    
44016    
44017    /**
44018     * emulate PEAR::raiseError()
44019     * 
44020     * @return PEAR_Error
44021     */
44022    function raiseError()
44023    {
44024        require_once 'PEAR.php';
44025        $args = func_get_args();
44026        return call_user_func_array(array('PEAR', 'raiseError'), $args);
44027    }
44028}
44029$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
44030$stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
44031?>
44032PEAR-1.9.4/PEAR/Exception.php0000644000076500000240000003326611605156614014444 0ustar  helgistaff<?php
44033/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
44034/**
44035 * PEAR_Exception
44036 *
44037 * PHP versions 4 and 5
44038 *
44039 * @category   pear
44040 * @package    PEAR
44041 * @author     Tomas V. V. Cox <cox@idecnet.com>
44042 * @author     Hans Lellelid <hans@velum.net>
44043 * @author     Bertrand Mansion <bmansion@mamasam.com>
44044 * @author     Greg Beaver <cellog@php.net>
44045 * @copyright  1997-2009 The Authors
44046 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
44047 * @version    CVS: $Id: Exception.php 313023 2011-07-06 19:17:11Z dufuz $
44048 * @link       http://pear.php.net/package/PEAR
44049 * @since      File available since Release 1.3.3
44050 */
44051
44052
44053/**
44054 * Base PEAR_Exception Class
44055 *
44056 * 1) Features:
44057 *
44058 * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
44059 * - Definable triggers, shot when exceptions occur
44060 * - Pretty and informative error messages
44061 * - Added more context info available (like class, method or cause)
44062 * - cause can be a PEAR_Exception or an array of mixed
44063 *   PEAR_Exceptions/PEAR_ErrorStack warnings
44064 * - callbacks for specific exception classes and their children
44065 *
44066 * 2) Ideas:
44067 *
44068 * - Maybe a way to define a 'template' for the output
44069 *
44070 * 3) Inherited properties from PHP Exception Class:
44071 *
44072 * protected $message
44073 * protected $code
44074 * protected $line
44075 * protected $file
44076 * private   $trace
44077 *
44078 * 4) Inherited methods from PHP Exception Class:
44079 *
44080 * __clone
44081 * __construct
44082 * getMessage
44083 * getCode
44084 * getFile
44085 * getLine
44086 * getTraceSafe
44087 * getTraceSafeAsString
44088 * __toString
44089 *
44090 * 5) Usage example
44091 *
44092 * <code>
44093 *  require_once 'PEAR/Exception.php';
44094 *
44095 *  class Test {
44096 *     function foo() {
44097 *         throw new PEAR_Exception('Error Message', ERROR_CODE);
44098 *     }
44099 *  }
44100 *
44101 *  function myLogger($pear_exception) {
44102 *     echo $pear_exception->getMessage();
44103 *  }
44104 *  // each time a exception is thrown the 'myLogger' will be called
44105 *  // (its use is completely optional)
44106 *  PEAR_Exception::addObserver('myLogger');
44107 *  $test = new Test;
44108 *  try {
44109 *     $test->foo();
44110 *  } catch (PEAR_Exception $e) {
44111 *     print $e;
44112 *  }
44113 * </code>
44114 *
44115 * @category   pear
44116 * @package    PEAR
44117 * @author     Tomas V.V.Cox <cox@idecnet.com>
44118 * @author     Hans Lellelid <hans@velum.net>
44119 * @author     Bertrand Mansion <bmansion@mamasam.com>
44120 * @author     Greg Beaver <cellog@php.net>
44121 * @copyright  1997-2009 The Authors
44122 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
44123 * @version    Release: 1.9.4
44124 * @link       http://pear.php.net/package/PEAR
44125 * @since      Class available since Release 1.3.3
44126 *
44127 */
44128class PEAR_Exception extends Exception
44129{
44130    const OBSERVER_PRINT = -2;
44131    const OBSERVER_TRIGGER = -4;
44132    const OBSERVER_DIE = -8;
44133    protected $cause;
44134    private static $_observers = array();
44135    private static $_uniqueid = 0;
44136    private $_trace;
44137
44138    /**
44139     * Supported signatures:
44140     *  - PEAR_Exception(string $message);
44141     *  - PEAR_Exception(string $message, int $code);
44142     *  - PEAR_Exception(string $message, Exception $cause);
44143     *  - PEAR_Exception(string $message, Exception $cause, int $code);
44144     *  - PEAR_Exception(string $message, PEAR_Error $cause);
44145     *  - PEAR_Exception(string $message, PEAR_Error $cause, int $code);
44146     *  - PEAR_Exception(string $message, array $causes);
44147     *  - PEAR_Exception(string $message, array $causes, int $code);
44148     * @param string exception message
44149     * @param int|Exception|PEAR_Error|array|null exception cause
44150     * @param int|null exception code or null
44151     */
44152    public function __construct($message, $p2 = null, $p3 = null)
44153    {
44154        if (is_int($p2)) {
44155            $code = $p2;
44156            $this->cause = null;
44157        } elseif (is_object($p2) || is_array($p2)) {
44158            // using is_object allows both Exception and PEAR_Error
44159            if (is_object($p2) && !($p2 instanceof Exception)) {
44160                if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
44161                    throw new PEAR_Exception('exception cause must be Exception, ' .
44162                        'array, or PEAR_Error');
44163                }
44164            }
44165            $code = $p3;
44166            if (is_array($p2) && isset($p2['message'])) {
44167                // fix potential problem of passing in a single warning
44168                $p2 = array($p2);
44169            }
44170            $this->cause = $p2;
44171        } else {
44172            $code = null;
44173            $this->cause = null;
44174        }
44175        parent::__construct($message, $code);
44176        $this->signal();
44177    }
44178
44179    /**
44180     * @param mixed $callback  - A valid php callback, see php func is_callable()
44181     *                         - A PEAR_Exception::OBSERVER_* constant
44182     *                         - An array(const PEAR_Exception::OBSERVER_*,
44183     *                           mixed $options)
44184     * @param string $label    The name of the observer. Use this if you want
44185     *                         to remove it later with removeObserver()
44186     */
44187    public static function addObserver($callback, $label = 'default')
44188    {
44189        self::$_observers[$label] = $callback;
44190    }
44191
44192    public static function removeObserver($label = 'default')
44193    {
44194        unset(self::$_observers[$label]);
44195    }
44196
44197    /**
44198     * @return int unique identifier for an observer
44199     */
44200    public static function getUniqueId()
44201    {
44202        return self::$_uniqueid++;
44203    }
44204
44205    private function signal()
44206    {
44207        foreach (self::$_observers as $func) {
44208            if (is_callable($func)) {
44209                call_user_func($func, $this);
44210                continue;
44211            }
44212            settype($func, 'array');
44213            switch ($func[0]) {
44214                case self::OBSERVER_PRINT :
44215                    $f = (isset($func[1])) ? $func[1] : '%s';
44216                    printf($f, $this->getMessage());
44217                    break;
44218                case self::OBSERVER_TRIGGER :
44219                    $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
44220                    trigger_error($this->getMessage(), $f);
44221                    break;
44222                case self::OBSERVER_DIE :
44223                    $f = (isset($func[1])) ? $func[1] : '%s';
44224                    die(printf($f, $this->getMessage()));
44225                    break;
44226                default:
44227                    trigger_error('invalid observer type', E_USER_WARNING);
44228            }
44229        }
44230    }
44231
44232    /**
44233     * Return specific error information that can be used for more detailed
44234     * error messages or translation.
44235     *
44236     * This method may be overridden in child exception classes in order
44237     * to add functionality not present in PEAR_Exception and is a placeholder
44238     * to define API
44239     *
44240     * The returned array must be an associative array of parameter => value like so:
44241     * <pre>
44242     * array('name' => $name, 'context' => array(...))
44243     * </pre>
44244     * @return array
44245     */
44246    public function getErrorData()
44247    {
44248        return array();
44249    }
44250
44251    /**
44252     * Returns the exception that caused this exception to be thrown
44253     * @access public
44254     * @return Exception|array The context of the exception
44255     */
44256    public function getCause()
44257    {
44258        return $this->cause;
44259    }
44260
44261    /**
44262     * Function must be public to call on caused exceptions
44263     * @param array
44264     */
44265    public function getCauseMessage(&$causes)
44266    {
44267        $trace = $this->getTraceSafe();
44268        $cause = array('class'   => get_class($this),
44269                       'message' => $this->message,
44270                       'file' => 'unknown',
44271                       'line' => 'unknown');
44272        if (isset($trace[0])) {
44273            if (isset($trace[0]['file'])) {
44274                $cause['file'] = $trace[0]['file'];
44275                $cause['line'] = $trace[0]['line'];
44276            }
44277        }
44278        $causes[] = $cause;
44279        if ($this->cause instanceof PEAR_Exception) {
44280            $this->cause->getCauseMessage($causes);
44281        } elseif ($this->cause instanceof Exception) {
44282            $causes[] = array('class'   => get_class($this->cause),
44283                              'message' => $this->cause->getMessage(),
44284                              'file' => $this->cause->getFile(),
44285                              'line' => $this->cause->getLine());
44286        } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
44287            $causes[] = array('class' => get_class($this->cause),
44288                              'message' => $this->cause->getMessage(),
44289                              'file' => 'unknown',
44290                              'line' => 'unknown');
44291        } elseif (is_array($this->cause)) {
44292            foreach ($this->cause as $cause) {
44293                if ($cause instanceof PEAR_Exception) {
44294                    $cause->getCauseMessage($causes);
44295                } elseif ($cause instanceof Exception) {
44296                    $causes[] = array('class'   => get_class($cause),
44297                                   'message' => $cause->getMessage(),
44298                                   'file' => $cause->getFile(),
44299                                   'line' => $cause->getLine());
44300                } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
44301                    $causes[] = array('class' => get_class($cause),
44302                                      'message' => $cause->getMessage(),
44303                                      'file' => 'unknown',
44304                                      'line' => 'unknown');
44305                } elseif (is_array($cause) && isset($cause['message'])) {
44306                    // PEAR_ErrorStack warning
44307                    $causes[] = array(
44308                        'class' => $cause['package'],
44309                        'message' => $cause['message'],
44310                        'file' => isset($cause['context']['file']) ?
44311                                            $cause['context']['file'] :
44312                                            'unknown',
44313                        'line' => isset($cause['context']['line']) ?
44314                                            $cause['context']['line'] :
44315                                            'unknown',
44316                    );
44317                }
44318            }
44319        }
44320    }
44321
44322    public function getTraceSafe()
44323    {
44324        if (!isset($this->_trace)) {
44325            $this->_trace = $this->getTrace();
44326            if (empty($this->_trace)) {
44327                $backtrace = debug_backtrace();
44328                $this->_trace = array($backtrace[count($backtrace)-1]);
44329            }
44330        }
44331        return $this->_trace;
44332    }
44333
44334    public function getErrorClass()
44335    {
44336        $trace = $this->getTraceSafe();
44337        return $trace[0]['class'];
44338    }
44339
44340    public function getErrorMethod()
44341    {
44342        $trace = $this->getTraceSafe();
44343        return $trace[0]['function'];
44344    }
44345
44346    public function __toString()
44347    {
44348        if (isset($_SERVER['REQUEST_URI'])) {
44349            return $this->toHtml();
44350        }
44351        return $this->toText();
44352    }
44353
44354    public function toHtml()
44355    {
44356        $trace = $this->getTraceSafe();
44357        $causes = array();
44358        $this->getCauseMessage($causes);
44359        $html =  '<table style="border: 1px" cellspacing="0">' . "\n";
44360        foreach ($causes as $i => $cause) {
44361            $html .= '<tr><td colspan="3" style="background: #ff9999">'
44362               . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
44363               . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
44364               . 'on line <b>' . $cause['line'] . '</b>'
44365               . "</td></tr>\n";
44366        }
44367        $html .= '<tr><td colspan="3" style="background-color: #aaaaaa; text-align: center; font-weight: bold;">Exception trace</td></tr>' . "\n"
44368               . '<tr><td style="text-align: center; background: #cccccc; width:20px; font-weight: bold;">#</td>'
44369               . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Function</td>'
44370               . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Location</td></tr>' . "\n";
44371
44372        foreach ($trace as $k => $v) {
44373            $html .= '<tr><td style="text-align: center;">' . $k . '</td>'
44374                   . '<td>';
44375            if (!empty($v['class'])) {
44376                $html .= $v['class'] . $v['type'];
44377            }
44378            $html .= $v['function'];
44379            $args = array();
44380            if (!empty($v['args'])) {
44381                foreach ($v['args'] as $arg) {
44382                    if (is_null($arg)) $args[] = 'null';
44383                    elseif (is_array($arg)) $args[] = 'Array';
44384                    elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
44385                    elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
44386                    elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
44387                    else {
44388                        $arg = (string)$arg;
44389                        $str = htmlspecialchars(substr($arg, 0, 16));
44390                        if (strlen($arg) > 16) $str .= '&hellip;';
44391                        $args[] = "'" . $str . "'";
44392                    }
44393                }
44394            }
44395            $html .= '(' . implode(', ',$args) . ')'
44396                   . '</td>'
44397                   . '<td>' . (isset($v['file']) ? $v['file'] : 'unknown')
44398                   . ':' . (isset($v['line']) ? $v['line'] : 'unknown')
44399                   . '</td></tr>' . "\n";
44400        }
44401        $html .= '<tr><td style="text-align: center;">' . ($k+1) . '</td>'
44402               . '<td>{main}</td>'
44403               . '<td>&nbsp;</td></tr>' . "\n"
44404               . '</table>';
44405        return $html;
44406    }
44407
44408    public function toText()
44409    {
44410        $causes = array();
44411        $this->getCauseMessage($causes);
44412        $causeMsg = '';
44413        foreach ($causes as $i => $cause) {
44414            $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
44415                   . $cause['message'] . ' in ' . $cause['file']
44416                   . ' on line ' . $cause['line'] . "\n";
44417        }
44418        return $causeMsg . $this->getTraceAsString();
44419    }
44420}PEAR-1.9.4/PEAR/FixPHP5PEARWarnings.php0000644000076500000240000000023111605156614016034 0ustar  helgistaff<?php
44421if ($skipmsg) {
44422    $a = &new $ec($code, $mode, $options, $userinfo);
44423} else {
44424    $a = &new $ec($message, $code, $mode, $options, $userinfo);
44425}
44426?>PEAR-1.9.4/PEAR/Frontend.php0000644000076500000240000001507711605156614014265 0ustar  helgistaff<?php
44427/**
44428 * PEAR_Frontend, the singleton-based frontend for user input/output
44429 *
44430 * PHP versions 4 and 5
44431 *
44432 * @category   pear
44433 * @package    PEAR
44434 * @author     Greg Beaver <cellog@php.net>
44435 * @copyright  1997-2009 The Authors
44436 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
44437 * @version    CVS: $Id: Frontend.php 313023 2011-07-06 19:17:11Z dufuz $
44438 * @link       http://pear.php.net/package/PEAR
44439 * @since      File available since Release 1.4.0a1
44440 */
44441
44442/**
44443 * Include error handling
44444 */
44445//require_once 'PEAR.php';
44446
44447/**
44448 * Which user interface class is being used.
44449 * @var string class name
44450 */
44451$GLOBALS['_PEAR_FRONTEND_CLASS'] = 'PEAR_Frontend_CLI';
44452
44453/**
44454 * Instance of $_PEAR_Command_uiclass.
44455 * @var object
44456 */
44457$GLOBALS['_PEAR_FRONTEND_SINGLETON'] = null;
44458
44459/**
44460 * Singleton-based frontend for PEAR user input/output
44461 *
44462 * @category   pear
44463 * @package    PEAR
44464 * @author     Greg Beaver <cellog@php.net>
44465 * @copyright  1997-2009 The Authors
44466 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
44467 * @version    Release: 1.9.4
44468 * @link       http://pear.php.net/package/PEAR
44469 * @since      Class available since Release 1.4.0a1
44470 */
44471class PEAR_Frontend extends PEAR
44472{
44473    /**
44474     * Retrieve the frontend object
44475     * @return PEAR_Frontend_CLI|PEAR_Frontend_Web|PEAR_Frontend_Gtk
44476     * @static
44477     */
44478    function &singleton($type = null)
44479    {
44480        if ($type === null) {
44481            if (!isset($GLOBALS['_PEAR_FRONTEND_SINGLETON'])) {
44482                $a = false;
44483                return $a;
44484            }
44485            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
44486        }
44487
44488        $a = PEAR_Frontend::setFrontendClass($type);
44489        return $a;
44490    }
44491
44492    /**
44493     * Set the frontend class that will be used by calls to {@link singleton()}
44494     *
44495     * Frontends are expected to conform to the PEAR naming standard of
44496     * _ => DIRECTORY_SEPARATOR (PEAR_Frontend_CLI is in PEAR/Frontend/CLI.php)
44497     * @param string $uiclass full class name
44498     * @return PEAR_Frontend
44499     * @static
44500     */
44501    function &setFrontendClass($uiclass)
44502    {
44503        if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
44504              is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], $uiclass)) {
44505            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
44506        }
44507
44508        if (!class_exists($uiclass)) {
44509            $file = str_replace('_', '/', $uiclass) . '.php';
44510            if (PEAR_Frontend::isIncludeable($file)) {
44511                include_once $file;
44512            }
44513        }
44514
44515        if (class_exists($uiclass)) {
44516            $obj = &new $uiclass;
44517            // quick test to see if this class implements a few of the most
44518            // important frontend methods
44519            if (is_a($obj, 'PEAR_Frontend')) {
44520                $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$obj;
44521                $GLOBALS['_PEAR_FRONTEND_CLASS'] = $uiclass;
44522                return $obj;
44523            }
44524
44525            $err = PEAR::raiseError("not a frontend class: $uiclass");
44526            return $err;
44527        }
44528
44529        $err = PEAR::raiseError("no such class: $uiclass");
44530        return $err;
44531    }
44532
44533    /**
44534     * Set the frontend class that will be used by calls to {@link singleton()}
44535     *
44536     * Frontends are expected to be a descendant of PEAR_Frontend
44537     * @param PEAR_Frontend
44538     * @return PEAR_Frontend
44539     * @static
44540     */
44541    function &setFrontendObject($uiobject)
44542    {
44543        if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
44544              is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], get_class($uiobject))) {
44545            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
44546        }
44547
44548        if (!is_a($uiobject, 'PEAR_Frontend')) {
44549            $err = PEAR::raiseError('not a valid frontend class: (' .
44550                get_class($uiobject) . ')');
44551            return $err;
44552        }
44553
44554        $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$uiobject;
44555        $GLOBALS['_PEAR_FRONTEND_CLASS'] = get_class($uiobject);
44556        return $uiobject;
44557    }
44558
44559    /**
44560     * @param string $path relative or absolute include path
44561     * @return boolean
44562     * @static
44563     */
44564    function isIncludeable($path)
44565    {
44566        if (file_exists($path) && is_readable($path)) {
44567            return true;
44568        }
44569
44570        $fp = @fopen($path, 'r', true);
44571        if ($fp) {
44572            fclose($fp);
44573            return true;
44574        }
44575
44576        return false;
44577    }
44578
44579    /**
44580     * @param PEAR_Config
44581     */
44582    function setConfig(&$config)
44583    {
44584    }
44585
44586    /**
44587     * This can be overridden to allow session-based temporary file management
44588     *
44589     * By default, all files are deleted at the end of a session.  The web installer
44590     * needs to be able to sustain a list over many sessions in order to support
44591     * user interaction with install scripts
44592     */
44593    function addTempFile($file)
44594    {
44595        $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
44596    }
44597
44598    /**
44599     * Log an action
44600     *
44601     * @param string $msg the message to log
44602     * @param boolean $append_crlf
44603     * @return boolean true
44604     * @abstract
44605     */
44606    function log($msg, $append_crlf = true)
44607    {
44608    }
44609
44610    /**
44611     * Run a post-installation script
44612     *
44613     * @param array $scripts array of post-install scripts
44614     * @abstract
44615     */
44616    function runPostinstallScripts(&$scripts)
44617    {
44618    }
44619
44620    /**
44621     * Display human-friendly output formatted depending on the
44622     * $command parameter.
44623     *
44624     * This should be able to handle basic output data with no command
44625     * @param mixed  $data    data structure containing the information to display
44626     * @param string $command command from which this method was called
44627     * @abstract
44628     */
44629    function outputData($data, $command = '_default')
44630    {
44631    }
44632
44633    /**
44634     * Display a modal form dialog and return the given input
44635     *
44636     * A frontend that requires multiple requests to retrieve and process
44637     * data must take these needs into account, and implement the request
44638     * handling code.
44639     * @param string $command  command from which this method was called
44640     * @param array  $prompts  associative array. keys are the input field names
44641     *                         and values are the description
44642     * @param array  $types    array of input field types (text, password,
44643     *                         etc.) keys have to be the same like in $prompts
44644     * @param array  $defaults array of default values. again keys have
44645     *                         to be the same like in $prompts.  Do not depend
44646     *                         on a default value being set.
44647     * @return array input sent by the user
44648     * @abstract
44649     */
44650    function userDialog($command, $prompts, $types = array(), $defaults = array())
44651    {
44652    }
44653}PEAR-1.9.4/PEAR/Installer.php0000644000076500000240000021147511605156614014443 0ustar  helgistaff<?php
44654/**
44655 * PEAR_Installer
44656 *
44657 * PHP versions 4 and 5
44658 *
44659 * @category   pear
44660 * @package    PEAR
44661 * @author     Stig Bakken <ssb@php.net>
44662 * @author     Tomas V.V. Cox <cox@idecnet.com>
44663 * @author     Martin Jansen <mj@php.net>
44664 * @author     Greg Beaver <cellog@php.net>
44665 * @copyright  1997-2009 The Authors
44666 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
44667 * @version    CVS: $Id: Installer.php 313024 2011-07-06 19:51:24Z dufuz $
44668 * @link       http://pear.php.net/package/PEAR
44669 * @since      File available since Release 0.1
44670 */
44671
44672/**
44673 * Used for installation groups in package.xml 2.0 and platform exceptions
44674 */
44675require_once 'OS/Guess.php';
44676require_once 'PEAR/Downloader.php';
44677
44678define('PEAR_INSTALLER_NOBINARY', -240);
44679/**
44680 * Administration class used to install PEAR packages and maintain the
44681 * installed package database.
44682 *
44683 * @category   pear
44684 * @package    PEAR
44685 * @author     Stig Bakken <ssb@php.net>
44686 * @author     Tomas V.V. Cox <cox@idecnet.com>
44687 * @author     Martin Jansen <mj@php.net>
44688 * @author     Greg Beaver <cellog@php.net>
44689 * @copyright  1997-2009 The Authors
44690 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
44691 * @version    Release: 1.9.4
44692 * @link       http://pear.php.net/package/PEAR
44693 * @since      Class available since Release 0.1
44694 */
44695class PEAR_Installer extends PEAR_Downloader
44696{
44697    // {{{ properties
44698
44699    /** name of the package directory, for example Foo-1.0
44700     * @var string
44701     */
44702    var $pkgdir;
44703
44704    /** directory where PHP code files go
44705     * @var string
44706     */
44707    var $phpdir;
44708
44709    /** directory where PHP extension files go
44710     * @var string
44711     */
44712    var $extdir;
44713
44714    /** directory where documentation goes
44715     * @var string
44716     */
44717    var $docdir;
44718
44719    /** installation root directory (ala PHP's INSTALL_ROOT or
44720     * automake's DESTDIR
44721     * @var string
44722     */
44723    var $installroot = '';
44724
44725    /** debug level
44726     * @var int
44727     */
44728    var $debug = 1;
44729
44730    /** temporary directory
44731     * @var string
44732     */
44733    var $tmpdir;
44734
44735    /**
44736     * PEAR_Registry object used by the installer
44737     * @var PEAR_Registry
44738     */
44739    var $registry;
44740
44741    /**
44742     * array of PEAR_Downloader_Packages
44743     * @var array
44744     */
44745    var $_downloadedPackages;
44746
44747    /** List of file transactions queued for an install/upgrade/uninstall.
44748     *
44749     *  Format:
44750     *    array(
44751     *      0 => array("rename => array("from-file", "to-file")),
44752     *      1 => array("delete" => array("file-to-delete")),
44753     *      ...
44754     *    )
44755     *
44756     * @var array
44757     */
44758    var $file_operations = array();
44759
44760    // }}}
44761
44762    // {{{ constructor
44763
44764    /**
44765     * PEAR_Installer constructor.
44766     *
44767     * @param object $ui user interface object (instance of PEAR_Frontend_*)
44768     *
44769     * @access public
44770     */
44771    function PEAR_Installer(&$ui)
44772    {
44773        parent::PEAR_Common();
44774        $this->setFrontendObject($ui);
44775        $this->debug = $this->config->get('verbose');
44776    }
44777
44778    function setOptions($options)
44779    {
44780        $this->_options = $options;
44781    }
44782
44783    function setConfig(&$config)
44784    {
44785        $this->config    = &$config;
44786        $this->_registry = &$config->getRegistry();
44787    }
44788
44789    // }}}
44790
44791    function _removeBackups($files)
44792    {
44793        foreach ($files as $path) {
44794            $this->addFileOperation('removebackup', array($path));
44795        }
44796    }
44797
44798    // {{{ _deletePackageFiles()
44799
44800    /**
44801     * Delete a package's installed files, does not remove empty directories.
44802     *
44803     * @param string package name
44804     * @param string channel name
44805     * @param bool if true, then files are backed up first
44806     * @return bool TRUE on success, or a PEAR error on failure
44807     * @access protected
44808     */
44809    function _deletePackageFiles($package, $channel = false, $backup = false)
44810    {
44811        if (!$channel) {
44812            $channel = 'pear.php.net';
44813        }
44814
44815        if (!strlen($package)) {
44816            return $this->raiseError("No package to uninstall given");
44817        }
44818
44819        if (strtolower($package) == 'pear' && $channel == 'pear.php.net') {
44820            // to avoid race conditions, include all possible needed files
44821            require_once 'PEAR/Task/Common.php';
44822            require_once 'PEAR/Task/Replace.php';
44823            require_once 'PEAR/Task/Unixeol.php';
44824            require_once 'PEAR/Task/Windowseol.php';
44825            require_once 'PEAR/PackageFile/v1.php';
44826            require_once 'PEAR/PackageFile/v2.php';
44827            require_once 'PEAR/PackageFile/Generator/v1.php';
44828            require_once 'PEAR/PackageFile/Generator/v2.php';
44829        }
44830
44831        $filelist = $this->_registry->packageInfo($package, 'filelist', $channel);
44832        if ($filelist == null) {
44833            return $this->raiseError("$channel/$package not installed");
44834        }
44835
44836        $ret = array();
44837        foreach ($filelist as $file => $props) {
44838            if (empty($props['installed_as'])) {
44839                continue;
44840            }
44841
44842            $path = $props['installed_as'];
44843            if ($backup) {
44844                $this->addFileOperation('backup', array($path));
44845                $ret[] = $path;
44846            }
44847
44848            $this->addFileOperation('delete', array($path));
44849        }
44850
44851        if ($backup) {
44852            return $ret;
44853        }
44854
44855        return true;
44856    }
44857
44858    // }}}
44859    // {{{ _installFile()
44860
44861    /**
44862     * @param string filename
44863     * @param array attributes from <file> tag in package.xml
44864     * @param string path to install the file in
44865     * @param array options from command-line
44866     * @access private
44867     */
44868    function _installFile($file, $atts, $tmp_path, $options)
44869    {
44870        // {{{ return if this file is meant for another platform
44871        static $os;
44872        if (!isset($this->_registry)) {
44873            $this->_registry = &$this->config->getRegistry();
44874        }
44875
44876        if (isset($atts['platform'])) {
44877            if (empty($os)) {
44878                $os = new OS_Guess();
44879            }
44880
44881            if (strlen($atts['platform']) && $atts['platform']{0} == '!') {
44882                $negate   = true;
44883                $platform = substr($atts['platform'], 1);
44884            } else {
44885                $negate    = false;
44886                $platform = $atts['platform'];
44887            }
44888
44889            if ((bool) $os->matchSignature($platform) === $negate) {
44890                $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
44891                return PEAR_INSTALLER_SKIPPED;
44892            }
44893        }
44894        // }}}
44895
44896        $channel = $this->pkginfo->getChannel();
44897        // {{{ assemble the destination paths
44898        switch ($atts['role']) {
44899            case 'src':
44900            case 'extsrc':
44901                $this->source_files++;
44902                return;
44903            case 'doc':
44904            case 'data':
44905            case 'test':
44906                $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) .
44907                            DIRECTORY_SEPARATOR . $this->pkginfo->getPackage();
44908                unset($atts['baseinstalldir']);
44909                break;
44910            case 'ext':
44911            case 'php':
44912                $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel);
44913                break;
44914            case 'script':
44915                $dest_dir = $this->config->get('bin_dir', null, $channel);
44916                break;
44917            default:
44918                return $this->raiseError("Invalid role `$atts[role]' for file $file");
44919        }
44920
44921        $save_destdir = $dest_dir;
44922        if (!empty($atts['baseinstalldir'])) {
44923            $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
44924        }
44925
44926        if (dirname($file) != '.' && empty($atts['install-as'])) {
44927            $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
44928        }
44929
44930        if (empty($atts['install-as'])) {
44931            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
44932        } else {
44933            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
44934        }
44935        $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
44936
44937        // Clean up the DIRECTORY_SEPARATOR mess
44938        $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
44939        list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
44940                                                    array(DIRECTORY_SEPARATOR,
44941                                                          DIRECTORY_SEPARATOR,
44942                                                          DIRECTORY_SEPARATOR),
44943                                                    array($dest_file, $orig_file));
44944        $final_dest_file = $installed_as = $dest_file;
44945        if (isset($this->_options['packagingroot'])) {
44946            $installedas_dest_dir  = dirname($final_dest_file);
44947            $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
44948            $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']);
44949        } else {
44950            $installedas_dest_dir  = dirname($final_dest_file);
44951            $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
44952        }
44953
44954        $dest_dir  = dirname($final_dest_file);
44955        $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
44956        if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
44957            return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
44958        }
44959        // }}}
44960
44961        if (empty($this->_options['register-only']) &&
44962              (!file_exists($dest_dir) || !is_dir($dest_dir))) {
44963            if (!$this->mkDirHier($dest_dir)) {
44964                return $this->raiseError("failed to mkdir $dest_dir",
44965                                         PEAR_INSTALLER_FAILED);
44966            }
44967            $this->log(3, "+ mkdir $dest_dir");
44968        }
44969
44970        // pretty much nothing happens if we are only registering the install
44971        if (empty($this->_options['register-only'])) {
44972            if (empty($atts['replacements'])) {
44973                if (!file_exists($orig_file)) {
44974                    return $this->raiseError("file $orig_file does not exist",
44975                                             PEAR_INSTALLER_FAILED);
44976                }
44977
44978                if (!@copy($orig_file, $dest_file)) {
44979                    return $this->raiseError("failed to write $dest_file: $php_errormsg",
44980                                             PEAR_INSTALLER_FAILED);
44981                }
44982
44983                $this->log(3, "+ cp $orig_file $dest_file");
44984                if (isset($atts['md5sum'])) {
44985                    $md5sum = md5_file($dest_file);
44986                }
44987            } else {
44988                // {{{ file with replacements
44989                if (!file_exists($orig_file)) {
44990                    return $this->raiseError("file does not exist",
44991                                             PEAR_INSTALLER_FAILED);
44992                }
44993
44994                $contents = file_get_contents($orig_file);
44995                if ($contents === false) {
44996                    $contents = '';
44997                }
44998
44999                if (isset($atts['md5sum'])) {
45000                    $md5sum = md5($contents);
45001                }
45002
45003                $subst_from = $subst_to = array();
45004                foreach ($atts['replacements'] as $a) {
45005                    $to = '';
45006                    if ($a['type'] == 'php-const') {
45007                        if (preg_match('/^[a-z0-9_]+\\z/i', $a['to'])) {
45008                            eval("\$to = $a[to];");
45009                        } else {
45010                            if (!isset($options['soft'])) {
45011                                $this->log(0, "invalid php-const replacement: $a[to]");
45012                            }
45013                            continue;
45014                        }
45015                    } elseif ($a['type'] == 'pear-config') {
45016                        if ($a['to'] == 'master_server') {
45017                            $chan = $this->_registry->getChannel($channel);
45018                            if (!PEAR::isError($chan)) {
45019                                $to = $chan->getServer();
45020                            } else {
45021                                $to = $this->config->get($a['to'], null, $channel);
45022                            }
45023                        } else {
45024                            $to = $this->config->get($a['to'], null, $channel);
45025                        }
45026                        if (is_null($to)) {
45027                            if (!isset($options['soft'])) {
45028                                $this->log(0, "invalid pear-config replacement: $a[to]");
45029                            }
45030                            continue;
45031                        }
45032                    } elseif ($a['type'] == 'package-info') {
45033                        if ($t = $this->pkginfo->packageInfo($a['to'])) {
45034                            $to = $t;
45035                        } else {
45036                            if (!isset($options['soft'])) {
45037                                $this->log(0, "invalid package-info replacement: $a[to]");
45038                            }
45039                            continue;
45040                        }
45041                    }
45042                    if (!is_null($to)) {
45043                        $subst_from[] = $a['from'];
45044                        $subst_to[] = $to;
45045                    }
45046                }
45047
45048                $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
45049                if (sizeof($subst_from)) {
45050                    $contents = str_replace($subst_from, $subst_to, $contents);
45051                }
45052
45053                $wp = @fopen($dest_file, "wb");
45054                if (!is_resource($wp)) {
45055                    return $this->raiseError("failed to create $dest_file: $php_errormsg",
45056                                             PEAR_INSTALLER_FAILED);
45057                }
45058
45059                if (@fwrite($wp, $contents) === false) {
45060                    return $this->raiseError("failed writing to $dest_file: $php_errormsg",
45061                                             PEAR_INSTALLER_FAILED);
45062                }
45063
45064                fclose($wp);
45065                // }}}
45066            }
45067
45068            // {{{ check the md5
45069            if (isset($md5sum)) {
45070                if (strtolower($md5sum) === strtolower($atts['md5sum'])) {
45071                    $this->log(2, "md5sum ok: $final_dest_file");
45072                } else {
45073                    if (empty($options['force'])) {
45074                        // delete the file
45075                        if (file_exists($dest_file)) {
45076                            unlink($dest_file);
45077                        }
45078
45079                        if (!isset($options['ignore-errors'])) {
45080                            return $this->raiseError("bad md5sum for file $final_dest_file",
45081                                                 PEAR_INSTALLER_FAILED);
45082                        }
45083
45084                        if (!isset($options['soft'])) {
45085                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
45086                        }
45087                    } else {
45088                        if (!isset($options['soft'])) {
45089                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
45090                        }
45091                    }
45092                }
45093            }
45094            // }}}
45095            // {{{ set file permissions
45096            if (!OS_WINDOWS) {
45097                if ($atts['role'] == 'script') {
45098                    $mode = 0777 & ~(int)octdec($this->config->get('umask'));
45099                    $this->log(3, "+ chmod +x $dest_file");
45100                } else {
45101                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
45102                }
45103
45104                if ($atts['role'] != 'src') {
45105                    $this->addFileOperation("chmod", array($mode, $dest_file));
45106                    if (!@chmod($dest_file, $mode)) {
45107                        if (!isset($options['soft'])) {
45108                            $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
45109                        }
45110                    }
45111                }
45112            }
45113            // }}}
45114
45115            if ($atts['role'] == 'src') {
45116                rename($dest_file, $final_dest_file);
45117                $this->log(2, "renamed source file $dest_file to $final_dest_file");
45118            } else {
45119                $this->addFileOperation("rename", array($dest_file, $final_dest_file,
45120                    $atts['role'] == 'ext'));
45121            }
45122        }
45123
45124        // Store the full path where the file was installed for easy unistall
45125        if ($atts['role'] != 'script') {
45126            $loc = $this->config->get($atts['role'] . '_dir');
45127        } else {
45128            $loc = $this->config->get('bin_dir');
45129        }
45130
45131        if ($atts['role'] != 'src') {
45132            $this->addFileOperation("installed_as", array($file, $installed_as,
45133                                    $loc,
45134                                    dirname(substr($installedas_dest_file, strlen($loc)))));
45135        }
45136
45137        //$this->log(2, "installed: $dest_file");
45138        return PEAR_INSTALLER_OK;
45139    }
45140
45141    // }}}
45142    // {{{ _installFile2()
45143
45144    /**
45145     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
45146     * @param string filename
45147     * @param array attributes from <file> tag in package.xml
45148     * @param string path to install the file in
45149     * @param array options from command-line
45150     * @access private
45151     */
45152    function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options)
45153    {
45154        $atts = $real_atts;
45155        if (!isset($this->_registry)) {
45156            $this->_registry = &$this->config->getRegistry();
45157        }
45158
45159        $channel = $pkg->getChannel();
45160        // {{{ assemble the destination paths
45161        if (!in_array($atts['attribs']['role'],
45162              PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
45163            return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
45164                    "' for file $file");
45165        }
45166
45167        $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config);
45168        $err  = $role->setup($this, $pkg, $atts['attribs'], $file);
45169        if (PEAR::isError($err)) {
45170            return $err;
45171        }
45172
45173        if (!$role->isInstallable()) {
45174            return;
45175        }
45176
45177        $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path);
45178        if (PEAR::isError($info)) {
45179            return $info;
45180        }
45181
45182        list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info;
45183        if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
45184            return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
45185        }
45186
45187        $final_dest_file = $installed_as = $dest_file;
45188        if (isset($this->_options['packagingroot'])) {
45189            $final_dest_file = $this->_prependPath($final_dest_file,
45190                $this->_options['packagingroot']);
45191        }
45192
45193        $dest_dir  = dirname($final_dest_file);
45194        $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
45195        // }}}
45196
45197        if (empty($this->_options['register-only'])) {
45198            if (!file_exists($dest_dir) || !is_dir($dest_dir)) {
45199                if (!$this->mkDirHier($dest_dir)) {
45200                    return $this->raiseError("failed to mkdir $dest_dir",
45201                                             PEAR_INSTALLER_FAILED);
45202                }
45203                $this->log(3, "+ mkdir $dest_dir");
45204            }
45205        }
45206
45207        $attribs = $atts['attribs'];
45208        unset($atts['attribs']);
45209        // pretty much nothing happens if we are only registering the install
45210        if (empty($this->_options['register-only'])) {
45211            if (!count($atts)) { // no tasks
45212                if (!file_exists($orig_file)) {
45213                    return $this->raiseError("file $orig_file does not exist",
45214                                             PEAR_INSTALLER_FAILED);
45215                }
45216
45217                if (!@copy($orig_file, $dest_file)) {
45218                    return $this->raiseError("failed to write $dest_file: $php_errormsg",
45219                                             PEAR_INSTALLER_FAILED);
45220                }
45221
45222                $this->log(3, "+ cp $orig_file $dest_file");
45223                if (isset($attribs['md5sum'])) {
45224                    $md5sum = md5_file($dest_file);
45225                }
45226            } else { // file with tasks
45227                if (!file_exists($orig_file)) {
45228                    return $this->raiseError("file $orig_file does not exist",
45229                                             PEAR_INSTALLER_FAILED);
45230                }
45231
45232                $contents = file_get_contents($orig_file);
45233                if ($contents === false) {
45234                    $contents = '';
45235                }
45236
45237                if (isset($attribs['md5sum'])) {
45238                    $md5sum = md5($contents);
45239                }
45240
45241                foreach ($atts as $tag => $raw) {
45242                    $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), array('', '_'), $tag);
45243                    $task = "PEAR_Task_$tag";
45244                    $task = &new $task($this->config, $this, PEAR_TASK_INSTALL);
45245                    if (!$task->isScript()) { // scripts are only handled after installation
45246                        $task->init($raw, $attribs, $pkg->getLastInstalledVersion());
45247                        $res = $task->startSession($pkg, $contents, $final_dest_file);
45248                        if ($res === false) {
45249                            continue; // skip this file
45250                        }
45251
45252                        if (PEAR::isError($res)) {
45253                            return $res;
45254                        }
45255
45256                        $contents = $res; // save changes
45257                    }
45258
45259                    $wp = @fopen($dest_file, "wb");
45260                    if (!is_resource($wp)) {
45261                        return $this->raiseError("failed to create $dest_file: $php_errormsg",
45262                                                 PEAR_INSTALLER_FAILED);
45263                    }
45264
45265                    if (fwrite($wp, $contents) === false) {
45266                        return $this->raiseError("failed writing to $dest_file: $php_errormsg",
45267                                                 PEAR_INSTALLER_FAILED);
45268                    }
45269
45270                    fclose($wp);
45271                }
45272            }
45273
45274            // {{{ check the md5
45275            if (isset($md5sum)) {
45276                // Make sure the original md5 sum matches with expected
45277                if (strtolower($md5sum) === strtolower($attribs['md5sum'])) {
45278                    $this->log(2, "md5sum ok: $final_dest_file");
45279
45280                    if (isset($contents)) {
45281                        // set md5 sum based on $content in case any tasks were run.
45282                        $real_atts['attribs']['md5sum'] = md5($contents);
45283                    }
45284                } else {
45285                    if (empty($options['force'])) {
45286                        // delete the file
45287                        if (file_exists($dest_file)) {
45288                            unlink($dest_file);
45289                        }
45290
45291                        if (!isset($options['ignore-errors'])) {
45292                            return $this->raiseError("bad md5sum for file $final_dest_file",
45293                                                     PEAR_INSTALLER_FAILED);
45294                        }
45295
45296                        if (!isset($options['soft'])) {
45297                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
45298                        }
45299                    } else {
45300                        if (!isset($options['soft'])) {
45301                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
45302                        }
45303                    }
45304                }
45305            } else {
45306                $real_atts['attribs']['md5sum'] = md5_file($dest_file);
45307            }
45308
45309            // }}}
45310            // {{{ set file permissions
45311            if (!OS_WINDOWS) {
45312                if ($role->isExecutable()) {
45313                    $mode = 0777 & ~(int)octdec($this->config->get('umask'));
45314                    $this->log(3, "+ chmod +x $dest_file");
45315                } else {
45316                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
45317                }
45318
45319                if ($attribs['role'] != 'src') {
45320                    $this->addFileOperation("chmod", array($mode, $dest_file));
45321                    if (!@chmod($dest_file, $mode)) {
45322                        if (!isset($options['soft'])) {
45323                            $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
45324                        }
45325                    }
45326                }
45327            }
45328            // }}}
45329
45330            if ($attribs['role'] == 'src') {
45331                rename($dest_file, $final_dest_file);
45332                $this->log(2, "renamed source file $dest_file to $final_dest_file");
45333            } else {
45334                $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension()));
45335            }
45336        }
45337
45338        // Store the full path where the file was installed for easy uninstall
45339        if ($attribs['role'] != 'src') {
45340            $loc = $this->config->get($role->getLocationConfig(), null, $channel);
45341            $this->addFileOperation('installed_as', array($file, $installed_as,
45342                                $loc,
45343                                dirname(substr($installed_as, strlen($loc)))));
45344        }
45345
45346        //$this->log(2, "installed: $dest_file");
45347        return PEAR_INSTALLER_OK;
45348    }
45349
45350    // }}}
45351    // {{{ addFileOperation()
45352
45353    /**
45354     * Add a file operation to the current file transaction.
45355     *
45356     * @see startFileTransaction()
45357     * @param string $type This can be one of:
45358     *    - rename:  rename a file ($data has 3 values)
45359     *    - backup:  backup an existing file ($data has 1 value)
45360     *    - removebackup:  clean up backups created during install ($data has 1 value)
45361     *    - chmod:   change permissions on a file ($data has 2 values)
45362     *    - delete:  delete a file ($data has 1 value)
45363     *    - rmdir:   delete a directory if empty ($data has 1 value)
45364     *    - installed_as: mark a file as installed ($data has 4 values).
45365     * @param array $data For all file operations, this array must contain the
45366     *    full path to the file or directory that is being operated on.  For
45367     *    the rename command, the first parameter must be the file to rename,
45368     *    the second its new name, the third whether this is a PHP extension.
45369     *
45370     *    The installed_as operation contains 4 elements in this order:
45371     *    1. Filename as listed in the filelist element from package.xml
45372     *    2. Full path to the installed file
45373     *    3. Full path from the php_dir configuration variable used in this
45374     *       installation
45375     *    4. Relative path from the php_dir that this file is installed in
45376     */
45377    function addFileOperation($type, $data)
45378    {
45379        if (!is_array($data)) {
45380            return $this->raiseError('Internal Error: $data in addFileOperation'
45381                . ' must be an array, was ' . gettype($data));
45382        }
45383
45384        if ($type == 'chmod') {
45385            $octmode = decoct($data[0]);
45386            $this->log(3, "adding to transaction: $type $octmode $data[1]");
45387        } else {
45388            $this->log(3, "adding to transaction: $type " . implode(" ", $data));
45389        }
45390        $this->file_operations[] = array($type, $data);
45391    }
45392
45393    // }}}
45394    // {{{ startFileTransaction()
45395
45396    function startFileTransaction($rollback_in_case = false)
45397    {
45398        if (count($this->file_operations) && $rollback_in_case) {
45399            $this->rollbackFileTransaction();
45400        }
45401        $this->file_operations = array();
45402    }
45403
45404    // }}}
45405    // {{{ commitFileTransaction()
45406
45407    function commitFileTransaction()
45408    {
45409        // {{{ first, check permissions and such manually
45410        $errors = array();
45411        foreach ($this->file_operations as $key => $tr) {
45412            list($type, $data) = $tr;
45413            switch ($type) {
45414                case 'rename':
45415                    if (!file_exists($data[0])) {
45416                        $errors[] = "cannot rename file $data[0], doesn't exist";
45417                    }
45418
45419                    // check that dest dir. is writable
45420                    if (!is_writable(dirname($data[1]))) {
45421                        $errors[] = "permission denied ($type): $data[1]";
45422                    }
45423                    break;
45424                case 'chmod':
45425                    // check that file is writable
45426                    if (!is_writable($data[1])) {
45427                        $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
45428                    }
45429                    break;
45430                case 'delete':
45431                    if (!file_exists($data[0])) {
45432                        $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
45433                    }
45434                    // check that directory is writable
45435                    if (file_exists($data[0])) {
45436                        if (!is_writable(dirname($data[0]))) {
45437                            $errors[] = "permission denied ($type): $data[0]";
45438                        } else {
45439                            // make sure the file to be deleted can be opened for writing
45440                            $fp = false;
45441                            if (!is_dir($data[0]) &&
45442                                  (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) {
45443                                $errors[] = "permission denied ($type): $data[0]";
45444                            } elseif ($fp) {
45445                                fclose($fp);
45446                            }
45447                        }
45448
45449                        /* Verify we are not deleting a file owned by another package
45450                         * This can happen when a file moves from package A to B in
45451                         * an upgrade ala http://pear.php.net/17986
45452                         */
45453                        $info = array(
45454                            'package' => strtolower($this->pkginfo->getName()),
45455                            'channel' => strtolower($this->pkginfo->getChannel()),
45456                        );
45457                        $result = $this->_registry->checkFileMap($data[0], $info, '1.1');
45458                        if (is_array($result)) {
45459                            $res = array_diff($result, $info);
45460                            if (!empty($res)) {
45461                                $new = $this->_registry->getPackage($result[1], $result[0]);
45462                                $this->file_operations[$key] = false;
45463                                $this->log(3, "file $data[0] was scheduled for removal from {$this->pkginfo->getName()} but is owned by {$new->getChannel()}/{$new->getName()}, removal has been cancelled.");
45464                            }
45465                        }
45466                    }
45467                    break;
45468            }
45469
45470        }
45471        // }}}
45472
45473        $n = count($this->file_operations);
45474        $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName());
45475
45476        $m = count($errors);
45477        if ($m > 0) {
45478            foreach ($errors as $error) {
45479                if (!isset($this->_options['soft'])) {
45480                    $this->log(1, $error);
45481                }
45482            }
45483
45484            if (!isset($this->_options['ignore-errors'])) {
45485                return false;
45486            }
45487        }
45488
45489        $this->_dirtree = array();
45490        // {{{ really commit the transaction
45491        foreach ($this->file_operations as $i => $tr) {
45492            if (!$tr) {
45493                // support removal of non-existing backups
45494                continue;
45495            }
45496
45497            list($type, $data) = $tr;
45498            switch ($type) {
45499                case 'backup':
45500                    if (!file_exists($data[0])) {
45501                        $this->file_operations[$i] = false;
45502                        break;
45503                    }
45504
45505                    if (!@copy($data[0], $data[0] . '.bak')) {
45506                        $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] .
45507                            '.bak ' . $php_errormsg);
45508                        return false;
45509                    }
45510                    $this->log(3, "+ backup $data[0] to $data[0].bak");
45511                    break;
45512                case 'removebackup':
45513                    if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
45514                        unlink($data[0] . '.bak');
45515                        $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
45516                    }
45517                    break;
45518                case 'rename':
45519                    $test = file_exists($data[1]) ? @unlink($data[1]) : null;
45520                    if (!$test && file_exists($data[1])) {
45521                        if ($data[2]) {
45522                            $extra = ', this extension must be installed manually.  Rename to "' .
45523                                basename($data[1]) . '"';
45524                        } else {
45525                            $extra = '';
45526                        }
45527
45528                        if (!isset($this->_options['soft'])) {
45529                            $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' .
45530                                $data[0] . $extra);
45531                        }
45532
45533                        if (!isset($this->_options['ignore-errors'])) {
45534                            return false;
45535                        }
45536                    }
45537
45538                    // permissions issues with rename - copy() is far superior
45539                    $perms = @fileperms($data[0]);
45540                    if (!@copy($data[0], $data[1])) {
45541                        $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] .
45542                            ' ' . $php_errormsg);
45543                        return false;
45544                    }
45545
45546                    // copy over permissions, otherwise they are lost
45547                    @chmod($data[1], $perms);
45548                    @unlink($data[0]);
45549                    $this->log(3, "+ mv $data[0] $data[1]");
45550                    break;
45551                case 'chmod':
45552                    if (!@chmod($data[1], $data[0])) {
45553                        $this->log(1, 'Could not chmod ' . $data[1] . ' to ' .
45554                            decoct($data[0]) . ' ' . $php_errormsg);
45555                        return false;
45556                    }
45557
45558                    $octmode = decoct($data[0]);
45559                    $this->log(3, "+ chmod $octmode $data[1]");
45560                    break;
45561                case 'delete':
45562                    if (file_exists($data[0])) {
45563                        if (!@unlink($data[0])) {
45564                            $this->log(1, 'Could not delete ' . $data[0] . ' ' .
45565                                $php_errormsg);
45566                            return false;
45567                        }
45568                        $this->log(3, "+ rm $data[0]");
45569                    }
45570                    break;
45571                case 'rmdir':
45572                    if (file_exists($data[0])) {
45573                        do {
45574                            $testme = opendir($data[0]);
45575                            while (false !== ($entry = readdir($testme))) {
45576                                if ($entry == '.' || $entry == '..') {
45577                                    continue;
45578                                }
45579                                closedir($testme);
45580                                break 2; // this directory is not empty and can't be
45581                                         // deleted
45582                            }
45583
45584                            closedir($testme);
45585                            if (!@rmdir($data[0])) {
45586                                $this->log(1, 'Could not rmdir ' . $data[0] . ' ' .
45587                                    $php_errormsg);
45588                                return false;
45589                            }
45590                            $this->log(3, "+ rmdir $data[0]");
45591                        } while (false);
45592                    }
45593                    break;
45594                case 'installed_as':
45595                    $this->pkginfo->setInstalledAs($data[0], $data[1]);
45596                    if (!isset($this->_dirtree[dirname($data[1])])) {
45597                        $this->_dirtree[dirname($data[1])] = true;
45598                        $this->pkginfo->setDirtree(dirname($data[1]));
45599
45600                        while(!empty($data[3]) && dirname($data[3]) != $data[3] &&
45601                                $data[3] != '/' && $data[3] != '\\') {
45602                            $this->pkginfo->setDirtree($pp =
45603                                $this->_prependPath($data[3], $data[2]));
45604                            $this->_dirtree[$pp] = true;
45605                            $data[3] = dirname($data[3]);
45606                        }
45607                    }
45608                    break;
45609            }
45610        }
45611        // }}}
45612        $this->log(2, "successfully committed $n file operations");
45613        $this->file_operations = array();
45614        return true;
45615    }
45616
45617    // }}}
45618    // {{{ rollbackFileTransaction()
45619
45620    function rollbackFileTransaction()
45621    {
45622        $n = count($this->file_operations);
45623        $this->log(2, "rolling back $n file operations");
45624        foreach ($this->file_operations as $tr) {
45625            list($type, $data) = $tr;
45626            switch ($type) {
45627                case 'backup':
45628                    if (file_exists($data[0] . '.bak')) {
45629                        if (file_exists($data[0] && is_writable($data[0]))) {
45630                            unlink($data[0]);
45631                        }
45632                        @copy($data[0] . '.bak', $data[0]);
45633                        $this->log(3, "+ restore $data[0] from $data[0].bak");
45634                    }
45635                    break;
45636                case 'removebackup':
45637                    if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
45638                        unlink($data[0] . '.bak');
45639                        $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
45640                    }
45641                    break;
45642                case 'rename':
45643                    @unlink($data[0]);
45644                    $this->log(3, "+ rm $data[0]");
45645                    break;
45646                case 'mkdir':
45647                    @rmdir($data[0]);
45648                    $this->log(3, "+ rmdir $data[0]");
45649                    break;
45650                case 'chmod':
45651                    break;
45652                case 'delete':
45653                    break;
45654                case 'installed_as':
45655                    $this->pkginfo->setInstalledAs($data[0], false);
45656                    break;
45657            }
45658        }
45659        $this->pkginfo->resetDirtree();
45660        $this->file_operations = array();
45661    }
45662
45663    // }}}
45664    // {{{ mkDirHier($dir)
45665
45666    function mkDirHier($dir)
45667    {
45668        $this->addFileOperation('mkdir', array($dir));
45669        return parent::mkDirHier($dir);
45670    }
45671
45672    // }}}
45673    // {{{ download()
45674
45675    /**
45676     * Download any files and their dependencies, if necessary
45677     *
45678     * @param array a mixed list of package names, local files, or package.xml
45679     * @param PEAR_Config
45680     * @param array options from the command line
45681     * @param array this is the array that will be populated with packages to
45682     *              install.  Format of each entry:
45683     *
45684     * <code>
45685     * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
45686     *    'info' => array() // parsed package.xml
45687     * );
45688     * </code>
45689     * @param array this will be populated with any error messages
45690     * @param false private recursion variable
45691     * @param false private recursion variable
45692     * @param false private recursion variable
45693     * @deprecated in favor of PEAR_Downloader
45694     */
45695    function download($packages, $options, &$config, &$installpackages,
45696                      &$errors, $installed = false, $willinstall = false, $state = false)
45697    {
45698        // trickiness: initialize here
45699        parent::PEAR_Downloader($this->ui, $options, $config);
45700        $ret             = parent::download($packages);
45701        $errors          = $this->getErrorMsgs();
45702        $installpackages = $this->getDownloadedPackages();
45703        trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " .
45704                      "in favor of PEAR_Downloader class", E_USER_WARNING);
45705        return $ret;
45706    }
45707
45708    // }}}
45709    // {{{ _parsePackageXml()
45710
45711    function _parsePackageXml(&$descfile)
45712    {
45713        // Parse xml file -----------------------------------------------
45714        $pkg = new PEAR_PackageFile($this->config, $this->debug);
45715        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
45716        $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
45717        PEAR::staticPopErrorHandling();
45718        if (PEAR::isError($p)) {
45719            if (is_array($p->getUserInfo())) {
45720                foreach ($p->getUserInfo() as $err) {
45721                    $loglevel = $err['level'] == 'error' ? 0 : 1;
45722                    if (!isset($this->_options['soft'])) {
45723                        $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']);
45724                    }
45725                }
45726            }
45727            return $this->raiseError('Installation failed: invalid package file');
45728        }
45729
45730        $descfile = $p->getPackageFile();
45731        return $p;
45732    }
45733
45734    // }}}
45735    /**
45736     * Set the list of PEAR_Downloader_Package objects to allow more sane
45737     * dependency validation
45738     * @param array
45739     */
45740    function setDownloadedPackages(&$pkgs)
45741    {
45742        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
45743        $err = $this->analyzeDependencies($pkgs);
45744        PEAR::popErrorHandling();
45745        if (PEAR::isError($err)) {
45746            return $err;
45747        }
45748        $this->_downloadedPackages = &$pkgs;
45749    }
45750
45751    /**
45752     * Set the list of PEAR_Downloader_Package objects to allow more sane
45753     * dependency validation
45754     * @param array
45755     */
45756    function setUninstallPackages(&$pkgs)
45757    {
45758        $this->_downloadedPackages = &$pkgs;
45759    }
45760
45761    function getInstallPackages()
45762    {
45763        return $this->_downloadedPackages;
45764    }
45765
45766    // {{{ install()
45767
45768    /**
45769     * Installs the files within the package file specified.
45770     *
45771     * @param string|PEAR_Downloader_Package $pkgfile path to the package file,
45772     *        or a pre-initialized packagefile object
45773     * @param array $options
45774     * recognized options:
45775     * - installroot   : optional prefix directory for installation
45776     * - force         : force installation
45777     * - register-only : update registry but don't install files
45778     * - upgrade       : upgrade existing install
45779     * - soft          : fail silently
45780     * - nodeps        : ignore dependency conflicts/missing dependencies
45781     * - alldeps       : install all dependencies
45782     * - onlyreqdeps   : install only required dependencies
45783     *
45784     * @return array|PEAR_Error package info if successful
45785     */
45786    function install($pkgfile, $options = array())
45787    {
45788        $this->_options = $options;
45789        $this->_registry = &$this->config->getRegistry();
45790        if (is_object($pkgfile)) {
45791            $dlpkg    = &$pkgfile;
45792            $pkg      = $pkgfile->getPackageFile();
45793            $pkgfile  = $pkg->getArchiveFile();
45794            $descfile = $pkg->getPackageFile();
45795        } else {
45796            $descfile = $pkgfile;
45797            $pkg      = $this->_parsePackageXml($descfile);
45798            if (PEAR::isError($pkg)) {
45799                return $pkg;
45800            }
45801        }
45802
45803        $tmpdir = dirname($descfile);
45804        if (realpath($descfile) != realpath($pkgfile)) {
45805            // Use the temp_dir since $descfile can contain the download dir path
45806            $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net');
45807            $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"');
45808
45809            $tar = new Archive_Tar($pkgfile);
45810            if (!$tar->extract($tmpdir)) {
45811                return $this->raiseError("unable to unpack $pkgfile");
45812            }
45813        }
45814
45815        $pkgname = $pkg->getName();
45816        $channel = $pkg->getChannel();
45817        if (isset($this->_options['packagingroot'])) {
45818            $regdir = $this->_prependPath(
45819                $this->config->get('php_dir', null, 'pear.php.net'),
45820                $this->_options['packagingroot']);
45821
45822            $packrootphp_dir = $this->_prependPath(
45823                $this->config->get('php_dir', null, $channel),
45824                $this->_options['packagingroot']);
45825        }
45826
45827        if (isset($options['installroot'])) {
45828            $this->config->setInstallRoot($options['installroot']);
45829            $this->_registry = &$this->config->getRegistry();
45830            $installregistry = &$this->_registry;
45831            $this->installroot = ''; // all done automagically now
45832            $php_dir = $this->config->get('php_dir', null, $channel);
45833        } else {
45834            $this->config->setInstallRoot(false);
45835            $this->_registry = &$this->config->getRegistry();
45836            if (isset($this->_options['packagingroot'])) {
45837                $installregistry = &new PEAR_Registry($regdir);
45838                if (!$installregistry->channelExists($channel, true)) {
45839                    // we need to fake a channel-discover of this channel
45840                    $chanobj = $this->_registry->getChannel($channel, true);
45841                    $installregistry->addChannel($chanobj);
45842                }
45843                $php_dir = $packrootphp_dir;
45844            } else {
45845                $installregistry = &$this->_registry;
45846                $php_dir = $this->config->get('php_dir', null, $channel);
45847            }
45848            $this->installroot = '';
45849        }
45850
45851        // {{{ checks to do when not in "force" mode
45852        if (empty($options['force']) &&
45853              (file_exists($this->config->get('php_dir')) &&
45854               is_dir($this->config->get('php_dir')))) {
45855            $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname);
45856            $instfilelist = $pkg->getInstallationFileList(true);
45857            if (PEAR::isError($instfilelist)) {
45858                return $instfilelist;
45859            }
45860
45861            // ensure we have the most accurate registry
45862            $installregistry->flushFileMap();
45863            $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1');
45864            if (PEAR::isError($test)) {
45865                return $test;
45866            }
45867
45868            if (sizeof($test)) {
45869                $pkgs = $this->getInstallPackages();
45870                $found = false;
45871                foreach ($pkgs as $param) {
45872                    if ($pkg->isSubpackageOf($param)) {
45873                        $found = true;
45874                        break;
45875                    }
45876                }
45877
45878                if ($found) {
45879                    // subpackages can conflict with earlier versions of parent packages
45880                    $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel());
45881                    $tmp = $test;
45882                    foreach ($tmp as $file => $info) {
45883                        if (is_array($info)) {
45884                            if (strtolower($info[1]) == strtolower($param->getPackage()) &&
45885                                  strtolower($info[0]) == strtolower($param->getChannel())
45886                            ) {
45887                                if (isset($parentreg['filelist'][$file])) {
45888                                    unset($parentreg['filelist'][$file]);
45889                                } else{
45890                                    $pos     = strpos($file, '/');
45891                                    $basedir = substr($file, 0, $pos);
45892                                    $file2   = substr($file, $pos + 1);
45893                                    if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
45894                                        && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
45895                                    ) {
45896                                        unset($parentreg['filelist'][$file2]);
45897                                    }
45898                                }
45899
45900                                unset($test[$file]);
45901                            }
45902                        } else {
45903                            if (strtolower($param->getChannel()) != 'pear.php.net') {
45904                                continue;
45905                            }
45906
45907                            if (strtolower($info) == strtolower($param->getPackage())) {
45908                                if (isset($parentreg['filelist'][$file])) {
45909                                    unset($parentreg['filelist'][$file]);
45910                                } else{
45911                                    $pos     = strpos($file, '/');
45912                                    $basedir = substr($file, 0, $pos);
45913                                    $file2   = substr($file, $pos + 1);
45914                                    if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
45915                                        && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
45916                                    ) {
45917                                        unset($parentreg['filelist'][$file2]);
45918                                    }
45919                                }
45920
45921                                unset($test[$file]);
45922                            }
45923                        }
45924                    }
45925
45926                    $pfk = &new PEAR_PackageFile($this->config);
45927                    $parentpkg = &$pfk->fromArray($parentreg);
45928                    $installregistry->updatePackage2($parentpkg);
45929                }
45930
45931                if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) {
45932                    $tmp = $test;
45933                    foreach ($tmp as $file => $info) {
45934                        if (is_string($info)) {
45935                            // pear.php.net packages are always stored as strings
45936                            if (strtolower($info) == strtolower($param->getPackage())) {
45937                                // upgrading existing package
45938                                unset($test[$file]);
45939                            }
45940                        }
45941                    }
45942                }
45943
45944                if (count($test)) {
45945                    $msg = "$channel/$pkgname: conflicting files found:\n";
45946                    $longest = max(array_map("strlen", array_keys($test)));
45947                    $fmt = "%${longest}s (%s)\n";
45948                    foreach ($test as $file => $info) {
45949                        if (!is_array($info)) {
45950                            $info = array('pear.php.net', $info);
45951                        }
45952                        $info = $info[0] . '/' . $info[1];
45953                        $msg .= sprintf($fmt, $file, $info);
45954                    }
45955
45956                    if (!isset($options['ignore-errors'])) {
45957                        return $this->raiseError($msg);
45958                    }
45959
45960                    if (!isset($options['soft'])) {
45961                        $this->log(0, "WARNING: $msg");
45962                    }
45963                }
45964            }
45965        }
45966        // }}}
45967
45968        $this->startFileTransaction();
45969
45970        $usechannel = $channel;
45971        if ($channel == 'pecl.php.net') {
45972            $test = $installregistry->packageExists($pkgname, $channel);
45973            if (!$test) {
45974                $test = $installregistry->packageExists($pkgname, 'pear.php.net');
45975                $usechannel = 'pear.php.net';
45976            }
45977        } else {
45978            $test = $installregistry->packageExists($pkgname, $channel);
45979        }
45980
45981        if (empty($options['upgrade']) && empty($options['soft'])) {
45982            // checks to do only when installing new packages
45983            if (empty($options['force']) && $test) {
45984                return $this->raiseError("$channel/$pkgname is already installed");
45985            }
45986        } else {
45987            // Upgrade
45988            if ($test) {
45989                $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel);
45990                $v2 = $pkg->getVersion();
45991                $cmp = version_compare("$v1", "$v2", 'gt');
45992                if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
45993                    return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
45994                }
45995            }
45996        }
45997
45998        // Do cleanups for upgrade and install, remove old release's files first
45999        if ($test && empty($options['register-only'])) {
46000            // when upgrading, remove old release's files first:
46001            if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel,
46002                  true))) {
46003                if (!isset($options['ignore-errors'])) {
46004                    return $this->raiseError($err);
46005                }
46006
46007                if (!isset($options['soft'])) {
46008                    $this->log(0, 'WARNING: ' . $err->getMessage());
46009                }
46010            } else {
46011                $backedup = $err;
46012            }
46013        }
46014
46015        // {{{ Copy files to dest dir ---------------------------------------
46016
46017        // info from the package it self we want to access from _installFile
46018        $this->pkginfo = &$pkg;
46019        // used to determine whether we should build any C code
46020        $this->source_files = 0;
46021
46022        $savechannel = $this->config->get('default_channel');
46023        if (empty($options['register-only']) && !is_dir($php_dir)) {
46024            if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) {
46025                return $this->raiseError("no installation destination directory '$php_dir'\n");
46026            }
46027        }
46028
46029        if (substr($pkgfile, -4) != '.xml') {
46030            $tmpdir .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion();
46031        }
46032
46033        $this->configSet('default_channel', $channel);
46034        // {{{ install files
46035
46036        $ver = $pkg->getPackagexmlVersion();
46037        if (version_compare($ver, '2.0', '>=')) {
46038            $filelist = $pkg->getInstallationFilelist();
46039        } else {
46040            $filelist = $pkg->getFileList();
46041        }
46042
46043        if (PEAR::isError($filelist)) {
46044            return $filelist;
46045        }
46046
46047        $p = &$installregistry->getPackage($pkgname, $channel);
46048        $dirtree = (empty($options['register-only']) && $p) ? $p->getDirTree() : false;
46049
46050        $pkg->resetFilelist();
46051        $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(),
46052            'version', $pkg->getChannel()));
46053        foreach ($filelist as $file => $atts) {
46054            $this->expectError(PEAR_INSTALLER_FAILED);
46055            if ($pkg->getPackagexmlVersion() == '1.0') {
46056                $res = $this->_installFile($file, $atts, $tmpdir, $options);
46057            } else {
46058                $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options);
46059            }
46060            $this->popExpect();
46061
46062            if (PEAR::isError($res)) {
46063                if (empty($options['ignore-errors'])) {
46064                    $this->rollbackFileTransaction();
46065                    if ($res->getMessage() == "file does not exist") {
46066                        $this->raiseError("file $file in package.xml does not exist");
46067                    }
46068
46069                    return $this->raiseError($res);
46070                }
46071
46072                if (!isset($options['soft'])) {
46073                    $this->log(0, "Warning: " . $res->getMessage());
46074                }
46075            }
46076
46077            $real = isset($atts['attribs']) ? $atts['attribs'] : $atts;
46078            if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') {
46079                // Register files that were installed
46080                $pkg->installedFile($file, $atts);
46081            }
46082        }
46083        // }}}
46084
46085        // {{{ compile and install source files
46086        if ($this->source_files > 0 && empty($options['nobuild'])) {
46087            if (PEAR::isError($err =
46088                  $this->_compileSourceFiles($savechannel, $pkg))) {
46089                return $err;
46090            }
46091        }
46092        // }}}
46093
46094        if (isset($backedup)) {
46095            $this->_removeBackups($backedup);
46096        }
46097
46098        if (!$this->commitFileTransaction()) {
46099            $this->rollbackFileTransaction();
46100            $this->configSet('default_channel', $savechannel);
46101            return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
46102        }
46103        // }}}
46104
46105        $ret          = false;
46106        $installphase = 'install';
46107        $oldversion   = false;
46108        // {{{ Register that the package is installed -----------------------
46109        if (empty($options['upgrade'])) {
46110            // if 'force' is used, replace the info in registry
46111            $usechannel = $channel;
46112            if ($channel == 'pecl.php.net') {
46113                $test = $installregistry->packageExists($pkgname, $channel);
46114                if (!$test) {
46115                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
46116                    $usechannel = 'pear.php.net';
46117                }
46118            } else {
46119                $test = $installregistry->packageExists($pkgname, $channel);
46120            }
46121
46122            if (!empty($options['force']) && $test) {
46123                $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel);
46124                $installregistry->deletePackage($pkgname, $usechannel);
46125            }
46126            $ret = $installregistry->addPackage2($pkg);
46127        } else {
46128            if ($dirtree) {
46129                $this->startFileTransaction();
46130                // attempt to delete empty directories
46131                uksort($dirtree, array($this, '_sortDirs'));
46132                foreach($dirtree as $dir => $notused) {
46133                    $this->addFileOperation('rmdir', array($dir));
46134                }
46135                $this->commitFileTransaction();
46136            }
46137
46138            $usechannel = $channel;
46139            if ($channel == 'pecl.php.net') {
46140                $test = $installregistry->packageExists($pkgname, $channel);
46141                if (!$test) {
46142                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
46143                    $usechannel = 'pear.php.net';
46144                }
46145            } else {
46146                $test = $installregistry->packageExists($pkgname, $channel);
46147            }
46148
46149            // new: upgrade installs a package if it isn't installed
46150            if (!$test) {
46151                $ret = $installregistry->addPackage2($pkg);
46152            } else {
46153                if ($usechannel != $channel) {
46154                    $installregistry->deletePackage($pkgname, $usechannel);
46155                    $ret = $installregistry->addPackage2($pkg);
46156                } else {
46157                    $ret = $installregistry->updatePackage2($pkg);
46158                }
46159                $installphase = 'upgrade';
46160            }
46161        }
46162
46163        if (!$ret) {
46164            $this->configSet('default_channel', $savechannel);
46165            return $this->raiseError("Adding package $channel/$pkgname to registry failed");
46166        }
46167        // }}}
46168
46169        $this->configSet('default_channel', $savechannel);
46170        if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist
46171            if (PEAR_Task_Common::hasPostinstallTasks()) {
46172                PEAR_Task_Common::runPostinstallTasks($installphase);
46173            }
46174        }
46175
46176        return $pkg->toArray(true);
46177    }
46178
46179    // }}}
46180
46181    // {{{ _compileSourceFiles()
46182    /**
46183     * @param string
46184     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
46185     */
46186    function _compileSourceFiles($savechannel, &$filelist)
46187    {
46188        require_once 'PEAR/Builder.php';
46189        $this->log(1, "$this->source_files source files, building");
46190        $bob = &new PEAR_Builder($this->ui);
46191        $bob->debug = $this->debug;
46192        $built = $bob->build($filelist, array(&$this, '_buildCallback'));
46193        if (PEAR::isError($built)) {
46194            $this->rollbackFileTransaction();
46195            $this->configSet('default_channel', $savechannel);
46196            return $built;
46197        }
46198
46199        $this->log(1, "\nBuild process completed successfully");
46200        foreach ($built as $ext) {
46201            $bn = basename($ext['file']);
46202            list($_ext_name, $_ext_suff) = explode('.', $bn);
46203            if ($_ext_suff == '.so' || $_ext_suff == '.dll') {
46204                if (extension_loaded($_ext_name)) {
46205                    $this->raiseError("Extension '$_ext_name' already loaded. " .
46206                                      'Please unload it in your php.ini file ' .
46207                                      'prior to install or upgrade');
46208                }
46209                $role = 'ext';
46210            } else {
46211                $role = 'src';
46212            }
46213
46214            $dest = $ext['dest'];
46215            $packagingroot = '';
46216            if (isset($this->_options['packagingroot'])) {
46217                $packagingroot = $this->_options['packagingroot'];
46218            }
46219
46220            $copyto = $this->_prependPath($dest, $packagingroot);
46221            $extra  = $copyto != $dest ? " as '$copyto'" : '';
46222            $this->log(1, "Installing '$dest'$extra");
46223
46224            $copydir = dirname($copyto);
46225            // pretty much nothing happens if we are only registering the install
46226            if (empty($this->_options['register-only'])) {
46227                if (!file_exists($copydir) || !is_dir($copydir)) {
46228                    if (!$this->mkDirHier($copydir)) {
46229                        return $this->raiseError("failed to mkdir $copydir",
46230                            PEAR_INSTALLER_FAILED);
46231                    }
46232
46233                    $this->log(3, "+ mkdir $copydir");
46234                }
46235
46236                if (!@copy($ext['file'], $copyto)) {
46237                    return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED);
46238                }
46239
46240                $this->log(3, "+ cp $ext[file] $copyto");
46241                $this->addFileOperation('rename', array($ext['file'], $copyto));
46242                if (!OS_WINDOWS) {
46243                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
46244                    $this->addFileOperation('chmod', array($mode, $copyto));
46245                    if (!@chmod($copyto, $mode)) {
46246                        $this->log(0, "failed to change mode of $copyto ($php_errormsg)");
46247                    }
46248                }
46249            }
46250
46251
46252            $data = array(
46253                'role'         => $role,
46254                'name'         => $bn,
46255                'installed_as' => $dest,
46256                'php_api'      => $ext['php_api'],
46257                'zend_mod_api' => $ext['zend_mod_api'],
46258                'zend_ext_api' => $ext['zend_ext_api'],
46259            );
46260
46261            if ($filelist->getPackageXmlVersion() == '1.0') {
46262                $filelist->installedFile($bn, $data);
46263            } else {
46264                $filelist->installedFile($bn, array('attribs' => $data));
46265            }
46266        }
46267    }
46268
46269    // }}}
46270    function &getUninstallPackages()
46271    {
46272        return $this->_downloadedPackages;
46273    }
46274    // {{{ uninstall()
46275
46276    /**
46277     * Uninstall a package
46278     *
46279     * This method removes all files installed by the application, and then
46280     * removes any empty directories.
46281     * @param string package name
46282     * @param array Command-line options.  Possibilities include:
46283     *
46284     *              - installroot: base installation dir, if not the default
46285     *              - register-only : update registry but don't remove files
46286     *              - nodeps: do not process dependencies of other packages to ensure
46287     *                        uninstallation does not break things
46288     */
46289    function uninstall($package, $options = array())
46290    {
46291        $installRoot = isset($options['installroot']) ? $options['installroot'] : '';
46292        $this->config->setInstallRoot($installRoot);
46293
46294        $this->installroot = '';
46295        $this->_registry = &$this->config->getRegistry();
46296        if (is_object($package)) {
46297            $channel = $package->getChannel();
46298            $pkg     = $package;
46299            $package = $pkg->getPackage();
46300        } else {
46301            $pkg = false;
46302            $info = $this->_registry->parsePackageName($package,
46303                $this->config->get('default_channel'));
46304            $channel = $info['channel'];
46305            $package = $info['package'];
46306        }
46307
46308        $savechannel = $this->config->get('default_channel');
46309        $this->configSet('default_channel', $channel);
46310        if (!is_object($pkg)) {
46311            $pkg = $this->_registry->getPackage($package, $channel);
46312        }
46313
46314        if (!$pkg) {
46315            $this->configSet('default_channel', $savechannel);
46316            return $this->raiseError($this->_registry->parsedPackageNameToString(
46317                array(
46318                    'channel' => $channel,
46319                    'package' => $package
46320                ), true) . ' not installed');
46321        }
46322
46323        if ($pkg->getInstalledBinary()) {
46324            // this is just an alias for a binary package
46325            return $this->_registry->deletePackage($package, $channel);
46326        }
46327
46328        $filelist = $pkg->getFilelist();
46329        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
46330        if (!class_exists('PEAR_Dependency2')) {
46331            require_once 'PEAR/Dependency2.php';
46332        }
46333
46334        $depchecker = &new PEAR_Dependency2($this->config, $options,
46335            array('channel' => $channel, 'package' => $package),
46336            PEAR_VALIDATE_UNINSTALLING);
46337        $e = $depchecker->validatePackageUninstall($this);
46338        PEAR::staticPopErrorHandling();
46339        if (PEAR::isError($e)) {
46340            if (!isset($options['ignore-errors'])) {
46341                return $this->raiseError($e);
46342            }
46343
46344            if (!isset($options['soft'])) {
46345                $this->log(0, 'WARNING: ' . $e->getMessage());
46346            }
46347        } elseif (is_array($e)) {
46348            if (!isset($options['soft'])) {
46349                $this->log(0, $e[0]);
46350            }
46351        }
46352
46353        $this->pkginfo = &$pkg;
46354        // pretty much nothing happens if we are only registering the uninstall
46355        if (empty($options['register-only'])) {
46356            // {{{ Delete the files
46357            $this->startFileTransaction();
46358            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
46359            if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
46360                PEAR::popErrorHandling();
46361                $this->rollbackFileTransaction();
46362                $this->configSet('default_channel', $savechannel);
46363                if (!isset($options['ignore-errors'])) {
46364                    return $this->raiseError($err);
46365                }
46366
46367                if (!isset($options['soft'])) {
46368                    $this->log(0, 'WARNING: ' . $err->getMessage());
46369                }
46370            } else {
46371                PEAR::popErrorHandling();
46372            }
46373
46374            if (!$this->commitFileTransaction()) {
46375                $this->rollbackFileTransaction();
46376                if (!isset($options['ignore-errors'])) {
46377                    return $this->raiseError("uninstall failed");
46378                }
46379
46380                if (!isset($options['soft'])) {
46381                    $this->log(0, 'WARNING: uninstall failed');
46382                }
46383            } else {
46384                $this->startFileTransaction();
46385                $dirtree = $pkg->getDirTree();
46386                if ($dirtree === false) {
46387                    $this->configSet('default_channel', $savechannel);
46388                    return $this->_registry->deletePackage($package, $channel);
46389                }
46390
46391                // attempt to delete empty directories
46392                uksort($dirtree, array($this, '_sortDirs'));
46393                foreach($dirtree as $dir => $notused) {
46394                    $this->addFileOperation('rmdir', array($dir));
46395                }
46396
46397                if (!$this->commitFileTransaction()) {
46398                    $this->rollbackFileTransaction();
46399                    if (!isset($options['ignore-errors'])) {
46400                        return $this->raiseError("uninstall failed");
46401                    }
46402
46403                    if (!isset($options['soft'])) {
46404                        $this->log(0, 'WARNING: uninstall failed');
46405                    }
46406                }
46407            }
46408            // }}}
46409        }
46410
46411        $this->configSet('default_channel', $savechannel);
46412        // Register that the package is no longer installed
46413        return $this->_registry->deletePackage($package, $channel);
46414    }
46415
46416    /**
46417     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
46418     *
46419     * It also removes duplicate dependencies
46420     * @param array an array of PEAR_PackageFile_v[1/2] objects
46421     * @return array|PEAR_Error array of array(packagefilename, package.xml contents)
46422     */
46423    function sortPackagesForUninstall(&$packages)
46424    {
46425        $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config);
46426        if (PEAR::isError($this->_dependencyDB)) {
46427            return $this->_dependencyDB;
46428        }
46429        usort($packages, array(&$this, '_sortUninstall'));
46430    }
46431
46432    function _sortUninstall($a, $b)
46433    {
46434        if (!$a->getDeps() && !$b->getDeps()) {
46435            return 0; // neither package has dependencies, order is insignificant
46436        }
46437        if ($a->getDeps() && !$b->getDeps()) {
46438            return -1; // $a must be installed after $b because $a has dependencies
46439        }
46440        if (!$a->getDeps() && $b->getDeps()) {
46441            return 1; // $b must be installed after $a because $b has dependencies
46442        }
46443        // both packages have dependencies
46444        if ($this->_dependencyDB->dependsOn($a, $b)) {
46445            return -1;
46446        }
46447        if ($this->_dependencyDB->dependsOn($b, $a)) {
46448            return 1;
46449        }
46450        return 0;
46451    }
46452
46453    // }}}
46454    // {{{ _sortDirs()
46455    function _sortDirs($a, $b)
46456    {
46457        if (strnatcmp($a, $b) == -1) return 1;
46458        if (strnatcmp($a, $b) == 1) return -1;
46459        return 0;
46460    }
46461
46462    // }}}
46463
46464    // {{{ _buildCallback()
46465
46466    function _buildCallback($what, $data)
46467    {
46468        if (($what == 'cmdoutput' && $this->debug > 1) ||
46469            ($what == 'output' && $this->debug > 0)) {
46470            $this->ui->outputData(rtrim($data), 'build');
46471        }
46472    }
46473
46474    // }}}
46475}PEAR-1.9.4/PEAR/PackageFile.php0000644000076500000240000003705511605156614014641 0ustar  helgistaff<?php
46476/**
46477 * PEAR_PackageFile, package.xml parsing utility class
46478 *
46479 * PHP versions 4 and 5
46480 *
46481 * @category   pear
46482 * @package    PEAR
46483 * @author     Greg Beaver <cellog@php.net>
46484 * @copyright  1997-2009 The Authors
46485 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
46486 * @version    CVS: $Id: PackageFile.php 313024 2011-07-06 19:51:24Z dufuz $
46487 * @link       http://pear.php.net/package/PEAR
46488 * @since      File available since Release 1.4.0a1
46489 */
46490
46491/**
46492 * needed for PEAR_VALIDATE_* constants
46493 */
46494require_once 'PEAR/Validate.php';
46495/**
46496 * Error code if the package.xml <package> tag does not contain a valid version
46497 */
46498define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1);
46499/**
46500 * Error code if the package.xml <package> tag version is not supported (version 1.0 and 1.1 are the only supported versions,
46501 * currently
46502 */
46503define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2);
46504/**
46505 * Abstraction for the package.xml package description file
46506 *
46507 * @category   pear
46508 * @package    PEAR
46509 * @author     Greg Beaver <cellog@php.net>
46510 * @copyright  1997-2009 The Authors
46511 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
46512 * @version    Release: 1.9.4
46513 * @link       http://pear.php.net/package/PEAR
46514 * @since      Class available since Release 1.4.0a1
46515 */
46516class PEAR_PackageFile
46517{
46518    /**
46519     * @var PEAR_Config
46520     */
46521    var $_config;
46522    var $_debug;
46523
46524    var $_logger = false;
46525    /**
46526     * @var boolean
46527     */
46528    var $_rawReturn = false;
46529
46530    /**
46531     * helper for extracting Archive_Tar errors
46532     * @var array
46533     * @access private
46534     */
46535    var $_extractErrors = array();
46536
46537    /**
46538     *
46539     * @param   PEAR_Config $config
46540     * @param   ?   $debug
46541     * @param   string @tmpdir Optional temporary directory for uncompressing
46542     *          files
46543     */
46544    function PEAR_PackageFile(&$config, $debug = false)
46545    {
46546        $this->_config = $config;
46547        $this->_debug = $debug;
46548    }
46549
46550    /**
46551     * Turn off validation - return a parsed package.xml without checking it
46552     *
46553     * This is used by the package-validate command
46554     */
46555    function rawReturn()
46556    {
46557        $this->_rawReturn = true;
46558    }
46559
46560    function setLogger(&$l)
46561    {
46562        $this->_logger = &$l;
46563    }
46564
46565    /**
46566     * Create a PEAR_PackageFile_Parser_v* of a given version.
46567     * @param   int $version
46568     * @return  PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1
46569     */
46570    function &parserFactory($version)
46571    {
46572        if (!in_array($version{0}, array('1', '2'))) {
46573            $a = false;
46574            return $a;
46575        }
46576
46577        include_once 'PEAR/PackageFile/Parser/v' . $version{0} . '.php';
46578        $version = $version{0};
46579        $class = "PEAR_PackageFile_Parser_v$version";
46580        $a = new $class;
46581        return $a;
46582    }
46583
46584    /**
46585     * For simpler unit-testing
46586     * @return string
46587     */
46588    function getClassPrefix()
46589    {
46590        return 'PEAR_PackageFile_v';
46591    }
46592
46593    /**
46594     * Create a PEAR_PackageFile_v* of a given version.
46595     * @param   int $version
46596     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v1
46597     */
46598    function &factory($version)
46599    {
46600        if (!in_array($version{0}, array('1', '2'))) {
46601            $a = false;
46602            return $a;
46603        }
46604
46605        include_once 'PEAR/PackageFile/v' . $version{0} . '.php';
46606        $version = $version{0};
46607        $class = $this->getClassPrefix() . $version;
46608        $a = new $class;
46609        return $a;
46610    }
46611
46612    /**
46613     * Create a PEAR_PackageFile_v* from its toArray() method
46614     *
46615     * WARNING: no validation is performed, the array is assumed to be valid,
46616     * always parse from xml if you want validation.
46617     * @param   array $arr
46618     * @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2
46619     * @uses    factory() to construct the returned object.
46620     */
46621    function &fromArray($arr)
46622    {
46623        if (isset($arr['xsdversion'])) {
46624            $obj = &$this->factory($arr['xsdversion']);
46625            if ($this->_logger) {
46626                $obj->setLogger($this->_logger);
46627            }
46628
46629            $obj->setConfig($this->_config);
46630            $obj->fromArray($arr);
46631            return $obj;
46632        }
46633
46634        if (isset($arr['package']['attribs']['version'])) {
46635            $obj = &$this->factory($arr['package']['attribs']['version']);
46636        } else {
46637            $obj = &$this->factory('1.0');
46638        }
46639
46640        if ($this->_logger) {
46641            $obj->setLogger($this->_logger);
46642        }
46643
46644        $obj->setConfig($this->_config);
46645        $obj->fromArray($arr);
46646        return $obj;
46647    }
46648
46649    /**
46650     * Create a PEAR_PackageFile_v* from an XML string.
46651     * @access  public
46652     * @param   string $data contents of package.xml file
46653     * @param   int $state package state (one of PEAR_VALIDATE_* constants)
46654     * @param   string $file full path to the package.xml file (and the files
46655     *          it references)
46656     * @param   string $archive optional name of the archive that the XML was
46657     *          extracted from, if any
46658     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
46659     * @uses    parserFactory() to construct a parser to load the package.
46660     */
46661    function &fromXmlString($data, $state, $file, $archive = false)
46662    {
46663        if (preg_match('/<package[^>]+version=[\'"]([0-9]+\.[0-9]+)[\'"]/', $data, $packageversion)) {
46664            if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) {
46665                return PEAR::raiseError('package.xml version "' . $packageversion[1] .
46666                    '" is not supported, only 1.0, 2.0, and 2.1 are supported.');
46667            }
46668
46669            $object = &$this->parserFactory($packageversion[1]);
46670            if ($this->_logger) {
46671                $object->setLogger($this->_logger);
46672            }
46673
46674            $object->setConfig($this->_config);
46675            $pf = $object->parse($data, $file, $archive);
46676            if (PEAR::isError($pf)) {
46677                return $pf;
46678            }
46679
46680            if ($this->_rawReturn) {
46681                return $pf;
46682            }
46683
46684            if (!$pf->validate($state)) {;
46685                if ($this->_config->get('verbose') > 0
46686                    && $this->_logger && $pf->getValidationWarnings(false)
46687                ) {
46688                    foreach ($pf->getValidationWarnings(false) as $warning) {
46689                        $this->_logger->log(0, 'ERROR: ' . $warning['message']);
46690                    }
46691                }
46692
46693                $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
46694                    2, null, null, $pf->getValidationWarnings());
46695                return $a;
46696            }
46697
46698            if ($this->_logger && $pf->getValidationWarnings(false)) {
46699                foreach ($pf->getValidationWarnings() as $warning) {
46700                    $this->_logger->log(0, 'WARNING: ' . $warning['message']);
46701                }
46702            }
46703
46704            if (method_exists($pf, 'flattenFilelist')) {
46705                $pf->flattenFilelist(); // for v2
46706            }
46707
46708            return $pf;
46709        } elseif (preg_match('/<package[^>]+version=[\'"]([^"\']+)[\'"]/', $data, $packageversion)) {
46710            $a = PEAR::raiseError('package.xml file "' . $file .
46711                '" has unsupported package.xml <package> version "' . $packageversion[1] . '"');
46712            return $a;
46713        } else {
46714            if (!class_exists('PEAR_ErrorStack')) {
46715                require_once 'PEAR/ErrorStack.php';
46716            }
46717
46718            PEAR_ErrorStack::staticPush('PEAR_PackageFile',
46719                PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION,
46720                'warning', array('xml' => $data), 'package.xml "' . $file .
46721                    '" has no package.xml <package> version');
46722            $object = &$this->parserFactory('1.0');
46723            $object->setConfig($this->_config);
46724            $pf = $object->parse($data, $file, $archive);
46725            if (PEAR::isError($pf)) {
46726                return $pf;
46727            }
46728
46729            if ($this->_rawReturn) {
46730                return $pf;
46731            }
46732
46733            if (!$pf->validate($state)) {
46734                $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
46735                    2, null, null, $pf->getValidationWarnings());
46736                return $a;
46737            }
46738
46739            if ($this->_logger && $pf->getValidationWarnings(false)) {
46740                foreach ($pf->getValidationWarnings() as $warning) {
46741                    $this->_logger->log(0, 'WARNING: ' . $warning['message']);
46742                }
46743            }
46744
46745            if (method_exists($pf, 'flattenFilelist')) {
46746                $pf->flattenFilelist(); // for v2
46747            }
46748
46749            return $pf;
46750        }
46751    }
46752
46753    /**
46754     * Register a temporary file or directory.  When the destructor is
46755     * executed, all registered temporary files and directories are
46756     * removed.
46757     *
46758     * @param string  $file  name of file or directory
46759     * @return  void
46760     */
46761    function addTempFile($file)
46762    {
46763        $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
46764    }
46765
46766    /**
46767     * Create a PEAR_PackageFile_v* from a compresed Tar or Tgz file.
46768     * @access  public
46769     * @param string contents of package.xml file
46770     * @param int package state (one of PEAR_VALIDATE_* constants)
46771     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
46772     * @using   Archive_Tar to extract the files
46773     * @using   fromPackageFile() to load the package after the package.xml
46774     *          file is extracted.
46775     */
46776    function &fromTgzFile($file, $state)
46777    {
46778        if (!class_exists('Archive_Tar')) {
46779            require_once 'Archive/Tar.php';
46780        }
46781
46782        $tar = new Archive_Tar($file);
46783        if ($this->_debug <= 1) {
46784            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
46785        }
46786
46787        $content = $tar->listContent();
46788        if ($this->_debug <= 1) {
46789            $tar->popErrorHandling();
46790        }
46791
46792        if (!is_array($content)) {
46793            if (is_string($file) && strlen($file < 255) &&
46794                  (!file_exists($file) || !@is_file($file))) {
46795                $ret = PEAR::raiseError("could not open file \"$file\"");
46796                return $ret;
46797            }
46798
46799            $file = realpath($file);
46800            $ret = PEAR::raiseError("Could not get contents of package \"$file\"".
46801                                     '. Invalid tgz file.');
46802            return $ret;
46803        }
46804
46805        if (!count($content) && !@is_file($file)) {
46806            $ret = PEAR::raiseError("could not open file \"$file\"");
46807            return $ret;
46808        }
46809
46810        $xml      = null;
46811        $origfile = $file;
46812        foreach ($content as $file) {
46813            $name = $file['filename'];
46814            if ($name == 'package2.xml') { // allow a .tgz to distribute both versions
46815                $xml = $name;
46816                break;
46817            }
46818
46819            if ($name == 'package.xml') {
46820                $xml = $name;
46821                break;
46822            } elseif (preg_match('/package.xml$/', $name, $match)) {
46823                $xml = $name;
46824                break;
46825            }
46826        }
46827
46828        $tmpdir = System::mktemp('-t "' . $this->_config->get('temp_dir') . '" -d pear');
46829        if ($tmpdir === false) {
46830            $ret = PEAR::raiseError("there was a problem with getting the configured temp directory");
46831            return $ret;
46832        }
46833
46834        PEAR_PackageFile::addTempFile($tmpdir);
46835
46836        $this->_extractErrors();
46837        PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors'));
46838
46839        if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
46840            $extra = implode("\n", $this->_extractErrors());
46841            if ($extra) {
46842                $extra = ' ' . $extra;
46843            }
46844
46845            PEAR::staticPopErrorHandling();
46846            $ret = PEAR::raiseError('could not extract the package.xml file from "' .
46847                $origfile . '"' . $extra);
46848            return $ret;
46849        }
46850
46851        PEAR::staticPopErrorHandling();
46852        $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile);
46853        return $ret;
46854    }
46855
46856    /**
46857     * helper callback for extracting Archive_Tar errors
46858     *
46859     * @param PEAR_Error|null $err
46860     * @return array
46861     * @access private
46862     */
46863    function _extractErrors($err = null)
46864    {
46865        static $errors = array();
46866        if ($err === null) {
46867            $e = $errors;
46868            $errors = array();
46869            return $e;
46870        }
46871        $errors[] = $err->getMessage();
46872    }
46873
46874    /**
46875     * Create a PEAR_PackageFile_v* from a package.xml file.
46876     *
46877     * @access public
46878     * @param   string  $descfile  name of package xml file
46879     * @param   int     $state package state (one of PEAR_VALIDATE_* constants)
46880     * @param   string|false $archive name of the archive this package.xml came
46881     *          from, if any
46882     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
46883     * @uses    PEAR_PackageFile::fromXmlString to create the oject after the
46884     *          XML is loaded from the package.xml file.
46885     */
46886    function &fromPackageFile($descfile, $state, $archive = false)
46887    {
46888        $fp = false;
46889        if (is_string($descfile) && strlen($descfile) < 255 &&
46890             (
46891              !file_exists($descfile) || !is_file($descfile) || !is_readable($descfile)
46892              || (!$fp = @fopen($descfile, 'r'))
46893             )
46894        ) {
46895            $a = PEAR::raiseError("Unable to open $descfile");
46896            return $a;
46897        }
46898
46899        // read the whole thing so we only get one cdata callback
46900        // for each block of cdata
46901        fclose($fp);
46902        $data = file_get_contents($descfile);
46903        $ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive);
46904        return $ret;
46905    }
46906
46907    /**
46908     * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file.
46909     *
46910     * This method is able to extract information about a package from a .tgz
46911     * archive or from a XML package definition file.
46912     *
46913     * @access public
46914     * @param   string  $info file name
46915     * @param   int     $state package state (one of PEAR_VALIDATE_* constants)
46916     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
46917     * @uses    fromPackageFile() if the file appears to be XML
46918     * @uses    fromTgzFile() to load all non-XML files
46919     */
46920    function &fromAnyFile($info, $state)
46921    {
46922        if (is_dir($info)) {
46923            $dir_name = realpath($info);
46924            if (file_exists($dir_name . '/package.xml')) {
46925                $info = PEAR_PackageFile::fromPackageFile($dir_name .  '/package.xml', $state);
46926            } elseif (file_exists($dir_name .  '/package2.xml')) {
46927                $info = PEAR_PackageFile::fromPackageFile($dir_name .  '/package2.xml', $state);
46928            } else {
46929                $info = PEAR::raiseError("No package definition found in '$info' directory");
46930            }
46931
46932            return $info;
46933        }
46934
46935        $fp = false;
46936        if (is_string($info) && strlen($info) < 255 &&
46937             (file_exists($info) || ($fp = @fopen($info, 'r')))
46938        ) {
46939
46940            if ($fp) {
46941                fclose($fp);
46942            }
46943
46944            $tmp = substr($info, -4);
46945            if ($tmp == '.xml') {
46946                $info = &PEAR_PackageFile::fromPackageFile($info, $state);
46947            } elseif ($tmp == '.tar' || $tmp == '.tgz') {
46948                $info = &PEAR_PackageFile::fromTgzFile($info, $state);
46949            } else {
46950                $fp   = fopen($info, 'r');
46951                $test = fread($fp, 5);
46952                fclose($fp);
46953                if ($test == '<?xml') {
46954                    $info = &PEAR_PackageFile::fromPackageFile($info, $state);
46955                } else {
46956                    $info = &PEAR_PackageFile::fromTgzFile($info, $state);
46957                }
46958            }
46959
46960            return $info;
46961        }
46962
46963        $info = PEAR::raiseError("Cannot open '$info' for parsing");
46964        return $info;
46965    }
46966}PEAR-1.9.4/PEAR/Packager.php0000644000076500000240000001715111605156614014216 0ustar  helgistaff<?php
46967/**
46968 * PEAR_Packager for generating releases
46969 *
46970 * PHP versions 4 and 5
46971 *
46972 * @category   pear
46973 * @package    PEAR
46974 * @author     Stig Bakken <ssb@php.net>
46975 * @author     Tomas V. V. Cox <cox@idecnet.com>
46976 * @author     Greg Beaver <cellog@php.net>
46977 * @copyright  1997-2009 The Authors
46978 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
46979 * @version    CVS: $Id: Packager.php 313023 2011-07-06 19:17:11Z dufuz $
46980 * @link       http://pear.php.net/package/PEAR
46981 * @since      File available since Release 0.1
46982 */
46983
46984/**
46985 * base class
46986 */
46987require_once 'PEAR/Common.php';
46988require_once 'PEAR/PackageFile.php';
46989require_once 'System.php';
46990
46991/**
46992 * Administration class used to make a PEAR release tarball.
46993 *
46994 * @category   pear
46995 * @package    PEAR
46996 * @author     Greg Beaver <cellog@php.net>
46997 * @copyright  1997-2009 The Authors
46998 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
46999 * @version    Release: 1.9.4
47000 * @link       http://pear.php.net/package/PEAR
47001 * @since      Class available since Release 0.1
47002 */
47003class PEAR_Packager extends PEAR_Common
47004{
47005    /**
47006     * @var PEAR_Registry
47007     */
47008    var $_registry;
47009
47010    function package($pkgfile = null, $compress = true, $pkg2 = null)
47011    {
47012        // {{{ validate supplied package.xml file
47013        if (empty($pkgfile)) {
47014            $pkgfile = 'package.xml';
47015        }
47016
47017        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
47018        $pkg  = &new PEAR_PackageFile($this->config, $this->debug);
47019        $pf   = &$pkg->fromPackageFile($pkgfile, PEAR_VALIDATE_NORMAL);
47020        $main = &$pf;
47021        PEAR::staticPopErrorHandling();
47022        if (PEAR::isError($pf)) {
47023            if (is_array($pf->getUserInfo())) {
47024                foreach ($pf->getUserInfo() as $error) {
47025                    $this->log(0, 'Error: ' . $error['message']);
47026                }
47027            }
47028
47029            $this->log(0, $pf->getMessage());
47030            return $this->raiseError("Cannot package, errors in package file");
47031        }
47032
47033        foreach ($pf->getValidationWarnings() as $warning) {
47034            $this->log(1, 'Warning: ' . $warning['message']);
47035        }
47036
47037        // }}}
47038        if ($pkg2) {
47039            $this->log(0, 'Attempting to process the second package file');
47040            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
47041            $pf2 = &$pkg->fromPackageFile($pkg2, PEAR_VALIDATE_NORMAL);
47042            PEAR::staticPopErrorHandling();
47043            if (PEAR::isError($pf2)) {
47044                if (is_array($pf2->getUserInfo())) {
47045                    foreach ($pf2->getUserInfo() as $error) {
47046                        $this->log(0, 'Error: ' . $error['message']);
47047                    }
47048                }
47049                $this->log(0, $pf2->getMessage());
47050                return $this->raiseError("Cannot package, errors in second package file");
47051            }
47052
47053            foreach ($pf2->getValidationWarnings() as $warning) {
47054                $this->log(1, 'Warning: ' . $warning['message']);
47055            }
47056
47057            if ($pf2->getPackagexmlVersion() == '2.0' ||
47058                  $pf2->getPackagexmlVersion() == '2.1'
47059            ) {
47060                $main  = &$pf2;
47061                $other = &$pf;
47062            } else {
47063                $main  = &$pf;
47064                $other = &$pf2;
47065            }
47066
47067            if ($main->getPackagexmlVersion() != '2.0' &&
47068                  $main->getPackagexmlVersion() != '2.1') {
47069                return PEAR::raiseError('Error: cannot package two package.xml version 1.0, can ' .
47070                    'only package together a package.xml 1.0 and package.xml 2.0');
47071            }
47072
47073            if ($other->getPackagexmlVersion() != '1.0') {
47074                return PEAR::raiseError('Error: cannot package two package.xml version 2.0, can ' .
47075                    'only package together a package.xml 1.0 and package.xml 2.0');
47076            }
47077        }
47078
47079        $main->setLogger($this);
47080        if (!$main->validate(PEAR_VALIDATE_PACKAGING)) {
47081            foreach ($main->getValidationWarnings() as $warning) {
47082                $this->log(0, 'Error: ' . $warning['message']);
47083            }
47084            return $this->raiseError("Cannot package, errors in package");
47085        }
47086
47087        foreach ($main->getValidationWarnings() as $warning) {
47088            $this->log(1, 'Warning: ' . $warning['message']);
47089        }
47090
47091        if ($pkg2) {
47092            $other->setLogger($this);
47093            $a = false;
47094            if (!$other->validate(PEAR_VALIDATE_NORMAL) || $a = !$main->isEquivalent($other)) {
47095                foreach ($other->getValidationWarnings() as $warning) {
47096                    $this->log(0, 'Error: ' . $warning['message']);
47097                }
47098
47099                foreach ($main->getValidationWarnings() as $warning) {
47100                    $this->log(0, 'Error: ' . $warning['message']);
47101                }
47102
47103                if ($a) {
47104                    return $this->raiseError('The two package.xml files are not equivalent!');
47105                }
47106
47107                return $this->raiseError("Cannot package, errors in package");
47108            }
47109
47110            foreach ($other->getValidationWarnings() as $warning) {
47111                $this->log(1, 'Warning: ' . $warning['message']);
47112            }
47113
47114            $gen = &$main->getDefaultGenerator();
47115            $tgzfile = $gen->toTgz2($this, $other, $compress);
47116            if (PEAR::isError($tgzfile)) {
47117                return $tgzfile;
47118            }
47119
47120            $dest_package = basename($tgzfile);
47121            $pkgdir       = dirname($pkgfile);
47122
47123            // TAR the Package -------------------------------------------
47124            $this->log(1, "Package $dest_package done");
47125            if (file_exists("$pkgdir/CVS/Root")) {
47126                $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion());
47127                $cvstag = "RELEASE_$cvsversion";
47128                $this->log(1, 'Tag the released code with "pear cvstag ' .
47129                    $main->getPackageFile() . '"');
47130                $this->log(1, "(or set the CVS tag $cvstag by hand)");
47131            } elseif (file_exists("$pkgdir/.svn")) {
47132                $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion());
47133                $svntag = $pf->getName() . "-$svnversion";
47134                $this->log(1, 'Tag the released code with "pear svntag ' .
47135                    $main->getPackageFile() . '"');
47136                $this->log(1, "(or set the SVN tag $svntag by hand)");
47137            }
47138        } else { // this branch is executed for single packagefile packaging
47139            $gen = &$pf->getDefaultGenerator();
47140            $tgzfile = $gen->toTgz($this, $compress);
47141            if (PEAR::isError($tgzfile)) {
47142                $this->log(0, $tgzfile->getMessage());
47143                return $this->raiseError("Cannot package, errors in package");
47144            }
47145
47146            $dest_package = basename($tgzfile);
47147            $pkgdir       = dirname($pkgfile);
47148
47149            // TAR the Package -------------------------------------------
47150            $this->log(1, "Package $dest_package done");
47151            if (file_exists("$pkgdir/CVS/Root")) {
47152                $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion());
47153                $cvstag = "RELEASE_$cvsversion";
47154                $this->log(1, "Tag the released code with `pear cvstag $pkgfile'");
47155                $this->log(1, "(or set the CVS tag $cvstag by hand)");
47156            } elseif (file_exists("$pkgdir/.svn")) {
47157                $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion());
47158                $svntag = $pf->getName() . "-$svnversion";
47159                $this->log(1, "Tag the released code with `pear svntag $pkgfile'");
47160                $this->log(1, "(or set the SVN tag $svntag by hand)");
47161            }
47162        }
47163
47164        return $dest_package;
47165    }
47166}PEAR-1.9.4/PEAR/Registry.php0000644000076500000240000022412411605156614014311 0ustar  helgistaff<?php
47167/**
47168 * PEAR_Registry
47169 *
47170 * PHP versions 4 and 5
47171 *
47172 * @category   pear
47173 * @package    PEAR
47174 * @author     Stig Bakken <ssb@php.net>
47175 * @author     Tomas V. V. Cox <cox@idecnet.com>
47176 * @author     Greg Beaver <cellog@php.net>
47177 * @copyright  1997-2009 The Authors
47178 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
47179 * @version    CVS: $Id: Registry.php 313023 2011-07-06 19:17:11Z dufuz $
47180 * @link       http://pear.php.net/package/PEAR
47181 * @since      File available since Release 0.1
47182 */
47183
47184/**
47185 * for PEAR_Error
47186 */
47187require_once 'PEAR.php';
47188require_once 'PEAR/DependencyDB.php';
47189
47190define('PEAR_REGISTRY_ERROR_LOCK',         -2);
47191define('PEAR_REGISTRY_ERROR_FORMAT',       -3);
47192define('PEAR_REGISTRY_ERROR_FILE',         -4);
47193define('PEAR_REGISTRY_ERROR_CONFLICT',     -5);
47194define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6);
47195
47196/**
47197 * Administration class used to maintain the installed package database.
47198 * @category   pear
47199 * @package    PEAR
47200 * @author     Stig Bakken <ssb@php.net>
47201 * @author     Tomas V. V. Cox <cox@idecnet.com>
47202 * @author     Greg Beaver <cellog@php.net>
47203 * @copyright  1997-2009 The Authors
47204 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
47205 * @version    Release: 1.9.4
47206 * @link       http://pear.php.net/package/PEAR
47207 * @since      Class available since Release 1.4.0a1
47208 */
47209class PEAR_Registry extends PEAR
47210{
47211    /**
47212     * File containing all channel information.
47213     * @var string
47214     */
47215    var $channels = '';
47216
47217    /** Directory where registry files are stored.
47218     * @var string
47219     */
47220    var $statedir = '';
47221
47222    /** File where the file map is stored
47223     * @var string
47224     */
47225    var $filemap = '';
47226
47227    /** Directory where registry files for channels are stored.
47228     * @var string
47229     */
47230    var $channelsdir = '';
47231
47232    /** Name of file used for locking the registry
47233     * @var string
47234     */
47235    var $lockfile = '';
47236
47237    /** File descriptor used during locking
47238     * @var resource
47239     */
47240    var $lock_fp = null;
47241
47242    /** Mode used during locking
47243     * @var int
47244     */
47245    var $lock_mode = 0; // XXX UNUSED
47246
47247    /** Cache of package information.  Structure:
47248     * array(
47249     *   'package' => array('id' => ... ),
47250     *   ... )
47251     * @var array
47252     */
47253    var $pkginfo_cache = array();
47254
47255    /** Cache of file map.  Structure:
47256     * array( '/path/to/file' => 'package', ... )
47257     * @var array
47258     */
47259    var $filemap_cache = array();
47260
47261    /**
47262     * @var false|PEAR_ChannelFile
47263     */
47264    var $_pearChannel;
47265
47266    /**
47267     * @var false|PEAR_ChannelFile
47268     */
47269    var $_peclChannel;
47270
47271    /**
47272     * @var false|PEAR_ChannelFile
47273     */
47274    var $_docChannel;
47275
47276    /**
47277     * @var PEAR_DependencyDB
47278     */
47279    var $_dependencyDB;
47280
47281    /**
47282     * @var PEAR_Config
47283     */
47284    var $_config;
47285
47286    /**
47287     * PEAR_Registry constructor.
47288     *
47289     * @param string (optional) PEAR install directory (for .php files)
47290     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if
47291     *        default values are not desired.  Only used the very first time a PEAR
47292     *        repository is initialized
47293     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if
47294     *        default values are not desired.  Only used the very first time a PEAR
47295     *        repository is initialized
47296     *
47297     * @access public
47298     */
47299    function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false,
47300                           $pecl_channel = false)
47301    {
47302        parent::PEAR();
47303        $this->setInstallDir($pear_install_dir);
47304        $this->_pearChannel = $pear_channel;
47305        $this->_peclChannel = $pecl_channel;
47306        $this->_config      = false;
47307    }
47308
47309    function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR)
47310    {
47311        $ds = DIRECTORY_SEPARATOR;
47312        $this->install_dir = $pear_install_dir;
47313        $this->channelsdir = $pear_install_dir.$ds.'.channels';
47314        $this->statedir    = $pear_install_dir.$ds.'.registry';
47315        $this->filemap     = $pear_install_dir.$ds.'.filemap';
47316        $this->lockfile    = $pear_install_dir.$ds.'.lock';
47317    }
47318
47319    function hasWriteAccess()
47320    {
47321        if (!file_exists($this->install_dir)) {
47322            $dir = $this->install_dir;
47323            while ($dir && $dir != '.') {
47324                $olddir = $dir;
47325                $dir    = dirname($dir);
47326                if ($dir != '.' && file_exists($dir)) {
47327                    if (is_writeable($dir)) {
47328                        return true;
47329                    }
47330
47331                    return false;
47332                }
47333
47334                if ($dir == $olddir) { // this can happen in safe mode
47335                    return @is_writable($dir);
47336                }
47337            }
47338
47339            return false;
47340        }
47341
47342        return is_writeable($this->install_dir);
47343    }
47344
47345    function setConfig(&$config, $resetInstallDir = true)
47346    {
47347        $this->_config = &$config;
47348        if ($resetInstallDir) {
47349            $this->setInstallDir($config->get('php_dir'));
47350        }
47351    }
47352
47353    function _initializeChannelDirs()
47354    {
47355        static $running = false;
47356        if (!$running) {
47357            $running = true;
47358            $ds = DIRECTORY_SEPARATOR;
47359            if (!is_dir($this->channelsdir) ||
47360                  !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
47361                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
47362                  !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
47363                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
47364                if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
47365                    $pear_channel = $this->_pearChannel;
47366                    if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) {
47367                        if (!class_exists('PEAR_ChannelFile')) {
47368                            require_once 'PEAR/ChannelFile.php';
47369                        }
47370
47371                        $pear_channel = new PEAR_ChannelFile;
47372                        $pear_channel->setAlias('pear');
47373                        $pear_channel->setServer('pear.php.net');
47374                        $pear_channel->setSummary('PHP Extension and Application Repository');
47375                        $pear_channel->setDefaultPEARProtocols();
47376                        $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
47377                        $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
47378                        $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
47379                        //$pear_channel->setBaseURL('REST1.4', 'http://pear.php.net/rest/');
47380                    } else {
47381                        $pear_channel->setServer('pear.php.net');
47382                        $pear_channel->setAlias('pear');
47383                    }
47384
47385                    $pear_channel->validate();
47386                    $this->_addChannel($pear_channel);
47387                }
47388
47389                if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) {
47390                    $pecl_channel = $this->_peclChannel;
47391                    if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) {
47392                        if (!class_exists('PEAR_ChannelFile')) {
47393                            require_once 'PEAR/ChannelFile.php';
47394                        }
47395
47396                        $pecl_channel = new PEAR_ChannelFile;
47397                        $pecl_channel->setAlias('pecl');
47398                        $pecl_channel->setServer('pecl.php.net');
47399                        $pecl_channel->setSummary('PHP Extension Community Library');
47400                        $pecl_channel->setDefaultPEARProtocols();
47401                        $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
47402                        $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
47403                        $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
47404                    } else {
47405                        $pecl_channel->setServer('pecl.php.net');
47406                        $pecl_channel->setAlias('pecl');
47407                    }
47408
47409                    $pecl_channel->validate();
47410                    $this->_addChannel($pecl_channel);
47411                }
47412
47413                if (!file_exists($this->channelsdir . $ds . 'doc.php.net.reg')) {
47414                    $doc_channel = $this->_docChannel;
47415                    if (!is_a($doc_channel, 'PEAR_ChannelFile') || !$doc_channel->validate()) {
47416                        if (!class_exists('PEAR_ChannelFile')) {
47417                            require_once 'PEAR/ChannelFile.php';
47418                        }
47419
47420                        $doc_channel = new PEAR_ChannelFile;
47421                        $doc_channel->setAlias('phpdocs');
47422                        $doc_channel->setServer('doc.php.net');
47423                        $doc_channel->setSummary('PHP Documentation Team');
47424                        $doc_channel->setDefaultPEARProtocols();
47425                        $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
47426                        $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
47427                        $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
47428                    } else {
47429                        $doc_channel->setServer('doc.php.net');
47430                        $doc_channel->setAlias('doc');
47431                    }
47432
47433                    $doc_channel->validate();
47434                    $this->_addChannel($doc_channel);
47435                }
47436
47437                if (!file_exists($this->channelsdir . $ds . '__uri.reg')) {
47438                    if (!class_exists('PEAR_ChannelFile')) {
47439                        require_once 'PEAR/ChannelFile.php';
47440                    }
47441
47442                    $private = new PEAR_ChannelFile;
47443                    $private->setName('__uri');
47444                    $private->setDefaultPEARProtocols();
47445                    $private->setBaseURL('REST1.0', '****');
47446                    $private->setSummary('Pseudo-channel for static packages');
47447                    $this->_addChannel($private);
47448                }
47449                $this->_rebuildFileMap();
47450            }
47451
47452            $running = false;
47453        }
47454    }
47455
47456    function _initializeDirs()
47457    {
47458        $ds = DIRECTORY_SEPARATOR;
47459        // XXX Compatibility code should be removed in the future
47460        // rename all registry files if any to lowercase
47461        if (!OS_WINDOWS && file_exists($this->statedir) && is_dir($this->statedir) &&
47462              $handle = opendir($this->statedir)) {
47463            $dest = $this->statedir . $ds;
47464            while (false !== ($file = readdir($handle))) {
47465                if (preg_match('/^.*[A-Z].*\.reg\\z/', $file)) {
47466                    rename($dest . $file, $dest . strtolower($file));
47467                }
47468            }
47469            closedir($handle);
47470        }
47471
47472        $this->_initializeChannelDirs();
47473        if (!file_exists($this->filemap)) {
47474            $this->_rebuildFileMap();
47475        }
47476        $this->_initializeDepDB();
47477    }
47478
47479    function _initializeDepDB()
47480    {
47481        if (!isset($this->_dependencyDB)) {
47482            static $initializing = false;
47483            if (!$initializing) {
47484                $initializing = true;
47485                if (!$this->_config) { // never used?
47486                    $file = OS_WINDOWS ? 'pear.ini' : '.pearrc';
47487                    $this->_config = &new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR .
47488                        $file);
47489                    $this->_config->setRegistry($this);
47490                    $this->_config->set('php_dir', $this->install_dir);
47491                }
47492
47493                $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
47494                if (PEAR::isError($this->_dependencyDB)) {
47495                    // attempt to recover by removing the dep db
47496                    if (file_exists($this->_config->get('php_dir', null, 'pear.php.net') .
47497                        DIRECTORY_SEPARATOR . '.depdb')) {
47498                        @unlink($this->_config->get('php_dir', null, 'pear.php.net') .
47499                            DIRECTORY_SEPARATOR . '.depdb');
47500                    }
47501
47502                    $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
47503                    if (PEAR::isError($this->_dependencyDB)) {
47504                        echo $this->_dependencyDB->getMessage();
47505                        echo 'Unrecoverable error';
47506                        exit(1);
47507                    }
47508                }
47509
47510                $initializing = false;
47511            }
47512        }
47513    }
47514
47515    /**
47516     * PEAR_Registry destructor.  Makes sure no locks are forgotten.
47517     *
47518     * @access private
47519     */
47520    function _PEAR_Registry()
47521    {
47522        parent::_PEAR();
47523        if (is_resource($this->lock_fp)) {
47524            $this->_unlock();
47525        }
47526    }
47527
47528    /**
47529     * Make sure the directory where we keep registry files exists.
47530     *
47531     * @return bool TRUE if directory exists, FALSE if it could not be
47532     * created
47533     *
47534     * @access private
47535     */
47536    function _assertStateDir($channel = false)
47537    {
47538        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
47539            return $this->_assertChannelStateDir($channel);
47540        }
47541
47542        static $init = false;
47543        if (!file_exists($this->statedir)) {
47544            if (!$this->hasWriteAccess()) {
47545                return false;
47546            }
47547
47548            require_once 'System.php';
47549            if (!System::mkdir(array('-p', $this->statedir))) {
47550                return $this->raiseError("could not create directory '{$this->statedir}'");
47551            }
47552            $init = true;
47553        } elseif (!is_dir($this->statedir)) {
47554            return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' .
47555                'it already exists and is not a directory');
47556        }
47557
47558        $ds = DIRECTORY_SEPARATOR;
47559        if (!file_exists($this->channelsdir)) {
47560            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
47561                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
47562                  !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
47563                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
47564                $init = true;
47565            }
47566        } elseif (!is_dir($this->channelsdir)) {
47567            return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' .
47568                'it already exists and is not a directory');
47569        }
47570
47571        if ($init) {
47572            static $running = false;
47573            if (!$running) {
47574                $running = true;
47575                $this->_initializeDirs();
47576                $running = false;
47577                $init = false;
47578            }
47579        } else {
47580            $this->_initializeDepDB();
47581        }
47582
47583        return true;
47584    }
47585
47586    /**
47587     * Make sure the directory where we keep registry files exists for a non-standard channel.
47588     *
47589     * @param string channel name
47590     * @return bool TRUE if directory exists, FALSE if it could not be
47591     * created
47592     *
47593     * @access private
47594     */
47595    function _assertChannelStateDir($channel)
47596    {
47597        $ds = DIRECTORY_SEPARATOR;
47598        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
47599            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
47600                $this->_initializeChannelDirs();
47601            }
47602            return $this->_assertStateDir($channel);
47603        }
47604
47605        $channelDir = $this->_channelDirectoryName($channel);
47606        if (!is_dir($this->channelsdir) ||
47607              !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
47608            $this->_initializeChannelDirs();
47609        }
47610
47611        if (!file_exists($channelDir)) {
47612            if (!$this->hasWriteAccess()) {
47613                return false;
47614            }
47615
47616            require_once 'System.php';
47617            if (!System::mkdir(array('-p', $channelDir))) {
47618                return $this->raiseError("could not create directory '" . $channelDir .
47619                    "'");
47620            }
47621        } elseif (!is_dir($channelDir)) {
47622            return $this->raiseError("could not create directory '" . $channelDir .
47623                "', already exists and is not a directory");
47624        }
47625
47626        return true;
47627    }
47628
47629    /**
47630     * Make sure the directory where we keep registry files for channels exists
47631     *
47632     * @return bool TRUE if directory exists, FALSE if it could not be
47633     * created
47634     *
47635     * @access private
47636     */
47637    function _assertChannelDir()
47638    {
47639        if (!file_exists($this->channelsdir)) {
47640            if (!$this->hasWriteAccess()) {
47641                return false;
47642            }
47643
47644            require_once 'System.php';
47645            if (!System::mkdir(array('-p', $this->channelsdir))) {
47646                return $this->raiseError("could not create directory '{$this->channelsdir}'");
47647            }
47648        } elseif (!is_dir($this->channelsdir)) {
47649            return $this->raiseError("could not create directory '{$this->channelsdir}" .
47650                "', it already exists and is not a directory");
47651        }
47652
47653        if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
47654            if (!$this->hasWriteAccess()) {
47655                return false;
47656            }
47657
47658            require_once 'System.php';
47659            if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) {
47660                return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'");
47661            }
47662        } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
47663            return $this->raiseError("could not create directory '{$this->channelsdir}" .
47664                "/.alias', it already exists and is not a directory");
47665        }
47666
47667        return true;
47668    }
47669
47670    /**
47671     * Get the name of the file where data for a given package is stored.
47672     *
47673     * @param string channel name, or false if this is a PEAR package
47674     * @param string package name
47675     *
47676     * @return string registry file name
47677     *
47678     * @access public
47679     */
47680    function _packageFileName($package, $channel = false)
47681    {
47682        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
47683            return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR .
47684                strtolower($package) . '.reg';
47685        }
47686
47687        return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
47688    }
47689
47690    /**
47691     * Get the name of the file where data for a given channel is stored.
47692     * @param string channel name
47693     * @return string registry file name
47694     */
47695    function _channelFileName($channel, $noaliases = false)
47696    {
47697        if (!$noaliases) {
47698            if (file_exists($this->_getChannelAliasFileName($channel))) {
47699                $channel = implode('', file($this->_getChannelAliasFileName($channel)));
47700            }
47701        }
47702        return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_',
47703            strtolower($channel)) . '.reg';
47704    }
47705
47706    /**
47707     * @param string
47708     * @return string
47709     */
47710    function _getChannelAliasFileName($alias)
47711    {
47712        return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' .
47713              DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt';
47714    }
47715
47716    /**
47717     * Get the name of a channel from its alias
47718     */
47719    function _getChannelFromAlias($channel)
47720    {
47721        if (!$this->_channelExists($channel)) {
47722            if ($channel == 'pear.php.net') {
47723                return 'pear.php.net';
47724            }
47725
47726            if ($channel == 'pecl.php.net') {
47727                return 'pecl.php.net';
47728            }
47729
47730            if ($channel == 'doc.php.net') {
47731                return 'doc.php.net';
47732            }
47733
47734            if ($channel == '__uri') {
47735                return '__uri';
47736            }
47737
47738            return false;
47739        }
47740
47741        $channel = strtolower($channel);
47742        if (file_exists($this->_getChannelAliasFileName($channel))) {
47743            // translate an alias to an actual channel
47744            return implode('', file($this->_getChannelAliasFileName($channel)));
47745        }
47746
47747        return $channel;
47748    }
47749
47750    /**
47751     * Get the alias of a channel from its alias or its name
47752     */
47753    function _getAlias($channel)
47754    {
47755        if (!$this->_channelExists($channel)) {
47756            if ($channel == 'pear.php.net') {
47757                return 'pear';
47758            }
47759
47760            if ($channel == 'pecl.php.net') {
47761                return 'pecl';
47762            }
47763
47764            if ($channel == 'doc.php.net') {
47765                return 'phpdocs';
47766            }
47767
47768            return false;
47769        }
47770
47771        $channel = $this->_getChannel($channel);
47772        if (PEAR::isError($channel)) {
47773            return $channel;
47774        }
47775
47776        return $channel->getAlias();
47777    }
47778
47779    /**
47780     * Get the name of the file where data for a given package is stored.
47781     *
47782     * @param string channel name, or false if this is a PEAR package
47783     * @param string package name
47784     *
47785     * @return string registry file name
47786     *
47787     * @access public
47788     */
47789    function _channelDirectoryName($channel)
47790    {
47791        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
47792            return $this->statedir;
47793        }
47794
47795        $ch = $this->_getChannelFromAlias($channel);
47796        if (!$ch) {
47797            $ch = $channel;
47798        }
47799
47800        return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' .
47801            str_replace('/', '_', $ch));
47802    }
47803
47804    function _openPackageFile($package, $mode, $channel = false)
47805    {
47806        if (!$this->_assertStateDir($channel)) {
47807            return null;
47808        }
47809
47810        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
47811            return null;
47812        }
47813
47814        $file = $this->_packageFileName($package, $channel);
47815        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
47816            return null;
47817        }
47818
47819        $fp = @fopen($file, $mode);
47820        if (!$fp) {
47821            return null;
47822        }
47823
47824        return $fp;
47825    }
47826
47827    function _closePackageFile($fp)
47828    {
47829        fclose($fp);
47830    }
47831
47832    function _openChannelFile($channel, $mode)
47833    {
47834        if (!$this->_assertChannelDir()) {
47835            return null;
47836        }
47837
47838        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
47839            return null;
47840        }
47841
47842        $file = $this->_channelFileName($channel);
47843        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
47844            return null;
47845        }
47846
47847        $fp = @fopen($file, $mode);
47848        if (!$fp) {
47849            return null;
47850        }
47851
47852        return $fp;
47853    }
47854
47855    function _closeChannelFile($fp)
47856    {
47857        fclose($fp);
47858    }
47859
47860    function _rebuildFileMap()
47861    {
47862        if (!class_exists('PEAR_Installer_Role')) {
47863            require_once 'PEAR/Installer/Role.php';
47864        }
47865
47866        $channels = $this->_listAllPackages();
47867        $files = array();
47868        foreach ($channels as $channel => $packages) {
47869            foreach ($packages as $package) {
47870                $version = $this->_packageInfo($package, 'version', $channel);
47871                $filelist = $this->_packageInfo($package, 'filelist', $channel);
47872                if (!is_array($filelist)) {
47873                    continue;
47874                }
47875
47876                foreach ($filelist as $name => $attrs) {
47877                    if (isset($attrs['attribs'])) {
47878                        $attrs = $attrs['attribs'];
47879                    }
47880
47881                    // it is possible for conflicting packages in different channels to
47882                    // conflict with data files/doc files
47883                    if ($name == 'dirtree') {
47884                        continue;
47885                    }
47886
47887                    if (isset($attrs['role']) && !in_array($attrs['role'],
47888                          PEAR_Installer_Role::getInstallableRoles())) {
47889                        // these are not installed
47890                        continue;
47891                    }
47892
47893                    if (isset($attrs['role']) && !in_array($attrs['role'],
47894                          PEAR_Installer_Role::getBaseinstallRoles())) {
47895                        $attrs['baseinstalldir'] = $package;
47896                    }
47897
47898                    if (isset($attrs['baseinstalldir'])) {
47899                        $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
47900                    } else {
47901                        $file = $name;
47902                    }
47903
47904                    $file = preg_replace(',^/+,', '', $file);
47905                    if ($channel != 'pear.php.net') {
47906                        if (!isset($files[$attrs['role']])) {
47907                            $files[$attrs['role']] = array();
47908                        }
47909                        $files[$attrs['role']][$file] = array(strtolower($channel),
47910                            strtolower($package));
47911                    } else {
47912                        if (!isset($files[$attrs['role']])) {
47913                            $files[$attrs['role']] = array();
47914                        }
47915                        $files[$attrs['role']][$file] = strtolower($package);
47916                    }
47917                }
47918            }
47919        }
47920
47921
47922        $this->_assertStateDir();
47923        if (!$this->hasWriteAccess()) {
47924            return false;
47925        }
47926
47927        $fp = @fopen($this->filemap, 'wb');
47928        if (!$fp) {
47929            return false;
47930        }
47931
47932        $this->filemap_cache = $files;
47933        fwrite($fp, serialize($files));
47934        fclose($fp);
47935        return true;
47936    }
47937
47938    function _readFileMap()
47939    {
47940        if (!file_exists($this->filemap)) {
47941            return array();
47942        }
47943
47944        $fp = @fopen($this->filemap, 'r');
47945        if (!$fp) {
47946            return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg);
47947        }
47948
47949        clearstatcache();
47950        $rt = get_magic_quotes_runtime();
47951        set_magic_quotes_runtime(0);
47952        $fsize = filesize($this->filemap);
47953        fclose($fp);
47954        $data = file_get_contents($this->filemap);
47955        set_magic_quotes_runtime($rt);
47956        $tmp = unserialize($data);
47957        if (!$tmp && $fsize > 7) {
47958            return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
47959        }
47960
47961        $this->filemap_cache = $tmp;
47962        return true;
47963    }
47964
47965    /**
47966     * Lock the registry.
47967     *
47968     * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
47969     *                See flock manual for more information.
47970     *
47971     * @return bool TRUE on success, FALSE if locking failed, or a
47972     *              PEAR error if some other error occurs (such as the
47973     *              lock file not being writable).
47974     *
47975     * @access private
47976     */
47977    function _lock($mode = LOCK_EX)
47978    {
47979        if (stristr(php_uname(), 'Windows 9')) {
47980            return true;
47981        }
47982
47983        if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
47984            // XXX does not check type of lock (LOCK_SH/LOCK_EX)
47985            return true;
47986        }
47987
47988        if (!$this->_assertStateDir()) {
47989            if ($mode == LOCK_EX) {
47990                return $this->raiseError('Registry directory is not writeable by the current user');
47991            }
47992
47993            return true;
47994        }
47995
47996        $open_mode = 'w';
47997        // XXX People reported problems with LOCK_SH and 'w'
47998        if ($mode === LOCK_SH || $mode === LOCK_UN) {
47999            if (!file_exists($this->lockfile)) {
48000                touch($this->lockfile);
48001            }
48002            $open_mode = 'r';
48003        }
48004
48005        if (!is_resource($this->lock_fp)) {
48006            $this->lock_fp = @fopen($this->lockfile, $open_mode);
48007        }
48008
48009        if (!is_resource($this->lock_fp)) {
48010            $this->lock_fp = null;
48011            return $this->raiseError("could not create lock file" .
48012                                     (isset($php_errormsg) ? ": " . $php_errormsg : ""));
48013        }
48014
48015        if (!(int)flock($this->lock_fp, $mode)) {
48016            switch ($mode) {
48017                case LOCK_SH: $str = 'shared';    break;
48018                case LOCK_EX: $str = 'exclusive'; break;
48019                case LOCK_UN: $str = 'unlock';    break;
48020                default:      $str = 'unknown';   break;
48021            }
48022
48023            //is resource at this point, close it on error.
48024            fclose($this->lock_fp);
48025            $this->lock_fp = null;
48026            return $this->raiseError("could not acquire $str lock ($this->lockfile)",
48027                                     PEAR_REGISTRY_ERROR_LOCK);
48028        }
48029
48030        return true;
48031    }
48032
48033    function _unlock()
48034    {
48035        $ret = $this->_lock(LOCK_UN);
48036        if (is_resource($this->lock_fp)) {
48037            fclose($this->lock_fp);
48038        }
48039
48040        $this->lock_fp = null;
48041        return $ret;
48042    }
48043
48044    function _packageExists($package, $channel = false)
48045    {
48046        return file_exists($this->_packageFileName($package, $channel));
48047    }
48048
48049    /**
48050     * Determine whether a channel exists in the registry
48051     *
48052     * @param string Channel name
48053     * @param bool if true, then aliases will be ignored
48054     * @return boolean
48055     */
48056    function _channelExists($channel, $noaliases = false)
48057    {
48058        $a = file_exists($this->_channelFileName($channel, $noaliases));
48059        if (!$a && $channel == 'pear.php.net') {
48060            return true;
48061        }
48062
48063        if (!$a && $channel == 'pecl.php.net') {
48064            return true;
48065        }
48066
48067        if (!$a && $channel == 'doc.php.net') {
48068            return true;
48069        }
48070
48071        return $a;
48072    }
48073
48074    /**
48075     * Determine whether a mirror exists within the deafult channel in the registry
48076     *
48077     * @param string Channel name
48078     * @param string Mirror name
48079     *
48080     * @return boolean
48081     */
48082    function _mirrorExists($channel, $mirror)
48083    {
48084        $data = $this->_channelInfo($channel);
48085        if (!isset($data['servers']['mirror'])) {
48086            return false;
48087        }
48088
48089        foreach ($data['servers']['mirror'] as $m) {
48090            if ($m['attribs']['host'] == $mirror) {
48091                return true;
48092            }
48093        }
48094
48095        return false;
48096    }
48097
48098    /**
48099     * @param PEAR_ChannelFile Channel object
48100     * @param donotuse
48101     * @param string Last-Modified HTTP tag from remote request
48102     * @return boolean|PEAR_Error True on creation, false if it already exists
48103     */
48104    function _addChannel($channel, $update = false, $lastmodified = false)
48105    {
48106        if (!is_a($channel, 'PEAR_ChannelFile')) {
48107            return false;
48108        }
48109
48110        if (!$channel->validate()) {
48111            return false;
48112        }
48113
48114        if (file_exists($this->_channelFileName($channel->getName()))) {
48115            if (!$update) {
48116                return false;
48117            }
48118
48119            $checker = $this->_getChannel($channel->getName());
48120            if (PEAR::isError($checker)) {
48121                return $checker;
48122            }
48123
48124            if ($channel->getAlias() != $checker->getAlias()) {
48125                if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) {
48126                    @unlink($this->_getChannelAliasFileName($checker->getAlias()));
48127                }
48128            }
48129        } else {
48130            if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net', 'doc.php.net'))) {
48131                return false;
48132            }
48133        }
48134
48135        $ret = $this->_assertChannelDir();
48136        if (PEAR::isError($ret)) {
48137            return $ret;
48138        }
48139
48140        $ret = $this->_assertChannelStateDir($channel->getName());
48141        if (PEAR::isError($ret)) {
48142            return $ret;
48143        }
48144
48145        if ($channel->getAlias() != $channel->getName()) {
48146            if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) &&
48147                  $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) {
48148                $channel->setAlias($channel->getName());
48149            }
48150
48151            if (!$this->hasWriteAccess()) {
48152                return false;
48153            }
48154
48155            $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w');
48156            if (!$fp) {
48157                return false;
48158            }
48159
48160            fwrite($fp, $channel->getName());
48161            fclose($fp);
48162        }
48163
48164        if (!$this->hasWriteAccess()) {
48165            return false;
48166        }
48167
48168        $fp = @fopen($this->_channelFileName($channel->getName()), 'wb');
48169        if (!$fp) {
48170            return false;
48171        }
48172
48173        $info = $channel->toArray();
48174        if ($lastmodified) {
48175            $info['_lastmodified'] = $lastmodified;
48176        } else {
48177            $info['_lastmodified'] = date('r');
48178        }
48179
48180        fwrite($fp, serialize($info));
48181        fclose($fp);
48182        return true;
48183    }
48184
48185    /**
48186     * Deletion fails if there are any packages installed from the channel
48187     * @param string|PEAR_ChannelFile channel name
48188     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
48189     */
48190    function _deleteChannel($channel)
48191    {
48192        if (!is_string($channel)) {
48193            if (!is_a($channel, 'PEAR_ChannelFile')) {
48194                return false;
48195            }
48196
48197            if (!$channel->validate()) {
48198                return false;
48199            }
48200            $channel = $channel->getName();
48201        }
48202
48203        if ($this->_getChannelFromAlias($channel) == '__uri') {
48204            return false;
48205        }
48206
48207        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
48208            return false;
48209        }
48210
48211        if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
48212            return false;
48213        }
48214
48215        if (!$this->_channelExists($channel)) {
48216            return false;
48217        }
48218
48219        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
48220            return false;
48221        }
48222
48223        $channel = $this->_getChannelFromAlias($channel);
48224        if ($channel == 'pear.php.net') {
48225            return false;
48226        }
48227
48228        $test = $this->_listChannelPackages($channel);
48229        if (count($test)) {
48230            return false;
48231        }
48232
48233        $test = @rmdir($this->_channelDirectoryName($channel));
48234        if (!$test) {
48235            return false;
48236        }
48237
48238        $file = $this->_getChannelAliasFileName($this->_getAlias($channel));
48239        if (file_exists($file)) {
48240            $test = @unlink($file);
48241            if (!$test) {
48242                return false;
48243            }
48244        }
48245
48246        $file = $this->_channelFileName($channel);
48247        $ret = true;
48248        if (file_exists($file)) {
48249            $ret = @unlink($file);
48250        }
48251
48252        return $ret;
48253    }
48254
48255    /**
48256     * Determine whether a channel exists in the registry
48257     * @param string Channel Alias
48258     * @return boolean
48259     */
48260    function _isChannelAlias($alias)
48261    {
48262        return file_exists($this->_getChannelAliasFileName($alias));
48263    }
48264
48265    /**
48266     * @param string|null
48267     * @param string|null
48268     * @param string|null
48269     * @return array|null
48270     * @access private
48271     */
48272    function _packageInfo($package = null, $key = null, $channel = 'pear.php.net')
48273    {
48274        if ($package === null) {
48275            if ($channel === null) {
48276                $channels = $this->_listChannels();
48277                $ret = array();
48278                foreach ($channels as $channel) {
48279                    $channel = strtolower($channel);
48280                    $ret[$channel] = array();
48281                    $packages = $this->_listPackages($channel);
48282                    foreach ($packages as $package) {
48283                        $ret[$channel][] = $this->_packageInfo($package, null, $channel);
48284                    }
48285                }
48286
48287                return $ret;
48288            }
48289
48290            $ps = $this->_listPackages($channel);
48291            if (!count($ps)) {
48292                return array();
48293            }
48294            return array_map(array(&$this, '_packageInfo'),
48295                             $ps, array_fill(0, count($ps), null),
48296                             array_fill(0, count($ps), $channel));
48297        }
48298
48299        $fp = $this->_openPackageFile($package, 'r', $channel);
48300        if ($fp === null) {
48301            return null;
48302        }
48303
48304        $rt = get_magic_quotes_runtime();
48305        set_magic_quotes_runtime(0);
48306        clearstatcache();
48307        $this->_closePackageFile($fp);
48308        $data = file_get_contents($this->_packageFileName($package, $channel));
48309        set_magic_quotes_runtime($rt);
48310        $data = unserialize($data);
48311        if ($key === null) {
48312            return $data;
48313        }
48314
48315        // compatibility for package.xml version 2.0
48316        if (isset($data['old'][$key])) {
48317            return $data['old'][$key];
48318        }
48319
48320        if (isset($data[$key])) {
48321            return $data[$key];
48322        }
48323
48324        return null;
48325    }
48326
48327    /**
48328     * @param string Channel name
48329     * @param bool whether to strictly retrieve info of channels, not just aliases
48330     * @return array|null
48331     */
48332    function _channelInfo($channel, $noaliases = false)
48333    {
48334        if (!$this->_channelExists($channel, $noaliases)) {
48335            return null;
48336        }
48337
48338        $fp = $this->_openChannelFile($channel, 'r');
48339        if ($fp === null) {
48340            return null;
48341        }
48342
48343        $rt = get_magic_quotes_runtime();
48344        set_magic_quotes_runtime(0);
48345        clearstatcache();
48346        $this->_closeChannelFile($fp);
48347        $data = file_get_contents($this->_channelFileName($channel));
48348        set_magic_quotes_runtime($rt);
48349        $data = unserialize($data);
48350        return $data;
48351    }
48352
48353    function _listChannels()
48354    {
48355        $channellist = array();
48356        if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) {
48357            return array('pear.php.net', 'pecl.php.net', 'doc.php.net', '__uri');
48358        }
48359
48360        $dp = opendir($this->channelsdir);
48361        while ($ent = readdir($dp)) {
48362            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
48363                continue;
48364            }
48365
48366            if ($ent == '__uri.reg') {
48367                $channellist[] = '__uri';
48368                continue;
48369            }
48370
48371            $channellist[] = str_replace('_', '/', substr($ent, 0, -4));
48372        }
48373
48374        closedir($dp);
48375        if (!in_array('pear.php.net', $channellist)) {
48376            $channellist[] = 'pear.php.net';
48377        }
48378
48379        if (!in_array('pecl.php.net', $channellist)) {
48380            $channellist[] = 'pecl.php.net';
48381        }
48382
48383        if (!in_array('doc.php.net', $channellist)) {
48384            $channellist[] = 'doc.php.net';
48385        }
48386
48387
48388        if (!in_array('__uri', $channellist)) {
48389            $channellist[] = '__uri';
48390        }
48391
48392        natsort($channellist);
48393        return $channellist;
48394    }
48395
48396    function _listPackages($channel = false)
48397    {
48398        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
48399            return $this->_listChannelPackages($channel);
48400        }
48401
48402        if (!file_exists($this->statedir) || !is_dir($this->statedir)) {
48403            return array();
48404        }
48405
48406        $pkglist = array();
48407        $dp = opendir($this->statedir);
48408        if (!$dp) {
48409            return $pkglist;
48410        }
48411
48412        while ($ent = readdir($dp)) {
48413            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
48414                continue;
48415            }
48416
48417            $pkglist[] = substr($ent, 0, -4);
48418        }
48419        closedir($dp);
48420        return $pkglist;
48421    }
48422
48423    function _listChannelPackages($channel)
48424    {
48425        $pkglist = array();
48426        if (!file_exists($this->_channelDirectoryName($channel)) ||
48427              !is_dir($this->_channelDirectoryName($channel))) {
48428            return array();
48429        }
48430
48431        $dp = opendir($this->_channelDirectoryName($channel));
48432        if (!$dp) {
48433            return $pkglist;
48434        }
48435
48436        while ($ent = readdir($dp)) {
48437            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
48438                continue;
48439            }
48440            $pkglist[] = substr($ent, 0, -4);
48441        }
48442
48443        closedir($dp);
48444        return $pkglist;
48445    }
48446
48447    function _listAllPackages()
48448    {
48449        $ret = array();
48450        foreach ($this->_listChannels() as $channel) {
48451            $ret[$channel] = $this->_listPackages($channel);
48452        }
48453
48454        return $ret;
48455    }
48456
48457    /**
48458     * Add an installed package to the registry
48459     * @param string package name
48460     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
48461     * @return bool success of saving
48462     * @access private
48463     */
48464    function _addPackage($package, $info)
48465    {
48466        if ($this->_packageExists($package)) {
48467            return false;
48468        }
48469
48470        $fp = $this->_openPackageFile($package, 'wb');
48471        if ($fp === null) {
48472            return false;
48473        }
48474
48475        $info['_lastmodified'] = time();
48476        fwrite($fp, serialize($info));
48477        $this->_closePackageFile($fp);
48478        if (isset($info['filelist'])) {
48479            $this->_rebuildFileMap();
48480        }
48481
48482        return true;
48483    }
48484
48485    /**
48486     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
48487     * @return bool
48488     * @access private
48489     */
48490    function _addPackage2($info)
48491    {
48492        if (!is_a($info, 'PEAR_PackageFile_v1') && !is_a($info, 'PEAR_PackageFile_v2')) {
48493            return false;
48494        }
48495
48496        if (!$info->validate()) {
48497            if (class_exists('PEAR_Common')) {
48498                $ui = PEAR_Frontend::singleton();
48499                if ($ui) {
48500                    foreach ($info->getValidationWarnings() as $err) {
48501                        $ui->log($err['message'], true);
48502                    }
48503                }
48504            }
48505            return false;
48506        }
48507
48508        $channel = $info->getChannel();
48509        $package = $info->getPackage();
48510        $save = $info;
48511        if ($this->_packageExists($package, $channel)) {
48512            return false;
48513        }
48514
48515        if (!$this->_channelExists($channel, true)) {
48516            return false;
48517        }
48518
48519        $info = $info->toArray(true);
48520        if (!$info) {
48521            return false;
48522        }
48523
48524        $fp = $this->_openPackageFile($package, 'wb', $channel);
48525        if ($fp === null) {
48526            return false;
48527        }
48528
48529        $info['_lastmodified'] = time();
48530        fwrite($fp, serialize($info));
48531        $this->_closePackageFile($fp);
48532        $this->_rebuildFileMap();
48533        return true;
48534    }
48535
48536    /**
48537     * @param string Package name
48538     * @param array parsed package.xml 1.0
48539     * @param bool this parameter is only here for BC.  Don't use it.
48540     * @access private
48541     */
48542    function _updatePackage($package, $info, $merge = true)
48543    {
48544        $oldinfo = $this->_packageInfo($package);
48545        if (empty($oldinfo)) {
48546            return false;
48547        }
48548
48549        $fp = $this->_openPackageFile($package, 'w');
48550        if ($fp === null) {
48551            return false;
48552        }
48553
48554        if (is_object($info)) {
48555            $info = $info->toArray();
48556        }
48557        $info['_lastmodified'] = time();
48558
48559        $newinfo = $info;
48560        if ($merge) {
48561            $info = array_merge($oldinfo, $info);
48562        } else {
48563            $diff = $info;
48564        }
48565
48566        fwrite($fp, serialize($info));
48567        $this->_closePackageFile($fp);
48568        if (isset($newinfo['filelist'])) {
48569            $this->_rebuildFileMap();
48570        }
48571
48572        return true;
48573    }
48574
48575    /**
48576     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
48577     * @return bool
48578     * @access private
48579     */
48580    function _updatePackage2($info)
48581    {
48582        if (!$this->_packageExists($info->getPackage(), $info->getChannel())) {
48583            return false;
48584        }
48585
48586        $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel());
48587        if ($fp === null) {
48588            return false;
48589        }
48590
48591        $save = $info;
48592        $info = $save->getArray(true);
48593        $info['_lastmodified'] = time();
48594        fwrite($fp, serialize($info));
48595        $this->_closePackageFile($fp);
48596        $this->_rebuildFileMap();
48597        return true;
48598    }
48599
48600    /**
48601     * @param string Package name
48602     * @param string Channel name
48603     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
48604     * @access private
48605     */
48606    function &_getPackage($package, $channel = 'pear.php.net')
48607    {
48608        $info = $this->_packageInfo($package, null, $channel);
48609        if ($info === null) {
48610            return $info;
48611        }
48612
48613        $a = $this->_config;
48614        if (!$a) {
48615            $this->_config = &new PEAR_Config;
48616            $this->_config->set('php_dir', $this->statedir);
48617        }
48618
48619        if (!class_exists('PEAR_PackageFile')) {
48620            require_once 'PEAR/PackageFile.php';
48621        }
48622
48623        $pkg = &new PEAR_PackageFile($this->_config);
48624        $pf = &$pkg->fromArray($info);
48625        return $pf;
48626    }
48627
48628    /**
48629     * @param string channel name
48630     * @param bool whether to strictly retrieve channel names
48631     * @return PEAR_ChannelFile|PEAR_Error
48632     * @access private
48633     */
48634    function &_getChannel($channel, $noaliases = false)
48635    {
48636        $ch = false;
48637        if ($this->_channelExists($channel, $noaliases)) {
48638            $chinfo = $this->_channelInfo($channel, $noaliases);
48639            if ($chinfo) {
48640                if (!class_exists('PEAR_ChannelFile')) {
48641                    require_once 'PEAR/ChannelFile.php';
48642                }
48643
48644                $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo);
48645            }
48646        }
48647
48648        if ($ch) {
48649            if ($ch->validate()) {
48650                return $ch;
48651            }
48652
48653            foreach ($ch->getErrors(true) as $err) {
48654                $message = $err['message'] . "\n";
48655            }
48656
48657            $ch = PEAR::raiseError($message);
48658            return $ch;
48659        }
48660
48661        if ($this->_getChannelFromAlias($channel) == 'pear.php.net') {
48662            // the registry is not properly set up, so use defaults
48663            if (!class_exists('PEAR_ChannelFile')) {
48664                require_once 'PEAR/ChannelFile.php';
48665            }
48666
48667            $pear_channel = new PEAR_ChannelFile;
48668            $pear_channel->setServer('pear.php.net');
48669            $pear_channel->setAlias('pear');
48670            $pear_channel->setSummary('PHP Extension and Application Repository');
48671            $pear_channel->setDefaultPEARProtocols();
48672            $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
48673            $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
48674            $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
48675            return $pear_channel;
48676        }
48677
48678        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
48679            // the registry is not properly set up, so use defaults
48680            if (!class_exists('PEAR_ChannelFile')) {
48681                require_once 'PEAR/ChannelFile.php';
48682            }
48683            $pear_channel = new PEAR_ChannelFile;
48684            $pear_channel->setServer('pecl.php.net');
48685            $pear_channel->setAlias('pecl');
48686            $pear_channel->setSummary('PHP Extension Community Library');
48687            $pear_channel->setDefaultPEARProtocols();
48688            $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
48689            $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
48690            $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
48691            return $pear_channel;
48692        }
48693
48694        if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
48695            // the registry is not properly set up, so use defaults
48696            if (!class_exists('PEAR_ChannelFile')) {
48697                require_once 'PEAR/ChannelFile.php';
48698            }
48699
48700            $doc_channel = new PEAR_ChannelFile;
48701            $doc_channel->setServer('doc.php.net');
48702            $doc_channel->setAlias('phpdocs');
48703            $doc_channel->setSummary('PHP Documentation Team');
48704            $doc_channel->setDefaultPEARProtocols();
48705            $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
48706            $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
48707            $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
48708            return $doc_channel;
48709        }
48710
48711
48712        if ($this->_getChannelFromAlias($channel) == '__uri') {
48713            // the registry is not properly set up, so use defaults
48714            if (!class_exists('PEAR_ChannelFile')) {
48715                require_once 'PEAR/ChannelFile.php';
48716            }
48717
48718            $private = new PEAR_ChannelFile;
48719            $private->setName('__uri');
48720            $private->setDefaultPEARProtocols();
48721            $private->setBaseURL('REST1.0', '****');
48722            $private->setSummary('Pseudo-channel for static packages');
48723            return $private;
48724        }
48725
48726        return $ch;
48727    }
48728
48729    /**
48730     * @param string Package name
48731     * @param string Channel name
48732     * @return bool
48733     */
48734    function packageExists($package, $channel = 'pear.php.net')
48735    {
48736        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
48737            return $e;
48738        }
48739        $ret = $this->_packageExists($package, $channel);
48740        $this->_unlock();
48741        return $ret;
48742    }
48743
48744    // }}}
48745
48746    // {{{ channelExists()
48747
48748    /**
48749     * @param string channel name
48750     * @param bool if true, then aliases will be ignored
48751     * @return bool
48752     */
48753    function channelExists($channel, $noaliases = false)
48754    {
48755        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
48756            return $e;
48757        }
48758        $ret = $this->_channelExists($channel, $noaliases);
48759        $this->_unlock();
48760        return $ret;
48761    }
48762
48763    // }}}
48764
48765    /**
48766     * @param string channel name mirror is in
48767     * @param string mirror name
48768     *
48769     * @return bool
48770     */
48771    function mirrorExists($channel, $mirror)
48772    {
48773        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
48774            return $e;
48775        }
48776
48777        $ret = $this->_mirrorExists($channel, $mirror);
48778        $this->_unlock();
48779        return $ret;
48780    }
48781
48782    // {{{ isAlias()
48783
48784    /**
48785     * Determines whether the parameter is an alias of a channel
48786     * @param string
48787     * @return bool
48788     */
48789    function isAlias($alias)
48790    {
48791        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
48792            return $e;
48793        }
48794        $ret = $this->_isChannelAlias($alias);
48795        $this->_unlock();
48796        return $ret;
48797    }
48798
48799    // }}}
48800    // {{{ packageInfo()
48801
48802    /**
48803     * @param string|null
48804     * @param string|null
48805     * @param string
48806     * @return array|null
48807     */
48808    function packageInfo($package = null, $key = null, $channel = 'pear.php.net')
48809    {
48810        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
48811            return $e;
48812        }
48813        $ret = $this->_packageInfo($package, $key, $channel);
48814        $this->_unlock();
48815        return $ret;
48816    }
48817
48818    // }}}
48819    // {{{ channelInfo()
48820
48821    /**
48822     * Retrieve a raw array of channel data.
48823     *
48824     * Do not use this, instead use {@link getChannel()} for normal
48825     * operations.  Array structure is undefined in this method
48826     * @param string channel name
48827     * @param bool whether to strictly retrieve information only on non-aliases
48828     * @return array|null|PEAR_Error
48829     */
48830    function channelInfo($channel = null, $noaliases = false)
48831    {
48832        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
48833            return $e;
48834        }
48835        $ret = $this->_channelInfo($channel, $noaliases);
48836        $this->_unlock();
48837        return $ret;
48838    }
48839
48840    // }}}
48841
48842    /**
48843     * @param string
48844     */
48845    function channelName($channel)
48846    {
48847        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
48848            return $e;
48849        }
48850        $ret = $this->_getChannelFromAlias($channel);
48851        $this->_unlock();
48852        return $ret;
48853    }
48854
48855    /**
48856     * @param string
48857     */
48858    function channelAlias($channel)
48859    {
48860        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
48861            return $e;
48862        }
48863        $ret = $this->_getAlias($channel);
48864        $this->_unlock();
48865        return $ret;
48866    }
48867    // {{{ listPackages()
48868
48869    function listPackages($channel = false)
48870    {
48871        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
48872            return $e;
48873        }
48874        $ret = $this->_listPackages($channel);
48875        $this->_unlock();
48876        return $ret;
48877    }
48878
48879    // }}}
48880    // {{{ listAllPackages()
48881
48882    function listAllPackages()
48883    {
48884        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
48885            return $e;
48886        }
48887        $ret = $this->_listAllPackages();
48888        $this->_unlock();
48889        return $ret;
48890    }
48891
48892    // }}}
48893    // {{{ listChannel()
48894
48895    function listChannels()
48896    {
48897        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
48898            return $e;
48899        }
48900        $ret = $this->_listChannels();
48901        $this->_unlock();
48902        return $ret;
48903    }
48904
48905    // }}}
48906    // {{{ addPackage()
48907
48908    /**
48909     * Add an installed package to the registry
48910     * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object
48911     *               that will be passed to {@link addPackage2()}
48912     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
48913     * @return bool success of saving
48914     */
48915    function addPackage($package, $info)
48916    {
48917        if (is_object($info)) {
48918            return $this->addPackage2($info);
48919        }
48920        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
48921            return $e;
48922        }
48923        $ret = $this->_addPackage($package, $info);
48924        $this->_unlock();
48925        if ($ret) {
48926            if (!class_exists('PEAR_PackageFile_v1')) {
48927                require_once 'PEAR/PackageFile/v1.php';
48928            }
48929            $pf = new PEAR_PackageFile_v1;
48930            $pf->setConfig($this->_config);
48931            $pf->fromArray($info);
48932            $this->_dependencyDB->uninstallPackage($pf);
48933            $this->_dependencyDB->installPackage($pf);
48934        }
48935        return $ret;
48936    }
48937
48938    // }}}
48939    // {{{ addPackage2()
48940
48941    function addPackage2($info)
48942    {
48943        if (!is_object($info)) {
48944            return $this->addPackage($info['package'], $info);
48945        }
48946        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
48947            return $e;
48948        }
48949        $ret = $this->_addPackage2($info);
48950        $this->_unlock();
48951        if ($ret) {
48952            $this->_dependencyDB->uninstallPackage($info);
48953            $this->_dependencyDB->installPackage($info);
48954        }
48955        return $ret;
48956    }
48957
48958    // }}}
48959    // {{{ updateChannel()
48960
48961    /**
48962     * For future expandibility purposes, separate this
48963     * @param PEAR_ChannelFile
48964     */
48965    function updateChannel($channel, $lastmodified = null)
48966    {
48967        if ($channel->getName() == '__uri') {
48968            return false;
48969        }
48970        return $this->addChannel($channel, $lastmodified, true);
48971    }
48972
48973    // }}}
48974    // {{{ deleteChannel()
48975
48976    /**
48977     * Deletion fails if there are any packages installed from the channel
48978     * @param string|PEAR_ChannelFile channel name
48979     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
48980     */
48981    function deleteChannel($channel)
48982    {
48983        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
48984            return $e;
48985        }
48986
48987        $ret = $this->_deleteChannel($channel);
48988        $this->_unlock();
48989        if ($ret && is_a($this->_config, 'PEAR_Config')) {
48990            $this->_config->setChannels($this->listChannels());
48991        }
48992
48993        return $ret;
48994    }
48995
48996    // }}}
48997    // {{{ addChannel()
48998
48999    /**
49000     * @param PEAR_ChannelFile Channel object
49001     * @param string Last-Modified header from HTTP for caching
49002     * @return boolean|PEAR_Error True on creation, false if it already exists
49003     */
49004    function addChannel($channel, $lastmodified = false, $update = false)
49005    {
49006        if (!is_a($channel, 'PEAR_ChannelFile') || !$channel->validate()) {
49007            return false;
49008        }
49009
49010        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
49011            return $e;
49012        }
49013
49014        $ret = $this->_addChannel($channel, $update, $lastmodified);
49015        $this->_unlock();
49016        if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) {
49017            $this->_config->setChannels($this->listChannels());
49018        }
49019
49020        return $ret;
49021    }
49022
49023    // }}}
49024    // {{{ deletePackage()
49025
49026    function deletePackage($package, $channel = 'pear.php.net')
49027    {
49028        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
49029            return $e;
49030        }
49031
49032        $file = $this->_packageFileName($package, $channel);
49033        $ret  = file_exists($file) ? @unlink($file) : false;
49034        $this->_rebuildFileMap();
49035        $this->_unlock();
49036        $p = array('channel' => $channel, 'package' => $package);
49037        $this->_dependencyDB->uninstallPackage($p);
49038        return $ret;
49039    }
49040
49041    // }}}
49042    // {{{ updatePackage()
49043
49044    function updatePackage($package, $info, $merge = true)
49045    {
49046        if (is_object($info)) {
49047            return $this->updatePackage2($info, $merge);
49048        }
49049        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
49050            return $e;
49051        }
49052        $ret = $this->_updatePackage($package, $info, $merge);
49053        $this->_unlock();
49054        if ($ret) {
49055            if (!class_exists('PEAR_PackageFile_v1')) {
49056                require_once 'PEAR/PackageFile/v1.php';
49057            }
49058            $pf = new PEAR_PackageFile_v1;
49059            $pf->setConfig($this->_config);
49060            $pf->fromArray($this->packageInfo($package));
49061            $this->_dependencyDB->uninstallPackage($pf);
49062            $this->_dependencyDB->installPackage($pf);
49063        }
49064        return $ret;
49065    }
49066
49067    // }}}
49068    // {{{ updatePackage2()
49069
49070    function updatePackage2($info)
49071    {
49072
49073        if (!is_object($info)) {
49074            return $this->updatePackage($info['package'], $info, $merge);
49075        }
49076
49077        if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) {
49078            return false;
49079        }
49080
49081        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
49082            return $e;
49083        }
49084
49085        $ret = $this->_updatePackage2($info);
49086        $this->_unlock();
49087        if ($ret) {
49088            $this->_dependencyDB->uninstallPackage($info);
49089            $this->_dependencyDB->installPackage($info);
49090        }
49091
49092        return $ret;
49093    }
49094
49095    // }}}
49096    // {{{ getChannel()
49097    /**
49098     * @param string channel name
49099     * @param bool whether to strictly return raw channels (no aliases)
49100     * @return PEAR_ChannelFile|PEAR_Error
49101     */
49102    function &getChannel($channel, $noaliases = false)
49103    {
49104        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
49105            return $e;
49106        }
49107        $ret = &$this->_getChannel($channel, $noaliases);
49108        $this->_unlock();
49109        if (!$ret) {
49110            return PEAR::raiseError('Unknown channel: ' . $channel);
49111        }
49112        return $ret;
49113    }
49114
49115    // }}}
49116    // {{{ getPackage()
49117    /**
49118     * @param string package name
49119     * @param string channel name
49120     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
49121     */
49122    function &getPackage($package, $channel = 'pear.php.net')
49123    {
49124        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
49125            return $e;
49126        }
49127        $pf = &$this->_getPackage($package, $channel);
49128        $this->_unlock();
49129        return $pf;
49130    }
49131
49132    // }}}
49133
49134    /**
49135     * Get PEAR_PackageFile_v[1/2] objects representing the contents of
49136     * a dependency group that are installed.
49137     *
49138     * This is used at uninstall-time
49139     * @param array
49140     * @return array|false
49141     */
49142    function getInstalledGroup($group)
49143    {
49144        $ret = array();
49145        if (isset($group['package'])) {
49146            if (!isset($group['package'][0])) {
49147                $group['package'] = array($group['package']);
49148            }
49149            foreach ($group['package'] as $package) {
49150                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
49151                $p = &$this->getPackage($package['name'], $depchannel);
49152                if ($p) {
49153                    $save = &$p;
49154                    $ret[] = &$save;
49155                }
49156            }
49157        }
49158        if (isset($group['subpackage'])) {
49159            if (!isset($group['subpackage'][0])) {
49160                $group['subpackage'] = array($group['subpackage']);
49161            }
49162            foreach ($group['subpackage'] as $package) {
49163                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
49164                $p = &$this->getPackage($package['name'], $depchannel);
49165                if ($p) {
49166                    $save = &$p;
49167                    $ret[] = &$save;
49168                }
49169            }
49170        }
49171        if (!count($ret)) {
49172            return false;
49173        }
49174        return $ret;
49175    }
49176
49177    // {{{ getChannelValidator()
49178    /**
49179     * @param string channel name
49180     * @return PEAR_Validate|false
49181     */
49182    function &getChannelValidator($channel)
49183    {
49184        $chan = $this->getChannel($channel);
49185        if (PEAR::isError($chan)) {
49186            return $chan;
49187        }
49188        $val = $chan->getValidationObject();
49189        return $val;
49190    }
49191    // }}}
49192    // {{{ getChannels()
49193    /**
49194     * @param string channel name
49195     * @return array an array of PEAR_ChannelFile objects representing every installed channel
49196     */
49197    function &getChannels()
49198    {
49199        $ret = array();
49200        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
49201            return $e;
49202        }
49203        foreach ($this->_listChannels() as $channel) {
49204            $e = &$this->_getChannel($channel);
49205            if (!$e || PEAR::isError($e)) {
49206                continue;
49207            }
49208            $ret[] = $e;
49209        }
49210        $this->_unlock();
49211        return $ret;
49212    }
49213
49214    // }}}
49215    // {{{ checkFileMap()
49216
49217    /**
49218     * Test whether a file or set of files belongs to a package.
49219     *
49220     * If an array is passed in
49221     * @param string|array file path, absolute or relative to the pear
49222     *                     install dir
49223     * @param string|array name of PEAR package or array('package' => name, 'channel' =>
49224     *                     channel) of a package that will be ignored
49225     * @param string API version - 1.1 will exclude any files belonging to a package
49226     * @param array private recursion variable
49227     * @return array|false which package and channel the file belongs to, or an empty
49228     *                     string if the file does not belong to an installed package,
49229     *                     or belongs to the second parameter's package
49230     */
49231    function checkFileMap($path, $package = false, $api = '1.0', $attrs = false)
49232    {
49233        if (is_array($path)) {
49234            static $notempty;
49235            if (empty($notempty)) {
49236                if (!class_exists('PEAR_Installer_Role')) {
49237                    require_once 'PEAR/Installer/Role.php';
49238                }
49239                $notempty = create_function('$a','return !empty($a);');
49240            }
49241            $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1]))
49242                : strtolower($package);
49243            $pkgs = array();
49244            foreach ($path as $name => $attrs) {
49245                if (is_array($attrs)) {
49246                    if (isset($attrs['install-as'])) {
49247                        $name = $attrs['install-as'];
49248                    }
49249                    if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) {
49250                        // these are not installed
49251                        continue;
49252                    }
49253                    if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) {
49254                        $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package;
49255                    }
49256                    if (isset($attrs['baseinstalldir'])) {
49257                        $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name;
49258                    }
49259                }
49260                $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs);
49261                if (PEAR::isError($pkgs[$name])) {
49262                    return $pkgs[$name];
49263                }
49264            }
49265            return array_filter($pkgs, $notempty);
49266        }
49267        if (empty($this->filemap_cache)) {
49268            if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
49269                return $e;
49270            }
49271            $err = $this->_readFileMap();
49272            $this->_unlock();
49273            if (PEAR::isError($err)) {
49274                return $err;
49275            }
49276        }
49277        if (!$attrs) {
49278            $attrs = array('role' => 'php'); // any old call would be for PHP role only
49279        }
49280        if (isset($this->filemap_cache[$attrs['role']][$path])) {
49281            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
49282                return false;
49283            }
49284            return $this->filemap_cache[$attrs['role']][$path];
49285        }
49286        $l = strlen($this->install_dir);
49287        if (substr($path, 0, $l) == $this->install_dir) {
49288            $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
49289        }
49290        if (isset($this->filemap_cache[$attrs['role']][$path])) {
49291            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
49292                return false;
49293            }
49294            return $this->filemap_cache[$attrs['role']][$path];
49295        }
49296        return false;
49297    }
49298
49299    // }}}
49300    // {{{ flush()
49301    /**
49302     * Force a reload of the filemap
49303     * @since 1.5.0RC3
49304     */
49305    function flushFileMap()
49306    {
49307        $this->filemap_cache = null;
49308        clearstatcache(); // ensure that the next read gets the full, current filemap
49309    }
49310
49311    // }}}
49312    // {{{ apiVersion()
49313    /**
49314     * Get the expected API version.  Channels API is version 1.1, as it is backwards
49315     * compatible with 1.0
49316     * @return string
49317     */
49318    function apiVersion()
49319    {
49320        return '1.1';
49321    }
49322    // }}}
49323
49324
49325    /**
49326     * Parse a package name, or validate a parsed package name array
49327     * @param string|array pass in an array of format
49328     *                     array(
49329     *                      'package' => 'pname',
49330     *                     ['channel' => 'channame',]
49331     *                     ['version' => 'version',]
49332     *                     ['state' => 'state',]
49333     *                     ['group' => 'groupname'])
49334     *                     or a string of format
49335     *                     [channel://][channame/]pname[-version|-state][/group=groupname]
49336     * @return array|PEAR_Error
49337     */
49338    function parsePackageName($param, $defaultchannel = 'pear.php.net')
49339    {
49340        $saveparam = $param;
49341        if (is_array($param)) {
49342            // convert to string for error messages
49343            $saveparam = $this->parsedPackageNameToString($param);
49344            // process the array
49345            if (!isset($param['package'])) {
49346                return PEAR::raiseError('parsePackageName(): array $param ' .
49347                    'must contain a valid package name in index "param"',
49348                    'package', null, null, $param);
49349            }
49350            if (!isset($param['uri'])) {
49351                if (!isset($param['channel'])) {
49352                    $param['channel'] = $defaultchannel;
49353                }
49354            } else {
49355                $param['channel'] = '__uri';
49356            }
49357        } else {
49358            $components = @parse_url((string) $param);
49359            if (isset($components['scheme'])) {
49360                if ($components['scheme'] == 'http') {
49361                    // uri package
49362                    $param = array('uri' => $param, 'channel' => '__uri');
49363                } elseif($components['scheme'] != 'channel') {
49364                    return PEAR::raiseError('parsePackageName(): only channel:// uris may ' .
49365                        'be downloaded, not "' . $param . '"', 'invalid', null, null, $param);
49366                }
49367            }
49368            if (!isset($components['path'])) {
49369                return PEAR::raiseError('parsePackageName(): array $param ' .
49370                    'must contain a valid package name in "' . $param . '"',
49371                    'package', null, null, $param);
49372            }
49373            if (isset($components['host'])) {
49374                // remove the leading "/"
49375                $components['path'] = substr($components['path'], 1);
49376            }
49377            if (!isset($components['scheme'])) {
49378                if (strpos($components['path'], '/') !== false) {
49379                    if ($components['path']{0} == '/') {
49380                        return PEAR::raiseError('parsePackageName(): this is not ' .
49381                            'a package name, it begins with "/" in "' . $param . '"',
49382                            'invalid', null, null, $param);
49383                    }
49384                    $parts = explode('/', $components['path']);
49385                    $components['host'] = array_shift($parts);
49386                    if (count($parts) > 1) {
49387                        $components['path'] = array_pop($parts);
49388                        $components['host'] .= '/' . implode('/', $parts);
49389                    } else {
49390                        $components['path'] = implode('/', $parts);
49391                    }
49392                } else {
49393                    $components['host'] = $defaultchannel;
49394                }
49395            } else {
49396                if (strpos($components['path'], '/')) {
49397                    $parts = explode('/', $components['path']);
49398                    $components['path'] = array_pop($parts);
49399                    $components['host'] .= '/' . implode('/', $parts);
49400                }
49401            }
49402
49403            if (is_array($param)) {
49404                $param['package'] = $components['path'];
49405            } else {
49406                $param = array(
49407                    'package' => $components['path']
49408                    );
49409                if (isset($components['host'])) {
49410                    $param['channel'] = $components['host'];
49411                }
49412            }
49413            if (isset($components['fragment'])) {
49414                $param['group'] = $components['fragment'];
49415            }
49416            if (isset($components['user'])) {
49417                $param['user'] = $components['user'];
49418            }
49419            if (isset($components['pass'])) {
49420                $param['pass'] = $components['pass'];
49421            }
49422            if (isset($components['query'])) {
49423                parse_str($components['query'], $param['opts']);
49424            }
49425            // check for extension
49426            $pathinfo = pathinfo($param['package']);
49427            if (isset($pathinfo['extension']) &&
49428                  in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) {
49429                $param['extension'] = $pathinfo['extension'];
49430                $param['package'] = substr($pathinfo['basename'], 0,
49431                    strlen($pathinfo['basename']) - 4);
49432            }
49433            // check for version
49434            if (strpos($param['package'], '-')) {
49435                $test = explode('-', $param['package']);
49436                if (count($test) != 2) {
49437                    return PEAR::raiseError('parsePackageName(): only one version/state ' .
49438                        'delimiter "-" is allowed in "' . $saveparam . '"',
49439                        'version', null, null, $param);
49440                }
49441                list($param['package'], $param['version']) = $test;
49442            }
49443        }
49444        // validation
49445        $info = $this->channelExists($param['channel']);
49446        if (PEAR::isError($info)) {
49447            return $info;
49448        }
49449        if (!$info) {
49450            return PEAR::raiseError('unknown channel "' . $param['channel'] .
49451                '" in "' . $saveparam . '"', 'channel', null, null, $param);
49452        }
49453        $chan = $this->getChannel($param['channel']);
49454        if (PEAR::isError($chan)) {
49455            return $chan;
49456        }
49457        if (!$chan) {
49458            return PEAR::raiseError("Exception: corrupt registry, could not " .
49459                "retrieve channel " . $param['channel'] . " information",
49460                'registry', null, null, $param);
49461        }
49462        $param['channel'] = $chan->getName();
49463        $validate = $chan->getValidationObject();
49464        $vpackage = $chan->getValidationPackage();
49465        // validate package name
49466        if (!$validate->validPackageName($param['package'], $vpackage['_content'])) {
49467            return PEAR::raiseError('parsePackageName(): invalid package name "' .
49468                $param['package'] . '" in "' . $saveparam . '"',
49469                'package', null, null, $param);
49470        }
49471        if (isset($param['group'])) {
49472            if (!PEAR_Validate::validGroupName($param['group'])) {
49473                return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] .
49474                    '" is not a valid group name in "' . $saveparam . '"', 'group', null, null,
49475                    $param);
49476            }
49477        }
49478        if (isset($param['state'])) {
49479            if (!in_array(strtolower($param['state']), $validate->getValidStates())) {
49480                return PEAR::raiseError('parsePackageName(): state "' . $param['state']
49481                    . '" is not a valid state in "' . $saveparam . '"',
49482                    'state', null, null, $param);
49483            }
49484        }
49485        if (isset($param['version'])) {
49486            if (isset($param['state'])) {
49487                return PEAR::raiseError('parsePackageName(): cannot contain both ' .
49488                    'a version and a stability (state) in "' . $saveparam . '"',
49489                    'version/state', null, null, $param);
49490            }
49491            // check whether version is actually a state
49492            if (in_array(strtolower($param['version']), $validate->getValidStates())) {
49493                $param['state'] = strtolower($param['version']);
49494                unset($param['version']);
49495            } else {
49496                if (!$validate->validVersion($param['version'])) {
49497                    return PEAR::raiseError('parsePackageName(): "' . $param['version'] .
49498                        '" is neither a valid version nor a valid state in "' .
49499                        $saveparam . '"', 'version/state', null, null, $param);
49500                }
49501            }
49502        }
49503        return $param;
49504    }
49505
49506    /**
49507     * @param array
49508     * @return string
49509     */
49510    function parsedPackageNameToString($parsed, $brief = false)
49511    {
49512        if (is_string($parsed)) {
49513            return $parsed;
49514        }
49515        if (is_object($parsed)) {
49516            $p = $parsed;
49517            $parsed = array(
49518                'package' => $p->getPackage(),
49519                'channel' => $p->getChannel(),
49520                'version' => $p->getVersion(),
49521            );
49522        }
49523        if (isset($parsed['uri'])) {
49524            return $parsed['uri'];
49525        }
49526        if ($brief) {
49527            if ($channel = $this->channelAlias($parsed['channel'])) {
49528                return $channel . '/' . $parsed['package'];
49529            }
49530        }
49531        $upass = '';
49532        if (isset($parsed['user'])) {
49533            $upass = $parsed['user'];
49534            if (isset($parsed['pass'])) {
49535                $upass .= ':' . $parsed['pass'];
49536            }
49537            $upass = "$upass@";
49538        }
49539        $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package'];
49540        if (isset($parsed['version']) || isset($parsed['state'])) {
49541            $ver = isset($parsed['version']) ? $parsed['version'] : '';
49542            $ver .= isset($parsed['state']) ? $parsed['state'] : '';
49543            $ret .= '-' . $ver;
49544        }
49545        if (isset($parsed['extension'])) {
49546            $ret .= '.' . $parsed['extension'];
49547        }
49548        if (isset($parsed['opts'])) {
49549            $ret .= '?';
49550            foreach ($parsed['opts'] as $name => $value) {
49551                $parsed['opts'][$name] = "$name=$value";
49552            }
49553            $ret .= implode('&', $parsed['opts']);
49554        }
49555        if (isset($parsed['group'])) {
49556            $ret .= '#' . $parsed['group'];
49557        }
49558        return $ret;
49559    }
49560}PEAR-1.9.4/PEAR/REST.php0000644000076500000240000004151711605156614013261 0ustar  helgistaff<?php
49561/**
49562 * PEAR_REST
49563 *
49564 * PHP versions 4 and 5
49565 *
49566 * @category   pear
49567 * @package    PEAR
49568 * @author     Greg Beaver <cellog@php.net>
49569 * @copyright  1997-2009 The Authors
49570 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
49571 * @version    CVS: $Id: REST.php 313023 2011-07-06 19:17:11Z dufuz $
49572 * @link       http://pear.php.net/package/PEAR
49573 * @since      File available since Release 1.4.0a1
49574 */
49575
49576/**
49577 * For downloading xml files
49578 */
49579require_once 'PEAR.php';
49580require_once 'PEAR/XMLParser.php';
49581
49582/**
49583 * Intelligently retrieve data, following hyperlinks if necessary, and re-directing
49584 * as well
49585 * @category   pear
49586 * @package    PEAR
49587 * @author     Greg Beaver <cellog@php.net>
49588 * @copyright  1997-2009 The Authors
49589 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
49590 * @version    Release: 1.9.4
49591 * @link       http://pear.php.net/package/PEAR
49592 * @since      Class available since Release 1.4.0a1
49593 */
49594class PEAR_REST
49595{
49596    var $config;
49597    var $_options;
49598
49599    function PEAR_REST(&$config, $options = array())
49600    {
49601        $this->config   = &$config;
49602        $this->_options = $options;
49603    }
49604
49605    /**
49606     * Retrieve REST data, but always retrieve the local cache if it is available.
49607     *
49608     * This is useful for elements that should never change, such as information on a particular
49609     * release
49610     * @param string full URL to this resource
49611     * @param array|false contents of the accept-encoding header
49612     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
49613     *                    parsed using PEAR_XMLParser
49614     * @return string|array
49615     */
49616    function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false)
49617    {
49618        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
49619            md5($url) . 'rest.cachefile';
49620
49621        if (file_exists($cachefile)) {
49622            return unserialize(implode('', file($cachefile)));
49623        }
49624
49625        return $this->retrieveData($url, $accept, $forcestring, $channel);
49626    }
49627
49628    /**
49629     * Retrieve a remote REST resource
49630     * @param string full URL to this resource
49631     * @param array|false contents of the accept-encoding header
49632     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
49633     *                    parsed using PEAR_XMLParser
49634     * @return string|array
49635     */
49636    function retrieveData($url, $accept = false, $forcestring = false, $channel = false)
49637    {
49638        $cacheId = $this->getCacheId($url);
49639        if ($ret = $this->useLocalCache($url, $cacheId)) {
49640            return $ret;
49641        }
49642
49643        $file = $trieddownload = false;
49644        if (!isset($this->_options['offline'])) {
49645            $trieddownload = true;
49646            $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel);
49647        }
49648
49649        if (PEAR::isError($file)) {
49650            if ($file->getCode() !== -9276) {
49651                return $file;
49652            }
49653
49654            $trieddownload = false;
49655            $file = false; // use local copy if available on socket connect error
49656        }
49657
49658        if (!$file) {
49659            $ret = $this->getCache($url);
49660            if (!PEAR::isError($ret) && $trieddownload) {
49661                // reset the age of the cache if the server says it was unmodified
49662                $result = $this->saveCache($url, $ret, null, true, $cacheId);
49663                if (PEAR::isError($result)) {
49664                    return PEAR::raiseError($result->getMessage());
49665                }
49666            }
49667
49668            return $ret;
49669        }
49670
49671        if (is_array($file)) {
49672            $headers      = $file[2];
49673            $lastmodified = $file[1];
49674            $content      = $file[0];
49675        } else {
49676            $headers      = array();
49677            $lastmodified = false;
49678            $content      = $file;
49679        }
49680
49681        if ($forcestring) {
49682            $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId);
49683            if (PEAR::isError($result)) {
49684                return PEAR::raiseError($result->getMessage());
49685            }
49686
49687            return $content;
49688        }
49689
49690        if (isset($headers['content-type'])) {
49691            switch ($headers['content-type']) {
49692                case 'text/xml' :
49693                case 'application/xml' :
49694                case 'text/plain' :
49695                    if ($headers['content-type'] === 'text/plain') {
49696                        $check = substr($content, 0, 5);
49697                        if ($check !== '<?xml') {
49698                            break;
49699                        }
49700                    }
49701
49702                    $parser = new PEAR_XMLParser;
49703                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
49704                    $err = $parser->parse($content);
49705                    PEAR::popErrorHandling();
49706                    if (PEAR::isError($err)) {
49707                        return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .
49708                            $err->getMessage());
49709                    }
49710                    $content = $parser->getData();
49711                case 'text/html' :
49712                default :
49713                    // use it as a string
49714            }
49715        } else {
49716            // assume XML
49717            $parser = new PEAR_XMLParser;
49718            $parser->parse($content);
49719            $content = $parser->getData();
49720        }
49721
49722        $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId);
49723        if (PEAR::isError($result)) {
49724            return PEAR::raiseError($result->getMessage());
49725        }
49726
49727        return $content;
49728    }
49729
49730    function useLocalCache($url, $cacheid = null)
49731    {
49732        if ($cacheid === null) {
49733            $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
49734                md5($url) . 'rest.cacheid';
49735            if (!file_exists($cacheidfile)) {
49736                return false;
49737            }
49738
49739            $cacheid = unserialize(implode('', file($cacheidfile)));
49740        }
49741
49742        $cachettl = $this->config->get('cache_ttl');
49743        // If cache is newer than $cachettl seconds, we use the cache!
49744        if (time() - $cacheid['age'] < $cachettl) {
49745            return $this->getCache($url);
49746        }
49747
49748        return false;
49749    }
49750
49751    function getCacheId($url)
49752    {
49753        $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
49754            md5($url) . 'rest.cacheid';
49755
49756        if (!file_exists($cacheidfile)) {
49757            return false;
49758        }
49759
49760        $ret = unserialize(implode('', file($cacheidfile)));
49761        return $ret;
49762    }
49763
49764    function getCache($url)
49765    {
49766        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
49767            md5($url) . 'rest.cachefile';
49768
49769        if (!file_exists($cachefile)) {
49770            return PEAR::raiseError('No cached content available for "' . $url . '"');
49771        }
49772
49773        return unserialize(implode('', file($cachefile)));
49774    }
49775
49776    /**
49777     * @param string full URL to REST resource
49778     * @param string original contents of the REST resource
49779     * @param array  HTTP Last-Modified and ETag headers
49780     * @param bool   if true, then the cache id file should be regenerated to
49781     *               trigger a new time-to-live value
49782     */
49783    function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null)
49784    {
49785        $cache_dir   = $this->config->get('cache_dir');
49786        $d           = $cache_dir . DIRECTORY_SEPARATOR . md5($url);
49787        $cacheidfile = $d . 'rest.cacheid';
49788        $cachefile   = $d . 'rest.cachefile';
49789
49790        if (!is_dir($cache_dir)) {
49791            if (System::mkdir(array('-p', $cache_dir)) === false) {
49792              return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed.");
49793            }
49794        }
49795
49796        if ($cacheid === null && $nochange) {
49797            $cacheid = unserialize(implode('', file($cacheidfile)));
49798        }
49799
49800        $idData = serialize(array(
49801            'age'        => time(),
49802            'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified),
49803        ));
49804
49805        $result = $this->saveCacheFile($cacheidfile, $idData);
49806        if (PEAR::isError($result)) {
49807            return $result;
49808        } elseif ($nochange) {
49809            return true;
49810        }
49811
49812        $result = $this->saveCacheFile($cachefile, serialize($contents));
49813        if (PEAR::isError($result)) {
49814            if (file_exists($cacheidfile)) {
49815              @unlink($cacheidfile);
49816            }
49817
49818            return $result;
49819        }
49820
49821        return true;
49822    }
49823
49824    function saveCacheFile($file, $contents)
49825    {
49826        $len = strlen($contents);
49827
49828        $cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode
49829        if ($cachefile_fp !== false) { // create file
49830            if (fwrite($cachefile_fp, $contents, $len) < $len) {
49831                fclose($cachefile_fp);
49832                return PEAR::raiseError("Could not write $file.");
49833            }
49834        } else { // update file
49835            $cachefile_lstat = lstat($file);
49836            $cachefile_fp = @fopen($file, 'wb');
49837            if (!$cachefile_fp) {
49838                return PEAR::raiseError("Could not open $file for writing.");
49839            }
49840
49841            $cachefile_fstat = fstat($cachefile_fp);
49842            if (
49843              $cachefile_lstat['mode'] == $cachefile_fstat['mode'] &&
49844              $cachefile_lstat['ino']  == $cachefile_fstat['ino'] &&
49845              $cachefile_lstat['dev']  == $cachefile_fstat['dev'] &&
49846              $cachefile_fstat['nlink'] === 1
49847            ) {
49848                if (fwrite($cachefile_fp, $contents, $len) < $len) {
49849                    fclose($cachefile_fp);
49850                    return PEAR::raiseError("Could not write $file.");
49851                }
49852            } else {
49853                fclose($cachefile_fp);
49854                $link = function_exists('readlink') ? readlink($file) : $file;
49855                return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack');
49856            }
49857        }
49858
49859        fclose($cachefile_fp);
49860        return true;
49861    }
49862
49863    /**
49864     * Efficiently Download a file through HTTP.  Returns downloaded file as a string in-memory
49865     * This is best used for small files
49866     *
49867     * If an HTTP proxy has been configured (http_proxy PEAR_Config
49868     * setting), the proxy will be used.
49869     *
49870     * @param string  $url       the URL to download
49871     * @param string  $save_dir  directory to save file in
49872     * @param false|string|array $lastmodified header values to check against for caching
49873     *                           use false to return the header values from this download
49874     * @param false|array $accept Accept headers to send
49875     * @return string|array  Returns the contents of the downloaded file or a PEAR
49876     *                       error on failure.  If the error is caused by
49877     *                       socket-related errors, the error object will
49878     *                       have the fsockopen error code available through
49879     *                       getCode().  If caching is requested, then return the header
49880     *                       values.
49881     *
49882     * @access public
49883     */
49884    function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false)
49885    {
49886        static $redirect = 0;
49887        // always reset , so we are clean case of error
49888        $wasredirect = $redirect;
49889        $redirect = 0;
49890
49891        $info = parse_url($url);
49892        if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
49893            return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
49894        }
49895
49896        if (!isset($info['host'])) {
49897            return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
49898        }
49899
49900        $host   = isset($info['host']) ? $info['host'] : null;
49901        $port   = isset($info['port']) ? $info['port'] : null;
49902        $path   = isset($info['path']) ? $info['path'] : null;
49903        $schema = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http';
49904
49905        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
49906        if ($this->config->get('http_proxy')&&
49907              $proxy = parse_url($this->config->get('http_proxy'))
49908        ) {
49909            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
49910            if ($schema === 'https') {
49911                $proxy_host = 'ssl://' . $proxy_host;
49912            }
49913
49914            $proxy_port   = isset($proxy['port']) ? $proxy['port'] : 8080;
49915            $proxy_user   = isset($proxy['user']) ? urldecode($proxy['user']) : null;
49916            $proxy_pass   = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
49917            $proxy_schema = (isset($proxy['scheme']) && $proxy['scheme'] == 'https') ? 'https' : 'http';
49918        }
49919
49920        if (empty($port)) {
49921            $port = (isset($info['scheme']) && $info['scheme'] == 'https')  ? 443 : 80;
49922        }
49923
49924        if (isset($proxy['host'])) {
49925            $request = "GET $url HTTP/1.1\r\n";
49926        } else {
49927            $request = "GET $path HTTP/1.1\r\n";
49928        }
49929
49930        $request .= "Host: $host\r\n";
49931        $ifmodifiedsince = '';
49932        if (is_array($lastmodified)) {
49933            if (isset($lastmodified['Last-Modified'])) {
49934                $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
49935            }
49936
49937            if (isset($lastmodified['ETag'])) {
49938                $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
49939            }
49940        } else {
49941            $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
49942        }
49943
49944        $request .= $ifmodifiedsince .
49945            "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n";
49946
49947        $username = $this->config->get('username', null, $channel);
49948        $password = $this->config->get('password', null, $channel);
49949
49950        if ($username && $password) {
49951            $tmp = base64_encode("$username:$password");
49952            $request .= "Authorization: Basic $tmp\r\n";
49953        }
49954
49955        if ($proxy_host != '' && $proxy_user != '') {
49956            $request .= 'Proxy-Authorization: Basic ' .
49957                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
49958        }
49959
49960        if ($accept) {
49961            $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
49962        }
49963
49964        $request .= "Accept-Encoding:\r\n";
49965        $request .= "Connection: close\r\n";
49966        $request .= "\r\n";
49967
49968        if ($proxy_host != '') {
49969            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15);
49970            if (!$fp) {
49971                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", -9276);
49972            }
49973        } else {
49974            if ($schema === 'https') {
49975                $host = 'ssl://' . $host;
49976            }
49977
49978            $fp = @fsockopen($host, $port, $errno, $errstr);
49979            if (!$fp) {
49980                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
49981            }
49982        }
49983
49984        fwrite($fp, $request);
49985
49986        $headers = array();
49987        $reply   = 0;
49988        while ($line = trim(fgets($fp, 1024))) {
49989            if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
49990                $headers[strtolower($matches[1])] = trim($matches[2]);
49991            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
49992                $reply = (int)$matches[1];
49993                if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
49994                    return false;
49995                }
49996
49997                if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) {
49998                    return PEAR::raiseError("File $schema://$host:$port$path not valid (received: $line)");
49999                }
50000            }
50001        }
50002
50003        if ($reply != 200) {
50004            if (!isset($headers['location'])) {
50005                return PEAR::raiseError("File $schema://$host:$port$path not valid (redirected but no location)");
50006            }
50007
50008            if ($wasredirect > 4) {
50009                return PEAR::raiseError("File $schema://$host:$port$path not valid (redirection looped more than 5 times)");
50010            }
50011
50012            $redirect = $wasredirect + 1;
50013            return $this->downloadHttp($headers['location'], $lastmodified, $accept, $channel);
50014        }
50015
50016        $length = isset($headers['content-length']) ? $headers['content-length'] : -1;
50017
50018        $data = '';
50019        while ($chunk = @fread($fp, 8192)) {
50020            $data .= $chunk;
50021        }
50022        fclose($fp);
50023
50024        if ($lastmodified === false || $lastmodified) {
50025            if (isset($headers['etag'])) {
50026                $lastmodified = array('ETag' => $headers['etag']);
50027            }
50028
50029            if (isset($headers['last-modified'])) {
50030                if (is_array($lastmodified)) {
50031                    $lastmodified['Last-Modified'] = $headers['last-modified'];
50032                } else {
50033                    $lastmodified = $headers['last-modified'];
50034                }
50035            }
50036
50037            return array($data, $lastmodified, $headers);
50038        }
50039
50040        return $data;
50041    }
50042}PEAR-1.9.4/PEAR/RunTest.php0000644000076500000240000010660211605156614014105 0ustar  helgistaff<?php
50043/**
50044 * PEAR_RunTest
50045 *
50046 * PHP versions 4 and 5
50047 *
50048 * @category   pear
50049 * @package    PEAR
50050 * @author     Tomas V.V.Cox <cox@idecnet.com>
50051 * @author     Greg Beaver <cellog@php.net>
50052 * @copyright  1997-2009 The Authors
50053 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
50054 * @version    CVS: $Id: RunTest.php 313024 2011-07-06 19:51:24Z dufuz $
50055 * @link       http://pear.php.net/package/PEAR
50056 * @since      File available since Release 1.3.3
50057 */
50058
50059/**
50060 * for error handling
50061 */
50062require_once 'PEAR.php';
50063require_once 'PEAR/Config.php';
50064
50065define('DETAILED', 1);
50066putenv("PHP_PEAR_RUNTESTS=1");
50067
50068/**
50069 * Simplified version of PHP's test suite
50070 *
50071 * Try it with:
50072 *
50073 * $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);'
50074 *
50075 *
50076 * @category   pear
50077 * @package    PEAR
50078 * @author     Tomas V.V.Cox <cox@idecnet.com>
50079 * @author     Greg Beaver <cellog@php.net>
50080 * @copyright  1997-2009 The Authors
50081 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
50082 * @version    Release: 1.9.4
50083 * @link       http://pear.php.net/package/PEAR
50084 * @since      Class available since Release 1.3.3
50085 */
50086class PEAR_RunTest
50087{
50088    var $_headers = array();
50089    var $_logger;
50090    var $_options;
50091    var $_php;
50092    var $tests_count;
50093    var $xdebug_loaded;
50094    /**
50095     * Saved value of php executable, used to reset $_php when we
50096     * have a test that uses cgi
50097     *
50098     * @var unknown_type
50099     */
50100    var $_savephp;
50101    var $ini_overwrites = array(
50102        'output_handler=',
50103        'open_basedir=',
50104        'safe_mode=0',
50105        'disable_functions=',
50106        'output_buffering=Off',
50107        'display_errors=1',
50108        'log_errors=0',
50109        'html_errors=0',
50110        'track_errors=1',
50111        'report_memleaks=0',
50112        'report_zend_debug=0',
50113        'docref_root=',
50114        'docref_ext=.html',
50115        'error_prepend_string=',
50116        'error_append_string=',
50117        'auto_prepend_file=',
50118        'auto_append_file=',
50119        'magic_quotes_runtime=0',
50120        'xdebug.default_enable=0',
50121        'allow_url_fopen=1',
50122    );
50123
50124    /**
50125     * An object that supports the PEAR_Common->log() signature, or null
50126     * @param PEAR_Common|null
50127     */
50128    function PEAR_RunTest($logger = null, $options = array())
50129    {
50130        if (!defined('E_DEPRECATED')) {
50131            define('E_DEPRECATED', 0);
50132        }
50133        if (!defined('E_STRICT')) {
50134            define('E_STRICT', 0);
50135        }
50136        $this->ini_overwrites[] = 'error_reporting=' . (E_ALL & ~(E_DEPRECATED | E_STRICT));
50137        if (is_null($logger)) {
50138            require_once 'PEAR/Common.php';
50139            $logger = new PEAR_Common;
50140        }
50141        $this->_logger  = $logger;
50142        $this->_options = $options;
50143
50144        $conf = &PEAR_Config::singleton();
50145        $this->_php = $conf->get('php_bin');
50146    }
50147
50148    /**
50149     * Taken from php-src/run-tests.php
50150     *
50151     * @param string $commandline command name
50152     * @param array $env
50153     * @param string $stdin standard input to pass to the command
50154     * @return unknown
50155     */
50156    function system_with_timeout($commandline, $env = null, $stdin = null)
50157    {
50158        $data = '';
50159        if (version_compare(phpversion(), '5.0.0', '<')) {
50160            $proc = proc_open($commandline, array(
50161                0 => array('pipe', 'r'),
50162                1 => array('pipe', 'w'),
50163                2 => array('pipe', 'w')
50164                ), $pipes);
50165        } else {
50166            $proc = proc_open($commandline, array(
50167                0 => array('pipe', 'r'),
50168                1 => array('pipe', 'w'),
50169                2 => array('pipe', 'w')
50170                ), $pipes, null, $env, array('suppress_errors' => true));
50171        }
50172
50173        if (!$proc) {
50174            return false;
50175        }
50176
50177        if (is_string($stdin)) {
50178            fwrite($pipes[0], $stdin);
50179        }
50180        fclose($pipes[0]);
50181
50182        while (true) {
50183            /* hide errors from interrupted syscalls */
50184            $r = $pipes;
50185            $e = $w = null;
50186            $n = @stream_select($r, $w, $e, 60);
50187
50188            if ($n === 0) {
50189                /* timed out */
50190                $data .= "\n ** ERROR: process timed out **\n";
50191                proc_terminate($proc);
50192                return array(1234567890, $data);
50193            } else if ($n > 0) {
50194                $line = fread($pipes[1], 8192);
50195                if (strlen($line) == 0) {
50196                    /* EOF */
50197                    break;
50198                }
50199                $data .= $line;
50200            }
50201        }
50202        if (function_exists('proc_get_status')) {
50203            $stat = proc_get_status($proc);
50204            if ($stat['signaled']) {
50205                $data .= "\nTermsig=".$stat['stopsig'];
50206            }
50207        }
50208        $code = proc_close($proc);
50209        if (function_exists('proc_get_status')) {
50210            $code = $stat['exitcode'];
50211        }
50212        return array($code, $data);
50213    }
50214
50215    /**
50216     * Turns a PHP INI string into an array
50217     *
50218     * Turns -d "include_path=/foo/bar" into this:
50219     * array(
50220     *   'include_path' => array(
50221     *          'operator' => '-d',
50222     *          'value'    => '/foo/bar',
50223     *   )
50224     * )
50225     * Works both with quotes and without
50226     *
50227     * @param string an PHP INI string, -d "include_path=/foo/bar"
50228     * @return array
50229     */
50230    function iniString2array($ini_string)
50231    {
50232        if (!$ini_string) {
50233            return array();
50234        }
50235        $split = preg_split('/[\s]|=/', $ini_string, -1, PREG_SPLIT_NO_EMPTY);
50236        $key   = $split[1][0] == '"'                     ? substr($split[1], 1)     : $split[1];
50237        $value = $split[2][strlen($split[2]) - 1] == '"' ? substr($split[2], 0, -1) : $split[2];
50238        // FIXME review if this is really the struct to go with
50239        $array = array($key => array('operator' => $split[0], 'value' => $value));
50240        return $array;
50241    }
50242
50243    function settings2array($settings, $ini_settings)
50244    {
50245        foreach ($settings as $setting) {
50246            if (strpos($setting, '=') !== false) {
50247                $setting = explode('=', $setting, 2);
50248                $name  = trim(strtolower($setting[0]));
50249                $value = trim($setting[1]);
50250                $ini_settings[$name] = $value;
50251            }
50252        }
50253        return $ini_settings;
50254    }
50255
50256    function settings2params($ini_settings)
50257    {
50258        $settings = '';
50259        foreach ($ini_settings as $name => $value) {
50260            if (is_array($value)) {
50261                $operator = $value['operator'];
50262                $value    = $value['value'];
50263            } else {
50264                $operator = '-d';
50265            }
50266            $value = addslashes($value);
50267            $settings .= " $operator \"$name=$value\"";
50268        }
50269        return $settings;
50270    }
50271
50272    function _preparePhpBin($php, $file, $ini_settings)
50273    {
50274        $file = escapeshellarg($file);
50275        // This was fixed in php 5.3 and is not needed after that
50276        if (OS_WINDOWS && version_compare(PHP_VERSION, '5.3', '<')) {
50277            $cmd = '"'.escapeshellarg($php).' '.$ini_settings.' -f ' . $file .'"';
50278        } else {
50279            $cmd = $php . $ini_settings . ' -f ' . $file;
50280        }
50281
50282        return $cmd;
50283    }
50284
50285    function runPHPUnit($file, $ini_settings = '')
50286    {
50287        if (!file_exists($file) && file_exists(getcwd() . DIRECTORY_SEPARATOR . $file)) {
50288            $file = realpath(getcwd() . DIRECTORY_SEPARATOR . $file);
50289        } elseif (file_exists($file)) {
50290            $file = realpath($file);
50291        }
50292
50293        $cmd = $this->_preparePhpBin($this->_php, $file, $ini_settings);
50294        if (isset($this->_logger)) {
50295            $this->_logger->log(2, 'Running command "' . $cmd . '"');
50296        }
50297
50298        $savedir = getcwd(); // in case the test moves us around
50299        chdir(dirname($file));
50300        echo `$cmd`;
50301        chdir($savedir);
50302        return 'PASSED'; // we have no way of knowing this information so assume passing
50303    }
50304
50305    /**
50306     * Runs an individual test case.
50307     *
50308     * @param string       The filename of the test
50309     * @param array|string INI settings to be applied to the test run
50310     * @param integer      Number what the current running test is of the
50311     *                     whole test suite being runned.
50312     *
50313     * @return string|object Returns PASSED, WARNED, FAILED depending on how the
50314     *                       test came out.
50315     *                       PEAR Error when the tester it self fails
50316     */
50317    function run($file, $ini_settings = array(), $test_number = 1)
50318    {
50319        if (isset($this->_savephp)) {
50320            $this->_php = $this->_savephp;
50321            unset($this->_savephp);
50322        }
50323        if (empty($this->_options['cgi'])) {
50324            // try to see if php-cgi is in the path
50325            $res = $this->system_with_timeout('php-cgi -v');
50326            if (false !== $res && !(is_array($res) && in_array($res[0], array(-1, 127)))) {
50327                $this->_options['cgi'] = 'php-cgi';
50328            }
50329        }
50330        if (1 < $len = strlen($this->tests_count)) {
50331            $test_number = str_pad($test_number, $len, ' ', STR_PAD_LEFT);
50332            $test_nr = "[$test_number/$this->tests_count] ";
50333        } else {
50334            $test_nr = '';
50335        }
50336
50337        $file = realpath($file);
50338        $section_text = $this->_readFile($file);
50339        if (PEAR::isError($section_text)) {
50340            return $section_text;
50341        }
50342
50343        if (isset($section_text['POST_RAW']) && isset($section_text['UPLOAD'])) {
50344            return PEAR::raiseError("Cannot contain both POST_RAW and UPLOAD in test file: $file");
50345        }
50346
50347        $cwd = getcwd();
50348
50349        $pass_options = '';
50350        if (!empty($this->_options['ini'])) {
50351            $pass_options = $this->_options['ini'];
50352        }
50353
50354        if (is_string($ini_settings)) {
50355            $ini_settings = $this->iniString2array($ini_settings);
50356        }
50357
50358        $ini_settings = $this->settings2array($this->ini_overwrites, $ini_settings);
50359        if ($section_text['INI']) {
50360            if (strpos($section_text['INI'], '{PWD}') !== false) {
50361                $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']);
50362            }
50363            $ini = preg_split( "/[\n\r]+/", $section_text['INI']);
50364            $ini_settings = $this->settings2array($ini, $ini_settings);
50365        }
50366        $ini_settings = $this->settings2params($ini_settings);
50367        $shortname = str_replace($cwd . DIRECTORY_SEPARATOR, '', $file);
50368
50369        $tested = trim($section_text['TEST']);
50370        $tested.= !isset($this->_options['simple']) ? "[$shortname]" : ' ';
50371
50372        if (!empty($section_text['POST']) || !empty($section_text['POST_RAW']) ||
50373              !empty($section_text['UPLOAD']) || !empty($section_text['GET']) ||
50374              !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) {
50375            if (empty($this->_options['cgi'])) {
50376                if (!isset($this->_options['quiet'])) {
50377                    $this->_logger->log(0, "SKIP $test_nr$tested (reason: --cgi option needed for this test, type 'pear help run-tests')");
50378                }
50379                if (isset($this->_options['tapoutput'])) {
50380                    return array('ok', ' # skip --cgi option needed for this test, "pear help run-tests" for info');
50381                }
50382                return 'SKIPPED';
50383            }
50384            $this->_savephp = $this->_php;
50385            $this->_php = $this->_options['cgi'];
50386        }
50387
50388        $temp_dir = realpath(dirname($file));
50389        $main_file_name = basename($file, 'phpt');
50390        $diff_filename     = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'diff';
50391        $log_filename      = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'log';
50392        $exp_filename      = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'exp';
50393        $output_filename   = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'out';
50394        $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'mem';
50395        $temp_file         = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'php';
50396        $temp_skipif       = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'skip.php';
50397        $temp_clean        = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'clean.php';
50398        $tmp_post          = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');
50399
50400        // unlink old test results
50401        $this->_cleanupOldFiles($file);
50402
50403        // Check if test should be skipped.
50404        $res  = $this->_runSkipIf($section_text, $temp_skipif, $tested, $ini_settings);
50405        if (count($res) != 2) {
50406            return $res;
50407        }
50408        $info = $res['info'];
50409        $warn = $res['warn'];
50410
50411        // We've satisfied the preconditions - run the test!
50412        if (isset($this->_options['coverage']) && $this->xdebug_loaded) {
50413            $xdebug_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'xdebug';
50414            $text = "\n" . 'function coverage_shutdown() {' .
50415                    "\n" . '    $xdebug = var_export(xdebug_get_code_coverage(), true);';
50416            if (!function_exists('file_put_contents')) {
50417                $text .= "\n" . '    $fh = fopen(\'' . $xdebug_file . '\', "wb");' .
50418                        "\n" . '    if ($fh !== false) {' .
50419                        "\n" . '        fwrite($fh, $xdebug);' .
50420                        "\n" . '        fclose($fh);' .
50421                        "\n" . '    }';
50422            } else {
50423                $text .= "\n" . '    file_put_contents(\'' . $xdebug_file . '\', $xdebug);';
50424            }
50425
50426            // Workaround for http://pear.php.net/bugs/bug.php?id=17292
50427            $lines             = explode("\n", $section_text['FILE']);
50428            $numLines          = count($lines);
50429            $namespace         = '';
50430            $coverage_shutdown = 'coverage_shutdown';
50431
50432            if (
50433                substr($lines[0], 0, 2) == '<?' ||
50434                substr($lines[0], 0, 5) == '<?php'
50435            ) {
50436                unset($lines[0]);
50437            }
50438
50439
50440            for ($i = 0; $i < $numLines; $i++) {
50441                if (isset($lines[$i]) && substr($lines[$i], 0, 9) == 'namespace') {
50442                    $namespace         = substr($lines[$i], 10, -1);
50443                    $coverage_shutdown = $namespace . '\\coverage_shutdown';
50444                    $namespace         = "namespace " . $namespace . ";\n";
50445
50446                    unset($lines[$i]);
50447                    break;
50448                }
50449            }
50450
50451            $text .= "\n    xdebug_stop_code_coverage();" .
50452                "\n" . '} // end coverage_shutdown()' .
50453                "\n\n" . 'register_shutdown_function("' . $coverage_shutdown . '");';
50454            $text .= "\n" . 'xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);' . "\n";
50455
50456            $this->save_text($temp_file, "<?php\n" . $namespace . $text  . "\n" . implode("\n", $lines));
50457        } else {
50458            $this->save_text($temp_file, $section_text['FILE']);
50459        }
50460
50461        $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : '';
50462        $cmd = $this->_preparePhpBin($this->_php, $temp_file, $ini_settings);
50463        $cmd.= "$args 2>&1";
50464        if (isset($this->_logger)) {
50465            $this->_logger->log(2, 'Running command "' . $cmd . '"');
50466        }
50467
50468        // Reset environment from any previous test.
50469        $env = $this->_resetEnv($section_text, $temp_file);
50470
50471        $section_text = $this->_processUpload($section_text, $file);
50472        if (PEAR::isError($section_text)) {
50473            return $section_text;
50474        }
50475
50476        if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) {
50477            $post = trim($section_text['POST_RAW']);
50478            $raw_lines = explode("\n", $post);
50479
50480            $request = '';
50481            $started = false;
50482            foreach ($raw_lines as $i => $line) {
50483                if (empty($env['CONTENT_TYPE']) &&
50484                    preg_match('/^Content-Type:(.*)/i', $line, $res)) {
50485                    $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1]));
50486                    continue;
50487                }
50488                if ($started) {
50489                    $request .= "\n";
50490                }
50491                $started = true;
50492                $request .= $line;
50493            }
50494
50495            $env['CONTENT_LENGTH'] = strlen($request);
50496            $env['REQUEST_METHOD'] = 'POST';
50497
50498            $this->save_text($tmp_post, $request);
50499            $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
50500        } elseif (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) {
50501            $post = trim($section_text['POST']);
50502            $this->save_text($tmp_post, $post);
50503            $content_length = strlen($post);
50504
50505            $env['REQUEST_METHOD'] = 'POST';
50506            $env['CONTENT_TYPE']   = 'application/x-www-form-urlencoded';
50507            $env['CONTENT_LENGTH'] = $content_length;
50508
50509            $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
50510        } else {
50511            $env['REQUEST_METHOD'] = 'GET';
50512            $env['CONTENT_TYPE']   = '';
50513            $env['CONTENT_LENGTH'] = '';
50514        }
50515
50516        if (OS_WINDOWS && isset($section_text['RETURNS'])) {
50517            ob_start();
50518            system($cmd, $return_value);
50519            $out = ob_get_contents();
50520            ob_end_clean();
50521            $section_text['RETURNS'] = (int) trim($section_text['RETURNS']);
50522            $returnfail = ($return_value != $section_text['RETURNS']);
50523        } else {
50524            $returnfail = false;
50525            $stdin = isset($section_text['STDIN']) ? $section_text['STDIN'] : null;
50526            $out = $this->system_with_timeout($cmd, $env, $stdin);
50527            $return_value = $out[0];
50528            $out = $out[1];
50529        }
50530
50531        $output = preg_replace('/\r\n/', "\n", trim($out));
50532
50533        if (isset($tmp_post) && realpath($tmp_post) && file_exists($tmp_post)) {
50534            @unlink(realpath($tmp_post));
50535        }
50536        chdir($cwd); // in case the test moves us around
50537
50538        $this->_testCleanup($section_text, $temp_clean);
50539
50540        /* when using CGI, strip the headers from the output */
50541        $output = $this->_stripHeadersCGI($output);
50542
50543        if (isset($section_text['EXPECTHEADERS'])) {
50544            $testheaders = $this->_processHeaders($section_text['EXPECTHEADERS']);
50545            $missing = array_diff_assoc($testheaders, $this->_headers);
50546            $changed = '';
50547            foreach ($missing as $header => $value) {
50548                if (isset($this->_headers[$header])) {
50549                    $changed .= "-$header: $value\n+$header: ";
50550                    $changed .= $this->_headers[$header];
50551                } else {
50552                    $changed .= "-$header: $value\n";
50553                }
50554            }
50555            if ($missing) {
50556                // tack on failed headers to output:
50557                $output .= "\n====EXPECTHEADERS FAILURE====:\n$changed";
50558            }
50559        }
50560        // Does the output match what is expected?
50561        do {
50562            if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) {
50563                if (isset($section_text['EXPECTF'])) {
50564                    $wanted = trim($section_text['EXPECTF']);
50565                } else {
50566                    $wanted = trim($section_text['EXPECTREGEX']);
50567                }
50568                $wanted_re = preg_replace('/\r\n/', "\n", $wanted);
50569                if (isset($section_text['EXPECTF'])) {
50570                    $wanted_re = preg_quote($wanted_re, '/');
50571                    // Stick to basics
50572                    $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy
50573                    $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re);
50574                    $wanted_re = str_replace("%d", "[0-9]+", $wanted_re);
50575                    $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re);
50576                    $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re);
50577                    $wanted_re = str_replace("%c", ".", $wanted_re);
50578                    // %f allows two points "-.0.0" but that is the best *simple* expression
50579                }
50580
50581    /* DEBUG YOUR REGEX HERE
50582            var_dump($wanted_re);
50583            print(str_repeat('=', 80) . "\n");
50584            var_dump($output);
50585    */
50586                if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) {
50587                    if (file_exists($temp_file)) {
50588                        unlink($temp_file);
50589                    }
50590                    if (array_key_exists('FAIL', $section_text)) {
50591                        break;
50592                    }
50593                    if (!isset($this->_options['quiet'])) {
50594                        $this->_logger->log(0, "PASS $test_nr$tested$info");
50595                    }
50596                    if (isset($this->_options['tapoutput'])) {
50597                        return array('ok', ' - ' . $tested);
50598                    }
50599                    return 'PASSED';
50600                }
50601            } else {
50602                if (isset($section_text['EXPECTFILE'])) {
50603                    $f = $temp_dir . '/' . trim($section_text['EXPECTFILE']);
50604                    if (!($fp = @fopen($f, 'rb'))) {
50605                        return PEAR::raiseError('--EXPECTFILE-- section file ' .
50606                            $f . ' not found');
50607                    }
50608                    fclose($fp);
50609                    $section_text['EXPECT'] = file_get_contents($f);
50610                }
50611
50612                if (isset($section_text['EXPECT'])) {
50613                    $wanted = preg_replace('/\r\n/', "\n", trim($section_text['EXPECT']));
50614                } else {
50615                    $wanted = '';
50616                }
50617
50618                // compare and leave on success
50619                if (!$returnfail && 0 == strcmp($output, $wanted)) {
50620                    if (file_exists($temp_file)) {
50621                        unlink($temp_file);
50622                    }
50623                    if (array_key_exists('FAIL', $section_text)) {
50624                        break;
50625                    }
50626                    if (!isset($this->_options['quiet'])) {
50627                        $this->_logger->log(0, "PASS $test_nr$tested$info");
50628                    }
50629                    if (isset($this->_options['tapoutput'])) {
50630                        return array('ok', ' - ' . $tested);
50631                    }
50632                    return 'PASSED';
50633                }
50634            }
50635        } while (false);
50636
50637        if (array_key_exists('FAIL', $section_text)) {
50638            // we expect a particular failure
50639            // this is only used for testing PEAR_RunTest
50640            $expectf  = isset($section_text['EXPECTF']) ? $wanted_re : null;
50641            $faildiff = $this->generate_diff($wanted, $output, null, $expectf);
50642            $faildiff = preg_replace('/\r/', '', $faildiff);
50643            $wanted   = preg_replace('/\r/', '', trim($section_text['FAIL']));
50644            if ($faildiff == $wanted) {
50645                if (!isset($this->_options['quiet'])) {
50646                    $this->_logger->log(0, "PASS $test_nr$tested$info");
50647                }
50648                if (isset($this->_options['tapoutput'])) {
50649                    return array('ok', ' - ' . $tested);
50650                }
50651                return 'PASSED';
50652            }
50653            unset($section_text['EXPECTF']);
50654            $output = $faildiff;
50655            if (isset($section_text['RETURNS'])) {
50656                return PEAR::raiseError('Cannot have both RETURNS and FAIL in the same test: ' .
50657                    $file);
50658            }
50659        }
50660
50661        // Test failed so we need to report details.
50662        $txt = $warn ? 'WARN ' : 'FAIL ';
50663        $this->_logger->log(0, $txt . $test_nr . $tested . $info);
50664
50665        // write .exp
50666        $res = $this->_writeLog($exp_filename, $wanted);
50667        if (PEAR::isError($res)) {
50668            return $res;
50669        }
50670
50671        // write .out
50672        $res = $this->_writeLog($output_filename, $output);
50673        if (PEAR::isError($res)) {
50674            return $res;
50675        }
50676
50677        // write .diff
50678        $returns = isset($section_text['RETURNS']) ?
50679                        array(trim($section_text['RETURNS']), $return_value) : null;
50680        $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null;
50681        $data = $this->generate_diff($wanted, $output, $returns, $expectf);
50682        $res  = $this->_writeLog($diff_filename, $data);
50683        if (PEAR::isError($res)) {
50684            return $res;
50685        }
50686
50687        // write .log
50688        $data = "
50689---- EXPECTED OUTPUT
50690$wanted
50691---- ACTUAL OUTPUT
50692$output
50693---- FAILED
50694";
50695
50696        if ($returnfail) {
50697            $data .= "
50698---- EXPECTED RETURN
50699$section_text[RETURNS]
50700---- ACTUAL RETURN
50701$return_value
50702";
50703        }
50704
50705        $res = $this->_writeLog($log_filename, $data);
50706        if (PEAR::isError($res)) {
50707            return $res;
50708        }
50709
50710        if (isset($this->_options['tapoutput'])) {
50711            $wanted = explode("\n", $wanted);
50712            $wanted = "# Expected output:\n#\n#" . implode("\n#", $wanted);
50713            $output = explode("\n", $output);
50714            $output = "#\n#\n# Actual output:\n#\n#" . implode("\n#", $output);
50715            return array($wanted . $output . 'not ok', ' - ' . $tested);
50716        }
50717        return $warn ? 'WARNED' : 'FAILED';
50718    }
50719
50720    function generate_diff($wanted, $output, $rvalue, $wanted_re)
50721    {
50722        $w  = explode("\n", $wanted);
50723        $o  = explode("\n", $output);
50724        $wr = explode("\n", $wanted_re);
50725        $w1 = array_diff_assoc($w, $o);
50726        $o1 = array_diff_assoc($o, $w);
50727        $o2 = $w2 = array();
50728        foreach ($w1 as $idx => $val) {
50729            if (!$wanted_re || !isset($wr[$idx]) || !isset($o1[$idx]) ||
50730                  !preg_match('/^' . $wr[$idx] . '\\z/', $o1[$idx])) {
50731                $w2[sprintf("%03d<", $idx)] = sprintf("%03d- ", $idx + 1) . $val;
50732            }
50733        }
50734        foreach ($o1 as $idx => $val) {
50735            if (!$wanted_re || !isset($wr[$idx]) ||
50736                  !preg_match('/^' . $wr[$idx] . '\\z/', $val)) {
50737                $o2[sprintf("%03d>", $idx)] = sprintf("%03d+ ", $idx + 1) . $val;
50738            }
50739        }
50740        $diff = array_merge($w2, $o2);
50741        ksort($diff);
50742        $extra = $rvalue ? "##EXPECTED: $rvalue[0]\r\n##RETURNED: $rvalue[1]" : '';
50743        return implode("\r\n", $diff) . $extra;
50744    }
50745
50746    //  Write the given text to a temporary file, and return the filename.
50747    function save_text($filename, $text)
50748    {
50749        if (!$fp = fopen($filename, 'w')) {
50750            return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)");
50751        }
50752        fwrite($fp, $text);
50753        fclose($fp);
50754    if (1 < DETAILED) echo "
50755FILE $filename {{{
50756$text
50757}}}
50758";
50759    }
50760
50761    function _cleanupOldFiles($file)
50762    {
50763        $temp_dir = realpath(dirname($file));
50764        $mainFileName = basename($file, 'phpt');
50765        $diff_filename     = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'diff';
50766        $log_filename      = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'log';
50767        $exp_filename      = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'exp';
50768        $output_filename   = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'out';
50769        $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'mem';
50770        $temp_file         = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'php';
50771        $temp_skipif       = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'skip.php';
50772        $temp_clean        = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'clean.php';
50773        $tmp_post          = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');
50774
50775        // unlink old test results
50776        @unlink($diff_filename);
50777        @unlink($log_filename);
50778        @unlink($exp_filename);
50779        @unlink($output_filename);
50780        @unlink($memcheck_filename);
50781        @unlink($temp_file);
50782        @unlink($temp_skipif);
50783        @unlink($tmp_post);
50784        @unlink($temp_clean);
50785    }
50786
50787    function _runSkipIf($section_text, $temp_skipif, $tested, $ini_settings)
50788    {
50789        $info = '';
50790        $warn = false;
50791        if (array_key_exists('SKIPIF', $section_text) && trim($section_text['SKIPIF'])) {
50792            $this->save_text($temp_skipif, $section_text['SKIPIF']);
50793            $output = $this->system_with_timeout("$this->_php$ini_settings -f \"$temp_skipif\"");
50794            $output = $output[1];
50795            $loutput = ltrim($output);
50796            unlink($temp_skipif);
50797            if (!strncasecmp('skip', $loutput, 4)) {
50798                $skipreason = "SKIP $tested";
50799                if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) {
50800                    $skipreason .= '(reason: ' . $m[1] . ')';
50801                }
50802                if (!isset($this->_options['quiet'])) {
50803                    $this->_logger->log(0, $skipreason);
50804                }
50805                if (isset($this->_options['tapoutput'])) {
50806                    return array('ok', ' # skip ' . $reason);
50807                }
50808                return 'SKIPPED';
50809            }
50810
50811            if (!strncasecmp('info', $loutput, 4)
50812                && preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) {
50813                $info = " (info: $m[1])";
50814            }
50815
50816            if (!strncasecmp('warn', $loutput, 4)
50817                && preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) {
50818                $warn = true; /* only if there is a reason */
50819                $info = " (warn: $m[1])";
50820            }
50821        }
50822
50823        return array('warn' => $warn, 'info' => $info);
50824    }
50825
50826    function _stripHeadersCGI($output)
50827    {
50828        $this->headers = array();
50829        if (!empty($this->_options['cgi']) &&
50830              $this->_php == $this->_options['cgi'] &&
50831              preg_match("/^(.*?)(?:\n\n(.*)|\\z)/s", $output, $match)) {
50832            $output = isset($match[2]) ? trim($match[2]) : '';
50833            $this->_headers = $this->_processHeaders($match[1]);
50834        }
50835
50836        return $output;
50837    }
50838
50839    /**
50840     * Return an array that can be used with array_diff() to compare headers
50841     *
50842     * @param string $text
50843     */
50844    function _processHeaders($text)
50845    {
50846        $headers = array();
50847        $rh = preg_split("/[\n\r]+/", $text);
50848        foreach ($rh as $line) {
50849            if (strpos($line, ':')!== false) {
50850                $line = explode(':', $line, 2);
50851                $headers[trim($line[0])] = trim($line[1]);
50852            }
50853        }
50854        return $headers;
50855    }
50856
50857    function _readFile($file)
50858    {
50859        // Load the sections of the test file.
50860        $section_text = array(
50861            'TEST'   => '(unnamed test)',
50862            'SKIPIF' => '',
50863            'GET'    => '',
50864            'COOKIE' => '',
50865            'POST'   => '',
50866            'ARGS'   => '',
50867            'INI'    => '',
50868            'CLEAN'  => '',
50869        );
50870
50871        if (!is_file($file) || !$fp = fopen($file, "r")) {
50872            return PEAR::raiseError("Cannot open test file: $file");
50873        }
50874
50875        $section = '';
50876        while (!feof($fp)) {
50877            $line = fgets($fp);
50878
50879            // Match the beginning of a section.
50880            if (preg_match('/^--([_A-Z]+)--/', $line, $r)) {
50881                $section = $r[1];
50882                $section_text[$section] = '';
50883                continue;
50884            } elseif (empty($section)) {
50885                fclose($fp);
50886                return PEAR::raiseError("Invalid sections formats in test file: $file");
50887            }
50888
50889            // Add to the section text.
50890            $section_text[$section] .= $line;
50891        }
50892        fclose($fp);
50893
50894        return $section_text;
50895    }
50896
50897    function _writeLog($logname, $data)
50898    {
50899        if (!$log = fopen($logname, 'w')) {
50900            return PEAR::raiseError("Cannot create test log - $logname");
50901        }
50902        fwrite($log, $data);
50903        fclose($log);
50904    }
50905
50906    function _resetEnv($section_text, $temp_file)
50907    {
50908        $env = $_ENV;
50909        $env['REDIRECT_STATUS'] = '';
50910        $env['QUERY_STRING']    = '';
50911        $env['PATH_TRANSLATED'] = '';
50912        $env['SCRIPT_FILENAME'] = '';
50913        $env['REQUEST_METHOD']  = '';
50914        $env['CONTENT_TYPE']    = '';
50915        $env['CONTENT_LENGTH']  = '';
50916        if (!empty($section_text['ENV'])) {
50917            if (strpos($section_text['ENV'], '{PWD}') !== false) {
50918                $section_text['ENV'] = str_replace('{PWD}', dirname($temp_file), $section_text['ENV']);
50919            }
50920            foreach (explode("\n", trim($section_text['ENV'])) as $e) {
50921                $e = explode('=', trim($e), 2);
50922                if (!empty($e[0]) && isset($e[1])) {
50923                    $env[$e[0]] = $e[1];
50924                }
50925            }
50926        }
50927        if (array_key_exists('GET', $section_text)) {
50928            $env['QUERY_STRING'] = trim($section_text['GET']);
50929        } else {
50930            $env['QUERY_STRING'] = '';
50931        }
50932        if (array_key_exists('COOKIE', $section_text)) {
50933            $env['HTTP_COOKIE'] = trim($section_text['COOKIE']);
50934        } else {
50935            $env['HTTP_COOKIE'] = '';
50936        }
50937        $env['REDIRECT_STATUS'] = '1';
50938        $env['PATH_TRANSLATED'] = $temp_file;
50939        $env['SCRIPT_FILENAME'] = $temp_file;
50940
50941        return $env;
50942    }
50943
50944    function _processUpload($section_text, $file)
50945    {
50946        if (array_key_exists('UPLOAD', $section_text) && !empty($section_text['UPLOAD'])) {
50947            $upload_files = trim($section_text['UPLOAD']);
50948            $upload_files = explode("\n", $upload_files);
50949
50950            $request = "Content-Type: multipart/form-data; boundary=---------------------------20896060251896012921717172737\n" .
50951                       "-----------------------------20896060251896012921717172737\n";
50952            foreach ($upload_files as $fileinfo) {
50953                $fileinfo = explode('=', $fileinfo);
50954                if (count($fileinfo) != 2) {
50955                    return PEAR::raiseError("Invalid UPLOAD section in test file: $file");
50956                }
50957                if (!realpath(dirname($file) . '/' . $fileinfo[1])) {
50958                    return PEAR::raiseError("File for upload does not exist: $fileinfo[1] " .
50959                        "in test file: $file");
50960                }
50961                $file_contents = file_get_contents(dirname($file) . '/' . $fileinfo[1]);
50962                $fileinfo[1] = basename($fileinfo[1]);
50963                $request .= "Content-Disposition: form-data; name=\"$fileinfo[0]\"; filename=\"$fileinfo[1]\"\n";
50964                $request .= "Content-Type: text/plain\n\n";
50965                $request .= $file_contents . "\n" .
50966                    "-----------------------------20896060251896012921717172737\n";
50967            }
50968
50969            if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) {
50970                // encode POST raw
50971                $post = trim($section_text['POST']);
50972                $post = explode('&', $post);
50973                foreach ($post as $i => $post_info) {
50974                    $post_info = explode('=', $post_info);
50975                    if (count($post_info) != 2) {
50976                        return PEAR::raiseError("Invalid POST data in test file: $file");
50977                    }
50978                    $post_info[0] = rawurldecode($post_info[0]);
50979                    $post_info[1] = rawurldecode($post_info[1]);
50980                    $post[$i] = $post_info;
50981                }
50982                foreach ($post as $post_info) {
50983                    $request .= "Content-Disposition: form-data; name=\"$post_info[0]\"\n\n";
50984                    $request .= $post_info[1] . "\n" .
50985                        "-----------------------------20896060251896012921717172737\n";
50986                }
50987                unset($section_text['POST']);
50988            }
50989            $section_text['POST_RAW'] = $request;
50990        }
50991
50992        return $section_text;
50993    }
50994
50995    function _testCleanup($section_text, $temp_clean)
50996    {
50997        if ($section_text['CLEAN']) {
50998            // perform test cleanup
50999            $this->save_text($temp_clean, $section_text['CLEAN']);
51000            $output = $this->system_with_timeout("$this->_php $temp_clean  2>&1");
51001            if (strlen($output[1])) {
51002                echo "BORKED --CLEAN-- section! output:\n", $output[1];
51003            }
51004            if (file_exists($temp_clean)) {
51005                unlink($temp_clean);
51006            }
51007        }
51008    }
51009}
51010PEAR-1.9.4/PEAR/Validate.php0000644000076500000240000005305511605156614014235 0ustar  helgistaff<?php
51011/**
51012 * PEAR_Validate
51013 *
51014 * PHP versions 4 and 5
51015 *
51016 * @category   pear
51017 * @package    PEAR
51018 * @author     Greg Beaver <cellog@php.net>
51019 * @copyright  1997-2009 The Authors
51020 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
51021 * @version    CVS: $Id: Validate.php 313023 2011-07-06 19:17:11Z dufuz $
51022 * @link       http://pear.php.net/package/PEAR
51023 * @since      File available since Release 1.4.0a1
51024 */
51025/**#@+
51026 * Constants for install stage
51027 */
51028define('PEAR_VALIDATE_INSTALLING', 1);
51029define('PEAR_VALIDATE_UNINSTALLING', 2); // this is not bit-mapped like the others
51030define('PEAR_VALIDATE_NORMAL', 3);
51031define('PEAR_VALIDATE_DOWNLOADING', 4); // this is not bit-mapped like the others
51032define('PEAR_VALIDATE_PACKAGING', 7);
51033/**#@-*/
51034require_once 'PEAR/Common.php';
51035require_once 'PEAR/Validator/PECL.php';
51036
51037/**
51038 * Validation class for package.xml - channel-level advanced validation
51039 * @category   pear
51040 * @package    PEAR
51041 * @author     Greg Beaver <cellog@php.net>
51042 * @copyright  1997-2009 The Authors
51043 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
51044 * @version    Release: 1.9.4
51045 * @link       http://pear.php.net/package/PEAR
51046 * @since      Class available since Release 1.4.0a1
51047 */
51048class PEAR_Validate
51049{
51050    var $packageregex = _PEAR_COMMON_PACKAGE_NAME_PREG;
51051    /**
51052     * @var PEAR_PackageFile_v1|PEAR_PackageFile_v2
51053     */
51054    var $_packagexml;
51055    /**
51056     * @var int one of the PEAR_VALIDATE_* constants
51057     */
51058    var $_state = PEAR_VALIDATE_NORMAL;
51059    /**
51060     * Format: ('error' => array('field' => name, 'reason' => reason), 'warning' => same)
51061     * @var array
51062     * @access private
51063     */
51064    var $_failures = array('error' => array(), 'warning' => array());
51065
51066    /**
51067     * Override this method to handle validation of normal package names
51068     * @param string
51069     * @return bool
51070     * @access protected
51071     */
51072    function _validPackageName($name)
51073    {
51074        return (bool) preg_match('/^' . $this->packageregex . '\\z/', $name);
51075    }
51076
51077    /**
51078     * @param string package name to validate
51079     * @param string name of channel-specific validation package
51080     * @final
51081     */
51082    function validPackageName($name, $validatepackagename = false)
51083    {
51084        if ($validatepackagename) {
51085            if (strtolower($name) == strtolower($validatepackagename)) {
51086                return (bool) preg_match('/^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*\\z/', $name);
51087            }
51088        }
51089        return $this->_validPackageName($name);
51090    }
51091
51092    /**
51093     * This validates a bundle name, and bundle names must conform
51094     * to the PEAR naming convention, so the method is final and static.
51095     * @param string
51096     * @final
51097     * @static
51098     */
51099    function validGroupName($name)
51100    {
51101        return (bool) preg_match('/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/', $name);
51102    }
51103
51104    /**
51105     * Determine whether $state represents a valid stability level
51106     * @param string
51107     * @return bool
51108     * @static
51109     * @final
51110     */
51111    function validState($state)
51112    {
51113        return in_array($state, array('snapshot', 'devel', 'alpha', 'beta', 'stable'));
51114    }
51115
51116    /**
51117     * Get a list of valid stability levels
51118     * @return array
51119     * @static
51120     * @final
51121     */
51122    function getValidStates()
51123    {
51124        return array('snapshot', 'devel', 'alpha', 'beta', 'stable');
51125    }
51126
51127    /**
51128     * Determine whether a version is a properly formatted version number that can be used
51129     * by version_compare
51130     * @param string
51131     * @return bool
51132     * @static
51133     * @final
51134     */
51135    function validVersion($ver)
51136    {
51137        return (bool) preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
51138    }
51139
51140    /**
51141     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
51142     */
51143    function setPackageFile(&$pf)
51144    {
51145        $this->_packagexml = &$pf;
51146    }
51147
51148    /**
51149     * @access private
51150     */
51151    function _addFailure($field, $reason)
51152    {
51153        $this->_failures['errors'][] = array('field' => $field, 'reason' => $reason);
51154    }
51155
51156    /**
51157     * @access private
51158     */
51159    function _addWarning($field, $reason)
51160    {
51161        $this->_failures['warnings'][] = array('field' => $field, 'reason' => $reason);
51162    }
51163
51164    function getFailures()
51165    {
51166        $failures = $this->_failures;
51167        $this->_failures = array('warnings' => array(), 'errors' => array());
51168        return $failures;
51169    }
51170
51171    /**
51172     * @param int one of the PEAR_VALIDATE_* constants
51173     */
51174    function validate($state = null)
51175    {
51176        if (!isset($this->_packagexml)) {
51177            return false;
51178        }
51179        if ($state !== null) {
51180            $this->_state = $state;
51181        }
51182        $this->_failures = array('warnings' => array(), 'errors' => array());
51183        $this->validatePackageName();
51184        $this->validateVersion();
51185        $this->validateMaintainers();
51186        $this->validateDate();
51187        $this->validateSummary();
51188        $this->validateDescription();
51189        $this->validateLicense();
51190        $this->validateNotes();
51191        if ($this->_packagexml->getPackagexmlVersion() == '1.0') {
51192            $this->validateState();
51193            $this->validateFilelist();
51194        } elseif ($this->_packagexml->getPackagexmlVersion() == '2.0' ||
51195                  $this->_packagexml->getPackagexmlVersion() == '2.1') {
51196            $this->validateTime();
51197            $this->validateStability();
51198            $this->validateDeps();
51199            $this->validateMainFilelist();
51200            $this->validateReleaseFilelist();
51201            //$this->validateGlobalTasks();
51202            $this->validateChangelog();
51203        }
51204        return !((bool) count($this->_failures['errors']));
51205    }
51206
51207    /**
51208     * @access protected
51209     */
51210    function validatePackageName()
51211    {
51212        if ($this->_state == PEAR_VALIDATE_PACKAGING ||
51213              $this->_state == PEAR_VALIDATE_NORMAL) {
51214            if (($this->_packagexml->getPackagexmlVersion() == '2.0' ||
51215                 $this->_packagexml->getPackagexmlVersion() == '2.1') &&
51216                  $this->_packagexml->getExtends()) {
51217                $version = $this->_packagexml->getVersion() . '';
51218                $name = $this->_packagexml->getPackage();
51219                $test = array_shift($a = explode('.', $version));
51220                if ($test == '0') {
51221                    return true;
51222                }
51223                $vlen = strlen($test);
51224                $majver = substr($name, strlen($name) - $vlen);
51225                while ($majver && !is_numeric($majver{0})) {
51226                    $majver = substr($majver, 1);
51227                }
51228                if ($majver != $test) {
51229                    $this->_addWarning('package', "package $name extends package " .
51230                        $this->_packagexml->getExtends() . ' and so the name should ' .
51231                        'have a postfix equal to the major version like "' .
51232                        $this->_packagexml->getExtends() . $test . '"');
51233                    return true;
51234                } elseif (substr($name, 0, strlen($name) - $vlen) !=
51235                            $this->_packagexml->getExtends()) {
51236                    $this->_addWarning('package', "package $name extends package " .
51237                        $this->_packagexml->getExtends() . ' and so the name must ' .
51238                        'be an extension like "' . $this->_packagexml->getExtends() .
51239                        $test . '"');
51240                    return true;
51241                }
51242            }
51243        }
51244        if (!$this->validPackageName($this->_packagexml->getPackage())) {
51245            $this->_addFailure('name', 'package name "' .
51246                $this->_packagexml->getPackage() . '" is invalid');
51247            return false;
51248        }
51249    }
51250
51251    /**
51252     * @access protected
51253     */
51254    function validateVersion()
51255    {
51256        if ($this->_state != PEAR_VALIDATE_PACKAGING) {
51257            if (!$this->validVersion($this->_packagexml->getVersion())) {
51258                $this->_addFailure('version',
51259                    'Invalid version number "' . $this->_packagexml->getVersion() . '"');
51260            }
51261            return false;
51262        }
51263        $version = $this->_packagexml->getVersion();
51264        $versioncomponents = explode('.', $version);
51265        if (count($versioncomponents) != 3) {
51266            $this->_addWarning('version',
51267                'A version number should have 3 decimals (x.y.z)');
51268            return true;
51269        }
51270        $name = $this->_packagexml->getPackage();
51271        // version must be based upon state
51272        switch ($this->_packagexml->getState()) {
51273            case 'snapshot' :
51274                return true;
51275            case 'devel' :
51276                if ($versioncomponents[0] . 'a' == '0a') {
51277                    return true;
51278                }
51279                if ($versioncomponents[0] == 0) {
51280                    $versioncomponents[0] = '0';
51281                    $this->_addWarning('version',
51282                        'version "' . $version . '" should be "' .
51283                        implode('.' ,$versioncomponents) . '"');
51284                } else {
51285                    $this->_addWarning('version',
51286                        'packages with devel stability must be < version 1.0.0');
51287                }
51288                return true;
51289            break;
51290            case 'alpha' :
51291            case 'beta' :
51292                // check for a package that extends a package,
51293                // like Foo and Foo2
51294                if ($this->_state == PEAR_VALIDATE_PACKAGING) {
51295                    if (substr($versioncomponents[2], 1, 2) == 'rc') {
51296                        $this->_addFailure('version', 'Release Candidate versions ' .
51297                            'must have capital RC, not lower-case rc');
51298                        return false;
51299                    }
51300                }
51301                if (!$this->_packagexml->getExtends()) {
51302                    if ($versioncomponents[0] == '1') {
51303                        if ($versioncomponents[2]{0} == '0') {
51304                            if ($versioncomponents[2] == '0') {
51305                                // version 1.*.0000
51306                                $this->_addWarning('version',
51307                                    'version 1.' . $versioncomponents[1] .
51308                                        '.0 probably should not be alpha or beta');
51309                                return true;
51310                            } elseif (strlen($versioncomponents[2]) > 1) {
51311                                // version 1.*.0RC1 or 1.*.0beta24 etc.
51312                                return true;
51313                            } else {
51314                                // version 1.*.0
51315                                $this->_addWarning('version',
51316                                    'version 1.' . $versioncomponents[1] .
51317                                        '.0 probably should not be alpha or beta');
51318                                return true;
51319                            }
51320                        } else {
51321                            $this->_addWarning('version',
51322                                'bugfix versions (1.3.x where x > 0) probably should ' .
51323                                'not be alpha or beta');
51324                            return true;
51325                        }
51326                    } elseif ($versioncomponents[0] != '0') {
51327                        $this->_addWarning('version',
51328                            'major versions greater than 1 are not allowed for packages ' .
51329                            'without an <extends> tag or an identical postfix (foo2 v2.0.0)');
51330                        return true;
51331                    }
51332                    if ($versioncomponents[0] . 'a' == '0a') {
51333                        return true;
51334                    }
51335                    if ($versioncomponents[0] == 0) {
51336                        $versioncomponents[0] = '0';
51337                        $this->_addWarning('version',
51338                            'version "' . $version . '" should be "' .
51339                            implode('.' ,$versioncomponents) . '"');
51340                    }
51341                } else {
51342                    $vlen = strlen($versioncomponents[0] . '');
51343                    $majver = substr($name, strlen($name) - $vlen);
51344                    while ($majver && !is_numeric($majver{0})) {
51345                        $majver = substr($majver, 1);
51346                    }
51347                    if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
51348                        $this->_addWarning('version', 'first version number "' .
51349                            $versioncomponents[0] . '" must match the postfix of ' .
51350                            'package name "' . $name . '" (' .
51351                            $majver . ')');
51352                        return true;
51353                    }
51354                    if ($versioncomponents[0] == $majver) {
51355                        if ($versioncomponents[2]{0} == '0') {
51356                            if ($versioncomponents[2] == '0') {
51357                                // version 2.*.0000
51358                                $this->_addWarning('version',
51359                                    "version $majver." . $versioncomponents[1] .
51360                                        '.0 probably should not be alpha or beta');
51361                                return false;
51362                            } elseif (strlen($versioncomponents[2]) > 1) {
51363                                // version 2.*.0RC1 or 2.*.0beta24 etc.
51364                                return true;
51365                            } else {
51366                                // version 2.*.0
51367                                $this->_addWarning('version',
51368                                    "version $majver." . $versioncomponents[1] .
51369                                        '.0 cannot be alpha or beta');
51370                                return true;
51371                            }
51372                        } else {
51373                            $this->_addWarning('version',
51374                                "bugfix versions ($majver.x.y where y > 0) should " .
51375                                'not be alpha or beta');
51376                            return true;
51377                        }
51378                    } elseif ($versioncomponents[0] != '0') {
51379                        $this->_addWarning('version',
51380                            "only versions 0.x.y and $majver.x.y are allowed for alpha/beta releases");
51381                        return true;
51382                    }
51383                    if ($versioncomponents[0] . 'a' == '0a') {
51384                        return true;
51385                    }
51386                    if ($versioncomponents[0] == 0) {
51387                        $versioncomponents[0] = '0';
51388                        $this->_addWarning('version',
51389                            'version "' . $version . '" should be "' .
51390                            implode('.' ,$versioncomponents) . '"');
51391                    }
51392                }
51393                return true;
51394            break;
51395            case 'stable' :
51396                if ($versioncomponents[0] == '0') {
51397                    $this->_addWarning('version', 'versions less than 1.0.0 cannot ' .
51398                    'be stable');
51399                    return true;
51400                }
51401                if (!is_numeric($versioncomponents[2])) {
51402                    if (preg_match('/\d+(rc|a|alpha|b|beta)\d*/i',
51403                          $versioncomponents[2])) {
51404                        $this->_addWarning('version', 'version "' . $version . '" or any ' .
51405                            'RC/beta/alpha version cannot be stable');
51406                        return true;
51407                    }
51408                }
51409                // check for a package that extends a package,
51410                // like Foo and Foo2
51411                if ($this->_packagexml->getExtends()) {
51412                    $vlen = strlen($versioncomponents[0] . '');
51413                    $majver = substr($name, strlen($name) - $vlen);
51414                    while ($majver && !is_numeric($majver{0})) {
51415                        $majver = substr($majver, 1);
51416                    }
51417                    if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
51418                        $this->_addWarning('version', 'first version number "' .
51419                            $versioncomponents[0] . '" must match the postfix of ' .
51420                            'package name "' . $name . '" (' .
51421                            $majver . ')');
51422                        return true;
51423                    }
51424                } elseif ($versioncomponents[0] > 1) {
51425                    $this->_addWarning('version', 'major version x in x.y.z may not be greater than ' .
51426                        '1 for any package that does not have an <extends> tag');
51427                }
51428                return true;
51429            break;
51430            default :
51431                return false;
51432            break;
51433        }
51434    }
51435
51436    /**
51437     * @access protected
51438     */
51439    function validateMaintainers()
51440    {
51441        // maintainers can only be truly validated server-side for most channels
51442        // but allow this customization for those who wish it
51443        return true;
51444    }
51445
51446    /**
51447     * @access protected
51448     */
51449    function validateDate()
51450    {
51451        if ($this->_state == PEAR_VALIDATE_NORMAL ||
51452              $this->_state == PEAR_VALIDATE_PACKAGING) {
51453
51454            if (!preg_match('/(\d\d\d\d)\-(\d\d)\-(\d\d)/',
51455                  $this->_packagexml->getDate(), $res) ||
51456                  count($res) < 4
51457                  || !checkdate($res[2], $res[3], $res[1])
51458                ) {
51459                $this->_addFailure('date', 'invalid release date "' .
51460                    $this->_packagexml->getDate() . '"');
51461                return false;
51462            }
51463
51464            if ($this->_state == PEAR_VALIDATE_PACKAGING &&
51465                  $this->_packagexml->getDate() != date('Y-m-d')) {
51466                $this->_addWarning('date', 'Release Date "' .
51467                    $this->_packagexml->getDate() . '" is not today');
51468            }
51469        }
51470        return true;
51471    }
51472
51473    /**
51474     * @access protected
51475     */
51476    function validateTime()
51477    {
51478        if (!$this->_packagexml->getTime()) {
51479            // default of no time value set
51480            return true;
51481        }
51482
51483        // packager automatically sets time, so only validate if pear validate is called
51484        if ($this->_state = PEAR_VALIDATE_NORMAL) {
51485            if (!preg_match('/\d\d:\d\d:\d\d/',
51486                  $this->_packagexml->getTime())) {
51487                $this->_addFailure('time', 'invalid release time "' .
51488                    $this->_packagexml->getTime() . '"');
51489                return false;
51490            }
51491
51492            $result = preg_match('|\d{2}\:\d{2}\:\d{2}|', $this->_packagexml->getTime(), $matches);
51493            if ($result === false || empty($matches)) {
51494                $this->_addFailure('time', 'invalid release time "' .
51495                    $this->_packagexml->getTime() . '"');
51496                return false;
51497            }
51498        }
51499
51500        return true;
51501    }
51502
51503    /**
51504     * @access protected
51505     */
51506    function validateState()
51507    {
51508        // this is the closest to "final" php4 can get
51509        if (!PEAR_Validate::validState($this->_packagexml->getState())) {
51510            if (strtolower($this->_packagexml->getState() == 'rc')) {
51511                $this->_addFailure('state', 'RC is not a state, it is a version ' .
51512                    'postfix, use ' . $this->_packagexml->getVersion() . 'RC1, state beta');
51513            }
51514            $this->_addFailure('state', 'invalid release state "' .
51515                $this->_packagexml->getState() . '", must be one of: ' .
51516                implode(', ', PEAR_Validate::getValidStates()));
51517            return false;
51518        }
51519        return true;
51520    }
51521
51522    /**
51523     * @access protected
51524     */
51525    function validateStability()
51526    {
51527        $ret = true;
51528        $packagestability = $this->_packagexml->getState();
51529        $apistability = $this->_packagexml->getState('api');
51530        if (!PEAR_Validate::validState($packagestability)) {
51531            $this->_addFailure('state', 'invalid release stability "' .
51532                $this->_packagexml->getState() . '", must be one of: ' .
51533                implode(', ', PEAR_Validate::getValidStates()));
51534            $ret = false;
51535        }
51536        $apistates = PEAR_Validate::getValidStates();
51537        array_shift($apistates); // snapshot is not allowed
51538        if (!in_array($apistability, $apistates)) {
51539            $this->_addFailure('state', 'invalid API stability "' .
51540                $this->_packagexml->getState('api') . '", must be one of: ' .
51541                implode(', ', $apistates));
51542            $ret = false;
51543        }
51544        return $ret;
51545    }
51546
51547    /**
51548     * @access protected
51549     */
51550    function validateSummary()
51551    {
51552        return true;
51553    }
51554
51555    /**
51556     * @access protected
51557     */
51558    function validateDescription()
51559    {
51560        return true;
51561    }
51562
51563    /**
51564     * @access protected
51565     */
51566    function validateLicense()
51567    {
51568        return true;
51569    }
51570
51571    /**
51572     * @access protected
51573     */
51574    function validateNotes()
51575    {
51576        return true;
51577    }
51578
51579    /**
51580     * for package.xml 2.0 only - channels can't use package.xml 1.0
51581     * @access protected
51582     */
51583    function validateDependencies()
51584    {
51585        return true;
51586    }
51587
51588    /**
51589     * for package.xml 1.0 only
51590     * @access private
51591     */
51592    function _validateFilelist()
51593    {
51594        return true; // placeholder for now
51595    }
51596
51597    /**
51598     * for package.xml 2.0 only
51599     * @access protected
51600     */
51601    function validateMainFilelist()
51602    {
51603        return true; // placeholder for now
51604    }
51605
51606    /**
51607     * for package.xml 2.0 only
51608     * @access protected
51609     */
51610    function validateReleaseFilelist()
51611    {
51612        return true; // placeholder for now
51613    }
51614
51615    /**
51616     * @access protected
51617     */
51618    function validateChangelog()
51619    {
51620        return true;
51621    }
51622
51623    /**
51624     * @access protected
51625     */
51626    function validateFilelist()
51627    {
51628        return true;
51629    }
51630
51631    /**
51632     * @access protected
51633     */
51634    function validateDeps()
51635    {
51636        return true;
51637    }
51638}PEAR-1.9.4/PEAR/XMLParser.php0000644000076500000240000001600111605156614014307 0ustar  helgistaff<?php
51639/**
51640 * PEAR_XMLParser
51641 *
51642 * PHP versions 4 and 5
51643 *
51644 * @category   pear
51645 * @package    PEAR
51646 * @author     Greg Beaver <cellog@php.net>
51647 * @author     Stephan Schmidt (original XML_Unserializer code)
51648 * @copyright  1997-2009 The Authors
51649 * @license   http://opensource.org/licenses/bsd-license New BSD License
51650 * @version    CVS: $Id: XMLParser.php 313023 2011-07-06 19:17:11Z dufuz $
51651 * @link       http://pear.php.net/package/PEAR
51652 * @since      File available since Release 1.4.0a1
51653 */
51654
51655/**
51656 * Parser for any xml file
51657 * @category  pear
51658 * @package   PEAR
51659 * @author    Greg Beaver <cellog@php.net>
51660 * @author    Stephan Schmidt (original XML_Unserializer code)
51661 * @copyright 1997-2009 The Authors
51662 * @license   http://opensource.org/licenses/bsd-license New BSD License
51663 * @version   Release: 1.9.4
51664 * @link      http://pear.php.net/package/PEAR
51665 * @since     Class available since Release 1.4.0a1
51666 */
51667class PEAR_XMLParser
51668{
51669    /**
51670     * unserilialized data
51671     * @var string $_serializedData
51672     */
51673    var $_unserializedData = null;
51674
51675    /**
51676     * name of the root tag
51677     * @var string $_root
51678     */
51679    var $_root = null;
51680
51681    /**
51682     * stack for all data that is found
51683     * @var array    $_dataStack
51684     */
51685    var $_dataStack = array();
51686
51687    /**
51688     * stack for all values that are generated
51689     * @var array    $_valStack
51690     */
51691    var $_valStack = array();
51692
51693    /**
51694     * current tag depth
51695     * @var int    $_depth
51696     */
51697    var $_depth = 0;
51698
51699    /**
51700     * The XML encoding to use
51701     * @var string $encoding
51702     */
51703    var $encoding = 'ISO-8859-1';
51704
51705    /**
51706     * @return array
51707     */
51708    function getData()
51709    {
51710        return $this->_unserializedData;
51711    }
51712
51713    /**
51714     * @param string xml content
51715     * @return true|PEAR_Error
51716     */
51717    function parse($data)
51718    {
51719        if (!extension_loaded('xml')) {
51720            include_once 'PEAR.php';
51721            return PEAR::raiseError("XML Extension not found", 1);
51722        }
51723        $this->_dataStack =  $this->_valStack = array();
51724        $this->_depth = 0;
51725
51726        if (
51727            strpos($data, 'encoding="UTF-8"')
51728            || strpos($data, 'encoding="utf-8"')
51729            || strpos($data, "encoding='UTF-8'")
51730            || strpos($data, "encoding='utf-8'")
51731        ) {
51732            $this->encoding = 'UTF-8';
51733        }
51734
51735        if (version_compare(phpversion(), '5.0.0', 'lt') && $this->encoding == 'UTF-8') {
51736            $data = utf8_decode($data);
51737            $this->encoding = 'ISO-8859-1';
51738        }
51739
51740        $xp = xml_parser_create($this->encoding);
51741        xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, 0);
51742        xml_set_object($xp, $this);
51743        xml_set_element_handler($xp, 'startHandler', 'endHandler');
51744        xml_set_character_data_handler($xp, 'cdataHandler');
51745        if (!xml_parse($xp, $data)) {
51746            $msg = xml_error_string(xml_get_error_code($xp));
51747            $line = xml_get_current_line_number($xp);
51748            xml_parser_free($xp);
51749            include_once 'PEAR.php';
51750            return PEAR::raiseError("XML Error: '$msg' on line '$line'", 2);
51751        }
51752        xml_parser_free($xp);
51753        return true;
51754    }
51755
51756    /**
51757     * Start element handler for XML parser
51758     *
51759     * @access private
51760     * @param  object $parser  XML parser object
51761     * @param  string $element XML element
51762     * @param  array  $attribs attributes of XML tag
51763     * @return void
51764     */
51765    function startHandler($parser, $element, $attribs)
51766    {
51767        $this->_depth++;
51768        $this->_dataStack[$this->_depth] = null;
51769
51770        $val = array(
51771            'name'         => $element,
51772            'value'        => null,
51773            'type'         => 'string',
51774            'childrenKeys' => array(),
51775            'aggregKeys'   => array()
51776       );
51777
51778        if (count($attribs) > 0) {
51779            $val['children'] = array();
51780            $val['type'] = 'array';
51781            $val['children']['attribs'] = $attribs;
51782        }
51783
51784        array_push($this->_valStack, $val);
51785    }
51786
51787    /**
51788     * post-process data
51789     *
51790     * @param string $data
51791     * @param string $element element name
51792     */
51793    function postProcess($data, $element)
51794    {
51795        return trim($data);
51796    }
51797
51798    /**
51799     * End element handler for XML parser
51800     *
51801     * @access private
51802     * @param  object XML parser object
51803     * @param  string
51804     * @return void
51805     */
51806    function endHandler($parser, $element)
51807    {
51808        $value = array_pop($this->_valStack);
51809        $data  = $this->postProcess($this->_dataStack[$this->_depth], $element);
51810
51811        // adjust type of the value
51812        switch (strtolower($value['type'])) {
51813            // unserialize an array
51814            case 'array':
51815                if ($data !== '') {
51816                    $value['children']['_content'] = $data;
51817                }
51818
51819                $value['value'] = isset($value['children']) ? $value['children'] : array();
51820                break;
51821
51822            /*
51823             * unserialize a null value
51824             */
51825            case 'null':
51826                $data = null;
51827                break;
51828
51829            /*
51830             * unserialize any scalar value
51831             */
51832            default:
51833                settype($data, $value['type']);
51834                $value['value'] = $data;
51835                break;
51836        }
51837
51838        $parent = array_pop($this->_valStack);
51839        if ($parent === null) {
51840            $this->_unserializedData = &$value['value'];
51841            $this->_root = &$value['name'];
51842            return true;
51843        }
51844
51845        // parent has to be an array
51846        if (!isset($parent['children']) || !is_array($parent['children'])) {
51847            $parent['children'] = array();
51848            if ($parent['type'] != 'array') {
51849                $parent['type'] = 'array';
51850            }
51851        }
51852
51853        if (!empty($value['name'])) {
51854            // there already has been a tag with this name
51855            if (in_array($value['name'], $parent['childrenKeys'])) {
51856                // no aggregate has been created for this tag
51857                if (!in_array($value['name'], $parent['aggregKeys'])) {
51858                    if (isset($parent['children'][$value['name']])) {
51859                        $parent['children'][$value['name']] = array($parent['children'][$value['name']]);
51860                    } else {
51861                        $parent['children'][$value['name']] = array();
51862                    }
51863                    array_push($parent['aggregKeys'], $value['name']);
51864                }
51865                array_push($parent['children'][$value['name']], $value['value']);
51866            } else {
51867                $parent['children'][$value['name']] = &$value['value'];
51868                array_push($parent['childrenKeys'], $value['name']);
51869            }
51870        } else {
51871            array_push($parent['children'],$value['value']);
51872        }
51873        array_push($this->_valStack, $parent);
51874
51875        $this->_depth--;
51876    }
51877
51878    /**
51879     * Handler for character data
51880     *
51881     * @access private
51882     * @param  object XML parser object
51883     * @param  string CDATA
51884     * @return void
51885     */
51886    function cdataHandler($parser, $cdata)
51887    {
51888        $this->_dataStack[$this->_depth] .= $cdata;
51889    }
51890}PEAR-1.9.4/scripts/pear.bat0000755000076500000240000001036111605156614014346 0ustar  helgistaff@ECHO OFF
51891
51892REM ----------------------------------------------------------------------
51893REM PHP version 5
51894REM ----------------------------------------------------------------------
51895REM Copyright (c) 1997-2010 The Authors
51896REM ----------------------------------------------------------------------
51897REM http://opensource.org/licenses/bsd-license.php New BSD License
51898REM ----------------------------------------------------------------------
51899REM  Authors:     Alexander Merz (alexmerz@php.net)
51900REM ----------------------------------------------------------------------
51901REM
51902REM  Last updated 12/29/2004 ($Id$ is not replaced if the file is binary)
51903
51904REM change this lines to match the paths of your system
51905REM -------------------
51906
51907
51908REM Test to see if this is a raw pear.bat (uninstalled version)
51909SET TMPTMPTMPTMPT=@includ
51910SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
51911FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)
51912
51913REM Check PEAR global ENV, set them if they do not exist
51914IF "%PHP_PEAR_INSTALL_DIR%"=="" SET "PHP_PEAR_INSTALL_DIR=@include_path@"
51915IF "%PHP_PEAR_BIN_DIR%"=="" SET "PHP_PEAR_BIN_DIR=@bin_dir@"
51916IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=@php_bin@"
51917
51918GOTO :INSTALLED
51919
51920:NOTINSTALLED
51921ECHO WARNING: This is a raw, uninstalled pear.bat
51922
51923REM Check to see if we can grab the directory of this file (Windows NT+)
51924IF %~n0 == pear (
51925FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
51926SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
51927echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
51928"%PHP_PEAR_PHP_BIN%" -v
51929GOTO :NEXTTEST
51930))
51931GOTO :FAILAUTODETECT
51932:NEXTTEST
51933IF "%PHP_PEAR_PHP_BIN%" NEQ "" (
51934
51935REM We can use this PHP to run a temporary php file to get the dirname of pear
51936
51937echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
51938"%PHP_PEAR_PHP_BIN%" ~~getloc.php
51939set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
51940DEL ~a.a
51941DEL ~~getloc.php
51942set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"
51943
51944REM Make sure there is a pearcmd.php at our disposal
51945
51946IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
51947IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
51948IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
51949IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
51950)
51951)
51952GOTO :INSTALLED
51953) ELSE (
51954REM Windows Me/98 cannot succeed, so allow the batch to fail
51955)
51956:FAILAUTODETECT
51957echo WARNING: failed to auto-detect pear information
51958:INSTALLED
51959
51960REM Check Folders and files
51961IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%" GOTO PEAR_INSTALL_ERROR
51962IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" GOTO PEAR_INSTALL_ERROR2
51963IF NOT EXIST "%PHP_PEAR_BIN_DIR%" GOTO PEAR_BIN_ERROR
51964IF NOT EXIST "%PHP_PEAR_PHP_BIN%" GOTO PEAR_PHPBIN_ERROR
51965
51966REM launch pearcmd
51967GOTO RUN
51968:PEAR_INSTALL_ERROR
51969ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
51970ECHO Please fix it using your environment variable or modify
51971ECHO the default value in pear.bat
51972ECHO The current value is:
51973ECHO %PHP_PEAR_INSTALL_DIR%
51974GOTO END
51975:PEAR_INSTALL_ERROR2
51976ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
51977ECHO pearcmd.php could not be found there.
51978ECHO Please fix it using your environment variable or modify
51979ECHO the default value in pear.bat
51980ECHO The current value is:
51981ECHO %PHP_PEAR_INSTALL_DIR%
51982GOTO END
51983:PEAR_BIN_ERROR
51984ECHO PHP_PEAR_BIN_DIR is not set correctly.
51985ECHO Please fix it using your environment variable or modify
51986ECHO the default value in pear.bat
51987ECHO The current value is:
51988ECHO %PHP_PEAR_BIN_DIR%
51989GOTO END
51990:PEAR_PHPBIN_ERROR
51991ECHO PHP_PEAR_PHP_BIN is not set correctly.
51992ECHO Please fix it using your environment variable or modify
51993ECHO the default value in pear.bat
51994ECHO The current value is:
51995ECHO %PHP_PEAR_PHP_BIN%
51996GOTO END
51997:RUN
51998"%PHP_PEAR_PHP_BIN%" -C -d date.timezone=UTC -d output_buffering=1 -d safe_mode=0 -d open_basedir="" -d auto_prepend_file="" -d auto_append_file="" -d variables_order=EGPCS -d register_argc_argv="On" -d "include_path='%PHP_PEAR_INSTALL_DIR%'" -f "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" -- %1 %2 %3 %4 %5 %6 %7 %8 %9
51999:END
52000@ECHO ONPEAR-1.9.4/scripts/peardev.bat0000644000076500000240000001113711605156614015044 0ustar  helgistaff@ECHO OFF
52001
52002REM ----------------------------------------------------------------------
52003REM PHP version 5
52004REM ----------------------------------------------------------------------
52005REM Copyright (c) 1997-2004 The PHP Group
52006REM ----------------------------------------------------------------------
52007REM  This source file is subject to version 3.0 of the PHP license,
52008REM  that is bundled with this package in the file LICENSE, and is
52009REM  available at through the world-wide-web at
52010REM  http://www.php.net/license/3_0.txt.
52011REM  If you did not receive a copy of the PHP license and are unable to
52012REM  obtain it through the world-wide-web, please send a note to
52013REM  license@php.net so we can mail you a copy immediately.
52014REM ----------------------------------------------------------------------
52015REM  Authors:     Alexander Merz (alexmerz@php.net)
52016REM ----------------------------------------------------------------------
52017REM
52018REM  $Id: peardev.bat,v 1.6 2007-09-03 03:00:17 cellog Exp $
52019
52020REM change this lines to match the paths of your system
52021REM -------------------
52022
52023
52024REM Test to see if this is a raw pear.bat (uninstalled version)
52025SET TMPTMPTMPTMPT=@includ
52026SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
52027FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)
52028
52029REM Check PEAR global ENV, set them if they do not exist
52030IF "%PHP_PEAR_INSTALL_DIR%"=="" SET "PHP_PEAR_INSTALL_DIR=@include_path@"
52031IF "%PHP_PEAR_BIN_DIR%"=="" SET "PHP_PEAR_BIN_DIR=@bin_dir@"
52032IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=@php_bin@"
52033GOTO :INSTALLED
52034
52035:NOTINSTALLED
52036ECHO WARNING: This is a raw, uninstalled pear.bat
52037
52038REM Check to see if we can grab the directory of this file (Windows NT+)
52039IF %~n0 == pear (
52040FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
52041SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
52042echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
52043"%PHP_PEAR_PHP_BIN%" -v
52044GOTO :NEXTTEST
52045))
52046GOTO :FAILAUTODETECT
52047:NEXTTEST
52048IF "%PHP_PEAR_PHP_BIN%" NEQ "" (
52049
52050REM We can use this PHP to run a temporary php file to get the dirname of pear
52051
52052echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
52053"%PHP_PEAR_PHP_BIN%" ~~getloc.php
52054set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
52055DEL ~a.a
52056DEL ~~getloc.php
52057set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"
52058
52059REM Make sure there is a pearcmd.php at our disposal
52060
52061IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
52062IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
52063IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
52064IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
52065)
52066)
52067GOTO :INSTALLED
52068) ELSE (
52069REM Windows Me/98 cannot succeed, so allow the batch to fail
52070)
52071:FAILAUTODETECT
52072echo WARNING: failed to auto-detect pear information
52073:INSTALLED
52074
52075REM Check Folders and files
52076IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%" GOTO PEAR_INSTALL_ERROR
52077IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" GOTO PEAR_INSTALL_ERROR2
52078IF NOT EXIST "%PHP_PEAR_BIN_DIR%" GOTO PEAR_BIN_ERROR
52079IF NOT EXIST "%PHP_PEAR_PHP_BIN%" GOTO PEAR_PHPBIN_ERROR
52080REM launch pearcmd
52081GOTO RUN
52082:PEAR_INSTALL_ERROR
52083ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
52084ECHO Please fix it using your environment variable or modify
52085ECHO the default value in pear.bat
52086ECHO The current value is:
52087ECHO %PHP_PEAR_INSTALL_DIR%
52088GOTO END
52089:PEAR_INSTALL_ERROR2
52090ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
52091ECHO pearcmd.php could not be found there.
52092ECHO Please fix it using your environment variable or modify
52093ECHO the default value in pear.bat
52094ECHO The current value is:
52095ECHO %PHP_PEAR_INSTALL_DIR%
52096GOTO END
52097:PEAR_BIN_ERROR
52098ECHO PHP_PEAR_BIN_DIR is not set correctly.
52099ECHO Please fix it using your environment variable or modify
52100ECHO the default value in pear.bat
52101ECHO The current value is:
52102ECHO %PHP_PEAR_BIN_DIR%
52103GOTO END
52104:PEAR_PHPBIN_ERROR
52105ECHO PHP_PEAR_PHP_BIN is not set correctly.
52106ECHO Please fix it using your environment variable or modify
52107ECHO the default value in pear.bat
52108ECHO The current value is:
52109ECHO %PHP_PEAR_PHP_BIN%
52110GOTO END
52111:RUN
52112"%PHP_PEAR_PHP_BIN%" -C -d date.timezone=UTC -d memory_limit="-1" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d auto_append_file="" -d variables_order=EGPCS -d open_basedir="" -d output_buffering=1 -d "include_path='%PHP_PEAR_INSTALL_DIR%'" -f "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" -- %1 %2 %3 %4 %5 %6 %7 %8 %9
52113:END
52114@ECHO ONPEAR-1.9.4/scripts/pecl.bat0000644000076500000240000001103011605156614014331 0ustar  helgistaff@ECHO OFF
52115
52116REM ----------------------------------------------------------------------
52117REM PHP version 5
52118REM ----------------------------------------------------------------------
52119REM Copyright (c) 1997-2004 The PHP Group
52120REM ----------------------------------------------------------------------
52121REM  This source file is subject to version 3.0 of the PHP license,
52122REM  that is bundled with this package in the file LICENSE, and is
52123REM  available at through the world-wide-web at
52124REM  http://www.php.net/license/3_0.txt.
52125REM  If you did not receive a copy of the PHP license and are unable to
52126REM  obtain it through the world-wide-web, please send a note to
52127REM  license@php.net so we can mail you a copy immediately.
52128REM ----------------------------------------------------------------------
52129REM  Authors:     Alexander Merz (alexmerz@php.net)
52130REM ----------------------------------------------------------------------
52131REM
52132REM  Last updated 02/08/2004 ($Id$ is not replaced if the file is binary)
52133
52134REM change this lines to match the paths of your system
52135REM -------------------
52136
52137
52138REM Test to see if this is a raw pear.bat (uninstalled version)
52139SET TMPTMPTMPTMPT=@includ
52140SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
52141FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)
52142
52143REM Check PEAR global ENV, set them if they do not exist
52144IF "%PHP_PEAR_INSTALL_DIR%"=="" SET "PHP_PEAR_INSTALL_DIR=@include_path@"
52145IF "%PHP_PEAR_BIN_DIR%"=="" SET "PHP_PEAR_BIN_DIR=@bin_dir@"
52146IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=@php_bin@"
52147GOTO :INSTALLED
52148
52149:NOTINSTALLED
52150ECHO WARNING: This is a raw, uninstalled pear.bat
52151
52152REM Check to see if we can grab the directory of this file (Windows NT+)
52153IF %~n0 == pear (
52154FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
52155SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
52156echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
52157"%PHP_PEAR_PHP_BIN%" -v
52158GOTO :NEXTTEST
52159))
52160GOTO :FAILAUTODETECT
52161:NEXTTEST
52162IF "%PHP_PEAR_PHP_BIN%" NEQ "" (
52163
52164REM We can use this PHP to run a temporary php file to get the dirname of pear
52165
52166echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
52167"%PHP_PEAR_PHP_BIN%" ~~getloc.php
52168set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
52169DEL ~a.a
52170DEL ~~getloc.php
52171set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"
52172
52173REM Make sure there is a pearcmd.php at our disposal
52174
52175IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
52176IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
52177IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
52178IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
52179)
52180)
52181GOTO :INSTALLED
52182) ELSE (
52183REM Windows Me/98 cannot succeed, so allow the batch to fail
52184)
52185:FAILAUTODETECT
52186echo WARNING: failed to auto-detect pear information
52187:INSTALLED
52188
52189REM Check Folders and files
52190IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%" GOTO PEAR_INSTALL_ERROR
52191IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" GOTO PEAR_INSTALL_ERROR2
52192IF NOT EXIST "%PHP_PEAR_BIN_DIR%" GOTO PEAR_BIN_ERROR
52193IF NOT EXIST "%PHP_PEAR_PHP_BIN%" GOTO PEAR_PHPBIN_ERROR
52194REM launch pearcmd
52195GOTO RUN
52196:PEAR_INSTALL_ERROR
52197ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
52198ECHO Please fix it using your environment variable or modify
52199ECHO the default value in pear.bat
52200ECHO The current value is:
52201ECHO %PHP_PEAR_INSTALL_DIR%
52202GOTO END
52203:PEAR_INSTALL_ERROR2
52204ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
52205ECHO pearcmd.php could not be found there.
52206ECHO Please fix it using your environment variable or modify
52207ECHO the default value in pear.bat
52208ECHO The current value is:
52209ECHO %PHP_PEAR_INSTALL_DIR%
52210GOTO END
52211:PEAR_BIN_ERROR
52212ECHO PHP_PEAR_BIN_DIR is not set correctly.
52213ECHO Please fix it using your environment variable or modify
52214ECHO the default value in pear.bat
52215ECHO The current value is:
52216ECHO %PHP_PEAR_BIN_DIR%
52217GOTO END
52218:PEAR_PHPBIN_ERROR
52219ECHO PHP_PEAR_PHP_BIN is not set correctly.
52220ECHO Please fix it using your environment variable or modify
52221ECHO the default value in pear.bat
52222ECHO The current value is:
52223ECHO %PHP_PEAR_PHP_BIN%
52224GOTO END
52225:RUN
52226"%PHP_PEAR_PHP_BIN%" -C -n -d date.timezone=UTC -d output_buffering=1 -d safe_mode=0 -d "include_path='%PHP_PEAR_INSTALL_DIR%'" -d register_argc_argv="On" -d variables_order=EGPCS -f "%PHP_PEAR_INSTALL_DIR%\peclcmd.php" -- %1 %2 %3 %4 %5 %6 %7 %8 %9
52227:END
52228@ECHO ONPEAR-1.9.4/scripts/pear.sh0000644000076500000240000000140411605156614014205 0ustar  helgistaff#!/bin/sh
52229
52230# first find which PHP binary to use
52231if test "x$PHP_PEAR_PHP_BIN" != "x"; then
52232  PHP="$PHP_PEAR_PHP_BIN"
52233else
52234  if test "@php_bin@" = '@'php_bin'@'; then
52235    PHP=php
52236  else
52237    PHP="@php_bin@"
52238  fi
52239fi
52240
52241# then look for the right pear include dir
52242if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
52243  INCDIR=$PHP_PEAR_INSTALL_DIR
52244  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
52245else
52246  if test "@php_dir@" = '@'php_dir'@'; then
52247    INCDIR=`dirname $0`
52248    INCARG=""
52249  else
52250    INCDIR="@php_dir@"
52251    INCARG="-d include_path=@php_dir@"
52252  fi
52253fi
52254
52255exec $PHP -C -q $INCARG -d date.timezone=UTC -d output_buffering=1 -d variables_order=EGPCS -d open_basedir="" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d auto_append_file="" $INCDIR/pearcmd.php "$@"
52256PEAR-1.9.4/scripts/peardev.sh0000644000076500000240000000143111605156614014704 0ustar  helgistaff#!/bin/sh
52257
52258# first find which PHP binary to use
52259if test "x$PHP_PEAR_PHP_BIN" != "x"; then
52260  PHP="$PHP_PEAR_PHP_BIN"
52261else
52262  if test "@php_bin@" = '@'php_bin'@'; then
52263    PHP=php
52264  else
52265    PHP="@php_bin@"
52266  fi
52267fi
52268
52269# then look for the right pear include dir
52270if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
52271  INCDIR=$PHP_PEAR_INSTALL_DIR
52272  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
52273else
52274  if test "@php_dir@" = '@'php_dir'@'; then
52275    INCDIR=`dirname $0`
52276    INCARG=""
52277  else
52278    INCDIR="@php_dir@"
52279    INCARG="-d include_path=@php_dir@"
52280  fi
52281fi
52282
52283exec $PHP -d date.timezone=UTC -d memory_limit="-1" -C -q $INCARG -d output_buffering=1 -d open_basedir="" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d variables_order=EGPCS -d auto_append_file="" $INCDIR/pearcmd.php "$@"
52284PEAR-1.9.4/scripts/pecl.sh0000644000076500000240000000130511605156614014201 0ustar  helgistaff#!/bin/sh
52285
52286# first find which PHP binary to use
52287if test "x$PHP_PEAR_PHP_BIN" != "x"; then
52288  PHP="$PHP_PEAR_PHP_BIN"
52289else
52290  if test "@php_bin@" = '@'php_bin'@'; then
52291    PHP=php
52292  else
52293    PHP="@php_bin@"
52294  fi
52295fi
52296
52297# then look for the right pear include dir
52298if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
52299  INCDIR=$PHP_PEAR_INSTALL_DIR
52300  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
52301else
52302  if test "@php_dir@" = '@'php_dir'@'; then
52303    INCDIR=`dirname $0`
52304    INCARG=""
52305  else
52306    INCDIR="@php_dir@"
52307    INCARG="-d include_path=@php_dir@"
52308  fi
52309fi
52310
52311exec $PHP -C -n -q $INCARG -d date.timezone=UTC -d output_buffering=1 -d variables_order=EGPCS -d safe_mode=0 -d register_argc_argv="On" $INCDIR/peclcmd.php "$@"
52312PEAR-1.9.4/scripts/pearcmd.php0000644000076500000240000003414511605156614015056 0ustar  helgistaff<?php
52313/**
52314 * PEAR, the PHP Extension and Application Repository
52315 *
52316 * Command line interface
52317 *
52318 * PHP versions 4 and 5
52319 *
52320 * @category   pear
52321 * @package    PEAR
52322 * @author     Stig Bakken <ssb@php.net>
52323 * @author     Tomas V.V.Cox <cox@idecnet.com>
52324 * @copyright  1997-2009 The Authors
52325 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
52326 * @version    CVS: $Id: pearcmd.php 313023 2011-07-06 19:17:11Z dufuz $
52327 * @link       http://pear.php.net/package/PEAR
52328 */
52329
52330ob_end_clean();
52331if (!defined('PEAR_RUNTYPE')) {
52332    // this is defined in peclcmd.php as 'pecl'
52333    define('PEAR_RUNTYPE', 'pear');
52334}
52335define('PEAR_IGNORE_BACKTRACE', 1);
52336/**
52337 * @nodep Gtk
52338 */
52339if ('@include_path@' != '@'.'include_path'.'@') {
52340    ini_set('include_path', '@include_path@');
52341    $raw = false;
52342} else {
52343    // this is a raw, uninstalled pear, either a cvs checkout, or php distro
52344    $raw = true;
52345}
52346@ini_set('allow_url_fopen', true);
52347if (!ini_get('safe_mode')) {
52348    @set_time_limit(0);
52349}
52350ob_implicit_flush(true);
52351@ini_set('track_errors', true);
52352@ini_set('html_errors', false);
52353@ini_set('magic_quotes_runtime', false);
52354$_PEAR_PHPDIR = '#$%^&*';
52355set_error_handler('error_handler');
52356
52357$pear_package_version = "@pear_version@";
52358
52359require_once 'PEAR.php';
52360require_once 'PEAR/Frontend.php';
52361require_once 'PEAR/Config.php';
52362require_once 'PEAR/Command.php';
52363require_once 'Console/Getopt.php';
52364
52365
52366PEAR_Command::setFrontendType('CLI');
52367$all_commands = PEAR_Command::getCommands();
52368
52369// remove this next part when we stop supporting that crap-ass PHP 4.2
52370if (!isset($_SERVER['argv']) && !isset($argv) && !isset($HTTP_SERVER_VARS['argv'])) {
52371    echo 'ERROR: either use the CLI php executable, or set register_argc_argv=On in php.ini';
52372    exit(1);
52373}
52374
52375$argv = Console_Getopt::readPHPArgv();
52376// fix CGI sapi oddity - the -- in pear.bat/pear is not removed
52377if (php_sapi_name() != 'cli' && isset($argv[1]) && $argv[1] == '--') {
52378    unset($argv[1]);
52379    $argv = array_values($argv);
52380}
52381$progname = PEAR_RUNTYPE;
52382array_shift($argv);
52383$options = Console_Getopt::getopt2($argv, "c:C:d:D:Gh?sSqu:vV");
52384if (PEAR::isError($options)) {
52385    usage($options);
52386}
52387
52388$opts = $options[0];
52389
52390$fetype = 'CLI';
52391if ($progname == 'gpear' || $progname == 'pear-gtk') {
52392    $fetype = 'Gtk';
52393} else {
52394    foreach ($opts as $opt) {
52395        if ($opt[0] == 'G') {
52396            $fetype = 'Gtk';
52397        }
52398    }
52399}
52400//Check if Gtk and PHP >= 5.1.0
52401if ($fetype == 'Gtk' && version_compare(phpversion(), '5.1.0', '>=')) {
52402    $fetype = 'Gtk2';
52403}
52404
52405$pear_user_config = '';
52406$pear_system_config = '';
52407$store_user_config = false;
52408$store_system_config = false;
52409$verbose = 1;
52410
52411foreach ($opts as $opt) {
52412    switch ($opt[0]) {
52413        case 'c':
52414            $pear_user_config = $opt[1];
52415            break;
52416        case 'C':
52417            $pear_system_config = $opt[1];
52418            break;
52419    }
52420}
52421
52422PEAR_Command::setFrontendType($fetype);
52423$ui = &PEAR_Command::getFrontendObject();
52424$config = &PEAR_Config::singleton($pear_user_config, $pear_system_config);
52425
52426if (PEAR::isError($config)) {
52427    $_file = '';
52428    if ($pear_user_config !== false) {
52429       $_file .= $pear_user_config;
52430    }
52431    if ($pear_system_config !== false) {
52432       $_file .= '/' . $pear_system_config;
52433    }
52434    if ($_file == '/') {
52435        $_file = 'The default config file';
52436    }
52437    $config->getMessage();
52438    $ui->outputData("ERROR: $_file is not a valid config file or is corrupted.");
52439    // We stop, we have no idea where we are :)
52440    exit(1);
52441}
52442
52443// this is used in the error handler to retrieve a relative path
52444$_PEAR_PHPDIR = $config->get('php_dir');
52445$ui->setConfig($config);
52446PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($ui, "displayFatalError"));
52447if (ini_get('safe_mode')) {
52448    $ui->outputData('WARNING: running in safe mode requires that all files created ' .
52449        'be the same uid as the current script.  PHP reports this script is uid: ' .
52450        @getmyuid() . ', and current user is: ' . @get_current_user());
52451}
52452
52453$verbose = $config->get("verbose");
52454$cmdopts = array();
52455
52456if ($raw) {
52457    if (!$config->isDefinedLayer('user') && !$config->isDefinedLayer('system')) {
52458        $found = false;
52459        foreach ($opts as $opt) {
52460            if ($opt[0] == 'd' || $opt[0] == 'D') {
52461                $found = true; // the user knows what they are doing, and are setting config values
52462            }
52463        }
52464        if (!$found) {
52465            // no prior runs, try to install PEAR
52466            if (strpos(dirname(__FILE__), 'scripts')) {
52467                $packagexml = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'package2.xml';
52468                $pearbase = dirname(dirname(__FILE__));
52469            } else {
52470                $packagexml = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'package2.xml';
52471                $pearbase = dirname(__FILE__);
52472            }
52473            if (file_exists($packagexml)) {
52474                $options[1] = array(
52475                    'install',
52476                    $packagexml
52477                );
52478                $config->set('php_dir', $pearbase . DIRECTORY_SEPARATOR . 'php');
52479                $config->set('data_dir', $pearbase . DIRECTORY_SEPARATOR . 'data');
52480                $config->set('doc_dir', $pearbase . DIRECTORY_SEPARATOR . 'docs');
52481                $config->set('test_dir', $pearbase . DIRECTORY_SEPARATOR . 'tests');
52482                $config->set('ext_dir', $pearbase . DIRECTORY_SEPARATOR . 'extensions');
52483                $config->set('bin_dir', $pearbase);
52484                $config->mergeConfigFile($pearbase . 'pear.ini', false);
52485                $config->store();
52486                $config->set('auto_discover', 1);
52487            }
52488        }
52489    }
52490}
52491foreach ($opts as $opt) {
52492    $param = !empty($opt[1]) ? $opt[1] : true;
52493    switch ($opt[0]) {
52494        case 'd':
52495            if ($param === true) {
52496                die('Invalid usage of "-d" option, expected -d config_value=value, ' .
52497                    'received "-d"' . "\n");
52498            }
52499            $possible = explode('=', $param);
52500            if (count($possible) != 2) {
52501                die('Invalid usage of "-d" option, expected -d config_value=value, received "' .
52502                    $param . '"' . "\n");
52503            }
52504            list($key, $value) = explode('=', $param);
52505            $config->set($key, $value, 'user');
52506            break;
52507        case 'D':
52508            if ($param === true) {
52509                die('Invalid usage of "-d" option, expected -d config_value=value, ' .
52510                    'received "-d"' . "\n");
52511            }
52512            $possible = explode('=', $param);
52513            if (count($possible) != 2) {
52514                die('Invalid usage of "-d" option, expected -d config_value=value, received "' .
52515                    $param . '"' . "\n");
52516            }
52517            list($key, $value) = explode('=', $param);
52518            $config->set($key, $value, 'system');
52519            break;
52520        case 's':
52521            $store_user_config = true;
52522            break;
52523        case 'S':
52524            $store_system_config = true;
52525            break;
52526        case 'u':
52527            $config->remove($param, 'user');
52528            break;
52529        case 'v':
52530            $config->set('verbose', $config->get('verbose') + 1);
52531            break;
52532        case 'q':
52533            $config->set('verbose', $config->get('verbose') - 1);
52534            break;
52535        case 'V':
52536            usage(null, 'version');
52537        case 'c':
52538        case 'C':
52539            break;
52540        default:
52541            // all non pear params goes to the command
52542            $cmdopts[$opt[0]] = $param;
52543            break;
52544    }
52545}
52546
52547if ($store_system_config) {
52548    $config->store('system');
52549}
52550
52551if ($store_user_config) {
52552    $config->store('user');
52553}
52554
52555$command = (isset($options[1][0])) ? $options[1][0] : null;
52556if (empty($command) && ($store_user_config || $store_system_config)) {
52557    exit;
52558}
52559
52560if ($fetype == 'Gtk' || $fetype == 'Gtk2') {
52561    if (!$config->validConfiguration()) {
52562        PEAR::raiseError('CRITICAL ERROR: no existing valid configuration files found in files ' .
52563            "'$pear_user_config' or '$pear_system_config', please copy an existing configuration" .
52564            'file to one of these locations, or use the -c and -s options to create one');
52565    }
52566    Gtk::main();
52567} else do {
52568    if ($command == 'help') {
52569        usage(null, @$options[1][1]);
52570    }
52571
52572    if (!$config->validConfiguration()) {
52573        PEAR::raiseError('CRITICAL ERROR: no existing valid configuration files found in files ' .
52574            "'$pear_user_config' or '$pear_system_config', please copy an existing configuration" .
52575            'file to one of these locations, or use the -c and -s options to create one');
52576    }
52577
52578    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
52579    $cmd = PEAR_Command::factory($command, $config);
52580    PEAR::popErrorHandling();
52581    if (PEAR::isError($cmd)) {
52582        usage(null, @$options[1][0]);
52583    }
52584
52585    $short_args = $long_args = null;
52586    PEAR_Command::getGetoptArgs($command, $short_args, $long_args);
52587    array_shift($options[1]);
52588    $tmp = Console_Getopt::getopt2($options[1], $short_args, $long_args);
52589
52590    if (PEAR::isError($tmp)) {
52591        break;
52592    }
52593
52594    list($tmpopt, $params) = $tmp;
52595    $opts = array();
52596    foreach ($tmpopt as $foo => $tmp2) {
52597        list($opt, $value) = $tmp2;
52598        if ($value === null) {
52599            $value = true; // options without args
52600        }
52601
52602        if (strlen($opt) == 1) {
52603            $cmdoptions = $cmd->getOptions($command);
52604            foreach ($cmdoptions as $o => $d) {
52605                if (isset($d['shortopt']) && $d['shortopt'] == $opt) {
52606                    $opts[$o] = $value;
52607                }
52608            }
52609        } else {
52610            if (substr($opt, 0, 2) == '--') {
52611                $opts[substr($opt, 2)] = $value;
52612            }
52613        }
52614    }
52615
52616    $ok = $cmd->run($command, $opts, $params);
52617    if ($ok === false) {
52618        PEAR::raiseError("unknown command `$command'");
52619    }
52620
52621    if (PEAR::isError($ok)) {
52622        PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($ui, "displayFatalError"));
52623        PEAR::raiseError($ok);
52624    }
52625} while (false);
52626
52627// {{{ usage()
52628
52629function usage($error = null, $helpsubject = null)
52630{
52631    global $progname, $all_commands;
52632    $stdout = fopen('php://stdout', 'w');
52633    if (PEAR::isError($error)) {
52634        fputs($stdout, $error->getMessage() . "\n");
52635    } elseif ($error !== null) {
52636        fputs($stdout, "$error\n");
52637    }
52638
52639    if ($helpsubject != null) {
52640        $put = cmdHelp($helpsubject);
52641    } else {
52642        $put = "Commands:\n";
52643        $maxlen = max(array_map("strlen", $all_commands));
52644        $formatstr = "%-{$maxlen}s  %s\n";
52645        ksort($all_commands);
52646        foreach ($all_commands as $cmd => $class) {
52647            $put .= sprintf($formatstr, $cmd, PEAR_Command::getDescription($cmd));
52648        }
52649        $put .=
52650            "Usage: $progname [options] command [command-options] <parameters>\n".
52651            "Type \"$progname help options\" to list all options.\n".
52652            "Type \"$progname help shortcuts\" to list all command shortcuts.\n".
52653            "Type \"$progname help <command>\" to get the help for the specified command.";
52654    }
52655    fputs($stdout, "$put\n");
52656    fclose($stdout);
52657
52658    if ($error === null) {
52659        exit(0);
52660    }
52661    exit(1);
52662}
52663
52664function cmdHelp($command)
52665{
52666    global $progname, $all_commands, $config;
52667    if ($command == "options") {
52668        return
52669        "Options:\n".
52670        "     -v         increase verbosity level (default 1)\n".
52671        "     -q         be quiet, decrease verbosity level\n".
52672        "     -c file    find user configuration in `file'\n".
52673        "     -C file    find system configuration in `file'\n".
52674        "     -d foo=bar set user config variable `foo' to `bar'\n".
52675        "     -D foo=bar set system config variable `foo' to `bar'\n".
52676        "     -G         start in graphical (Gtk) mode\n".
52677        "     -s         store user configuration\n".
52678        "     -S         store system configuration\n".
52679        "     -u foo     unset `foo' in the user configuration\n".
52680        "     -h, -?     display help/usage (this message)\n".
52681        "     -V         version information\n";
52682    } elseif ($command == "shortcuts") {
52683        $sc = PEAR_Command::getShortcuts();
52684        $ret = "Shortcuts:\n";
52685        foreach ($sc as $s => $c) {
52686            $ret .= sprintf("     %-8s %s\n", $s, $c);
52687        }
52688        return $ret;
52689
52690    } elseif ($command == "version") {
52691        return "PEAR Version: ".$GLOBALS['pear_package_version'].
52692               "\nPHP Version: ".phpversion().
52693               "\nZend Engine Version: ".zend_version().
52694               "\nRunning on: ".php_uname();
52695
52696    } elseif ($help = PEAR_Command::getHelp($command)) {
52697        if (is_string($help)) {
52698            return "$progname $command [options] $help\n";
52699        }
52700
52701        if ($help[1] === null) {
52702            return "$progname $command $help[0]";
52703        }
52704
52705        return "$progname $command [options] $help[0]\n$help[1]";
52706    }
52707
52708    return "Command '$command' is not valid, try '$progname help'";
52709}
52710
52711// }}}
52712
52713function error_handler($errno, $errmsg, $file, $line, $vars) {
52714    if ((defined('E_STRICT') && $errno & E_STRICT) || (defined('E_DEPRECATED') &&
52715          $errno & E_DEPRECATED) || !error_reporting()) {
52716        if (defined('E_STRICT') && $errno & E_STRICT) {
52717            return; // E_STRICT
52718        }
52719        if (defined('E_DEPRECATED') && $errno & E_DEPRECATED) {
52720            return; // E_DEPRECATED
52721        }
52722        if ($GLOBALS['config']->get('verbose') < 4) {
52723            return false; // @silenced error, show all if debug is high enough
52724        }
52725    }
52726    $errortype = array (
52727        E_ERROR   =>  "Error",
52728        E_WARNING   =>  "Warning",
52729        E_PARSE   =>  "Parsing Error",
52730        E_NOTICE   =>  "Notice",
52731        E_CORE_ERROR  =>  "Core Error",
52732        E_CORE_WARNING  =>  "Core Warning",
52733        E_COMPILE_ERROR  =>  "Compile Error",
52734        E_COMPILE_WARNING =>  "Compile Warning",
52735        E_USER_ERROR =>  "User Error",
52736        E_USER_WARNING =>  "User Warning",
52737        E_USER_NOTICE =>  "User Notice"
52738    );
52739    $prefix = $errortype[$errno];
52740    global $_PEAR_PHPDIR;
52741    if (stristr($file, $_PEAR_PHPDIR)) {
52742        $file = substr($file, strlen($_PEAR_PHPDIR) + 1);
52743    } else {
52744        $file = basename($file);
52745    }
52746    print "\n$prefix: $errmsg in $file on line $line\n";
52747    return false;
52748}
52749
52750
52751/*
52752 * Local variables:
52753 * tab-width: 4
52754 * c-basic-offset: 4
52755 * indent-tabs-mode: nil
52756 * mode: php
52757 * End:
52758 */
52759// vim600:syn=phpPEAR-1.9.4/scripts/peclcmd.php0000644000076500000240000000163611605156614015051 0ustar  helgistaff<?php
52760/**
52761 * PEAR, the PHP Extension and Application Repository
52762 *
52763 * Command line interface
52764 *
52765 * PHP versions 4 and 5
52766 *
52767 * @category   pear
52768 * @package    PEAR
52769 * @author     Stig Bakken <ssb@php.net>
52770 * @author     Tomas V.V.Cox <cox@idecnet.com>
52771 * @copyright  1997-2009 The Authors
52772 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
52773 * @version    CVS: $Id: peclcmd.php 313023 2011-07-06 19:17:11Z dufuz $
52774 * @link       http://pear.php.net/package/PEAR
52775 */
52776
52777/**
52778 * @nodep Gtk
52779 */
52780if ('@include_path@' != '@'.'include_path'.'@') {
52781    ini_set('include_path', '@include_path@');
52782    $raw = false;
52783} else {
52784    // this is a raw, uninstalled pear, either a cvs checkout, or php distro
52785    $raw = true;
52786}
52787define('PEAR_RUNTYPE', 'pecl');
52788require_once 'pearcmd.php';
52789/*
52790 * Local variables:
52791 * tab-width: 4
52792 * c-basic-offset: 4
52793 * indent-tabs-mode: nil
52794 * mode: php
52795 * End:
52796 */
52797// vim600:syn=php
52798
52799?>
52800PEAR-1.9.4/LICENSE0000644000076500000240000000270511605156614012245 0ustar  helgistaffCopyright (c) 1997-2009,
52801 Stig Bakken <ssb@php.net>,
52802 Gregory Beaver <cellog@php.net>,
52803 Helgi Þormar Þorbjörnsson <helgi@php.net>,
52804 Tomas V.V.Cox <cox@idecnet.com>,
52805 Martin Jansen <mj@php.net>.
52806All rights reserved.
52807
52808Redistribution and use in source and binary forms, with or without
52809modification, are permitted provided that the following conditions are met:
52810
52811    * Redistributions of source code must retain the above copyright notice,
52812      this list of conditions and the following disclaimer.
52813    * Redistributions in binary form must reproduce the above copyright
52814      notice, this list of conditions and the following disclaimer in the
52815      documentation and/or other materials provided with the distribution.
52816
52817THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
52818AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52819IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
52820DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
52821FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52822DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
52823SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
52824CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
52825OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
52826OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52827PEAR-1.9.4/INSTALL0000644000076500000240000000425111605156614012267 0ustar  helgistaffPEAR - The PEAR Installer
52828=========================
52829Installing the PEAR Installer.
52830
52831You should install PEAR on a local development machine first.  Installing
52832PEAR on a remote production machine should only be done after you are
52833familiar with PEAR and have tested code using PEAR on your development
52834machine.
52835
52836There are two methods of installing PEAR
52837 - PEAR bundled in PHP
52838 - go-pear
52839
52840We will first examine how to install PEAR that is bundled with PHP.
52841
52842Microsoft Windows
52843=================
52844If you are running PHP 5.2.0 or newer, simply download and
52845run the windows installer (.msi) and PEAR can be automatically
52846installed.
52847
52848Otherwise, for older PHP versions, download the .zip of windows,
52849there is a script included with your PHP distribution that is called
52850"go-pear".  You must open a command box in order to run it.  Click
52851"start" then click "Run..." and type "cmd.exe" to open a command box.
52852Use "cd" to change directory to the location of PHP where you unzipped it,
52853and run the go-pear command.
52854
52855Unix
52856====
52857When compiling PHP from source, you simply need to include the
52858--with-pear directive on the "./configure" command.  This is "on"
52859by default in most PHP versions, but it doesn't hurt to list it
52860explicitly.  You should also consider enabling the zlib extension via
52861--enable-zlib, so that the PEAR installer will be able to handle gzipped
52862files (i.e. smaller package files for faster downloads).  Later, when you
52863run "make install" to install PHP itself, part of the process will be
52864prompts that ask you where you want PEAR to be installed.
52865
52866go-pear
52867=======
52868For users who cannot perform the above steps, or who wish to obtain the
52869latest PEAR with a slightly higher risk of failure, use go-pear.  go-pear
52870is obtained by downloading http://pear.php.net/go-pear and saving it as go-pear.php.
52871After downloading, simply run "php go-pear.php" or open it in a web browser
52872(windows only) to download and install PEAR.
52873
52874You can always ask general installation questions on pear-general@lists.php.net,
52875a public mailing list devoted to support for PEAR packages and installation-
52876related issues.
52877
52878Happy PHPing, we hope PEAR will be a great tool for your development work!
52879
52880$Id: INSTALL 313023 2011-07-06 19:17:11Z dufuz $PEAR-1.9.4/package.dtd0000644000076500000240000000647711605156614013342 0ustar  helgistaff<!--
52881     $Id: package.dtd,v 1.38 2005-11-12 02:23:07 cellog Exp $
52882
52883     This is the PEAR package description, version 1.0.
52884     It should be used with the informal public identifier:
52885
52886         "-//PHP Group//DTD PEAR Package 1.0//EN//XML"
52887
52888     Copyright (c) 1997-2005 The PHP Group             
52889
52890     This source file is subject to version 3.00 of the PHP license,
52891     that is bundled with this package in the file LICENSE, and is
52892     available at through the world-wide-web at
52893     http://www.php.net/license/3_0.txt. 
52894     If you did not receive a copy of the PHP license and are unable to
52895     obtain it through the world-wide-web, please send a note to
52896     license@php.net so we can mail you a copy immediately.
52897
52898     Authors:
52899         Stig S. Bakken <ssb@fast.no>
52900         Gregory Beaver <cellog@php.net>
52901
52902  -->
52903<!ENTITY % NUMBER "CDATA">
52904<!ELEMENT package (name,summary,description,license?,maintainers,release,changelog?)>
52905<!ATTLIST package type    (source|binary|empty) "empty"
52906                  version CDATA                 #REQUIRED
52907                  packagerversion CDATA         #IMPLIED>
52908
52909<!ELEMENT name (#PCDATA)>
52910
52911<!ELEMENT summary (#PCDATA)>
52912
52913<!ELEMENT license (#PCDATA)>
52914
52915<!ELEMENT description (#PCDATA)>
52916
52917<!ELEMENT maintainers (maintainer)+>
52918
52919<!ELEMENT maintainer (user|role|name|email)+>
52920
52921<!ELEMENT user (#PCDATA)>
52922
52923<!ELEMENT role (#PCDATA)>
52924
52925<!ELEMENT email (#PCDATA)>
52926
52927<!ELEMENT changelog (release)+>
52928
52929<!ELEMENT release (version,date,license,state,notes,warnings?,provides*,deps?,configureoptions?,filelist?)>
52930
52931<!ELEMENT version (#PCDATA)>
52932
52933<!ELEMENT date (#PCDATA)>
52934
52935<!ELEMENT state (#PCDATA)>
52936
52937<!ELEMENT notes (#PCDATA)>
52938
52939<!ELEMENT warnings (#PCDATA)>
52940
52941<!ELEMENT deps (dep*)>
52942
52943<!ELEMENT dep (#PCDATA)>
52944<!ATTLIST dep type    (pkg|ext|php) #REQUIRED
52945	                       rel     (has|eq|lt|le|gt|ge)                          #IMPLIED
52946	                       version CDATA                                     #IMPLIED
52947	                       optional (yes|no)     'no'>
52948
52949<!ELEMENT configureoptions (configureoption)+>
52950
52951<!ELEMENT configureoption EMPTY>
52952<!ATTLIST configureoption name CDATA #REQUIRED
52953                                           default CDATA #IMPLIED
52954                                           prompt CDATA #REQUIRED>
52955
52956<!ELEMENT provides EMPTY>
52957<!ATTLIST provides type (ext|prog|class|function|feature|api) #REQUIRED
52958                                name CDATA #REQUIRED
52959                                extends CDATA #IMPLIED>
52960<!ELEMENT filelist (dir|file)+>
52961
52962<!ELEMENT dir (dir|file)+>
52963<!ATTLIST dir name           CDATA #REQUIRED
52964              role           (php|ext|src|test|doc|data|script) 'php'
52965              baseinstalldir CDATA #IMPLIED>
52966
52967<!ELEMENT file (replace*)>
52968<!ATTLIST file role           (php|ext|src|test|doc|data|script) 'php'
52969               debug          (na|on|off)        'na'
52970               format         CDATA              #IMPLIED
52971               baseinstalldir CDATA              #IMPLIED
52972               platform       CDATA              #IMPLIED
52973               md5sum         CDATA              #IMPLIED
52974               name           CDATA              #REQUIRED
52975               install-as     CDATA              #IMPLIED>
52976
52977<!ELEMENT replace EMPTY>
52978<!ATTLIST replace type (php-const|pear-config|package-info) #REQUIRED
52979                              from CDATA #REQUIRED
52980                              to CDATA #REQUIRED>
52981
52982
52983PEAR-1.9.4/PEAR5.php0000644000076500000240000000207711605156614012567 0ustar  helgistaff<?php
52984/**
52985 * This is only meant for PHP 5 to get rid of certain strict warning
52986 * that doesn't get hidden since it's in the shutdown function
52987 */
52988class PEAR5
52989{
52990    /**
52991    * If you have a class that's mostly/entirely static, and you need static
52992    * properties, you can use this method to simulate them. Eg. in your method(s)
52993    * do this: $myVar = &PEAR5::getStaticProperty('myclass', 'myVar');
52994    * You MUST use a reference, or they will not persist!
52995    *
52996    * @access public
52997    * @param  string $class  The calling classname, to prevent clashes
52998    * @param  string $var    The variable to retrieve.
52999    * @return mixed   A reference to the variable. If not set it will be
53000    *                 auto initialised to NULL.
53001    */
53002    static function &getStaticProperty($class, $var)
53003    {
53004        static $properties;
53005        if (!isset($properties[$class])) {
53006            $properties[$class] = array();
53007        }
53008
53009        if (!array_key_exists($var, $properties[$class])) {
53010            $properties[$class][$var] = null;
53011        }
53012
53013        return $properties[$class][$var];
53014    }
53015}PEAR-1.9.4/PEAR.php0000644000076500000240000010215111605156614012474 0ustar  helgistaff<?php
53016/**
53017 * PEAR, the PHP Extension and Application Repository
53018 *
53019 * PEAR class and PEAR_Error class
53020 *
53021 * PHP versions 4 and 5
53022 *
53023 * @category   pear
53024 * @package    PEAR
53025 * @author     Sterling Hughes <sterling@php.net>
53026 * @author     Stig Bakken <ssb@php.net>
53027 * @author     Tomas V.V.Cox <cox@idecnet.com>
53028 * @author     Greg Beaver <cellog@php.net>
53029 * @copyright  1997-2010 The Authors
53030 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
53031 * @version    CVS: $Id: PEAR.php 313023 2011-07-06 19:17:11Z dufuz $
53032 * @link       http://pear.php.net/package/PEAR
53033 * @since      File available since Release 0.1
53034 */
53035
53036/**#@+
53037 * ERROR constants
53038 */
53039define('PEAR_ERROR_RETURN',     1);
53040define('PEAR_ERROR_PRINT',      2);
53041define('PEAR_ERROR_TRIGGER',    4);
53042define('PEAR_ERROR_DIE',        8);
53043define('PEAR_ERROR_CALLBACK',  16);
53044/**
53045 * WARNING: obsolete
53046 * @deprecated
53047 */
53048define('PEAR_ERROR_EXCEPTION', 32);
53049/**#@-*/
53050define('PEAR_ZE2', (function_exists('version_compare') &&
53051                    version_compare(zend_version(), "2-dev", "ge")));
53052
53053if (substr(PHP_OS, 0, 3) == 'WIN') {
53054    define('OS_WINDOWS', true);
53055    define('OS_UNIX',    false);
53056    define('PEAR_OS',    'Windows');
53057} else {
53058    define('OS_WINDOWS', false);
53059    define('OS_UNIX',    true);
53060    define('PEAR_OS',    'Unix'); // blatant assumption
53061}
53062
53063$GLOBALS['_PEAR_default_error_mode']     = PEAR_ERROR_RETURN;
53064$GLOBALS['_PEAR_default_error_options']  = E_USER_NOTICE;
53065$GLOBALS['_PEAR_destructor_object_list'] = array();
53066$GLOBALS['_PEAR_shutdown_funcs']         = array();
53067$GLOBALS['_PEAR_error_handler_stack']    = array();
53068
53069@ini_set('track_errors', true);
53070
53071/**
53072 * Base class for other PEAR classes.  Provides rudimentary
53073 * emulation of destructors.
53074 *
53075 * If you want a destructor in your class, inherit PEAR and make a
53076 * destructor method called _yourclassname (same name as the
53077 * constructor, but with a "_" prefix).  Also, in your constructor you
53078 * have to call the PEAR constructor: $this->PEAR();.
53079 * The destructor method will be called without parameters.  Note that
53080 * at in some SAPI implementations (such as Apache), any output during
53081 * the request shutdown (in which destructors are called) seems to be
53082 * discarded.  If you need to get any debug information from your
53083 * destructor, use error_log(), syslog() or something similar.
53084 *
53085 * IMPORTANT! To use the emulated destructors you need to create the
53086 * objects by reference: $obj =& new PEAR_child;
53087 *
53088 * @category   pear
53089 * @package    PEAR
53090 * @author     Stig Bakken <ssb@php.net>
53091 * @author     Tomas V.V. Cox <cox@idecnet.com>
53092 * @author     Greg Beaver <cellog@php.net>
53093 * @copyright  1997-2006 The PHP Group
53094 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
53095 * @version    Release: 1.9.4
53096 * @link       http://pear.php.net/package/PEAR
53097 * @see        PEAR_Error
53098 * @since      Class available since PHP 4.0.2
53099 * @link        http://pear.php.net/manual/en/core.pear.php#core.pear.pear
53100 */
53101class PEAR
53102{
53103    /**
53104     * Whether to enable internal debug messages.
53105     *
53106     * @var     bool
53107     * @access  private
53108     */
53109    var $_debug = false;
53110
53111    /**
53112     * Default error mode for this object.
53113     *
53114     * @var     int
53115     * @access  private
53116     */
53117    var $_default_error_mode = null;
53118
53119    /**
53120     * Default error options used for this object when error mode
53121     * is PEAR_ERROR_TRIGGER.
53122     *
53123     * @var     int
53124     * @access  private
53125     */
53126    var $_default_error_options = null;
53127
53128    /**
53129     * Default error handler (callback) for this object, if error mode is
53130     * PEAR_ERROR_CALLBACK.
53131     *
53132     * @var     string
53133     * @access  private
53134     */
53135    var $_default_error_handler = '';
53136
53137    /**
53138     * Which class to use for error objects.
53139     *
53140     * @var     string
53141     * @access  private
53142     */
53143    var $_error_class = 'PEAR_Error';
53144
53145    /**
53146     * An array of expected errors.
53147     *
53148     * @var     array
53149     * @access  private
53150     */
53151    var $_expected_errors = array();
53152
53153    /**
53154     * Constructor.  Registers this object in
53155     * $_PEAR_destructor_object_list for destructor emulation if a
53156     * destructor object exists.
53157     *
53158     * @param string $error_class  (optional) which class to use for
53159     *        error objects, defaults to PEAR_Error.
53160     * @access public
53161     * @return void
53162     */
53163    function PEAR($error_class = null)
53164    {
53165        $classname = strtolower(get_class($this));
53166        if ($this->_debug) {
53167            print "PEAR constructor called, class=$classname\n";
53168        }
53169
53170        if ($error_class !== null) {
53171            $this->_error_class = $error_class;
53172        }
53173
53174        while ($classname && strcasecmp($classname, "pear")) {
53175            $destructor = "_$classname";
53176            if (method_exists($this, $destructor)) {
53177                global $_PEAR_destructor_object_list;
53178                $_PEAR_destructor_object_list[] = &$this;
53179                if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
53180                    register_shutdown_function("_PEAR_call_destructors");
53181                    $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
53182                }
53183                break;
53184            } else {
53185                $classname = get_parent_class($classname);
53186            }
53187        }
53188    }
53189
53190    /**
53191     * Destructor (the emulated type of...).  Does nothing right now,
53192     * but is included for forward compatibility, so subclass
53193     * destructors should always call it.
53194     *
53195     * See the note in the class desciption about output from
53196     * destructors.
53197     *
53198     * @access public
53199     * @return void
53200     */
53201    function _PEAR() {
53202        if ($this->_debug) {
53203            printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
53204        }
53205    }
53206
53207    /**
53208    * If you have a class that's mostly/entirely static, and you need static
53209    * properties, you can use this method to simulate them. Eg. in your method(s)
53210    * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
53211    * You MUST use a reference, or they will not persist!
53212    *
53213    * @access public
53214    * @param  string $class  The calling classname, to prevent clashes
53215    * @param  string $var    The variable to retrieve.
53216    * @return mixed   A reference to the variable. If not set it will be
53217    *                 auto initialised to NULL.
53218    */
53219    function &getStaticProperty($class, $var)
53220    {
53221        static $properties;
53222        if (!isset($properties[$class])) {
53223            $properties[$class] = array();
53224        }
53225
53226        if (!array_key_exists($var, $properties[$class])) {
53227            $properties[$class][$var] = null;
53228        }
53229
53230        return $properties[$class][$var];
53231    }
53232
53233    /**
53234    * Use this function to register a shutdown method for static
53235    * classes.
53236    *
53237    * @access public
53238    * @param  mixed $func  The function name (or array of class/method) to call
53239    * @param  mixed $args  The arguments to pass to the function
53240    * @return void
53241    */
53242    function registerShutdownFunc($func, $args = array())
53243    {
53244        // if we are called statically, there is a potential
53245        // that no shutdown func is registered.  Bug #6445
53246        if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
53247            register_shutdown_function("_PEAR_call_destructors");
53248            $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
53249        }
53250        $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
53251    }
53252
53253    /**
53254     * Tell whether a value is a PEAR error.
53255     *
53256     * @param   mixed $data   the value to test
53257     * @param   int   $code   if $data is an error object, return true
53258     *                        only if $code is a string and
53259     *                        $obj->getMessage() == $code or
53260     *                        $code is an integer and $obj->getCode() == $code
53261     * @access  public
53262     * @return  bool    true if parameter is an error
53263     */
53264    function isError($data, $code = null)
53265    {
53266        if (!is_a($data, 'PEAR_Error')) {
53267            return false;
53268        }
53269
53270        if (is_null($code)) {
53271            return true;
53272        } elseif (is_string($code)) {
53273            return $data->getMessage() == $code;
53274        }
53275
53276        return $data->getCode() == $code;
53277    }
53278
53279    /**
53280     * Sets how errors generated by this object should be handled.
53281     * Can be invoked both in objects and statically.  If called
53282     * statically, setErrorHandling sets the default behaviour for all
53283     * PEAR objects.  If called in an object, setErrorHandling sets
53284     * the default behaviour for that object.
53285     *
53286     * @param int $mode
53287     *        One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
53288     *        PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
53289     *        PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
53290     *
53291     * @param mixed $options
53292     *        When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
53293     *        of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
53294     *
53295     *        When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
53296     *        to be the callback function or method.  A callback
53297     *        function is a string with the name of the function, a
53298     *        callback method is an array of two elements: the element
53299     *        at index 0 is the object, and the element at index 1 is
53300     *        the name of the method to call in the object.
53301     *
53302     *        When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
53303     *        a printf format string used when printing the error
53304     *        message.
53305     *
53306     * @access public
53307     * @return void
53308     * @see PEAR_ERROR_RETURN
53309     * @see PEAR_ERROR_PRINT
53310     * @see PEAR_ERROR_TRIGGER
53311     * @see PEAR_ERROR_DIE
53312     * @see PEAR_ERROR_CALLBACK
53313     * @see PEAR_ERROR_EXCEPTION
53314     *
53315     * @since PHP 4.0.5
53316     */
53317    function setErrorHandling($mode = null, $options = null)
53318    {
53319        if (isset($this) && is_a($this, 'PEAR')) {
53320            $setmode     = &$this->_default_error_mode;
53321            $setoptions  = &$this->_default_error_options;
53322        } else {
53323            $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
53324            $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
53325        }
53326
53327        switch ($mode) {
53328            case PEAR_ERROR_EXCEPTION:
53329            case PEAR_ERROR_RETURN:
53330            case PEAR_ERROR_PRINT:
53331            case PEAR_ERROR_TRIGGER:
53332            case PEAR_ERROR_DIE:
53333            case null:
53334                $setmode = $mode;
53335                $setoptions = $options;
53336                break;
53337
53338            case PEAR_ERROR_CALLBACK:
53339                $setmode = $mode;
53340                // class/object method callback
53341                if (is_callable($options)) {
53342                    $setoptions = $options;
53343                } else {
53344                    trigger_error("invalid error callback", E_USER_WARNING);
53345                }
53346                break;
53347
53348            default:
53349                trigger_error("invalid error mode", E_USER_WARNING);
53350                break;
53351        }
53352    }
53353
53354    /**
53355     * This method is used to tell which errors you expect to get.
53356     * Expected errors are always returned with error mode
53357     * PEAR_ERROR_RETURN.  Expected error codes are stored in a stack,
53358     * and this method pushes a new element onto it.  The list of
53359     * expected errors are in effect until they are popped off the
53360     * stack with the popExpect() method.
53361     *
53362     * Note that this method can not be called statically
53363     *
53364     * @param mixed $code a single error code or an array of error codes to expect
53365     *
53366     * @return int     the new depth of the "expected errors" stack
53367     * @access public
53368     */
53369    function expectError($code = '*')
53370    {
53371        if (is_array($code)) {
53372            array_push($this->_expected_errors, $code);
53373        } else {
53374            array_push($this->_expected_errors, array($code));
53375        }
53376        return count($this->_expected_errors);
53377    }
53378
53379    /**
53380     * This method pops one element off the expected error codes
53381     * stack.
53382     *
53383     * @return array   the list of error codes that were popped
53384     */
53385    function popExpect()
53386    {
53387        return array_pop($this->_expected_errors);
53388    }
53389
53390    /**
53391     * This method checks unsets an error code if available
53392     *
53393     * @param mixed error code
53394     * @return bool true if the error code was unset, false otherwise
53395     * @access private
53396     * @since PHP 4.3.0
53397     */
53398    function _checkDelExpect($error_code)
53399    {
53400        $deleted = false;
53401        foreach ($this->_expected_errors as $key => $error_array) {
53402            if (in_array($error_code, $error_array)) {
53403                unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
53404                $deleted = true;
53405            }
53406
53407            // clean up empty arrays
53408            if (0 == count($this->_expected_errors[$key])) {
53409                unset($this->_expected_errors[$key]);
53410            }
53411        }
53412
53413        return $deleted;
53414    }
53415
53416    /**
53417     * This method deletes all occurences of the specified element from
53418     * the expected error codes stack.
53419     *
53420     * @param  mixed $error_code error code that should be deleted
53421     * @return mixed list of error codes that were deleted or error
53422     * @access public
53423     * @since PHP 4.3.0
53424     */
53425    function delExpect($error_code)
53426    {
53427        $deleted = false;
53428        if ((is_array($error_code) && (0 != count($error_code)))) {
53429            // $error_code is a non-empty array here; we walk through it trying
53430            // to unset all values
53431            foreach ($error_code as $key => $error) {
53432                $deleted =  $this->_checkDelExpect($error) ? true : false;
53433            }
53434
53435            return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
53436        } elseif (!empty($error_code)) {
53437            // $error_code comes alone, trying to unset it
53438            if ($this->_checkDelExpect($error_code)) {
53439                return true;
53440            }
53441
53442            return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
53443        }
53444
53445        // $error_code is empty
53446        return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
53447    }
53448
53449    /**
53450     * This method is a wrapper that returns an instance of the
53451     * configured error class with this object's default error
53452     * handling applied.  If the $mode and $options parameters are not
53453     * specified, the object's defaults are used.
53454     *
53455     * @param mixed $message a text error message or a PEAR error object
53456     *
53457     * @param int $code      a numeric error code (it is up to your class
53458     *                  to define these if you want to use codes)
53459     *
53460     * @param int $mode      One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
53461     *                  PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
53462     *                  PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
53463     *
53464     * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
53465     *                  specifies the PHP-internal error level (one of
53466     *                  E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
53467     *                  If $mode is PEAR_ERROR_CALLBACK, this
53468     *                  parameter specifies the callback function or
53469     *                  method.  In other error modes this parameter
53470     *                  is ignored.
53471     *
53472     * @param string $userinfo If you need to pass along for example debug
53473     *                  information, this parameter is meant for that.
53474     *
53475     * @param string $error_class The returned error object will be
53476     *                  instantiated from this class, if specified.
53477     *
53478     * @param bool $skipmsg If true, raiseError will only pass error codes,
53479     *                  the error message parameter will be dropped.
53480     *
53481     * @access public
53482     * @return object   a PEAR error object
53483     * @see PEAR::setErrorHandling
53484     * @since PHP 4.0.5
53485     */
53486    function &raiseError($message = null,
53487                         $code = null,
53488                         $mode = null,
53489                         $options = null,
53490                         $userinfo = null,
53491                         $error_class = null,
53492                         $skipmsg = false)
53493    {
53494        // The error is yet a PEAR error object
53495        if (is_object($message)) {
53496            $code        = $message->getCode();
53497            $userinfo    = $message->getUserInfo();
53498            $error_class = $message->getType();
53499            $message->error_message_prefix = '';
53500            $message     = $message->getMessage();
53501        }
53502
53503        if (
53504            isset($this) &&
53505            isset($this->_expected_errors) &&
53506            count($this->_expected_errors) > 0 &&
53507            count($exp = end($this->_expected_errors))
53508        ) {
53509            if ($exp[0] == "*" ||
53510                (is_int(reset($exp)) && in_array($code, $exp)) ||
53511                (is_string(reset($exp)) && in_array($message, $exp))
53512            ) {
53513                $mode = PEAR_ERROR_RETURN;
53514            }
53515        }
53516
53517        // No mode given, try global ones
53518        if ($mode === null) {
53519            // Class error handler
53520            if (isset($this) && isset($this->_default_error_mode)) {
53521                $mode    = $this->_default_error_mode;
53522                $options = $this->_default_error_options;
53523            // Global error handler
53524            } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
53525                $mode    = $GLOBALS['_PEAR_default_error_mode'];
53526                $options = $GLOBALS['_PEAR_default_error_options'];
53527            }
53528        }
53529
53530        if ($error_class !== null) {
53531            $ec = $error_class;
53532        } elseif (isset($this) && isset($this->_error_class)) {
53533            $ec = $this->_error_class;
53534        } else {
53535            $ec = 'PEAR_Error';
53536        }
53537
53538        if (intval(PHP_VERSION) < 5) {
53539            // little non-eval hack to fix bug #12147
53540            include 'PEAR/FixPHP5PEARWarnings.php';
53541            return $a;
53542        }
53543
53544        if ($skipmsg) {
53545            $a = new $ec($code, $mode, $options, $userinfo);
53546        } else {
53547            $a = new $ec($message, $code, $mode, $options, $userinfo);
53548        }
53549
53550        return $a;
53551    }
53552
53553    /**
53554     * Simpler form of raiseError with fewer options.  In most cases
53555     * message, code and userinfo are enough.
53556     *
53557     * @param mixed $message a text error message or a PEAR error object
53558     *
53559     * @param int $code      a numeric error code (it is up to your class
53560     *                  to define these if you want to use codes)
53561     *
53562     * @param string $userinfo If you need to pass along for example debug
53563     *                  information, this parameter is meant for that.
53564     *
53565     * @access public
53566     * @return object   a PEAR error object
53567     * @see PEAR::raiseError
53568     */
53569    function &throwError($message = null, $code = null, $userinfo = null)
53570    {
53571        if (isset($this) && is_a($this, 'PEAR')) {
53572            $a = &$this->raiseError($message, $code, null, null, $userinfo);
53573            return $a;
53574        }
53575
53576        $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
53577        return $a;
53578    }
53579
53580    function staticPushErrorHandling($mode, $options = null)
53581    {
53582        $stack       = &$GLOBALS['_PEAR_error_handler_stack'];
53583        $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
53584        $def_options = &$GLOBALS['_PEAR_default_error_options'];
53585        $stack[] = array($def_mode, $def_options);
53586        switch ($mode) {
53587            case PEAR_ERROR_EXCEPTION:
53588            case PEAR_ERROR_RETURN:
53589            case PEAR_ERROR_PRINT:
53590            case PEAR_ERROR_TRIGGER:
53591            case PEAR_ERROR_DIE:
53592            case null:
53593                $def_mode = $mode;
53594                $def_options = $options;
53595                break;
53596
53597            case PEAR_ERROR_CALLBACK:
53598                $def_mode = $mode;
53599                // class/object method callback
53600                if (is_callable($options)) {
53601                    $def_options = $options;
53602                } else {
53603                    trigger_error("invalid error callback", E_USER_WARNING);
53604                }
53605                break;
53606
53607            default:
53608                trigger_error("invalid error mode", E_USER_WARNING);
53609                break;
53610        }
53611        $stack[] = array($mode, $options);
53612        return true;
53613    }
53614
53615    function staticPopErrorHandling()
53616    {
53617        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
53618        $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
53619        $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
53620        array_pop($stack);
53621        list($mode, $options) = $stack[sizeof($stack) - 1];
53622        array_pop($stack);
53623        switch ($mode) {
53624            case PEAR_ERROR_EXCEPTION:
53625            case PEAR_ERROR_RETURN:
53626            case PEAR_ERROR_PRINT:
53627            case PEAR_ERROR_TRIGGER:
53628            case PEAR_ERROR_DIE:
53629            case null:
53630                $setmode = $mode;
53631                $setoptions = $options;
53632                break;
53633
53634            case PEAR_ERROR_CALLBACK:
53635                $setmode = $mode;
53636                // class/object method callback
53637                if (is_callable($options)) {
53638                    $setoptions = $options;
53639                } else {
53640                    trigger_error("invalid error callback", E_USER_WARNING);
53641                }
53642                break;
53643
53644            default:
53645                trigger_error("invalid error mode", E_USER_WARNING);
53646                break;
53647        }
53648        return true;
53649    }
53650
53651    /**
53652     * Push a new error handler on top of the error handler options stack. With this
53653     * you can easily override the actual error handler for some code and restore
53654     * it later with popErrorHandling.
53655     *
53656     * @param mixed $mode (same as setErrorHandling)
53657     * @param mixed $options (same as setErrorHandling)
53658     *
53659     * @return bool Always true
53660     *
53661     * @see PEAR::setErrorHandling
53662     */
53663    function pushErrorHandling($mode, $options = null)
53664    {
53665        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
53666        if (isset($this) && is_a($this, 'PEAR')) {
53667            $def_mode    = &$this->_default_error_mode;
53668            $def_options = &$this->_default_error_options;
53669        } else {
53670            $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
53671            $def_options = &$GLOBALS['_PEAR_default_error_options'];
53672        }
53673        $stack[] = array($def_mode, $def_options);
53674
53675        if (isset($this) && is_a($this, 'PEAR')) {
53676            $this->setErrorHandling($mode, $options);
53677        } else {
53678            PEAR::setErrorHandling($mode, $options);
53679        }
53680        $stack[] = array($mode, $options);
53681        return true;
53682    }
53683
53684    /**
53685    * Pop the last error handler used
53686    *
53687    * @return bool Always true
53688    *
53689    * @see PEAR::pushErrorHandling
53690    */
53691    function popErrorHandling()
53692    {
53693        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
53694        array_pop($stack);
53695        list($mode, $options) = $stack[sizeof($stack) - 1];
53696        array_pop($stack);
53697        if (isset($this) && is_a($this, 'PEAR')) {
53698            $this->setErrorHandling($mode, $options);
53699        } else {
53700            PEAR::setErrorHandling($mode, $options);
53701        }
53702        return true;
53703    }
53704
53705    /**
53706    * OS independant PHP extension load. Remember to take care
53707    * on the correct extension name for case sensitive OSes.
53708    *
53709    * @param string $ext The extension name
53710    * @return bool Success or not on the dl() call
53711    */
53712    function loadExtension($ext)
53713    {
53714        if (extension_loaded($ext)) {
53715            return true;
53716        }
53717
53718        // if either returns true dl() will produce a FATAL error, stop that
53719        if (
53720            function_exists('dl') === false ||
53721            ini_get('enable_dl') != 1 ||
53722            ini_get('safe_mode') == 1
53723        ) {
53724            return false;
53725        }
53726
53727        if (OS_WINDOWS) {
53728            $suffix = '.dll';
53729        } elseif (PHP_OS == 'HP-UX') {
53730            $suffix = '.sl';
53731        } elseif (PHP_OS == 'AIX') {
53732            $suffix = '.a';
53733        } elseif (PHP_OS == 'OSX') {
53734            $suffix = '.bundle';
53735        } else {
53736            $suffix = '.so';
53737        }
53738
53739        return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
53740    }
53741}
53742
53743if (PEAR_ZE2) {
53744    include_once 'PEAR5.php';
53745}
53746
53747function _PEAR_call_destructors()
53748{
53749    global $_PEAR_destructor_object_list;
53750    if (is_array($_PEAR_destructor_object_list) &&
53751        sizeof($_PEAR_destructor_object_list))
53752    {
53753        reset($_PEAR_destructor_object_list);
53754        if (PEAR_ZE2) {
53755            $destructLifoExists = PEAR5::getStaticProperty('PEAR', 'destructlifo');
53756        } else {
53757            $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo');
53758        }
53759
53760        if ($destructLifoExists) {
53761            $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
53762        }
53763
53764        while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
53765            $classname = get_class($objref);
53766            while ($classname) {
53767                $destructor = "_$classname";
53768                if (method_exists($objref, $destructor)) {
53769                    $objref->$destructor();
53770                    break;
53771                } else {
53772                    $classname = get_parent_class($classname);
53773                }
53774            }
53775        }
53776        // Empty the object list to ensure that destructors are
53777        // not called more than once.
53778        $_PEAR_destructor_object_list = array();
53779    }
53780
53781    // Now call the shutdown functions
53782    if (
53783        isset($GLOBALS['_PEAR_shutdown_funcs']) &&
53784        is_array($GLOBALS['_PEAR_shutdown_funcs']) &&
53785        !empty($GLOBALS['_PEAR_shutdown_funcs'])
53786    ) {
53787        foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
53788            call_user_func_array($value[0], $value[1]);
53789        }
53790    }
53791}
53792
53793/**
53794 * Standard PEAR error class for PHP 4
53795 *
53796 * This class is supserseded by {@link PEAR_Exception} in PHP 5
53797 *
53798 * @category   pear
53799 * @package    PEAR
53800 * @author     Stig Bakken <ssb@php.net>
53801 * @author     Tomas V.V. Cox <cox@idecnet.com>
53802 * @author     Gregory Beaver <cellog@php.net>
53803 * @copyright  1997-2006 The PHP Group
53804 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
53805 * @version    Release: 1.9.4
53806 * @link       http://pear.php.net/manual/en/core.pear.pear-error.php
53807 * @see        PEAR::raiseError(), PEAR::throwError()
53808 * @since      Class available since PHP 4.0.2
53809 */
53810class PEAR_Error
53811{
53812    var $error_message_prefix = '';
53813    var $mode                 = PEAR_ERROR_RETURN;
53814    var $level                = E_USER_NOTICE;
53815    var $code                 = -1;
53816    var $message              = '';
53817    var $userinfo             = '';
53818    var $backtrace            = null;
53819
53820    /**
53821     * PEAR_Error constructor
53822     *
53823     * @param string $message  message
53824     *
53825     * @param int $code     (optional) error code
53826     *
53827     * @param int $mode     (optional) error mode, one of: PEAR_ERROR_RETURN,
53828     * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
53829     * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
53830     *
53831     * @param mixed $options   (optional) error level, _OR_ in the case of
53832     * PEAR_ERROR_CALLBACK, the callback function or object/method
53833     * tuple.
53834     *
53835     * @param string $userinfo (optional) additional user/debug info
53836     *
53837     * @access public
53838     *
53839     */
53840    function PEAR_Error($message = 'unknown error', $code = null,
53841                        $mode = null, $options = null, $userinfo = null)
53842    {
53843        if ($mode === null) {
53844            $mode = PEAR_ERROR_RETURN;
53845        }
53846        $this->message   = $message;
53847        $this->code      = $code;
53848        $this->mode      = $mode;
53849        $this->userinfo  = $userinfo;
53850
53851        if (PEAR_ZE2) {
53852            $skiptrace = PEAR5::getStaticProperty('PEAR_Error', 'skiptrace');
53853        } else {
53854            $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace');
53855        }
53856
53857        if (!$skiptrace) {
53858            $this->backtrace = debug_backtrace();
53859            if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
53860                unset($this->backtrace[0]['object']);
53861            }
53862        }
53863
53864        if ($mode & PEAR_ERROR_CALLBACK) {
53865            $this->level = E_USER_NOTICE;
53866            $this->callback = $options;
53867        } else {
53868            if ($options === null) {
53869                $options = E_USER_NOTICE;
53870            }
53871
53872            $this->level = $options;
53873            $this->callback = null;
53874        }
53875
53876        if ($this->mode & PEAR_ERROR_PRINT) {
53877            if (is_null($options) || is_int($options)) {
53878                $format = "%s";
53879            } else {
53880                $format = $options;
53881            }
53882
53883            printf($format, $this->getMessage());
53884        }
53885
53886        if ($this->mode & PEAR_ERROR_TRIGGER) {
53887            trigger_error($this->getMessage(), $this->level);
53888        }
53889
53890        if ($this->mode & PEAR_ERROR_DIE) {
53891            $msg = $this->getMessage();
53892            if (is_null($options) || is_int($options)) {
53893                $format = "%s";
53894                if (substr($msg, -1) != "\n") {
53895                    $msg .= "\n";
53896                }
53897            } else {
53898                $format = $options;
53899            }
53900            die(sprintf($format, $msg));
53901        }
53902
53903        if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) {
53904            call_user_func($this->callback, $this);
53905        }
53906
53907        if ($this->mode & PEAR_ERROR_EXCEPTION) {
53908            trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
53909            eval('$e = new Exception($this->message, $this->code);throw($e);');
53910        }
53911    }
53912
53913    /**
53914     * Get the error mode from an error object.
53915     *
53916     * @return int error mode
53917     * @access public
53918     */
53919    function getMode()
53920    {
53921        return $this->mode;
53922    }
53923
53924    /**
53925     * Get the callback function/method from an error object.
53926     *
53927     * @return mixed callback function or object/method array
53928     * @access public
53929     */
53930    function getCallback()
53931    {
53932        return $this->callback;
53933    }
53934
53935    /**
53936     * Get the error message from an error object.
53937     *
53938     * @return  string  full error message
53939     * @access public
53940     */
53941    function getMessage()
53942    {
53943        return ($this->error_message_prefix . $this->message);
53944    }
53945
53946    /**
53947     * Get error code from an error object
53948     *
53949     * @return int error code
53950     * @access public
53951     */
53952     function getCode()
53953     {
53954        return $this->code;
53955     }
53956
53957    /**
53958     * Get the name of this error/exception.
53959     *
53960     * @return string error/exception name (type)
53961     * @access public
53962     */
53963    function getType()
53964    {
53965        return get_class($this);
53966    }
53967
53968    /**
53969     * Get additional user-supplied information.
53970     *
53971     * @return string user-supplied information
53972     * @access public
53973     */
53974    function getUserInfo()
53975    {
53976        return $this->userinfo;
53977    }
53978
53979    /**
53980     * Get additional debug information supplied by the application.
53981     *
53982     * @return string debug information
53983     * @access public
53984     */
53985    function getDebugInfo()
53986    {
53987        return $this->getUserInfo();
53988    }
53989
53990    /**
53991     * Get the call backtrace from where the error was generated.
53992     * Supported with PHP 4.3.0 or newer.
53993     *
53994     * @param int $frame (optional) what frame to fetch
53995     * @return array Backtrace, or NULL if not available.
53996     * @access public
53997     */
53998    function getBacktrace($frame = null)
53999    {
54000        if (defined('PEAR_IGNORE_BACKTRACE')) {
54001            return null;
54002        }
54003        if ($frame === null) {
54004            return $this->backtrace;
54005        }
54006        return $this->backtrace[$frame];
54007    }
54008
54009    function addUserInfo($info)
54010    {
54011        if (empty($this->userinfo)) {
54012            $this->userinfo = $info;
54013        } else {
54014            $this->userinfo .= " ** $info";
54015        }
54016    }
54017
54018    function __toString()
54019    {
54020        return $this->getMessage();
54021    }
54022
54023    /**
54024     * Make a string representation of this object.
54025     *
54026     * @return string a string with an object summary
54027     * @access public
54028     */
54029    function toString()
54030    {
54031        $modes = array();
54032        $levels = array(E_USER_NOTICE  => 'notice',
54033                        E_USER_WARNING => 'warning',
54034                        E_USER_ERROR   => 'error');
54035        if ($this->mode & PEAR_ERROR_CALLBACK) {
54036            if (is_array($this->callback)) {
54037                $callback = (is_object($this->callback[0]) ?
54038                    strtolower(get_class($this->callback[0])) :
54039                    $this->callback[0]) . '::' .
54040                    $this->callback[1];
54041            } else {
54042                $callback = $this->callback;
54043            }
54044            return sprintf('[%s: message="%s" code=%d mode=callback '.
54045                           'callback=%s prefix="%s" info="%s"]',
54046                           strtolower(get_class($this)), $this->message, $this->code,
54047                           $callback, $this->error_message_prefix,
54048                           $this->userinfo);
54049        }
54050        if ($this->mode & PEAR_ERROR_PRINT) {
54051            $modes[] = 'print';
54052        }
54053        if ($this->mode & PEAR_ERROR_TRIGGER) {
54054            $modes[] = 'trigger';
54055        }
54056        if ($this->mode & PEAR_ERROR_DIE) {
54057            $modes[] = 'die';
54058        }
54059        if ($this->mode & PEAR_ERROR_RETURN) {
54060            $modes[] = 'return';
54061        }
54062        return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
54063                       'prefix="%s" info="%s"]',
54064                       strtolower(get_class($this)), $this->message, $this->code,
54065                       implode("|", $modes), $levels[$this->level],
54066                       $this->error_message_prefix,
54067                       $this->userinfo);
54068    }
54069}
54070
54071/*
54072 * Local Variables:
54073 * mode: php
54074 * tab-width: 4
54075 * c-basic-offset: 4
54076 * End:
54077 */
54078PEAR-1.9.4/README0000644000076500000240000000224511605156614012117 0ustar  helgistaffPEAR - The PEAR Installer
54079=========================
54080
54081What is the PEAR Installer?  What is PEAR?
54082
54083PEAR is the PHP Extension and Application Repository, found at
54084http://pear.php.net.  The PEAR Installer is this software, which
54085contains executable files and PHP code that is used to download
54086and install PEAR code from pear.php.net.
54087
54088PEAR contains useful software libraries and applications such as
54089MDB2 (database abstraction), HTML_QuickForm (HTML forms management),
54090PhpDocumentor (auto-documentation generator), DB_DataObject
54091(Data Access Abstraction), and many hundreds more.  Browse all
54092available packages at http://pear.php.net, the list is constantly
54093growing and updating to reflect improvements in the PHP language.
54094
54095DOCUMENTATION
54096=============
54097
54098Documentation for PEAR can be found at http://pear.php.net/manual/.
54099Installation documentation can be found in the INSTALL file included
54100in this tarball.
54101
54102WARNING: DO NOT RUN PEAR WITHOUT INSTALLING IT - if you downloaded this
54103tarball manually, you MUST install it.  Read the instructions in INSTALL
54104prior to use.
54105
54106
54107Happy PHPing, we hope PEAR will be a great tool for your development work!
54108
54109$Id: README 313023 2011-07-06 19:17:11Z dufuz $PEAR-1.9.4/System.php0000644000076500000240000004742311605156614013243 0ustar  helgistaff<?php
54110/**
54111 * File/Directory manipulation
54112 *
54113 * PHP versions 4 and 5
54114 *
54115 * @category   pear
54116 * @package    System
54117 * @author     Tomas V.V.Cox <cox@idecnet.com>
54118 * @copyright  1997-2009 The Authors
54119 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
54120 * @version    CVS: $Id: System.php 313024 2011-07-06 19:51:24Z dufuz $
54121 * @link       http://pear.php.net/package/PEAR
54122 * @since      File available since Release 0.1
54123 */
54124
54125/**
54126 * base class
54127 */
54128require_once 'PEAR.php';
54129require_once 'Console/Getopt.php';
54130
54131$GLOBALS['_System_temp_files'] = array();
54132
54133/**
54134* System offers cross plattform compatible system functions
54135*
54136* Static functions for different operations. Should work under
54137* Unix and Windows. The names and usage has been taken from its respectively
54138* GNU commands. The functions will return (bool) false on error and will
54139* trigger the error with the PHP trigger_error() function (you can silence
54140* the error by prefixing a '@' sign after the function call, but this
54141* is not recommended practice.  Instead use an error handler with
54142* {@link set_error_handler()}).
54143*
54144* Documentation on this class you can find in:
54145* http://pear.php.net/manual/
54146*
54147* Example usage:
54148* if (!@System::rm('-r file1 dir1')) {
54149*    print "could not delete file1 or dir1";
54150* }
54151*
54152* In case you need to to pass file names with spaces,
54153* pass the params as an array:
54154*
54155* System::rm(array('-r', $file1, $dir1));
54156*
54157* @category   pear
54158* @package    System
54159* @author     Tomas V.V. Cox <cox@idecnet.com>
54160* @copyright  1997-2006 The PHP Group
54161* @license    http://opensource.org/licenses/bsd-license.php New BSD License
54162* @version    Release: 1.9.4
54163* @link       http://pear.php.net/package/PEAR
54164* @since      Class available since Release 0.1
54165* @static
54166*/
54167class System
54168{
54169    /**
54170     * returns the commandline arguments of a function
54171     *
54172     * @param    string  $argv           the commandline
54173     * @param    string  $short_options  the allowed option short-tags
54174     * @param    string  $long_options   the allowed option long-tags
54175     * @return   array   the given options and there values
54176     * @static
54177     * @access private
54178     */
54179    function _parseArgs($argv, $short_options, $long_options = null)
54180    {
54181        if (!is_array($argv) && $argv !== null) {
54182            // Find all items, quoted or otherwise
54183            preg_match_all("/(?:[\"'])(.*?)(?:['\"])|([^\s]+)/", $argv, $av);
54184            $argv = $av[1];
54185            foreach ($av[2] as $k => $a) {
54186                if (empty($a)) {
54187                    continue;
54188                }
54189                $argv[$k] = trim($a) ;
54190            }
54191        }
54192        return Console_Getopt::getopt2($argv, $short_options, $long_options);
54193    }
54194
54195    /**
54196     * Output errors with PHP trigger_error(). You can silence the errors
54197     * with prefixing a "@" sign to the function call: @System::mkdir(..);
54198     *
54199     * @param mixed $error a PEAR error or a string with the error message
54200     * @return bool false
54201     * @static
54202     * @access private
54203     */
54204    function raiseError($error)
54205    {
54206        if (PEAR::isError($error)) {
54207            $error = $error->getMessage();
54208        }
54209        trigger_error($error, E_USER_WARNING);
54210        return false;
54211    }
54212
54213    /**
54214     * Creates a nested array representing the structure of a directory
54215     *
54216     * System::_dirToStruct('dir1', 0) =>
54217     *   Array
54218     *    (
54219     *    [dirs] => Array
54220     *        (
54221     *            [0] => dir1
54222     *        )
54223     *
54224     *    [files] => Array
54225     *        (
54226     *            [0] => dir1/file2
54227     *            [1] => dir1/file3
54228     *        )
54229     *    )
54230     * @param    string  $sPath      Name of the directory
54231     * @param    integer $maxinst    max. deep of the lookup
54232     * @param    integer $aktinst    starting deep of the lookup
54233     * @param    bool    $silent     if true, do not emit errors.
54234     * @return   array   the structure of the dir
54235     * @static
54236     * @access   private
54237     */
54238    function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false)
54239    {
54240        $struct = array('dirs' => array(), 'files' => array());
54241        if (($dir = @opendir($sPath)) === false) {
54242            if (!$silent) {
54243                System::raiseError("Could not open dir $sPath");
54244            }
54245            return $struct; // XXX could not open error
54246        }
54247
54248        $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?
54249        $list = array();
54250        while (false !== ($file = readdir($dir))) {
54251            if ($file != '.' && $file != '..') {
54252                $list[] = $file;
54253            }
54254        }
54255
54256        closedir($dir);
54257        natsort($list);
54258        if ($aktinst < $maxinst || $maxinst == 0) {
54259            foreach ($list as $val) {
54260                $path = $sPath . DIRECTORY_SEPARATOR . $val;
54261                if (is_dir($path) && !is_link($path)) {
54262                    $tmp    = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent);
54263                    $struct = array_merge_recursive($struct, $tmp);
54264                } else {
54265                    $struct['files'][] = $path;
54266                }
54267            }
54268        }
54269
54270        return $struct;
54271    }
54272
54273    /**
54274     * Creates a nested array representing the structure of a directory and files
54275     *
54276     * @param    array $files Array listing files and dirs
54277     * @return   array
54278     * @static
54279     * @see System::_dirToStruct()
54280     */
54281    function _multipleToStruct($files)
54282    {
54283        $struct = array('dirs' => array(), 'files' => array());
54284        settype($files, 'array');
54285        foreach ($files as $file) {
54286            if (is_dir($file) && !is_link($file)) {
54287                $tmp    = System::_dirToStruct($file, 0);
54288                $struct = array_merge_recursive($tmp, $struct);
54289            } else {
54290                if (!in_array($file, $struct['files'])) {
54291                    $struct['files'][] = $file;
54292                }
54293            }
54294        }
54295        return $struct;
54296    }
54297
54298    /**
54299     * The rm command for removing files.
54300     * Supports multiple files and dirs and also recursive deletes
54301     *
54302     * @param    string  $args   the arguments for rm
54303     * @return   mixed   PEAR_Error or true for success
54304     * @static
54305     * @access   public
54306     */
54307    function rm($args)
54308    {
54309        $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-)
54310        if (PEAR::isError($opts)) {
54311            return System::raiseError($opts);
54312        }
54313        foreach ($opts[0] as $opt) {
54314            if ($opt[0] == 'r') {
54315                $do_recursive = true;
54316            }
54317        }
54318        $ret = true;
54319        if (isset($do_recursive)) {
54320            $struct = System::_multipleToStruct($opts[1]);
54321            foreach ($struct['files'] as $file) {
54322                if (!@unlink($file)) {
54323                    $ret = false;
54324                }
54325            }
54326
54327            rsort($struct['dirs']);
54328            foreach ($struct['dirs'] as $dir) {
54329                if (!@rmdir($dir)) {
54330                    $ret = false;
54331                }
54332            }
54333        } else {
54334            foreach ($opts[1] as $file) {
54335                $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
54336                if (!@$delete($file)) {
54337                    $ret = false;
54338                }
54339            }
54340        }
54341        return $ret;
54342    }
54343
54344    /**
54345     * Make directories.
54346     *
54347     * The -p option will create parent directories
54348     * @param    string  $args    the name of the director(y|ies) to create
54349     * @return   bool    True for success
54350     * @static
54351     * @access   public
54352     */
54353    function mkDir($args)
54354    {
54355        $opts = System::_parseArgs($args, 'pm:');
54356        if (PEAR::isError($opts)) {
54357            return System::raiseError($opts);
54358        }
54359
54360        $mode = 0777; // default mode
54361        foreach ($opts[0] as $opt) {
54362            if ($opt[0] == 'p') {
54363                $create_parents = true;
54364            } elseif ($opt[0] == 'm') {
54365                // if the mode is clearly an octal number (starts with 0)
54366                // convert it to decimal
54367                if (strlen($opt[1]) && $opt[1]{0} == '0') {
54368                    $opt[1] = octdec($opt[1]);
54369                } else {
54370                    // convert to int
54371                    $opt[1] += 0;
54372                }
54373                $mode = $opt[1];
54374            }
54375        }
54376
54377        $ret = true;
54378        if (isset($create_parents)) {
54379            foreach ($opts[1] as $dir) {
54380                $dirstack = array();
54381                while ((!file_exists($dir) || !is_dir($dir)) &&
54382                        $dir != DIRECTORY_SEPARATOR) {
54383                    array_unshift($dirstack, $dir);
54384                    $dir = dirname($dir);
54385                }
54386
54387                while ($newdir = array_shift($dirstack)) {
54388                    if (!is_writeable(dirname($newdir))) {
54389                        $ret = false;
54390                        break;
54391                    }
54392
54393                    if (!mkdir($newdir, $mode)) {
54394                        $ret = false;
54395                    }
54396                }
54397            }
54398        } else {
54399            foreach($opts[1] as $dir) {
54400                if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) {
54401                    $ret = false;
54402                }
54403            }
54404        }
54405
54406        return $ret;
54407    }
54408
54409    /**
54410     * Concatenate files
54411     *
54412     * Usage:
54413     * 1) $var = System::cat('sample.txt test.txt');
54414     * 2) System::cat('sample.txt test.txt > final.txt');
54415     * 3) System::cat('sample.txt test.txt >> final.txt');
54416     *
54417     * Note: as the class use fopen, urls should work also (test that)
54418     *
54419     * @param    string  $args   the arguments
54420     * @return   boolean true on success
54421     * @static
54422     * @access   public
54423     */
54424    function &cat($args)
54425    {
54426        $ret = null;
54427        $files = array();
54428        if (!is_array($args)) {
54429            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
54430        }
54431
54432        $count_args = count($args);
54433        for ($i = 0; $i < $count_args; $i++) {
54434            if ($args[$i] == '>') {
54435                $mode = 'wb';
54436                $outputfile = $args[$i+1];
54437                break;
54438            } elseif ($args[$i] == '>>') {
54439                $mode = 'ab+';
54440                $outputfile = $args[$i+1];
54441                break;
54442            } else {
54443                $files[] = $args[$i];
54444            }
54445        }
54446        $outputfd = false;
54447        if (isset($mode)) {
54448            if (!$outputfd = fopen($outputfile, $mode)) {
54449                $err = System::raiseError("Could not open $outputfile");
54450                return $err;
54451            }
54452            $ret = true;
54453        }
54454        foreach ($files as $file) {
54455            if (!$fd = fopen($file, 'r')) {
54456                System::raiseError("Could not open $file");
54457                continue;
54458            }
54459            while ($cont = fread($fd, 2048)) {
54460                if (is_resource($outputfd)) {
54461                    fwrite($outputfd, $cont);
54462                } else {
54463                    $ret .= $cont;
54464                }
54465            }
54466            fclose($fd);
54467        }
54468        if (is_resource($outputfd)) {
54469            fclose($outputfd);
54470        }
54471        return $ret;
54472    }
54473
54474    /**
54475     * Creates temporary files or directories. This function will remove
54476     * the created files when the scripts finish its execution.
54477     *
54478     * Usage:
54479     *   1) $tempfile = System::mktemp("prefix");
54480     *   2) $tempdir  = System::mktemp("-d prefix");
54481     *   3) $tempfile = System::mktemp();
54482     *   4) $tempfile = System::mktemp("-t /var/tmp prefix");
54483     *
54484     * prefix -> The string that will be prepended to the temp name
54485     *           (defaults to "tmp").
54486     * -d     -> A temporary dir will be created instead of a file.
54487     * -t     -> The target dir where the temporary (file|dir) will be created. If
54488     *           this param is missing by default the env vars TMP on Windows or
54489     *           TMPDIR in Unix will be used. If these vars are also missing
54490     *           c:\windows\temp or /tmp will be used.
54491     *
54492     * @param   string  $args  The arguments
54493     * @return  mixed   the full path of the created (file|dir) or false
54494     * @see System::tmpdir()
54495     * @static
54496     * @access  public
54497     */
54498    function mktemp($args = null)
54499    {
54500        static $first_time = true;
54501        $opts = System::_parseArgs($args, 't:d');
54502        if (PEAR::isError($opts)) {
54503            return System::raiseError($opts);
54504        }
54505
54506        foreach ($opts[0] as $opt) {
54507            if ($opt[0] == 'd') {
54508                $tmp_is_dir = true;
54509            } elseif ($opt[0] == 't') {
54510                $tmpdir = $opt[1];
54511            }
54512        }
54513
54514        $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
54515        if (!isset($tmpdir)) {
54516            $tmpdir = System::tmpdir();
54517        }
54518
54519        if (!System::mkDir(array('-p', $tmpdir))) {
54520            return false;
54521        }
54522
54523        $tmp = tempnam($tmpdir, $prefix);
54524        if (isset($tmp_is_dir)) {
54525            unlink($tmp); // be careful possible race condition here
54526            if (!mkdir($tmp, 0700)) {
54527                return System::raiseError("Unable to create temporary directory $tmpdir");
54528            }
54529        }
54530
54531        $GLOBALS['_System_temp_files'][] = $tmp;
54532        if (isset($tmp_is_dir)) {
54533            //$GLOBALS['_System_temp_files'][] = dirname($tmp);
54534        }
54535
54536        if ($first_time) {
54537            PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
54538            $first_time = false;
54539        }
54540
54541        return $tmp;
54542    }
54543
54544    /**
54545     * Remove temporary files created my mkTemp. This function is executed
54546     * at script shutdown time
54547     *
54548     * @static
54549     * @access private
54550     */
54551    function _removeTmpFiles()
54552    {
54553        if (count($GLOBALS['_System_temp_files'])) {
54554            $delete = $GLOBALS['_System_temp_files'];
54555            array_unshift($delete, '-r');
54556            System::rm($delete);
54557            $GLOBALS['_System_temp_files'] = array();
54558        }
54559    }
54560
54561    /**
54562     * Get the path of the temporal directory set in the system
54563     * by looking in its environments variables.
54564     * Note: php.ini-recommended removes the "E" from the variables_order setting,
54565     * making unavaible the $_ENV array, that s why we do tests with _ENV
54566     *
54567     * @static
54568     * @return string The temporary directory on the system
54569     */
54570    function tmpdir()
54571    {
54572        if (OS_WINDOWS) {
54573            if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
54574                return $var;
54575            }
54576            if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
54577                return $var;
54578            }
54579            if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) {
54580                return $var;
54581            }
54582            if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
54583                return $var;
54584            }
54585            return getenv('SystemRoot') . '\temp';
54586        }
54587        if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
54588            return $var;
54589        }
54590        return realpath('/tmp');
54591    }
54592
54593    /**
54594     * The "which" command (show the full path of a command)
54595     *
54596     * @param string $program The command to search for
54597     * @param mixed  $fallback Value to return if $program is not found
54598     *
54599     * @return mixed A string with the full path or false if not found
54600     * @static
54601     * @author Stig Bakken <ssb@php.net>
54602     */
54603    function which($program, $fallback = false)
54604    {
54605        // enforce API
54606        if (!is_string($program) || '' == $program) {
54607            return $fallback;
54608        }
54609
54610        // full path given
54611        if (basename($program) != $program) {
54612            $path_elements[] = dirname($program);
54613            $program = basename($program);
54614        } else {
54615            // Honor safe mode
54616            if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) {
54617                $path = getenv('PATH');
54618                if (!$path) {
54619                    $path = getenv('Path'); // some OSes are just stupid enough to do this
54620                }
54621            }
54622            $path_elements = explode(PATH_SEPARATOR, $path);
54623        }
54624
54625        if (OS_WINDOWS) {
54626            $exe_suffixes = getenv('PATHEXT')
54627                                ? explode(PATH_SEPARATOR, getenv('PATHEXT'))
54628                                : array('.exe','.bat','.cmd','.com');
54629            // allow passing a command.exe param
54630            if (strpos($program, '.') !== false) {
54631                array_unshift($exe_suffixes, '');
54632            }
54633            // is_executable() is not available on windows for PHP4
54634            $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file';
54635        } else {
54636            $exe_suffixes = array('');
54637            $pear_is_executable = 'is_executable';
54638        }
54639
54640        foreach ($exe_suffixes as $suff) {
54641            foreach ($path_elements as $dir) {
54642                $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
54643                if (@$pear_is_executable($file)) {
54644                    return $file;
54645                }
54646            }
54647        }
54648        return $fallback;
54649    }
54650
54651    /**
54652     * The "find" command
54653     *
54654     * Usage:
54655     *
54656     * System::find($dir);
54657     * System::find("$dir -type d");
54658     * System::find("$dir -type f");
54659     * System::find("$dir -name *.php");
54660     * System::find("$dir -name *.php -name *.htm*");
54661     * System::find("$dir -maxdepth 1");
54662     *
54663     * Params implmented:
54664     * $dir            -> Start the search at this directory
54665     * -type d         -> return only directories
54666     * -type f         -> return only files
54667     * -maxdepth <n>   -> max depth of recursion
54668     * -name <pattern> -> search pattern (bash style). Multiple -name param allowed
54669     *
54670     * @param  mixed Either array or string with the command line
54671     * @return array Array of found files
54672     * @static
54673     *
54674     */
54675    function find($args)
54676    {
54677        if (!is_array($args)) {
54678            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
54679        }
54680        $dir = realpath(array_shift($args));
54681        if (!$dir) {
54682            return array();
54683        }
54684        $patterns = array();
54685        $depth = 0;
54686        $do_files = $do_dirs = true;
54687        $args_count = count($args);
54688        for ($i = 0; $i < $args_count; $i++) {
54689            switch ($args[$i]) {
54690                case '-type':
54691                    if (in_array($args[$i+1], array('d', 'f'))) {
54692                        if ($args[$i+1] == 'd') {
54693                            $do_files = false;
54694                        } else {
54695                            $do_dirs = false;
54696                        }
54697                    }
54698                    $i++;
54699                    break;
54700                case '-name':
54701                    $name = preg_quote($args[$i+1], '#');
54702                    // our magic characters ? and * have just been escaped,
54703                    // so now we change the escaped versions to PCRE operators
54704                    $name = strtr($name, array('\?' => '.', '\*' => '.*'));
54705                    $patterns[] = '('.$name.')';
54706                    $i++;
54707                    break;
54708                case '-maxdepth':
54709                    $depth = $args[$i+1];
54710                    break;
54711            }
54712        }
54713        $path = System::_dirToStruct($dir, $depth, 0, true);
54714        if ($do_files && $do_dirs) {
54715            $files = array_merge($path['files'], $path['dirs']);
54716        } elseif ($do_dirs) {
54717            $files = $path['dirs'];
54718        } else {
54719            $files = $path['files'];
54720        }
54721        if (count($patterns)) {
54722            $dsq = preg_quote(DIRECTORY_SEPARATOR, '#');
54723            $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#';
54724            $ret = array();
54725            $files_count = count($files);
54726            for ($i = 0; $i < $files_count; $i++) {
54727                // only search in the part of the file below the current directory
54728                $filepart = basename($files[$i]);
54729                if (preg_match($pattern, $filepart)) {
54730                    $ret[] = $files[$i];
54731                }
54732            }
54733            return $ret;
54734        }
54735        return $files;
54736    }
54737}PEAR-1.9.4/template.spec0000644000076500000240000000372511605156614013732 0ustar  helgistaffSummary: PEAR: @summary@
54738Name: @rpm_package@
54739Version: @version@
54740Release: 1
54741License: @release_license@
54742Group: Development/Libraries
54743Source: http://@master_server@/get/@package@-%{version}.tgz
54744BuildRoot: %{_tmppath}/%{name}-root
54745URL: http://@master_server@/package/@package@
54746Prefix: %{_prefix}
54747BuildArchitectures: @arch@
54748@extra_headers@
54749
54750%description
54751@description@
54752
54753%prep
54754rm -rf %{buildroot}/*
54755%setup -c -T
54756# XXX Source files location is missing here in pear cmd
54757pear -v -c %{buildroot}/pearrc \
54758        -d php_dir=%{_libdir}/php/pear \
54759        -d doc_dir=/docs \
54760        -d bin_dir=%{_bindir} \
54761        -d data_dir=%{_libdir}/php/pear/data \
54762        -d test_dir=%{_libdir}/php/pear/tests \
54763        -d ext_dir=%{_libdir} \@extra_config@
54764        -s
54765
54766%build
54767echo BuildRoot=%{buildroot}
54768
54769%postun
54770# if refcount = 0 then package has been removed (not upgraded)
54771if [ "$1" -eq "0" ]; then
54772    pear uninstall --nodeps -r @possible_channel@@package@
54773    rm @rpm_xml_dir@/@package@.xml
54774fi
54775
54776
54777%post
54778# if refcount = 2 then package has been upgraded
54779if [ "$1" -ge "2" ]; then
54780    pear upgrade --nodeps -r @rpm_xml_dir@/@package@.xml
54781else
54782    pear install --nodeps -r @rpm_xml_dir@/@package@.xml
54783fi
54784
54785%install
54786pear -c %{buildroot}/pearrc install --nodeps -R %{buildroot} \
54787        $RPM_SOURCE_DIR/@package@-%{version}.tgz
54788rm %{buildroot}/pearrc
54789rm %{buildroot}/%{_libdir}/php/pear/.filemap
54790rm %{buildroot}/%{_libdir}/php/pear/.lock
54791rm -rf %{buildroot}/%{_libdir}/php/pear/.registry
54792if [ "@doc_files@" != "" ]; then
54793     mv %{buildroot}/docs/@package@/* .
54794     rm -rf %{buildroot}/docs
54795fi
54796mkdir -p %{buildroot}@rpm_xml_dir@
54797tar -xzf $RPM_SOURCE_DIR/@package@-%{version}.tgz package@package2xml@.xml
54798cp -p package@package2xml@.xml %{buildroot}@rpm_xml_dir@/@package@.xml
54799
54800#rm -rf %{buildroot}/*
54801#pear -q install -R %{buildroot} -n package@package2xml@.xml
54802#mkdir -p %{buildroot}@rpm_xml_dir@
54803#cp -p package@package2xml@.xml %{buildroot}@rpm_xml_dir@/@package@.xml
54804
54805%files
54806    %defattr(-,root,root)
54807    %doc @doc_files@
54808    /
54809package.xml0000644000076500000240000010644711605156615012170 0ustar  helgistaff<?xml version="1.0" encoding="UTF-8" ?>
54810<!DOCTYPE package SYSTEM "http://pear.php.net/dtd/package-1.0">
54811<package version="1.0" packagerversion="1.9.4">
54812 <name>PEAR</name>
54813 <summary>PEAR Base System</summary>
54814 <description>The PEAR package contains:
54815 * the PEAR installer, for creating, distributing
54816   and installing packages
54817 * the PEAR_Exception PHP5 error handling mechanism
54818 * the PEAR_ErrorStack advanced error handling mechanism
54819 * the PEAR_Error error handling mechanism
54820 * the OS_Guess class for retrieving info about the OS
54821   where PHP is running on
54822 * the System class for quick handling of common operations
54823   with files and directories
54824 * the PEAR base class
54825
54826  Features in a nutshell:
54827  * full support for channels
54828  * pre-download dependency validation
54829  * new package.xml 2.0 format allows tremendous flexibility while maintaining BC
54830  * support for optional dependency groups and limited support for sub-packaging
54831  * robust dependency support
54832  * full dependency validation on uninstall
54833  * remote install for hosts with only ftp access - no more problems with
54834    restricted host installation
54835  * full support for mirroring
54836  * support for bundling several packages into a single tarball
54837  * support for static dependencies on a url-based package
54838  * support for custom file roles and installation tasks
54839 </description>
54840 <maintainers>
54841  <maintainer>
54842   <user>cellog</user>
54843   <name>Greg Beaver</name>
54844   <email>cellog@php.net</email>
54845   <role>lead</role>
54846  </maintainer>
54847  <maintainer>
54848   <user>pajoye</user>
54849   <name>Pierre-Alain Joye</name>
54850   <email>pierre@php.net</email>
54851   <role>lead</role>
54852  </maintainer>
54853  <maintainer>
54854   <user>ssb</user>
54855   <name>Stig Bakken</name>
54856   <email>stig@php.net</email>
54857   <role>lead</role>
54858  </maintainer>
54859  <maintainer>
54860   <user>cox</user>
54861   <name>Tomas V.V.Cox</name>
54862   <email>cox@idecnet.com</email>
54863   <role>lead</role>
54864  </maintainer>
54865  <maintainer>
54866   <user>dufuz</user>
54867   <name>Helgi Thormar</name>
54868   <email>dufuz@php.net</email>
54869   <role>lead</role>
54870  </maintainer>
54871  <maintainer>
54872   <user>tias</user>
54873   <name>Tias Guns</name>
54874   <email>tias@php.net</email>
54875   <role>developer</role>
54876  </maintainer>
54877  <maintainer>
54878   <user>timj</user>
54879   <name>Tim Jackson</name>
54880   <email>timj@php.net</email>
54881   <role>helper</role>
54882  </maintainer>
54883  <maintainer>
54884   <user>toggg</user>
54885   <name>Bertrand Gugger</name>
54886   <email>toggg@php.net</email>
54887   <role>helper</role>
54888  </maintainer>
54889  <maintainer>
54890   <user>mj</user>
54891   <name>Martin Jansen</name>
54892   <email>mj@php.net</email>
54893   <role>helper</role>
54894  </maintainer>
54895  </maintainers>
54896 <release>
54897  <version>1.9.4</version>
54898  <date>2011-07-06</date>
54899  <license>New BSD License</license>
54900  <state>stable</state>
54901  <notes>Bug Fixes:
54902* Bug #17350: &quot;pear install --force&quot; doesn&apos;t uninstall files from previous pkg versions [dufuz]
54903* Bug #18362: A whitespace TEMP_DIR path breaks install/upgrade functionality [dufuz]
54904* Bug #18440: bad tmp folder path on install : Unable to create path for C:/Program/tmp [dufuz]
54905* Bug #18581: &quot;config-get -c&quot; not returning channel&apos;s configuration when using alias [dufuz]
54906* Bug #18639: regression: installing xdebug fails most likely due to another fix [dufuz]
54907Features
54908* All System (the class) functions can now take in spaced paths as long as they are surrounded in quotes.
54909  Prior to this it was possible to do that by passing all values in as an array (by product of #18362, #18440) [dufuz]
54910  </notes>
54911  <deps>
54912   <dep type="php" rel="ge" version="4.4.0"/>
54913   <dep type="pkg" rel="ge" version="1.3.3">PEAR</dep>
54914   <dep type="pkg" rel="ge" version="1.3.7">Archive_Tar</dep>
54915   <dep type="pkg" rel="ge" version="1.2">Console_Getopt</dep>
54916   <dep type="pkg" rel="ge" version="1.0.2">Structures_Graph</dep>
54917   <dep type="pkg" rel="ge" version="0.5.0" optional="yes">PEAR_Frontend_Web</dep>
54918   <dep type="pkg" rel="ge" version="0.4.0" optional="yes">PEAR_Frontend_Gtk</dep>
54919   <dep type="ext" rel="has">xml</dep>
54920   <dep type="ext" rel="has">pcre</dep>
54921  </deps>
54922  <provides type="class" name="OS_Guess" />
54923  <provides type="class" name="System" />
54924  <filelist>
54925   <file role="php" name="OS/Guess.php">
54926    <replace from="@package_version@" to="version" type="package-info"/>
54927   </file>
54928   <file role="php" name="PEAR/ChannelFile/Parser.php">
54929    <replace from="@package_version@" to="version" type="package-info"/>
54930   </file>
54931   <file role="php" name="PEAR/Command/Auth.xml"/>
54932   <file role="php" name="PEAR/Command/Auth.php">
54933    <replace from="@package_version@" to="version" type="package-info"/>
54934   </file>
54935   <file role="php" name="PEAR/Command/Build.xml"/>
54936   <file role="php" name="PEAR/Command/Build.php">
54937    <replace from="@package_version@" to="version" type="package-info"/>
54938   </file>
54939   <file role="php" name="PEAR/Command/Channels.xml"/>
54940   <file role="php" name="PEAR/Command/Channels.php">
54941    <replace from="@package_version@" to="version" type="package-info"/>
54942   </file>
54943   <file role="php" name="PEAR/Command/Common.php">
54944    <replace from="@package_version@" to="version" type="package-info"/>
54945   </file>
54946   <file role="php" name="PEAR/Command/Config.xml"/>
54947   <file role="php" name="PEAR/Command/Config.php">
54948    <replace from="@package_version@" to="version" type="package-info"/>
54949   </file>
54950   <file role="php" name="PEAR/Command/Install.xml"/>
54951   <file role="php" name="PEAR/Command/Install.php">
54952    <replace from="@package_version@" to="version" type="package-info"/>
54953   </file>
54954   <file role="php" name="PEAR/Command/Package.xml"/>
54955   <file role="php" name="PEAR/Command/Package.php">
54956    <replace from="@DATA-DIR@" to="data_dir" type="pear-config"/>
54957    <replace from="@package_version@" to="version" type="package-info"/>
54958   </file>
54959   <file role="php" name="PEAR/Command/Pickle.xml"/>
54960   <file role="php" name="PEAR/Command/Pickle.php">
54961    <replace from="@package_version@" to="version" type="package-info"/>
54962   </file>
54963   <file role="php" name="PEAR/Command/Registry.xml"/>
54964   <file role="php" name="PEAR/Command/Registry.php">
54965    <replace from="@package_version@" to="version" type="package-info"/>
54966   </file>
54967   <file role="php" name="PEAR/Command/Remote.xml"/>
54968   <file role="php" name="PEAR/Command/Remote.php">
54969    <replace from="@package_version@" to="version" type="package-info"/>
54970   </file>
54971   <file role="php" name="PEAR/Command/Mirror.xml"/>
54972   <file role="php" name="PEAR/Command/Mirror.php">
54973    <replace from="@package_version@" to="version" type="package-info"/>
54974   </file>
54975   <file role="php" name="PEAR/Command/Test.xml"/>
54976   <file role="php" name="PEAR/Command/Test.php">
54977    <replace from="@package_version@" to="version" type="package-info"/>
54978   </file>
54979   <file role="php" name="PEAR/Downloader/Package.php">
54980    <replace from="@PEAR-VER@" to="version" type="package-info"/>
54981   </file>
54982   <file role="php" name="PEAR/Frontend/CLI.php">
54983    <replace from="@package_version@" to="version" type="package-info"/>
54984   </file>
54985   <file role="php" name="PEAR/Installer/Role/Common.php">
54986    <replace from="@package_version@" to="version" type="package-info"/>
54987   </file>
54988   <file role="php" name="PEAR/Installer/Role/Cfg.xml"/>
54989   <file role="php" name="PEAR/Installer/Role/Cfg.php">
54990    <replace from="@package_version@" to="version" type="package-info"/>
54991   </file>
54992   <file role="php" name="PEAR/Installer/Role/Data.xml"/>
54993   <file role="php" name="PEAR/Installer/Role/Data.php">
54994    <replace from="@package_version@" to="version" type="package-info"/>
54995   </file>
54996   <file role="php" name="PEAR/Installer/Role/Doc.xml"/>
54997   <file role="php" name="PEAR/Installer/Role/Doc.php">
54998    <replace from="@package_version@" to="version" type="package-info"/>
54999   </file>
55000   <file role="php" name="PEAR/Installer/Role/Ext.xml"/>
55001   <file role="php" name="PEAR/Installer/Role/Ext.php">
55002    <replace from="@package_version@" to="version" type="package-info"/>
55003   </file>
55004   <file role="php" name="PEAR/Installer/Role/Php.xml"/>
55005   <file role="php" name="PEAR/Installer/Role/Php.php">
55006    <replace from="@package_version@" to="version" type="package-info"/>
55007   </file>
55008   <file role="php" name="PEAR/Installer/Role/Script.xml"/>
55009   <file role="php" name="PEAR/Installer/Role/Script.php">
55010    <replace from="@package_version@" to="version" type="package-info"/>
55011   </file>
55012   <file role="php" name="PEAR/Installer/Role/Src.xml"/>
55013   <file role="php" name="PEAR/Installer/Role/Src.php">
55014    <replace from="@package_version@" to="version" type="package-info"/>
55015   </file>
55016   <file role="php" name="PEAR/Installer/Role/Test.xml"/>
55017   <file role="php" name="PEAR/Installer/Role/Test.php">
55018    <replace from="@package_version@" to="version" type="package-info"/>
55019   </file>
55020   <file role="php" name="PEAR/Installer/Role/Www.xml"/>
55021   <file role="php" name="PEAR/Installer/Role/Www.php">
55022    <replace from="@package_version@" to="version" type="package-info"/>
55023   </file>
55024   <file role="php" name="PEAR/Installer/Role.php">
55025    <replace from="@package_version@" to="version" type="package-info"/>
55026   </file>
55027   <file role="php" name="PEAR/PackageFile/Generator/v1.php">
55028    <replace from="@PEAR-VER@" to="version" type="package-info"/>
55029   </file>
55030   <file role="php" name="PEAR/PackageFile/Generator/v2.php">
55031    <replace from="@PEAR-VER@" to="version" type="package-info"/>
55032   </file>
55033   <file role="php" name="PEAR/PackageFile/Parser/v1.php">
55034    <replace from="@package_version@" to="version" type="package-info"/>
55035   </file>
55036   <file role="php" name="PEAR/PackageFile/Parser/v2.php">
55037    <replace from="@package_version@" to="version" type="package-info"/>
55038   </file>
55039   <file role="php" name="PEAR/PackageFile/v2/rw.php">
55040    <replace from="@package_version@" to="version" type="package-info"/>
55041   </file>
55042   <file role="php" name="PEAR/PackageFile/v2/Validator.php">
55043    <replace from="@package_version@" to="version" type="package-info"/>
55044   </file>
55045   <file role="php" name="PEAR/PackageFile/v1.php">
55046    <replace from="@package_version@" to="version" type="package-info"/>
55047   </file>
55048   <file role="php" name="PEAR/PackageFile/v2.php">
55049    <replace from="@package_version@" to="version" type="package-info"/>
55050   </file>
55051   <file role="php" name="PEAR/REST/10.php">
55052    <replace from="@package_version@" to="version" type="package-info"/>
55053   </file>
55054   <file role="php" name="PEAR/REST/11.php">
55055    <replace from="@package_version@" to="version" type="package-info"/>
55056   </file>
55057   <file role="php" name="PEAR/REST/13.php">
55058    <replace from="@package_version@" to="version" type="package-info"/>
55059   </file>
55060   <file role="php" name="PEAR/Task/Postinstallscript/rw.php">
55061    <replace from="@package_version@" to="version" type="package-info"/>
55062   </file>
55063   <file role="php" name="PEAR/Task/Replace/rw.php">
55064    <replace from="@package_version@" to="version" type="package-info"/>
55065   </file>
55066   <file role="php" name="PEAR/Task/Unixeol/rw.php">
55067    <replace from="@package_version@" to="version" type="package-info"/>
55068   </file>
55069   <file role="php" name="PEAR/Task/Windowseol/rw.php">
55070    <replace from="@package_version@" to="version" type="package-info"/>
55071   </file>
55072   <file role="php" name="PEAR/Task/Common.php">
55073    <replace from="@package_version@" to="version" type="package-info"/>
55074   </file>
55075   <file role="php" name="PEAR/Task/Postinstallscript.php">
55076    <replace from="@package_version@" to="version" type="package-info"/>
55077   </file>
55078   <file role="php" name="PEAR/Task/Replace.php">
55079    <replace from="@package_version@" to="version" type="package-info"/>
55080   </file>
55081   <file role="php" name="PEAR/Task/Unixeol.php">
55082    <replace from="@package_version@" to="version" type="package-info"/>
55083   </file>
55084   <file role="php" name="PEAR/Task/Windowseol.php">
55085    <replace from="@package_version@" to="version" type="package-info"/>
55086   </file>
55087   <file role="php" name="PEAR/Validator/PECL.php">
55088    <replace from="@package_version@" to="version" type="package-info"/>
55089   </file>
55090   <file role="php" name="PEAR/Autoloader.php">
55091    <replace from="@package_version@" to="version" type="package-info"/>
55092   </file>
55093   <file role="php" name="PEAR/Builder.php">
55094    <replace from="@PEAR-VER@" to="version" type="package-info"/>
55095   </file>
55096   <file role="php" name="PEAR/ChannelFile.php">
55097    <replace from="@package_version@" to="version" type="package-info"/>
55098   </file>
55099   <file role="php" name="PEAR/Command.php">
55100    <replace from="@package_version@" to="version" type="package-info"/>
55101   </file>
55102   <file role="php" name="PEAR/Common.php">
55103    <replace from="@package_version@" to="version" type="package-info"/>
55104   </file>
55105   <file role="php" name="PEAR/Config.php">
55106    <replace from="@package_version@" to="version" type="package-info"/>
55107   </file>
55108   <file role="php" name="PEAR/DependencyDB.php">
55109    <replace from="@package_version@" to="version" type="package-info"/>
55110   </file>
55111   <file role="php" name="PEAR/Dependency2.php">
55112    <replace from="@PEAR-VER@" to="version" type="package-info"/>
55113   </file>
55114   <file role="php" name="PEAR/Downloader.php">
55115    <replace from="@package_version@" to="version" type="package-info"/>
55116   </file>
55117   <file role="php" name="PEAR/ErrorStack.php">
55118    <replace from="@package_version@" to="version" type="package-info"/>
55119   </file>
55120   <file role="php" name="PEAR/Exception.php">
55121    <replace from="@package_version@" to="version" type="package-info"/>
55122   </file>
55123   <file role="php" name="PEAR/FixPHP5PEARWarnings.php"/>
55124   <file role="php" name="PEAR/Frontend.php">
55125    <replace from="@package_version@" to="version" type="package-info"/>
55126   </file>
55127   <file role="php" name="PEAR/Installer.php">
55128    <replace from="@package_version@" to="version" type="package-info"/>
55129   </file>
55130   <file role="php" name="PEAR/Packager.php">
55131    <replace from="@package_version@" to="version" type="package-info"/>
55132   </file>
55133   <file role="php" name="PEAR/PackageFile.php">
55134    <replace from="@PEAR-VER@" to="version" type="package-info"/>
55135   </file>
55136   <file role="php" name="PEAR/Registry.php">
55137    <replace from="@package_version@" to="version" type="package-info"/>
55138   </file>
55139   <file role="php" name="PEAR/REST.php">
55140    <replace from="@package_version@" to="version" type="package-info"/>
55141   </file>
55142   <file role="php" name="PEAR/RunTest.php">
55143    <replace from="@package_version@" to="version" type="package-info"/>
55144   </file>
55145   <file role="php" name="PEAR/Validate.php">
55146    <replace from="@package_version@" to="version" type="package-info"/>
55147   </file>
55148   <file role="php" name="PEAR/XMLParser.php">
55149    <replace from="@package_version@" to="version" type="package-info"/>
55150   </file>
55151   <file role="script" baseinstalldir="/" platform="!windows" install-as="pear" name="scripts/pear.sh">
55152    <replace from="@php_bin@" to="php_bin" type="pear-config"/>
55153    <replace from="@php_dir@" to="php_dir" type="pear-config"/>
55154    <replace from="@pear_version@" to="version" type="package-info"/>
55155    <replace from="@include_path@" to="php_dir" type="pear-config"/>
55156   </file>
55157   <file role="script" baseinstalldir="/" platform="!windows" install-as="peardev" name="scripts/peardev.sh">
55158    <replace from="@php_bin@" to="php_bin" type="pear-config"/>
55159    <replace from="@php_dir@" to="php_dir" type="pear-config"/>
55160    <replace from="@pear_version@" to="version" type="package-info"/>
55161    <replace from="@include_path@" to="php_dir" type="pear-config"/>
55162   </file>
55163   <file role="script" baseinstalldir="/" platform="!windows" install-as="pecl" name="scripts/pecl.sh">
55164    <replace from="@php_bin@" to="php_bin" type="pear-config"/>
55165    <replace from="@php_dir@" to="php_dir" type="pear-config"/>
55166    <replace from="@pear_version@" to="version" type="package-info"/>
55167    <replace from="@include_path@" to="php_dir" type="pear-config"/>
55168   </file>
55169   <file role="script" baseinstalldir="/" platform="windows" install-as="peardev.bat" name="scripts/peardev.bat">
55170    <replace from="@bin_dir@" to="bin_dir" type="pear-config"/>
55171    <replace from="@php_bin@" to="php_bin" type="pear-config"/>
55172    <replace from="@include_path@" to="php_dir" type="pear-config"/>
55173   </file>
55174   <file role="script" baseinstalldir="/" platform="windows" install-as="pear.bat" name="scripts/pear.bat">
55175    <replace from="@bin_dir@" to="bin_dir" type="pear-config"/>
55176    <replace from="@php_bin@" to="php_bin" type="pear-config"/>
55177    <replace from="@include_path@" to="php_dir" type="pear-config"/>
55178   </file>
55179   <file role="script" baseinstalldir="/" platform="windows" install-as="pecl.bat" name="scripts/pecl.bat">
55180    <replace from="@bin_dir@" to="bin_dir" type="pear-config"/>
55181    <replace from="@php_bin@" to="php_bin" type="pear-config"/>
55182    <replace from="@include_path@" to="php_dir" type="pear-config"/>
55183   </file>
55184   <file role="php" baseinstalldir="/" install-as="pearcmd.php" name="scripts/pearcmd.php">
55185    <replace from="@php_bin@" to="php_bin" type="pear-config"/>
55186    <replace from="@php_dir@" to="php_dir" type="pear-config"/>
55187    <replace from="@pear_version@" to="version" type="package-info"/>
55188    <replace from="@include_path@" to="php_dir" type="pear-config"/>
55189   </file>
55190   <file role="php" baseinstalldir="/" install-as="peclcmd.php" name="scripts/peclcmd.php">
55191    <replace from="@php_bin@" to="php_bin" type="pear-config"/>
55192    <replace from="@php_dir@" to="php_dir" type="pear-config"/>
55193    <replace from="@pear_version@" to="version" type="package-info"/>
55194    <replace from="@include_path@" to="php_dir" type="pear-config"/>
55195   </file>
55196   <file role="doc" baseinstalldir="/" name="LICENSE"/>
55197   <file role="doc" baseinstalldir="/" name="INSTALL"/>
55198   <file role="data" baseinstalldir="/" name="package.dtd"/>
55199   <file role="data" baseinstalldir="/" name="template.spec"/>
55200   <file role="php" baseinstalldir="/" name="PEAR.php">
55201    <replace from="@package_version@" to="version" type="package-info"/>
55202   </file>
55203   <file role="php" baseinstalldir="/" name="PEAR5.php"/>
55204   <file role="doc" baseinstalldir="/" name="README"/>
55205   <file role="php" baseinstalldir="/" name="System.php">
55206    <replace from="@package_version@" to="version" type="package-info"/>
55207   </file>
55208  </filelist>
55209 </release>
55210 <changelog>
55211   <release>
55212    <version>1.8.0alpha1</version>
55213    <date>2009-03-09</date>
55214    <license>New BSD License</license>
55215    <state>alpha</state>
55216    <notes>* Implement Request #10373: if pref_state=stable and installed package=beta, allow up to latest beta version [dufuz]
55217* Implement Request #10581: login / logout should map to channel-login / channel-logout [dufuz]
55218* Implement Request #10825: Only display the &quot;invalid or missing package file&quot;-error if it makes sense [dufuz]
55219* Implement Request #11170: script to generate Command/[command].xml [dufuz]
55220* Implement Request #11176: improve channel ... has updated its protocols message [dufuz]
55221* Implement Request #12706: pear list -a hard to read [dufuz]
55222* Implement Request #11353: upgrade-all and upgrade commands to upgrade within the same stability level [dufuz]
55223* Implement Request #13015: Add https discovery for channel.xml [dufuz / initial patch by Martin Roos]
55224* Implement Request #13927: install-pear.php should have option to set www_dir [timj]
55225* Implement Request #14324: Make the pear install command behave similar to apt-get [dufuz]
55226* Implement Request #14325: make pear upgrade with no params behave like pear upgrade-all [dufuz]
55227  - upgrade-all can be considered deprecated in favor of calling upgrade with no parameters to replicate
55228    better what other package managers are doing. upgrade-all will still work as intended.
55229* Implement Request #14504: add a channel parameter support to the upgrade function [dufuz]
55230  - Options -c ezc and --channel=ezc got added to upgrade and upgrade-all to allow for
55231    channel specific upgrades
55232* Implement Request #14556: install-pear-nozlib.phar should get download_dir config and other options [cweiske]
55233* Implement Request #15566: Add doc.php.net as a default channel [dufuz / saltybeagle]
55234* Fix PHP Bug #43857: --program-suffix not always reflected everywhere [cellog]
55235* Fix PHP Bug #47323: strotime warnings in make install [dufuz]
55236* Fix Bug #13908: pear info command and maintainers inactive not mentioned [dufuz]
55237* Fix Bug #13926: install-pear.php does not set cfg_dir if -d option set with no -c option [timj]
55238* Fix Bug #13943: tests fail when php.exe path contains spaces [dufuz / jorrit]
55239* Fix Bug #13953: config-set/config-show with channel alias fail [cellog]
55240* Fix Bug #13958: When a phpt tests exit() or die() xdebug coverage is not generated, patch by izi (David Jean Louis) [izi / dufuz]
55241* Fix Bug #14041: Unpredictable unit test processing sequence [dufuz]
55242* Fix Bug #14140: Strict warning not suppressed in the shutdown function [dufuz]
55243* Fix Bug #14210: pear list -ia brings warnings [dufuz]
55244* Fix Bug #14274: PEAR packager mangles package.xml encoding, then complains about it [dufuz]
55245* Fix Bug #14287: cannot upgrade from stable to beta via -beta when config is set to stable [dufuz]
55246* Fix Bug #14300: Package files themselves can not be served over https [dufuz / initial patch by Martin Roos]
55247* Fix Bug #14437: openbasedir warning when loading config [dufuz]
55248* Fix Bug #14558: PackageFile.php creates tmp directory outside configured temp_dir [cweiske]
55249* Fix Bug #14947: downloadHttp() is missing Host part of the HTTP Request when using Proxy [ifeghali]
55250* Fix Bug #14977: PEAR/Frontend.php doesn&apos;t require_once PEAR.php [dufuz]
55251* Fix Bug #15750: Unreachable code in PEAR_Downloader [dufuz]
55252* Fix Bug #15979: Package files incorrectly removed when splitting a package into multiple pkgs [dufuz]
55253* Fix Bug #15914: pear upgrade installs different version if desired version not found [dufuz]
55254NOTE!
55255Functions that have been deprecated for 3+ years in PEAR_Common, please take a moment
55256to migrate over to one of the alternatives that have ben provided:
55257* PEAR_Common-&gt;downloadHttp (use PEAR_Downloader-&gt;downloadHttp instead)
55258* PEAR_Common-&gt;infoFromTgzFile (use PEAR_PackageFile-&gt;fromTgzFile instead)
55259* PEAR_Common-&gt;infoFromDescriptionFile (use PEAR_PackageFile-&gt;fromPackageFile instead)
55260* PEAR_Common-&gt;infoFromString (use PEAR_PackageFile-&gt;fromXmlstring instead)
55261* PEAR_Common-&gt;infoFromArray (use PEAR_PackageFile-&gt;fromAnyFile instead)
55262* PEAR_Common-&gt;xmlFromInfo (use a PEAR_PackageFile_v* object&apos;s generator instead)
55263* PEAR_Common-&gt;validatePackageInfo (use the validation of PEAR_PackageFile objects)
55264* PEAR_Common-&gt;analyzeSourceCode (use a PEAR_PackageFile_v* object instead)
55265* PEAR_Common-&gt;detectDependencies (use PEAR_Downloader_Package-&gt;detectDependencies instead)
55266* PEAR_Common-&gt;buildProvidesArray (use PEAR_PackageFile_v1-&gt;_buildProvidesArray or
55267  PEAR_PackageFile_v2_Validator-&gt;_buildProvidesArray)
55268PHP 4.4 and 5.1.6 are now the minimum PHP requirements, for brave souls
55269pear upgrade -f PEAR will allow people with lower versions
55270to upgrade to this release but no guarantees will be made that it will work properly.
55271Support for XML RPC channels has been dropped - The only ones that used it
55272(pear.php.net and pecl.php.net) have used the REST interface for years now.
55273SOAP support also removed as it was only proof of concept.
55274Move codebase from the PHP License to New BSD 2 clause license
55275    </notes>
55276   </release>
55277   <release>
55278    <version>1.8.0RC1</version>
55279    <date>2009-03-27</date>
55280    <license>New BSD License</license>
55281    <state>beta</state>
55282    <notes>* Fix Bug #14331: pear cvstag only works from inside the package directory [dufuz]
55283* Fix Bug #16045: E_Notice: Undefined index: channel in PEAR/DependencyDB.php [dufuz]
55284* Implemented Request #11230: better error message when mirror not in channel.xml file [dufuz]
55285* Implemented Request #13150: Add support for following HTTP 302 redirects [dufuz]
55286    </notes>
55287   </release>
55288   <release>
55289   </release>
55290   <release>
55291    <license>New BSD License</license>
55292    <notes>Changes since RC1:
55293  * Fix Bug #14792: Bad md5sum for files with replaced content [dufuz]
55294  * Fix Bug #16057:-r is limited to 4 directories in depth [dufuz]
55295  * Fix Bug #16077: PEAR5::getStaticProperty does not return a reference to the property [dufuz]
55296
55297  Remove custom XML_Util class in favor of using upstream XML_Util package as dependency
55298
55299RC1 Release Notes:
55300  * Fix Bug #14331: pear cvstag only works from inside the package directory [dufuz]
55301  * Fix Bug #16045: E_Notice: Undefined index: channel in PEAR/DependencyDB.php [dufuz]
55302
55303  * Implemented Request #11230: better error message when mirror not in channel.xml file [dufuz]
55304  * Implemented Request #13150: Add support for following HTTP 302 redirects [dufuz]
55305
55306Alpha1 Release Notes:
55307  * Implement Request #10373: if pref_state=stable and installed package=beta, allow up to latest beta version [dufuz]
55308  * Implement Request #10581: login / logout should map to channel-login / channel-logout [dufuz]
55309  * Implement Request #10825: Only display the &quot;invalid or missing package file&quot;-error if it makes sense [dufuz]
55310  * Implement Request #11170: script to generate Command/[command].xml [dufuz]
55311  * Implement Request #11176: improve channel ... has updated its protocols message [dufuz]
55312  * Implement Request #12706: pear list -a hard to read [dufuz]
55313  * Implement Request #11353: upgrade-all and upgrade commands to upgrade within the same stability level [dufuz]
55314  * Implement Request #13015: Add https discovery for channel.xml [dufuz / initial patch by Martin Roos]
55315  * Implement Request #13927: install-pear.php should have option to set www_dir [timj]
55316  * Implement Request #14324: Make the pear install command behave similar to apt-get [dufuz]
55317  * Implement Request #14325: make pear upgrade with no params behave like pear upgrade-all [dufuz]
55318    - upgrade-all can be considered deprecated in favor of calling upgrade with no parameters to replicate
55319      better what other package managers are doing. upgrade-all will still work as intended.
55320  * Implement Request #14504: add a channel parameter support to the upgrade function [dufuz]
55321    - Options -c ezc and --channel=ezc got added to upgrade and upgrade-all to allow for
55322      channel specific upgrades
55323  * Implement Request #14556: install-pear-nozlib.phar should get download_dir config and other options [cweiske]
55324  * Implement Request #15566: Add doc.php.net as a default channel [dufuz / saltybeagle]
55325
55326  * Fix PHP Bug #43857: --program-suffix not always reflected everywhere [cellog]
55327  * Fix PHP Bug #47323: strotime warnings in make install [dufuz]
55328
55329  * Fix Bug #13908: pear info command and maintainers inactive not mentioned [dufuz]
55330  * Fix Bug #13926: install-pear.php does not set cfg_dir if -d option set with no -c option [timj]
55331  * Fix Bug #13943: tests fail when php.exe path contains spaces [dufuz / jorrit]
55332  * Fix Bug #13953: config-set/config-show with channel alias fail [cellog]
55333  * Fix Bug #13958: When a phpt tests exit() or die() xdebug coverage is not generated, patch by izi (David Jean Louis) [izi / dufuz]
55334  * Fix Bug #14041: Unpredictable unit test processing sequence [dufuz]
55335  * Fix Bug #14140: Strict warning not suppressed in the shutdown function [dufuz]
55336  * Fix Bug #14210: pear list -ia brings warnings [dufuz]
55337  * Fix Bug #14274: PEAR packager mangles package.xml encoding, then complains about it [dufuz]
55338  * Fix Bug #14287: cannot upgrade from stable to beta via -beta when config is set to stable [dufuz]
55339  * Fix Bug #14300: Package files themselves can not be served over https [dufuz / initial patch by Martin Roos]
55340  * Fix Bug #14437: openbasedir warning when loading config [dufuz]
55341  * Fix Bug #14558: PackageFile.php creates tmp directory outside configured temp_dir [cweiske]
55342  * Fix Bug #14947: downloadHttp() is missing Host part of the HTTP Request when using Proxy [ifeghali]
55343  * Fix Bug #14977: PEAR/Frontend.php doesn&apos;t require_once PEAR.php [dufuz]
55344  * Fix Bug #15750: Unreachable code in PEAR_Downloader [dufuz]
55345  * Fix Bug #15979: Package files incorrectly removed when splitting a package into multiple pkgs [dufuz]
55346  * Fix Bug #15914: pear upgrade installs different version if desired version not found [dufuz]
55347
55348  NOTE!
55349  Functions that have been deprecated for 3+ years in PEAR_Common, please take a moment
55350  to migrate over to one of the alternatives that have ben provided:
55351  * PEAR_Common-&gt;downloadHttp (use PEAR_Downloader-&gt;downloadHttp instead)
55352  * PEAR_Common-&gt;infoFromTgzFile (use PEAR_PackageFile-&gt;fromTgzFile instead)
55353  * PEAR_Common-&gt;infoFromDescriptionFile (use PEAR_PackageFile-&gt;fromPackageFile instead)
55354  * PEAR_Common-&gt;infoFromString (use PEAR_PackageFile-&gt;fromXmlstring instead)
55355  * PEAR_Common-&gt;infoFromArray (use PEAR_PackageFile-&gt;fromAnyFile instead)
55356  * PEAR_Common-&gt;xmlFromInfo (use a PEAR_PackageFile_v* object&apos;s generator instead)
55357  * PEAR_Common-&gt;validatePackageInfo (use the validation of PEAR_PackageFile objects)
55358  * PEAR_Common-&gt;analyzeSourceCode (use a PEAR_PackageFile_v* object instead)
55359  * PEAR_Common-&gt;detectDependencies (use PEAR_Downloader_Package-&gt;detectDependencies instead)
55360  * PEAR_Common-&gt;buildProvidesArray (use PEAR_PackageFile_v1-&gt;_buildProvidesArray or
55361    PEAR_PackageFile_v2_Validator-&gt;_buildProvidesArray)
55362
55363  PHP 4.4 and 5.1.6 are now the minimum PHP requirements, for brave souls
55364  pear upgrade -f PEAR will allow people with lower versions
55365  to upgrade to this release but no guarantees will be made that it will work properly.
55366
55367  Support for XML RPC channels has been dropped - The only ones that used it
55368  (pear.php.net and pecl.php.net) have used the REST interface for years now.
55369  SOAP support also removed as it was only proof of concept.
55370
55371  Move codebase from the PHP License to New BSD 2 clause license
55372    </notes>
55373   </release>
55374   <release>
55375    <version>1.8.1</version>
55376    <date>2009-04-15</date>
55377    <license>New BSD License</license>
55378    <state>stable</state>
55379    <notes>* Fix Bug #16099 	PEAR crash on PHP4 (parse error) [dufuz]
55380    </notes>
55381   </release>
55382   <release>
55383    <version>1.9.0RC1</version>
55384    <date>2009-08-18</date>
55385    <license>New BSD License</license>
55386    <state>beta</state>
55387    <notes>* Implement Request #16213: add alias to list-channels output [dufuz]
55388* Implement Request #16378: pear svntag [dufuz]
55389* Implement Request #16386: PEAR_Config::remove() does not support specifying a channel [timj]
55390* Implement Request #16396: package-dependencies should allow package names [dufuz]
55391* Fix Bug #11181: pear requests channel.xml from main server instead from mirror [dufuz]
55392* Fix Bug #14493: pear install --offline doesn&apos;t print out errors [dufuz]
55393* Fix Bug #11348: pear package-dependencies isn&apos;t well explained [dufuz]
55394* Fix Bug #16108: PEAR_PackageFile_Generator_v2 PHP4 parse error when running upgrade-all [dufuz]
55395* Fix Bug #16113: Installing certain packages fails due incorrect encoding handling [dufuz]
55396* Fix Bug #16122: PEAR RunTest failed to run as expected [dufuz]
55397* Fix Bug #16366: compiling 5.2.10 leads to non-functioning pear [dufuz]
55398* Fix Bug #16387: channel-logout does not support logging out from a non-default channel [timj]
55399* Fix Bug #16444: Setting preferred mirror fails [dufuz]
55400* Fix the shutdown functions where a index might not exist and thus raise a notice [derick]
55401    </notes>
55402   </release>
55403   <release>
55404    <version>1.9.0RC2</version>
55405    <date>2009-08-20</date>
55406    <license>New BSD License</license>
55407    <state>beta</state>
55408    <notes>* REST 1.4 file was occasionally being included but REST 1.4 is not intended for this release cycle [dufuz]
55409    </notes>
55410   </release>
55411   <release>
55412    <version>1.9.0RC3</version>
55413    <date>2009-08-21</date>
55414    <license>New BSD License</license>
55415    <state>beta</state>
55416    <notes>* Improved svntag support to handle packages like PEAR it self [dufuz]
55417    </notes>
55418   </release>
55419   <release>
55420    <version>1.9.0RC4</version>
55421    <date>2009-08-23</date>
55422    <license>New BSD License</license>
55423    <state>beta</state>
55424    <notes>* Fixed a problem where the original channel could not be set as a preferred_mirror again [dufuz]
55425* Make sure channel aliases can&apos;t be made to start with - [dufuz]
55426* Output issues with pear search [dufuz]
55427* Fixed couple of stray notices [dufuz]
55428    </notes>
55429   </release>
55430   <release>
55431    <version>1.9.0</version>
55432    <date>2009-09-03</date>
55433    <license>New BSD License</license>
55434    <state>stable</state>
55435    <notes>* Fix  Bug #16547: The phar for PEAR installer uses ereg() which is deprecated [dufuz]
55436    </notes>
55437   </release>
55438   <release>
55439    <version>1.9.1</version>
55440    <date>2010-05-26</date>
55441    <license>New BSD License</license>
55442    <state>stable</state>
55443    <notes>* svntag improvements, tag package files passed into the command and better directory checks [dufuz]
55444* rely on Structures_Graph minimum version instead of recommended version [saltybeagle]
55445* Fix Bug #12613: running go-pear.phar from C:\ fails [dufuz]
55446* Fix Bug #14841: Installing pear into directory with space fails [dufuz]
55447* Fix Bug #16644: pear.bat returns syntax error when parenthesis are in install path. [dufuz] [patch by bwaters (Bryan Waters)]
55448* Fix Bug #16767: Use of Depreciated HTML Attributes in the Exception class [dufuz] [patch by fuhrysteve (Stephen J. Fuhry)]
55449* Fix Bug #16864: &quot;pear list-upgrades -i&quot; issues E_WARNINGS [dufuz] [patch by rquadling (Richard Quadling)]
55450* Fix Bug #17220: command `pear help` outputs to stderr instead of stdout [dufuz]
55451* Fix Bug #17234: channel-discover adds port to HTTP Host header [dufuz]
55452* Fix Bug #17292: Code Coverage in PEAR_RunTest does not work with namespaces [sebastian]
55453* Fix Bug #17359: loadExtension() fails over missing dl() when used in multithread env [dufuz]
55454* Fix Bug #17378: pear info $package fails if directory with that name exists [dufuz]
55455    </notes>
55456   </release>
55457   <release>
55458    <version>1.9.2</version>
55459    <date>2011-02-28</date>
55460    <license>New BSD License</license>
55461    <state>stable</state>
55462    <notes>Important! This is a security fix release. The advisory can be found at
55463http://pear.php.net/advisory-20110228.txt
55464
55465    Bugs:
55466    * Fixed Bug #17463: Regression: On Windows, svntag [patch by doconnor]
55467    * Fixed Bug #17641: pecl-list doesn&apos;t sort packages by name [dufuz]
55468    * Fixed Bug #17781: invalid argument warning on foreach due to an empty optional dependencie [dufuz]
55469    * Fixed Bug #17801: PEAR run-tests wrongly detects php-cgi [patch by David Jean Louis (izi)]
55470    * Fixed Bug #17839: pear svntag does not tag package.xml file [dufuz]
55471    * Fixed Bug #17986: PEAR Installer cannot handle files moved between packages [dufuz]
55472    * Fixed Bug #17997: Strange output if directories are not writeable [dufuz]
55473    * Fixed Bug #18001: PEAR/RunTest coverage fails [dufuz]
55474    * Fixed Bug #18056 [SECURITY]: Symlink attack in PEAR install [dufuz]
55475    * Fixed Bug #18218: &quot;pear package&quot; does not allow the use of late static binding [dufuz and Christer Edvartsen]
55476    * Fixed Bug #18238: Wrong return code from &quot;pear help&quot; [till]
55477    * Fixed Bug #18308: Broken error message about missing channel validator [yunosh]
55478
55479    This feature is implemented as a result of #18056
55480    * Implemented Request #16648: Use TMPDIR for builds instead of /var/tmp [dufuz]
55481    </notes>
55482   </release>
55483   <release>
55484    <version>1.9.3</version>
55485    <date>2011-06-04</date>
55486    <license>New BSD License</license>
55487    <state>stable</state>
55488    <notes>* Fixed Bug #17744: Empty changelog causes fatal error in setChangelogentry [dufuz]
55489* Fixed Bug #18340: raiseErro typo [doconnor]
55490* Fixed Bug #18349: package.xml version not recognized when single quoted [dufuz]
55491* Fixed Bug #18364: date.timezone errors for sh/bat files when TZ is not set in php.ini [dufuz]
55492* Fixed Bug #18388: Parentheses error in REST.php line 232 [dufuz]
55493* Fixed Bug #18428: invalid preg_match patterns [glen]
55494* Fixed Bug #18486: REST/10.php does not check error condition [dufuz]
55495* Fixed a problem in RunTest and code coverage. Correctly register the
55496  code coverage shutdown function in case we are inside a namespace. [sebastian]
55497* Fixed a bug with extensions not providing their config.m4 and co in the root directory
55498  of their pecl package but rather in a sub directory, such as xhprof. [dufuz]
55499    </notes>
55500   </release>
55501 </changelog>
55502</package>
55503<?php
55504/**
55505 * PEAR, the PHP Extension and Application Repository
55506 *
55507 * PEAR class and PEAR_Error class
55508 *
55509 * PHP versions 4 and 5
55510 *
55511 * @category   pear
55512 * @package    PEAR
55513 * @author     Sterling Hughes <sterling@php.net>
55514 * @author     Stig Bakken <ssb@php.net>
55515 * @author     Tomas V.V.Cox <cox@idecnet.com>
55516 * @author     Greg Beaver <cellog@php.net>
55517 * @copyright  1997-2010 The Authors
55518 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
55519 * @version    CVS: $Id: PEAR.php 313023 2011-07-06 19:17:11Z dufuz $
55520 * @link       http://pear.php.net/package/PEAR
55521 * @since      File available since Release 0.1
55522 */
55523
55524/**#@+
55525 * ERROR constants
55526 */
55527define('PEAR_ERROR_RETURN',     1);
55528define('PEAR_ERROR_PRINT',      2);
55529define('PEAR_ERROR_TRIGGER',    4);
55530define('PEAR_ERROR_DIE',        8);
55531define('PEAR_ERROR_CALLBACK',  16);
55532/**
55533 * WARNING: obsolete
55534 * @deprecated
55535 */
55536define('PEAR_ERROR_EXCEPTION', 32);
55537/**#@-*/
55538define('PEAR_ZE2', (function_exists('version_compare') &&
55539                    version_compare(zend_version(), "2-dev", "ge")));
55540
55541if (substr(PHP_OS, 0, 3) == 'WIN') {
55542    define('OS_WINDOWS', true);
55543    define('OS_UNIX',    false);
55544    define('PEAR_OS',    'Windows');
55545} else {
55546    define('OS_WINDOWS', false);
55547    define('OS_UNIX',    true);
55548    define('PEAR_OS',    'Unix'); // blatant assumption
55549}
55550
55551$GLOBALS['_PEAR_default_error_mode']     = PEAR_ERROR_RETURN;
55552$GLOBALS['_PEAR_default_error_options']  = E_USER_NOTICE;
55553$GLOBALS['_PEAR_destructor_object_list'] = array();
55554$GLOBALS['_PEAR_shutdown_funcs']         = array();
55555$GLOBALS['_PEAR_error_handler_stack']    = array();
55556
55557@ini_set('track_errors', true);
55558
55559/**
55560 * Base class for other PEAR classes.  Provides rudimentary
55561 * emulation of destructors.
55562 *
55563 * If you want a destructor in your class, inherit PEAR and make a
55564 * destructor method called _yourclassname (same name as the
55565 * constructor, but with a "_" prefix).  Also, in your constructor you
55566 * have to call the PEAR constructor: $this->PEAR();.
55567 * The destructor method will be called without parameters.  Note that
55568 * at in some SAPI implementations (such as Apache), any output during
55569 * the request shutdown (in which destructors are called) seems to be
55570 * discarded.  If you need to get any debug information from your
55571 * destructor, use error_log(), syslog() or something similar.
55572 *
55573 * IMPORTANT! To use the emulated destructors you need to create the
55574 * objects by reference: $obj =& new PEAR_child;
55575 *
55576 * @category   pear
55577 * @package    PEAR
55578 * @author     Stig Bakken <ssb@php.net>
55579 * @author     Tomas V.V. Cox <cox@idecnet.com>
55580 * @author     Greg Beaver <cellog@php.net>
55581 * @copyright  1997-2006 The PHP Group
55582 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
55583 * @version    Release: 1.9.4
55584 * @link       http://pear.php.net/package/PEAR
55585 * @see        PEAR_Error
55586 * @since      Class available since PHP 4.0.2
55587 * @link        http://pear.php.net/manual/en/core.pear.php#core.pear.pear
55588 */
55589class PEAR
55590{
55591    /**
55592     * Whether to enable internal debug messages.
55593     *
55594     * @var     bool
55595     * @access  private
55596     */
55597    var $_debug = false;
55598
55599    /**
55600     * Default error mode for this object.
55601     *
55602     * @var     int
55603     * @access  private
55604     */
55605    var $_default_error_mode = null;
55606
55607    /**
55608     * Default error options used for this object when error mode
55609     * is PEAR_ERROR_TRIGGER.
55610     *
55611     * @var     int
55612     * @access  private
55613     */
55614    var $_default_error_options = null;
55615
55616    /**
55617     * Default error handler (callback) for this object, if error mode is
55618     * PEAR_ERROR_CALLBACK.
55619     *
55620     * @var     string
55621     * @access  private
55622     */
55623    var $_default_error_handler = '';
55624
55625    /**
55626     * Which class to use for error objects.
55627     *
55628     * @var     string
55629     * @access  private
55630     */
55631    var $_error_class = 'PEAR_Error';
55632
55633    /**
55634     * An array of expected errors.
55635     *
55636     * @var     array
55637     * @access  private
55638     */
55639    var $_expected_errors = array();
55640
55641    /**
55642     * Constructor.  Registers this object in
55643     * $_PEAR_destructor_object_list for destructor emulation if a
55644     * destructor object exists.
55645     *
55646     * @param string $error_class  (optional) which class to use for
55647     *        error objects, defaults to PEAR_Error.
55648     * @access public
55649     * @return void
55650     */
55651    function PEAR($error_class = null)
55652    {
55653        $classname = strtolower(get_class($this));
55654        if ($this->_debug) {
55655            print "PEAR constructor called, class=$classname\n";
55656        }
55657
55658        if ($error_class !== null) {
55659            $this->_error_class = $error_class;
55660        }
55661
55662        while ($classname && strcasecmp($classname, "pear")) {
55663            $destructor = "_$classname";
55664            if (method_exists($this, $destructor)) {
55665                global $_PEAR_destructor_object_list;
55666                $_PEAR_destructor_object_list[] = &$this;
55667                if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
55668                    register_shutdown_function("_PEAR_call_destructors");
55669                    $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
55670                }
55671                break;
55672            } else {
55673                $classname = get_parent_class($classname);
55674            }
55675        }
55676    }
55677
55678    /**
55679     * Destructor (the emulated type of...).  Does nothing right now,
55680     * but is included for forward compatibility, so subclass
55681     * destructors should always call it.
55682     *
55683     * See the note in the class desciption about output from
55684     * destructors.
55685     *
55686     * @access public
55687     * @return void
55688     */
55689    function _PEAR() {
55690        if ($this->_debug) {
55691            printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
55692        }
55693    }
55694
55695    /**
55696    * If you have a class that's mostly/entirely static, and you need static
55697    * properties, you can use this method to simulate them. Eg. in your method(s)
55698    * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
55699    * You MUST use a reference, or they will not persist!
55700    *
55701    * @access public
55702    * @param  string $class  The calling classname, to prevent clashes
55703    * @param  string $var    The variable to retrieve.
55704    * @return mixed   A reference to the variable. If not set it will be
55705    *                 auto initialised to NULL.
55706    */
55707    function &getStaticProperty($class, $var)
55708    {
55709        static $properties;
55710        if (!isset($properties[$class])) {
55711            $properties[$class] = array();
55712        }
55713
55714        if (!array_key_exists($var, $properties[$class])) {
55715            $properties[$class][$var] = null;
55716        }
55717
55718        return $properties[$class][$var];
55719    }
55720
55721    /**
55722    * Use this function to register a shutdown method for static
55723    * classes.
55724    *
55725    * @access public
55726    * @param  mixed $func  The function name (or array of class/method) to call
55727    * @param  mixed $args  The arguments to pass to the function
55728    * @return void
55729    */
55730    function registerShutdownFunc($func, $args = array())
55731    {
55732        // if we are called statically, there is a potential
55733        // that no shutdown func is registered.  Bug #6445
55734        if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
55735            register_shutdown_function("_PEAR_call_destructors");
55736            $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
55737        }
55738        $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
55739    }
55740
55741    /**
55742     * Tell whether a value is a PEAR error.
55743     *
55744     * @param   mixed $data   the value to test
55745     * @param   int   $code   if $data is an error object, return true
55746     *                        only if $code is a string and
55747     *                        $obj->getMessage() == $code or
55748     *                        $code is an integer and $obj->getCode() == $code
55749     * @access  public
55750     * @return  bool    true if parameter is an error
55751     */
55752    function isError($data, $code = null)
55753    {
55754        if (!is_a($data, 'PEAR_Error')) {
55755            return false;
55756        }
55757
55758        if (is_null($code)) {
55759            return true;
55760        } elseif (is_string($code)) {
55761            return $data->getMessage() == $code;
55762        }
55763
55764        return $data->getCode() == $code;
55765    }
55766
55767    /**
55768     * Sets how errors generated by this object should be handled.
55769     * Can be invoked both in objects and statically.  If called
55770     * statically, setErrorHandling sets the default behaviour for all
55771     * PEAR objects.  If called in an object, setErrorHandling sets
55772     * the default behaviour for that object.
55773     *
55774     * @param int $mode
55775     *        One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
55776     *        PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
55777     *        PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
55778     *
55779     * @param mixed $options
55780     *        When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
55781     *        of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
55782     *
55783     *        When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
55784     *        to be the callback function or method.  A callback
55785     *        function is a string with the name of the function, a
55786     *        callback method is an array of two elements: the element
55787     *        at index 0 is the object, and the element at index 1 is
55788     *        the name of the method to call in the object.
55789     *
55790     *        When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
55791     *        a printf format string used when printing the error
55792     *        message.
55793     *
55794     * @access public
55795     * @return void
55796     * @see PEAR_ERROR_RETURN
55797     * @see PEAR_ERROR_PRINT
55798     * @see PEAR_ERROR_TRIGGER
55799     * @see PEAR_ERROR_DIE
55800     * @see PEAR_ERROR_CALLBACK
55801     * @see PEAR_ERROR_EXCEPTION
55802     *
55803     * @since PHP 4.0.5
55804     */
55805    function setErrorHandling($mode = null, $options = null)
55806    {
55807        if (isset($this) && is_a($this, 'PEAR')) {
55808            $setmode     = &$this->_default_error_mode;
55809            $setoptions  = &$this->_default_error_options;
55810        } else {
55811            $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
55812            $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
55813        }
55814
55815        switch ($mode) {
55816            case PEAR_ERROR_EXCEPTION:
55817            case PEAR_ERROR_RETURN:
55818            case PEAR_ERROR_PRINT:
55819            case PEAR_ERROR_TRIGGER:
55820            case PEAR_ERROR_DIE:
55821            case null:
55822                $setmode = $mode;
55823                $setoptions = $options;
55824                break;
55825
55826            case PEAR_ERROR_CALLBACK:
55827                $setmode = $mode;
55828                // class/object method callback
55829                if (is_callable($options)) {
55830                    $setoptions = $options;
55831                } else {
55832                    trigger_error("invalid error callback", E_USER_WARNING);
55833                }
55834                break;
55835
55836            default:
55837                trigger_error("invalid error mode", E_USER_WARNING);
55838                break;
55839        }
55840    }
55841
55842    /**
55843     * This method is used to tell which errors you expect to get.
55844     * Expected errors are always returned with error mode
55845     * PEAR_ERROR_RETURN.  Expected error codes are stored in a stack,
55846     * and this method pushes a new element onto it.  The list of
55847     * expected errors are in effect until they are popped off the
55848     * stack with the popExpect() method.
55849     *
55850     * Note that this method can not be called statically
55851     *
55852     * @param mixed $code a single error code or an array of error codes to expect
55853     *
55854     * @return int     the new depth of the "expected errors" stack
55855     * @access public
55856     */
55857    function expectError($code = '*')
55858    {
55859        if (is_array($code)) {
55860            array_push($this->_expected_errors, $code);
55861        } else {
55862            array_push($this->_expected_errors, array($code));
55863        }
55864        return count($this->_expected_errors);
55865    }
55866
55867    /**
55868     * This method pops one element off the expected error codes
55869     * stack.
55870     *
55871     * @return array   the list of error codes that were popped
55872     */
55873    function popExpect()
55874    {
55875        return array_pop($this->_expected_errors);
55876    }
55877
55878    /**
55879     * This method checks unsets an error code if available
55880     *
55881     * @param mixed error code
55882     * @return bool true if the error code was unset, false otherwise
55883     * @access private
55884     * @since PHP 4.3.0
55885     */
55886    function _checkDelExpect($error_code)
55887    {
55888        $deleted = false;
55889        foreach ($this->_expected_errors as $key => $error_array) {
55890            if (in_array($error_code, $error_array)) {
55891                unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
55892                $deleted = true;
55893            }
55894
55895            // clean up empty arrays
55896            if (0 == count($this->_expected_errors[$key])) {
55897                unset($this->_expected_errors[$key]);
55898            }
55899        }
55900
55901        return $deleted;
55902    }
55903
55904    /**
55905     * This method deletes all occurences of the specified element from
55906     * the expected error codes stack.
55907     *
55908     * @param  mixed $error_code error code that should be deleted
55909     * @return mixed list of error codes that were deleted or error
55910     * @access public
55911     * @since PHP 4.3.0
55912     */
55913    function delExpect($error_code)
55914    {
55915        $deleted = false;
55916        if ((is_array($error_code) && (0 != count($error_code)))) {
55917            // $error_code is a non-empty array here; we walk through it trying
55918            // to unset all values
55919            foreach ($error_code as $key => $error) {
55920                $deleted =  $this->_checkDelExpect($error) ? true : false;
55921            }
55922
55923            return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
55924        } elseif (!empty($error_code)) {
55925            // $error_code comes alone, trying to unset it
55926            if ($this->_checkDelExpect($error_code)) {
55927                return true;
55928            }
55929
55930            return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
55931        }
55932
55933        // $error_code is empty
55934        return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
55935    }
55936
55937    /**
55938     * This method is a wrapper that returns an instance of the
55939     * configured error class with this object's default error
55940     * handling applied.  If the $mode and $options parameters are not
55941     * specified, the object's defaults are used.
55942     *
55943     * @param mixed $message a text error message or a PEAR error object
55944     *
55945     * @param int $code      a numeric error code (it is up to your class
55946     *                  to define these if you want to use codes)
55947     *
55948     * @param int $mode      One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
55949     *                  PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
55950     *                  PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
55951     *
55952     * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
55953     *                  specifies the PHP-internal error level (one of
55954     *                  E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
55955     *                  If $mode is PEAR_ERROR_CALLBACK, this
55956     *                  parameter specifies the callback function or
55957     *                  method.  In other error modes this parameter
55958     *                  is ignored.
55959     *
55960     * @param string $userinfo If you need to pass along for example debug
55961     *                  information, this parameter is meant for that.
55962     *
55963     * @param string $error_class The returned error object will be
55964     *                  instantiated from this class, if specified.
55965     *
55966     * @param bool $skipmsg If true, raiseError will only pass error codes,
55967     *                  the error message parameter will be dropped.
55968     *
55969     * @access public
55970     * @return object   a PEAR error object
55971     * @see PEAR::setErrorHandling
55972     * @since PHP 4.0.5
55973     */
55974    function &raiseError($message = null,
55975                         $code = null,
55976                         $mode = null,
55977                         $options = null,
55978                         $userinfo = null,
55979                         $error_class = null,
55980                         $skipmsg = false)
55981    {
55982        // The error is yet a PEAR error object
55983        if (is_object($message)) {
55984            $code        = $message->getCode();
55985            $userinfo    = $message->getUserInfo();
55986            $error_class = $message->getType();
55987            $message->error_message_prefix = '';
55988            $message     = $message->getMessage();
55989        }
55990
55991        if (
55992            isset($this) &&
55993            isset($this->_expected_errors) &&
55994            count($this->_expected_errors) > 0 &&
55995            count($exp = end($this->_expected_errors))
55996        ) {
55997            if ($exp[0] == "*" ||
55998                (is_int(reset($exp)) && in_array($code, $exp)) ||
55999                (is_string(reset($exp)) && in_array($message, $exp))
56000            ) {
56001                $mode = PEAR_ERROR_RETURN;
56002            }
56003        }
56004
56005        // No mode given, try global ones
56006        if ($mode === null) {
56007            // Class error handler
56008            if (isset($this) && isset($this->_default_error_mode)) {
56009                $mode    = $this->_default_error_mode;
56010                $options = $this->_default_error_options;
56011            // Global error handler
56012            } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
56013                $mode    = $GLOBALS['_PEAR_default_error_mode'];
56014                $options = $GLOBALS['_PEAR_default_error_options'];
56015            }
56016        }
56017
56018        if ($error_class !== null) {
56019            $ec = $error_class;
56020        } elseif (isset($this) && isset($this->_error_class)) {
56021            $ec = $this->_error_class;
56022        } else {
56023            $ec = 'PEAR_Error';
56024        }
56025
56026        if (intval(PHP_VERSION) < 5) {
56027            // little non-eval hack to fix bug #12147
56028            include 'phar://install-pear-nozlib.phar/' . 'PEAR/FixPHP5PEARWarnings.php';
56029            return $a;
56030        }
56031
56032        if ($skipmsg) {
56033            $a = new $ec($code, $mode, $options, $userinfo);
56034        } else {
56035            $a = new $ec($message, $code, $mode, $options, $userinfo);
56036        }
56037
56038        return $a;
56039    }
56040
56041    /**
56042     * Simpler form of raiseError with fewer options.  In most cases
56043     * message, code and userinfo are enough.
56044     *
56045     * @param mixed $message a text error message or a PEAR error object
56046     *
56047     * @param int $code      a numeric error code (it is up to your class
56048     *                  to define these if you want to use codes)
56049     *
56050     * @param string $userinfo If you need to pass along for example debug
56051     *                  information, this parameter is meant for that.
56052     *
56053     * @access public
56054     * @return object   a PEAR error object
56055     * @see PEAR::raiseError
56056     */
56057    function &throwError($message = null, $code = null, $userinfo = null)
56058    {
56059        if (isset($this) && is_a($this, 'PEAR')) {
56060            $a = &$this->raiseError($message, $code, null, null, $userinfo);
56061            return $a;
56062        }
56063
56064        $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
56065        return $a;
56066    }
56067
56068    function staticPushErrorHandling($mode, $options = null)
56069    {
56070        $stack       = &$GLOBALS['_PEAR_error_handler_stack'];
56071        $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
56072        $def_options = &$GLOBALS['_PEAR_default_error_options'];
56073        $stack[] = array($def_mode, $def_options);
56074        switch ($mode) {
56075            case PEAR_ERROR_EXCEPTION:
56076            case PEAR_ERROR_RETURN:
56077            case PEAR_ERROR_PRINT:
56078            case PEAR_ERROR_TRIGGER:
56079            case PEAR_ERROR_DIE:
56080            case null:
56081                $def_mode = $mode;
56082                $def_options = $options;
56083                break;
56084
56085            case PEAR_ERROR_CALLBACK:
56086                $def_mode = $mode;
56087                // class/object method callback
56088                if (is_callable($options)) {
56089                    $def_options = $options;
56090                } else {
56091                    trigger_error("invalid error callback", E_USER_WARNING);
56092                }
56093                break;
56094
56095            default:
56096                trigger_error("invalid error mode", E_USER_WARNING);
56097                break;
56098        }
56099        $stack[] = array($mode, $options);
56100        return true;
56101    }
56102
56103    function staticPopErrorHandling()
56104    {
56105        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
56106        $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
56107        $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
56108        array_pop($stack);
56109        list($mode, $options) = $stack[sizeof($stack) - 1];
56110        array_pop($stack);
56111        switch ($mode) {
56112            case PEAR_ERROR_EXCEPTION:
56113            case PEAR_ERROR_RETURN:
56114            case PEAR_ERROR_PRINT:
56115            case PEAR_ERROR_TRIGGER:
56116            case PEAR_ERROR_DIE:
56117            case null:
56118                $setmode = $mode;
56119                $setoptions = $options;
56120                break;
56121
56122            case PEAR_ERROR_CALLBACK:
56123                $setmode = $mode;
56124                // class/object method callback
56125                if (is_callable($options)) {
56126                    $setoptions = $options;
56127                } else {
56128                    trigger_error("invalid error callback", E_USER_WARNING);
56129                }
56130                break;
56131
56132            default:
56133                trigger_error("invalid error mode", E_USER_WARNING);
56134                break;
56135        }
56136        return true;
56137    }
56138
56139    /**
56140     * Push a new error handler on top of the error handler options stack. With this
56141     * you can easily override the actual error handler for some code and restore
56142     * it later with popErrorHandling.
56143     *
56144     * @param mixed $mode (same as setErrorHandling)
56145     * @param mixed $options (same as setErrorHandling)
56146     *
56147     * @return bool Always true
56148     *
56149     * @see PEAR::setErrorHandling
56150     */
56151    function pushErrorHandling($mode, $options = null)
56152    {
56153        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
56154        if (isset($this) && is_a($this, 'PEAR')) {
56155            $def_mode    = &$this->_default_error_mode;
56156            $def_options = &$this->_default_error_options;
56157        } else {
56158            $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
56159            $def_options = &$GLOBALS['_PEAR_default_error_options'];
56160        }
56161        $stack[] = array($def_mode, $def_options);
56162
56163        if (isset($this) && is_a($this, 'PEAR')) {
56164            $this->setErrorHandling($mode, $options);
56165        } else {
56166            PEAR::setErrorHandling($mode, $options);
56167        }
56168        $stack[] = array($mode, $options);
56169        return true;
56170    }
56171
56172    /**
56173    * Pop the last error handler used
56174    *
56175    * @return bool Always true
56176    *
56177    * @see PEAR::pushErrorHandling
56178    */
56179    function popErrorHandling()
56180    {
56181        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
56182        array_pop($stack);
56183        list($mode, $options) = $stack[sizeof($stack) - 1];
56184        array_pop($stack);
56185        if (isset($this) && is_a($this, 'PEAR')) {
56186            $this->setErrorHandling($mode, $options);
56187        } else {
56188            PEAR::setErrorHandling($mode, $options);
56189        }
56190        return true;
56191    }
56192
56193    /**
56194    * OS independant PHP extension load. Remember to take care
56195    * on the correct extension name for case sensitive OSes.
56196    *
56197    * @param string $ext The extension name
56198    * @return bool Success or not on the dl() call
56199    */
56200    function loadExtension($ext)
56201    {
56202        if (extension_loaded($ext)) {
56203            return true;
56204        }
56205
56206        // if either returns true dl() will produce a FATAL error, stop that
56207        if (
56208            function_exists('dl') === false ||
56209            ini_get('enable_dl') != 1 ||
56210            ini_get('safe_mode') == 1
56211        ) {
56212            return false;
56213        }
56214
56215        if (OS_WINDOWS) {
56216            $suffix = '.dll';
56217        } elseif (PHP_OS == 'HP-UX') {
56218            $suffix = '.sl';
56219        } elseif (PHP_OS == 'AIX') {
56220            $suffix = '.a';
56221        } elseif (PHP_OS == 'OSX') {
56222            $suffix = '.bundle';
56223        } else {
56224            $suffix = '.so';
56225        }
56226
56227        return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
56228    }
56229}
56230
56231if (PEAR_ZE2) {
56232    include_once 'phar://install-pear-nozlib.phar/' . 'PEAR5.php';
56233}
56234
56235function _PEAR_call_destructors()
56236{
56237    global $_PEAR_destructor_object_list;
56238    if (is_array($_PEAR_destructor_object_list) &&
56239        sizeof($_PEAR_destructor_object_list))
56240    {
56241        reset($_PEAR_destructor_object_list);
56242        if (PEAR_ZE2) {
56243            $destructLifoExists = PEAR5::getStaticProperty('PEAR', 'destructlifo');
56244        } else {
56245            $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo');
56246        }
56247
56248        if ($destructLifoExists) {
56249            $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
56250        }
56251
56252        while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
56253            $classname = get_class($objref);
56254            while ($classname) {
56255                $destructor = "_$classname";
56256                if (method_exists($objref, $destructor)) {
56257                    $objref->$destructor();
56258                    break;
56259                } else {
56260                    $classname = get_parent_class($classname);
56261                }
56262            }
56263        }
56264        // Empty the object list to ensure that destructors are
56265        // not called more than once.
56266        $_PEAR_destructor_object_list = array();
56267    }
56268
56269    // Now call the shutdown functions
56270    if (
56271        isset($GLOBALS['_PEAR_shutdown_funcs']) &&
56272        is_array($GLOBALS['_PEAR_shutdown_funcs']) &&
56273        !empty($GLOBALS['_PEAR_shutdown_funcs'])
56274    ) {
56275        foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
56276            call_user_func_array($value[0], $value[1]);
56277        }
56278    }
56279}
56280
56281/**
56282 * Standard PEAR error class for PHP 4
56283 *
56284 * This class is supserseded by {@link PEAR_Exception} in PHP 5
56285 *
56286 * @category   pear
56287 * @package    PEAR
56288 * @author     Stig Bakken <ssb@php.net>
56289 * @author     Tomas V.V. Cox <cox@idecnet.com>
56290 * @author     Gregory Beaver <cellog@php.net>
56291 * @copyright  1997-2006 The PHP Group
56292 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
56293 * @version    Release: 1.9.4
56294 * @link       http://pear.php.net/manual/en/core.pear.pear-error.php
56295 * @see        PEAR::raiseError(), PEAR::throwError()
56296 * @since      Class available since PHP 4.0.2
56297 */
56298class PEAR_Error
56299{
56300    var $error_message_prefix = '';
56301    var $mode                 = PEAR_ERROR_RETURN;
56302    var $level                = E_USER_NOTICE;
56303    var $code                 = -1;
56304    var $message              = '';
56305    var $userinfo             = '';
56306    var $backtrace            = null;
56307
56308    /**
56309     * PEAR_Error constructor
56310     *
56311     * @param string $message  message
56312     *
56313     * @param int $code     (optional) error code
56314     *
56315     * @param int $mode     (optional) error mode, one of: PEAR_ERROR_RETURN,
56316     * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
56317     * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
56318     *
56319     * @param mixed $options   (optional) error level, _OR_ in the case of
56320     * PEAR_ERROR_CALLBACK, the callback function or object/method
56321     * tuple.
56322     *
56323     * @param string $userinfo (optional) additional user/debug info
56324     *
56325     * @access public
56326     *
56327     */
56328    function PEAR_Error($message = 'unknown error', $code = null,
56329                        $mode = null, $options = null, $userinfo = null)
56330    {
56331        if ($mode === null) {
56332            $mode = PEAR_ERROR_RETURN;
56333        }
56334        $this->message   = $message;
56335        $this->code      = $code;
56336        $this->mode      = $mode;
56337        $this->userinfo  = $userinfo;
56338
56339        if (PEAR_ZE2) {
56340            $skiptrace = PEAR5::getStaticProperty('PEAR_Error', 'skiptrace');
56341        } else {
56342            $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace');
56343        }
56344
56345        if (!$skiptrace) {
56346            $this->backtrace = debug_backtrace();
56347            if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
56348                unset($this->backtrace[0]['object']);
56349            }
56350        }
56351
56352        if ($mode & PEAR_ERROR_CALLBACK) {
56353            $this->level = E_USER_NOTICE;
56354            $this->callback = $options;
56355        } else {
56356            if ($options === null) {
56357                $options = E_USER_NOTICE;
56358            }
56359
56360            $this->level = $options;
56361            $this->callback = null;
56362        }
56363
56364        if ($this->mode & PEAR_ERROR_PRINT) {
56365            if (is_null($options) || is_int($options)) {
56366                $format = "%s";
56367            } else {
56368                $format = $options;
56369            }
56370
56371            printf($format, $this->getMessage());
56372        }
56373
56374        if ($this->mode & PEAR_ERROR_TRIGGER) {
56375            trigger_error($this->getMessage(), $this->level);
56376        }
56377
56378        if ($this->mode & PEAR_ERROR_DIE) {
56379            $msg = $this->getMessage();
56380            if (is_null($options) || is_int($options)) {
56381                $format = "%s";
56382                if (substr($msg, -1) != "\n") {
56383                    $msg .= "\n";
56384                }
56385            } else {
56386                $format = $options;
56387            }
56388            die(sprintf($format, $msg));
56389        }
56390
56391        if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) {
56392            call_user_func($this->callback, $this);
56393        }
56394
56395        if ($this->mode & PEAR_ERROR_EXCEPTION) {
56396            trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
56397            eval('$e = new Exception($this->message, $this->code);throw($e);');
56398        }
56399    }
56400
56401    /**
56402     * Get the error mode from an error object.
56403     *
56404     * @return int error mode
56405     * @access public
56406     */
56407    function getMode()
56408    {
56409        return $this->mode;
56410    }
56411
56412    /**
56413     * Get the callback function/method from an error object.
56414     *
56415     * @return mixed callback function or object/method array
56416     * @access public
56417     */
56418    function getCallback()
56419    {
56420        return $this->callback;
56421    }
56422
56423    /**
56424     * Get the error message from an error object.
56425     *
56426     * @return  string  full error message
56427     * @access public
56428     */
56429    function getMessage()
56430    {
56431        return ($this->error_message_prefix . $this->message);
56432    }
56433
56434    /**
56435     * Get error code from an error object
56436     *
56437     * @return int error code
56438     * @access public
56439     */
56440     function getCode()
56441     {
56442        return $this->code;
56443     }
56444
56445    /**
56446     * Get the name of this error/exception.
56447     *
56448     * @return string error/exception name (type)
56449     * @access public
56450     */
56451    function getType()
56452    {
56453        return get_class($this);
56454    }
56455
56456    /**
56457     * Get additional user-supplied information.
56458     *
56459     * @return string user-supplied information
56460     * @access public
56461     */
56462    function getUserInfo()
56463    {
56464        return $this->userinfo;
56465    }
56466
56467    /**
56468     * Get additional debug information supplied by the application.
56469     *
56470     * @return string debug information
56471     * @access public
56472     */
56473    function getDebugInfo()
56474    {
56475        return $this->getUserInfo();
56476    }
56477
56478    /**
56479     * Get the call backtrace from where the error was generated.
56480     * Supported with PHP 4.3.0 or newer.
56481     *
56482     * @param int $frame (optional) what frame to fetch
56483     * @return array Backtrace, or NULL if not available.
56484     * @access public
56485     */
56486    function getBacktrace($frame = null)
56487    {
56488        if (defined('PEAR_IGNORE_BACKTRACE')) {
56489            return null;
56490        }
56491        if ($frame === null) {
56492            return $this->backtrace;
56493        }
56494        return $this->backtrace[$frame];
56495    }
56496
56497    function addUserInfo($info)
56498    {
56499        if (empty($this->userinfo)) {
56500            $this->userinfo = $info;
56501        } else {
56502            $this->userinfo .= " ** $info";
56503        }
56504    }
56505
56506    function __toString()
56507    {
56508        return $this->getMessage();
56509    }
56510
56511    /**
56512     * Make a string representation of this object.
56513     *
56514     * @return string a string with an object summary
56515     * @access public
56516     */
56517    function toString()
56518    {
56519        $modes = array();
56520        $levels = array(E_USER_NOTICE  => 'notice',
56521                        E_USER_WARNING => 'warning',
56522                        E_USER_ERROR   => 'error');
56523        if ($this->mode & PEAR_ERROR_CALLBACK) {
56524            if (is_array($this->callback)) {
56525                $callback = (is_object($this->callback[0]) ?
56526                    strtolower(get_class($this->callback[0])) :
56527                    $this->callback[0]) . '::' .
56528                    $this->callback[1];
56529            } else {
56530                $callback = $this->callback;
56531            }
56532            return sprintf('[%s: message="%s" code=%d mode=callback '.
56533                           'callback=%s prefix="%s" info="%s"]',
56534                           strtolower(get_class($this)), $this->message, $this->code,
56535                           $callback, $this->error_message_prefix,
56536                           $this->userinfo);
56537        }
56538        if ($this->mode & PEAR_ERROR_PRINT) {
56539            $modes[] = 'print';
56540        }
56541        if ($this->mode & PEAR_ERROR_TRIGGER) {
56542            $modes[] = 'trigger';
56543        }
56544        if ($this->mode & PEAR_ERROR_DIE) {
56545            $modes[] = 'die';
56546        }
56547        if ($this->mode & PEAR_ERROR_RETURN) {
56548            $modes[] = 'return';
56549        }
56550        return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
56551                       'prefix="%s" info="%s"]',
56552                       strtolower(get_class($this)), $this->message, $this->code,
56553                       implode("|", $modes), $levels[$this->level],
56554                       $this->error_message_prefix,
56555                       $this->userinfo);
56556    }
56557}
56558
56559/*
56560 * Local Variables:
56561 * mode: php
56562 * tab-width: 4
56563 * c-basic-offset: 4
56564 * End:
56565 */
56566<?php
56567/**
56568 * Class auto-loader
56569 *
56570 * PHP versions 4
56571
56572 *
56573 * @category   pear
56574 * @package    PEAR
56575 * @author     Stig Bakken <ssb@php.net>
56576 * @copyright  1997-2009 The Authors
56577 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
56578 * @version    CVS: $Id: Autoloader.php 313023 2011-07-06 19:17:11Z dufuz $
56579 * @link       http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader
56580 * @since      File available since Release 0.1
56581 * @deprecated File deprecated in Release 1.4.0a1
56582 */
56583
56584// /* vim: set expandtab tabstop=4 shiftwidth=4: */
56585
56586if (!extension_loaded("overload")) {
56587    // die hard without ext/overload
56588    die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader");
56589}
56590
56591/**
56592 * Include for PEAR_Error and PEAR classes
56593 */
56594require_once 'phar://install-pear-nozlib.phar/' . "PEAR.php";
56595
56596/**
56597 * This class is for objects where you want to separate the code for
56598 * some methods into separate classes.  This is useful if you have a
56599 * class with not-frequently-used methods that contain lots of code
56600 * that you would like to avoid always parsing.
56601 *
56602 * The PEAR_Autoloader class provides autoloading and aggregation.
56603 * The autoloading lets you set up in which classes the separated
56604 * methods are found.  Aggregation is the technique used to import new
56605 * methods, an instance of each class providing separated methods is
56606 * stored and called every time the aggregated method is called.
56607 *
56608 * @category   pear
56609 * @package    PEAR
56610 * @author Stig Bakken <ssb@php.net>
56611 * @copyright  1997-2009 The Authors
56612 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
56613 * @version    Release: 1.9.4
56614 * @link       http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader
56615 * @since      File available since Release 0.1
56616 * @deprecated File deprecated in Release 1.4.0a1
56617 */
56618class PEAR_Autoloader extends PEAR
56619{
56620    // {{{ properties
56621
56622    /**
56623     * Map of methods and classes where they are defined
56624     *
56625     * @var array
56626     *
56627     * @access private
56628     */
56629    var $_autoload_map = array();
56630
56631    /**
56632     * Map of methods and aggregate objects
56633     *
56634     * @var array
56635     *
56636     * @access private
56637     */
56638    var $_method_map = array();
56639
56640    // }}}
56641    // {{{ addAutoload()
56642
56643    /**
56644     * Add one or more autoload entries.
56645     *
56646     * @param string $method     which method to autoload
56647     *
56648     * @param string $classname  (optional) which class to find the method in.
56649     *                           If the $method parameter is an array, this
56650     *                           parameter may be omitted (and will be ignored
56651     *                           if not), and the $method parameter will be
56652     *                           treated as an associative array with method
56653     *                           names as keys and class names as values.
56654     *
56655     * @return void
56656     *
56657     * @access public
56658     */
56659    function addAutoload($method, $classname = null)
56660    {
56661        if (is_array($method)) {
56662            array_walk($method, create_function('$a,&$b', '$b = strtolower($b);'));
56663            $this->_autoload_map = array_merge($this->_autoload_map, $method);
56664        } else {
56665            $this->_autoload_map[strtolower($method)] = $classname;
56666        }
56667    }
56668
56669    // }}}
56670    // {{{ removeAutoload()
56671
56672    /**
56673     * Remove an autoload entry.
56674     *
56675     * @param string $method  which method to remove the autoload entry for
56676     *
56677     * @return bool TRUE if an entry was removed, FALSE if not
56678     *
56679     * @access public
56680     */
56681    function removeAutoload($method)
56682    {
56683        $method = strtolower($method);
56684        $ok = isset($this->_autoload_map[$method]);
56685        unset($this->_autoload_map[$method]);
56686        return $ok;
56687    }
56688
56689    // }}}
56690    // {{{ addAggregateObject()
56691
56692    /**
56693     * Add an aggregate object to this object.  If the specified class
56694     * is not defined, loading it will be attempted following PEAR's
56695     * file naming scheme.  All the methods in the class will be
56696     * aggregated, except private ones (name starting with an
56697     * underscore) and constructors.
56698     *
56699     * @param string $classname  what class to instantiate for the object.
56700     *
56701     * @return void
56702     *
56703     * @access public
56704     */
56705    function addAggregateObject($classname)
56706    {
56707        $classname = strtolower($classname);
56708        if (!class_exists($classname)) {
56709            $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname);
56710            include_once 'phar://install-pear-nozlib.phar/' . $include_file;
56711        }
56712        $obj =& new $classname;
56713        $methods = get_class_methods($classname);
56714        foreach ($methods as $method) {
56715            // don't import priviate methods and constructors
56716            if ($method{0} != '_' && $method != $classname) {
56717                $this->_method_map[$method] = $obj;
56718            }
56719        }
56720    }
56721
56722    // }}}
56723    // {{{ removeAggregateObject()
56724
56725    /**
56726     * Remove an aggregate object.
56727     *
56728     * @param string $classname  the class of the object to remove
56729     *
56730     * @return bool  TRUE if an object was removed, FALSE if not
56731     *
56732     * @access public
56733     */
56734    function removeAggregateObject($classname)
56735    {
56736        $ok = false;
56737        $classname = strtolower($classname);
56738        reset($this->_method_map);
56739        while (list($method, $obj) = each($this->_method_map)) {
56740            if (is_a($obj, $classname)) {
56741                unset($this->_method_map[$method]);
56742                $ok = true;
56743            }
56744        }
56745        return $ok;
56746    }
56747
56748    // }}}
56749    // {{{ __call()
56750
56751    /**
56752     * Overloaded object call handler, called each time an
56753     * undefined/aggregated method is invoked.  This method repeats
56754     * the call in the right aggregate object and passes on the return
56755     * value.
56756     *
56757     * @param string $method  which method that was called
56758     *
56759     * @param string $args    An array of the parameters passed in the
56760     *                        original call
56761     *
56762     * @return mixed  The return value from the aggregated method, or a PEAR
56763     *                error if the called method was unknown.
56764     */
56765    function __call($method, $args, &$retval)
56766    {
56767        $method = strtolower($method);
56768        if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) {
56769            $this->addAggregateObject($this->_autoload_map[$method]);
56770        }
56771        if (isset($this->_method_map[$method])) {
56772            $retval = call_user_func_array(array($this->_method_map[$method], $method), $args);
56773            return true;
56774        }
56775        return false;
56776    }
56777
56778    // }}}
56779}
56780
56781overload("PEAR_Autoloader");
56782
56783?>
56784<?php
56785/**
56786 * PEAR_Builder for building PHP extensions (PECL packages)
56787 *
56788 * PHP versions 4 and 5
56789 *
56790 * @category   pear
56791 * @package    PEAR
56792 * @author     Stig Bakken <ssb@php.net>
56793 * @author     Greg Beaver <cellog@php.net>
56794 * @copyright  1997-2009 The Authors
56795 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
56796 * @version    CVS: $Id: Builder.php 313024 2011-07-06 19:51:24Z dufuz $
56797 * @link       http://pear.php.net/package/PEAR
56798 * @since      File available since Release 0.1
56799 *
56800 * TODO: log output parameters in PECL command line
56801 * TODO: msdev path in configuration
56802 */
56803
56804/**
56805 * Needed for extending PEAR_Builder
56806 */
56807require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
56808require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
56809
56810/**
56811 * Class to handle building (compiling) extensions.
56812 *
56813 * @category   pear
56814 * @package    PEAR
56815 * @author     Stig Bakken <ssb@php.net>
56816 * @author     Greg Beaver <cellog@php.net>
56817 * @copyright  1997-2009 The Authors
56818 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
56819 * @version    Release: 1.9.4
56820 * @link       http://pear.php.net/package/PEAR
56821 * @since      Class available since PHP 4.0.2
56822 * @see        http://pear.php.net/manual/en/core.ppm.pear-builder.php
56823 */
56824class PEAR_Builder extends PEAR_Common
56825{
56826    var $php_api_version = 0;
56827    var $zend_module_api_no = 0;
56828    var $zend_extension_api_no = 0;
56829
56830    var $extensions_built = array();
56831
56832    /**
56833     * @var string Used for reporting when it is not possible to pass function
56834     *             via extra parameter, e.g. log, msdevCallback
56835     */
56836    var $current_callback = null;
56837
56838    // used for msdev builds
56839    var $_lastline = null;
56840    var $_firstline = null;
56841
56842    /**
56843     * PEAR_Builder constructor.
56844     *
56845     * @param object $ui user interface object (instance of PEAR_Frontend_*)
56846     *
56847     * @access public
56848     */
56849    function PEAR_Builder(&$ui)
56850    {
56851        parent::PEAR_Common();
56852        $this->setFrontendObject($ui);
56853    }
56854
56855    /**
56856     * Build an extension from source on windows.
56857     * requires msdev
56858     */
56859    function _build_win32($descfile, $callback = null)
56860    {
56861        if (is_object($descfile)) {
56862            $pkg = $descfile;
56863            $descfile = $pkg->getPackageFile();
56864        } else {
56865            $pf = &new PEAR_PackageFile($this->config, $this->debug);
56866            $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
56867            if (PEAR::isError($pkg)) {
56868                return $pkg;
56869            }
56870        }
56871        $dir = dirname($descfile);
56872        $old_cwd = getcwd();
56873
56874        if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
56875            return $this->raiseError("could not chdir to $dir");
56876        }
56877
56878        // packages that were in a .tar have the packagefile in this directory
56879        $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
56880        if (file_exists($dir) && is_dir($vdir)) {
56881            if (!chdir($vdir)) {
56882                return $this->raiseError("could not chdir to " . realpath($vdir));
56883            }
56884
56885            $dir = getcwd();
56886        }
56887
56888        $this->log(2, "building in $dir");
56889
56890        $dsp = $pkg->getPackage().'.dsp';
56891        if (!file_exists("$dir/$dsp")) {
56892            return $this->raiseError("The DSP $dsp does not exist.");
56893        }
56894        // XXX TODO: make release build type configurable
56895        $command = 'msdev '.$dsp.' /MAKE "'.$pkg->getPackage(). ' - Release"';
56896
56897        $err = $this->_runCommand($command, array(&$this, 'msdevCallback'));
56898        if (PEAR::isError($err)) {
56899            return $err;
56900        }
56901
56902        // figure out the build platform and type
56903        $platform = 'Win32';
56904        $buildtype = 'Release';
56905        if (preg_match('/.*?'.$pkg->getPackage().'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) {
56906            $platform = $matches[1];
56907            $buildtype = $matches[2];
56908        }
56909
56910        if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/', $this->_lastline, $matches)) {
56911            if ($matches[2]) {
56912                // there were errors in the build
56913                return $this->raiseError("There were errors during compilation.");
56914            }
56915            $out = $matches[1];
56916        } else {
56917            return $this->raiseError("Did not understand the completion status returned from msdev.exe.");
56918        }
56919
56920        // msdev doesn't tell us the output directory :/
56921        // open the dsp, find /out and use that directory
56922        $dsptext = join(file($dsp),'');
56923
56924        // this regex depends on the build platform and type having been
56925        // correctly identified above.
56926        $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'.
56927                    $pkg->getPackage().'\s-\s'.
56928                    $platform.'\s'.
56929                    $buildtype.'").*?'.
56930                    '\/out:"(.*?)"/is';
56931
56932        if ($dsptext && preg_match($regex, $dsptext, $matches)) {
56933            // what we get back is a relative path to the output file itself.
56934            $outfile = realpath($matches[2]);
56935        } else {
56936            return $this->raiseError("Could not retrieve output information from $dsp.");
56937        }
56938        // realpath returns false if the file doesn't exist
56939        if ($outfile && copy($outfile, "$dir/$out")) {
56940            $outfile = "$dir/$out";
56941        }
56942
56943        $built_files[] = array(
56944            'file' => "$outfile",
56945            'php_api' => $this->php_api_version,
56946            'zend_mod_api' => $this->zend_module_api_no,
56947            'zend_ext_api' => $this->zend_extension_api_no,
56948            );
56949
56950        return $built_files;
56951    }
56952    // }}}
56953
56954    // {{{ msdevCallback()
56955    function msdevCallback($what, $data)
56956    {
56957        if (!$this->_firstline)
56958            $this->_firstline = $data;
56959        $this->_lastline = $data;
56960        call_user_func($this->current_callback, $what, $data);
56961    }
56962
56963    /**
56964     * @param string
56965     * @param string
56966     * @param array
56967     * @access private
56968     */
56969    function _harvestInstDir($dest_prefix, $dirname, &$built_files)
56970    {
56971        $d = opendir($dirname);
56972        if (!$d)
56973            return false;
56974
56975        $ret = true;
56976        while (($ent = readdir($d)) !== false) {
56977            if ($ent{0} == '.')
56978                continue;
56979
56980            $full = $dirname . DIRECTORY_SEPARATOR . $ent;
56981            if (is_dir($full)) {
56982                if (!$this->_harvestInstDir(
56983                        $dest_prefix . DIRECTORY_SEPARATOR . $ent,
56984                        $full, $built_files)) {
56985                    $ret = false;
56986                    break;
56987                }
56988            } else {
56989                $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent;
56990                $built_files[] = array(
56991                        'file' => $full,
56992                        'dest' => $dest,
56993                        'php_api' => $this->php_api_version,
56994                        'zend_mod_api' => $this->zend_module_api_no,
56995                        'zend_ext_api' => $this->zend_extension_api_no,
56996                        );
56997            }
56998        }
56999        closedir($d);
57000        return $ret;
57001    }
57002
57003    /**
57004     * Build an extension from source.  Runs "phpize" in the source
57005     * directory, but compiles in a temporary directory
57006     * (TMPDIR/pear-build-USER/PACKAGE-VERSION).
57007     *
57008     * @param string|PEAR_PackageFile_v* $descfile path to XML package description file, or
57009     *               a PEAR_PackageFile object
57010     *
57011     * @param mixed $callback callback function used to report output,
57012     * see PEAR_Builder::_runCommand for details
57013     *
57014     * @return array an array of associative arrays with built files,
57015     * format:
57016     * array( array( 'file' => '/path/to/ext.so',
57017     *               'php_api' => YYYYMMDD,
57018     *               'zend_mod_api' => YYYYMMDD,
57019     *               'zend_ext_api' => YYYYMMDD ),
57020     *        ... )
57021     *
57022     * @access public
57023     *
57024     * @see PEAR_Builder::_runCommand
57025     */
57026    function build($descfile, $callback = null)
57027    {
57028        if (preg_match('/(\\/|\\\\|^)([^\\/\\\\]+)?php(.+)?$/',
57029                       $this->config->get('php_bin'), $matches)) {
57030            if (isset($matches[2]) && strlen($matches[2]) &&
57031                trim($matches[2]) != trim($this->config->get('php_prefix'))) {
57032                $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') .
57033                           ' appears to have a prefix ' . $matches[2] . ', but' .
57034                           ' config variable php_prefix does not match');
57035            }
57036
57037            if (isset($matches[3]) && strlen($matches[3]) &&
57038                trim($matches[3]) != trim($this->config->get('php_suffix'))) {
57039                $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') .
57040                           ' appears to have a suffix ' . $matches[3] . ', but' .
57041                           ' config variable php_suffix does not match');
57042            }
57043        }
57044
57045        $this->current_callback = $callback;
57046        if (PEAR_OS == "Windows") {
57047            return $this->_build_win32($descfile, $callback);
57048        }
57049
57050        if (PEAR_OS != 'Unix') {
57051            return $this->raiseError("building extensions not supported on this platform");
57052        }
57053
57054        if (is_object($descfile)) {
57055            $pkg = $descfile;
57056            $descfile = $pkg->getPackageFile();
57057            if (is_a($pkg, 'PEAR_PackageFile_v1')) {
57058                $dir = dirname($descfile);
57059            } else {
57060                $dir = $pkg->_config->get('temp_dir') . '/' . $pkg->getName();
57061                // automatically delete at session end
57062                $this->addTempFile($dir);
57063            }
57064        } else {
57065            $pf = &new PEAR_PackageFile($this->config);
57066            $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
57067            if (PEAR::isError($pkg)) {
57068                return $pkg;
57069            }
57070            $dir = dirname($descfile);
57071        }
57072
57073        // Find config. outside of normal path - e.g. config.m4
57074        foreach (array_keys($pkg->getInstallationFileList()) as $item) {
57075          if (stristr(basename($item), 'config.m4') && dirname($item) != '.') {
57076            $dir .= DIRECTORY_SEPARATOR . dirname($item);
57077            break;
57078          }
57079        }
57080
57081        $old_cwd = getcwd();
57082        if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
57083            return $this->raiseError("could not chdir to $dir");
57084        }
57085
57086        $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
57087        if (is_dir($vdir)) {
57088            chdir($vdir);
57089        }
57090
57091        $dir = getcwd();
57092        $this->log(2, "building in $dir");
57093        putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH'));
57094        $err = $this->_runCommand($this->config->get('php_prefix')
57095                                . "phpize" .
57096                                $this->config->get('php_suffix'),
57097                                array(&$this, 'phpizeCallback'));
57098        if (PEAR::isError($err)) {
57099            return $err;
57100        }
57101
57102        if (!$err) {
57103            return $this->raiseError("`phpize' failed");
57104        }
57105
57106        // {{{ start of interactive part
57107        $configure_command = "$dir/configure";
57108        $configure_options = $pkg->getConfigureOptions();
57109        if ($configure_options) {
57110            foreach ($configure_options as $o) {
57111                $default = array_key_exists('default', $o) ? $o['default'] : null;
57112                list($r) = $this->ui->userDialog('build',
57113                                                 array($o['prompt']),
57114                                                 array('text'),
57115                                                 array($default));
57116                if (substr($o['name'], 0, 5) == 'with-' &&
57117                    ($r == 'yes' || $r == 'autodetect')) {
57118                    $configure_command .= " --$o[name]";
57119                } else {
57120                    $configure_command .= " --$o[name]=".trim($r);
57121                }
57122            }
57123        }
57124        // }}} end of interactive part
57125
57126        // FIXME make configurable
57127        if (!$user=getenv('USER')) {
57128            $user='defaultuser';
57129        }
57130
57131        $tmpdir = $this->config->get('temp_dir');
57132        $build_basedir = System::mktemp(' -t "' . $tmpdir . '" -d "pear-build-' . $user . '"');
57133        $build_dir = "$build_basedir/$vdir";
57134        $inst_dir = "$build_basedir/install-$vdir";
57135        $this->log(1, "building in $build_dir");
57136        if (is_dir($build_dir)) {
57137            System::rm(array('-rf', $build_dir));
57138        }
57139
57140        if (!System::mkDir(array('-p', $build_dir))) {
57141            return $this->raiseError("could not create build dir: $build_dir");
57142        }
57143
57144        $this->addTempFile($build_dir);
57145        if (!System::mkDir(array('-p', $inst_dir))) {
57146            return $this->raiseError("could not create temporary install dir: $inst_dir");
57147        }
57148        $this->addTempFile($inst_dir);
57149
57150        $make_command = getenv('MAKE') ? getenv('MAKE') : 'make';
57151
57152        $to_run = array(
57153            $configure_command,
57154            $make_command,
57155            "$make_command INSTALL_ROOT=\"$inst_dir\" install",
57156            "find \"$inst_dir\" | xargs ls -dils"
57157            );
57158        if (!file_exists($build_dir) || !is_dir($build_dir) || !chdir($build_dir)) {
57159            return $this->raiseError("could not chdir to $build_dir");
57160        }
57161        putenv('PHP_PEAR_VERSION=1.9.4');
57162        foreach ($to_run as $cmd) {
57163            $err = $this->_runCommand($cmd, $callback);
57164            if (PEAR::isError($err)) {
57165                chdir($old_cwd);
57166                return $err;
57167            }
57168            if (!$err) {
57169                chdir($old_cwd);
57170                return $this->raiseError("`$cmd' failed");
57171            }
57172        }
57173        if (!($dp = opendir("modules"))) {
57174            chdir($old_cwd);
57175            return $this->raiseError("no `modules' directory found");
57176        }
57177        $built_files = array();
57178        $prefix = exec($this->config->get('php_prefix')
57179                        . "php-config" .
57180                       $this->config->get('php_suffix') . " --prefix");
57181        $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files);
57182        chdir($old_cwd);
57183        return $built_files;
57184    }
57185
57186    /**
57187     * Message callback function used when running the "phpize"
57188     * program.  Extracts the API numbers used.  Ignores other message
57189     * types than "cmdoutput".
57190     *
57191     * @param string $what the type of message
57192     * @param mixed $data the message
57193     *
57194     * @return void
57195     *
57196     * @access public
57197     */
57198    function phpizeCallback($what, $data)
57199    {
57200        if ($what != 'cmdoutput') {
57201            return;
57202        }
57203        $this->log(1, rtrim($data));
57204        if (preg_match('/You should update your .aclocal.m4/', $data)) {
57205            return;
57206        }
57207        $matches = array();
57208        if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) {
57209            $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1]));
57210            $apino = (int)$matches[2];
57211            if (isset($this->$member)) {
57212                $this->$member = $apino;
57213                //$msg = sprintf("%-22s : %d", $matches[1], $apino);
57214                //$this->log(1, $msg);
57215            }
57216        }
57217    }
57218
57219    /**
57220     * Run an external command, using a message callback to report
57221     * output.  The command will be run through popen and output is
57222     * reported for every line with a "cmdoutput" message with the
57223     * line string, including newlines, as payload.
57224     *
57225     * @param string $command the command to run
57226     *
57227     * @param mixed $callback (optional) function to use as message
57228     * callback
57229     *
57230     * @return bool whether the command was successful (exit code 0
57231     * means success, any other means failure)
57232     *
57233     * @access private
57234     */
57235    function _runCommand($command, $callback = null)
57236    {
57237        $this->log(1, "running: $command");
57238        $pp = popen("$command 2>&1", "r");
57239        if (!$pp) {
57240            return $this->raiseError("failed to run `$command'");
57241        }
57242        if ($callback && $callback[0]->debug == 1) {
57243            $olddbg = $callback[0]->debug;
57244            $callback[0]->debug = 2;
57245        }
57246
57247        while ($line = fgets($pp, 1024)) {
57248            if ($callback) {
57249                call_user_func($callback, 'cmdoutput', $line);
57250            } else {
57251                $this->log(2, rtrim($line));
57252            }
57253        }
57254        if ($callback && isset($olddbg)) {
57255            $callback[0]->debug = $olddbg;
57256        }
57257
57258        $exitcode = is_resource($pp) ? pclose($pp) : -1;
57259        return ($exitcode == 0);
57260    }
57261
57262    function log($level, $msg)
57263    {
57264        if ($this->current_callback) {
57265            if ($this->debug >= $level) {
57266                call_user_func($this->current_callback, 'output', $msg);
57267            }
57268            return;
57269        }
57270        return PEAR_Common::log($level, $msg);
57271    }
57272}<?php
57273/**
57274 * PEAR_ChannelFile, the channel handling class
57275 *
57276 * PHP versions 4 and 5
57277 *
57278 * @category   pear
57279 * @package    PEAR
57280 * @author     Greg Beaver <cellog@php.net>
57281 * @copyright  1997-2009 The Authors
57282 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
57283 * @version    CVS: $Id: ChannelFile.php 313023 2011-07-06 19:17:11Z dufuz $
57284 * @link       http://pear.php.net/package/PEAR
57285 * @since      File available since Release 1.4.0a1
57286 */
57287
57288/**
57289 * Needed for error handling
57290 */
57291require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ErrorStack.php';
57292require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';
57293require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
57294
57295/**
57296 * Error code if the channel.xml <channel> tag does not contain a valid version
57297 */
57298define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1);
57299/**
57300 * Error code if the channel.xml <channel> tag version is not supported (version 1.0 is the only supported version,
57301 * currently
57302 */
57303define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2);
57304
57305/**
57306 * Error code if parsing is attempted with no xml extension
57307 */
57308define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3);
57309
57310/**
57311 * Error code if creating the xml parser resource fails
57312 */
57313define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4);
57314
57315/**
57316 * Error code used for all sax xml parsing errors
57317 */
57318define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5);
57319
57320/**#@+
57321 * Validation errors
57322 */
57323/**
57324 * Error code when channel name is missing
57325 */
57326define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6);
57327/**
57328 * Error code when channel name is invalid
57329 */
57330define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7);
57331/**
57332 * Error code when channel summary is missing
57333 */
57334define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8);
57335/**
57336 * Error code when channel summary is multi-line
57337 */
57338define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9);
57339/**
57340 * Error code when channel server is missing for protocol
57341 */
57342define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10);
57343/**
57344 * Error code when channel server is invalid for protocol
57345 */
57346define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11);
57347/**
57348 * Error code when a mirror name is invalid
57349 */
57350define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21);
57351/**
57352 * Error code when a mirror type is invalid
57353 */
57354define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22);
57355/**
57356 * Error code when an attempt is made to generate xml, but the parsed content is invalid
57357 */
57358define('PEAR_CHANNELFILE_ERROR_INVALID', 23);
57359/**
57360 * Error code when an empty package name validate regex is passed in
57361 */
57362define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24);
57363/**
57364 * Error code when a <function> tag has no version
57365 */
57366define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25);
57367/**
57368 * Error code when a <function> tag has no name
57369 */
57370define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26);
57371/**
57372 * Error code when a <validatepackage> tag has no name
57373 */
57374define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27);
57375/**
57376 * Error code when a <validatepackage> tag has no version attribute
57377 */
57378define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28);
57379/**
57380 * Error code when a mirror does not exist but is called for in one of the set*
57381 * methods.
57382 */
57383define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32);
57384/**
57385 * Error code when a server port is not numeric
57386 */
57387define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33);
57388/**
57389 * Error code when <static> contains no version attribute
57390 */
57391define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34);
57392/**
57393 * Error code when <baseurl> contains no type attribute in a <rest> protocol definition
57394 */
57395define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35);
57396/**
57397 * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel
57398 */
57399define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36);
57400/**
57401 * Error code when ssl attribute is present and is not "yes"
57402 */
57403define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37);
57404/**#@-*/
57405
57406/**
57407 * Mirror types allowed.  Currently only internet servers are recognized.
57408 */
57409$GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] =  array('server');
57410
57411
57412/**
57413 * The Channel handling class
57414 *
57415 * @category   pear
57416 * @package    PEAR
57417 * @author     Greg Beaver <cellog@php.net>
57418 * @copyright  1997-2009 The Authors
57419 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
57420 * @version    Release: 1.9.4
57421 * @link       http://pear.php.net/package/PEAR
57422 * @since      Class available since Release 1.4.0a1
57423 */
57424class PEAR_ChannelFile
57425{
57426    /**
57427     * @access private
57428     * @var PEAR_ErrorStack
57429     * @access private
57430     */
57431    var $_stack;
57432
57433    /**
57434     * Supported channel.xml versions, for parsing
57435     * @var array
57436     * @access private
57437     */
57438    var $_supportedVersions = array('1.0');
57439
57440    /**
57441     * Parsed channel information
57442     * @var array
57443     * @access private
57444     */
57445    var $_channelInfo;
57446
57447    /**
57448     * index into the subchannels array, used for parsing xml
57449     * @var int
57450     * @access private
57451     */
57452    var $_subchannelIndex;
57453
57454    /**
57455     * index into the mirrors array, used for parsing xml
57456     * @var int
57457     * @access private
57458     */
57459    var $_mirrorIndex;
57460
57461    /**
57462     * Flag used to determine the validity of parsed content
57463     * @var boolean
57464     * @access private
57465     */
57466    var $_isValid = false;
57467
57468    function PEAR_ChannelFile()
57469    {
57470        $this->_stack = &new PEAR_ErrorStack('PEAR_ChannelFile');
57471        $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
57472        $this->_isValid = false;
57473    }
57474
57475    /**
57476     * @return array
57477     * @access protected
57478     */
57479    function _getErrorMessage()
57480    {
57481        return
57482            array(
57483                PEAR_CHANNELFILE_ERROR_INVALID_VERSION =>
57484                    'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%',
57485                PEAR_CHANNELFILE_ERROR_NO_VERSION =>
57486                    'No version number found in <channel> tag',
57487                PEAR_CHANNELFILE_ERROR_NO_XML_EXT =>
57488                    '%error%',
57489                PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER =>
57490                    'Unable to create XML parser',
57491                PEAR_CHANNELFILE_ERROR_PARSER_ERROR =>
57492                    '%error%',
57493                PEAR_CHANNELFILE_ERROR_NO_NAME =>
57494                    'Missing channel name',
57495                PEAR_CHANNELFILE_ERROR_INVALID_NAME =>
57496                    'Invalid channel %tag% "%name%"',
57497                PEAR_CHANNELFILE_ERROR_NO_SUMMARY =>
57498                    'Missing channel summary',
57499                PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY =>
57500                    'Channel summary should be on one line, but is multi-line',
57501                PEAR_CHANNELFILE_ERROR_NO_HOST =>
57502                    'Missing channel server for %type% server',
57503                PEAR_CHANNELFILE_ERROR_INVALID_HOST =>
57504                    'Server name "%server%" is invalid for %type% server',
57505                PEAR_CHANNELFILE_ERROR_INVALID_MIRROR =>
57506                    'Invalid mirror name "%name%", mirror type %type%',
57507                PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE =>
57508                    'Invalid mirror type "%type%"',
57509                PEAR_CHANNELFILE_ERROR_INVALID =>
57510                    'Cannot generate xml, contents are invalid',
57511                PEAR_CHANNELFILE_ERROR_EMPTY_REGEX =>
57512                    'packagenameregex cannot be empty',
57513                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION =>
57514                    '%parent% %protocol% function has no version',
57515                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME =>
57516                    '%parent% %protocol% function has no name',
57517                PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE =>
57518                    '%parent% rest baseurl has no type',
57519                PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME =>
57520                    'Validation package has no name in <validatepackage> tag',
57521                PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION =>
57522                    'Validation package "%package%" has no version',
57523                PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND =>
57524                    'Mirror "%mirror%" does not exist',
57525                PEAR_CHANNELFILE_ERROR_INVALID_PORT =>
57526                    'Port "%port%" must be numeric',
57527                PEAR_CHANNELFILE_ERROR_NO_STATICVERSION =>
57528                    '<static> tag must contain version attribute',
57529                PEAR_CHANNELFILE_URI_CANT_MIRROR =>
57530                    'The __uri pseudo-channel cannot have mirrors',
57531                PEAR_CHANNELFILE_ERROR_INVALID_SSL =>
57532                    '%server% has invalid ssl attribute "%ssl%" can only be yes or not present',
57533            );
57534    }
57535
57536    /**
57537     * @param string contents of package.xml file
57538     * @return bool success of parsing
57539     */
57540    function fromXmlString($data)
57541    {
57542        if (preg_match('/<channel\s+version="([0-9]+\.[0-9]+)"/', $data, $channelversion)) {
57543            if (!in_array($channelversion[1], $this->_supportedVersions)) {
57544                $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error',
57545                    array('version' => $channelversion[1]));
57546                return false;
57547            }
57548            $parser = new PEAR_XMLParser;
57549            $result = $parser->parse($data);
57550            if ($result !== true) {
57551                if ($result->getCode() == 1) {
57552                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error',
57553                        array('error' => $result->getMessage()));
57554                } else {
57555                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error');
57556                }
57557                return false;
57558            }
57559            $this->_channelInfo = $parser->getData();
57560            return true;
57561        } else {
57562            $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data));
57563            return false;
57564        }
57565    }
57566
57567    /**
57568     * @return array
57569     */
57570    function toArray()
57571    {
57572        if (!$this->_isValid && !$this->validate()) {
57573            return false;
57574        }
57575        return $this->_channelInfo;
57576    }
57577
57578    /**
57579     * @param array
57580     * @static
57581     * @return PEAR_ChannelFile|false false if invalid
57582     */
57583    function &fromArray($data, $compatibility = false, $stackClass = 'PEAR_ErrorStack')
57584    {
57585        $a = new PEAR_ChannelFile($compatibility, $stackClass);
57586        $a->_fromArray($data);
57587        if (!$a->validate()) {
57588            $a = false;
57589            return $a;
57590        }
57591        return $a;
57592    }
57593
57594    /**
57595     * Unlike {@link fromArray()} this does not do any validation
57596     * @param array
57597     * @static
57598     * @return PEAR_ChannelFile
57599     */
57600    function &fromArrayWithErrors($data, $compatibility = false,
57601                                  $stackClass = 'PEAR_ErrorStack')
57602    {
57603        $a = new PEAR_ChannelFile($compatibility, $stackClass);
57604        $a->_fromArray($data);
57605        return $a;
57606    }
57607
57608    /**
57609     * @param array
57610     * @access private
57611     */
57612    function _fromArray($data)
57613    {
57614        $this->_channelInfo = $data;
57615    }
57616
57617    /**
57618     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
57619     * @param boolean determines whether to purge the error stack after retrieving
57620     * @return array
57621     */
57622    function getErrors($purge = false)
57623    {
57624        return $this->_stack->getErrors($purge);
57625    }
57626
57627    /**
57628     * Unindent given string (?)
57629     *
57630     * @param string $str The string that has to be unindented.
57631     * @return string
57632     * @access private
57633     */
57634    function _unIndent($str)
57635    {
57636        // remove leading newlines
57637        $str = preg_replace('/^[\r\n]+/', '', $str);
57638        // find whitespace at the beginning of the first line
57639        $indent_len = strspn($str, " \t");
57640        $indent = substr($str, 0, $indent_len);
57641        $data = '';
57642        // remove the same amount of whitespace from following lines
57643        foreach (explode("\n", $str) as $line) {
57644            if (substr($line, 0, $indent_len) == $indent) {
57645                $data .= substr($line, $indent_len) . "\n";
57646            }
57647        }
57648        return $data;
57649    }
57650
57651    /**
57652     * Parse a channel.xml file.  Expects the name of
57653     * a channel xml file as input.
57654     *
57655     * @param string  $descfile  name of channel xml file
57656     * @return bool success of parsing
57657     */
57658    function fromXmlFile($descfile)
57659    {
57660        if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) ||
57661             (!$fp = fopen($descfile, 'r'))) {
57662            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
57663            return PEAR::raiseError("Unable to open $descfile");
57664        }
57665
57666        // read the whole thing so we only get one cdata callback
57667        // for each block of cdata
57668        fclose($fp);
57669        $data = file_get_contents($descfile);
57670        return $this->fromXmlString($data);
57671    }
57672
57673    /**
57674     * Parse channel information from different sources
57675     *
57676     * This method is able to extract information about a channel
57677     * from an .xml file or a string
57678     *
57679     * @access public
57680     * @param  string Filename of the source or the source itself
57681     * @return bool
57682     */
57683    function fromAny($info)
57684    {
57685        if (is_string($info) && file_exists($info) && strlen($info) < 255) {
57686            $tmp = substr($info, -4);
57687            if ($tmp == '.xml') {
57688                $info = $this->fromXmlFile($info);
57689            } else {
57690                $fp = fopen($info, "r");
57691                $test = fread($fp, 5);
57692                fclose($fp);
57693                if ($test == "<?xml") {
57694                    $info = $this->fromXmlFile($info);
57695                }
57696            }
57697            if (PEAR::isError($info)) {
57698                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
57699                return PEAR::raiseError($info);
57700            }
57701        }
57702        if (is_string($info)) {
57703            $info = $this->fromXmlString($info);
57704        }
57705        return $info;
57706    }
57707
57708    /**
57709     * Return an XML document based on previous parsing and modifications
57710     *
57711     * @return string XML data
57712     *
57713     * @access public
57714     */
57715    function toXml()
57716    {
57717        if (!$this->_isValid && !$this->validate()) {
57718            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID);
57719            return false;
57720        }
57721        if (!isset($this->_channelInfo['attribs']['version'])) {
57722            $this->_channelInfo['attribs']['version'] = '1.0';
57723        }
57724        $channelInfo = $this->_channelInfo;
57725        $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
57726        $ret .= "<channel version=\"" .
57727            $channelInfo['attribs']['version'] . "\" xmlns=\"http://pear.php.net/channel-1.0\"
57728  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
57729  xsi:schemaLocation=\"http://pear.php.net/dtd/channel-"
57730            . $channelInfo['attribs']['version'] . " http://pear.php.net/dtd/channel-" .
57731            $channelInfo['attribs']['version'] . ".xsd\">
57732 <name>$channelInfo[name]</name>
57733 <summary>" . htmlspecialchars($channelInfo['summary'])."</summary>
57734";
57735        if (isset($channelInfo['suggestedalias'])) {
57736            $ret .= ' <suggestedalias>' . $channelInfo['suggestedalias'] . "</suggestedalias>\n";
57737        }
57738        if (isset($channelInfo['validatepackage'])) {
57739            $ret .= ' <validatepackage version="' .
57740                $channelInfo['validatepackage']['attribs']['version']. '">' .
57741                htmlspecialchars($channelInfo['validatepackage']['_content']) .
57742                "</validatepackage>\n";
57743        }
57744        $ret .= " <servers>\n";
57745        $ret .= '  <primary';
57746        if (isset($channelInfo['servers']['primary']['attribs']['ssl'])) {
57747            $ret .= ' ssl="' . $channelInfo['servers']['primary']['attribs']['ssl'] . '"';
57748        }
57749        if (isset($channelInfo['servers']['primary']['attribs']['port'])) {
57750            $ret .= ' port="' . $channelInfo['servers']['primary']['attribs']['port'] . '"';
57751        }
57752        $ret .= ">\n";
57753        if (isset($channelInfo['servers']['primary']['rest'])) {
57754            $ret .= $this->_makeRestXml($channelInfo['servers']['primary']['rest'], '   ');
57755        }
57756        $ret .= "  </primary>\n";
57757        if (isset($channelInfo['servers']['mirror'])) {
57758            $ret .= $this->_makeMirrorsXml($channelInfo);
57759        }
57760        $ret .= " </servers>\n";
57761        $ret .= "</channel>";
57762        return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret));
57763    }
57764
57765    /**
57766     * Generate the <rest> tag
57767     * @access private
57768     */
57769    function _makeRestXml($info, $indent)
57770    {
57771        $ret = $indent . "<rest>\n";
57772        if (isset($info['baseurl']) && !isset($info['baseurl'][0])) {
57773            $info['baseurl'] = array($info['baseurl']);
57774        }
57775
57776        if (isset($info['baseurl'])) {
57777            foreach ($info['baseurl'] as $url) {
57778                $ret .= "$indent <baseurl type=\"" . $url['attribs']['type'] . "\"";
57779                $ret .= ">" . $url['_content'] . "</baseurl>\n";
57780            }
57781        }
57782        $ret .= $indent . "</rest>\n";
57783        return $ret;
57784    }
57785
57786    /**
57787     * Generate the <mirrors> tag
57788     * @access private
57789     */
57790    function _makeMirrorsXml($channelInfo)
57791    {
57792        $ret = "";
57793        if (!isset($channelInfo['servers']['mirror'][0])) {
57794            $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']);
57795        }
57796        foreach ($channelInfo['servers']['mirror'] as $mirror) {
57797            $ret .= '  <mirror host="' . $mirror['attribs']['host'] . '"';
57798            if (isset($mirror['attribs']['port'])) {
57799                $ret .= ' port="' . $mirror['attribs']['port'] . '"';
57800            }
57801            if (isset($mirror['attribs']['ssl'])) {
57802                $ret .= ' ssl="' . $mirror['attribs']['ssl'] . '"';
57803            }
57804            $ret .= ">\n";
57805            if (isset($mirror['rest'])) {
57806                if (isset($mirror['rest'])) {
57807                    $ret .= $this->_makeRestXml($mirror['rest'], '   ');
57808                }
57809                $ret .= "  </mirror>\n";
57810            } else {
57811                $ret .= "/>\n";
57812            }
57813        }
57814        return $ret;
57815    }
57816
57817    /**
57818     * Generate the <functions> tag
57819     * @access private
57820     */
57821    function _makeFunctionsXml($functions, $indent, $rest = false)
57822    {
57823        $ret = '';
57824        if (!isset($functions[0])) {
57825            $functions = array($functions);
57826        }
57827        foreach ($functions as $function) {
57828            $ret .= "$indent<function version=\"" . $function['attribs']['version'] . "\"";
57829            if ($rest) {
57830                $ret .= ' uri="' . $function['attribs']['uri'] . '"';
57831            }
57832            $ret .= ">" . $function['_content'] . "</function>\n";
57833        }
57834        return $ret;
57835    }
57836
57837    /**
57838     * Validation error.  Also marks the object contents as invalid
57839     * @param error code
57840     * @param array error information
57841     * @access private
57842     */
57843    function _validateError($code, $params = array())
57844    {
57845        $this->_stack->push($code, 'error', $params);
57846        $this->_isValid = false;
57847    }
57848
57849    /**
57850     * Validation warning.  Does not mark the object contents invalid.
57851     * @param error code
57852     * @param array error information
57853     * @access private
57854     */
57855    function _validateWarning($code, $params = array())
57856    {
57857        $this->_stack->push($code, 'warning', $params);
57858    }
57859
57860    /**
57861     * Validate parsed file.
57862     *
57863     * @access public
57864     * @return boolean
57865     */
57866    function validate()
57867    {
57868        $this->_isValid = true;
57869        $info = $this->_channelInfo;
57870        if (empty($info['name'])) {
57871            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME);
57872        } elseif (!$this->validChannelServer($info['name'])) {
57873            if ($info['name'] != '__uri') {
57874                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name',
57875                    'name' => $info['name']));
57876            }
57877        }
57878        if (empty($info['summary'])) {
57879            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
57880        } elseif (strpos(trim($info['summary']), "\n") !== false) {
57881            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
57882                array('summary' => $info['summary']));
57883        }
57884        if (isset($info['suggestedalias'])) {
57885            if (!$this->validChannelServer($info['suggestedalias'])) {
57886                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
57887                    array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias']));
57888            }
57889        }
57890        if (isset($info['localalias'])) {
57891            if (!$this->validChannelServer($info['localalias'])) {
57892                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
57893                    array('tag' => 'localalias', 'name' =>$info['localalias']));
57894            }
57895        }
57896        if (isset($info['validatepackage'])) {
57897            if (!isset($info['validatepackage']['_content'])) {
57898                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME);
57899            }
57900            if (!isset($info['validatepackage']['attribs']['version'])) {
57901                $content = isset($info['validatepackage']['_content']) ?
57902                    $info['validatepackage']['_content'] :
57903                    null;
57904                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION,
57905                    array('package' => $content));
57906            }
57907        }
57908
57909        if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['port']) &&
57910              !is_numeric($info['servers']['primary']['attribs']['port'])) {
57911            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT,
57912                array('port' => $info['servers']['primary']['attribs']['port']));
57913        }
57914
57915        if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['ssl']) &&
57916              $info['servers']['primary']['attribs']['ssl'] != 'yes') {
57917            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
57918                array('ssl' => $info['servers']['primary']['attribs']['ssl'],
57919                    'server' => $info['name']));
57920        }
57921
57922        if (isset($info['servers']['primary']['rest']) &&
57923              isset($info['servers']['primary']['rest']['baseurl'])) {
57924            $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']);
57925        }
57926        if (isset($info['servers']['mirror'])) {
57927            if ($this->_channelInfo['name'] == '__uri') {
57928                $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR);
57929            }
57930            if (!isset($info['servers']['mirror'][0])) {
57931                $info['servers']['mirror'] = array($info['servers']['mirror']);
57932            }
57933            foreach ($info['servers']['mirror'] as $mirror) {
57934                if (!isset($mirror['attribs']['host'])) {
57935                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST,
57936                      array('type' => 'mirror'));
57937                } elseif (!$this->validChannelServer($mirror['attribs']['host'])) {
57938                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST,
57939                        array('server' => $mirror['attribs']['host'], 'type' => 'mirror'));
57940                }
57941                if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') {
57942                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
57943                        array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host']));
57944                }
57945                if (isset($mirror['rest'])) {
57946                    $this->_validateFunctions('rest', $mirror['rest']['baseurl'],
57947                        $mirror['attribs']['host']);
57948                }
57949            }
57950        }
57951        return $this->_isValid;
57952    }
57953
57954    /**
57955     * @param string  rest - protocol name this function applies to
57956     * @param array the functions
57957     * @param string the name of the parent element (mirror name, for instance)
57958     */
57959    function _validateFunctions($protocol, $functions, $parent = '')
57960    {
57961        if (!isset($functions[0])) {
57962            $functions = array($functions);
57963        }
57964
57965        foreach ($functions as $function) {
57966            if (!isset($function['_content']) || empty($function['_content'])) {
57967                $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME,
57968                    array('parent' => $parent, 'protocol' => $protocol));
57969            }
57970
57971            if ($protocol == 'rest') {
57972                if (!isset($function['attribs']['type']) ||
57973                      empty($function['attribs']['type'])) {
57974                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE,
57975                        array('parent' => $parent, 'protocol' => $protocol));
57976                }
57977            } else {
57978                if (!isset($function['attribs']['version']) ||
57979                      empty($function['attribs']['version'])) {
57980                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION,
57981                        array('parent' => $parent, 'protocol' => $protocol));
57982                }
57983            }
57984        }
57985    }
57986
57987    /**
57988     * Test whether a string contains a valid channel server.
57989     * @param string $ver the package version to test
57990     * @return bool
57991     */
57992    function validChannelServer($server)
57993    {
57994        if ($server == '__uri') {
57995            return true;
57996        }
57997        return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server);
57998    }
57999
58000    /**
58001     * @return string|false
58002     */
58003    function getName()
58004    {
58005        if (isset($this->_channelInfo['name'])) {
58006            return $this->_channelInfo['name'];
58007        }
58008
58009        return false;
58010    }
58011
58012    /**
58013     * @return string|false
58014     */
58015    function getServer()
58016    {
58017        if (isset($this->_channelInfo['name'])) {
58018            return $this->_channelInfo['name'];
58019        }
58020
58021        return false;
58022    }
58023
58024    /**
58025     * @return int|80 port number to connect to
58026     */
58027    function getPort($mirror = false)
58028    {
58029        if ($mirror) {
58030            if ($mir = $this->getMirror($mirror)) {
58031                if (isset($mir['attribs']['port'])) {
58032                    return $mir['attribs']['port'];
58033                }
58034
58035                if ($this->getSSL($mirror)) {
58036                    return 443;
58037                }
58038
58039                return 80;
58040            }
58041
58042            return false;
58043        }
58044
58045        if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) {
58046            return $this->_channelInfo['servers']['primary']['attribs']['port'];
58047        }
58048
58049        if ($this->getSSL()) {
58050            return 443;
58051        }
58052
58053        return 80;
58054    }
58055
58056    /**
58057     * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel
58058     */
58059    function getSSL($mirror = false)
58060    {
58061        if ($mirror) {
58062            if ($mir = $this->getMirror($mirror)) {
58063                if (isset($mir['attribs']['ssl'])) {
58064                    return true;
58065                }
58066
58067                return false;
58068            }
58069
58070            return false;
58071        }
58072
58073        if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
58074            return true;
58075        }
58076
58077        return false;
58078    }
58079
58080    /**
58081     * @return string|false
58082     */
58083    function getSummary()
58084    {
58085        if (isset($this->_channelInfo['summary'])) {
58086            return $this->_channelInfo['summary'];
58087        }
58088
58089        return false;
58090    }
58091
58092    /**
58093     * @param string protocol type
58094     * @param string Mirror name
58095     * @return array|false
58096     */
58097    function getFunctions($protocol, $mirror = false)
58098    {
58099        if ($this->getName() == '__uri') {
58100            return false;
58101        }
58102
58103        $function = $protocol == 'rest' ? 'baseurl' : 'function';
58104        if ($mirror) {
58105            if ($mir = $this->getMirror($mirror)) {
58106                if (isset($mir[$protocol][$function])) {
58107                    return $mir[$protocol][$function];
58108                }
58109            }
58110
58111            return false;
58112        }
58113
58114        if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) {
58115            return $this->_channelInfo['servers']['primary'][$protocol][$function];
58116        }
58117
58118        return false;
58119    }
58120
58121    /**
58122     * @param string Protocol type
58123     * @param string Function name (null to return the
58124     *               first protocol of the type requested)
58125     * @param string Mirror name, if any
58126     * @return array
58127     */
58128     function getFunction($type, $name = null, $mirror = false)
58129     {
58130        $protocols = $this->getFunctions($type, $mirror);
58131        if (!$protocols) {
58132            return false;
58133        }
58134
58135        foreach ($protocols as $protocol) {
58136            if ($name === null) {
58137                return $protocol;
58138            }
58139
58140            if ($protocol['_content'] != $name) {
58141                continue;
58142            }
58143
58144            return $protocol;
58145        }
58146
58147        return false;
58148     }
58149
58150    /**
58151     * @param string protocol type
58152     * @param string protocol name
58153     * @param string version
58154     * @param string mirror name
58155     * @return boolean
58156     */
58157    function supports($type, $name = null, $mirror = false, $version = '1.0')
58158    {
58159        $protocols = $this->getFunctions($type, $mirror);
58160        if (!$protocols) {
58161            return false;
58162        }
58163
58164        foreach ($protocols as $protocol) {
58165            if ($protocol['attribs']['version'] != $version) {
58166                continue;
58167            }
58168
58169            if ($name === null) {
58170                return true;
58171            }
58172
58173            if ($protocol['_content'] != $name) {
58174                continue;
58175            }
58176
58177            return true;
58178        }
58179
58180        return false;
58181    }
58182
58183    /**
58184     * Determines whether a channel supports Representational State Transfer (REST) protocols
58185     * for retrieving channel information
58186     * @param string
58187     * @return bool
58188     */
58189    function supportsREST($mirror = false)
58190    {
58191        if ($mirror == $this->_channelInfo['name']) {
58192            $mirror = false;
58193        }
58194
58195        if ($mirror) {
58196            if ($mir = $this->getMirror($mirror)) {
58197                return isset($mir['rest']);
58198            }
58199
58200            return false;
58201        }
58202
58203        return isset($this->_channelInfo['servers']['primary']['rest']);
58204    }
58205
58206    /**
58207     * Get the URL to access a base resource.
58208     *
58209     * Hyperlinks in the returned xml will be used to retrieve the proper information
58210     * needed.  This allows extreme extensibility and flexibility in implementation
58211     * @param string Resource Type to retrieve
58212     */
58213    function getBaseURL($resourceType, $mirror = false)
58214    {
58215        if ($mirror == $this->_channelInfo['name']) {
58216            $mirror = false;
58217        }
58218
58219        if ($mirror) {
58220            $mir = $this->getMirror($mirror);
58221            if (!$mir) {
58222                return false;
58223            }
58224
58225            $rest = $mir['rest'];
58226        } else {
58227            $rest = $this->_channelInfo['servers']['primary']['rest'];
58228        }
58229
58230        if (!isset($rest['baseurl'][0])) {
58231            $rest['baseurl'] = array($rest['baseurl']);
58232        }
58233
58234        foreach ($rest['baseurl'] as $baseurl) {
58235            if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) {
58236                return $baseurl['_content'];
58237            }
58238        }
58239
58240        return false;
58241    }
58242
58243    /**
58244     * Since REST does not implement RPC, provide this as a logical wrapper around
58245     * resetFunctions for REST
58246     * @param string|false mirror name, if any
58247     */
58248    function resetREST($mirror = false)
58249    {
58250        return $this->resetFunctions('rest', $mirror);
58251    }
58252
58253    /**
58254     * Empty all protocol definitions
58255     * @param string protocol type
58256     * @param string|false mirror name, if any
58257     */
58258    function resetFunctions($type, $mirror = false)
58259    {
58260        if ($mirror) {
58261            if (isset($this->_channelInfo['servers']['mirror'])) {
58262                $mirrors = $this->_channelInfo['servers']['mirror'];
58263                if (!isset($mirrors[0])) {
58264                    $mirrors = array($mirrors);
58265                }
58266
58267                foreach ($mirrors as $i => $mir) {
58268                    if ($mir['attribs']['host'] == $mirror) {
58269                        if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) {
58270                            unset($this->_channelInfo['servers']['mirror'][$i][$type]);
58271                        }
58272
58273                        return true;
58274                    }
58275                }
58276
58277                return false;
58278            }
58279
58280            return false;
58281        }
58282
58283        if (isset($this->_channelInfo['servers']['primary'][$type])) {
58284            unset($this->_channelInfo['servers']['primary'][$type]);
58285        }
58286
58287        return true;
58288    }
58289
58290    /**
58291     * Set a channel's protocols to the protocols supported by pearweb
58292     */
58293    function setDefaultPEARProtocols($version = '1.0', $mirror = false)
58294    {
58295        switch ($version) {
58296            case '1.0' :
58297                $this->resetREST($mirror);
58298
58299                if (!isset($this->_channelInfo['servers'])) {
58300                    $this->_channelInfo['servers'] = array('primary' =>
58301                        array('rest' => array()));
58302                } elseif (!isset($this->_channelInfo['servers']['primary'])) {
58303                    $this->_channelInfo['servers']['primary'] = array('rest' => array());
58304                }
58305
58306                return true;
58307            break;
58308            default :
58309                return false;
58310            break;
58311        }
58312    }
58313
58314    /**
58315     * @return array
58316     */
58317    function getMirrors()
58318    {
58319        if (isset($this->_channelInfo['servers']['mirror'])) {
58320            $mirrors = $this->_channelInfo['servers']['mirror'];
58321            if (!isset($mirrors[0])) {
58322                $mirrors = array($mirrors);
58323            }
58324
58325            return $mirrors;
58326        }
58327
58328        return array();
58329    }
58330
58331    /**
58332     * Get the unserialized XML representing a mirror
58333     * @return array|false
58334     */
58335    function getMirror($server)
58336    {
58337        foreach ($this->getMirrors() as $mirror) {
58338            if ($mirror['attribs']['host'] == $server) {
58339                return $mirror;
58340            }
58341        }
58342
58343        return false;
58344    }
58345
58346    /**
58347     * @param string
58348     * @return string|false
58349     * @error PEAR_CHANNELFILE_ERROR_NO_NAME
58350     * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME
58351     */
58352    function setName($name)
58353    {
58354        return $this->setServer($name);
58355    }
58356
58357    /**
58358     * Set the socket number (port) that is used to connect to this channel
58359     * @param integer
58360     * @param string|false name of the mirror server, or false for the primary
58361     */
58362    function setPort($port, $mirror = false)
58363    {
58364        if ($mirror) {
58365            if (!isset($this->_channelInfo['servers']['mirror'])) {
58366                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
58367                    array('mirror' => $mirror));
58368                return false;
58369            }
58370
58371            if (isset($this->_channelInfo['servers']['mirror'][0])) {
58372                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
58373                    if ($mirror == $mir['attribs']['host']) {
58374                        $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port;
58375                        return true;
58376                    }
58377                }
58378
58379                return false;
58380            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
58381                $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port;
58382                $this->_isValid = false;
58383                return true;
58384            }
58385        }
58386
58387        $this->_channelInfo['servers']['primary']['attribs']['port'] = $port;
58388        $this->_isValid = false;
58389        return true;
58390    }
58391
58392    /**
58393     * Set the socket number (port) that is used to connect to this channel
58394     * @param bool Determines whether to turn on SSL support or turn it off
58395     * @param string|false name of the mirror server, or false for the primary
58396     */
58397    function setSSL($ssl = true, $mirror = false)
58398    {
58399        if ($mirror) {
58400            if (!isset($this->_channelInfo['servers']['mirror'])) {
58401                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
58402                    array('mirror' => $mirror));
58403                return false;
58404            }
58405
58406            if (isset($this->_channelInfo['servers']['mirror'][0])) {
58407                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
58408                    if ($mirror == $mir['attribs']['host']) {
58409                        if (!$ssl) {
58410                            if (isset($this->_channelInfo['servers']['mirror'][$i]
58411                                  ['attribs']['ssl'])) {
58412                                unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']);
58413                            }
58414                        } else {
58415                            $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes';
58416                        }
58417
58418                        return true;
58419                    }
58420                }
58421
58422                return false;
58423            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
58424                if (!$ssl) {
58425                    if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) {
58426                        unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']);
58427                    }
58428                } else {
58429                    $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes';
58430                }
58431
58432                $this->_isValid = false;
58433                return true;
58434            }
58435        }
58436
58437        if ($ssl) {
58438            $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes';
58439        } else {
58440            if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
58441                unset($this->_channelInfo['servers']['primary']['attribs']['ssl']);
58442            }
58443        }
58444
58445        $this->_isValid = false;
58446        return true;
58447    }
58448
58449    /**
58450     * @param string
58451     * @return string|false
58452     * @error PEAR_CHANNELFILE_ERROR_NO_SERVER
58453     * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER
58454     */
58455    function setServer($server, $mirror = false)
58456    {
58457        if (empty($server)) {
58458            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER);
58459            return false;
58460        } elseif (!$this->validChannelServer($server)) {
58461            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
58462                array('tag' => 'name', 'name' => $server));
58463            return false;
58464        }
58465
58466        if ($mirror) {
58467            $found = false;
58468            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
58469                if ($mirror == $mir['attribs']['host']) {
58470                    $found = true;
58471                    break;
58472                }
58473            }
58474
58475            if (!$found) {
58476                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
58477                    array('mirror' => $mirror));
58478                return false;
58479            }
58480
58481            $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server;
58482            return true;
58483        }
58484
58485        $this->_channelInfo['name'] = $server;
58486        return true;
58487    }
58488
58489    /**
58490     * @param string
58491     * @return boolean success
58492     * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY
58493     * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY
58494     */
58495    function setSummary($summary)
58496    {
58497        if (empty($summary)) {
58498            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
58499            return false;
58500        } elseif (strpos(trim($summary), "\n") !== false) {
58501            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
58502                array('summary' => $summary));
58503        }
58504
58505        $this->_channelInfo['summary'] = $summary;
58506        return true;
58507    }
58508
58509    /**
58510     * @param string
58511     * @param boolean determines whether the alias is in channel.xml or local
58512     * @return boolean success
58513     */
58514    function setAlias($alias, $local = false)
58515    {
58516        if (!$this->validChannelServer($alias)) {
58517            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
58518                array('tag' => 'suggestedalias', 'name' => $alias));
58519            return false;
58520        }
58521
58522        if ($local) {
58523            $this->_channelInfo['localalias'] = $alias;
58524        } else {
58525            $this->_channelInfo['suggestedalias'] = $alias;
58526        }
58527
58528        return true;
58529    }
58530
58531    /**
58532     * @return string
58533     */
58534    function getAlias()
58535    {
58536        if (isset($this->_channelInfo['localalias'])) {
58537            return $this->_channelInfo['localalias'];
58538        }
58539        if (isset($this->_channelInfo['suggestedalias'])) {
58540            return $this->_channelInfo['suggestedalias'];
58541        }
58542        if (isset($this->_channelInfo['name'])) {
58543            return $this->_channelInfo['name'];
58544        }
58545        return '';
58546    }
58547
58548    /**
58549     * Set the package validation object if it differs from PEAR's default
58550     * The class must be includeable via changing _ in the classname to path separator,
58551     * but no checking of this is made.
58552     * @param string|false pass in false to reset to the default packagename regex
58553     * @return boolean success
58554     */
58555    function setValidationPackage($validateclass, $version)
58556    {
58557        if (empty($validateclass)) {
58558            unset($this->_channelInfo['validatepackage']);
58559        }
58560        $this->_channelInfo['validatepackage'] = array('_content' => $validateclass);
58561        $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version);
58562    }
58563
58564    /**
58565     * Add a protocol to the provides section
58566     * @param string protocol type
58567     * @param string protocol version
58568     * @param string protocol name, if any
58569     * @param string mirror name, if this is a mirror's protocol
58570     * @return bool
58571     */
58572    function addFunction($type, $version, $name = '', $mirror = false)
58573    {
58574        if ($mirror) {
58575            return $this->addMirrorFunction($mirror, $type, $version, $name);
58576        }
58577
58578        $set = array('attribs' => array('version' => $version), '_content' => $name);
58579        if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) {
58580            if (!isset($this->_channelInfo['servers'])) {
58581                $this->_channelInfo['servers'] = array('primary' =>
58582                    array($type => array()));
58583            } elseif (!isset($this->_channelInfo['servers']['primary'])) {
58584                $this->_channelInfo['servers']['primary'] = array($type => array());
58585            }
58586
58587            $this->_channelInfo['servers']['primary'][$type]['function'] = $set;
58588            $this->_isValid = false;
58589            return true;
58590        } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) {
58591            $this->_channelInfo['servers']['primary'][$type]['function'] = array(
58592                $this->_channelInfo['servers']['primary'][$type]['function']);
58593        }
58594
58595        $this->_channelInfo['servers']['primary'][$type]['function'][] = $set;
58596        return true;
58597    }
58598    /**
58599     * Add a protocol to a mirror's provides section
58600     * @param string mirror name (server)
58601     * @param string protocol type
58602     * @param string protocol version
58603     * @param string protocol name, if any
58604     */
58605    function addMirrorFunction($mirror, $type, $version, $name = '')
58606    {
58607        if (!isset($this->_channelInfo['servers']['mirror'])) {
58608            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
58609                array('mirror' => $mirror));
58610            return false;
58611        }
58612
58613        $setmirror = false;
58614        if (isset($this->_channelInfo['servers']['mirror'][0])) {
58615            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
58616                if ($mirror == $mir['attribs']['host']) {
58617                    $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
58618                    break;
58619                }
58620            }
58621        } else {
58622            if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
58623                $setmirror = &$this->_channelInfo['servers']['mirror'];
58624            }
58625        }
58626
58627        if (!$setmirror) {
58628            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
58629                array('mirror' => $mirror));
58630            return false;
58631        }
58632
58633        $set = array('attribs' => array('version' => $version), '_content' => $name);
58634        if (!isset($setmirror[$type]['function'])) {
58635            $setmirror[$type]['function'] = $set;
58636            $this->_isValid = false;
58637            return true;
58638        } elseif (!isset($setmirror[$type]['function'][0])) {
58639            $setmirror[$type]['function'] = array($setmirror[$type]['function']);
58640        }
58641
58642        $setmirror[$type]['function'][] = $set;
58643        $this->_isValid = false;
58644        return true;
58645    }
58646
58647    /**
58648     * @param string Resource Type this url links to
58649     * @param string URL
58650     * @param string|false mirror name, if this is not a primary server REST base URL
58651     */
58652    function setBaseURL($resourceType, $url, $mirror = false)
58653    {
58654        if ($mirror) {
58655            if (!isset($this->_channelInfo['servers']['mirror'])) {
58656                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
58657                    array('mirror' => $mirror));
58658                return false;
58659            }
58660
58661            $setmirror = false;
58662            if (isset($this->_channelInfo['servers']['mirror'][0])) {
58663                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
58664                    if ($mirror == $mir['attribs']['host']) {
58665                        $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
58666                        break;
58667                    }
58668                }
58669            } else {
58670                if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
58671                    $setmirror = &$this->_channelInfo['servers']['mirror'];
58672                }
58673            }
58674        } else {
58675            $setmirror = &$this->_channelInfo['servers']['primary'];
58676        }
58677
58678        $set = array('attribs' => array('type' => $resourceType), '_content' => $url);
58679        if (!isset($setmirror['rest'])) {
58680            $setmirror['rest'] = array();
58681        }
58682
58683        if (!isset($setmirror['rest']['baseurl'])) {
58684            $setmirror['rest']['baseurl'] = $set;
58685            $this->_isValid = false;
58686            return true;
58687        } elseif (!isset($setmirror['rest']['baseurl'][0])) {
58688            $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']);
58689        }
58690
58691        foreach ($setmirror['rest']['baseurl'] as $i => $url) {
58692            if ($url['attribs']['type'] == $resourceType) {
58693                $this->_isValid = false;
58694                $setmirror['rest']['baseurl'][$i] = $set;
58695                return true;
58696            }
58697        }
58698
58699        $setmirror['rest']['baseurl'][] = $set;
58700        $this->_isValid = false;
58701        return true;
58702    }
58703
58704    /**
58705     * @param string mirror server
58706     * @param int mirror http port
58707     * @return boolean
58708     */
58709    function addMirror($server, $port = null)
58710    {
58711        if ($this->_channelInfo['name'] == '__uri') {
58712            return false; // the __uri channel cannot have mirrors by definition
58713        }
58714
58715        $set = array('attribs' => array('host' => $server));
58716        if (is_numeric($port)) {
58717            $set['attribs']['port'] = $port;
58718        }
58719
58720        if (!isset($this->_channelInfo['servers']['mirror'])) {
58721            $this->_channelInfo['servers']['mirror'] = $set;
58722            return true;
58723        }
58724
58725        if (!isset($this->_channelInfo['servers']['mirror'][0])) {
58726            $this->_channelInfo['servers']['mirror'] =
58727                array($this->_channelInfo['servers']['mirror']);
58728        }
58729
58730        $this->_channelInfo['servers']['mirror'][] = $set;
58731        return true;
58732    }
58733
58734    /**
58735     * Retrieve the name of the validation package for this channel
58736     * @return string|false
58737     */
58738    function getValidationPackage()
58739    {
58740        if (!$this->_isValid && !$this->validate()) {
58741            return false;
58742        }
58743
58744        if (!isset($this->_channelInfo['validatepackage'])) {
58745            return array('attribs' => array('version' => 'default'),
58746                '_content' => 'PEAR_Validate');
58747        }
58748
58749        return $this->_channelInfo['validatepackage'];
58750    }
58751
58752    /**
58753     * Retrieve the object that can be used for custom validation
58754     * @param string|false the name of the package to validate.  If the package is
58755     *                     the channel validation package, PEAR_Validate is returned
58756     * @return PEAR_Validate|false false is returned if the validation package
58757     *         cannot be located
58758     */
58759    function &getValidationObject($package = false)
58760    {
58761        if (!class_exists('PEAR_Validate')) {
58762            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validate.php';
58763        }
58764
58765        if (!$this->_isValid) {
58766            if (!$this->validate()) {
58767                $a = false;
58768                return $a;
58769            }
58770        }
58771
58772        if (isset($this->_channelInfo['validatepackage'])) {
58773            if ($package == $this->_channelInfo['validatepackage']) {
58774                // channel validation packages are always validated by PEAR_Validate
58775                $val = &new PEAR_Validate;
58776                return $val;
58777            }
58778
58779            if (!class_exists(str_replace('.', '_',
58780                  $this->_channelInfo['validatepackage']['_content']))) {
58781                if ($this->isIncludeable(str_replace('_', '/',
58782                      $this->_channelInfo['validatepackage']['_content']) . '.php')) {
58783                    include_once 'phar://install-pear-nozlib.phar/' . str_replace('_', '/',
58784                        $this->_channelInfo['validatepackage']['_content']) . '.php';
58785                    $vclass = str_replace('.', '_',
58786                        $this->_channelInfo['validatepackage']['_content']);
58787                    $val = &new $vclass;
58788                } else {
58789                    $a = false;
58790                    return $a;
58791                }
58792            } else {
58793                $vclass = str_replace('.', '_',
58794                    $this->_channelInfo['validatepackage']['_content']);
58795                $val = &new $vclass;
58796            }
58797        } else {
58798            $val = &new PEAR_Validate;
58799        }
58800
58801        return $val;
58802    }
58803
58804    function isIncludeable($path)
58805    {
58806        $possibilities = explode(PATH_SEPARATOR, ini_get('include_path'));
58807        foreach ($possibilities as $dir) {
58808            if (file_exists($dir . DIRECTORY_SEPARATOR . $path)
58809                  && is_readable($dir . DIRECTORY_SEPARATOR . $path)) {
58810                return true;
58811            }
58812        }
58813
58814        return false;
58815    }
58816
58817    /**
58818     * This function is used by the channel updater and retrieves a value set by
58819     * the registry, or the current time if it has not been set
58820     * @return string
58821     */
58822    function lastModified()
58823    {
58824        if (isset($this->_channelInfo['_lastmodified'])) {
58825            return $this->_channelInfo['_lastmodified'];
58826        }
58827
58828        return time();
58829    }
58830}<?php
58831/**
58832 * PEAR_ChannelFile_Parser for parsing channel.xml
58833 *
58834 * PHP versions 4 and 5
58835 *
58836 * @category   pear
58837 * @package    PEAR
58838 * @author     Greg Beaver <cellog@php.net>
58839 * @copyright  1997-2009 The Authors
58840 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
58841 * @version    CVS: $Id: Parser.php 313023 2011-07-06 19:17:11Z dufuz $
58842 * @link       http://pear.php.net/package/PEAR
58843 * @since      File available since Release 1.4.0a1
58844 */
58845
58846/**
58847 * base xml parser class
58848 */
58849require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';
58850require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
58851/**
58852 * Parser for channel.xml
58853 * @category   pear
58854 * @package    PEAR
58855 * @author     Greg Beaver <cellog@php.net>
58856 * @copyright  1997-2009 The Authors
58857 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
58858 * @version    Release: 1.9.4
58859 * @link       http://pear.php.net/package/PEAR
58860 * @since      Class available since Release 1.4.0a1
58861 */
58862class PEAR_ChannelFile_Parser extends PEAR_XMLParser
58863{
58864    var $_config;
58865    var $_logger;
58866    var $_registry;
58867
58868    function setConfig(&$c)
58869    {
58870        $this->_config = &$c;
58871        $this->_registry = &$c->getRegistry();
58872    }
58873
58874    function setLogger(&$l)
58875    {
58876        $this->_logger = &$l;
58877    }
58878
58879    function parse($data, $file)
58880    {
58881        if (PEAR::isError($err = parent::parse($data, $file))) {
58882            return $err;
58883        }
58884
58885        $ret = new PEAR_ChannelFile;
58886        $ret->setConfig($this->_config);
58887        if (isset($this->_logger)) {
58888            $ret->setLogger($this->_logger);
58889        }
58890
58891        $ret->fromArray($this->_unserializedData);
58892        // make sure the filelist is in the easy to read format needed
58893        $ret->flattenFilelist();
58894        $ret->setPackagefile($file, $archive);
58895        return $ret;
58896    }
58897}<?php
58898/**
58899 * PEAR_Command, command pattern class
58900 *
58901 * PHP versions 4 and 5
58902 *
58903 * @category   pear
58904 * @package    PEAR
58905 * @author     Stig Bakken <ssb@php.net>
58906 * @author     Greg Beaver <cellog@php.net>
58907 * @copyright  1997-2009 The Authors
58908 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
58909 * @version    CVS: $Id: Command.php 313023 2011-07-06 19:17:11Z dufuz $
58910 * @link       http://pear.php.net/package/PEAR
58911 * @since      File available since Release 0.1
58912 */
58913
58914/**
58915 * Needed for error handling
58916 */
58917require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
58918require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Frontend.php';
58919require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';
58920
58921/**
58922 * List of commands and what classes they are implemented in.
58923 * @var array command => implementing class
58924 */
58925$GLOBALS['_PEAR_Command_commandlist'] = array();
58926
58927/**
58928 * List of commands and their descriptions
58929 * @var array command => description
58930 */
58931$GLOBALS['_PEAR_Command_commanddesc'] = array();
58932
58933/**
58934 * List of shortcuts to common commands.
58935 * @var array shortcut => command
58936 */
58937$GLOBALS['_PEAR_Command_shortcuts'] = array();
58938
58939/**
58940 * Array of command objects
58941 * @var array class => object
58942 */
58943$GLOBALS['_PEAR_Command_objects'] = array();
58944
58945/**
58946 * PEAR command class, a simple factory class for administrative
58947 * commands.
58948 *
58949 * How to implement command classes:
58950 *
58951 * - The class must be called PEAR_Command_Nnn, installed in the
58952 *   "PEAR/Common" subdir, with a method called getCommands() that
58953 *   returns an array of the commands implemented by the class (see
58954 *   PEAR/Command/Install.php for an example).
58955 *
58956 * - The class must implement a run() function that is called with three
58957 *   params:
58958 *
58959 *    (string) command name
58960 *    (array)  assoc array with options, freely defined by each
58961 *             command, for example:
58962 *             array('force' => true)
58963 *    (array)  list of the other parameters
58964 *
58965 *   The run() function returns a PEAR_CommandResponse object.  Use
58966 *   these methods to get information:
58967 *
58968 *    int getStatus()   Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL)
58969 *                      *_PARTIAL means that you need to issue at least
58970 *                      one more command to complete the operation
58971 *                      (used for example for validation steps).
58972 *
58973 *    string getMessage()  Returns a message for the user.  Remember,
58974 *                         no HTML or other interface-specific markup.
58975 *
58976 *   If something unexpected happens, run() returns a PEAR error.
58977 *
58978 * - DON'T OUTPUT ANYTHING! Return text for output instead.
58979 *
58980 * - DON'T USE HTML! The text you return will be used from both Gtk,
58981 *   web and command-line interfaces, so for now, keep everything to
58982 *   plain text.
58983 *
58984 * - DON'T USE EXIT OR DIE! Always use pear errors.  From static
58985 *   classes do PEAR::raiseError(), from other classes do
58986 *   $this->raiseError().
58987 * @category   pear
58988 * @package    PEAR
58989 * @author     Stig Bakken <ssb@php.net>
58990 * @author     Greg Beaver <cellog@php.net>
58991 * @copyright  1997-2009 The Authors
58992 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
58993 * @version    Release: 1.9.4
58994 * @link       http://pear.php.net/package/PEAR
58995 * @since      Class available since Release 0.1
58996 */
58997class PEAR_Command
58998{
58999    // {{{ factory()
59000
59001    /**
59002     * Get the right object for executing a command.
59003     *
59004     * @param string $command The name of the command
59005     * @param object $config  Instance of PEAR_Config object
59006     *
59007     * @return object the command object or a PEAR error
59008     *
59009     * @access public
59010     * @static
59011     */
59012    function &factory($command, &$config)
59013    {
59014        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
59015            PEAR_Command::registerCommands();
59016        }
59017        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
59018            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
59019        }
59020        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
59021            $a = PEAR::raiseError("unknown command `$command'");
59022            return $a;
59023        }
59024        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
59025        if (!class_exists($class)) {
59026            require_once $GLOBALS['_PEAR_Command_objects'][$class];
59027        }
59028        if (!class_exists($class)) {
59029            $a = PEAR::raiseError("unknown command `$command'");
59030            return $a;
59031        }
59032        $ui =& PEAR_Command::getFrontendObject();
59033        $obj = &new $class($ui, $config);
59034        return $obj;
59035    }
59036
59037    // }}}
59038    // {{{ & getObject()
59039    function &getObject($command)
59040    {
59041        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
59042        if (!class_exists($class)) {
59043            require_once $GLOBALS['_PEAR_Command_objects'][$class];
59044        }
59045        if (!class_exists($class)) {
59046            return PEAR::raiseError("unknown command `$command'");
59047        }
59048        $ui =& PEAR_Command::getFrontendObject();
59049        $config = &PEAR_Config::singleton();
59050        $obj = &new $class($ui, $config);
59051        return $obj;
59052    }
59053
59054    // }}}
59055    // {{{ & getFrontendObject()
59056
59057    /**
59058     * Get instance of frontend object.
59059     *
59060     * @return object|PEAR_Error
59061     * @static
59062     */
59063    function &getFrontendObject()
59064    {
59065        $a = &PEAR_Frontend::singleton();
59066        return $a;
59067    }
59068
59069    // }}}
59070    // {{{ & setFrontendClass()
59071
59072    /**
59073     * Load current frontend class.
59074     *
59075     * @param string $uiclass Name of class implementing the frontend
59076     *
59077     * @return object the frontend object, or a PEAR error
59078     * @static
59079     */
59080    function &setFrontendClass($uiclass)
59081    {
59082        $a = &PEAR_Frontend::setFrontendClass($uiclass);
59083        return $a;
59084    }
59085
59086    // }}}
59087    // {{{ setFrontendType()
59088
59089    /**
59090     * Set current frontend.
59091     *
59092     * @param string $uitype Name of the frontend type (for example "CLI")
59093     *
59094     * @return object the frontend object, or a PEAR error
59095     * @static
59096     */
59097    function setFrontendType($uitype)
59098    {
59099        $uiclass = 'PEAR_Frontend_' . $uitype;
59100        return PEAR_Command::setFrontendClass($uiclass);
59101    }
59102
59103    // }}}
59104    // {{{ registerCommands()
59105
59106    /**
59107     * Scan through the Command directory looking for classes
59108     * and see what commands they implement.
59109     *
59110     * @param bool   (optional) if FALSE (default), the new list of
59111     *               commands should replace the current one.  If TRUE,
59112     *               new entries will be merged with old.
59113     *
59114     * @param string (optional) where (what directory) to look for
59115     *               classes, defaults to the Command subdirectory of
59116     *               the directory from where this file (__FILE__) is
59117     *               included.
59118     *
59119     * @return bool TRUE on success, a PEAR error on failure
59120     *
59121     * @access public
59122     * @static
59123     */
59124    function registerCommands($merge = false, $dir = null)
59125    {
59126        $parser = new PEAR_XMLParser;
59127        if ($dir === null) {
59128            $dir = dirname(__FILE__) . '/Command';
59129        }
59130        if (!is_dir($dir)) {
59131            return PEAR::raiseError("registerCommands: opendir($dir) '$dir' does not exist or is not a directory");
59132        }
59133        $dp = @opendir($dir);
59134        if (empty($dp)) {
59135            return PEAR::raiseError("registerCommands: opendir($dir) failed");
59136        }
59137        if (!$merge) {
59138            $GLOBALS['_PEAR_Command_commandlist'] = array();
59139        }
59140
59141        while ($file = readdir($dp)) {
59142            if ($file{0} == '.' || substr($file, -4) != '.xml') {
59143                continue;
59144            }
59145
59146            $f = substr($file, 0, -4);
59147            $class = "PEAR_Command_" . $f;
59148            // List of commands
59149            if (empty($GLOBALS['_PEAR_Command_objects'][$class])) {
59150                $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . $f . '.php';
59151            }
59152
59153            $parser->parse(file_get_contents("$dir/$file"));
59154            $implements = $parser->getData();
59155            foreach ($implements as $command => $desc) {
59156                if ($command == 'attribs') {
59157                    continue;
59158                }
59159
59160                if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
59161                    return PEAR::raiseError('Command "' . $command . '" already registered in ' .
59162                        'class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
59163                }
59164
59165                $GLOBALS['_PEAR_Command_commandlist'][$command] = $class;
59166                $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc['summary'];
59167                if (isset($desc['shortcut'])) {
59168                    $shortcut = $desc['shortcut'];
59169                    if (isset($GLOBALS['_PEAR_Command_shortcuts'][$shortcut])) {
59170                        return PEAR::raiseError('Command shortcut "' . $shortcut . '" already ' .
59171                            'registered to command "' . $command . '" in class "' .
59172                            $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
59173                    }
59174                    $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command;
59175                }
59176
59177                if (isset($desc['options']) && $desc['options']) {
59178                    foreach ($desc['options'] as $oname => $option) {
59179                        if (isset($option['shortopt']) && strlen($option['shortopt']) > 1) {
59180                            return PEAR::raiseError('Option "' . $oname . '" short option "' .
59181                                $option['shortopt'] . '" must be ' .
59182                                'only 1 character in Command "' . $command . '" in class "' .
59183                                $class . '"');
59184                        }
59185                    }
59186                }
59187            }
59188        }
59189
59190        ksort($GLOBALS['_PEAR_Command_shortcuts']);
59191        ksort($GLOBALS['_PEAR_Command_commandlist']);
59192        @closedir($dp);
59193        return true;
59194    }
59195
59196    // }}}
59197    // {{{ getCommands()
59198
59199    /**
59200     * Get the list of currently supported commands, and what
59201     * classes implement them.
59202     *
59203     * @return array command => implementing class
59204     *
59205     * @access public
59206     * @static
59207     */
59208    function getCommands()
59209    {
59210        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
59211            PEAR_Command::registerCommands();
59212        }
59213        return $GLOBALS['_PEAR_Command_commandlist'];
59214    }
59215
59216    // }}}
59217    // {{{ getShortcuts()
59218
59219    /**
59220     * Get the list of command shortcuts.
59221     *
59222     * @return array shortcut => command
59223     *
59224     * @access public
59225     * @static
59226     */
59227    function getShortcuts()
59228    {
59229        if (empty($GLOBALS['_PEAR_Command_shortcuts'])) {
59230            PEAR_Command::registerCommands();
59231        }
59232        return $GLOBALS['_PEAR_Command_shortcuts'];
59233    }
59234
59235    // }}}
59236    // {{{ getGetoptArgs()
59237
59238    /**
59239     * Compiles arguments for getopt.
59240     *
59241     * @param string $command     command to get optstring for
59242     * @param string $short_args  (reference) short getopt format
59243     * @param array  $long_args   (reference) long getopt format
59244     *
59245     * @return void
59246     *
59247     * @access public
59248     * @static
59249     */
59250    function getGetoptArgs($command, &$short_args, &$long_args)
59251    {
59252        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
59253            PEAR_Command::registerCommands();
59254        }
59255        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
59256            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
59257        }
59258        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
59259            return null;
59260        }
59261        $obj = &PEAR_Command::getObject($command);
59262        return $obj->getGetoptArgs($command, $short_args, $long_args);
59263    }
59264
59265    // }}}
59266    // {{{ getDescription()
59267
59268    /**
59269     * Get description for a command.
59270     *
59271     * @param  string $command Name of the command
59272     *
59273     * @return string command description
59274     *
59275     * @access public
59276     * @static
59277     */
59278    function getDescription($command)
59279    {
59280        if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) {
59281            return null;
59282        }
59283        return $GLOBALS['_PEAR_Command_commanddesc'][$command];
59284    }
59285
59286    // }}}
59287    // {{{ getHelp()
59288
59289    /**
59290     * Get help for command.
59291     *
59292     * @param string $command Name of the command to return help for
59293     *
59294     * @access public
59295     * @static
59296     */
59297    function getHelp($command)
59298    {
59299        $cmds = PEAR_Command::getCommands();
59300        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
59301            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
59302        }
59303        if (isset($cmds[$command])) {
59304            $obj = &PEAR_Command::getObject($command);
59305            return $obj->getHelp($command);
59306        }
59307        return false;
59308    }
59309    // }}}
59310}<?php
59311/**
59312 * PEAR_Command_Auth (login, logout commands)
59313 *
59314 * PHP versions 4 and 5
59315 *
59316 * @category   pear
59317 * @package    PEAR
59318 * @author     Stig Bakken <ssb@php.net>
59319 * @author     Greg Beaver <cellog@php.net>
59320 * @copyright  1997-2009 The Authors
59321 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
59322 * @version    CVS: $Id: Auth.php 313023 2011-07-06 19:17:11Z dufuz $
59323 * @link       http://pear.php.net/package/PEAR
59324 * @since      File available since Release 0.1
59325 * @deprecated since 1.8.0alpha1
59326 */
59327
59328/**
59329 * base class
59330 */
59331require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Channels.php';
59332
59333/**
59334 * PEAR commands for login/logout
59335 *
59336 * @category   pear
59337 * @package    PEAR
59338 * @author     Stig Bakken <ssb@php.net>
59339 * @author     Greg Beaver <cellog@php.net>
59340 * @copyright  1997-2009 The Authors
59341 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
59342 * @version    Release: 1.9.4
59343 * @link       http://pear.php.net/package/PEAR
59344 * @since      Class available since Release 0.1
59345 * @deprecated since 1.8.0alpha1
59346 */
59347class PEAR_Command_Auth extends PEAR_Command_Channels
59348{
59349    var $commands = array(
59350        'login' => array(
59351            'summary' => 'Connects and authenticates to remote server [Deprecated in favor of channel-login]',
59352            'shortcut' => 'li',
59353            'function' => 'doLogin',
59354            'options' => array(),
59355            'doc' => '<channel name>
59356WARNING: This function is deprecated in favor of using channel-login
59357
59358Log in to a remote channel server.  If <channel name> is not supplied,
59359the default channel is used. To use remote functions in the installer
59360that require any kind of privileges, you need to log in first.  The
59361username and password you enter here will be stored in your per-user
59362PEAR configuration (~/.pearrc on Unix-like systems).  After logging
59363in, your username and password will be sent along in subsequent
59364operations on the remote server.',
59365            ),
59366        'logout' => array(
59367            'summary' => 'Logs out from the remote server [Deprecated in favor of channel-logout]',
59368            'shortcut' => 'lo',
59369            'function' => 'doLogout',
59370            'options' => array(),
59371            'doc' => '
59372WARNING: This function is deprecated in favor of using channel-logout
59373
59374Logs out from the remote server.  This command does not actually
59375connect to the remote server, it only deletes the stored username and
59376password from your user configuration.',
59377            )
59378
59379        );
59380
59381    /**
59382     * PEAR_Command_Auth constructor.
59383     *
59384     * @access public
59385     */
59386    function PEAR_Command_Auth(&$ui, &$config)
59387    {
59388        parent::PEAR_Command_Channels($ui, $config);
59389    }
59390}<commands version="1.0">
59391 <login>
59392  <summary>Connects and authenticates to remote server [Deprecated in favor of channel-login]</summary>
59393  <function>doLogin</function>
59394  <shortcut>li</shortcut>
59395  <options />
59396  <doc>&lt;channel name&gt;
59397WARNING: This function is deprecated in favor of using channel-login
59398
59399Log in to a remote channel server.  If &lt;channel name&gt; is not supplied,
59400the default channel is used. To use remote functions in the installer
59401that require any kind of privileges, you need to log in first.  The
59402username and password you enter here will be stored in your per-user
59403PEAR configuration (~/.pearrc on Unix-like systems).  After logging
59404in, your username and password will be sent along in subsequent
59405operations on the remote server.</doc>
59406 </login>
59407 <logout>
59408  <summary>Logs out from the remote server [Deprecated in favor of channel-logout]</summary>
59409  <function>doLogout</function>
59410  <shortcut>lo</shortcut>
59411  <options />
59412  <doc>
59413WARNING: This function is deprecated in favor of using channel-logout
59414
59415Logs out from the remote server.  This command does not actually
59416connect to the remote server, it only deletes the stored username and
59417password from your user configuration.</doc>
59418 </logout>
59419</commands><?php
59420/**
59421 * PEAR_Command_Auth (build command)
59422 *
59423 * PHP versions 4 and 5
59424 *
59425 * @category   pear
59426 * @package    PEAR
59427 * @author     Stig Bakken <ssb@php.net>
59428 * @author     Tomas V.V.Cox <cox@idecnet.com>
59429 * @author     Greg Beaver <cellog@php.net>
59430 * @copyright  1997-2009 The Authors
59431 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
59432 * @version    CVS: $Id: Build.php 313023 2011-07-06 19:17:11Z dufuz $
59433 * @link       http://pear.php.net/package/PEAR
59434 * @since      File available since Release 0.1
59435 */
59436
59437/**
59438 * base class
59439 */
59440require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Common.php';
59441
59442/**
59443 * PEAR commands for building extensions.
59444 *
59445 * @category   pear
59446 * @package    PEAR
59447 * @author     Stig Bakken <ssb@php.net>
59448 * @author     Tomas V.V.Cox <cox@idecnet.com>
59449 * @author     Greg Beaver <cellog@php.net>
59450 * @copyright  1997-2009 The Authors
59451 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
59452 * @version    Release: 1.9.4
59453 * @link       http://pear.php.net/package/PEAR
59454 * @since      Class available since Release 0.1
59455 */
59456class PEAR_Command_Build extends PEAR_Command_Common
59457{
59458    var $commands = array(
59459        'build' => array(
59460            'summary' => 'Build an Extension From C Source',
59461            'function' => 'doBuild',
59462            'shortcut' => 'b',
59463            'options' => array(),
59464            'doc' => '[package.xml]
59465Builds one or more extensions contained in a package.'
59466            ),
59467        );
59468
59469    /**
59470     * PEAR_Command_Build constructor.
59471     *
59472     * @access public
59473     */
59474    function PEAR_Command_Build(&$ui, &$config)
59475    {
59476        parent::PEAR_Command_Common($ui, $config);
59477    }
59478
59479    function doBuild($command, $options, $params)
59480    {
59481        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Builder.php';
59482        if (sizeof($params) < 1) {
59483            $params[0] = 'package.xml';
59484        }
59485
59486        $builder = &new PEAR_Builder($this->ui);
59487        $this->debug = $this->config->get('verbose');
59488        $err = $builder->build($params[0], array(&$this, 'buildCallback'));
59489        if (PEAR::isError($err)) {
59490            return $err;
59491        }
59492
59493        return true;
59494    }
59495
59496    function buildCallback($what, $data)
59497    {
59498        if (($what == 'cmdoutput' && $this->debug > 1) ||
59499            ($what == 'output' && $this->debug > 0)) {
59500            $this->ui->outputData(rtrim($data), 'build');
59501        }
59502    }
59503}<commands version="1.0">
59504 <build>
59505  <summary>Build an Extension From C Source</summary>
59506  <function>doBuild</function>
59507  <shortcut>b</shortcut>
59508  <options />
59509  <doc>[package.xml]
59510Builds one or more extensions contained in a package.</doc>
59511 </build>
59512</commands><?php
59513// /* vim: set expandtab tabstop=4 shiftwidth=4: */
59514/**
59515 * PEAR_Command_Channels (list-channels, update-channels, channel-delete, channel-add,
59516 * channel-update, channel-info, channel-alias, channel-discover commands)
59517 *
59518 * PHP versions 4 and 5
59519 *
59520 * @category   pear
59521 * @package    PEAR
59522 * @author     Stig Bakken <ssb@php.net>
59523 * @author     Greg Beaver <cellog@php.net>
59524 * @copyright  1997-2009 The Authors
59525 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
59526 * @version    CVS: $Id: Channels.php 313023 2011-07-06 19:17:11Z dufuz $
59527 * @link       http://pear.php.net/package/PEAR
59528 * @since      File available since Release 1.4.0a1
59529 */
59530
59531/**
59532 * base class
59533 */
59534require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Common.php';
59535
59536define('PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS', -500);
59537
59538/**
59539 * PEAR commands for managing channels.
59540 *
59541 * @category   pear
59542 * @package    PEAR
59543 * @author     Greg Beaver <cellog@php.net>
59544 * @copyright  1997-2009 The Authors
59545 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
59546 * @version    Release: 1.9.4
59547 * @link       http://pear.php.net/package/PEAR
59548 * @since      Class available since Release 1.4.0a1
59549 */
59550class PEAR_Command_Channels extends PEAR_Command_Common
59551{
59552    var $commands = array(
59553        'list-channels' => array(
59554            'summary' => 'List Available Channels',
59555            'function' => 'doList',
59556            'shortcut' => 'lc',
59557            'options' => array(),
59558            'doc' => '
59559List all available channels for installation.
59560',
59561            ),
59562        'update-channels' => array(
59563            'summary' => 'Update the Channel List',
59564            'function' => 'doUpdateAll',
59565            'shortcut' => 'uc',
59566            'options' => array(),
59567            'doc' => '
59568List all installed packages in all channels.
59569'
59570            ),
59571        'channel-delete' => array(
59572            'summary' => 'Remove a Channel From the List',
59573            'function' => 'doDelete',
59574            'shortcut' => 'cde',
59575            'options' => array(),
59576            'doc' => '<channel name>
59577Delete a channel from the registry.  You may not
59578remove any channel that has installed packages.
59579'
59580            ),
59581        'channel-add' => array(
59582            'summary' => 'Add a Channel',
59583            'function' => 'doAdd',
59584            'shortcut' => 'ca',
59585            'options' => array(),
59586            'doc' => '<channel.xml>
59587Add a private channel to the channel list.  Note that all
59588public channels should be synced using "update-channels".
59589Parameter may be either a local file or remote URL to a
59590channel.xml.
59591'
59592            ),
59593        'channel-update' => array(
59594            'summary' => 'Update an Existing Channel',
59595            'function' => 'doUpdate',
59596            'shortcut' => 'cu',
59597            'options' => array(
59598                'force' => array(
59599                    'shortopt' => 'f',
59600                    'doc' => 'will force download of new channel.xml if an existing channel name is used',
59601                    ),
59602                'channel' => array(
59603                    'shortopt' => 'c',
59604                    'arg' => 'CHANNEL',
59605                    'doc' => 'will force download of new channel.xml if an existing channel name is used',
59606                    ),
59607),
59608            'doc' => '[<channel.xml>|<channel name>]
59609Update a channel in the channel list directly.  Note that all
59610public channels can be synced using "update-channels".
59611Parameter may be a local or remote channel.xml, or the name of
59612an existing channel.
59613'
59614            ),
59615        'channel-info' => array(
59616            'summary' => 'Retrieve Information on a Channel',
59617            'function' => 'doInfo',
59618            'shortcut' => 'ci',
59619            'options' => array(),
59620            'doc' => '<package>
59621List the files in an installed package.
59622'
59623            ),
59624        'channel-alias' => array(
59625            'summary' => 'Specify an alias to a channel name',
59626            'function' => 'doAlias',
59627            'shortcut' => 'cha',
59628            'options' => array(),
59629            'doc' => '<channel> <alias>
59630Specify a specific alias to use for a channel name.
59631The alias may not be an existing channel name or
59632alias.
59633'
59634            ),
59635        'channel-discover' => array(
59636            'summary' => 'Initialize a Channel from its server',
59637            'function' => 'doDiscover',
59638            'shortcut' => 'di',
59639            'options' => array(),
59640            'doc' => '[<channel.xml>|<channel name>]
59641Initialize a channel from its server and create a local channel.xml.
59642If <channel name> is in the format "<username>:<password>@<channel>" then
59643<username> and <password> will be set as the login username/password for
59644<channel>. Use caution when passing the username/password in this way, as
59645it may allow other users on your computer to briefly view your username/
59646password via the system\'s process list.
59647'
59648            ),
59649        'channel-login' => array(
59650            'summary' => 'Connects and authenticates to remote channel server',
59651            'shortcut' => 'cli',
59652            'function' => 'doLogin',
59653            'options' => array(),
59654            'doc' => '<channel name>
59655Log in to a remote channel server.  If <channel name> is not supplied,
59656the default channel is used. To use remote functions in the installer
59657that require any kind of privileges, you need to log in first.  The
59658username and password you enter here will be stored in your per-user
59659PEAR configuration (~/.pearrc on Unix-like systems).  After logging
59660in, your username and password will be sent along in subsequent
59661operations on the remote server.',
59662            ),
59663        'channel-logout' => array(
59664            'summary' => 'Logs out from the remote channel server',
59665            'shortcut' => 'clo',
59666            'function' => 'doLogout',
59667            'options' => array(),
59668            'doc' => '<channel name>
59669Logs out from a remote channel server.  If <channel name> is not supplied,
59670the default channel is used. This command does not actually connect to the
59671remote server, it only deletes the stored username and password from your user
59672configuration.',
59673            ),
59674        );
59675
59676    /**
59677     * PEAR_Command_Registry constructor.
59678     *
59679     * @access public
59680     */
59681    function PEAR_Command_Channels(&$ui, &$config)
59682    {
59683        parent::PEAR_Command_Common($ui, $config);
59684    }
59685
59686    function _sortChannels($a, $b)
59687    {
59688        return strnatcasecmp($a->getName(), $b->getName());
59689    }
59690
59691    function doList($command, $options, $params)
59692    {
59693        $reg = &$this->config->getRegistry();
59694        $registered = $reg->getChannels();
59695        usort($registered, array(&$this, '_sortchannels'));
59696        $i = $j = 0;
59697        $data = array(
59698            'caption' => 'Registered Channels:',
59699            'border' => true,
59700            'headline' => array('Channel', 'Alias', 'Summary')
59701            );
59702        foreach ($registered as $channel) {
59703            $data['data'][] = array($channel->getName(),
59704                                    $channel->getAlias(),
59705                                    $channel->getSummary());
59706        }
59707
59708        if (count($registered) === 0) {
59709            $data = '(no registered channels)';
59710        }
59711        $this->ui->outputData($data, $command);
59712        return true;
59713    }
59714
59715    function doUpdateAll($command, $options, $params)
59716    {
59717        $reg = &$this->config->getRegistry();
59718        $channels = $reg->getChannels();
59719
59720        $success = true;
59721        foreach ($channels as $channel) {
59722            if ($channel->getName() != '__uri') {
59723                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
59724                $err = $this->doUpdate('channel-update',
59725                                          $options,
59726                                          array($channel->getName()));
59727                if (PEAR::isError($err)) {
59728                    $this->ui->outputData($err->getMessage(), $command);
59729                    $success = false;
59730                } else {
59731                    $success &= $err;
59732                }
59733            }
59734        }
59735        return $success;
59736    }
59737
59738    function doInfo($command, $options, $params)
59739    {
59740        if (count($params) !== 1) {
59741            return $this->raiseError("No channel specified");
59742        }
59743
59744        $reg     = &$this->config->getRegistry();
59745        $channel = strtolower($params[0]);
59746        if ($reg->channelExists($channel)) {
59747            $chan = $reg->getChannel($channel);
59748            if (PEAR::isError($chan)) {
59749                return $this->raiseError($chan);
59750            }
59751        } else {
59752            if (strpos($channel, '://')) {
59753                $downloader = &$this->getDownloader();
59754                $tmpdir = $this->config->get('temp_dir');
59755                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
59756                $loc = $downloader->downloadHttp($channel, $this->ui, $tmpdir);
59757                PEAR::staticPopErrorHandling();
59758                if (PEAR::isError($loc)) {
59759                    return $this->raiseError('Cannot open "' . $channel .
59760                        '" (' . $loc->getMessage() . ')');
59761                } else {
59762                    $contents = implode('', file($loc));
59763                }
59764            } else {
59765                if (!file_exists($params[0])) {
59766                    return $this->raiseError('Unknown channel "' . $channel . '"');
59767                }
59768
59769                $fp = fopen($params[0], 'r');
59770                if (!$fp) {
59771                    return $this->raiseError('Cannot open "' . $params[0] . '"');
59772                }
59773
59774                $contents = '';
59775                while (!feof($fp)) {
59776                    $contents .= fread($fp, 1024);
59777                }
59778                fclose($fp);
59779            }
59780
59781            if (!class_exists('PEAR_ChannelFile')) {
59782                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
59783            }
59784
59785            $chan = new PEAR_ChannelFile;
59786            $chan->fromXmlString($contents);
59787            $chan->validate();
59788            if ($errs = $chan->getErrors(true)) {
59789                foreach ($errs as $err) {
59790                    $this->ui->outputData($err['level'] . ': ' . $err['message']);
59791                }
59792                return $this->raiseError('Channel file "' . $params[0] . '" is not valid');
59793            }
59794        }
59795
59796        if (!$chan) {
59797            return $this->raiseError('Serious error: Channel "' . $params[0] .
59798                '" has a corrupted registry entry');
59799        }
59800
59801        $channel = $chan->getName();
59802        $caption = 'Channel ' . $channel . ' Information:';
59803        $data1 = array(
59804            'caption' => $caption,
59805            'border' => true);
59806        $data1['data']['server'] = array('Name and Server', $chan->getName());
59807        if ($chan->getAlias() != $chan->getName()) {
59808            $data1['data']['alias'] = array('Alias', $chan->getAlias());
59809        }
59810
59811        $data1['data']['summary'] = array('Summary', $chan->getSummary());
59812        $validate = $chan->getValidationPackage();
59813        $data1['data']['vpackage'] = array('Validation Package Name', $validate['_content']);
59814        $data1['data']['vpackageversion'] =
59815            array('Validation Package Version', $validate['attribs']['version']);
59816        $d = array();
59817        $d['main'] = $data1;
59818
59819        $data['data'] = array();
59820        $data['caption'] = 'Server Capabilities';
59821        $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
59822        if ($chan->supportsREST()) {
59823            if ($chan->supportsREST()) {
59824                $funcs = $chan->getFunctions('rest');
59825                if (!isset($funcs[0])) {
59826                    $funcs = array($funcs);
59827                }
59828                foreach ($funcs as $protocol) {
59829                    $data['data'][] = array('rest', $protocol['attribs']['type'],
59830                        $protocol['_content']);
59831                }
59832            }
59833        } else {
59834            $data['data'][] = array('No supported protocols');
59835        }
59836
59837        $d['protocols'] = $data;
59838        $data['data'] = array();
59839        $mirrors = $chan->getMirrors();
59840        if ($mirrors) {
59841            $data['caption'] = 'Channel ' . $channel . ' Mirrors:';
59842            unset($data['headline']);
59843            foreach ($mirrors as $mirror) {
59844                $data['data'][] = array($mirror['attribs']['host']);
59845                $d['mirrors'] = $data;
59846            }
59847
59848            foreach ($mirrors as $i => $mirror) {
59849                $data['data'] = array();
59850                $data['caption'] = 'Mirror ' . $mirror['attribs']['host'] . ' Capabilities';
59851                $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
59852                if ($chan->supportsREST($mirror['attribs']['host'])) {
59853                    if ($chan->supportsREST($mirror['attribs']['host'])) {
59854                        $funcs = $chan->getFunctions('rest', $mirror['attribs']['host']);
59855                        if (!isset($funcs[0])) {
59856                            $funcs = array($funcs);
59857                        }
59858
59859                        foreach ($funcs as $protocol) {
59860                            $data['data'][] = array('rest', $protocol['attribs']['type'],
59861                                $protocol['_content']);
59862                        }
59863                    }
59864                } else {
59865                    $data['data'][] = array('No supported protocols');
59866                }
59867                $d['mirrorprotocols' . $i] = $data;
59868            }
59869        }
59870        $this->ui->outputData($d, 'channel-info');
59871    }
59872
59873    // }}}
59874
59875    function doDelete($command, $options, $params)
59876    {
59877        if (count($params) !== 1) {
59878            return $this->raiseError('channel-delete: no channel specified');
59879        }
59880
59881        $reg = &$this->config->getRegistry();
59882        if (!$reg->channelExists($params[0])) {
59883            return $this->raiseError('channel-delete: channel "' . $params[0] . '" does not exist');
59884        }
59885
59886        $channel = $reg->channelName($params[0]);
59887        if ($channel == 'pear.php.net') {
59888            return $this->raiseError('Cannot delete the pear.php.net channel');
59889        }
59890
59891        if ($channel == 'pecl.php.net') {
59892            return $this->raiseError('Cannot delete the pecl.php.net channel');
59893        }
59894
59895        if ($channel == 'doc.php.net') {
59896            return $this->raiseError('Cannot delete the doc.php.net channel');
59897        }
59898
59899        if ($channel == '__uri') {
59900            return $this->raiseError('Cannot delete the __uri pseudo-channel');
59901        }
59902
59903        if (PEAR::isError($err = $reg->listPackages($channel))) {
59904            return $err;
59905        }
59906
59907        if (count($err)) {
59908            return $this->raiseError('Channel "' . $channel .
59909                '" has installed packages, cannot delete');
59910        }
59911
59912        if (!$reg->deleteChannel($channel)) {
59913            return $this->raiseError('Channel "' . $channel . '" deletion failed');
59914        } else {
59915            $this->config->deleteChannel($channel);
59916            $this->ui->outputData('Channel "' . $channel . '" deleted', $command);
59917        }
59918    }
59919
59920    function doAdd($command, $options, $params)
59921    {
59922        if (count($params) !== 1) {
59923            return $this->raiseError('channel-add: no channel file specified');
59924        }
59925
59926        if (strpos($params[0], '://')) {
59927            $downloader = &$this->getDownloader();
59928            $tmpdir = $this->config->get('temp_dir');
59929            if (!file_exists($tmpdir)) {
59930                require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
59931                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
59932                $err = System::mkdir(array('-p', $tmpdir));
59933                PEAR::staticPopErrorHandling();
59934                if (PEAR::isError($err)) {
59935                    return $this->raiseError('channel-add: temp_dir does not exist: "' .
59936                        $tmpdir .
59937                        '" - You can change this location with "pear config-set temp_dir"');
59938                }
59939            }
59940
59941            if (!is_writable($tmpdir)) {
59942                return $this->raiseError('channel-add: temp_dir is not writable: "' .
59943                    $tmpdir .
59944                    '" - You can change this location with "pear config-set temp_dir"');
59945            }
59946
59947            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
59948            $loc = $downloader->downloadHttp($params[0], $this->ui, $tmpdir, null, false);
59949            PEAR::staticPopErrorHandling();
59950            if (PEAR::isError($loc)) {
59951                return $this->raiseError('channel-add: Cannot open "' . $params[0] .
59952                    '" (' . $loc->getMessage() . ')');
59953            }
59954
59955            list($loc, $lastmodified) = $loc;
59956            $contents = implode('', file($loc));
59957        } else {
59958            $lastmodified = $fp = false;
59959            if (file_exists($params[0])) {
59960                $fp = fopen($params[0], 'r');
59961            }
59962
59963            if (!$fp) {
59964                return $this->raiseError('channel-add: cannot open "' . $params[0] . '"');
59965            }
59966
59967            $contents = '';
59968            while (!feof($fp)) {
59969                $contents .= fread($fp, 1024);
59970            }
59971            fclose($fp);
59972        }
59973
59974        if (!class_exists('PEAR_ChannelFile')) {
59975            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
59976        }
59977
59978        $channel = new PEAR_ChannelFile;
59979        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
59980        $result = $channel->fromXmlString($contents);
59981        PEAR::staticPopErrorHandling();
59982        if (!$result) {
59983            $exit = false;
59984            if (count($errors = $channel->getErrors(true))) {
59985                foreach ($errors as $error) {
59986                    $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
59987                    if (!$exit) {
59988                        $exit = $error['level'] == 'error' ? true : false;
59989                    }
59990                }
59991                if ($exit) {
59992                    return $this->raiseError('channel-add: invalid channel.xml file');
59993                }
59994            }
59995        }
59996
59997        $reg = &$this->config->getRegistry();
59998        if ($reg->channelExists($channel->getName())) {
59999            return $this->raiseError('channel-add: Channel "' . $channel->getName() .
60000                '" exists, use channel-update to update entry', PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS);
60001        }
60002
60003        $ret = $reg->addChannel($channel, $lastmodified);
60004        if (PEAR::isError($ret)) {
60005            return $ret;
60006        }
60007
60008        if (!$ret) {
60009            return $this->raiseError('channel-add: adding Channel "' . $channel->getName() .
60010                '" to registry failed');
60011        }
60012
60013        $this->config->setChannels($reg->listChannels());
60014        $this->config->writeConfigFile();
60015        $this->ui->outputData('Adding Channel "' . $channel->getName() . '" succeeded', $command);
60016    }
60017
60018    function doUpdate($command, $options, $params)
60019    {
60020        if (count($params) !== 1) {
60021            return $this->raiseError("No channel file specified");
60022        }
60023
60024        $tmpdir = $this->config->get('temp_dir');
60025        if (!file_exists($tmpdir)) {
60026            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
60027            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
60028            $err = System::mkdir(array('-p', $tmpdir));
60029            PEAR::staticPopErrorHandling();
60030            if (PEAR::isError($err)) {
60031                return $this->raiseError('channel-add: temp_dir does not exist: "' .
60032                    $tmpdir .
60033                    '" - You can change this location with "pear config-set temp_dir"');
60034            }
60035        }
60036
60037        if (!is_writable($tmpdir)) {
60038            return $this->raiseError('channel-add: temp_dir is not writable: "' .
60039                $tmpdir .
60040                '" - You can change this location with "pear config-set temp_dir"');
60041        }
60042
60043        $reg = &$this->config->getRegistry();
60044        $lastmodified = false;
60045        if ((!file_exists($params[0]) || is_dir($params[0]))
60046              && $reg->channelExists(strtolower($params[0]))) {
60047            $c = $reg->getChannel(strtolower($params[0]));
60048            if (PEAR::isError($c)) {
60049                return $this->raiseError($c);
60050            }
60051
60052            $this->ui->outputData("Updating channel \"$params[0]\"", $command);
60053            $dl = &$this->getDownloader(array());
60054            // if force is specified, use a timestamp of "1" to force retrieval
60055            $lastmodified = isset($options['force']) ? false : $c->lastModified();
60056            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
60057            $contents = $dl->downloadHttp('http://' . $c->getName() . '/channel.xml',
60058                $this->ui, $tmpdir, null, $lastmodified);
60059            PEAR::staticPopErrorHandling();
60060            if (PEAR::isError($contents)) {
60061                // Attempt to fall back to https
60062                $this->ui->outputData("Channel \"$params[0]\" is not responding over http://, failed with message: " . $contents->getMessage());
60063                $this->ui->outputData("Trying channel \"$params[0]\" over https:// instead");
60064                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
60065                $contents = $dl->downloadHttp('https://' . $c->getName() . '/channel.xml',
60066                    $this->ui, $tmpdir, null, $lastmodified);
60067                PEAR::staticPopErrorHandling();
60068                if (PEAR::isError($contents)) {
60069                    return $this->raiseError('Cannot retrieve channel.xml for channel "' .
60070                        $c->getName() . '" (' . $contents->getMessage() . ')');
60071                }
60072            }
60073
60074            list($contents, $lastmodified) = $contents;
60075            if (!$contents) {
60076                $this->ui->outputData("Channel \"$params[0]\" is up to date");
60077                return;
60078            }
60079
60080            $contents = implode('', file($contents));
60081            if (!class_exists('PEAR_ChannelFile')) {
60082                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
60083            }
60084
60085            $channel = new PEAR_ChannelFile;
60086            $channel->fromXmlString($contents);
60087            if (!$channel->getErrors()) {
60088                // security check: is the downloaded file for the channel we got it from?
60089                if (strtolower($channel->getName()) != strtolower($c->getName())) {
60090                    if (!isset($options['force'])) {
60091                        return $this->raiseError('ERROR: downloaded channel definition file' .
60092                            ' for channel "' . $channel->getName() . '" from channel "' .
60093                            strtolower($c->getName()) . '"');
60094                    }
60095
60096                    $this->ui->log(0, 'WARNING: downloaded channel definition file' .
60097                        ' for channel "' . $channel->getName() . '" from channel "' .
60098                        strtolower($c->getName()) . '"');
60099                }
60100            }
60101        } else {
60102            if (strpos($params[0], '://')) {
60103                $dl = &$this->getDownloader();
60104                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
60105                $loc = $dl->downloadHttp($params[0],
60106                    $this->ui, $tmpdir, null, $lastmodified);
60107                PEAR::staticPopErrorHandling();
60108                if (PEAR::isError($loc)) {
60109                    return $this->raiseError("Cannot open " . $params[0] .
60110                         ' (' . $loc->getMessage() . ')');
60111                }
60112
60113                list($loc, $lastmodified) = $loc;
60114                $contents = implode('', file($loc));
60115            } else {
60116                $fp = false;
60117                if (file_exists($params[0])) {
60118                    $fp = fopen($params[0], 'r');
60119                }
60120
60121                if (!$fp) {
60122                    return $this->raiseError("Cannot open " . $params[0]);
60123                }
60124
60125                $contents = '';
60126                while (!feof($fp)) {
60127                    $contents .= fread($fp, 1024);
60128                }
60129                fclose($fp);
60130            }
60131
60132            if (!class_exists('PEAR_ChannelFile')) {
60133                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
60134            }
60135
60136            $channel = new PEAR_ChannelFile;
60137            $channel->fromXmlString($contents);
60138        }
60139
60140        $exit = false;
60141        if (count($errors = $channel->getErrors(true))) {
60142            foreach ($errors as $error) {
60143                $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
60144                if (!$exit) {
60145                    $exit = $error['level'] == 'error' ? true : false;
60146                }
60147            }
60148            if ($exit) {
60149                return $this->raiseError('Invalid channel.xml file');
60150            }
60151        }
60152
60153        if (!$reg->channelExists($channel->getName())) {
60154            return $this->raiseError('Error: Channel "' . $channel->getName() .
60155                '" does not exist, use channel-add to add an entry');
60156        }
60157
60158        $ret = $reg->updateChannel($channel, $lastmodified);
60159        if (PEAR::isError($ret)) {
60160            return $ret;
60161        }
60162
60163        if (!$ret) {
60164            return $this->raiseError('Updating Channel "' . $channel->getName() .
60165                '" in registry failed');
60166        }
60167
60168        $this->config->setChannels($reg->listChannels());
60169        $this->config->writeConfigFile();
60170        $this->ui->outputData('Update of Channel "' . $channel->getName() . '" succeeded');
60171    }
60172
60173    function &getDownloader()
60174    {
60175        if (!class_exists('PEAR_Downloader')) {
60176            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader.php';
60177        }
60178        $a = new PEAR_Downloader($this->ui, array(), $this->config);
60179        return $a;
60180    }
60181
60182    function doAlias($command, $options, $params)
60183    {
60184        if (count($params) === 1) {
60185            return $this->raiseError('No channel alias specified');
60186        }
60187
60188        if (count($params) !== 2 || (!empty($params[1]) && $params[1]{0} == '-')) {
60189            return $this->raiseError(
60190                'Invalid format, correct is: channel-alias channel alias');
60191        }
60192
60193        $reg = &$this->config->getRegistry();
60194        if (!$reg->channelExists($params[0], true)) {
60195            $extra = '';
60196            if ($reg->isAlias($params[0])) {
60197                $extra = ' (use "channel-alias ' . $reg->channelName($params[0]) . ' ' .
60198                    strtolower($params[1]) . '")';
60199            }
60200
60201            return $this->raiseError('"' . $params[0] . '" is not a valid channel' . $extra);
60202        }
60203
60204        if ($reg->isAlias($params[1])) {
60205            return $this->raiseError('Channel "' . $reg->channelName($params[1]) . '" is ' .
60206                'already aliased to "' . strtolower($params[1]) . '", cannot re-alias');
60207        }
60208
60209        $chan = &$reg->getChannel($params[0]);
60210        if (PEAR::isError($chan)) {
60211            return $this->raiseError('Corrupt registry?  Error retrieving channel "' . $params[0] .
60212                '" information (' . $chan->getMessage() . ')');
60213        }
60214
60215        // make it a local alias
60216        if (!$chan->setAlias(strtolower($params[1]), true)) {
60217            return $this->raiseError('Alias "' . strtolower($params[1]) .
60218                '" is not a valid channel alias');
60219        }
60220
60221        $reg->updateChannel($chan);
60222        $this->ui->outputData('Channel "' . $chan->getName() . '" aliased successfully to "' .
60223            strtolower($params[1]) . '"');
60224    }
60225
60226    /**
60227     * The channel-discover command
60228     *
60229     * @param string $command command name
60230     * @param array  $options option_name => value
60231     * @param array  $params  list of additional parameters.
60232     *               $params[0] should contain a string with either:
60233     *               - <channel name> or
60234     *               - <username>:<password>@<channel name>
60235     * @return null|PEAR_Error
60236     */
60237    function doDiscover($command, $options, $params)
60238    {
60239        if (count($params) !== 1) {
60240            return $this->raiseError("No channel server specified");
60241        }
60242
60243        // Look for the possible input format "<username>:<password>@<channel>"
60244        if (preg_match('/^(.+):(.+)@(.+)\\z/', $params[0], $matches)) {
60245            $username = $matches[1];
60246            $password = $matches[2];
60247            $channel  = $matches[3];
60248        } else {
60249            $channel = $params[0];
60250        }
60251
60252        $reg = &$this->config->getRegistry();
60253        if ($reg->channelExists($channel)) {
60254            if (!$reg->isAlias($channel)) {
60255                return $this->raiseError("Channel \"$channel\" is already initialized", PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS);
60256            }
60257
60258            return $this->raiseError("A channel alias named \"$channel\" " .
60259                'already exists, aliasing channel "' . $reg->channelName($channel)
60260                . '"');
60261        }
60262
60263        $this->pushErrorHandling(PEAR_ERROR_RETURN);
60264        $err = $this->doAdd($command, $options, array('http://' . $channel . '/channel.xml'));
60265        $this->popErrorHandling();
60266        if (PEAR::isError($err)) {
60267            if ($err->getCode() === PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS) {
60268                return $this->raiseError("Discovery of channel \"$channel\" failed (" .
60269                    $err->getMessage() . ')');
60270            }
60271            // Attempt fetch via https
60272            $this->ui->outputData("Discovering channel $channel over http:// failed with message: " . $err->getMessage());
60273            $this->ui->outputData("Trying to discover channel $channel over https:// instead");
60274            $this->pushErrorHandling(PEAR_ERROR_RETURN);
60275            $err = $this->doAdd($command, $options, array('https://' . $channel . '/channel.xml'));
60276            $this->popErrorHandling();
60277            if (PEAR::isError($err)) {
60278                return $this->raiseError("Discovery of channel \"$channel\" failed (" .
60279                    $err->getMessage() . ')');
60280            }
60281        }
60282
60283        // Store username/password if they were given
60284        // Arguably we should do a logintest on the channel here, but since
60285        // that's awkward on a REST-based channel (even "pear login" doesn't
60286        // do it for those), and XML-RPC is deprecated, it's fairly pointless.
60287        if (isset($username)) {
60288            $this->config->set('username', $username, 'user', $channel);
60289            $this->config->set('password', $password, 'user', $channel);
60290            $this->config->store();
60291            $this->ui->outputData("Stored login for channel \"$channel\" using username \"$username\"", $command);
60292        }
60293
60294        $this->ui->outputData("Discovery of channel \"$channel\" succeeded", $command);
60295    }
60296
60297    /**
60298     * Execute the 'login' command.
60299     *
60300     * @param string $command command name
60301     * @param array $options option_name => value
60302     * @param array $params list of additional parameters
60303     *
60304     * @return bool TRUE on success or
60305     * a PEAR error on failure
60306     *
60307     * @access public
60308     */
60309    function doLogin($command, $options, $params)
60310    {
60311        $reg = &$this->config->getRegistry();
60312
60313        // If a parameter is supplied, use that as the channel to log in to
60314        $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel');
60315
60316        $chan = $reg->getChannel($channel);
60317        if (PEAR::isError($chan)) {
60318            return $this->raiseError($chan);
60319        }
60320
60321        $server   = $this->config->get('preferred_mirror', null, $channel);
60322        $username = $this->config->get('username',         null, $channel);
60323        if (empty($username)) {
60324            $username = isset($_ENV['USER']) ? $_ENV['USER'] : null;
60325        }
60326        $this->ui->outputData("Logging in to $server.", $command);
60327
60328        list($username, $password) = $this->ui->userDialog(
60329            $command,
60330            array('Username', 'Password'),
60331            array('text',     'password'),
60332            array($username,  '')
60333            );
60334        $username = trim($username);
60335        $password = trim($password);
60336
60337        $ourfile = $this->config->getConfFile('user');
60338        if (!$ourfile) {
60339            $ourfile = $this->config->getConfFile('system');
60340        }
60341
60342        $this->config->set('username', $username, 'user', $channel);
60343        $this->config->set('password', $password, 'user', $channel);
60344
60345        if ($chan->supportsREST()) {
60346            $ok = true;
60347        }
60348
60349        if ($ok !== true) {
60350            return $this->raiseError('Login failed!');
60351        }
60352
60353        $this->ui->outputData("Logged in.", $command);
60354        // avoid changing any temporary settings changed with -d
60355        $ourconfig = new PEAR_Config($ourfile, $ourfile);
60356        $ourconfig->set('username', $username, 'user', $channel);
60357        $ourconfig->set('password', $password, 'user', $channel);
60358        $ourconfig->store();
60359
60360        return true;
60361    }
60362
60363    /**
60364     * Execute the 'logout' command.
60365     *
60366     * @param string $command command name
60367     * @param array $options option_name => value
60368     * @param array $params list of additional parameters
60369     *
60370     * @return bool TRUE on success or
60371     * a PEAR error on failure
60372     *
60373     * @access public
60374     */
60375    function doLogout($command, $options, $params)
60376    {
60377        $reg     = &$this->config->getRegistry();
60378
60379        // If a parameter is supplied, use that as the channel to log in to
60380        $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel');
60381
60382        $chan    = $reg->getChannel($channel);
60383        if (PEAR::isError($chan)) {
60384            return $this->raiseError($chan);
60385        }
60386
60387        $server = $this->config->get('preferred_mirror', null, $channel);
60388        $this->ui->outputData("Logging out from $server.", $command);
60389        $this->config->remove('username', 'user', $channel);
60390        $this->config->remove('password', 'user', $channel);
60391        $this->config->store();
60392        return true;
60393    }
60394}<commands version="1.0">
60395 <list-channels>
60396  <summary>List Available Channels</summary>
60397  <function>doList</function>
60398  <shortcut>lc</shortcut>
60399  <options />
60400  <doc>
60401List all available channels for installation.
60402</doc>
60403 </list-channels>
60404 <update-channels>
60405  <summary>Update the Channel List</summary>
60406  <function>doUpdateAll</function>
60407  <shortcut>uc</shortcut>
60408  <options />
60409  <doc>
60410List all installed packages in all channels.
60411</doc>
60412 </update-channels>
60413 <channel-delete>
60414  <summary>Remove a Channel From the List</summary>
60415  <function>doDelete</function>
60416  <shortcut>cde</shortcut>
60417  <options />
60418  <doc>&lt;channel name&gt;
60419Delete a channel from the registry.  You may not
60420remove any channel that has installed packages.
60421</doc>
60422 </channel-delete>
60423 <channel-add>
60424  <summary>Add a Channel</summary>
60425  <function>doAdd</function>
60426  <shortcut>ca</shortcut>
60427  <options />
60428  <doc>&lt;channel.xml&gt;
60429Add a private channel to the channel list.  Note that all
60430public channels should be synced using &quot;update-channels&quot;.
60431Parameter may be either a local file or remote URL to a
60432channel.xml.
60433</doc>
60434 </channel-add>
60435 <channel-update>
60436  <summary>Update an Existing Channel</summary>
60437  <function>doUpdate</function>
60438  <shortcut>cu</shortcut>
60439  <options>
60440   <force>
60441    <shortopt>f</shortopt>
60442    <doc>will force download of new channel.xml if an existing channel name is used</doc>
60443   </force>
60444   <channel>
60445    <shortopt>c</shortopt>
60446    <doc>will force download of new channel.xml if an existing channel name is used</doc>
60447    <arg>CHANNEL</arg>
60448   </channel>
60449  </options>
60450  <doc>[&lt;channel.xml&gt;|&lt;channel name&gt;]
60451Update a channel in the channel list directly.  Note that all
60452public channels can be synced using &quot;update-channels&quot;.
60453Parameter may be a local or remote channel.xml, or the name of
60454an existing channel.
60455</doc>
60456 </channel-update>
60457 <channel-info>
60458  <summary>Retrieve Information on a Channel</summary>
60459  <function>doInfo</function>
60460  <shortcut>ci</shortcut>
60461  <options />
60462  <doc>&lt;package&gt;
60463List the files in an installed package.
60464</doc>
60465 </channel-info>
60466 <channel-alias>
60467  <summary>Specify an alias to a channel name</summary>
60468  <function>doAlias</function>
60469  <shortcut>cha</shortcut>
60470  <options />
60471  <doc>&lt;channel&gt; &lt;alias&gt;
60472Specify a specific alias to use for a channel name.
60473The alias may not be an existing channel name or
60474alias.
60475</doc>
60476 </channel-alias>
60477 <channel-discover>
60478  <summary>Initialize a Channel from its server</summary>
60479  <function>doDiscover</function>
60480  <shortcut>di</shortcut>
60481  <options />
60482  <doc>[&lt;channel.xml&gt;|&lt;channel name&gt;]
60483Initialize a channel from its server and create a local channel.xml.
60484If &lt;channel name&gt; is in the format &quot;&lt;username&gt;:&lt;password&gt;@&lt;channel&gt;&quot; then
60485&lt;username&gt; and &lt;password&gt; will be set as the login username/password for
60486&lt;channel&gt;. Use caution when passing the username/password in this way, as
60487it may allow other users on your computer to briefly view your username/
60488password via the system&#039;s process list.
60489</doc>
60490 </channel-discover>
60491 <channel-login>
60492  <summary>Connects and authenticates to remote channel server</summary>
60493  <function>doLogin</function>
60494  <shortcut>cli</shortcut>
60495  <options />
60496  <doc>&lt;channel name&gt;
60497Log in to a remote channel server.  If &lt;channel name&gt; is not supplied,
60498the default channel is used. To use remote functions in the installer
60499that require any kind of privileges, you need to log in first.  The
60500username and password you enter here will be stored in your per-user
60501PEAR configuration (~/.pearrc on Unix-like systems).  After logging
60502in, your username and password will be sent along in subsequent
60503operations on the remote server.</doc>
60504 </channel-login>
60505 <channel-logout>
60506  <summary>Logs out from the remote channel server</summary>
60507  <function>doLogout</function>
60508  <shortcut>clo</shortcut>
60509  <options />
60510  <doc>&lt;channel name&gt;
60511Logs out from a remote channel server.  If &lt;channel name&gt; is not supplied,
60512the default channel is used. This command does not actually connect to the
60513remote server, it only deletes the stored username and password from your user
60514configuration.</doc>
60515 </channel-logout>
60516</commands><?php
60517/**
60518 * PEAR_Command_Common base class
60519 *
60520 * PHP versions 4 and 5
60521 *
60522 * @category   pear
60523 * @package    PEAR
60524 * @author     Stig Bakken <ssb@php.net>
60525 * @author     Greg Beaver <cellog@php.net>
60526 * @copyright  1997-2009 The Authors
60527 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
60528 * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
60529 * @link       http://pear.php.net/package/PEAR
60530 * @since      File available since Release 0.1
60531 */
60532
60533/**
60534 * base class
60535 */
60536require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
60537
60538/**
60539 * PEAR commands base class
60540 *
60541 * @category   pear
60542 * @package    PEAR
60543 * @author     Stig Bakken <ssb@php.net>
60544 * @author     Greg Beaver <cellog@php.net>
60545 * @copyright  1997-2009 The Authors
60546 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
60547 * @version    Release: 1.9.4
60548 * @link       http://pear.php.net/package/PEAR
60549 * @since      Class available since Release 0.1
60550 */
60551class PEAR_Command_Common extends PEAR
60552{
60553    /**
60554     * PEAR_Config object used to pass user system and configuration
60555     * on when executing commands
60556     *
60557     * @var PEAR_Config
60558     */
60559    var $config;
60560    /**
60561     * @var PEAR_Registry
60562     * @access protected
60563     */
60564    var $_registry;
60565
60566    /**
60567     * User Interface object, for all interaction with the user.
60568     * @var object
60569     */
60570    var $ui;
60571
60572    var $_deps_rel_trans = array(
60573                                 'lt' => '<',
60574                                 'le' => '<=',
60575                                 'eq' => '=',
60576                                 'ne' => '!=',
60577                                 'gt' => '>',
60578                                 'ge' => '>=',
60579                                 'has' => '=='
60580                                 );
60581
60582    var $_deps_type_trans = array(
60583                                  'pkg' => 'package',
60584                                  'ext' => 'extension',
60585                                  'php' => 'PHP',
60586                                  'prog' => 'external program',
60587                                  'ldlib' => 'external library for linking',
60588                                  'rtlib' => 'external runtime library',
60589                                  'os' => 'operating system',
60590                                  'websrv' => 'web server',
60591                                  'sapi' => 'SAPI backend'
60592                                  );
60593
60594    /**
60595     * PEAR_Command_Common constructor.
60596     *
60597     * @access public
60598     */
60599    function PEAR_Command_Common(&$ui, &$config)
60600    {
60601        parent::PEAR();
60602        $this->config = &$config;
60603        $this->ui = &$ui;
60604    }
60605
60606    /**
60607     * Return a list of all the commands defined by this class.
60608     * @return array list of commands
60609     * @access public
60610     */
60611    function getCommands()
60612    {
60613        $ret = array();
60614        foreach (array_keys($this->commands) as $command) {
60615            $ret[$command] = $this->commands[$command]['summary'];
60616        }
60617
60618        return $ret;
60619    }
60620
60621    /**
60622     * Return a list of all the command shortcuts defined by this class.
60623     * @return array shortcut => command
60624     * @access public
60625     */
60626    function getShortcuts()
60627    {
60628        $ret = array();
60629        foreach (array_keys($this->commands) as $command) {
60630            if (isset($this->commands[$command]['shortcut'])) {
60631                $ret[$this->commands[$command]['shortcut']] = $command;
60632            }
60633        }
60634
60635        return $ret;
60636    }
60637
60638    function getOptions($command)
60639    {
60640        $shortcuts = $this->getShortcuts();
60641        if (isset($shortcuts[$command])) {
60642            $command = $shortcuts[$command];
60643        }
60644
60645        if (isset($this->commands[$command]) &&
60646              isset($this->commands[$command]['options'])) {
60647            return $this->commands[$command]['options'];
60648        }
60649
60650        return null;
60651    }
60652
60653    function getGetoptArgs($command, &$short_args, &$long_args)
60654    {
60655        $short_args = '';
60656        $long_args = array();
60657        if (empty($this->commands[$command]) || empty($this->commands[$command]['options'])) {
60658            return;
60659        }
60660
60661        reset($this->commands[$command]['options']);
60662        while (list($option, $info) = each($this->commands[$command]['options'])) {
60663            $larg = $sarg = '';
60664            if (isset($info['arg'])) {
60665                if ($info['arg']{0} == '(') {
60666                    $larg = '==';
60667                    $sarg = '::';
60668                    $arg = substr($info['arg'], 1, -1);
60669                } else {
60670                    $larg = '=';
60671                    $sarg = ':';
60672                    $arg = $info['arg'];
60673                }
60674            }
60675
60676            if (isset($info['shortopt'])) {
60677                $short_args .= $info['shortopt'] . $sarg;
60678            }
60679
60680            $long_args[] = $option . $larg;
60681        }
60682    }
60683
60684    /**
60685    * Returns the help message for the given command
60686    *
60687    * @param string $command The command
60688    * @return mixed A fail string if the command does not have help or
60689    *               a two elements array containing [0]=>help string,
60690    *               [1]=> help string for the accepted cmd args
60691    */
60692    function getHelp($command)
60693    {
60694        $config = &PEAR_Config::singleton();
60695        if (!isset($this->commands[$command])) {
60696            return "No such command \"$command\"";
60697        }
60698
60699        $help = null;
60700        if (isset($this->commands[$command]['doc'])) {
60701            $help = $this->commands[$command]['doc'];
60702        }
60703
60704        if (empty($help)) {
60705            // XXX (cox) Fallback to summary if there is no doc (show both?)
60706            if (!isset($this->commands[$command]['summary'])) {
60707                return "No help for command \"$command\"";
60708            }
60709            $help = $this->commands[$command]['summary'];
60710        }
60711
60712        if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) {
60713            foreach($matches[0] as $k => $v) {
60714                $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help);
60715            }
60716        }
60717
60718        return array($help, $this->getHelpArgs($command));
60719    }
60720
60721    /**
60722     * Returns the help for the accepted arguments of a command
60723     *
60724     * @param  string $command
60725     * @return string The help string
60726     */
60727    function getHelpArgs($command)
60728    {
60729        if (isset($this->commands[$command]['options']) &&
60730            count($this->commands[$command]['options']))
60731        {
60732            $help = "Options:\n";
60733            foreach ($this->commands[$command]['options'] as $k => $v) {
60734                if (isset($v['arg'])) {
60735                    if ($v['arg'][0] == '(') {
60736                        $arg = substr($v['arg'], 1, -1);
60737                        $sapp = " [$arg]";
60738                        $lapp = "[=$arg]";
60739                    } else {
60740                        $sapp = " $v[arg]";
60741                        $lapp = "=$v[arg]";
60742                    }
60743                } else {
60744                    $sapp = $lapp = "";
60745                }
60746
60747                if (isset($v['shortopt'])) {
60748                    $s = $v['shortopt'];
60749                    $help .= "  -$s$sapp, --$k$lapp\n";
60750                } else {
60751                    $help .= "  --$k$lapp\n";
60752                }
60753
60754                $p = "        ";
60755                $doc = rtrim(str_replace("\n", "\n$p", $v['doc']));
60756                $help .= "        $doc\n";
60757            }
60758
60759            return $help;
60760        }
60761
60762        return null;
60763    }
60764
60765    function run($command, $options, $params)
60766    {
60767        if (empty($this->commands[$command]['function'])) {
60768            // look for shortcuts
60769            foreach (array_keys($this->commands) as $cmd) {
60770                if (isset($this->commands[$cmd]['shortcut']) && $this->commands[$cmd]['shortcut'] == $command) {
60771                    if (empty($this->commands[$cmd]['function'])) {
60772                        return $this->raiseError("unknown command `$command'");
60773                    } else {
60774                        $func = $this->commands[$cmd]['function'];
60775                    }
60776                    $command = $cmd;
60777
60778                    //$command = $this->commands[$cmd]['function'];
60779                    break;
60780                }
60781            }
60782        } else {
60783            $func = $this->commands[$command]['function'];
60784        }
60785
60786        return $this->$func($command, $options, $params);
60787    }
60788}<?php
60789/**
60790 * PEAR_Command_Config (config-show, config-get, config-set, config-help, config-create commands)
60791 *
60792 * PHP versions 4 and 5
60793 *
60794 * @category   pear
60795 * @package    PEAR
60796 * @author     Stig Bakken <ssb@php.net>
60797 * @author     Greg Beaver <cellog@php.net>
60798 * @copyright  1997-2009 The Authors
60799 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
60800 * @version    CVS: $Id: Config.php 313024 2011-07-06 19:51:24Z dufuz $
60801 * @link       http://pear.php.net/package/PEAR
60802 * @since      File available since Release 0.1
60803 */
60804
60805/**
60806 * base class
60807 */
60808require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Common.php';
60809
60810/**
60811 * PEAR commands for managing configuration data.
60812 *
60813 * @category   pear
60814 * @package    PEAR
60815 * @author     Stig Bakken <ssb@php.net>
60816 * @author     Greg Beaver <cellog@php.net>
60817 * @copyright  1997-2009 The Authors
60818 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
60819 * @version    Release: 1.9.4
60820 * @link       http://pear.php.net/package/PEAR
60821 * @since      Class available since Release 0.1
60822 */
60823class PEAR_Command_Config extends PEAR_Command_Common
60824{
60825    var $commands = array(
60826        'config-show' => array(
60827            'summary' => 'Show All Settings',
60828            'function' => 'doConfigShow',
60829            'shortcut' => 'csh',
60830            'options' => array(
60831                'channel' => array(
60832                    'shortopt' => 'c',
60833                    'doc' => 'show configuration variables for another channel',
60834                    'arg' => 'CHAN',
60835                    ),
60836),
60837            'doc' => '[layer]
60838Displays all configuration values.  An optional argument
60839may be used to tell which configuration layer to display.  Valid
60840configuration layers are "user", "system" and "default". To display
60841configurations for different channels, set the default_channel
60842configuration variable and run config-show again.
60843',
60844            ),
60845        'config-get' => array(
60846            'summary' => 'Show One Setting',
60847            'function' => 'doConfigGet',
60848            'shortcut' => 'cg',
60849            'options' => array(
60850                'channel' => array(
60851                    'shortopt' => 'c',
60852                    'doc' => 'show configuration variables for another channel',
60853                    'arg' => 'CHAN',
60854                    ),
60855),
60856            'doc' => '<parameter> [layer]
60857Displays the value of one configuration parameter.  The
60858first argument is the name of the parameter, an optional second argument
60859may be used to tell which configuration layer to look in.  Valid configuration
60860layers are "user", "system" and "default".  If no layer is specified, a value
60861will be picked from the first layer that defines the parameter, in the order
60862just specified.  The configuration value will be retrieved for the channel
60863specified by the default_channel configuration variable.
60864',
60865            ),
60866        'config-set' => array(
60867            'summary' => 'Change Setting',
60868            'function' => 'doConfigSet',
60869            'shortcut' => 'cs',
60870            'options' => array(
60871                'channel' => array(
60872                    'shortopt' => 'c',
60873                    'doc' => 'show configuration variables for another channel',
60874                    'arg' => 'CHAN',
60875                    ),
60876),
60877            'doc' => '<parameter> <value> [layer]
60878Sets the value of one configuration parameter.  The first argument is
60879the name of the parameter, the second argument is the new value.  Some
60880parameters are subject to validation, and the command will fail with
60881an error message if the new value does not make sense.  An optional
60882third argument may be used to specify in which layer to set the
60883configuration parameter.  The default layer is "user".  The
60884configuration value will be set for the current channel, which
60885is controlled by the default_channel configuration variable.
60886',
60887            ),
60888        'config-help' => array(
60889            'summary' => 'Show Information About Setting',
60890            'function' => 'doConfigHelp',
60891            'shortcut' => 'ch',
60892            'options' => array(),
60893            'doc' => '[parameter]
60894Displays help for a configuration parameter.  Without arguments it
60895displays help for all configuration parameters.
60896',
60897           ),
60898        'config-create' => array(
60899            'summary' => 'Create a Default configuration file',
60900            'function' => 'doConfigCreate',
60901            'shortcut' => 'coc',
60902            'options' => array(
60903                'windows' => array(
60904                    'shortopt' => 'w',
60905                    'doc' => 'create a config file for a windows install',
60906                    ),
60907            ),
60908            'doc' => '<root path> <filename>
60909Create a default configuration file with all directory configuration
60910variables set to subdirectories of <root path>, and save it as <filename>.
60911This is useful especially for creating a configuration file for a remote
60912PEAR installation (using the --remoteconfig option of install, upgrade,
60913and uninstall).
60914',
60915            ),
60916        );
60917
60918    /**
60919     * PEAR_Command_Config constructor.
60920     *
60921     * @access public
60922     */
60923    function PEAR_Command_Config(&$ui, &$config)
60924    {
60925        parent::PEAR_Command_Common($ui, $config);
60926    }
60927
60928    function doConfigShow($command, $options, $params)
60929    {
60930        $layer = null;
60931        if (is_array($params)) {
60932            $layer = isset($params[0]) ? $params[0] : null;
60933        }
60934
60935        // $params[0] -> the layer
60936        if ($error = $this->_checkLayer($layer)) {
60937            return $this->raiseError("config-show:$error");
60938        }
60939
60940        $keys = $this->config->getKeys();
60941        sort($keys);
60942        $channel = isset($options['channel']) ? $options['channel'] :
60943            $this->config->get('default_channel');
60944        $reg = &$this->config->getRegistry();
60945        if (!$reg->channelExists($channel)) {
60946            return $this->raiseError('Channel "' . $channel . '" does not exist');
60947        }
60948
60949        $channel = $reg->channelName($channel);
60950        $data = array('caption' => 'Configuration (channel ' . $channel . '):');
60951        foreach ($keys as $key) {
60952            $type = $this->config->getType($key);
60953            $value = $this->config->get($key, $layer, $channel);
60954            if ($type == 'password' && $value) {
60955                $value = '********';
60956            }
60957
60958            if ($value === false) {
60959                $value = 'false';
60960            } elseif ($value === true) {
60961                $value = 'true';
60962            }
60963
60964            $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value);
60965        }
60966
60967        foreach ($this->config->getLayers() as $layer) {
60968            $data['data']['Config Files'][] = array(ucfirst($layer) . ' Configuration File', 'Filename' , $this->config->getConfFile($layer));
60969        }
60970
60971        $this->ui->outputData($data, $command);
60972        return true;
60973    }
60974
60975    function doConfigGet($command, $options, $params)
60976    {
60977        $args_cnt = is_array($params) ? count($params) : 0;
60978        switch ($args_cnt) {
60979            case 1:
60980                $config_key = $params[0];
60981                $layer = null;
60982                break;
60983            case 2:
60984                $config_key = $params[0];
60985                $layer = $params[1];
60986                if ($error = $this->_checkLayer($layer)) {
60987                    return $this->raiseError("config-get:$error");
60988                }
60989                break;
60990            case 0:
60991            default:
60992                return $this->raiseError("config-get expects 1 or 2 parameters");
60993        }
60994
60995        $reg = &$this->config->getRegistry();
60996        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
60997        if (!$reg->channelExists($channel)) {
60998            return $this->raiseError('Channel "' . $channel . '" does not exist');
60999        }
61000
61001        $channel = $reg->channelName($channel);
61002        $this->ui->outputData($this->config->get($config_key, $layer, $channel), $command);
61003        return true;
61004    }
61005
61006    function doConfigSet($command, $options, $params)
61007    {
61008        // $param[0] -> a parameter to set
61009        // $param[1] -> the value for the parameter
61010        // $param[2] -> the layer
61011        $failmsg = '';
61012        if (count($params) < 2 || count($params) > 3) {
61013            $failmsg .= "config-set expects 2 or 3 parameters";
61014            return PEAR::raiseError($failmsg);
61015        }
61016
61017        if (isset($params[2]) && ($error = $this->_checkLayer($params[2]))) {
61018            $failmsg .= $error;
61019            return PEAR::raiseError("config-set:$failmsg");
61020        }
61021
61022        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
61023        $reg = &$this->config->getRegistry();
61024        if (!$reg->channelExists($channel)) {
61025            return $this->raiseError('Channel "' . $channel . '" does not exist');
61026        }
61027
61028        $channel = $reg->channelName($channel);
61029        if ($params[0] == 'default_channel' && !$reg->channelExists($params[1])) {
61030            return $this->raiseError('Channel "' . $params[1] . '" does not exist');
61031        }
61032
61033        if ($params[0] == 'preferred_mirror'
61034            && (
61035                !$reg->mirrorExists($channel, $params[1]) &&
61036                (!$reg->channelExists($params[1]) || $channel != $params[1])
61037            )
61038        ) {
61039            $msg  = 'Channel Mirror "' . $params[1] . '" does not exist';
61040            $msg .= ' in your registry for channel "' . $channel . '".';
61041            $msg .= "\n" . 'Attempt to run "pear channel-update ' . $channel .'"';
61042            $msg .= ' if you believe this mirror should exist as you may';
61043            $msg .= ' have outdated channel information.';
61044            return $this->raiseError($msg);
61045        }
61046
61047        if (count($params) == 2) {
61048            array_push($params, 'user');
61049            $layer = 'user';
61050        } else {
61051            $layer = $params[2];
61052        }
61053
61054        array_push($params, $channel);
61055        if (!call_user_func_array(array(&$this->config, 'set'), $params)) {
61056            array_pop($params);
61057            $failmsg = "config-set (" . implode(", ", $params) . ") failed, channel $channel";
61058        } else {
61059            $this->config->store($layer);
61060        }
61061
61062        if ($failmsg) {
61063            return $this->raiseError($failmsg);
61064        }
61065
61066        $this->ui->outputData('config-set succeeded', $command);
61067        return true;
61068    }
61069
61070    function doConfigHelp($command, $options, $params)
61071    {
61072        if (empty($params)) {
61073            $params = $this->config->getKeys();
61074        }
61075
61076        $data['caption']  = "Config help" . ((count($params) == 1) ? " for $params[0]" : '');
61077        $data['headline'] = array('Name', 'Type', 'Description');
61078        $data['border']   = true;
61079        foreach ($params as $name) {
61080            $type = $this->config->getType($name);
61081            $docs = $this->config->getDocs($name);
61082            if ($type == 'set') {
61083                $docs = rtrim($docs) . "\nValid set: " .
61084                    implode(' ', $this->config->getSetValues($name));
61085            }
61086
61087            $data['data'][] = array($name, $type, $docs);
61088        }
61089
61090        $this->ui->outputData($data, $command);
61091    }
61092
61093    function doConfigCreate($command, $options, $params)
61094    {
61095        if (count($params) != 2) {
61096            return PEAR::raiseError('config-create: must have 2 parameters, root path and ' .
61097                'filename to save as');
61098        }
61099
61100        $root = $params[0];
61101        // Clean up the DIRECTORY_SEPARATOR mess
61102        $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
61103        $root = preg_replace(array('!\\\\+!', '!/+!', "!$ds2+!"),
61104                             array('/', '/', '/'),
61105                            $root);
61106        if ($root{0} != '/') {
61107            if (!isset($options['windows'])) {
61108                return PEAR::raiseError('Root directory must be an absolute path beginning ' .
61109                    'with "/", was: "' . $root . '"');
61110            }
61111
61112            if (!preg_match('/^[A-Za-z]:/', $root)) {
61113                return PEAR::raiseError('Root directory must be an absolute path beginning ' .
61114                    'with "\\" or "C:\\", was: "' . $root . '"');
61115            }
61116        }
61117
61118        $windows = isset($options['windows']);
61119        if ($windows) {
61120            $root = str_replace('/', '\\', $root);
61121        }
61122
61123        if (!file_exists($params[1]) && !@touch($params[1])) {
61124            return PEAR::raiseError('Could not create "' . $params[1] . '"');
61125        }
61126
61127        $params[1] = realpath($params[1]);
61128        $config = &new PEAR_Config($params[1], '#no#system#config#', false, false);
61129        if ($root{strlen($root) - 1} == '/') {
61130            $root = substr($root, 0, strlen($root) - 1);
61131        }
61132
61133        $config->noRegistry();
61134        $config->set('php_dir', $windows ? "$root\\pear\\php" : "$root/pear/php", 'user');
61135        $config->set('data_dir', $windows ? "$root\\pear\\data" : "$root/pear/data");
61136        $config->set('www_dir', $windows ? "$root\\pear\\www" : "$root/pear/www");
61137        $config->set('cfg_dir', $windows ? "$root\\pear\\cfg" : "$root/pear/cfg");
61138        $config->set('ext_dir', $windows ? "$root\\pear\\ext" : "$root/pear/ext");
61139        $config->set('doc_dir', $windows ? "$root\\pear\\docs" : "$root/pear/docs");
61140        $config->set('test_dir', $windows ? "$root\\pear\\tests" : "$root/pear/tests");
61141        $config->set('cache_dir', $windows ? "$root\\pear\\cache" : "$root/pear/cache");
61142        $config->set('download_dir', $windows ? "$root\\pear\\download" : "$root/pear/download");
61143        $config->set('temp_dir', $windows ? "$root\\pear\\temp" : "$root/pear/temp");
61144        $config->set('bin_dir', $windows ? "$root\\pear" : "$root/pear");
61145        $config->writeConfigFile();
61146        $this->_showConfig($config);
61147        $this->ui->outputData('Successfully created default configuration file "' . $params[1] . '"',
61148            $command);
61149    }
61150
61151    function _showConfig(&$config)
61152    {
61153        $params = array('user');
61154        $keys = $config->getKeys();
61155        sort($keys);
61156        $channel = 'pear.php.net';
61157        $data = array('caption' => 'Configuration (channel ' . $channel . '):');
61158        foreach ($keys as $key) {
61159            $type = $config->getType($key);
61160            $value = $config->get($key, 'user', $channel);
61161            if ($type == 'password' && $value) {
61162                $value = '********';
61163            }
61164
61165            if ($value === false) {
61166                $value = 'false';
61167            } elseif ($value === true) {
61168                $value = 'true';
61169            }
61170            $data['data'][$config->getGroup($key)][] =
61171                array($config->getPrompt($key) , $key, $value);
61172        }
61173
61174        foreach ($config->getLayers() as $layer) {
61175            $data['data']['Config Files'][] =
61176                array(ucfirst($layer) . ' Configuration File', 'Filename' ,
61177                    $config->getConfFile($layer));
61178        }
61179
61180        $this->ui->outputData($data, 'config-show');
61181        return true;
61182    }
61183
61184    /**
61185     * Checks if a layer is defined or not
61186     *
61187     * @param string $layer The layer to search for
61188     * @return mixed False on no error or the error message
61189     */
61190    function _checkLayer($layer = null)
61191    {
61192        if (!empty($layer) && $layer != 'default') {
61193            $layers = $this->config->getLayers();
61194            if (!in_array($layer, $layers)) {
61195                return " only the layers: \"" . implode('" or "', $layers) . "\" are supported";
61196            }
61197        }
61198
61199        return false;
61200    }
61201}<commands version="1.0">
61202 <config-show>
61203  <summary>Show All Settings</summary>
61204  <function>doConfigShow</function>
61205  <shortcut>csh</shortcut>
61206  <options>
61207   <channel>
61208    <shortopt>c</shortopt>
61209    <doc>show configuration variables for another channel</doc>
61210    <arg>CHAN</arg>
61211   </channel>
61212  </options>
61213  <doc>[layer]
61214Displays all configuration values.  An optional argument
61215may be used to tell which configuration layer to display.  Valid
61216configuration layers are &quot;user&quot;, &quot;system&quot; and &quot;default&quot;. To display
61217configurations for different channels, set the default_channel
61218configuration variable and run config-show again.
61219</doc>
61220 </config-show>
61221 <config-get>
61222  <summary>Show One Setting</summary>
61223  <function>doConfigGet</function>
61224  <shortcut>cg</shortcut>
61225  <options>
61226   <channel>
61227    <shortopt>c</shortopt>
61228    <doc>show configuration variables for another channel</doc>
61229    <arg>CHAN</arg>
61230   </channel>
61231  </options>
61232  <doc>&lt;parameter&gt; [layer]
61233Displays the value of one configuration parameter.  The
61234first argument is the name of the parameter, an optional second argument
61235may be used to tell which configuration layer to look in.  Valid configuration
61236layers are &quot;user&quot;, &quot;system&quot; and &quot;default&quot;.  If no layer is specified, a value
61237will be picked from the first layer that defines the parameter, in the order
61238just specified.  The configuration value will be retrieved for the channel
61239specified by the default_channel configuration variable.
61240</doc>
61241 </config-get>
61242 <config-set>
61243  <summary>Change Setting</summary>
61244  <function>doConfigSet</function>
61245  <shortcut>cs</shortcut>
61246  <options>
61247   <channel>
61248    <shortopt>c</shortopt>
61249    <doc>show configuration variables for another channel</doc>
61250    <arg>CHAN</arg>
61251   </channel>
61252  </options>
61253  <doc>&lt;parameter&gt; &lt;value&gt; [layer]
61254Sets the value of one configuration parameter.  The first argument is
61255the name of the parameter, the second argument is the new value.  Some
61256parameters are subject to validation, and the command will fail with
61257an error message if the new value does not make sense.  An optional
61258third argument may be used to specify in which layer to set the
61259configuration parameter.  The default layer is &quot;user&quot;.  The
61260configuration value will be set for the current channel, which
61261is controlled by the default_channel configuration variable.
61262</doc>
61263 </config-set>
61264 <config-help>
61265  <summary>Show Information About Setting</summary>
61266  <function>doConfigHelp</function>
61267  <shortcut>ch</shortcut>
61268  <options />
61269  <doc>[parameter]
61270Displays help for a configuration parameter.  Without arguments it
61271displays help for all configuration parameters.
61272</doc>
61273 </config-help>
61274 <config-create>
61275  <summary>Create a Default configuration file</summary>
61276  <function>doConfigCreate</function>
61277  <shortcut>coc</shortcut>
61278  <options>
61279   <windows>
61280    <shortopt>w</shortopt>
61281    <doc>create a config file for a windows install</doc>
61282   </windows>
61283  </options>
61284  <doc>&lt;root path&gt; &lt;filename&gt;
61285Create a default configuration file with all directory configuration
61286variables set to subdirectories of &lt;root path&gt;, and save it as &lt;filename&gt;.
61287This is useful especially for creating a configuration file for a remote
61288PEAR installation (using the --remoteconfig option of install, upgrade,
61289and uninstall).
61290</doc>
61291 </config-create>
61292</commands><?php
61293/**
61294 * PEAR_Command_Install (install, upgrade, upgrade-all, uninstall, bundle, run-scripts commands)
61295 *
61296 * PHP versions 4 and 5
61297 *
61298 * @category   pear
61299 * @package    PEAR
61300 * @author     Stig Bakken <ssb@php.net>
61301 * @author     Greg Beaver <cellog@php.net>
61302 * @copyright  1997-2009 The Authors
61303 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
61304 * @version    CVS: $Id: Install.php 313023 2011-07-06 19:17:11Z dufuz $
61305 * @link       http://pear.php.net/package/PEAR
61306 * @since      File available since Release 0.1
61307 */
61308
61309/**
61310 * base class
61311 */
61312require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Common.php';
61313
61314/**
61315 * PEAR commands for installation or deinstallation/upgrading of
61316 * packages.
61317 *
61318 * @category   pear
61319 * @package    PEAR
61320 * @author     Stig Bakken <ssb@php.net>
61321 * @author     Greg Beaver <cellog@php.net>
61322 * @copyright  1997-2009 The Authors
61323 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
61324 * @version    Release: 1.9.4
61325 * @link       http://pear.php.net/package/PEAR
61326 * @since      Class available since Release 0.1
61327 */
61328class PEAR_Command_Install extends PEAR_Command_Common
61329{
61330    // {{{ properties
61331
61332    var $commands = array(
61333        'install' => array(
61334            'summary' => 'Install Package',
61335            'function' => 'doInstall',
61336            'shortcut' => 'i',
61337            'options' => array(
61338                'force' => array(
61339                    'shortopt' => 'f',
61340                    'doc' => 'will overwrite newer installed packages',
61341                    ),
61342                'loose' => array(
61343                    'shortopt' => 'l',
61344                    'doc' => 'do not check for recommended dependency version',
61345                    ),
61346                'nodeps' => array(
61347                    'shortopt' => 'n',
61348                    'doc' => 'ignore dependencies, install anyway',
61349                    ),
61350                'register-only' => array(
61351                    'shortopt' => 'r',
61352                    'doc' => 'do not install files, only register the package as installed',
61353                    ),
61354                'soft' => array(
61355                    'shortopt' => 's',
61356                    'doc' => 'soft install, fail silently, or upgrade if already installed',
61357                    ),
61358                'nobuild' => array(
61359                    'shortopt' => 'B',
61360                    'doc' => 'don\'t build C extensions',
61361                    ),
61362                'nocompress' => array(
61363                    'shortopt' => 'Z',
61364                    'doc' => 'request uncompressed files when downloading',
61365                    ),
61366                'installroot' => array(
61367                    'shortopt' => 'R',
61368                    'arg' => 'DIR',
61369                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
61370                    ),
61371                'packagingroot' => array(
61372                    'shortopt' => 'P',
61373                    'arg' => 'DIR',
61374                    'doc' => 'root directory used when packaging files, like RPM packaging',
61375                    ),
61376                'ignore-errors' => array(
61377                    'doc' => 'force install even if there were errors',
61378                    ),
61379                'alldeps' => array(
61380                    'shortopt' => 'a',
61381                    'doc' => 'install all required and optional dependencies',
61382                    ),
61383                'onlyreqdeps' => array(
61384                    'shortopt' => 'o',
61385                    'doc' => 'install all required dependencies',
61386                    ),
61387                'offline' => array(
61388                    'shortopt' => 'O',
61389                    'doc' => 'do not attempt to download any urls or contact channels',
61390                    ),
61391                'pretend' => array(
61392                    'shortopt' => 'p',
61393                    'doc' => 'Only list the packages that would be downloaded',
61394                    ),
61395                ),
61396            'doc' => '[channel/]<package> ...
61397Installs one or more PEAR packages.  You can specify a package to
61398install in four ways:
61399
61400"Package-1.0.tgz" : installs from a local file
61401
61402"http://example.com/Package-1.0.tgz" : installs from
61403anywhere on the net.
61404
61405"package.xml" : installs the package described in
61406package.xml.  Useful for testing, or for wrapping a PEAR package in
61407another package manager such as RPM.
61408
61409"Package[-version/state][.tar]" : queries your default channel\'s server
61410({config master_server}) and downloads the newest package with
61411the preferred quality/state ({config preferred_state}).
61412
61413To retrieve Package version 1.1, use "Package-1.1," to retrieve
61414Package state beta, use "Package-beta."  To retrieve an uncompressed
61415file, append .tar (make sure there is no file by the same name first)
61416
61417To download a package from another channel, prefix with the channel name like
61418"channel/Package"
61419
61420More than one package may be specified at once.  It is ok to mix these
61421four ways of specifying packages.
61422'),
61423        'upgrade' => array(
61424            'summary' => 'Upgrade Package',
61425            'function' => 'doInstall',
61426            'shortcut' => 'up',
61427            'options' => array(
61428                'channel' => array(
61429                    'shortopt' => 'c',
61430                    'doc' => 'upgrade packages from a specific channel',
61431                    'arg' => 'CHAN',
61432                    ),
61433                'force' => array(
61434                    'shortopt' => 'f',
61435                    'doc' => 'overwrite newer installed packages',
61436                    ),
61437                'loose' => array(
61438                    'shortopt' => 'l',
61439                    'doc' => 'do not check for recommended dependency version',
61440                    ),
61441                'nodeps' => array(
61442                    'shortopt' => 'n',
61443                    'doc' => 'ignore dependencies, upgrade anyway',
61444                    ),
61445                'register-only' => array(
61446                    'shortopt' => 'r',
61447                    'doc' => 'do not install files, only register the package as upgraded',
61448                    ),
61449                'nobuild' => array(
61450                    'shortopt' => 'B',
61451                    'doc' => 'don\'t build C extensions',
61452                    ),
61453                'nocompress' => array(
61454                    'shortopt' => 'Z',
61455                    'doc' => 'request uncompressed files when downloading',
61456                    ),
61457                'installroot' => array(
61458                    'shortopt' => 'R',
61459                    'arg' => 'DIR',
61460                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
61461                    ),
61462                'ignore-errors' => array(
61463                    'doc' => 'force install even if there were errors',
61464                    ),
61465                'alldeps' => array(
61466                    'shortopt' => 'a',
61467                    'doc' => 'install all required and optional dependencies',
61468                    ),
61469                'onlyreqdeps' => array(
61470                    'shortopt' => 'o',
61471                    'doc' => 'install all required dependencies',
61472                    ),
61473                'offline' => array(
61474                    'shortopt' => 'O',
61475                    'doc' => 'do not attempt to download any urls or contact channels',
61476                    ),
61477                'pretend' => array(
61478                    'shortopt' => 'p',
61479                    'doc' => 'Only list the packages that would be downloaded',
61480                    ),
61481                ),
61482            'doc' => '<package> ...
61483Upgrades one or more PEAR packages.  See documentation for the
61484"install" command for ways to specify a package.
61485
61486When upgrading, your package will be updated if the provided new
61487package has a higher version number (use the -f option if you need to
61488upgrade anyway).
61489
61490More than one package may be specified at once.
61491'),
61492        'upgrade-all' => array(
61493            'summary' => 'Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]',
61494            'function' => 'doUpgradeAll',
61495            'shortcut' => 'ua',
61496            'options' => array(
61497                'channel' => array(
61498                    'shortopt' => 'c',
61499                    'doc' => 'upgrade packages from a specific channel',
61500                    'arg' => 'CHAN',
61501                    ),
61502                'nodeps' => array(
61503                    'shortopt' => 'n',
61504                    'doc' => 'ignore dependencies, upgrade anyway',
61505                    ),
61506                'register-only' => array(
61507                    'shortopt' => 'r',
61508                    'doc' => 'do not install files, only register the package as upgraded',
61509                    ),
61510                'nobuild' => array(
61511                    'shortopt' => 'B',
61512                    'doc' => 'don\'t build C extensions',
61513                    ),
61514                'nocompress' => array(
61515                    'shortopt' => 'Z',
61516                    'doc' => 'request uncompressed files when downloading',
61517                    ),
61518                'installroot' => array(
61519                    'shortopt' => 'R',
61520                    'arg' => 'DIR',
61521                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
61522                    ),
61523                'ignore-errors' => array(
61524                    'doc' => 'force install even if there were errors',
61525                    ),
61526                'loose' => array(
61527                    'doc' => 'do not check for recommended dependency version',
61528                    ),
61529                ),
61530            'doc' => '
61531WARNING: This function is deprecated in favor of using the upgrade command with no params
61532
61533Upgrades all packages that have a newer release available.  Upgrades are
61534done only if there is a release available of the state specified in
61535"preferred_state" (currently {config preferred_state}), or a state considered
61536more stable.
61537'),
61538        'uninstall' => array(
61539            'summary' => 'Un-install Package',
61540            'function' => 'doUninstall',
61541            'shortcut' => 'un',
61542            'options' => array(
61543                'nodeps' => array(
61544                    'shortopt' => 'n',
61545                    'doc' => 'ignore dependencies, uninstall anyway',
61546                    ),
61547                'register-only' => array(
61548                    'shortopt' => 'r',
61549                    'doc' => 'do not remove files, only register the packages as not installed',
61550                    ),
61551                'installroot' => array(
61552                    'shortopt' => 'R',
61553                    'arg' => 'DIR',
61554                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
61555                    ),
61556                'ignore-errors' => array(
61557                    'doc' => 'force install even if there were errors',
61558                    ),
61559                'offline' => array(
61560                    'shortopt' => 'O',
61561                    'doc' => 'do not attempt to uninstall remotely',
61562                    ),
61563                ),
61564            'doc' => '[channel/]<package> ...
61565Uninstalls one or more PEAR packages.  More than one package may be
61566specified at once.  Prefix with channel name to uninstall from a
61567channel not in your default channel ({config default_channel})
61568'),
61569        'bundle' => array(
61570            'summary' => 'Unpacks a Pecl Package',
61571            'function' => 'doBundle',
61572            'shortcut' => 'bun',
61573            'options' => array(
61574                'destination' => array(
61575                   'shortopt' => 'd',
61576                    'arg' => 'DIR',
61577                    'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)',
61578                    ),
61579                'force' => array(
61580                    'shortopt' => 'f',
61581                    'doc' => 'Force the unpacking even if there were errors in the package',
61582                ),
61583            ),
61584            'doc' => '<package>
61585Unpacks a Pecl Package into the selected location. It will download the
61586package if needed.
61587'),
61588        'run-scripts' => array(
61589            'summary' => 'Run Post-Install Scripts bundled with a package',
61590            'function' => 'doRunScripts',
61591            'shortcut' => 'rs',
61592            'options' => array(
61593            ),
61594            'doc' => '<package>
61595Run post-installation scripts in package <package>, if any exist.
61596'),
61597    );
61598
61599    // }}}
61600    // {{{ constructor
61601
61602    /**
61603     * PEAR_Command_Install constructor.
61604     *
61605     * @access public
61606     */
61607    function PEAR_Command_Install(&$ui, &$config)
61608    {
61609        parent::PEAR_Command_Common($ui, $config);
61610    }
61611
61612    // }}}
61613
61614    /**
61615     * For unit testing purposes
61616     */
61617    function &getDownloader(&$ui, $options, &$config)
61618    {
61619        if (!class_exists('PEAR_Downloader')) {
61620            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader.php';
61621        }
61622        $a = &new PEAR_Downloader($ui, $options, $config);
61623        return $a;
61624    }
61625
61626    /**
61627     * For unit testing purposes
61628     */
61629    function &getInstaller(&$ui)
61630    {
61631        if (!class_exists('PEAR_Installer')) {
61632            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Installer.php';
61633        }
61634        $a = &new PEAR_Installer($ui);
61635        return $a;
61636    }
61637
61638    function enableExtension($binaries, $type)
61639    {
61640        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
61641            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
61642        }
61643        $ini = $this->_parseIni($phpini);
61644        if (PEAR::isError($ini)) {
61645            return $ini;
61646        }
61647        $line = 0;
61648        if ($type == 'extsrc' || $type == 'extbin') {
61649            $search = 'extensions';
61650            $enable = 'extension';
61651        } else {
61652            $search = 'zend_extensions';
61653            ob_start();
61654            phpinfo(INFO_GENERAL);
61655            $info = ob_get_contents();
61656            ob_end_clean();
61657            $debug = function_exists('leak') ? '_debug' : '';
61658            $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
61659            $enable = 'zend_extension' . $debug . $ts;
61660        }
61661        foreach ($ini[$search] as $line => $extension) {
61662            if (in_array($extension, $binaries, true) || in_array(
61663                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
61664                // already enabled - assume if one is, all are
61665                return true;
61666            }
61667        }
61668        if ($line) {
61669            $newini = array_slice($ini['all'], 0, $line);
61670        } else {
61671            $newini = array();
61672        }
61673        foreach ($binaries as $binary) {
61674            if ($ini['extension_dir']) {
61675                $binary = basename($binary);
61676            }
61677            $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n");
61678        }
61679        $newini = array_merge($newini, array_slice($ini['all'], $line));
61680        $fp = @fopen($phpini, 'wb');
61681        if (!$fp) {
61682            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
61683        }
61684        foreach ($newini as $line) {
61685            fwrite($fp, $line);
61686        }
61687        fclose($fp);
61688        return true;
61689    }
61690
61691    function disableExtension($binaries, $type)
61692    {
61693        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
61694            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
61695        }
61696        $ini = $this->_parseIni($phpini);
61697        if (PEAR::isError($ini)) {
61698            return $ini;
61699        }
61700        $line = 0;
61701        if ($type == 'extsrc' || $type == 'extbin') {
61702            $search = 'extensions';
61703            $enable = 'extension';
61704        } else {
61705            $search = 'zend_extensions';
61706            ob_start();
61707            phpinfo(INFO_GENERAL);
61708            $info = ob_get_contents();
61709            ob_end_clean();
61710            $debug = function_exists('leak') ? '_debug' : '';
61711            $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
61712            $enable = 'zend_extension' . $debug . $ts;
61713        }
61714        $found = false;
61715        foreach ($ini[$search] as $line => $extension) {
61716            if (in_array($extension, $binaries, true) || in_array(
61717                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
61718                $found = true;
61719                break;
61720            }
61721        }
61722        if (!$found) {
61723            // not enabled
61724            return true;
61725        }
61726        $fp = @fopen($phpini, 'wb');
61727        if (!$fp) {
61728            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
61729        }
61730        if ($line) {
61731            $newini = array_slice($ini['all'], 0, $line);
61732            // delete the enable line
61733            $newini = array_merge($newini, array_slice($ini['all'], $line + 1));
61734        } else {
61735            $newini = array_slice($ini['all'], 1);
61736        }
61737        foreach ($newini as $line) {
61738            fwrite($fp, $line);
61739        }
61740        fclose($fp);
61741        return true;
61742    }
61743
61744    function _parseIni($filename)
61745    {
61746        if (!file_exists($filename)) {
61747            return PEAR::raiseError('php.ini "' . $filename . '" does not exist');
61748        }
61749
61750        if (filesize($filename) > 300000) {
61751            return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting');
61752        }
61753
61754        ob_start();
61755        phpinfo(INFO_GENERAL);
61756        $info = ob_get_contents();
61757        ob_end_clean();
61758        $debug = function_exists('leak') ? '_debug' : '';
61759        $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
61760        $zend_extension_line = 'zend_extension' . $debug . $ts;
61761        $all = @file($filename);
61762        if (!$all) {
61763            return PEAR::raiseError('php.ini "' . $filename .'" could not be read');
61764        }
61765        $zend_extensions = $extensions = array();
61766        // assume this is right, but pull from the php.ini if it is found
61767        $extension_dir = ini_get('extension_dir');
61768        foreach ($all as $linenum => $line) {
61769            $line = trim($line);
61770            if (!$line) {
61771                continue;
61772            }
61773            if ($line[0] == ';') {
61774                continue;
61775            }
61776            if (strtolower(substr($line, 0, 13)) == 'extension_dir') {
61777                $line = trim(substr($line, 13));
61778                if ($line[0] == '=') {
61779                    $x = trim(substr($line, 1));
61780                    $x = explode(';', $x);
61781                    $extension_dir = str_replace('"', '', array_shift($x));
61782                    continue;
61783                }
61784            }
61785            if (strtolower(substr($line, 0, 9)) == 'extension') {
61786                $line = trim(substr($line, 9));
61787                if ($line[0] == '=') {
61788                    $x = trim(substr($line, 1));
61789                    $x = explode(';', $x);
61790                    $extensions[$linenum] = str_replace('"', '', array_shift($x));
61791                    continue;
61792                }
61793            }
61794            if (strtolower(substr($line, 0, strlen($zend_extension_line))) ==
61795                  $zend_extension_line) {
61796                $line = trim(substr($line, strlen($zend_extension_line)));
61797                if ($line[0] == '=') {
61798                    $x = trim(substr($line, 1));
61799                    $x = explode(';', $x);
61800                    $zend_extensions[$linenum] = str_replace('"', '', array_shift($x));
61801                    continue;
61802                }
61803            }
61804        }
61805        return array(
61806            'extensions' => $extensions,
61807            'zend_extensions' => $zend_extensions,
61808            'extension_dir' => $extension_dir,
61809            'all' => $all,
61810        );
61811    }
61812
61813    // {{{ doInstall()
61814
61815    function doInstall($command, $options, $params)
61816    {
61817        if (!class_exists('PEAR_PackageFile')) {
61818            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
61819        }
61820
61821        if (isset($options['installroot']) && isset($options['packagingroot'])) {
61822            return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot');
61823        }
61824
61825        $reg = &$this->config->getRegistry();
61826        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
61827        if (!$reg->channelExists($channel)) {
61828            return $this->raiseError('Channel "' . $channel . '" does not exist');
61829        }
61830
61831        if (empty($this->installer)) {
61832            $this->installer = &$this->getInstaller($this->ui);
61833        }
61834
61835        if ($command == 'upgrade' || $command == 'upgrade-all') {
61836            // If people run the upgrade command but pass nothing, emulate a upgrade-all
61837            if ($command == 'upgrade' && empty($params)) {
61838                return $this->doUpgradeAll($command, $options, $params);
61839            }
61840            $options['upgrade'] = true;
61841        } else {
61842            $packages = $params;
61843        }
61844
61845        $instreg = &$reg; // instreg used to check if package is installed
61846        if (isset($options['packagingroot']) && !isset($options['upgrade'])) {
61847            $packrootphp_dir = $this->installer->_prependPath(
61848                $this->config->get('php_dir', null, 'pear.php.net'),
61849                $options['packagingroot']);
61850            $instreg = new PEAR_Registry($packrootphp_dir); // other instreg!
61851
61852            if ($this->config->get('verbose') > 2) {
61853                $this->ui->outputData('using package root: ' . $options['packagingroot']);
61854            }
61855        }
61856
61857        $abstractpackages = $otherpackages = array();
61858        // parse params
61859        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
61860
61861        foreach ($params as $param) {
61862            if (strpos($param, 'http://') === 0) {
61863                $otherpackages[] = $param;
61864                continue;
61865            }
61866
61867            if (strpos($param, 'channel://') === false && @file_exists($param)) {
61868                if (isset($options['force'])) {
61869                    $otherpackages[] = $param;
61870                    continue;
61871                }
61872
61873                $pkg = new PEAR_PackageFile($this->config);
61874                $pf  = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING);
61875                if (PEAR::isError($pf)) {
61876                    $otherpackages[] = $param;
61877                    continue;
61878                }
61879
61880                $exists   = $reg->packageExists($pf->getPackage(), $pf->getChannel());
61881                $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel());
61882                $version_compare = version_compare($pf->getVersion(), $pversion, '<=');
61883                if ($exists && $version_compare) {
61884                    if ($this->config->get('verbose')) {
61885                        $this->ui->outputData('Ignoring installed package ' .
61886                            $reg->parsedPackageNameToString(
61887                            array('package' => $pf->getPackage(),
61888                                  'channel' => $pf->getChannel()), true));
61889                    }
61890                    continue;
61891                }
61892                $otherpackages[] = $param;
61893                continue;
61894            }
61895
61896            $e = $reg->parsePackageName($param, $channel);
61897            if (PEAR::isError($e)) {
61898                $otherpackages[] = $param;
61899            } else {
61900                $abstractpackages[] = $e;
61901            }
61902        }
61903        PEAR::staticPopErrorHandling();
61904
61905        // if there are any local package .tgz or remote static url, we can't
61906        // filter.  The filter only works for abstract packages
61907        if (count($abstractpackages) && !isset($options['force'])) {
61908            // when not being forced, only do necessary upgrades/installs
61909            if (isset($options['upgrade'])) {
61910                $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command);
61911            } else {
61912                $count = count($abstractpackages);
61913                foreach ($abstractpackages as $i => $package) {
61914                    if (isset($package['group'])) {
61915                        // do not filter out install groups
61916                        continue;
61917                    }
61918
61919                    if ($instreg->packageExists($package['package'], $package['channel'])) {
61920                        if ($count > 1) {
61921                            if ($this->config->get('verbose')) {
61922                                $this->ui->outputData('Ignoring installed package ' .
61923                                    $reg->parsedPackageNameToString($package, true));
61924                            }
61925                            unset($abstractpackages[$i]);
61926                        } elseif ($count === 1) {
61927                            // Lets try to upgrade it since it's already installed
61928                            $options['upgrade'] = true;
61929                        }
61930                    }
61931                }
61932            }
61933            $abstractpackages =
61934                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
61935        } elseif (count($abstractpackages)) {
61936            $abstractpackages =
61937                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
61938        }
61939
61940        $packages = array_merge($abstractpackages, $otherpackages);
61941        if (!count($packages)) {
61942            $c = '';
61943            if (isset($options['channel'])){
61944                $c .= ' in channel "' . $options['channel'] . '"';
61945            }
61946            $this->ui->outputData('Nothing to ' . $command . $c);
61947            return true;
61948        }
61949
61950        $this->downloader = &$this->getDownloader($this->ui, $options, $this->config);
61951        $errors = $downloaded = $binaries = array();
61952        $downloaded = &$this->downloader->download($packages);
61953        if (PEAR::isError($downloaded)) {
61954            return $this->raiseError($downloaded);
61955        }
61956
61957        $errors = $this->downloader->getErrorMsgs();
61958        if (count($errors)) {
61959            $err = array();
61960            $err['data'] = array();
61961            foreach ($errors as $error) {
61962                if ($error !== null) {
61963                    $err['data'][] = array($error);
61964                }
61965            }
61966
61967            if (!empty($err['data'])) {
61968                $err['headline'] = 'Install Errors';
61969                $this->ui->outputData($err);
61970            }
61971
61972            if (!count($downloaded)) {
61973                return $this->raiseError("$command failed");
61974            }
61975        }
61976
61977        $data = array(
61978            'headline' => 'Packages that would be Installed'
61979        );
61980
61981        if (isset($options['pretend'])) {
61982            foreach ($downloaded as $package) {
61983                $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage()));
61984            }
61985            $this->ui->outputData($data, 'pretend');
61986            return true;
61987        }
61988
61989        $this->installer->setOptions($options);
61990        $this->installer->sortPackagesForInstall($downloaded);
61991        if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) {
61992            $this->raiseError($err->getMessage());
61993            return true;
61994        }
61995
61996        $binaries = $extrainfo = array();
61997        foreach ($downloaded as $param) {
61998            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
61999            $info = $this->installer->install($param, $options);
62000            PEAR::staticPopErrorHandling();
62001            if (PEAR::isError($info)) {
62002                $oldinfo = $info;
62003                $pkg = &$param->getPackageFile();
62004                if ($info->getCode() != PEAR_INSTALLER_NOBINARY) {
62005                    if (!($info = $pkg->installBinary($this->installer))) {
62006                        $this->ui->outputData('ERROR: ' .$oldinfo->getMessage());
62007                        continue;
62008                    }
62009
62010                    // we just installed a different package than requested,
62011                    // let's change the param and info so that the rest of this works
62012                    $param = $info[0];
62013                    $info  = $info[1];
62014                }
62015            }
62016
62017            if (!is_array($info)) {
62018                return $this->raiseError("$command failed");
62019            }
62020
62021            if ($param->getPackageType() == 'extsrc' ||
62022                  $param->getPackageType() == 'extbin' ||
62023                  $param->getPackageType() == 'zendextsrc' ||
62024                  $param->getPackageType() == 'zendextbin'
62025            ) {
62026                $pkg = &$param->getPackageFile();
62027                if ($instbin = $pkg->getInstalledBinary()) {
62028                    $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel());
62029                } else {
62030                    $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel());
62031                }
62032
62033                foreach ($instpkg->getFilelist() as $name => $atts) {
62034                    $pinfo = pathinfo($atts['installed_as']);
62035                    if (!isset($pinfo['extension']) ||
62036                          in_array($pinfo['extension'], array('c', 'h'))
62037                    ) {
62038                        continue; // make sure we don't match php_blah.h
62039                    }
62040
62041                    if ((strpos($pinfo['basename'], 'php_') === 0 &&
62042                          $pinfo['extension'] == 'dll') ||
62043                          // most unices
62044                          $pinfo['extension'] == 'so' ||
62045                          // hp-ux
62046                          $pinfo['extension'] == 'sl') {
62047                        $binaries[] = array($atts['installed_as'], $pinfo);
62048                        break;
62049                    }
62050                }
62051
62052                if (count($binaries)) {
62053                    foreach ($binaries as $pinfo) {
62054                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
62055                        $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType());
62056                        PEAR::staticPopErrorHandling();
62057                        if (PEAR::isError($ret)) {
62058                            $extrainfo[] = $ret->getMessage();
62059                            if ($param->getPackageType() == 'extsrc' ||
62060                                  $param->getPackageType() == 'extbin') {
62061                                $exttype = 'extension';
62062                            } else {
62063                                ob_start();
62064                                phpinfo(INFO_GENERAL);
62065                                $info = ob_get_contents();
62066                                ob_end_clean();
62067                                $debug = function_exists('leak') ? '_debug' : '';
62068                                $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
62069                                $exttype = 'zend_extension' . $debug . $ts;
62070                            }
62071                            $extrainfo[] = 'You should add "' . $exttype . '=' .
62072                                $pinfo[1]['basename'] . '" to php.ini';
62073                        } else {
62074                            $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() .
62075                                ' enabled in php.ini';
62076                        }
62077                    }
62078                }
62079            }
62080
62081            if ($this->config->get('verbose') > 0) {
62082                $chan = $param->getChannel();
62083                $label = $reg->parsedPackageNameToString(
62084                    array(
62085                        'channel' => $chan,
62086                        'package' => $param->getPackage(),
62087                        'version' => $param->getVersion(),
62088                    ));
62089                $out = array('data' => "$command ok: $label");
62090                if (isset($info['release_warnings'])) {
62091                    $out['release_warnings'] = $info['release_warnings'];
62092                }
62093                $this->ui->outputData($out, $command);
62094
62095                if (!isset($options['register-only']) && !isset($options['offline'])) {
62096                    if ($this->config->isDefinedLayer('ftp')) {
62097                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
62098                        $info = $this->installer->ftpInstall($param);
62099                        PEAR::staticPopErrorHandling();
62100                        if (PEAR::isError($info)) {
62101                            $this->ui->outputData($info->getMessage());
62102                            $this->ui->outputData("remote install failed: $label");
62103                        } else {
62104                            $this->ui->outputData("remote install ok: $label");
62105                        }
62106                    }
62107                }
62108            }
62109
62110            $deps = $param->getDeps();
62111            if ($deps) {
62112                if (isset($deps['group'])) {
62113                    $groups = $deps['group'];
62114                    if (!isset($groups[0])) {
62115                        $groups = array($groups);
62116                    }
62117
62118                    foreach ($groups as $group) {
62119                        if ($group['attribs']['name'] == 'default') {
62120                            // default group is always installed, unless the user
62121                            // explicitly chooses to install another group
62122                            continue;
62123                        }
62124                        $extrainfo[] = $param->getPackage() . ': Optional feature ' .
62125                            $group['attribs']['name'] . ' available (' .
62126                            $group['attribs']['hint'] . ')';
62127                    }
62128
62129                    $extrainfo[] = $param->getPackage() .
62130                        ': To install optional features use "pear install ' .
62131                        $reg->parsedPackageNameToString(
62132                            array('package' => $param->getPackage(),
62133                                  'channel' => $param->getChannel()), true) .
62134                              '#featurename"';
62135                }
62136            }
62137
62138            $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel());
62139            // $pkg may be NULL if install is a 'fake' install via --packagingroot
62140            if (is_object($pkg)) {
62141                $pkg->setConfig($this->config);
62142                if ($list = $pkg->listPostinstallScripts()) {
62143                    $pn = $reg->parsedPackageNameToString(array('channel' =>
62144                       $param->getChannel(), 'package' => $param->getPackage()), true);
62145                    $extrainfo[] = $pn . ' has post-install scripts:';
62146                    foreach ($list as $file) {
62147                        $extrainfo[] = $file;
62148                    }
62149                    $extrainfo[] = $param->getPackage() .
62150                        ': Use "pear run-scripts ' . $pn . '" to finish setup.';
62151                    $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES';
62152                }
62153            }
62154        }
62155
62156        if (count($extrainfo)) {
62157            foreach ($extrainfo as $info) {
62158                $this->ui->outputData($info);
62159            }
62160        }
62161
62162        return true;
62163    }
62164
62165    // }}}
62166    // {{{ doUpgradeAll()
62167
62168    function doUpgradeAll($command, $options, $params)
62169    {
62170        $reg = &$this->config->getRegistry();
62171        $upgrade = array();
62172
62173        if (isset($options['channel'])) {
62174            $channels = array($options['channel']);
62175        } else {
62176            $channels = $reg->listChannels();
62177        }
62178
62179        foreach ($channels as $channel) {
62180            if ($channel == '__uri') {
62181                continue;
62182            }
62183
62184            // parse name with channel
62185            foreach ($reg->listPackages($channel) as $name) {
62186                $upgrade[] = $reg->parsedPackageNameToString(array(
62187                        'channel' => $channel,
62188                        'package' => $name
62189                    ));
62190            }
62191        }
62192
62193        $err = $this->doInstall($command, $options, $upgrade);
62194        if (PEAR::isError($err)) {
62195            $this->ui->outputData($err->getMessage(), $command);
62196        }
62197   }
62198
62199    // }}}
62200    // {{{ doUninstall()
62201
62202    function doUninstall($command, $options, $params)
62203    {
62204        if (count($params) < 1) {
62205            return $this->raiseError("Please supply the package(s) you want to uninstall");
62206        }
62207
62208        if (empty($this->installer)) {
62209            $this->installer = &$this->getInstaller($this->ui);
62210        }
62211
62212        if (isset($options['remoteconfig'])) {
62213            $e = $this->config->readFTPConfigFile($options['remoteconfig']);
62214            if (!PEAR::isError($e)) {
62215                $this->installer->setConfig($this->config);
62216            }
62217        }
62218
62219        $reg = &$this->config->getRegistry();
62220        $newparams = array();
62221        $binaries = array();
62222        $badparams = array();
62223        foreach ($params as $pkg) {
62224            $channel = $this->config->get('default_channel');
62225            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
62226            $parsed = $reg->parsePackageName($pkg, $channel);
62227            PEAR::staticPopErrorHandling();
62228            if (!$parsed || PEAR::isError($parsed)) {
62229                $badparams[] = $pkg;
62230                continue;
62231            }
62232            $package = $parsed['package'];
62233            $channel = $parsed['channel'];
62234            $info = &$reg->getPackage($package, $channel);
62235            if ($info === null &&
62236                 ($channel == 'pear.php.net' || $channel == 'pecl.php.net')) {
62237                // make sure this isn't a package that has flipped from pear to pecl but
62238                // used a package.xml 1.0
62239                $testc = ($channel == 'pear.php.net') ? 'pecl.php.net' : 'pear.php.net';
62240                $info = &$reg->getPackage($package, $testc);
62241                if ($info !== null) {
62242                    $channel = $testc;
62243                }
62244            }
62245            if ($info === null) {
62246                $badparams[] = $pkg;
62247            } else {
62248                $newparams[] = &$info;
62249                // check for binary packages (this is an alias for those packages if so)
62250                if ($installedbinary = $info->getInstalledBinary()) {
62251                    $this->ui->log('adding binary package ' .
62252                        $reg->parsedPackageNameToString(array('channel' => $channel,
62253                            'package' => $installedbinary), true));
62254                    $newparams[] = &$reg->getPackage($installedbinary, $channel);
62255                }
62256                // add the contents of a dependency group to the list of installed packages
62257                if (isset($parsed['group'])) {
62258                    $group = $info->getDependencyGroup($parsed['group']);
62259                    if ($group) {
62260                        $installed = $reg->getInstalledGroup($group);
62261                        if ($installed) {
62262                            foreach ($installed as $i => $p) {
62263                                $newparams[] = &$installed[$i];
62264                            }
62265                        }
62266                    }
62267                }
62268            }
62269        }
62270        $err = $this->installer->sortPackagesForUninstall($newparams);
62271        if (PEAR::isError($err)) {
62272            $this->ui->outputData($err->getMessage(), $command);
62273            return true;
62274        }
62275        $params = $newparams;
62276        // twist this to use it to check on whether dependent packages are also being uninstalled
62277        // for circular dependencies like subpackages
62278        $this->installer->setUninstallPackages($newparams);
62279        $params = array_merge($params, $badparams);
62280        $binaries = array();
62281        foreach ($params as $pkg) {
62282            $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
62283            if ($err = $this->installer->uninstall($pkg, $options)) {
62284                $this->installer->popErrorHandling();
62285                if (PEAR::isError($err)) {
62286                    $this->ui->outputData($err->getMessage(), $command);
62287                    continue;
62288                }
62289                if ($pkg->getPackageType() == 'extsrc' ||
62290                      $pkg->getPackageType() == 'extbin' ||
62291                      $pkg->getPackageType() == 'zendextsrc' ||
62292                      $pkg->getPackageType() == 'zendextbin') {
62293                    if ($instbin = $pkg->getInstalledBinary()) {
62294                        continue; // this will be uninstalled later
62295                    }
62296
62297                    foreach ($pkg->getFilelist() as $name => $atts) {
62298                        $pinfo = pathinfo($atts['installed_as']);
62299                        if (!isset($pinfo['extension']) ||
62300                              in_array($pinfo['extension'], array('c', 'h'))) {
62301                            continue; // make sure we don't match php_blah.h
62302                        }
62303                        if ((strpos($pinfo['basename'], 'php_') === 0 &&
62304                              $pinfo['extension'] == 'dll') ||
62305                              // most unices
62306                              $pinfo['extension'] == 'so' ||
62307                              // hp-ux
62308                              $pinfo['extension'] == 'sl') {
62309                            $binaries[] = array($atts['installed_as'], $pinfo);
62310                            break;
62311                        }
62312                    }
62313                    if (count($binaries)) {
62314                        foreach ($binaries as $pinfo) {
62315                            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
62316                            $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType());
62317                            PEAR::staticPopErrorHandling();
62318                            if (PEAR::isError($ret)) {
62319                                $extrainfo[] = $ret->getMessage();
62320                                if ($pkg->getPackageType() == 'extsrc' ||
62321                                      $pkg->getPackageType() == 'extbin') {
62322                                    $exttype = 'extension';
62323                                } else {
62324                                    ob_start();
62325                                    phpinfo(INFO_GENERAL);
62326                                    $info = ob_get_contents();
62327                                    ob_end_clean();
62328                                    $debug = function_exists('leak') ? '_debug' : '';
62329                                    $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
62330                                    $exttype = 'zend_extension' . $debug . $ts;
62331                                }
62332                                $this->ui->outputData('Unable to remove "' . $exttype . '=' .
62333                                    $pinfo[1]['basename'] . '" from php.ini', $command);
62334                            } else {
62335                                $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() .
62336                                    ' disabled in php.ini', $command);
62337                            }
62338                        }
62339                    }
62340                }
62341                $savepkg = $pkg;
62342                if ($this->config->get('verbose') > 0) {
62343                    if (is_object($pkg)) {
62344                        $pkg = $reg->parsedPackageNameToString($pkg);
62345                    }
62346                    $this->ui->outputData("uninstall ok: $pkg", $command);
62347                }
62348                if (!isset($options['offline']) && is_object($savepkg) &&
62349                      defined('PEAR_REMOTEINSTALL_OK')) {
62350                    if ($this->config->isDefinedLayer('ftp')) {
62351                        $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
62352                        $info = $this->installer->ftpUninstall($savepkg);
62353                        $this->installer->popErrorHandling();
62354                        if (PEAR::isError($info)) {
62355                            $this->ui->outputData($info->getMessage());
62356                            $this->ui->outputData("remote uninstall failed: $pkg");
62357                        } else {
62358                            $this->ui->outputData("remote uninstall ok: $pkg");
62359                        }
62360                    }
62361                }
62362            } else {
62363                $this->installer->popErrorHandling();
62364                if (!is_object($pkg)) {
62365                    return $this->raiseError("uninstall failed: $pkg");
62366                }
62367                $pkg = $reg->parsedPackageNameToString($pkg);
62368            }
62369        }
62370
62371        return true;
62372    }
62373
62374    // }}}
62375
62376
62377    // }}}
62378    // {{{ doBundle()
62379    /*
62380    (cox) It just downloads and untars the package, does not do
62381            any check that the PEAR_Installer::_installFile() does.
62382    */
62383
62384    function doBundle($command, $options, $params)
62385    {
62386        $opts = array(
62387            'force'        => true,
62388            'nodeps'       => true,
62389            'soft'         => true,
62390            'downloadonly' => true
62391        );
62392        $downloader = &$this->getDownloader($this->ui, $opts, $this->config);
62393        $reg = &$this->config->getRegistry();
62394        if (count($params) < 1) {
62395            return $this->raiseError("Please supply the package you want to bundle");
62396        }
62397
62398        if (isset($options['destination'])) {
62399            if (!is_dir($options['destination'])) {
62400                System::mkdir('-p ' . $options['destination']);
62401            }
62402            $dest = realpath($options['destination']);
62403        } else {
62404            $pwd  = getcwd();
62405            $dir  = $pwd . DIRECTORY_SEPARATOR . 'ext';
62406            $dest = is_dir($dir) ? $dir : $pwd;
62407        }
62408        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
62409        $err = $downloader->setDownloadDir($dest);
62410        PEAR::staticPopErrorHandling();
62411        if (PEAR::isError($err)) {
62412            return PEAR::raiseError('download directory "' . $dest .
62413                '" is not writeable.');
62414        }
62415        $result = &$downloader->download(array($params[0]));
62416        if (PEAR::isError($result)) {
62417            return $result;
62418        }
62419        if (!isset($result[0])) {
62420            return $this->raiseError('unable to unpack ' . $params[0]);
62421        }
62422        $pkgfile = &$result[0]->getPackageFile();
62423        $pkgname = $pkgfile->getName();
62424        $pkgversion = $pkgfile->getVersion();
62425
62426        // Unpacking -------------------------------------------------
62427        $dest .= DIRECTORY_SEPARATOR . $pkgname;
62428        $orig = $pkgname . '-' . $pkgversion;
62429
62430        $tar = &new Archive_Tar($pkgfile->getArchiveFile());
62431        if (!$tar->extractModify($dest, $orig)) {
62432            return $this->raiseError('unable to unpack ' . $pkgfile->getArchiveFile());
62433        }
62434        $this->ui->outputData("Package ready at '$dest'");
62435    // }}}
62436    }
62437
62438    // }}}
62439
62440    function doRunScripts($command, $options, $params)
62441    {
62442        if (!isset($params[0])) {
62443            return $this->raiseError('run-scripts expects 1 parameter: a package name');
62444        }
62445
62446        $reg = &$this->config->getRegistry();
62447        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
62448        $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
62449        PEAR::staticPopErrorHandling();
62450        if (PEAR::isError($parsed)) {
62451            return $this->raiseError($parsed);
62452        }
62453
62454        $package = &$reg->getPackage($parsed['package'], $parsed['channel']);
62455        if (!is_object($package)) {
62456            return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry');
62457        }
62458
62459        $package->setConfig($this->config);
62460        $package->runPostinstallScripts();
62461        $this->ui->outputData('Install scripts complete', $command);
62462        return true;
62463    }
62464
62465    /**
62466     * Given a list of packages, filter out those ones that are already up to date
62467     *
62468     * @param $packages: packages, in parsed array format !
62469     * @return list of packages that can be upgraded
62470     */
62471    function _filterUptodatePackages($packages, $command)
62472    {
62473        $reg = &$this->config->getRegistry();
62474        $latestReleases = array();
62475
62476        $ret = array();
62477        foreach ($packages as $package) {
62478            if (isset($package['group'])) {
62479                $ret[] = $package;
62480                continue;
62481            }
62482
62483            $channel = $package['channel'];
62484            $name    = $package['package'];
62485            if (!$reg->packageExists($name, $channel)) {
62486                $ret[] = $package;
62487                continue;
62488            }
62489
62490            if (!isset($latestReleases[$channel])) {
62491                // fill in cache for this channel
62492                $chan = &$reg->getChannel($channel);
62493                if (PEAR::isError($chan)) {
62494                    return $this->raiseError($chan);
62495                }
62496
62497                $base2 = false;
62498                $preferred_mirror = $this->config->get('preferred_mirror', null, $channel);
62499                if ($chan->supportsREST($preferred_mirror) &&
62500                    (
62501                       //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) ||
62502                       ($base  = $chan->getBaseURL('REST1.0', $preferred_mirror))
62503                    )
62504                ) {
62505                    $dorest = true;
62506                }
62507
62508                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
62509                if (!isset($package['state'])) {
62510                    $state = $this->config->get('preferred_state', null, $channel);
62511                } else {
62512                    $state = $package['state'];
62513                }
62514
62515                if ($dorest) {
62516                    if ($base2) {
62517                        $rest = &$this->config->getREST('1.4', array());
62518                        $base = $base2;
62519                    } else {
62520                        $rest = &$this->config->getREST('1.0', array());
62521                    }
62522
62523                    $installed = array_flip($reg->listPackages($channel));
62524                    $latest    = $rest->listLatestUpgrades($base, $state, $installed, $channel, $reg);
62525                }
62526
62527                PEAR::staticPopErrorHandling();
62528                if (PEAR::isError($latest)) {
62529                    $this->ui->outputData('Error getting channel info from ' . $channel .
62530                        ': ' . $latest->getMessage());
62531                    continue;
62532                }
62533
62534                $latestReleases[$channel] = array_change_key_case($latest);
62535            }
62536
62537            // check package for latest release
62538            $name_lower = strtolower($name);
62539            if (isset($latestReleases[$channel][$name_lower])) {
62540                // if not set, up to date
62541                $inst_version    = $reg->packageInfo($name, 'version', $channel);
62542                $channel_version = $latestReleases[$channel][$name_lower]['version'];
62543                if (version_compare($channel_version, $inst_version, 'le')) {
62544                    // installed version is up-to-date
62545                    continue;
62546                }
62547
62548                // maintain BC
62549                if ($command == 'upgrade-all') {
62550                    $this->ui->outputData(array('data' => 'Will upgrade ' .
62551                        $reg->parsedPackageNameToString($package)), $command);
62552                }
62553                $ret[] = $package;
62554            }
62555        }
62556
62557        return $ret;
62558    }
62559}<commands version="1.0">
62560 <install>
62561  <summary>Install Package</summary>
62562  <function>doInstall</function>
62563  <shortcut>i</shortcut>
62564  <options>
62565   <force>
62566    <shortopt>f</shortopt>
62567    <doc>will overwrite newer installed packages</doc>
62568   </force>
62569   <loose>
62570    <shortopt>l</shortopt>
62571    <doc>do not check for recommended dependency version</doc>
62572   </loose>
62573   <nodeps>
62574    <shortopt>n</shortopt>
62575    <doc>ignore dependencies, install anyway</doc>
62576   </nodeps>
62577   <register-only>
62578    <shortopt>r</shortopt>
62579    <doc>do not install files, only register the package as installed</doc>
62580   </register-only>
62581   <soft>
62582    <shortopt>s</shortopt>
62583    <doc>soft install, fail silently, or upgrade if already installed</doc>
62584   </soft>
62585   <nobuild>
62586    <shortopt>B</shortopt>
62587    <doc>don&#039;t build C extensions</doc>
62588   </nobuild>
62589   <nocompress>
62590    <shortopt>Z</shortopt>
62591    <doc>request uncompressed files when downloading</doc>
62592   </nocompress>
62593   <installroot>
62594    <shortopt>R</shortopt>
62595    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT), use packagingroot for RPM</doc>
62596    <arg>DIR</arg>
62597   </installroot>
62598   <packagingroot>
62599    <shortopt>P</shortopt>
62600    <doc>root directory used when packaging files, like RPM packaging</doc>
62601    <arg>DIR</arg>
62602   </packagingroot>
62603   <ignore-errors>
62604    <shortopt></shortopt>
62605    <doc>force install even if there were errors</doc>
62606   </ignore-errors>
62607   <alldeps>
62608    <shortopt>a</shortopt>
62609    <doc>install all required and optional dependencies</doc>
62610   </alldeps>
62611   <onlyreqdeps>
62612    <shortopt>o</shortopt>
62613    <doc>install all required dependencies</doc>
62614   </onlyreqdeps>
62615   <offline>
62616    <shortopt>O</shortopt>
62617    <doc>do not attempt to download any urls or contact channels</doc>
62618   </offline>
62619   <pretend>
62620    <shortopt>p</shortopt>
62621    <doc>Only list the packages that would be downloaded</doc>
62622   </pretend>
62623  </options>
62624  <doc>[channel/]&lt;package&gt; ...
62625Installs one or more PEAR packages.  You can specify a package to
62626install in four ways:
62627
62628&quot;Package-1.0.tgz&quot; : installs from a local file
62629
62630&quot;http://example.com/Package-1.0.tgz"; : installs from
62631anywhere on the net.
62632
62633&quot;package.xml&quot; : installs the package described in
62634package.xml.  Useful for testing, or for wrapping a PEAR package in
62635another package manager such as RPM.
62636
62637&quot;Package[-version/state][.tar]&quot; : queries your default channel&#039;s server
62638({config master_server}) and downloads the newest package with
62639the preferred quality/state ({config preferred_state}).
62640
62641To retrieve Package version 1.1, use &quot;Package-1.1,&quot; to retrieve
62642Package state beta, use &quot;Package-beta.&quot;  To retrieve an uncompressed
62643file, append .tar (make sure there is no file by the same name first)
62644
62645To download a package from another channel, prefix with the channel name like
62646&quot;channel/Package&quot;
62647
62648More than one package may be specified at once.  It is ok to mix these
62649four ways of specifying packages.
62650</doc>
62651 </install>
62652 <upgrade>
62653  <summary>Upgrade Package</summary>
62654  <function>doInstall</function>
62655  <shortcut>up</shortcut>
62656  <options>
62657   <channel>
62658    <shortopt>c</shortopt>
62659    <doc>upgrade packages from a specific channel</doc>
62660    <arg>CHAN</arg>
62661   </channel>
62662   <force>
62663    <shortopt>f</shortopt>
62664    <doc>overwrite newer installed packages</doc>
62665   </force>
62666   <loose>
62667    <shortopt>l</shortopt>
62668    <doc>do not check for recommended dependency version</doc>
62669   </loose>
62670   <nodeps>
62671    <shortopt>n</shortopt>
62672    <doc>ignore dependencies, upgrade anyway</doc>
62673   </nodeps>
62674   <register-only>
62675    <shortopt>r</shortopt>
62676    <doc>do not install files, only register the package as upgraded</doc>
62677   </register-only>
62678   <nobuild>
62679    <shortopt>B</shortopt>
62680    <doc>don&#039;t build C extensions</doc>
62681   </nobuild>
62682   <nocompress>
62683    <shortopt>Z</shortopt>
62684    <doc>request uncompressed files when downloading</doc>
62685   </nocompress>
62686   <installroot>
62687    <shortopt>R</shortopt>
62688    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT)</doc>
62689    <arg>DIR</arg>
62690   </installroot>
62691   <ignore-errors>
62692    <shortopt></shortopt>
62693    <doc>force install even if there were errors</doc>
62694   </ignore-errors>
62695   <alldeps>
62696    <shortopt>a</shortopt>
62697    <doc>install all required and optional dependencies</doc>
62698   </alldeps>
62699   <onlyreqdeps>
62700    <shortopt>o</shortopt>
62701    <doc>install all required dependencies</doc>
62702   </onlyreqdeps>
62703   <offline>
62704    <shortopt>O</shortopt>
62705    <doc>do not attempt to download any urls or contact channels</doc>
62706   </offline>
62707   <pretend>
62708    <shortopt>p</shortopt>
62709    <doc>Only list the packages that would be downloaded</doc>
62710   </pretend>
62711  </options>
62712  <doc>&lt;package&gt; ...
62713Upgrades one or more PEAR packages.  See documentation for the
62714&quot;install&quot; command for ways to specify a package.
62715
62716When upgrading, your package will be updated if the provided new
62717package has a higher version number (use the -f option if you need to
62718upgrade anyway).
62719
62720More than one package may be specified at once.
62721</doc>
62722 </upgrade>
62723 <upgrade-all>
62724  <summary>Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]</summary>
62725  <function>doUpgradeAll</function>
62726  <shortcut>ua</shortcut>
62727  <options>
62728   <channel>
62729    <shortopt>c</shortopt>
62730    <doc>upgrade packages from a specific channel</doc>
62731    <arg>CHAN</arg>
62732   </channel>
62733   <nodeps>
62734    <shortopt>n</shortopt>
62735    <doc>ignore dependencies, upgrade anyway</doc>
62736   </nodeps>
62737   <register-only>
62738    <shortopt>r</shortopt>
62739    <doc>do not install files, only register the package as upgraded</doc>
62740   </register-only>
62741   <nobuild>
62742    <shortopt>B</shortopt>
62743    <doc>don&#039;t build C extensions</doc>
62744   </nobuild>
62745   <nocompress>
62746    <shortopt>Z</shortopt>
62747    <doc>request uncompressed files when downloading</doc>
62748   </nocompress>
62749   <installroot>
62750    <shortopt>R</shortopt>
62751    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT), use packagingroot for RPM</doc>
62752    <arg>DIR</arg>
62753   </installroot>
62754   <ignore-errors>
62755    <shortopt></shortopt>
62756    <doc>force install even if there were errors</doc>
62757   </ignore-errors>
62758   <loose>
62759    <shortopt></shortopt>
62760    <doc>do not check for recommended dependency version</doc>
62761   </loose>
62762  </options>
62763  <doc>
62764WARNING: This function is deprecated in favor of using the upgrade command with no params
62765
62766Upgrades all packages that have a newer release available.  Upgrades are
62767done only if there is a release available of the state specified in
62768&quot;preferred_state&quot; (currently {config preferred_state}), or a state considered
62769more stable.
62770</doc>
62771 </upgrade-all>
62772 <uninstall>
62773  <summary>Un-install Package</summary>
62774  <function>doUninstall</function>
62775  <shortcut>un</shortcut>
62776  <options>
62777   <nodeps>
62778    <shortopt>n</shortopt>
62779    <doc>ignore dependencies, uninstall anyway</doc>
62780   </nodeps>
62781   <register-only>
62782    <shortopt>r</shortopt>
62783    <doc>do not remove files, only register the packages as not installed</doc>
62784   </register-only>
62785   <installroot>
62786    <shortopt>R</shortopt>
62787    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT)</doc>
62788    <arg>DIR</arg>
62789   </installroot>
62790   <ignore-errors>
62791    <shortopt></shortopt>
62792    <doc>force install even if there were errors</doc>
62793   </ignore-errors>
62794   <offline>
62795    <shortopt>O</shortopt>
62796    <doc>do not attempt to uninstall remotely</doc>
62797   </offline>
62798  </options>
62799  <doc>[channel/]&lt;package&gt; ...
62800Uninstalls one or more PEAR packages.  More than one package may be
62801specified at once.  Prefix with channel name to uninstall from a
62802channel not in your default channel ({config default_channel})
62803</doc>
62804 </uninstall>
62805 <bundle>
62806  <summary>Unpacks a Pecl Package</summary>
62807  <function>doBundle</function>
62808  <shortcut>bun</shortcut>
62809  <options>
62810   <destination>
62811    <shortopt>d</shortopt>
62812    <doc>Optional destination directory for unpacking (defaults to current path or &quot;ext&quot; if exists)</doc>
62813    <arg>DIR</arg>
62814   </destination>
62815   <force>
62816    <shortopt>f</shortopt>
62817    <doc>Force the unpacking even if there were errors in the package</doc>
62818   </force>
62819  </options>
62820  <doc>&lt;package&gt;
62821Unpacks a Pecl Package into the selected location. It will download the
62822package if needed.
62823</doc>
62824 </bundle>
62825 <run-scripts>
62826  <summary>Run Post-Install Scripts bundled with a package</summary>
62827  <function>doRunScripts</function>
62828  <shortcut>rs</shortcut>
62829  <options />
62830  <doc>&lt;package&gt;
62831Run post-installation scripts in package &lt;package&gt;, if any exist.
62832</doc>
62833 </run-scripts>
62834</commands><?php
62835/**
62836 * PEAR_Command_Mirror (download-all command)
62837 *
62838 * PHP versions 4 and 5
62839 *
62840 * @category   pear
62841 * @package    PEAR
62842 * @author     Alexander Merz <alexmerz@php.net>
62843 * @copyright  1997-2009 The Authors
62844 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
62845 * @version    CVS: $Id: Mirror.php 313023 2011-07-06 19:17:11Z dufuz $
62846 * @link       http://pear.php.net/package/PEAR
62847 * @since      File available since Release 1.2.0
62848 */
62849
62850/**
62851 * base class
62852 */
62853require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Common.php';
62854
62855/**
62856 * PEAR commands for providing file mirrors
62857 *
62858 * @category   pear
62859 * @package    PEAR
62860 * @author     Alexander Merz <alexmerz@php.net>
62861 * @copyright  1997-2009 The Authors
62862 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
62863 * @version    Release: 1.9.4
62864 * @link       http://pear.php.net/package/PEAR
62865 * @since      Class available since Release 1.2.0
62866 */
62867class PEAR_Command_Mirror extends PEAR_Command_Common
62868{
62869    var $commands = array(
62870        'download-all' => array(
62871            'summary' => 'Downloads each available package from the default channel',
62872            'function' => 'doDownloadAll',
62873            'shortcut' => 'da',
62874            'options' => array(
62875                'channel' =>
62876                    array(
62877                    'shortopt' => 'c',
62878                    'doc' => 'specify a channel other than the default channel',
62879                    'arg' => 'CHAN',
62880                    ),
62881                ),
62882            'doc' => '
62883Requests a list of available packages from the default channel ({config default_channel})
62884and downloads them to current working directory.  Note: only
62885packages within preferred_state ({config preferred_state}) will be downloaded'
62886            ),
62887        );
62888
62889    /**
62890     * PEAR_Command_Mirror constructor.
62891     *
62892     * @access public
62893     * @param object PEAR_Frontend a reference to an frontend
62894     * @param object PEAR_Config a reference to the configuration data
62895     */
62896    function PEAR_Command_Mirror(&$ui, &$config)
62897    {
62898        parent::PEAR_Command_Common($ui, $config);
62899    }
62900
62901    /**
62902     * For unit-testing
62903     */
62904    function &factory($a)
62905    {
62906        $a = &PEAR_Command::factory($a, $this->config);
62907        return $a;
62908    }
62909
62910    /**
62911    * retrieves a list of avaible Packages from master server
62912    * and downloads them
62913    *
62914    * @access public
62915    * @param string $command the command
62916    * @param array $options the command options before the command
62917    * @param array $params the stuff after the command name
62918    * @return bool true if succesful
62919    * @throw PEAR_Error
62920    */
62921    function doDownloadAll($command, $options, $params)
62922    {
62923        $savechannel = $this->config->get('default_channel');
62924        $reg = &$this->config->getRegistry();
62925        $channel = isset($options['channel']) ? $options['channel'] :
62926            $this->config->get('default_channel');
62927        if (!$reg->channelExists($channel)) {
62928            $this->config->set('default_channel', $savechannel);
62929            return $this->raiseError('Channel "' . $channel . '" does not exist');
62930        }
62931        $this->config->set('default_channel', $channel);
62932
62933        $this->ui->outputData('Using Channel ' . $this->config->get('default_channel'));
62934        $chan = $reg->getChannel($channel);
62935        if (PEAR::isError($chan)) {
62936            return $this->raiseError($chan);
62937        }
62938
62939        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
62940              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
62941            $rest = &$this->config->getREST('1.0', array());
62942            $remoteInfo = array_flip($rest->listPackages($base, $channel));
62943        }
62944
62945        if (PEAR::isError($remoteInfo)) {
62946            return $remoteInfo;
62947        }
62948
62949        $cmd = &$this->factory("download");
62950        if (PEAR::isError($cmd)) {
62951            return $cmd;
62952        }
62953
62954        $this->ui->outputData('Using Preferred State of ' .
62955            $this->config->get('preferred_state'));
62956        $this->ui->outputData('Gathering release information, please wait...');
62957
62958        /**
62959         * Error handling not necessary, because already done by
62960         * the download command
62961         */
62962        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
62963        $err = $cmd->run('download', array('downloadonly' => true), array_keys($remoteInfo));
62964        PEAR::staticPopErrorHandling();
62965        $this->config->set('default_channel', $savechannel);
62966        if (PEAR::isError($err)) {
62967            $this->ui->outputData($err->getMessage());
62968        }
62969
62970        return true;
62971    }
62972}<commands version="1.0">
62973 <download-all>
62974  <summary>Downloads each available package from the default channel</summary>
62975  <function>doDownloadAll</function>
62976  <shortcut>da</shortcut>
62977  <options>
62978   <channel>
62979    <shortopt>c</shortopt>
62980    <doc>specify a channel other than the default channel</doc>
62981    <arg>CHAN</arg>
62982   </channel>
62983  </options>
62984  <doc>
62985Requests a list of available packages from the default channel ({config default_channel})
62986and downloads them to current working directory.  Note: only
62987packages within preferred_state ({config preferred_state}) will be downloaded</doc>
62988 </download-all>
62989</commands><?php
62990/**
62991 * PEAR_Command_Package (package, package-validate, cvsdiff, cvstag, package-dependencies,
62992 * sign, makerpm, convert commands)
62993 *
62994 * PHP versions 4 and 5
62995 *
62996 * @category   pear
62997 * @package    PEAR
62998 * @author     Stig Bakken <ssb@php.net>
62999 * @author     Martin Jansen <mj@php.net>
63000 * @author     Greg Beaver <cellog@php.net>
63001 * @copyright  1997-2009 The Authors
63002 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
63003 * @version    CVS: $Id: Package.php 313024 2011-07-06 19:51:24Z dufuz $
63004 * @link       http://pear.php.net/package/PEAR
63005 * @since      File available since Release 0.1
63006 */
63007
63008/**
63009 * base class
63010 */
63011require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Common.php';
63012
63013/**
63014 * PEAR commands for login/logout
63015 *
63016 * @category   pear
63017 * @package    PEAR
63018 * @author     Stig Bakken <ssb@php.net>
63019 * @author     Martin Jansen <mj@php.net>
63020 * @author     Greg Beaver <cellog@php.net>
63021 * @copyright  1997-2009 The Authors
63022 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
63023 * @version    Release: 1.10.0beta1
63024 * @link       http://pear.php.net/package/PEAR
63025 * @since      Class available since Release 0.1
63026 */
63027
63028class PEAR_Command_Package extends PEAR_Command_Common
63029{
63030    var $commands = array(
63031        'package' => array(
63032            'summary' => 'Build Package',
63033            'function' => 'doPackage',
63034            'shortcut' => 'p',
63035            'options' => array(
63036                'nocompress' => array(
63037                    'shortopt' => 'Z',
63038                    'doc' => 'Do not gzip the package file'
63039                    ),
63040                'showname' => array(
63041                    'shortopt' => 'n',
63042                    'doc' => 'Print the name of the packaged file.',
63043                    ),
63044                ),
63045            'doc' => '[descfile] [descfile2]
63046Creates a PEAR package from its description file (usually called
63047package.xml).  If a second packagefile is passed in, then
63048the packager will check to make sure that one is a package.xml
63049version 1.0, and the other is a package.xml version 2.0.  The
63050package.xml version 1.0 will be saved as "package.xml" in the archive,
63051and the other as "package2.xml" in the archive"
63052'
63053            ),
63054        'package-validate' => array(
63055            'summary' => 'Validate Package Consistency',
63056            'function' => 'doPackageValidate',
63057            'shortcut' => 'pv',
63058            'options' => array(),
63059            'doc' => '
63060',
63061            ),
63062        'cvsdiff' => array(
63063            'summary' => 'Run a "cvs diff" for all files in a package',
63064            'function' => 'doCvsDiff',
63065            'shortcut' => 'cd',
63066            'options' => array(
63067                'quiet' => array(
63068                    'shortopt' => 'q',
63069                    'doc' => 'Be quiet',
63070                    ),
63071                'reallyquiet' => array(
63072                    'shortopt' => 'Q',
63073                    'doc' => 'Be really quiet',
63074                    ),
63075                'date' => array(
63076                    'shortopt' => 'D',
63077                    'doc' => 'Diff against revision of DATE',
63078                    'arg' => 'DATE',
63079                    ),
63080                'release' => array(
63081                    'shortopt' => 'R',
63082                    'doc' => 'Diff against tag for package release REL',
63083                    'arg' => 'REL',
63084                    ),
63085                'revision' => array(
63086                    'shortopt' => 'r',
63087                    'doc' => 'Diff against revision REV',
63088                    'arg' => 'REV',
63089                    ),
63090                'context' => array(
63091                    'shortopt' => 'c',
63092                    'doc' => 'Generate context diff',
63093                    ),
63094                'unified' => array(
63095                    'shortopt' => 'u',
63096                    'doc' => 'Generate unified diff',
63097                    ),
63098                'ignore-case' => array(
63099                    'shortopt' => 'i',
63100                    'doc' => 'Ignore case, consider upper- and lower-case letters equivalent',
63101                    ),
63102                'ignore-whitespace' => array(
63103                    'shortopt' => 'b',
63104                    'doc' => 'Ignore changes in amount of white space',
63105                    ),
63106                'ignore-blank-lines' => array(
63107                    'shortopt' => 'B',
63108                    'doc' => 'Ignore changes that insert or delete blank lines',
63109                    ),
63110                'brief' => array(
63111                    'doc' => 'Report only whether the files differ, no details',
63112                    ),
63113                'dry-run' => array(
63114                    'shortopt' => 'n',
63115                    'doc' => 'Don\'t do anything, just pretend',
63116                    ),
63117                ),
63118            'doc' => '<package.xml>
63119Compares all the files in a package.  Without any options, this
63120command will compare the current code with the last checked-in code.
63121Using the -r or -R option you may compare the current code with that
63122of a specific release.
63123',
63124            ),
63125         'svntag' => array(
63126             'summary' => 'Set SVN Release Tag',
63127             'function' => 'doSvnTag',
63128             'shortcut' => 'sv',
63129             'options' => array(
63130                 'quiet' => array(
63131                     'shortopt' => 'q',
63132                     'doc' => 'Be quiet',
63133                     ),
63134                 'slide' => array(
63135                     'shortopt' => 'F',
63136                     'doc' => 'Move (slide) tag if it exists',
63137                     ),
63138                 'delete' => array(
63139                     'shortopt' => 'd',
63140                     'doc' => 'Remove tag',
63141                     ),
63142                 'dry-run' => array(
63143                     'shortopt' => 'n',
63144                     'doc' => 'Don\'t do anything, just pretend',
63145                     ),
63146                 ),
63147             'doc' => '<package.xml> [files...]
63148 Sets a SVN tag on all files in a package.  Use this command after you have
63149 packaged a distribution tarball with the "package" command to tag what
63150 revisions of what files were in that release.  If need to fix something
63151 after running svntag once, but before the tarball is released to the public,
63152 use the "slide" option to move the release tag.
63153
63154 to include files (such as a second package.xml, or tests not included in the
63155 release), pass them as additional parameters.
63156 ',
63157             ),
63158        'cvstag' => array(
63159            'summary' => 'Set CVS Release Tag',
63160            'function' => 'doCvsTag',
63161            'shortcut' => 'ct',
63162            'options' => array(
63163                'quiet' => array(
63164                    'shortopt' => 'q',
63165                    'doc' => 'Be quiet',
63166                    ),
63167                'reallyquiet' => array(
63168                    'shortopt' => 'Q',
63169                    'doc' => 'Be really quiet',
63170                    ),
63171                'slide' => array(
63172                    'shortopt' => 'F',
63173                    'doc' => 'Move (slide) tag if it exists',
63174                    ),
63175                'delete' => array(
63176                    'shortopt' => 'd',
63177                    'doc' => 'Remove tag',
63178                    ),
63179                'dry-run' => array(
63180                    'shortopt' => 'n',
63181                    'doc' => 'Don\'t do anything, just pretend',
63182                    ),
63183                ),
63184            'doc' => '<package.xml> [files...]
63185Sets a CVS tag on all files in a package.  Use this command after you have
63186packaged a distribution tarball with the "package" command to tag what
63187revisions of what files were in that release.  If need to fix something
63188after running cvstag once, but before the tarball is released to the public,
63189use the "slide" option to move the release tag.
63190
63191to include files (such as a second package.xml, or tests not included in the
63192release), pass them as additional parameters.
63193',
63194            ),
63195        'package-dependencies' => array(
63196            'summary' => 'Show package dependencies',
63197            'function' => 'doPackageDependencies',
63198            'shortcut' => 'pd',
63199            'options' => array(),
63200            'doc' => '<package-file> or <package.xml> or <install-package-name>
63201List all dependencies the package has.
63202Can take a tgz / tar file, package.xml or a package name of an installed package.'
63203            ),
63204        'sign' => array(
63205            'summary' => 'Sign a package distribution file',
63206            'function' => 'doSign',
63207            'shortcut' => 'si',
63208            'options' => array(
63209                'verbose' => array(
63210                    'shortopt' => 'v',
63211                    'doc' => 'Display GnuPG output',
63212                    ),
63213            ),
63214            'doc' => '<package-file>
63215Signs a package distribution (.tar or .tgz) file with GnuPG.',
63216            ),
63217        'makerpm' => array(
63218            'summary' => 'Builds an RPM spec file from a PEAR package',
63219            'function' => 'doMakeRPM',
63220            'shortcut' => 'rpm',
63221            'options' => array(
63222                'spec-template' => array(
63223                    'shortopt' => 't',
63224                    'arg' => 'FILE',
63225                    'doc' => 'Use FILE as RPM spec file template'
63226                    ),
63227                'rpm-pkgname' => array(
63228                    'shortopt' => 'p',
63229                    'arg' => 'FORMAT',
63230                    'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced
63231by the PEAR package name, defaults to "PEAR::%s".',
63232                    ),
63233                ),
63234            'doc' => '<package-file>
63235
63236Creates an RPM .spec file for wrapping a PEAR package inside an RPM
63237package.  Intended to be used from the SPECS directory, with the PEAR
63238package tarball in the SOURCES directory:
63239
63240$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
63241Wrote RPM spec file PEAR::Net_Geo-1.0.spec
63242$ rpm -bb PEAR::Net_Socket-1.0.spec
63243...
63244Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
63245',
63246            ),
63247        'convert' => array(
63248            'summary' => 'Convert a package.xml 1.0 to package.xml 2.0 format',
63249            'function' => 'doConvert',
63250            'shortcut' => 'c2',
63251            'options' => array(
63252                'flat' => array(
63253                    'shortopt' => 'f',
63254                    'doc' => 'do not beautify the filelist.',
63255                    ),
63256                ),
63257            'doc' => '[descfile] [descfile2]
63258Converts a package.xml in 1.0 format into a package.xml
63259in 2.0 format.  The new file will be named package2.xml by default,
63260and package.xml will be used as the old file by default.
63261This is not the most intelligent conversion, and should only be
63262used for automated conversion or learning the format.
63263'
63264            ),
63265        );
63266
63267    var $output;
63268
63269    /**
63270     * PEAR_Command_Package constructor.
63271     *
63272     * @access public
63273     */
63274    function PEAR_Command_Package(&$ui, &$config)
63275    {
63276        parent::PEAR_Command_Common($ui, $config);
63277    }
63278
63279    function _displayValidationResults($err, $warn, $strict = false)
63280    {
63281        foreach ($err as $e) {
63282            $this->output .= "Error: $e\n";
63283        }
63284        foreach ($warn as $w) {
63285            $this->output .= "Warning: $w\n";
63286        }
63287        $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n",
63288                                       sizeof($err), sizeof($warn));
63289        if ($strict && count($err) > 0) {
63290            $this->output .= "Fix these errors and try again.";
63291            return false;
63292        }
63293        return true;
63294    }
63295
63296    function &getPackager()
63297    {
63298        if (!class_exists('PEAR_Packager')) {
63299            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Packager.php';
63300        }
63301        $a = &new PEAR_Packager;
63302        return $a;
63303    }
63304
63305    function &getPackageFile($config, $debug = false)
63306    {
63307        if (!class_exists('PEAR_Common')) {
63308            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
63309        }
63310        if (!class_exists('PEAR_PackageFile')) {
63311            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
63312        }
63313        $a = &new PEAR_PackageFile($config, $debug);
63314        $common = new PEAR_Common;
63315        $common->ui = $this->ui;
63316        $a->setLogger($common);
63317        return $a;
63318    }
63319
63320    function doPackage($command, $options, $params)
63321    {
63322        $this->output = '';
63323        $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml';
63324        $pkg2 = isset($params[1]) ? $params[1] : null;
63325        if (!$pkg2 && !isset($params[0]) && file_exists('package2.xml')) {
63326            $pkg2 = 'package2.xml';
63327        }
63328
63329        $packager = &$this->getPackager();
63330        $compress = empty($options['nocompress']) ? true : false;
63331        $result   = $packager->package($pkginfofile, $compress, $pkg2);
63332        if (PEAR::isError($result)) {
63333            return $this->raiseError($result);
63334        }
63335
63336        // Don't want output, only the package file name just created
63337        if (isset($options['showname'])) {
63338            $this->output = $result;
63339        }
63340
63341        if ($this->output) {
63342            $this->ui->outputData($this->output, $command);
63343        }
63344
63345        return true;
63346    }
63347
63348    function doPackageValidate($command, $options, $params)
63349    {
63350        $this->output = '';
63351        if (count($params) < 1) {
63352            $params[0] = 'package.xml';
63353        }
63354
63355        $obj = &$this->getPackageFile($this->config, $this->_debug);
63356        $obj->rawReturn();
63357        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
63358        $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
63359        if (PEAR::isError($info)) {
63360            $info = $obj->fromPackageFile($params[0], PEAR_VALIDATE_NORMAL);
63361        } else {
63362            $archive = $info->getArchiveFile();
63363            $tar = &new Archive_Tar($archive);
63364            $tar->extract(dirname($info->getPackageFile()));
63365            $info->setPackageFile(dirname($info->getPackageFile()) . DIRECTORY_SEPARATOR .
63366                $info->getPackage() . '-' . $info->getVersion() . DIRECTORY_SEPARATOR .
63367                basename($info->getPackageFile()));
63368        }
63369
63370        PEAR::staticPopErrorHandling();
63371        if (PEAR::isError($info)) {
63372            return $this->raiseError($info);
63373        }
63374
63375        $valid = false;
63376        if ($info->getPackagexmlVersion() == '2.0') {
63377            if ($valid = $info->validate(PEAR_VALIDATE_NORMAL)) {
63378                $info->flattenFileList();
63379                $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
63380            }
63381        } else {
63382            $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
63383        }
63384
63385        $err = $warn = array();
63386        if ($errors = $info->getValidationWarnings()) {
63387            foreach ($errors as $error) {
63388                if ($error['level'] == 'warning') {
63389                    $warn[] = $error['message'];
63390                } else {
63391                    $err[] = $error['message'];
63392                }
63393            }
63394        }
63395
63396        $this->_displayValidationResults($err, $warn);
63397        $this->ui->outputData($this->output, $command);
63398        return true;
63399    }
63400
63401    function doSvnTag($command, $options, $params)
63402    {
63403        $this->output = '';
63404        $_cmd = $command;
63405        if (count($params) < 1) {
63406            $help = $this->getHelp($command);
63407            return $this->raiseError("$command: missing parameter: $help[0]");
63408        }
63409
63410        $packageFile = realpath($params[0]);
63411        $dir = dirname($packageFile);
63412        $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1);
63413        $obj  = &$this->getPackageFile($this->config, $this->_debug);
63414        $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL);
63415        if (PEAR::isError($info)) {
63416            return $this->raiseError($info);
63417        }
63418
63419        $err = $warn = array();
63420        if (!$info->validate()) {
63421            foreach ($info->getValidationWarnings() as $error) {
63422                if ($error['level'] == 'warning') {
63423                    $warn[] = $error['message'];
63424                } else {
63425                    $err[] = $error['message'];
63426                }
63427            }
63428        }
63429
63430        if (!$this->_displayValidationResults($err, $warn, true)) {
63431            $this->ui->outputData($this->output, $command);
63432            return $this->raiseError('SVN tag failed');
63433        }
63434
63435        $version    = $info->getVersion();
63436        $package    = $info->getName();
63437        $svntag     = "$package-$version";
63438
63439        if (isset($options['delete'])) {
63440            return $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options);
63441        }
63442
63443        $path = $this->_svnFindPath($packageFile);
63444
63445        // Check if there are any modified files
63446        $fp = popen('svn st --xml ' . dirname($packageFile), "r");
63447        $out = '';
63448        while ($line = fgets($fp, 1024)) {
63449            $out .= rtrim($line)."\n";
63450        }
63451        pclose($fp);
63452
63453        if (!isset($options['quiet']) && strpos($out, 'item="modified"')) {
63454            $params = array(array(
63455                'name' => 'modified',
63456                'type' => 'yesno',
63457                'default' => 'no',
63458                'prompt' => 'You have files in your SVN checkout (' . $path['from']  . ') that have been modified but not commited, do you still want to tag ' . $version . '?',
63459            ));
63460            $answers = $this->ui->confirmDialog($params);
63461
63462            if (!in_array($answers['modified'], array('y', 'yes', 'on', '1'))) {
63463                return true;
63464            }
63465        }
63466
63467        if (isset($options['slide'])) {
63468            $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options);
63469        }
63470
63471        // Check if tag already exists
63472        $releaseTag = $path['local']['base'] . 'tags' . DIRECTORY_SEPARATOR . $svntag;
63473        $existsCommand = 'svn ls ' . $path['base'] . 'tags/';
63474
63475        $fp = popen($existsCommand, "r");
63476        $out = '';
63477        while ($line = fgets($fp, 1024)) {
63478            $out .= rtrim($line)."\n";
63479        }
63480        pclose($fp);
63481
63482        if (in_array($svntag . DIRECTORY_SEPARATOR, explode("\n", $out))) {
63483            $this->ui->outputData($this->output, $command);
63484            return $this->raiseError('SVN tag ' . $svntag . ' for ' . $package . ' already exists.');
63485        } elseif (file_exists($path['local']['base'] . 'tags') === false) {
63486            return $this->raiseError('Can not locate the tags directory at ' . $path['local']['base'] . 'tags');
63487        } elseif (is_writeable($path['local']['base'] . 'tags') === false) {
63488            return $this->raiseError('Can not write to the tag directory at ' . $path['local']['base'] . 'tags');
63489        } else {
63490            $makeCommand = 'svn mkdir ' . $releaseTag;
63491            $this->output .= "+ $makeCommand\n";
63492            if (empty($options['dry-run'])) {
63493                // We need to create the tag dir.
63494                $fp = popen($makeCommand, "r");
63495                $out = '';
63496                while ($line = fgets($fp, 1024)) {
63497                    $out .= rtrim($line)."\n";
63498                }
63499                pclose($fp);
63500                $this->output .= "$out\n";
63501            }
63502        }
63503
63504        $command = 'svn';
63505        if (isset($options['quiet'])) {
63506            $command .= ' -q';
63507        }
63508
63509        $command .= ' copy --parents ';
63510
63511        $dir   = dirname($packageFile);
63512        $dir   = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1);
63513        $files = array_keys($info->getFilelist());
63514        if (!in_array(basename($packageFile), $files)) {
63515            $files[] = basename($packageFile);
63516        }
63517
63518        array_shift($params);
63519        if (count($params)) {
63520            // add in additional files to be tagged (package files and such)
63521            $files = array_merge($files, $params);
63522        }
63523
63524        $commands = array();
63525        foreach ($files as $file) {
63526            if (!file_exists($file)) {
63527                $file = $dir . DIRECTORY_SEPARATOR . $file;
63528            }
63529            $commands[] = $command . ' ' . escapeshellarg($file) . ' ' .
63530                          escapeshellarg($releaseTag . DIRECTORY_SEPARATOR . $file);
63531        }
63532
63533        $this->output .= implode("\n", $commands) . "\n";
63534        if (empty($options['dry-run'])) {
63535            foreach ($commands as $command) {
63536                $fp = popen($command, "r");
63537                while ($line = fgets($fp, 1024)) {
63538                    $this->output .= rtrim($line)."\n";
63539                }
63540                pclose($fp);
63541            }
63542        }
63543
63544        $command = 'svn ci -m "Tagging the ' . $version  . ' release" ' . $releaseTag . "\n";
63545        $this->output .= "+ $command\n";
63546        if (empty($options['dry-run'])) {
63547            $fp = popen($command, "r");
63548            while ($line = fgets($fp, 1024)) {
63549                $this->output .= rtrim($line)."\n";
63550            }
63551            pclose($fp);
63552        }
63553
63554        $this->ui->outputData($this->output, $_cmd);
63555        return true;
63556    }
63557
63558    function _svnFindPath($file)
63559    {
63560        $xml = '';
63561        $command = "svn info --xml $file";
63562        $fp = popen($command, "r");
63563        while ($line = fgets($fp, 1024)) {
63564            $xml .= rtrim($line)."\n";
63565        }
63566        pclose($fp);
63567        $url_tag = strpos($xml, '<url>');
63568        $url = substr($xml, $url_tag + 5, strpos($xml, '</url>', $url_tag + 5) - ($url_tag + 5));
63569
63570        $path = array();
63571        $path['from'] = substr($url, 0, strrpos($url, '/'));
63572        $path['base'] = substr($path['from'], 0, strrpos($path['from'], '/') + 1);
63573
63574        // Figure out the local paths - see http://pear.php.net/bugs/17463
63575        $pos = strpos($file, DIRECTORY_SEPARATOR . 'trunk' . DIRECTORY_SEPARATOR);
63576        if ($pos === false) {
63577            $pos = strpos($file, DIRECTORY_SEPARATOR . 'branches' . DIRECTORY_SEPARATOR);
63578        }
63579        $path['local']['base'] = substr($file, 0, $pos + 1);
63580
63581        return $path;
63582    }
63583
63584    function _svnRemoveTag($version, $package, $tag, $packageFile, $options)
63585    {
63586        $command = 'svn';
63587
63588        if (isset($options['quiet'])) {
63589            $command .= ' -q';
63590        }
63591
63592        $command .= ' remove';
63593        $command .= ' -m "Removing tag for the ' . $version  . ' release."';
63594
63595        $path = $this->_svnFindPath($packageFile);
63596        $command .= ' ' . $path['base'] . 'tags/' . $tag;
63597
63598
63599        if ($this->config->get('verbose') > 1) {
63600            $this->output .= "+ $command\n";
63601        }
63602
63603        $this->output .= "+ $command\n";
63604        if (empty($options['dry-run'])) {
63605            $fp = popen($command, "r");
63606            while ($line = fgets($fp, 1024)) {
63607                $this->output .= rtrim($line)."\n";
63608            }
63609            pclose($fp);
63610        }
63611
63612        $this->ui->outputData($this->output, $command);
63613        return true;
63614    }
63615
63616    function doCvsTag($command, $options, $params)
63617    {
63618        $this->output = '';
63619        $_cmd = $command;
63620        if (count($params) < 1) {
63621            $help = $this->getHelp($command);
63622            return $this->raiseError("$command: missing parameter: $help[0]");
63623        }
63624
63625        $packageFile = realpath($params[0]);
63626        $obj  = &$this->getPackageFile($this->config, $this->_debug);
63627        $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL);
63628        if (PEAR::isError($info)) {
63629            return $this->raiseError($info);
63630        }
63631
63632        $err = $warn = array();
63633        if (!$info->validate()) {
63634            foreach ($info->getValidationWarnings() as $error) {
63635                if ($error['level'] == 'warning') {
63636                    $warn[] = $error['message'];
63637                } else {
63638                    $err[] = $error['message'];
63639                }
63640            }
63641        }
63642
63643        if (!$this->_displayValidationResults($err, $warn, true)) {
63644            $this->ui->outputData($this->output, $command);
63645            return $this->raiseError('CVS tag failed');
63646        }
63647
63648        $version    = $info->getVersion();
63649        $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version);
63650        $cvstag     = "RELEASE_$cvsversion";
63651        $files      = array_keys($info->getFilelist());
63652        $command = 'cvs';
63653        if (isset($options['quiet'])) {
63654            $command .= ' -q';
63655        }
63656
63657        if (isset($options['reallyquiet'])) {
63658            $command .= ' -Q';
63659        }
63660
63661        $command .= ' tag';
63662        if (isset($options['slide'])) {
63663            $command .= ' -F';
63664        }
63665
63666        if (isset($options['delete'])) {
63667            $command .= ' -d';
63668        }
63669
63670        $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]);
63671        array_shift($params);
63672        if (count($params)) {
63673            // add in additional files to be tagged
63674            $files = array_merge($files, $params);
63675        }
63676
63677        $dir = dirname($packageFile);
63678        $dir = substr($dir, strrpos($dir, '/') + 1);
63679        foreach ($files as $file) {
63680            if (!file_exists($file)) {
63681                $file = $dir . DIRECTORY_SEPARATOR . $file;
63682            }
63683            $command .= ' ' . escapeshellarg($file);
63684        }
63685
63686        if ($this->config->get('verbose') > 1) {
63687            $this->output .= "+ $command\n";
63688        }
63689
63690        $this->output .= "+ $command\n";
63691        if (empty($options['dry-run'])) {
63692            $fp = popen($command, "r");
63693            while ($line = fgets($fp, 1024)) {
63694                $this->output .= rtrim($line)."\n";
63695            }
63696            pclose($fp);
63697        }
63698
63699        $this->ui->outputData($this->output, $_cmd);
63700        return true;
63701    }
63702
63703    function doCvsDiff($command, $options, $params)
63704    {
63705        $this->output = '';
63706        if (sizeof($params) < 1) {
63707            $help = $this->getHelp($command);
63708            return $this->raiseError("$command: missing parameter: $help[0]");
63709        }
63710
63711        $file = realpath($params[0]);
63712        $obj  = &$this->getPackageFile($this->config, $this->_debug);
63713        $info = $obj->fromAnyFile($file, PEAR_VALIDATE_NORMAL);
63714        if (PEAR::isError($info)) {
63715            return $this->raiseError($info);
63716        }
63717
63718        $err = $warn = array();
63719        if (!$info->validate()) {
63720            foreach ($info->getValidationWarnings() as $error) {
63721                if ($error['level'] == 'warning') {
63722                    $warn[] = $error['message'];
63723                } else {
63724                    $err[] = $error['message'];
63725                }
63726            }
63727        }
63728
63729        if (!$this->_displayValidationResults($err, $warn, true)) {
63730            $this->ui->outputData($this->output, $command);
63731            return $this->raiseError('CVS diff failed');
63732        }
63733
63734        $info1 = $info->getFilelist();
63735        $files = $info1;
63736        $cmd = "cvs";
63737        if (isset($options['quiet'])) {
63738            $cmd .= ' -q';
63739            unset($options['quiet']);
63740        }
63741
63742        if (isset($options['reallyquiet'])) {
63743            $cmd .= ' -Q';
63744            unset($options['reallyquiet']);
63745        }
63746
63747        if (isset($options['release'])) {
63748            $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']);
63749            $cvstag = "RELEASE_$cvsversion";
63750            $options['revision'] = $cvstag;
63751            unset($options['release']);
63752        }
63753
63754        $execute = true;
63755        if (isset($options['dry-run'])) {
63756            $execute = false;
63757            unset($options['dry-run']);
63758        }
63759
63760        $cmd .= ' diff';
63761        // the rest of the options are passed right on to "cvs diff"
63762        foreach ($options as $option => $optarg) {
63763            $arg = $short = false;
63764            if (isset($this->commands[$command]['options'][$option])) {
63765                $arg = $this->commands[$command]['options'][$option]['arg'];
63766                $short = $this->commands[$command]['options'][$option]['shortopt'];
63767            }
63768            $cmd .= $short ? " -$short" : " --$option";
63769            if ($arg && $optarg) {
63770                $cmd .= ($short ? '' : '=') . escapeshellarg($optarg);
63771            }
63772        }
63773
63774        foreach ($files as $file) {
63775            $cmd .= ' ' . escapeshellarg($file['name']);
63776        }
63777
63778        if ($this->config->get('verbose') > 1) {
63779            $this->output .= "+ $cmd\n";
63780        }
63781
63782        if ($execute) {
63783            $fp = popen($cmd, "r");
63784            while ($line = fgets($fp, 1024)) {
63785                $this->output .= rtrim($line)."\n";
63786            }
63787            pclose($fp);
63788        }
63789
63790        $this->ui->outputData($this->output, $command);
63791        return true;
63792    }
63793
63794    function doPackageDependencies($command, $options, $params)
63795    {
63796        // $params[0] -> the PEAR package to list its information
63797        if (count($params) !== 1) {
63798            return $this->raiseError("bad parameter(s), try \"help $command\"");
63799        }
63800
63801        $obj = &$this->getPackageFile($this->config, $this->_debug);
63802        if (is_file($params[0]) || strpos($params[0], '.xml') > 0) {
63803           $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
63804        } else {
63805            $reg  = $this->config->getRegistry();
63806            $info = $obj->fromArray($reg->packageInfo($params[0]));
63807        }
63808
63809        if (PEAR::isError($info)) {
63810            return $this->raiseError($info);
63811        }
63812
63813        $deps = $info->getDeps();
63814        if (is_array($deps)) {
63815            if ($info->getPackagexmlVersion() == '1.0') {
63816                $data = array(
63817                    'caption' => 'Dependencies for pear/' . $info->getPackage(),
63818                    'border' => true,
63819                    'headline' => array("Required?", "Type", "Name", "Relation", "Version"),
63820                    );
63821
63822                foreach ($deps as $d) {
63823                    if (isset($d['optional'])) {
63824                        if ($d['optional'] == 'yes') {
63825                            $req = 'No';
63826                        } else {
63827                            $req = 'Yes';
63828                        }
63829                    } else {
63830                        $req = 'Yes';
63831                    }
63832
63833                    if (isset($this->_deps_rel_trans[$d['rel']])) {
63834                        $rel = $this->_deps_rel_trans[$d['rel']];
63835                    } else {
63836                        $rel = $d['rel'];
63837                    }
63838
63839                    if (isset($this->_deps_type_trans[$d['type']])) {
63840                        $type = ucfirst($this->_deps_type_trans[$d['type']]);
63841                    } else {
63842                        $type = $d['type'];
63843                    }
63844
63845                    if (isset($d['name'])) {
63846                        $name = $d['name'];
63847                    } else {
63848                        $name = '';
63849                    }
63850
63851                    if (isset($d['version'])) {
63852                        $version = $d['version'];
63853                    } else {
63854                        $version = '';
63855                    }
63856
63857                    $data['data'][] = array($req, $type, $name, $rel, $version);
63858                }
63859            } else { // package.xml 2.0 dependencies display
63860                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
63861                $deps = $info->getDependencies();
63862                $reg = &$this->config->getRegistry();
63863                if (is_array($deps)) {
63864                    $d = new PEAR_Dependency2($this->config, array(), '');
63865                    $data = array(
63866                        'caption' => 'Dependencies for ' . $info->getPackage(),
63867                        'border' => true,
63868                        'headline' => array("Required?", "Type", "Name", 'Versioning', 'Group'),
63869                        );
63870                    foreach ($deps as $type => $subd) {
63871                        $req = ($type == 'required') ? 'Yes' : 'No';
63872                        if ($type == 'group') {
63873                            $group = $subd['attribs']['name'];
63874                        } else {
63875                            $group = '';
63876                        }
63877
63878                        if (!isset($subd[0])) {
63879                            $subd = array($subd);
63880                        }
63881
63882                        foreach ($subd as $groupa) {
63883                            foreach ($groupa as $deptype => $depinfo) {
63884                                if ($deptype == 'attribs') {
63885                                    continue;
63886                                }
63887
63888                                if ($deptype == 'pearinstaller') {
63889                                    $deptype = 'pear Installer';
63890                                }
63891
63892                                if (!isset($depinfo[0])) {
63893                                    $depinfo = array($depinfo);
63894                                }
63895
63896                                foreach ($depinfo as $inf) {
63897                                    $name = '';
63898                                    if (isset($inf['channel'])) {
63899                                        $alias = $reg->channelAlias($inf['channel']);
63900                                        if (!$alias) {
63901                                            $alias = '(channel?) ' .$inf['channel'];
63902                                        }
63903                                        $name = $alias . '/';
63904
63905                                    }
63906                                    if (isset($inf['name'])) {
63907                                        $name .= $inf['name'];
63908                                    } elseif (isset($inf['pattern'])) {
63909                                        $name .= $inf['pattern'];
63910                                    } else {
63911                                        $name .= '';
63912                                    }
63913
63914                                    if (isset($inf['uri'])) {
63915                                        $name .= ' [' . $inf['uri'] .  ']';
63916                                    }
63917
63918                                    if (isset($inf['conflicts'])) {
63919                                        $ver = 'conflicts';
63920                                    } else {
63921                                        $ver = $d->_getExtraString($inf);
63922                                    }
63923
63924                                    $data['data'][] = array($req, ucfirst($deptype), $name,
63925                                        $ver, $group);
63926                                }
63927                            }
63928                        }
63929                    }
63930                }
63931            }
63932
63933            $this->ui->outputData($data, $command);
63934            return true;
63935        }
63936
63937        // Fallback
63938        $this->ui->outputData("This package does not have any dependencies.", $command);
63939    }
63940
63941    function doSign($command, $options, $params)
63942    {
63943        // should move most of this code into PEAR_Packager
63944        // so it'll be easy to implement "pear package --sign"
63945        if (count($params) !== 1) {
63946            return $this->raiseError("bad parameter(s), try \"help $command\"");
63947        }
63948
63949        require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
63950        require_once 'phar://install-pear-nozlib.phar/' . 'Archive/Tar.php';
63951
63952        if (!file_exists($params[0])) {
63953            return $this->raiseError("file does not exist: $params[0]");
63954        }
63955
63956        $obj = $this->getPackageFile($this->config, $this->_debug);
63957        $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
63958        if (PEAR::isError($info)) {
63959            return $this->raiseError($info);
63960        }
63961
63962        $tar = new Archive_Tar($params[0]);
63963
63964        $tmpdir = $this->config->get('temp_dir');
63965        $tmpdir = System::mktemp(' -t "' . $tmpdir . '" -d pearsign');
63966        if (!$tar->extractList('package2.xml package.xml package.sig', $tmpdir)) {
63967            return $this->raiseError("failed to extract tar file");
63968        }
63969
63970        if (file_exists("$tmpdir/package.sig")) {
63971            return $this->raiseError("package already signed");
63972        }
63973
63974        $packagexml = 'package.xml';
63975        if (file_exists("$tmpdir/package2.xml")) {
63976            $packagexml = 'package2.xml';
63977        }
63978
63979        if (file_exists("$tmpdir/package.sig")) {
63980            unlink("$tmpdir/package.sig");
63981        }
63982
63983        if (!file_exists("$tmpdir/$packagexml")) {
63984            return $this->raiseError("Extracted file $tmpdir/$packagexml not found.");
63985        }
63986
63987        $input = $this->ui->userDialog($command,
63988                                       array('GnuPG Passphrase'),
63989                                       array('password'));
63990        if (!isset($input[0])) {
63991            //use empty passphrase
63992            $input[0] = '';
63993        }
63994
63995        $devnull = (isset($options['verbose'])) ? '' : ' 2>/dev/null';
63996        $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/$packagexml" . $devnull, "w");
63997        if (!$gpg) {
63998            return $this->raiseError("gpg command failed");
63999        }
64000
64001        fwrite($gpg, "$input[0]\n");
64002        if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) {
64003            return $this->raiseError("gpg sign failed");
64004        }
64005
64006        if (!$tar->addModify("$tmpdir/package.sig", '', $tmpdir)) {
64007            return $this->raiseError('failed adding signature to file');
64008        }
64009
64010        $this->ui->outputData("Package signed.", $command);
64011        return true;
64012    }
64013
64014    /**
64015     * For unit testing purposes
64016     */
64017    function &getInstaller(&$ui)
64018    {
64019        if (!class_exists('PEAR_Installer')) {
64020            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Installer.php';
64021        }
64022        $a = &new PEAR_Installer($ui);
64023        return $a;
64024    }
64025
64026    /**
64027     * For unit testing purposes
64028     */
64029    function &getCommandPackaging(&$ui, &$config)
64030    {
64031        if (!class_exists('PEAR_Command_Packaging')) {
64032            if ($fp = @fopen('PEAR/Command/Packaging.php', 'r', true)) {
64033                fclose($fp);
64034                include_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Packaging.php';
64035            }
64036        }
64037
64038        if (class_exists('PEAR_Command_Packaging')) {
64039            $a = &new PEAR_Command_Packaging($ui, $config);
64040        } else {
64041            $a = null;
64042        }
64043
64044        return $a;
64045    }
64046
64047    function doMakeRPM($command, $options, $params)
64048    {
64049
64050        // Check to see if PEAR_Command_Packaging is installed, and
64051        // transparently switch to use the "make-rpm-spec" command from it
64052        // instead, if it does. Otherwise, continue to use the old version
64053        // of "makerpm" supplied with this package (PEAR).
64054        $packaging_cmd = $this->getCommandPackaging($this->ui, $this->config);
64055        if ($packaging_cmd !== null) {
64056            $this->ui->outputData('PEAR_Command_Packaging is installed; using '.
64057                'newer "make-rpm-spec" command instead');
64058            return $packaging_cmd->run('make-rpm-spec', $options, $params);
64059        }
64060
64061        $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '.
64062          'improved version is available via "pear make-rpm-spec", which '.
64063          'is available by installing PEAR_Command_Packaging');
64064        return true;
64065    }
64066
64067    function doConvert($command, $options, $params)
64068    {
64069        $packagexml    = isset($params[0]) ? $params[0] : 'package.xml';
64070        $newpackagexml = isset($params[1]) ? $params[1] : dirname($packagexml) .
64071            DIRECTORY_SEPARATOR . 'package2.xml';
64072        $pkg = &$this->getPackageFile($this->config, $this->_debug);
64073        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
64074        $pf = $pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL);
64075        PEAR::staticPopErrorHandling();
64076        if (PEAR::isError($pf)) {
64077            if (is_array($pf->getUserInfo())) {
64078                foreach ($pf->getUserInfo() as $warning) {
64079                    $this->ui->outputData($warning['message']);
64080                }
64081            }
64082            return $this->raiseError($pf);
64083        }
64084
64085        if (is_a($pf, 'PEAR_PackageFile_v2')) {
64086            $this->ui->outputData($packagexml . ' is already a package.xml version 2.0');
64087            return true;
64088        }
64089
64090        $gen   = &$pf->getDefaultGenerator();
64091        $newpf = &$gen->toV2();
64092        $newpf->setPackagefile($newpackagexml);
64093        $gen = &$newpf->getDefaultGenerator();
64094        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
64095        $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL);
64096        $saved = $gen->toPackageFile(dirname($newpackagexml), $state, basename($newpackagexml));
64097        PEAR::staticPopErrorHandling();
64098        if (PEAR::isError($saved)) {
64099            if (is_array($saved->getUserInfo())) {
64100                foreach ($saved->getUserInfo() as $warning) {
64101                    $this->ui->outputData($warning['message']);
64102                }
64103            }
64104
64105            $this->ui->outputData($saved->getMessage());
64106            return true;
64107        }
64108
64109        $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"');
64110        return true;
64111    }
64112}<commands version="1.0">
64113 <package>
64114  <summary>Build Package</summary>
64115  <function>doPackage</function>
64116  <shortcut>p</shortcut>
64117  <options>
64118   <nocompress>
64119    <shortopt>Z</shortopt>
64120    <doc>Do not gzip the package file</doc>
64121   </nocompress>
64122   <showname>
64123    <shortopt>n</shortopt>
64124    <doc>Print the name of the packaged file.</doc>
64125   </showname>
64126  </options>
64127  <doc>[descfile] [descfile2]
64128Creates a PEAR package from its description file (usually called
64129package.xml).  If a second packagefile is passed in, then
64130the packager will check to make sure that one is a package.xml
64131version 1.0, and the other is a package.xml version 2.0.  The
64132package.xml version 1.0 will be saved as &quot;package.xml&quot; in the archive,
64133and the other as &quot;package2.xml&quot; in the archive&quot;
64134</doc>
64135 </package>
64136 <package-validate>
64137  <summary>Validate Package Consistency</summary>
64138  <function>doPackageValidate</function>
64139  <shortcut>pv</shortcut>
64140  <options />
64141  <doc>
64142</doc>
64143 </package-validate>
64144 <cvsdiff>
64145  <summary>Run a &quot;cvs diff&quot; for all files in a package</summary>
64146  <function>doCvsDiff</function>
64147  <shortcut>cd</shortcut>
64148  <options>
64149   <quiet>
64150    <shortopt>q</shortopt>
64151    <doc>Be quiet</doc>
64152   </quiet>
64153   <reallyquiet>
64154    <shortopt>Q</shortopt>
64155    <doc>Be really quiet</doc>
64156   </reallyquiet>
64157   <date>
64158    <shortopt>D</shortopt>
64159    <doc>Diff against revision of DATE</doc>
64160    <arg>DATE</arg>
64161   </date>
64162   <release>
64163    <shortopt>R</shortopt>
64164    <doc>Diff against tag for package release REL</doc>
64165    <arg>REL</arg>
64166   </release>
64167   <revision>
64168    <shortopt>r</shortopt>
64169    <doc>Diff against revision REV</doc>
64170    <arg>REV</arg>
64171   </revision>
64172   <context>
64173    <shortopt>c</shortopt>
64174    <doc>Generate context diff</doc>
64175   </context>
64176   <unified>
64177    <shortopt>u</shortopt>
64178    <doc>Generate unified diff</doc>
64179   </unified>
64180   <ignore-case>
64181    <shortopt>i</shortopt>
64182    <doc>Ignore case, consider upper- and lower-case letters equivalent</doc>
64183   </ignore-case>
64184   <ignore-whitespace>
64185    <shortopt>b</shortopt>
64186    <doc>Ignore changes in amount of white space</doc>
64187   </ignore-whitespace>
64188   <ignore-blank-lines>
64189    <shortopt>B</shortopt>
64190    <doc>Ignore changes that insert or delete blank lines</doc>
64191   </ignore-blank-lines>
64192   <brief>
64193    <shortopt></shortopt>
64194    <doc>Report only whether the files differ, no details</doc>
64195   </brief>
64196   <dry-run>
64197    <shortopt>n</shortopt>
64198    <doc>Don&#039;t do anything, just pretend</doc>
64199   </dry-run>
64200  </options>
64201  <doc>&lt;package.xml&gt;
64202Compares all the files in a package.  Without any options, this
64203command will compare the current code with the last checked-in code.
64204Using the -r or -R option you may compare the current code with that
64205of a specific release.
64206</doc>
64207 </cvsdiff>
64208 <svntag>
64209  <summary>Set SVN Release Tag</summary>
64210  <function>doSvnTag</function>
64211  <shortcut>sv</shortcut>
64212  <options>
64213   <quiet>
64214    <shortopt>q</shortopt>
64215    <doc>Be quiet</doc>
64216   </quiet>
64217   <slide>
64218    <shortopt>F</shortopt>
64219    <doc>Move (slide) tag if it exists</doc>
64220   </slide>
64221   <delete>
64222    <shortopt>d</shortopt>
64223    <doc>Remove tag</doc>
64224   </delete>
64225   <dry-run>
64226    <shortopt>n</shortopt>
64227    <doc>Don&#039;t do anything, just pretend</doc>
64228   </dry-run>
64229  </options>
64230  <doc>&lt;package.xml&gt; [files...]
64231 Sets a SVN tag on all files in a package.  Use this command after you have
64232 packaged a distribution tarball with the &quot;package&quot; command to tag what
64233 revisions of what files were in that release.  If need to fix something
64234 after running svntag once, but before the tarball is released to the public,
64235 use the &quot;slide&quot; option to move the release tag.
64236
64237 to include files (such as a second package.xml, or tests not included in the
64238 release), pass them as additional parameters.
64239 </doc>
64240 </svntag>
64241 <cvstag>
64242  <summary>Set CVS Release Tag</summary>
64243  <function>doCvsTag</function>
64244  <shortcut>ct</shortcut>
64245  <options>
64246   <quiet>
64247    <shortopt>q</shortopt>
64248    <doc>Be quiet</doc>
64249   </quiet>
64250   <reallyquiet>
64251    <shortopt>Q</shortopt>
64252    <doc>Be really quiet</doc>
64253   </reallyquiet>
64254   <slide>
64255    <shortopt>F</shortopt>
64256    <doc>Move (slide) tag if it exists</doc>
64257   </slide>
64258   <delete>
64259    <shortopt>d</shortopt>
64260    <doc>Remove tag</doc>
64261   </delete>
64262   <dry-run>
64263    <shortopt>n</shortopt>
64264    <doc>Don&#039;t do anything, just pretend</doc>
64265   </dry-run>
64266  </options>
64267  <doc>&lt;package.xml&gt; [files...]
64268Sets a CVS tag on all files in a package.  Use this command after you have
64269packaged a distribution tarball with the &quot;package&quot; command to tag what
64270revisions of what files were in that release.  If need to fix something
64271after running cvstag once, but before the tarball is released to the public,
64272use the &quot;slide&quot; option to move the release tag.
64273
64274to include files (such as a second package.xml, or tests not included in the
64275release), pass them as additional parameters.
64276</doc>
64277 </cvstag>
64278 <package-dependencies>
64279  <summary>Show package dependencies</summary>
64280  <function>doPackageDependencies</function>
64281  <shortcut>pd</shortcut>
64282  <options />
64283  <doc>&lt;package-file&gt; or &lt;package.xml&gt; or &lt;install-package-name&gt;
64284List all dependencies the package has.
64285Can take a tgz / tar file, package.xml or a package name of an installed package.</doc>
64286 </package-dependencies>
64287 <sign>
64288  <summary>Sign a package distribution file</summary>
64289  <function>doSign</function>
64290  <shortcut>si</shortcut>
64291  <options>
64292   <verbose>
64293    <shortopt>v</shortopt>
64294    <doc>Display GnuPG output</doc>
64295   </verbose>
64296  </options>
64297  <doc>&lt;package-file&gt;
64298Signs a package distribution (.tar or .tgz) file with GnuPG.</doc>
64299 </sign>
64300 <makerpm>
64301  <summary>Builds an RPM spec file from a PEAR package</summary>
64302  <function>doMakeRPM</function>
64303  <shortcut>rpm</shortcut>
64304  <options>
64305   <spec-template>
64306    <shortopt>t</shortopt>
64307    <doc>Use FILE as RPM spec file template</doc>
64308    <arg>FILE</arg>
64309   </spec-template>
64310   <rpm-pkgname>
64311    <shortopt>p</shortopt>
64312    <doc>Use FORMAT as format string for RPM package name, %s is replaced
64313by the PEAR package name, defaults to &quot;PEAR::%s&quot;.</doc>
64314    <arg>FORMAT</arg>
64315   </rpm-pkgname>
64316  </options>
64317  <doc>&lt;package-file&gt;
64318
64319Creates an RPM .spec file for wrapping a PEAR package inside an RPM
64320package.  Intended to be used from the SPECS directory, with the PEAR
64321package tarball in the SOURCES directory:
64322
64323$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
64324Wrote RPM spec file PEAR::Net_Geo-1.0.spec
64325$ rpm -bb PEAR::Net_Socket-1.0.spec
64326...
64327Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
64328</doc>
64329 </makerpm>
64330 <convert>
64331  <summary>Convert a package.xml 1.0 to package.xml 2.0 format</summary>
64332  <function>doConvert</function>
64333  <shortcut>c2</shortcut>
64334  <options>
64335   <flat>
64336    <shortopt>f</shortopt>
64337    <doc>do not beautify the filelist.</doc>
64338   </flat>
64339  </options>
64340  <doc>[descfile] [descfile2]
64341Converts a package.xml in 1.0 format into a package.xml
64342in 2.0 format.  The new file will be named package2.xml by default,
64343and package.xml will be used as the old file by default.
64344This is not the most intelligent conversion, and should only be
64345used for automated conversion or learning the format.
64346</doc>
64347 </convert>
64348</commands><?php
64349/**
64350 * PEAR_Command_Pickle (pickle command)
64351 *
64352 * PHP versions 4 and 5
64353 *
64354 * @category   pear
64355 * @package    PEAR
64356 * @author     Greg Beaver <cellog@php.net>
64357 * @copyright  2005-2009 The Authors
64358 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
64359 * @version    CVS: $Id: Pickle.php 313023 2011-07-06 19:17:11Z dufuz $
64360 * @link       http://pear.php.net/package/PEAR
64361 * @since      File available since Release 1.4.1
64362 */
64363
64364/**
64365 * base class
64366 */
64367require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Common.php';
64368
64369/**
64370 * PEAR commands for login/logout
64371 *
64372 * @category   pear
64373 * @package    PEAR
64374 * @author     Greg Beaver <cellog@php.net>
64375 * @copyright  2005-2009 The Authors
64376 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
64377 * @version    Release: 1.9.4
64378 * @link       http://pear.php.net/package/PEAR
64379 * @since      Class available since Release 1.4.1
64380 */
64381
64382class PEAR_Command_Pickle extends PEAR_Command_Common
64383{
64384    var $commands = array(
64385        'pickle' => array(
64386            'summary' => 'Build PECL Package',
64387            'function' => 'doPackage',
64388            'shortcut' => 'pi',
64389            'options' => array(
64390                'nocompress' => array(
64391                    'shortopt' => 'Z',
64392                    'doc' => 'Do not gzip the package file'
64393                    ),
64394                'showname' => array(
64395                    'shortopt' => 'n',
64396                    'doc' => 'Print the name of the packaged file.',
64397                    ),
64398                ),
64399            'doc' => '[descfile]
64400Creates a PECL package from its package2.xml file.
64401
64402An automatic conversion will be made to a package.xml 1.0 and written out to
64403disk in the current directory as "package.xml".  Note that
64404only simple package.xml 2.0 will be converted.  package.xml 2.0 with:
64405
64406 - dependency types other than required/optional PECL package/ext/php/pearinstaller
64407 - more than one extsrcrelease or zendextsrcrelease
64408 - zendextbinrelease, extbinrelease, phprelease, or bundle release type
64409 - dependency groups
64410 - ignore tags in release filelist
64411 - tasks other than replace
64412 - custom roles
64413
64414will cause pickle to fail, and output an error message.  If your package2.xml
64415uses any of these features, you are best off using PEAR_PackageFileManager to
64416generate both package.xml.
64417'
64418            ),
64419        );
64420
64421    /**
64422     * PEAR_Command_Package constructor.
64423     *
64424     * @access public
64425     */
64426    function PEAR_Command_Pickle(&$ui, &$config)
64427    {
64428        parent::PEAR_Command_Common($ui, $config);
64429    }
64430
64431    /**
64432     * For unit-testing ease
64433     *
64434     * @return PEAR_Packager
64435     */
64436    function &getPackager()
64437    {
64438        if (!class_exists('PEAR_Packager')) {
64439            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Packager.php';
64440        }
64441
64442        $a = &new PEAR_Packager;
64443        return $a;
64444    }
64445
64446    /**
64447     * For unit-testing ease
64448     *
64449     * @param PEAR_Config $config
64450     * @param bool $debug
64451     * @param string|null $tmpdir
64452     * @return PEAR_PackageFile
64453     */
64454    function &getPackageFile($config, $debug = false)
64455    {
64456        if (!class_exists('PEAR_Common')) {
64457            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
64458        }
64459
64460        if (!class_exists('PEAR_PackageFile')) {
64461            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
64462        }
64463
64464        $a = &new PEAR_PackageFile($config, $debug);
64465        $common = new PEAR_Common;
64466        $common->ui = $this->ui;
64467        $a->setLogger($common);
64468        return $a;
64469    }
64470
64471    function doPackage($command, $options, $params)
64472    {
64473        $this->output = '';
64474        $pkginfofile = isset($params[0]) ? $params[0] : 'package2.xml';
64475        $packager = &$this->getPackager();
64476        if (PEAR::isError($err = $this->_convertPackage($pkginfofile))) {
64477            return $err;
64478        }
64479
64480        $compress = empty($options['nocompress']) ? true : false;
64481        $result = $packager->package($pkginfofile, $compress, 'package.xml');
64482        if (PEAR::isError($result)) {
64483            return $this->raiseError($result);
64484        }
64485
64486        // Don't want output, only the package file name just created
64487        if (isset($options['showname'])) {
64488            $this->ui->outputData($result, $command);
64489        }
64490
64491        return true;
64492    }
64493
64494    function _convertPackage($packagexml)
64495    {
64496        $pkg = &$this->getPackageFile($this->config);
64497        $pf2 = &$pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL);
64498        if (!is_a($pf2, 'PEAR_PackageFile_v2')) {
64499            return $this->raiseError('Cannot process "' .
64500                $packagexml . '", is not a package.xml 2.0');
64501        }
64502
64503        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
64504        $pf = new PEAR_PackageFile_v1;
64505        $pf->setConfig($this->config);
64506        if ($pf2->getPackageType() != 'extsrc' && $pf2->getPackageType() != 'zendextsrc') {
64507            return $this->raiseError('Cannot safely convert "' . $packagexml .
64508            '", is not an extension source package.  Using a PEAR_PackageFileManager-based ' .
64509            'script is an option');
64510        }
64511
64512        if (is_array($pf2->getUsesRole())) {
64513            return $this->raiseError('Cannot safely convert "' . $packagexml .
64514            '", contains custom roles.  Using a PEAR_PackageFileManager-based script or ' .
64515            'the convert command is an option');
64516        }
64517
64518        if (is_array($pf2->getUsesTask())) {
64519            return $this->raiseError('Cannot safely convert "' . $packagexml .
64520            '", contains custom tasks.  Using a PEAR_PackageFileManager-based script or ' .
64521            'the convert command is an option');
64522        }
64523
64524        $deps = $pf2->getDependencies();
64525        if (isset($deps['group'])) {
64526            return $this->raiseError('Cannot safely convert "' . $packagexml .
64527            '", contains dependency groups.  Using a PEAR_PackageFileManager-based script ' .
64528            'or the convert command is an option');
64529        }
64530
64531        if (isset($deps['required']['subpackage']) ||
64532              isset($deps['optional']['subpackage'])) {
64533            return $this->raiseError('Cannot safely convert "' . $packagexml .
64534            '", contains subpackage dependencies.  Using a PEAR_PackageFileManager-based  '.
64535            'script is an option');
64536        }
64537
64538        if (isset($deps['required']['os'])) {
64539            return $this->raiseError('Cannot safely convert "' . $packagexml .
64540            '", contains os dependencies.  Using a PEAR_PackageFileManager-based  '.
64541            'script is an option');
64542        }
64543
64544        if (isset($deps['required']['arch'])) {
64545            return $this->raiseError('Cannot safely convert "' . $packagexml .
64546            '", contains arch dependencies.  Using a PEAR_PackageFileManager-based  '.
64547            'script is an option');
64548        }
64549
64550        $pf->setPackage($pf2->getPackage());
64551        $pf->setSummary($pf2->getSummary());
64552        $pf->setDescription($pf2->getDescription());
64553        foreach ($pf2->getMaintainers() as $maintainer) {
64554            $pf->addMaintainer($maintainer['role'], $maintainer['handle'],
64555                $maintainer['name'], $maintainer['email']);
64556        }
64557
64558        $pf->setVersion($pf2->getVersion());
64559        $pf->setDate($pf2->getDate());
64560        $pf->setLicense($pf2->getLicense());
64561        $pf->setState($pf2->getState());
64562        $pf->setNotes($pf2->getNotes());
64563        $pf->addPhpDep($deps['required']['php']['min'], 'ge');
64564        if (isset($deps['required']['php']['max'])) {
64565            $pf->addPhpDep($deps['required']['php']['max'], 'le');
64566        }
64567
64568        if (isset($deps['required']['package'])) {
64569            if (!isset($deps['required']['package'][0])) {
64570                $deps['required']['package'] = array($deps['required']['package']);
64571            }
64572
64573            foreach ($deps['required']['package'] as $dep) {
64574                if (!isset($dep['channel'])) {
64575                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
64576                    ' contains uri-based dependency on a package.  Using a ' .
64577                    'PEAR_PackageFileManager-based script is an option');
64578                }
64579
64580                if ($dep['channel'] != 'pear.php.net'
64581                    && $dep['channel'] != 'pecl.php.net'
64582                    && $dep['channel'] != 'doc.php.net') {
64583                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
64584                    ' contains dependency on a non-standard channel package.  Using a ' .
64585                    'PEAR_PackageFileManager-based script is an option');
64586                }
64587
64588                if (isset($dep['conflicts'])) {
64589                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
64590                    ' contains conflicts dependency.  Using a ' .
64591                    'PEAR_PackageFileManager-based script is an option');
64592                }
64593
64594                if (isset($dep['exclude'])) {
64595                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
64596                }
64597
64598                if (isset($dep['min'])) {
64599                    $pf->addPackageDep($dep['name'], $dep['min'], 'ge');
64600                }
64601
64602                if (isset($dep['max'])) {
64603                    $pf->addPackageDep($dep['name'], $dep['max'], 'le');
64604                }
64605            }
64606        }
64607
64608        if (isset($deps['required']['extension'])) {
64609            if (!isset($deps['required']['extension'][0])) {
64610                $deps['required']['extension'] = array($deps['required']['extension']);
64611            }
64612
64613            foreach ($deps['required']['extension'] as $dep) {
64614                if (isset($dep['conflicts'])) {
64615                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
64616                    ' contains conflicts dependency.  Using a ' .
64617                    'PEAR_PackageFileManager-based script is an option');
64618                }
64619
64620                if (isset($dep['exclude'])) {
64621                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
64622                }
64623
64624                if (isset($dep['min'])) {
64625                    $pf->addExtensionDep($dep['name'], $dep['min'], 'ge');
64626                }
64627
64628                if (isset($dep['max'])) {
64629                    $pf->addExtensionDep($dep['name'], $dep['max'], 'le');
64630                }
64631            }
64632        }
64633
64634        if (isset($deps['optional']['package'])) {
64635            if (!isset($deps['optional']['package'][0])) {
64636                $deps['optional']['package'] = array($deps['optional']['package']);
64637            }
64638
64639            foreach ($deps['optional']['package'] as $dep) {
64640                if (!isset($dep['channel'])) {
64641                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
64642                    ' contains uri-based dependency on a package.  Using a ' .
64643                    'PEAR_PackageFileManager-based script is an option');
64644                }
64645
64646                if ($dep['channel'] != 'pear.php.net'
64647                    && $dep['channel'] != 'pecl.php.net'
64648                    && $dep['channel'] != 'doc.php.net') {
64649                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
64650                    ' contains dependency on a non-standard channel package.  Using a ' .
64651                    'PEAR_PackageFileManager-based script is an option');
64652                }
64653
64654                if (isset($dep['exclude'])) {
64655                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
64656                }
64657
64658                if (isset($dep['min'])) {
64659                    $pf->addPackageDep($dep['name'], $dep['min'], 'ge', 'yes');
64660                }
64661
64662                if (isset($dep['max'])) {
64663                    $pf->addPackageDep($dep['name'], $dep['max'], 'le', 'yes');
64664                }
64665            }
64666        }
64667
64668        if (isset($deps['optional']['extension'])) {
64669            if (!isset($deps['optional']['extension'][0])) {
64670                $deps['optional']['extension'] = array($deps['optional']['extension']);
64671            }
64672
64673            foreach ($deps['optional']['extension'] as $dep) {
64674                if (isset($dep['exclude'])) {
64675                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
64676                }
64677
64678                if (isset($dep['min'])) {
64679                    $pf->addExtensionDep($dep['name'], $dep['min'], 'ge', 'yes');
64680                }
64681
64682                if (isset($dep['max'])) {
64683                    $pf->addExtensionDep($dep['name'], $dep['max'], 'le', 'yes');
64684                }
64685            }
64686        }
64687
64688        $contents = $pf2->getContents();
64689        $release  = $pf2->getReleases();
64690        if (isset($releases[0])) {
64691            return $this->raiseError('Cannot safely process "' . $packagexml . '" contains '
64692            . 'multiple extsrcrelease/zendextsrcrelease tags.  Using a PEAR_PackageFileManager-based script ' .
64693            'or the convert command is an option');
64694        }
64695
64696        if ($configoptions = $pf2->getConfigureOptions()) {
64697            foreach ($configoptions as $option) {
64698                $default = isset($option['default']) ? $option['default'] : false;
64699                $pf->addConfigureOption($option['name'], $option['prompt'], $default);
64700            }
64701        }
64702
64703        if (isset($release['filelist']['ignore'])) {
64704            return $this->raiseError('Cannot safely process "' . $packagexml . '" contains '
64705            . 'ignore tags.  Using a PEAR_PackageFileManager-based script or the convert' .
64706            ' command is an option');
64707        }
64708
64709        if (isset($release['filelist']['install']) &&
64710              !isset($release['filelist']['install'][0])) {
64711            $release['filelist']['install'] = array($release['filelist']['install']);
64712        }
64713
64714        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
64715            $baseinstalldir = $contents['dir']['attribs']['baseinstalldir'];
64716        } else {
64717            $baseinstalldir = false;
64718        }
64719
64720        if (!isset($contents['dir']['file'][0])) {
64721            $contents['dir']['file'] = array($contents['dir']['file']);
64722        }
64723
64724        foreach ($contents['dir']['file'] as $file) {
64725            if ($baseinstalldir && !isset($file['attribs']['baseinstalldir'])) {
64726                $file['attribs']['baseinstalldir'] = $baseinstalldir;
64727            }
64728
64729            $processFile = $file;
64730            unset($processFile['attribs']);
64731            if (count($processFile)) {
64732                foreach ($processFile as $name => $task) {
64733                    if ($name != $pf2->getTasksNs() . ':replace') {
64734                        return $this->raiseError('Cannot safely process "' . $packagexml .
64735                        '" contains tasks other than replace.  Using a ' .
64736                        'PEAR_PackageFileManager-based script is an option.');
64737                    }
64738                    $file['attribs']['replace'][] = $task;
64739                }
64740            }
64741
64742            if (!in_array($file['attribs']['role'], PEAR_Common::getFileRoles())) {
64743                return $this->raiseError('Cannot safely convert "' . $packagexml .
64744                '", contains custom roles.  Using a PEAR_PackageFileManager-based script ' .
64745                'or the convert command is an option');
64746            }
64747
64748            if (isset($release['filelist']['install'])) {
64749                foreach ($release['filelist']['install'] as $installas) {
64750                    if ($installas['attribs']['name'] == $file['attribs']['name']) {
64751                        $file['attribs']['install-as'] = $installas['attribs']['as'];
64752                    }
64753                }
64754            }
64755
64756            $pf->addFile('/', $file['attribs']['name'], $file['attribs']);
64757        }
64758
64759        if ($pf2->getChangeLog()) {
64760            $this->ui->outputData('WARNING: changelog is not translated to package.xml ' .
64761                '1.0, use PEAR_PackageFileManager-based script if you need changelog-' .
64762                'translation for package.xml 1.0');
64763        }
64764
64765        $gen = &$pf->getDefaultGenerator();
64766        $gen->toPackageFile('.');
64767    }
64768}<commands version="1.0">
64769 <pickle>
64770  <summary>Build PECL Package</summary>
64771  <function>doPackage</function>
64772  <shortcut>pi</shortcut>
64773  <options>
64774   <nocompress>
64775    <shortopt>Z</shortopt>
64776    <doc>Do not gzip the package file</doc>
64777   </nocompress>
64778   <showname>
64779    <shortopt>n</shortopt>
64780    <doc>Print the name of the packaged file.</doc>
64781   </showname>
64782  </options>
64783  <doc>[descfile]
64784Creates a PECL package from its package2.xml file.
64785
64786An automatic conversion will be made to a package.xml 1.0 and written out to
64787disk in the current directory as &quot;package.xml&quot;.  Note that
64788only simple package.xml 2.0 will be converted.  package.xml 2.0 with:
64789
64790 - dependency types other than required/optional PECL package/ext/php/pearinstaller
64791 - more than one extsrcrelease or zendextsrcrelease
64792 - zendextbinrelease, extbinrelease, phprelease, or bundle release type
64793 - dependency groups
64794 - ignore tags in release filelist
64795 - tasks other than replace
64796 - custom roles
64797
64798will cause pickle to fail, and output an error message.  If your package2.xml
64799uses any of these features, you are best off using PEAR_PackageFileManager to
64800generate both package.xml.
64801</doc>
64802 </pickle>
64803</commands><?php
64804/**
64805 * PEAR_Command_Registry (list, list-files, shell-test, info commands)
64806 *
64807 * PHP versions 4 and 5
64808 *
64809 * @category   pear
64810 * @package    PEAR
64811 * @author     Stig Bakken <ssb@php.net>
64812 * @author     Greg Beaver <cellog@php.net>
64813 * @copyright  1997-2009 The Authors
64814 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
64815 * @version    CVS: $Id: Registry.php 313023 2011-07-06 19:17:11Z dufuz $
64816 * @link       http://pear.php.net/package/PEAR
64817 * @since      File available since Release 0.1
64818 */
64819
64820/**
64821 * base class
64822 */
64823require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Common.php';
64824
64825/**
64826 * PEAR commands for registry manipulation
64827 *
64828 * @category   pear
64829 * @package    PEAR
64830 * @author     Stig Bakken <ssb@php.net>
64831 * @author     Greg Beaver <cellog@php.net>
64832 * @copyright  1997-2009 The Authors
64833 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
64834 * @version    Release: 1.9.4
64835 * @link       http://pear.php.net/package/PEAR
64836 * @since      Class available since Release 0.1
64837 */
64838class PEAR_Command_Registry extends PEAR_Command_Common
64839{
64840    var $commands = array(
64841        'list' => array(
64842            'summary' => 'List Installed Packages In The Default Channel',
64843            'function' => 'doList',
64844            'shortcut' => 'l',
64845            'options' => array(
64846                'channel' => array(
64847                    'shortopt' => 'c',
64848                    'doc' => 'list installed packages from this channel',
64849                    'arg' => 'CHAN',
64850                    ),
64851                'allchannels' => array(
64852                    'shortopt' => 'a',
64853                    'doc' => 'list installed packages from all channels',
64854                    ),
64855                'channelinfo' => array(
64856                    'shortopt' => 'i',
64857                    'doc' => 'output fully channel-aware data, even on failure',
64858                    ),
64859                ),
64860            'doc' => '<package>
64861If invoked without parameters, this command lists the PEAR packages
64862installed in your php_dir ({config php_dir}).  With a parameter, it
64863lists the files in a package.
64864',
64865            ),
64866        'list-files' => array(
64867            'summary' => 'List Files In Installed Package',
64868            'function' => 'doFileList',
64869            'shortcut' => 'fl',
64870            'options' => array(),
64871            'doc' => '<package>
64872List the files in an installed package.
64873'
64874            ),
64875        'shell-test' => array(
64876            'summary' => 'Shell Script Test',
64877            'function' => 'doShellTest',
64878            'shortcut' => 'st',
64879            'options' => array(),
64880            'doc' => '<package> [[relation] version]
64881Tests if a package is installed in the system. Will exit(1) if it is not.
64882   <relation>   The version comparison operator. One of:
64883                <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
64884   <version>    The version to compare with
64885'),
64886        'info' => array(
64887            'summary'  => 'Display information about a package',
64888            'function' => 'doInfo',
64889            'shortcut' => 'in',
64890            'options'  => array(),
64891            'doc'      => '<package>
64892Displays information about a package. The package argument may be a
64893local package file, an URL to a package file, or the name of an
64894installed package.'
64895            )
64896        );
64897
64898    /**
64899     * PEAR_Command_Registry constructor.
64900     *
64901     * @access public
64902     */
64903    function PEAR_Command_Registry(&$ui, &$config)
64904    {
64905        parent::PEAR_Command_Common($ui, $config);
64906    }
64907
64908    function _sortinfo($a, $b)
64909    {
64910        $apackage = isset($a['package']) ? $a['package'] : $a['name'];
64911        $bpackage = isset($b['package']) ? $b['package'] : $b['name'];
64912        return strcmp($apackage, $bpackage);
64913    }
64914
64915    function doList($command, $options, $params)
64916    {
64917        $reg = &$this->config->getRegistry();
64918        $channelinfo = isset($options['channelinfo']);
64919        if (isset($options['allchannels']) && !$channelinfo) {
64920            return $this->doListAll($command, array(), $params);
64921        }
64922
64923        if (isset($options['allchannels']) && $channelinfo) {
64924            // allchannels with $channelinfo
64925            unset($options['allchannels']);
64926            $channels = $reg->getChannels();
64927            $errors = array();
64928            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
64929            foreach ($channels as $channel) {
64930                $options['channel'] = $channel->getName();
64931                $ret = $this->doList($command, $options, $params);
64932
64933                if (PEAR::isError($ret)) {
64934                    $errors[] = $ret;
64935                }
64936            }
64937
64938            PEAR::staticPopErrorHandling();
64939            if (count($errors)) {
64940                // for now, only give first error
64941                return PEAR::raiseError($errors[0]);
64942            }
64943
64944            return true;
64945        }
64946
64947        if (count($params) === 1) {
64948            return $this->doFileList($command, $options, $params);
64949        }
64950
64951        if (isset($options['channel'])) {
64952            if (!$reg->channelExists($options['channel'])) {
64953                return $this->raiseError('Channel "' . $options['channel'] .'" does not exist');
64954            }
64955
64956            $channel = $reg->channelName($options['channel']);
64957        } else {
64958            $channel = $this->config->get('default_channel');
64959        }
64960
64961        $installed = $reg->packageInfo(null, null, $channel);
64962        usort($installed, array(&$this, '_sortinfo'));
64963
64964        $data = array(
64965            'caption' => 'Installed packages, channel ' .
64966                $channel . ':',
64967            'border' => true,
64968            'headline' => array('Package', 'Version', 'State'),
64969            'channel' => $channel,
64970            );
64971        if ($channelinfo) {
64972            $data['headline'] = array('Channel', 'Package', 'Version', 'State');
64973        }
64974
64975        if (count($installed) && !isset($data['data'])) {
64976            $data['data'] = array();
64977        }
64978
64979        foreach ($installed as $package) {
64980            $pobj = $reg->getPackage(isset($package['package']) ?
64981                                        $package['package'] : $package['name'], $channel);
64982            if ($channelinfo) {
64983                $packageinfo = array($pobj->getChannel(), $pobj->getPackage(), $pobj->getVersion(),
64984                                    $pobj->getState() ? $pobj->getState() : null);
64985            } else {
64986                $packageinfo = array($pobj->getPackage(), $pobj->getVersion(),
64987                                    $pobj->getState() ? $pobj->getState() : null);
64988            }
64989            $data['data'][] = $packageinfo;
64990        }
64991
64992        if (count($installed) === 0) {
64993            if (!$channelinfo) {
64994                $data = '(no packages installed from channel ' . $channel . ')';
64995            } else {
64996                $data = array(
64997                    'caption' => 'Installed packages, channel ' .
64998                        $channel . ':',
64999                    'border' => true,
65000                    'channel' => $channel,
65001                    'data' => array(array('(no packages installed)')),
65002                );
65003            }
65004        }
65005
65006        $this->ui->outputData($data, $command);
65007        return true;
65008    }
65009
65010    function doListAll($command, $options, $params)
65011    {
65012        // This duplicate code is deprecated over
65013        // list --channelinfo, which gives identical
65014        // output for list and list --allchannels.
65015        $reg = &$this->config->getRegistry();
65016        $installed = $reg->packageInfo(null, null, null);
65017        foreach ($installed as $channel => $packages) {
65018            usort($packages, array($this, '_sortinfo'));
65019            $data = array(
65020                'caption'  => 'Installed packages, channel ' . $channel . ':',
65021                'border'   => true,
65022                'headline' => array('Package', 'Version', 'State'),
65023                'channel'  => $channel
65024            );
65025
65026            foreach ($packages as $package) {
65027                $p = isset($package['package']) ? $package['package'] : $package['name'];
65028                $pobj = $reg->getPackage($p, $channel);
65029                $data['data'][] = array($pobj->getPackage(), $pobj->getVersion(),
65030                                        $pobj->getState() ? $pobj->getState() : null);
65031            }
65032
65033            // Adds a blank line after each section
65034            $data['data'][] = array();
65035
65036            if (count($packages) === 0) {
65037                $data = array(
65038                    'caption' => 'Installed packages, channel ' . $channel . ':',
65039                    'border' => true,
65040                    'data' => array(array('(no packages installed)'), array()),
65041                    'channel' => $channel
65042                    );
65043            }
65044            $this->ui->outputData($data, $command);
65045        }
65046        return true;
65047    }
65048
65049    function doFileList($command, $options, $params)
65050    {
65051        if (count($params) !== 1) {
65052            return $this->raiseError('list-files expects 1 parameter');
65053        }
65054
65055        $reg = &$this->config->getRegistry();
65056        $fp = false;
65057        if (!is_dir($params[0]) && (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))) {
65058            if ($fp) {
65059                fclose($fp);
65060            }
65061
65062            if (!class_exists('PEAR_PackageFile')) {
65063                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
65064            }
65065
65066            $pkg = &new PEAR_PackageFile($this->config, $this->_debug);
65067            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
65068            $info = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
65069            PEAR::staticPopErrorHandling();
65070            $headings = array('Package File', 'Install Path');
65071            $installed = false;
65072        } else {
65073            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
65074            $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
65075            PEAR::staticPopErrorHandling();
65076            if (PEAR::isError($parsed)) {
65077                return $this->raiseError($parsed);
65078            }
65079
65080            $info = &$reg->getPackage($parsed['package'], $parsed['channel']);
65081            $headings = array('Type', 'Install Path');
65082            $installed = true;
65083        }
65084
65085        if (PEAR::isError($info)) {
65086            return $this->raiseError($info);
65087        }
65088
65089        if ($info === null) {
65090            return $this->raiseError("`$params[0]' not installed");
65091        }
65092
65093        $list = ($info->getPackagexmlVersion() == '1.0' || $installed) ?
65094            $info->getFilelist() : $info->getContents();
65095        if ($installed) {
65096            $caption = 'Installed Files For ' . $params[0];
65097        } else {
65098            $caption = 'Contents of ' . basename($params[0]);
65099        }
65100
65101        $data = array(
65102            'caption' => $caption,
65103            'border' => true,
65104            'headline' => $headings);
65105        if ($info->getPackagexmlVersion() == '1.0' || $installed) {
65106            foreach ($list as $file => $att) {
65107                if ($installed) {
65108                    if (empty($att['installed_as'])) {
65109                        continue;
65110                    }
65111                    $data['data'][] = array($att['role'], $att['installed_as']);
65112                } else {
65113                    if (isset($att['baseinstalldir']) && !in_array($att['role'],
65114                          array('test', 'data', 'doc'))) {
65115                        $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR .
65116                            $file;
65117                    } else {
65118                        $dest = $file;
65119                    }
65120                    switch ($att['role']) {
65121                        case 'test':
65122                        case 'data':
65123                        case 'doc':
65124                            $role = $att['role'];
65125                            if ($role == 'test') {
65126                                $role .= 's';
65127                            }
65128                            $dest = $this->config->get($role . '_dir') . DIRECTORY_SEPARATOR .
65129                                $info->getPackage() . DIRECTORY_SEPARATOR . $dest;
65130                            break;
65131                        case 'php':
65132                        default:
65133                            $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR .
65134                                $dest;
65135                    }
65136                    $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
65137                    $dest = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
65138                                                    array(DIRECTORY_SEPARATOR,
65139                                                          DIRECTORY_SEPARATOR,
65140                                                          DIRECTORY_SEPARATOR),
65141                                                    $dest);
65142                    $file = preg_replace('!/+!', '/', $file);
65143                    $data['data'][] = array($file, $dest);
65144                }
65145            }
65146        } else { // package.xml 2.0, not installed
65147            if (!isset($list['dir']['file'][0])) {
65148                $list['dir']['file'] = array($list['dir']['file']);
65149            }
65150
65151            foreach ($list['dir']['file'] as $att) {
65152                $att = $att['attribs'];
65153                $file = $att['name'];
65154                $role = &PEAR_Installer_Role::factory($info, $att['role'], $this->config);
65155                $role->setup($this, $info, $att, $file);
65156                if (!$role->isInstallable()) {
65157                    $dest = '(not installable)';
65158                } else {
65159                    $dest = $role->processInstallation($info, $att, $file, '');
65160                    if (PEAR::isError($dest)) {
65161                        $dest = '(Unknown role "' . $att['role'] . ')';
65162                    } else {
65163                        list(,, $dest) = $dest;
65164                    }
65165                }
65166                $data['data'][] = array($file, $dest);
65167            }
65168        }
65169
65170        $this->ui->outputData($data, $command);
65171        return true;
65172    }
65173
65174    function doShellTest($command, $options, $params)
65175    {
65176        if (count($params) < 1) {
65177            return PEAR::raiseError('ERROR, usage: pear shell-test packagename [[relation] version]');
65178        }
65179
65180        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
65181        $reg = &$this->config->getRegistry();
65182        $info = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
65183        if (PEAR::isError($info)) {
65184            exit(1); // invalid package name
65185        }
65186
65187        $package = $info['package'];
65188        $channel = $info['channel'];
65189        // "pear shell-test Foo"
65190        if (!$reg->packageExists($package, $channel)) {
65191            if ($channel == 'pecl.php.net') {
65192                if ($reg->packageExists($package, 'pear.php.net')) {
65193                    $channel = 'pear.php.net'; // magically change channels for extensions
65194                }
65195            }
65196        }
65197
65198        if (count($params) === 1) {
65199            if (!$reg->packageExists($package, $channel)) {
65200                exit(1);
65201            }
65202            // "pear shell-test Foo 1.0"
65203        } elseif (count($params) === 2) {
65204            $v = $reg->packageInfo($package, 'version', $channel);
65205            if (!$v || !version_compare("$v", "{$params[1]}", "ge")) {
65206                exit(1);
65207            }
65208            // "pear shell-test Foo ge 1.0"
65209        } elseif (count($params) === 3) {
65210            $v = $reg->packageInfo($package, 'version', $channel);
65211            if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) {
65212                exit(1);
65213            }
65214        } else {
65215            PEAR::staticPopErrorHandling();
65216            $this->raiseError("$command: expects 1 to 3 parameters");
65217            exit(1);
65218        }
65219    }
65220
65221    function doInfo($command, $options, $params)
65222    {
65223        if (count($params) !== 1) {
65224            return $this->raiseError('pear info expects 1 parameter');
65225        }
65226
65227        $info = $fp = false;
65228        $reg = &$this->config->getRegistry();
65229        if (is_file($params[0]) && !is_dir($params[0]) &&
65230            (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))
65231        ) {
65232            if ($fp) {
65233                fclose($fp);
65234            }
65235
65236            if (!class_exists('PEAR_PackageFile')) {
65237                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
65238            }
65239
65240            $pkg = &new PEAR_PackageFile($this->config, $this->_debug);
65241            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
65242            $obj = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
65243            PEAR::staticPopErrorHandling();
65244            if (PEAR::isError($obj)) {
65245                $uinfo = $obj->getUserInfo();
65246                if (is_array($uinfo)) {
65247                    foreach ($uinfo as $message) {
65248                        if (is_array($message)) {
65249                            $message = $message['message'];
65250                        }
65251                        $this->ui->outputData($message);
65252                    }
65253                }
65254
65255                return $this->raiseError($obj);
65256            }
65257
65258            if ($obj->getPackagexmlVersion() != '1.0') {
65259                return $this->_doInfo2($command, $options, $params, $obj, false);
65260            }
65261
65262            $info = $obj->toArray();
65263        } else {
65264            $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
65265            if (PEAR::isError($parsed)) {
65266                return $this->raiseError($parsed);
65267            }
65268
65269            $package = $parsed['package'];
65270            $channel = $parsed['channel'];
65271            $info = $reg->packageInfo($package, null, $channel);
65272            if (isset($info['old'])) {
65273                $obj = $reg->getPackage($package, $channel);
65274                return $this->_doInfo2($command, $options, $params, $obj, true);
65275            }
65276        }
65277
65278        if (PEAR::isError($info)) {
65279            return $info;
65280        }
65281
65282        if (empty($info)) {
65283            $this->raiseError("No information found for `$params[0]'");
65284            return;
65285        }
65286
65287        unset($info['filelist']);
65288        unset($info['dirtree']);
65289        unset($info['changelog']);
65290        if (isset($info['xsdversion'])) {
65291            $info['package.xml version'] = $info['xsdversion'];
65292            unset($info['xsdversion']);
65293        }
65294
65295        if (isset($info['packagerversion'])) {
65296            $info['packaged with PEAR version'] = $info['packagerversion'];
65297            unset($info['packagerversion']);
65298        }
65299
65300        $keys = array_keys($info);
65301        $longtext = array('description', 'summary');
65302        foreach ($keys as $key) {
65303            if (is_array($info[$key])) {
65304                switch ($key) {
65305                    case 'maintainers': {
65306                        $i = 0;
65307                        $mstr = '';
65308                        foreach ($info[$key] as $m) {
65309                            if ($i++ > 0) {
65310                                $mstr .= "\n";
65311                            }
65312                            $mstr .= $m['name'] . " <";
65313                            if (isset($m['email'])) {
65314                                $mstr .= $m['email'];
65315                            } else {
65316                                $mstr .= $m['handle'] . '@php.net';
65317                            }
65318                            $mstr .= "> ($m[role])";
65319                        }
65320                        $info[$key] = $mstr;
65321                        break;
65322                    }
65323                    case 'release_deps': {
65324                        $i = 0;
65325                        $dstr = '';
65326                        foreach ($info[$key] as $d) {
65327                            if (isset($this->_deps_rel_trans[$d['rel']])) {
65328                                $rel = $this->_deps_rel_trans[$d['rel']];
65329                            } else {
65330                                $rel = $d['rel'];
65331                            }
65332                            if (isset($this->_deps_type_trans[$d['type']])) {
65333                                $type = ucfirst($this->_deps_type_trans[$d['type']]);
65334                            } else {
65335                                $type = $d['type'];
65336                            }
65337                            if (isset($d['name'])) {
65338                                $name = $d['name'] . ' ';
65339                            } else {
65340                                $name = '';
65341                            }
65342                            if (isset($d['version'])) {
65343                                $version = $d['version'] . ' ';
65344                            } else {
65345                                $version = '';
65346                            }
65347                            if (isset($d['optional']) && $d['optional'] == 'yes') {
65348                                $optional = ' (optional)';
65349                            } else {
65350                                $optional = '';
65351                            }
65352                            $dstr .= "$type $name$rel $version$optional\n";
65353                        }
65354                        $info[$key] = $dstr;
65355                        break;
65356                    }
65357                    case 'provides' : {
65358                        $debug = $this->config->get('verbose');
65359                        if ($debug < 2) {
65360                            $pstr = 'Classes: ';
65361                        } else {
65362                            $pstr = '';
65363                        }
65364                        $i = 0;
65365                        foreach ($info[$key] as $p) {
65366                            if ($debug < 2 && $p['type'] != "class") {
65367                                continue;
65368                            }
65369                            // Only print classes when verbosity mode is < 2
65370                            if ($debug < 2) {
65371                                if ($i++ > 0) {
65372                                    $pstr .= ", ";
65373                                }
65374                                $pstr .= $p['name'];
65375                            } else {
65376                                if ($i++ > 0) {
65377                                    $pstr .= "\n";
65378                                }
65379                                $pstr .= ucfirst($p['type']) . " " . $p['name'];
65380                                if (isset($p['explicit']) && $p['explicit'] == 1) {
65381                                    $pstr .= " (explicit)";
65382                                }
65383                            }
65384                        }
65385                        $info[$key] = $pstr;
65386                        break;
65387                    }
65388                    case 'configure_options' : {
65389                        foreach ($info[$key] as $i => $p) {
65390                            $info[$key][$i] = array_map(null, array_keys($p), array_values($p));
65391                            $info[$key][$i] = array_map(create_function('$a',
65392                                'return join(" = ",$a);'), $info[$key][$i]);
65393                            $info[$key][$i] = implode(', ', $info[$key][$i]);
65394                        }
65395                        $info[$key] = implode("\n", $info[$key]);
65396                        break;
65397                    }
65398                    default: {
65399                        $info[$key] = implode(", ", $info[$key]);
65400                        break;
65401                    }
65402                }
65403            }
65404
65405            if ($key == '_lastmodified') {
65406                $hdate = date('Y-m-d', $info[$key]);
65407                unset($info[$key]);
65408                $info['Last Modified'] = $hdate;
65409            } elseif ($key == '_lastversion') {
65410                $info['Previous Installed Version'] = $info[$key] ? $info[$key] : '- None -';
65411                unset($info[$key]);
65412            } else {
65413                $info[$key] = trim($info[$key]);
65414                if (in_array($key, $longtext)) {
65415                    $info[$key] = preg_replace('/  +/', ' ', $info[$key]);
65416                }
65417            }
65418        }
65419
65420        $caption = 'About ' . $info['package'] . '-' . $info['version'];
65421        $data = array(
65422            'caption' => $caption,
65423            'border' => true);
65424        foreach ($info as $key => $value) {
65425            $key = ucwords(trim(str_replace('_', ' ', $key)));
65426            $data['data'][] = array($key, $value);
65427        }
65428        $data['raw'] = $info;
65429
65430        $this->ui->outputData($data, 'package-info');
65431    }
65432
65433    /**
65434     * @access private
65435     */
65436    function _doInfo2($command, $options, $params, &$obj, $installed)
65437    {
65438        $reg = &$this->config->getRegistry();
65439        $caption = 'About ' . $obj->getChannel() . '/' .$obj->getPackage() . '-' .
65440            $obj->getVersion();
65441        $data = array(
65442            'caption' => $caption,
65443            'border' => true);
65444        switch ($obj->getPackageType()) {
65445            case 'php' :
65446                $release = 'PEAR-style PHP-based Package';
65447            break;
65448            case 'extsrc' :
65449                $release = 'PECL-style PHP extension (source code)';
65450            break;
65451            case 'zendextsrc' :
65452                $release = 'PECL-style Zend extension (source code)';
65453            break;
65454            case 'extbin' :
65455                $release = 'PECL-style PHP extension (binary)';
65456            break;
65457            case 'zendextbin' :
65458                $release = 'PECL-style Zend extension (binary)';
65459            break;
65460            case 'bundle' :
65461                $release = 'Package bundle (collection of packages)';
65462            break;
65463        }
65464        $extends = $obj->getExtends();
65465        $extends = $extends ?
65466            $obj->getPackage() . ' (extends ' . $extends . ')' : $obj->getPackage();
65467        if ($src = $obj->getSourcePackage()) {
65468            $extends .= ' (source package ' . $src['channel'] . '/' . $src['package'] . ')';
65469        }
65470
65471        $info = array(
65472            'Release Type' => $release,
65473            'Name' => $extends,
65474            'Channel' => $obj->getChannel(),
65475            'Summary' => preg_replace('/  +/', ' ', $obj->getSummary()),
65476            'Description' => preg_replace('/  +/', ' ', $obj->getDescription()),
65477            );
65478        $info['Maintainers'] = '';
65479        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
65480            $leads = $obj->{"get{$role}s"}();
65481            if (!$leads) {
65482                continue;
65483            }
65484
65485            if (isset($leads['active'])) {
65486                $leads = array($leads);
65487            }
65488
65489            foreach ($leads as $lead) {
65490                if (!empty($info['Maintainers'])) {
65491                    $info['Maintainers'] .= "\n";
65492                }
65493
65494                $active = $lead['active'] == 'no' ? ', inactive' : '';
65495                $info['Maintainers'] .= $lead['name'] . ' <';
65496                $info['Maintainers'] .= $lead['email'] . "> ($role$active)";
65497            }
65498        }
65499
65500        $info['Release Date'] = $obj->getDate();
65501        if ($time = $obj->getTime()) {
65502            $info['Release Date'] .= ' ' . $time;
65503        }
65504
65505        $info['Release Version'] = $obj->getVersion() . ' (' . $obj->getState() . ')';
65506        $info['API Version'] = $obj->getVersion('api') . ' (' . $obj->getState('api') . ')';
65507        $info['License'] = $obj->getLicense();
65508        $uri = $obj->getLicenseLocation();
65509        if ($uri) {
65510            if (isset($uri['uri'])) {
65511                $info['License'] .= ' (' . $uri['uri'] . ')';
65512            } else {
65513                $extra = $obj->getInstalledLocation($info['filesource']);
65514                if ($extra) {
65515                    $info['License'] .= ' (' . $uri['filesource'] . ')';
65516                }
65517            }
65518        }
65519
65520        $info['Release Notes'] = $obj->getNotes();
65521        if ($compat = $obj->getCompatible()) {
65522            if (!isset($compat[0])) {
65523                $compat = array($compat);
65524            }
65525
65526            $info['Compatible with'] = '';
65527            foreach ($compat as $package) {
65528                $info['Compatible with'] .= $package['channel'] . '/' . $package['name'] .
65529                    "\nVersions >= " . $package['min'] . ', <= ' . $package['max'];
65530                if (isset($package['exclude'])) {
65531                    if (is_array($package['exclude'])) {
65532                        $package['exclude'] = implode(', ', $package['exclude']);
65533                    }
65534
65535                    if (!isset($info['Not Compatible with'])) {
65536                        $info['Not Compatible with'] = '';
65537                    } else {
65538                        $info['Not Compatible with'] .= "\n";
65539                    }
65540                    $info['Not Compatible with'] .= $package['channel'] . '/' .
65541                        $package['name'] . "\nVersions " . $package['exclude'];
65542                }
65543            }
65544        }
65545
65546        $usesrole = $obj->getUsesrole();
65547        if ($usesrole) {
65548            if (!isset($usesrole[0])) {
65549                $usesrole = array($usesrole);
65550            }
65551
65552            foreach ($usesrole as $roledata) {
65553                if (isset($info['Uses Custom Roles'])) {
65554                    $info['Uses Custom Roles'] .= "\n";
65555                } else {
65556                    $info['Uses Custom Roles'] = '';
65557                }
65558
65559                if (isset($roledata['package'])) {
65560                    $rolepackage = $reg->parsedPackageNameToString($roledata, true);
65561                } else {
65562                    $rolepackage = $roledata['uri'];
65563                }
65564                $info['Uses Custom Roles'] .= $roledata['role'] . ' (' . $rolepackage . ')';
65565            }
65566        }
65567
65568        $usestask = $obj->getUsestask();
65569        if ($usestask) {
65570            if (!isset($usestask[0])) {
65571                $usestask = array($usestask);
65572            }
65573
65574            foreach ($usestask as $taskdata) {
65575                if (isset($info['Uses Custom Tasks'])) {
65576                    $info['Uses Custom Tasks'] .= "\n";
65577                } else {
65578                    $info['Uses Custom Tasks'] = '';
65579                }
65580
65581                if (isset($taskdata['package'])) {
65582                    $taskpackage = $reg->parsedPackageNameToString($taskdata, true);
65583                } else {
65584                    $taskpackage = $taskdata['uri'];
65585                }
65586                $info['Uses Custom Tasks'] .= $taskdata['task'] . ' (' . $taskpackage . ')';
65587            }
65588        }
65589
65590        $deps = $obj->getDependencies();
65591        $info['Required Dependencies'] = 'PHP version ' . $deps['required']['php']['min'];
65592        if (isset($deps['required']['php']['max'])) {
65593            $info['Required Dependencies'] .= '-' . $deps['required']['php']['max'] . "\n";
65594        } else {
65595            $info['Required Dependencies'] .= "\n";
65596        }
65597
65598        if (isset($deps['required']['php']['exclude'])) {
65599            if (!isset($info['Not Compatible with'])) {
65600                $info['Not Compatible with'] = '';
65601            } else {
65602                $info['Not Compatible with'] .= "\n";
65603            }
65604
65605            if (is_array($deps['required']['php']['exclude'])) {
65606                $deps['required']['php']['exclude'] =
65607                    implode(', ', $deps['required']['php']['exclude']);
65608            }
65609            $info['Not Compatible with'] .= "PHP versions\n  " .
65610                $deps['required']['php']['exclude'];
65611        }
65612
65613        $info['Required Dependencies'] .= 'PEAR installer version';
65614        if (isset($deps['required']['pearinstaller']['max'])) {
65615            $info['Required Dependencies'] .= 's ' .
65616                $deps['required']['pearinstaller']['min'] . '-' .
65617                $deps['required']['pearinstaller']['max'];
65618        } else {
65619            $info['Required Dependencies'] .= ' ' .
65620                $deps['required']['pearinstaller']['min'] . ' or newer';
65621        }
65622
65623        if (isset($deps['required']['pearinstaller']['exclude'])) {
65624            if (!isset($info['Not Compatible with'])) {
65625                $info['Not Compatible with'] = '';
65626            } else {
65627                $info['Not Compatible with'] .= "\n";
65628            }
65629
65630            if (is_array($deps['required']['pearinstaller']['exclude'])) {
65631                $deps['required']['pearinstaller']['exclude'] =
65632                    implode(', ', $deps['required']['pearinstaller']['exclude']);
65633            }
65634            $info['Not Compatible with'] .= "PEAR installer\n  Versions " .
65635                $deps['required']['pearinstaller']['exclude'];
65636        }
65637
65638        foreach (array('Package', 'Extension') as $type) {
65639            $index = strtolower($type);
65640            if (isset($deps['required'][$index])) {
65641                if (isset($deps['required'][$index]['name'])) {
65642                    $deps['required'][$index] = array($deps['required'][$index]);
65643                }
65644
65645                foreach ($deps['required'][$index] as $package) {
65646                    if (isset($package['conflicts'])) {
65647                        $infoindex = 'Not Compatible with';
65648                        if (!isset($info['Not Compatible with'])) {
65649                            $info['Not Compatible with'] = '';
65650                        } else {
65651                            $info['Not Compatible with'] .= "\n";
65652                        }
65653                    } else {
65654                        $infoindex = 'Required Dependencies';
65655                        $info[$infoindex] .= "\n";
65656                    }
65657
65658                    if ($index == 'extension') {
65659                        $name = $package['name'];
65660                    } else {
65661                        if (isset($package['channel'])) {
65662                            $name = $package['channel'] . '/' . $package['name'];
65663                        } else {
65664                            $name = '__uri/' . $package['name'] . ' (static URI)';
65665                        }
65666                    }
65667
65668                    $info[$infoindex] .= "$type $name";
65669                    if (isset($package['uri'])) {
65670                        $info[$infoindex] .= "\n  Download URI: $package[uri]";
65671                        continue;
65672                    }
65673
65674                    if (isset($package['max']) && isset($package['min'])) {
65675                        $info[$infoindex] .= " \n  Versions " .
65676                            $package['min'] . '-' . $package['max'];
65677                    } elseif (isset($package['min'])) {
65678                        $info[$infoindex] .= " \n  Version " .
65679                            $package['min'] . ' or newer';
65680                    } elseif (isset($package['max'])) {
65681                        $info[$infoindex] .= " \n  Version " .
65682                            $package['max'] . ' or older';
65683                    }
65684
65685                    if (isset($package['recommended'])) {
65686                        $info[$infoindex] .= "\n  Recommended version: $package[recommended]";
65687                    }
65688
65689                    if (isset($package['exclude'])) {
65690                        if (!isset($info['Not Compatible with'])) {
65691                            $info['Not Compatible with'] = '';
65692                        } else {
65693                            $info['Not Compatible with'] .= "\n";
65694                        }
65695
65696                        if (is_array($package['exclude'])) {
65697                            $package['exclude'] = implode(', ', $package['exclude']);
65698                        }
65699
65700                        $package['package'] = $package['name']; // for parsedPackageNameToString
65701                         if (isset($package['conflicts'])) {
65702                            $info['Not Compatible with'] .= '=> except ';
65703                        }
65704                       $info['Not Compatible with'] .= 'Package ' .
65705                            $reg->parsedPackageNameToString($package, true);
65706                        $info['Not Compatible with'] .= "\n  Versions " . $package['exclude'];
65707                    }
65708                }
65709            }
65710        }
65711
65712        if (isset($deps['required']['os'])) {
65713            if (isset($deps['required']['os']['name'])) {
65714                $dep['required']['os']['name'] = array($dep['required']['os']['name']);
65715            }
65716
65717            foreach ($dep['required']['os'] as $os) {
65718                if (isset($os['conflicts']) && $os['conflicts'] == 'yes') {
65719                    if (!isset($info['Not Compatible with'])) {
65720                        $info['Not Compatible with'] = '';
65721                    } else {
65722                        $info['Not Compatible with'] .= "\n";
65723                    }
65724                    $info['Not Compatible with'] .= "$os[name] Operating System";
65725                } else {
65726                    $info['Required Dependencies'] .= "\n";
65727                    $info['Required Dependencies'] .= "$os[name] Operating System";
65728                }
65729            }
65730        }
65731
65732        if (isset($deps['required']['arch'])) {
65733            if (isset($deps['required']['arch']['pattern'])) {
65734                $dep['required']['arch']['pattern'] = array($dep['required']['os']['pattern']);
65735            }
65736
65737            foreach ($dep['required']['arch'] as $os) {
65738                if (isset($os['conflicts']) && $os['conflicts'] == 'yes') {
65739                    if (!isset($info['Not Compatible with'])) {
65740                        $info['Not Compatible with'] = '';
65741                    } else {
65742                        $info['Not Compatible with'] .= "\n";
65743                    }
65744                    $info['Not Compatible with'] .= "OS/Arch matching pattern '/$os[pattern]/'";
65745                } else {
65746                    $info['Required Dependencies'] .= "\n";
65747                    $info['Required Dependencies'] .= "OS/Arch matching pattern '/$os[pattern]/'";
65748                }
65749            }
65750        }
65751
65752        if (isset($deps['optional'])) {
65753            foreach (array('Package', 'Extension') as $type) {
65754                $index = strtolower($type);
65755                if (isset($deps['optional'][$index])) {
65756                    if (isset($deps['optional'][$index]['name'])) {
65757                        $deps['optional'][$index] = array($deps['optional'][$index]);
65758                    }
65759
65760                    foreach ($deps['optional'][$index] as $package) {
65761                        if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
65762                            $infoindex = 'Not Compatible with';
65763                            if (!isset($info['Not Compatible with'])) {
65764                                $info['Not Compatible with'] = '';
65765                            } else {
65766                                $info['Not Compatible with'] .= "\n";
65767                            }
65768                        } else {
65769                            $infoindex = 'Optional Dependencies';
65770                            if (!isset($info['Optional Dependencies'])) {
65771                                $info['Optional Dependencies'] = '';
65772                            } else {
65773                                $info['Optional Dependencies'] .= "\n";
65774                            }
65775                        }
65776
65777                        if ($index == 'extension') {
65778                            $name = $package['name'];
65779                        } else {
65780                            if (isset($package['channel'])) {
65781                                $name = $package['channel'] . '/' . $package['name'];
65782                            } else {
65783                                $name = '__uri/' . $package['name'] . ' (static URI)';
65784                            }
65785                        }
65786
65787                        $info[$infoindex] .= "$type $name";
65788                        if (isset($package['uri'])) {
65789                            $info[$infoindex] .= "\n  Download URI: $package[uri]";
65790                            continue;
65791                        }
65792
65793                        if ($infoindex == 'Not Compatible with') {
65794                            // conflicts is only used to say that all versions conflict
65795                            continue;
65796                        }
65797
65798                        if (isset($package['max']) && isset($package['min'])) {
65799                            $info[$infoindex] .= " \n  Versions " .
65800                                $package['min'] . '-' . $package['max'];
65801                        } elseif (isset($package['min'])) {
65802                            $info[$infoindex] .= " \n  Version " .
65803                                $package['min'] . ' or newer';
65804                        } elseif (isset($package['max'])) {
65805                            $info[$infoindex] .= " \n  Version " .
65806                                $package['min'] . ' or older';
65807                        }
65808
65809                        if (isset($package['recommended'])) {
65810                            $info[$infoindex] .= "\n  Recommended version: $package[recommended]";
65811                        }
65812
65813                        if (isset($package['exclude'])) {
65814                            if (!isset($info['Not Compatible with'])) {
65815                                $info['Not Compatible with'] = '';
65816                            } else {
65817                                $info['Not Compatible with'] .= "\n";
65818                            }
65819
65820                            if (is_array($package['exclude'])) {
65821                                $package['exclude'] = implode(', ', $package['exclude']);
65822                            }
65823
65824                            $info['Not Compatible with'] .= "Package $package\n  Versions " .
65825                                $package['exclude'];
65826                        }
65827                    }
65828                }
65829            }
65830        }
65831
65832        if (isset($deps['group'])) {
65833            if (!isset($deps['group'][0])) {
65834                $deps['group'] = array($deps['group']);
65835            }
65836
65837            foreach ($deps['group'] as $group) {
65838                $info['Dependency Group ' . $group['attribs']['name']] = $group['attribs']['hint'];
65839                $groupindex = $group['attribs']['name'] . ' Contents';
65840                $info[$groupindex] = '';
65841                foreach (array('Package', 'Extension') as $type) {
65842                    $index = strtolower($type);
65843                    if (isset($group[$index])) {
65844                        if (isset($group[$index]['name'])) {
65845                            $group[$index] = array($group[$index]);
65846                        }
65847
65848                        foreach ($group[$index] as $package) {
65849                            if (!empty($info[$groupindex])) {
65850                                $info[$groupindex] .= "\n";
65851                            }
65852
65853                            if ($index == 'extension') {
65854                                $name = $package['name'];
65855                            } else {
65856                                if (isset($package['channel'])) {
65857                                    $name = $package['channel'] . '/' . $package['name'];
65858                                } else {
65859                                    $name = '__uri/' . $package['name'] . ' (static URI)';
65860                                }
65861                            }
65862
65863                            if (isset($package['uri'])) {
65864                                if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
65865                                    $info[$groupindex] .= "Not Compatible with $type $name";
65866                                } else {
65867                                    $info[$groupindex] .= "$type $name";
65868                                }
65869
65870                                $info[$groupindex] .= "\n  Download URI: $package[uri]";
65871                                continue;
65872                            }
65873
65874                            if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
65875                                $info[$groupindex] .= "Not Compatible with $type $name";
65876                                continue;
65877                            }
65878
65879                            $info[$groupindex] .= "$type $name";
65880                            if (isset($package['max']) && isset($package['min'])) {
65881                                $info[$groupindex] .= " \n  Versions " .
65882                                    $package['min'] . '-' . $package['max'];
65883                            } elseif (isset($package['min'])) {
65884                                $info[$groupindex] .= " \n  Version " .
65885                                    $package['min'] . ' or newer';
65886                            } elseif (isset($package['max'])) {
65887                                $info[$groupindex] .= " \n  Version " .
65888                                    $package['min'] . ' or older';
65889                            }
65890
65891                            if (isset($package['recommended'])) {
65892                                $info[$groupindex] .= "\n  Recommended version: $package[recommended]";
65893                            }
65894
65895                            if (isset($package['exclude'])) {
65896                                if (!isset($info['Not Compatible with'])) {
65897                                    $info['Not Compatible with'] = '';
65898                                } else {
65899                                    $info[$groupindex] .= "Not Compatible with\n";
65900                                }
65901
65902                                if (is_array($package['exclude'])) {
65903                                    $package['exclude'] = implode(', ', $package['exclude']);
65904                                }
65905                                $info[$groupindex] .= "  Package $package\n  Versions " .
65906                                    $package['exclude'];
65907                            }
65908                        }
65909                    }
65910                }
65911            }
65912        }
65913
65914        if ($obj->getPackageType() == 'bundle') {
65915            $info['Bundled Packages'] = '';
65916            foreach ($obj->getBundledPackages() as $package) {
65917                if (!empty($info['Bundled Packages'])) {
65918                    $info['Bundled Packages'] .= "\n";
65919                }
65920
65921                if (isset($package['uri'])) {
65922                    $info['Bundled Packages'] .= '__uri/' . $package['name'];
65923                    $info['Bundled Packages'] .= "\n  (URI: $package[uri]";
65924                } else {
65925                    $info['Bundled Packages'] .= $package['channel'] . '/' . $package['name'];
65926                }
65927            }
65928        }
65929
65930        $info['package.xml version'] = '2.0';
65931        if ($installed) {
65932            if ($obj->getLastModified()) {
65933                $info['Last Modified'] = date('Y-m-d H:i', $obj->getLastModified());
65934            }
65935
65936            $v = $obj->getLastInstalledVersion();
65937            $info['Previous Installed Version'] = $v ? $v : '- None -';
65938        }
65939
65940        foreach ($info as $key => $value) {
65941            $data['data'][] = array($key, $value);
65942        }
65943
65944        $data['raw'] = $obj->getArray(); // no validation needed
65945        $this->ui->outputData($data, 'package-info');
65946    }
65947}<commands version="1.0">
65948 <list>
65949  <summary>List Installed Packages In The Default Channel</summary>
65950  <function>doList</function>
65951  <shortcut>l</shortcut>
65952  <options>
65953   <channel>
65954    <shortopt>c</shortopt>
65955    <doc>list installed packages from this channel</doc>
65956    <arg>CHAN</arg>
65957   </channel>
65958   <allchannels>
65959    <shortopt>a</shortopt>
65960    <doc>list installed packages from all channels</doc>
65961   </allchannels>
65962   <channelinfo>
65963    <shortopt>i</shortopt>
65964    <doc>output fully channel-aware data, even on failure</doc>
65965   </channelinfo>
65966  </options>
65967  <doc>&lt;package&gt;
65968If invoked without parameters, this command lists the PEAR packages
65969installed in your php_dir ({config php_dir}).  With a parameter, it
65970lists the files in a package.
65971</doc>
65972 </list>
65973 <list-files>
65974  <summary>List Files In Installed Package</summary>
65975  <function>doFileList</function>
65976  <shortcut>fl</shortcut>
65977  <options />
65978  <doc>&lt;package&gt;
65979List the files in an installed package.
65980</doc>
65981 </list-files>
65982 <shell-test>
65983  <summary>Shell Script Test</summary>
65984  <function>doShellTest</function>
65985  <shortcut>st</shortcut>
65986  <options />
65987  <doc>&lt;package&gt; [[relation] version]
65988Tests if a package is installed in the system. Will exit(1) if it is not.
65989   &lt;relation&gt;   The version comparison operator. One of:
65990                &lt;, lt, &lt;=, le, &gt;, gt, &gt;=, ge, ==, =, eq, !=, &lt;&gt;, ne
65991   &lt;version&gt;    The version to compare with
65992</doc>
65993 </shell-test>
65994 <info>
65995  <summary>Display information about a package</summary>
65996  <function>doInfo</function>
65997  <shortcut>in</shortcut>
65998  <options />
65999  <doc>&lt;package&gt;
66000Displays information about a package. The package argument may be a
66001local package file, an URL to a package file, or the name of an
66002installed package.</doc>
66003 </info>
66004</commands><?php
66005/**
66006 * PEAR_Command_Remote (remote-info, list-upgrades, remote-list, search, list-all, download,
66007 * clear-cache commands)
66008 *
66009 * PHP versions 4 and 5
66010 *
66011 * @category   pear
66012 * @package    PEAR
66013 * @author     Stig Bakken <ssb@php.net>
66014 * @author     Greg Beaver <cellog@php.net>
66015 * @copyright  1997-2009 The Authors
66016 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
66017 * @version    CVS: $Id: Remote.php 313023 2011-07-06 19:17:11Z dufuz $
66018 * @link       http://pear.php.net/package/PEAR
66019 * @since      File available since Release 0.1
66020 */
66021
66022/**
66023 * base class
66024 */
66025require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Common.php';
66026require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/REST.php';
66027
66028/**
66029 * PEAR commands for remote server querying
66030 *
66031 * @category   pear
66032 * @package    PEAR
66033 * @author     Stig Bakken <ssb@php.net>
66034 * @author     Greg Beaver <cellog@php.net>
66035 * @copyright  1997-2009 The Authors
66036 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
66037 * @version    Release: 1.9.4
66038 * @link       http://pear.php.net/package/PEAR
66039 * @since      Class available since Release 0.1
66040 */
66041class PEAR_Command_Remote extends PEAR_Command_Common
66042{
66043    var $commands = array(
66044        'remote-info' => array(
66045            'summary' => 'Information About Remote Packages',
66046            'function' => 'doRemoteInfo',
66047            'shortcut' => 'ri',
66048            'options' => array(),
66049            'doc' => '<package>
66050Get details on a package from the server.',
66051            ),
66052        'list-upgrades' => array(
66053            'summary' => 'List Available Upgrades',
66054            'function' => 'doListUpgrades',
66055            'shortcut' => 'lu',
66056            'options' => array(
66057                'channelinfo' => array(
66058                    'shortopt' => 'i',
66059                    'doc' => 'output fully channel-aware data, even on failure',
66060                    ),
66061            ),
66062            'doc' => '[preferred_state]
66063List releases on the server of packages you have installed where
66064a newer version is available with the same release state (stable etc.)
66065or the state passed as the second parameter.'
66066            ),
66067        'remote-list' => array(
66068            'summary' => 'List Remote Packages',
66069            'function' => 'doRemoteList',
66070            'shortcut' => 'rl',
66071            'options' => array(
66072                'channel' =>
66073                    array(
66074                    'shortopt' => 'c',
66075                    'doc' => 'specify a channel other than the default channel',
66076                    'arg' => 'CHAN',
66077                    )
66078                ),
66079            'doc' => '
66080Lists the packages available on the configured server along with the
66081latest stable release of each package.',
66082            ),
66083        'search' => array(
66084            'summary' => 'Search remote package database',
66085            'function' => 'doSearch',
66086            'shortcut' => 'sp',
66087            'options' => array(
66088                'channel' =>
66089                    array(
66090                    'shortopt' => 'c',
66091                    'doc' => 'specify a channel other than the default channel',
66092                    'arg' => 'CHAN',
66093                    ),
66094                'allchannels' => array(
66095                    'shortopt' => 'a',
66096                    'doc' => 'search packages from all known channels',
66097                    ),
66098                'channelinfo' => array(
66099                    'shortopt' => 'i',
66100                    'doc' => 'output fully channel-aware data, even on failure',
66101                    ),
66102                ),
66103            'doc' => '[packagename] [packageinfo]
66104Lists all packages which match the search parameters.  The first
66105parameter is a fragment of a packagename.  The default channel
66106will be used unless explicitly overridden.  The second parameter
66107will be used to match any portion of the summary/description',
66108            ),
66109        'list-all' => array(
66110            'summary' => 'List All Packages',
66111            'function' => 'doListAll',
66112            'shortcut' => 'la',
66113            'options' => array(
66114                'channel' =>
66115                    array(
66116                    'shortopt' => 'c',
66117                    'doc' => 'specify a channel other than the default channel',
66118                    'arg' => 'CHAN',
66119                    ),
66120                'channelinfo' => array(
66121                    'shortopt' => 'i',
66122                    'doc' => 'output fully channel-aware data, even on failure',
66123                    ),
66124                ),
66125            'doc' => '
66126Lists the packages available on the configured server along with the
66127latest stable release of each package.',
66128            ),
66129        'download' => array(
66130            'summary' => 'Download Package',
66131            'function' => 'doDownload',
66132            'shortcut' => 'd',
66133            'options' => array(
66134                'nocompress' => array(
66135                    'shortopt' => 'Z',
66136                    'doc' => 'download an uncompressed (.tar) file',
66137                    ),
66138                ),
66139            'doc' => '<package>...
66140Download package tarballs.  The files will be named as suggested by the
66141server, for example if you download the DB package and the latest stable
66142version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.',
66143            ),
66144        'clear-cache' => array(
66145            'summary' => 'Clear Web Services Cache',
66146            'function' => 'doClearCache',
66147            'shortcut' => 'cc',
66148            'options' => array(),
66149            'doc' => '
66150Clear the REST cache. See also the cache_ttl configuration
66151parameter.
66152',
66153            ),
66154        );
66155
66156    /**
66157     * PEAR_Command_Remote constructor.
66158     *
66159     * @access public
66160     */
66161    function PEAR_Command_Remote(&$ui, &$config)
66162    {
66163        parent::PEAR_Command_Common($ui, $config);
66164    }
66165
66166    function _checkChannelForStatus($channel, $chan)
66167    {
66168        if (PEAR::isError($chan)) {
66169            $this->raiseError($chan);
66170        }
66171        if (!is_a($chan, 'PEAR_ChannelFile')) {
66172            return $this->raiseError('Internal corruption error: invalid channel "' .
66173                $channel . '"');
66174        }
66175        $rest = new PEAR_REST($this->config);
66176        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
66177        $mirror = $this->config->get('preferred_mirror', null,
66178                                     $channel);
66179        $a = $rest->downloadHttp('http://' . $channel .
66180            '/channel.xml', $chan->lastModified());
66181        PEAR::staticPopErrorHandling();
66182        if (!PEAR::isError($a) && $a) {
66183            $this->ui->outputData('WARNING: channel "' . $channel . '" has ' .
66184                'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $channel .
66185                '" to update');
66186        }
66187    }
66188
66189    function doRemoteInfo($command, $options, $params)
66190    {
66191        if (sizeof($params) != 1) {
66192            return $this->raiseError("$command expects one param: the remote package name");
66193        }
66194        $savechannel = $channel = $this->config->get('default_channel');
66195        $reg = &$this->config->getRegistry();
66196        $package = $params[0];
66197        $parsed = $reg->parsePackageName($package, $channel);
66198        if (PEAR::isError($parsed)) {
66199            return $this->raiseError('Invalid package name "' . $package . '"');
66200        }
66201
66202        $channel = $parsed['channel'];
66203        $this->config->set('default_channel', $channel);
66204        $chan = $reg->getChannel($channel);
66205        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
66206            return $e;
66207        }
66208
66209        $mirror = $this->config->get('preferred_mirror');
66210        if ($chan->supportsREST($mirror) && $base = $chan->getBaseURL('REST1.0', $mirror)) {
66211            $rest = &$this->config->getREST('1.0', array());
66212            $info = $rest->packageInfo($base, $parsed['package'], $channel);
66213        }
66214
66215        if (!isset($info)) {
66216            return $this->raiseError('No supported protocol was found');
66217        }
66218
66219        if (PEAR::isError($info)) {
66220            $this->config->set('default_channel', $savechannel);
66221            return $this->raiseError($info);
66222        }
66223
66224        if (!isset($info['name'])) {
66225            return $this->raiseError('No remote package "' . $package . '" was found');
66226        }
66227
66228        $installed = $reg->packageInfo($info['name'], null, $channel);
66229        $info['installed'] = $installed['version'] ? $installed['version'] : '- no -';
66230        if (is_array($info['installed'])) {
66231            $info['installed'] = $info['installed']['release'];
66232        }
66233
66234        $this->ui->outputData($info, $command);
66235        $this->config->set('default_channel', $savechannel);
66236
66237        return true;
66238    }
66239
66240    function doRemoteList($command, $options, $params)
66241    {
66242        $savechannel = $channel = $this->config->get('default_channel');
66243        $reg = &$this->config->getRegistry();
66244        if (isset($options['channel'])) {
66245            $channel = $options['channel'];
66246            if (!$reg->channelExists($channel)) {
66247                return $this->raiseError('Channel "' . $channel . '" does not exist');
66248            }
66249
66250            $this->config->set('default_channel', $channel);
66251        }
66252
66253        $chan = $reg->getChannel($channel);
66254        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
66255            return $e;
66256        }
66257
66258        $list_options = false;
66259        if ($this->config->get('preferred_state') == 'stable') {
66260            $list_options = true;
66261        }
66262
66263        $available = array();
66264        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
66265              $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))
66266        ) {
66267            // use faster list-all if available
66268            $rest = &$this->config->getREST('1.1', array());
66269            $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName());
66270        } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
66271              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
66272            $rest = &$this->config->getREST('1.0', array());
66273            $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName());
66274        }
66275
66276        if (PEAR::isError($available)) {
66277            $this->config->set('default_channel', $savechannel);
66278            return $this->raiseError($available);
66279        }
66280
66281        $i = $j = 0;
66282        $data = array(
66283            'caption' => 'Channel ' . $channel . ' Available packages:',
66284            'border' => true,
66285            'headline' => array('Package', 'Version'),
66286            'channel' => $channel
66287            );
66288
66289        if (count($available) == 0) {
66290            $data = '(no packages available yet)';
66291        } else {
66292            foreach ($available as $name => $info) {
66293                $version = (isset($info['stable']) && $info['stable']) ? $info['stable'] : '-n/a-';
66294                $data['data'][] = array($name, $version);
66295            }
66296        }
66297        $this->ui->outputData($data, $command);
66298        $this->config->set('default_channel', $savechannel);
66299        return true;
66300    }
66301
66302    function doListAll($command, $options, $params)
66303    {
66304        $savechannel = $channel = $this->config->get('default_channel');
66305        $reg = &$this->config->getRegistry();
66306        if (isset($options['channel'])) {
66307            $channel = $options['channel'];
66308            if (!$reg->channelExists($channel)) {
66309                return $this->raiseError("Channel \"$channel\" does not exist");
66310            }
66311
66312            $this->config->set('default_channel', $channel);
66313        }
66314
66315        $list_options = false;
66316        if ($this->config->get('preferred_state') == 'stable') {
66317            $list_options = true;
66318        }
66319
66320        $chan = $reg->getChannel($channel);
66321        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
66322            return $e;
66323        }
66324
66325        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
66326              $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))) {
66327            // use faster list-all if available
66328            $rest = &$this->config->getREST('1.1', array());
66329            $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName());
66330        } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
66331              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
66332            $rest = &$this->config->getREST('1.0', array());
66333            $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName());
66334        }
66335
66336        if (PEAR::isError($available)) {
66337            $this->config->set('default_channel', $savechannel);
66338            return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "' . $available->getMessage() . '")');
66339        }
66340
66341        $data = array(
66342            'caption' => 'All packages [Channel ' . $channel . ']:',
66343            'border' => true,
66344            'headline' => array('Package', 'Latest', 'Local'),
66345            'channel' => $channel,
66346            );
66347
66348        if (isset($options['channelinfo'])) {
66349            // add full channelinfo
66350            $data['caption'] = 'Channel ' . $channel . ' All packages:';
66351            $data['headline'] = array('Channel', 'Package', 'Latest', 'Local',
66352                'Description', 'Dependencies');
66353        }
66354        $local_pkgs = $reg->listPackages($channel);
66355
66356        foreach ($available as $name => $info) {
66357            $installed = $reg->packageInfo($name, null, $channel);
66358            if (is_array($installed['version'])) {
66359                $installed['version'] = $installed['version']['release'];
66360            }
66361            $desc = $info['summary'];
66362            if (isset($params[$name])) {
66363                $desc .= "\n\n".$info['description'];
66364            }
66365            if (isset($options['mode']))
66366            {
66367                if ($options['mode'] == 'installed' && !isset($installed['version'])) {
66368                    continue;
66369                }
66370                if ($options['mode'] == 'notinstalled' && isset($installed['version'])) {
66371                    continue;
66372                }
66373                if ($options['mode'] == 'upgrades'
66374                      && (!isset($installed['version']) || version_compare($installed['version'],
66375                      $info['stable'], '>='))) {
66376                    continue;
66377                }
66378            }
66379            $pos = array_search(strtolower($name), $local_pkgs);
66380            if ($pos !== false) {
66381                unset($local_pkgs[$pos]);
66382            }
66383
66384            if (isset($info['stable']) && !$info['stable']) {
66385                $info['stable'] = null;
66386            }
66387
66388            if (isset($options['channelinfo'])) {
66389                // add full channelinfo
66390                if ($info['stable'] === $info['unstable']) {
66391                    $state = $info['state'];
66392                } else {
66393                    $state = 'stable';
66394                }
66395                $latest = $info['stable'].' ('.$state.')';
66396                $local = '';
66397                if (isset($installed['version'])) {
66398                    $inst_state = $reg->packageInfo($name, 'release_state', $channel);
66399                    $local = $installed['version'].' ('.$inst_state.')';
66400                }
66401
66402                $packageinfo = array(
66403                    $channel,
66404                    $name,
66405                    $latest,
66406                    $local,
66407                    isset($desc) ? $desc : null,
66408                    isset($info['deps']) ? $info['deps'] : null,
66409                );
66410            } else {
66411                $packageinfo = array(
66412                    $reg->channelAlias($channel) . '/' . $name,
66413                    isset($info['stable']) ? $info['stable'] : null,
66414                    isset($installed['version']) ? $installed['version'] : null,
66415                    isset($desc) ? $desc : null,
66416                    isset($info['deps']) ? $info['deps'] : null,
66417                );
66418            }
66419            $data['data'][$info['category']][] = $packageinfo;
66420        }
66421
66422        if (isset($options['mode']) && in_array($options['mode'], array('notinstalled', 'upgrades'))) {
66423            $this->config->set('default_channel', $savechannel);
66424            $this->ui->outputData($data, $command);
66425            return true;
66426        }
66427
66428        foreach ($local_pkgs as $name) {
66429            $info = &$reg->getPackage($name, $channel);
66430            $data['data']['Local'][] = array(
66431                $reg->channelAlias($channel) . '/' . $info->getPackage(),
66432                '',
66433                $info->getVersion(),
66434                $info->getSummary(),
66435                $info->getDeps()
66436                );
66437        }
66438
66439        $this->config->set('default_channel', $savechannel);
66440        $this->ui->outputData($data, $command);
66441        return true;
66442    }
66443
66444    function doSearch($command, $options, $params)
66445    {
66446        if ((!isset($params[0]) || empty($params[0]))
66447            && (!isset($params[1]) || empty($params[1])))
66448        {
66449            return $this->raiseError('no valid search string supplied');
66450        }
66451
66452        $channelinfo = isset($options['channelinfo']);
66453        $reg = &$this->config->getRegistry();
66454        if (isset($options['allchannels'])) {
66455            // search all channels
66456            unset($options['allchannels']);
66457            $channels = $reg->getChannels();
66458            $errors = array();
66459            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
66460            foreach ($channels as $channel) {
66461                if ($channel->getName() != '__uri') {
66462                    $options['channel'] = $channel->getName();
66463                    $ret = $this->doSearch($command, $options, $params);
66464                    if (PEAR::isError($ret)) {
66465                        $errors[] = $ret;
66466                    }
66467                }
66468            }
66469
66470            PEAR::staticPopErrorHandling();
66471            if (count($errors) !== 0) {
66472                // for now, only give first error
66473                return PEAR::raiseError($errors[0]);
66474            }
66475
66476            return true;
66477        }
66478
66479        $savechannel = $channel = $this->config->get('default_channel');
66480        $package = strtolower($params[0]);
66481        $summary = isset($params[1]) ? $params[1] : false;
66482        if (isset($options['channel'])) {
66483            $reg = &$this->config->getRegistry();
66484            $channel = $options['channel'];
66485            if (!$reg->channelExists($channel)) {
66486                return $this->raiseError('Channel "' . $channel . '" does not exist');
66487            }
66488
66489            $this->config->set('default_channel', $channel);
66490        }
66491
66492        $chan = $reg->getChannel($channel);
66493        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
66494            return $e;
66495        }
66496
66497        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
66498              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
66499            $rest = &$this->config->getREST('1.0', array());
66500            $available = $rest->listAll($base, false, false, $package, $summary, $chan->getName());
66501        }
66502
66503        if (PEAR::isError($available)) {
66504            $this->config->set('default_channel', $savechannel);
66505            return $this->raiseError($available);
66506        }
66507
66508        if (!$available && !$channelinfo) {
66509            // clean exit when not found, no error !
66510            $data = 'no packages found that match pattern "' . $package . '", for channel '.$channel.'.';
66511            $this->ui->outputData($data);
66512            $this->config->set('default_channel', $channel);
66513            return true;
66514        }
66515
66516        if ($channelinfo) {
66517            $data = array(
66518                'caption' => 'Matched packages, channel ' . $channel . ':',
66519                'border' => true,
66520                'headline' => array('Channel', 'Package', 'Stable/(Latest)', 'Local'),
66521                'channel' => $channel
66522                );
66523        } else {
66524            $data = array(
66525                'caption' => 'Matched packages, channel ' . $channel . ':',
66526                'border' => true,
66527                'headline' => array('Package', 'Stable/(Latest)', 'Local'),
66528                'channel' => $channel
66529                );
66530        }
66531
66532        if (!$available && $channelinfo) {
66533            unset($data['headline']);
66534            $data['data'] = 'No packages found that match pattern "' . $package . '".';
66535            $available = array();
66536        }
66537
66538        foreach ($available as $name => $info) {
66539            $installed = $reg->packageInfo($name, null, $channel);
66540            $desc = $info['summary'];
66541            if (isset($params[$name]))
66542                $desc .= "\n\n".$info['description'];
66543
66544            if (!isset($info['stable']) || !$info['stable']) {
66545                $version_remote = 'none';
66546            } else {
66547                if ($info['unstable']) {
66548                    $version_remote = $info['unstable'];
66549                } else {
66550                    $version_remote = $info['stable'];
66551                }
66552                $version_remote .= ' ('.$info['state'].')';
66553            }
66554            $version = is_array($installed['version']) ? $installed['version']['release'] :
66555                $installed['version'];
66556            if ($channelinfo) {
66557                $packageinfo = array(
66558                    $channel,
66559                    $name,
66560                    $version_remote,
66561                    $version,
66562                    $desc,
66563                );
66564            } else {
66565                $packageinfo = array(
66566                    $name,
66567                    $version_remote,
66568                    $version,
66569                    $desc,
66570                );
66571            }
66572            $data['data'][$info['category']][] = $packageinfo;
66573        }
66574
66575        $this->ui->outputData($data, $command);
66576        $this->config->set('default_channel', $channel);
66577        return true;
66578    }
66579
66580    function &getDownloader($options)
66581    {
66582        if (!class_exists('PEAR_Downloader')) {
66583            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader.php';
66584        }
66585        $a = &new PEAR_Downloader($this->ui, $options, $this->config);
66586        return $a;
66587    }
66588
66589    function doDownload($command, $options, $params)
66590    {
66591        // make certain that dependencies are ignored
66592        $options['downloadonly'] = 1;
66593
66594        // eliminate error messages for preferred_state-related errors
66595        /* TODO: Should be an option, but until now download does respect
66596           prefered state */
66597        /* $options['ignorepreferred_state'] = 1; */
66598        // eliminate error messages for preferred_state-related errors
66599
66600        $downloader = &$this->getDownloader($options);
66601        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
66602        $e = $downloader->setDownloadDir(getcwd());
66603        PEAR::staticPopErrorHandling();
66604        if (PEAR::isError($e)) {
66605            return $this->raiseError('Current directory is not writeable, cannot download');
66606        }
66607
66608        $errors = array();
66609        $downloaded = array();
66610        $err = $downloader->download($params);
66611        if (PEAR::isError($err)) {
66612            return $err;
66613        }
66614
66615        $errors = $downloader->getErrorMsgs();
66616        if (count($errors)) {
66617            foreach ($errors as $error) {
66618                if ($error !== null) {
66619                    $this->ui->outputData($error);
66620                }
66621            }
66622
66623            return $this->raiseError("$command failed");
66624        }
66625
66626        $downloaded = $downloader->getDownloadedPackages();
66627        foreach ($downloaded as $pkg) {
66628            $this->ui->outputData("File $pkg[file] downloaded", $command);
66629        }
66630
66631        return true;
66632    }
66633
66634    function downloadCallback($msg, $params = null)
66635    {
66636        if ($msg == 'done') {
66637            $this->bytes_downloaded = $params;
66638        }
66639    }
66640
66641    function doListUpgrades($command, $options, $params)
66642    {
66643        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
66644        if (isset($params[0]) && !is_array(PEAR_Common::betterStates($params[0]))) {
66645            return $this->raiseError($params[0] . ' is not a valid state (stable/beta/alpha/devel/etc.) try "pear help list-upgrades"');
66646        }
66647
66648        $savechannel = $channel = $this->config->get('default_channel');
66649        $reg = &$this->config->getRegistry();
66650        foreach ($reg->listChannels() as $channel) {
66651            $inst = array_flip($reg->listPackages($channel));
66652            if (!count($inst)) {
66653                continue;
66654            }
66655
66656            if ($channel == '__uri') {
66657                continue;
66658            }
66659
66660            $this->config->set('default_channel', $channel);
66661            $state = empty($params[0]) ? $this->config->get('preferred_state') : $params[0];
66662
66663            $caption = $channel . ' Available Upgrades';
66664            $chan = $reg->getChannel($channel);
66665            if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
66666                return $e;
66667            }
66668
66669            $latest = array();
66670            $base2  = false;
66671            $preferred_mirror = $this->config->get('preferred_mirror');
66672            if ($chan->supportsREST($preferred_mirror) &&
66673                (
66674                   //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) ||
66675                   ($base  = $chan->getBaseURL('REST1.0', $preferred_mirror))
66676                )
66677
66678            ) {
66679                if ($base2) {
66680                    $rest = &$this->config->getREST('1.4', array());
66681                    $base = $base2;
66682                } else {
66683                    $rest = &$this->config->getREST('1.0', array());
66684                }
66685
66686                if (empty($state) || $state == 'any') {
66687                    $state = false;
66688                } else {
66689                    $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')';
66690                }
66691
66692                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
66693                $latest = $rest->listLatestUpgrades($base, $state, $inst, $channel, $reg);
66694                PEAR::staticPopErrorHandling();
66695            }
66696
66697            if (PEAR::isError($latest)) {
66698                $this->ui->outputData($latest->getMessage());
66699                continue;
66700            }
66701
66702            $caption .= ':';
66703            if (PEAR::isError($latest)) {
66704                $this->config->set('default_channel', $savechannel);
66705                return $latest;
66706            }
66707
66708            $data = array(
66709                'caption' => $caption,
66710                'border' => 1,
66711                'headline' => array('Channel', 'Package', 'Local', 'Remote', 'Size'),
66712                'channel' => $channel
66713                );
66714
66715            foreach ((array)$latest as $pkg => $info) {
66716                $package = strtolower($pkg);
66717                if (!isset($inst[$package])) {
66718                    // skip packages we don't have installed
66719                    continue;
66720                }
66721
66722                extract($info);
66723                $inst_version = $reg->packageInfo($package, 'version', $channel);
66724                $inst_state   = $reg->packageInfo($package, 'release_state', $channel);
66725                if (version_compare("$version", "$inst_version", "le")) {
66726                    // installed version is up-to-date
66727                    continue;
66728                }
66729
66730                if ($filesize >= 20480) {
66731                    $filesize += 1024 - ($filesize % 1024);
66732                    $fs = sprintf("%dkB", $filesize / 1024);
66733                } elseif ($filesize > 0) {
66734                    $filesize += 103 - ($filesize % 103);
66735                    $fs = sprintf("%.1fkB", $filesize / 1024.0);
66736                } else {
66737                    $fs = "  -"; // XXX center instead
66738                }
66739
66740                $data['data'][] = array($channel, $pkg, "$inst_version ($inst_state)", "$version ($state)", $fs);
66741            }
66742
66743            if (isset($options['channelinfo'])) {
66744                if (empty($data['data'])) {
66745                    unset($data['headline']);
66746                    if (count($inst) == 0) {
66747                        $data['data'] = '(no packages installed)';
66748                    } else {
66749                        $data['data'] = '(no upgrades available)';
66750                    }
66751                }
66752                $this->ui->outputData($data, $command);
66753            } else {
66754                if (empty($data['data'])) {
66755                    $this->ui->outputData('Channel ' . $channel . ': No upgrades available');
66756                } else {
66757                    $this->ui->outputData($data, $command);
66758                }
66759            }
66760        }
66761
66762        $this->config->set('default_channel', $savechannel);
66763        return true;
66764    }
66765
66766    function doClearCache($command, $options, $params)
66767    {
66768        $cache_dir = $this->config->get('cache_dir');
66769        $verbose   = $this->config->get('verbose');
66770        $output = '';
66771        if (!file_exists($cache_dir) || !is_dir($cache_dir)) {
66772            return $this->raiseError("$cache_dir does not exist or is not a directory");
66773        }
66774
66775        if (!($dp = @opendir($cache_dir))) {
66776            return $this->raiseError("opendir($cache_dir) failed: $php_errormsg");
66777        }
66778
66779        if ($verbose >= 1) {
66780            $output .= "reading directory $cache_dir\n";
66781        }
66782
66783        $num = 0;
66784        while ($ent = readdir($dp)) {
66785            if (preg_match('/rest.cache(file|id)\\z/', $ent)) {
66786                $path = $cache_dir . DIRECTORY_SEPARATOR . $ent;
66787                if (file_exists($path)) {
66788                    $ok = @unlink($path);
66789                } else {
66790                    $ok = false;
66791                    $php_errormsg = '';
66792                }
66793
66794                if ($ok) {
66795                    if ($verbose >= 2) {
66796                        $output .= "deleted $path\n";
66797                    }
66798                    $num++;
66799                } elseif ($verbose >= 1) {
66800                    $output .= "failed to delete $path $php_errormsg\n";
66801                }
66802            }
66803        }
66804
66805        closedir($dp);
66806        if ($verbose >= 1) {
66807            $output .= "$num cache entries cleared\n";
66808        }
66809
66810        $this->ui->outputData(rtrim($output), $command);
66811        return $num;
66812    }
66813}<commands version="1.0">
66814 <remote-info>
66815  <summary>Information About Remote Packages</summary>
66816  <function>doRemoteInfo</function>
66817  <shortcut>ri</shortcut>
66818  <options />
66819  <doc>&lt;package&gt;
66820Get details on a package from the server.</doc>
66821 </remote-info>
66822 <list-upgrades>
66823  <summary>List Available Upgrades</summary>
66824  <function>doListUpgrades</function>
66825  <shortcut>lu</shortcut>
66826  <options>
66827   <channelinfo>
66828    <shortopt>i</shortopt>
66829    <doc>output fully channel-aware data, even on failure</doc>
66830   </channelinfo>
66831  </options>
66832  <doc>[preferred_state]
66833List releases on the server of packages you have installed where
66834a newer version is available with the same release state (stable etc.)
66835or the state passed as the second parameter.</doc>
66836 </list-upgrades>
66837 <remote-list>
66838  <summary>List Remote Packages</summary>
66839  <function>doRemoteList</function>
66840  <shortcut>rl</shortcut>
66841  <options>
66842   <channel>
66843    <shortopt>c</shortopt>
66844    <doc>specify a channel other than the default channel</doc>
66845    <arg>CHAN</arg>
66846   </channel>
66847  </options>
66848  <doc>
66849Lists the packages available on the configured server along with the
66850latest stable release of each package.</doc>
66851 </remote-list>
66852 <search>
66853  <summary>Search remote package database</summary>
66854  <function>doSearch</function>
66855  <shortcut>sp</shortcut>
66856  <options>
66857   <channel>
66858    <shortopt>c</shortopt>
66859    <doc>specify a channel other than the default channel</doc>
66860    <arg>CHAN</arg>
66861   </channel>
66862   <allchannels>
66863    <shortopt>a</shortopt>
66864    <doc>search packages from all known channels</doc>
66865   </allchannels>
66866   <channelinfo>
66867    <shortopt>i</shortopt>
66868    <doc>output fully channel-aware data, even on failure</doc>
66869   </channelinfo>
66870  </options>
66871  <doc>[packagename] [packageinfo]
66872Lists all packages which match the search parameters.  The first
66873parameter is a fragment of a packagename.  The default channel
66874will be used unless explicitly overridden.  The second parameter
66875will be used to match any portion of the summary/description</doc>
66876 </search>
66877 <list-all>
66878  <summary>List All Packages</summary>
66879  <function>doListAll</function>
66880  <shortcut>la</shortcut>
66881  <options>
66882   <channel>
66883    <shortopt>c</shortopt>
66884    <doc>specify a channel other than the default channel</doc>
66885    <arg>CHAN</arg>
66886   </channel>
66887   <channelinfo>
66888    <shortopt>i</shortopt>
66889    <doc>output fully channel-aware data, even on failure</doc>
66890   </channelinfo>
66891  </options>
66892  <doc>
66893Lists the packages available on the configured server along with the
66894latest stable release of each package.</doc>
66895 </list-all>
66896 <download>
66897  <summary>Download Package</summary>
66898  <function>doDownload</function>
66899  <shortcut>d</shortcut>
66900  <options>
66901   <nocompress>
66902    <shortopt>Z</shortopt>
66903    <doc>download an uncompressed (.tar) file</doc>
66904   </nocompress>
66905  </options>
66906  <doc>&lt;package&gt;...
66907Download package tarballs.  The files will be named as suggested by the
66908server, for example if you download the DB package and the latest stable
66909version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.</doc>
66910 </download>
66911 <clear-cache>
66912  <summary>Clear Web Services Cache</summary>
66913  <function>doClearCache</function>
66914  <shortcut>cc</shortcut>
66915  <options />
66916  <doc>
66917Clear the XML-RPC/REST cache.  See also the cache_ttl configuration
66918parameter.
66919</doc>
66920 </clear-cache>
66921</commands><?php
66922/**
66923 * PEAR_Command_Test (run-tests)
66924 *
66925 * PHP versions 4 and 5
66926 *
66927 * @category   pear
66928 * @package    PEAR
66929 * @author     Stig Bakken <ssb@php.net>
66930 * @author     Martin Jansen <mj@php.net>
66931 * @author     Greg Beaver <cellog@php.net>
66932 * @copyright  1997-2009 The Authors
66933 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
66934 * @version    CVS: $Id: Test.php 313023 2011-07-06 19:17:11Z dufuz $
66935 * @link       http://pear.php.net/package/PEAR
66936 * @since      File available since Release 0.1
66937 */
66938
66939/**
66940 * base class
66941 */
66942require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command/Common.php';
66943
66944/**
66945 * PEAR commands for login/logout
66946 *
66947 * @category   pear
66948 * @package    PEAR
66949 * @author     Stig Bakken <ssb@php.net>
66950 * @author     Martin Jansen <mj@php.net>
66951 * @author     Greg Beaver <cellog@php.net>
66952 * @copyright  1997-2009 The Authors
66953 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
66954 * @version    Release: 1.9.4
66955 * @link       http://pear.php.net/package/PEAR
66956 * @since      Class available since Release 0.1
66957 */
66958
66959class PEAR_Command_Test extends PEAR_Command_Common
66960{
66961    var $commands = array(
66962        'run-tests' => array(
66963            'summary' => 'Run Regression Tests',
66964            'function' => 'doRunTests',
66965            'shortcut' => 'rt',
66966            'options' => array(
66967                'recur' => array(
66968                    'shortopt' => 'r',
66969                    'doc' => 'Run tests in child directories, recursively.  4 dirs deep maximum',
66970                ),
66971                'ini' => array(
66972                    'shortopt' => 'i',
66973                    'doc' => 'actual string of settings to pass to php in format " -d setting=blah"',
66974                    'arg' => 'SETTINGS'
66975                ),
66976                'realtimelog' => array(
66977                    'shortopt' => 'l',
66978                    'doc' => 'Log test runs/results as they are run',
66979                ),
66980                'quiet' => array(
66981                    'shortopt' => 'q',
66982                    'doc' => 'Only display detail for failed tests',
66983                ),
66984                'simple' => array(
66985                    'shortopt' => 's',
66986                    'doc' => 'Display simple output for all tests',
66987                ),
66988                'package' => array(
66989                    'shortopt' => 'p',
66990                    'doc' => 'Treat parameters as installed packages from which to run tests',
66991                ),
66992                'phpunit' => array(
66993                    'shortopt' => 'u',
66994                    'doc' => 'Search parameters for AllTests.php, and use that to run phpunit-based tests
66995If none is found, all .phpt tests will be tried instead.',
66996                ),
66997                'tapoutput' => array(
66998                    'shortopt' => 't',
66999                    'doc' => 'Output run-tests.log in TAP-compliant format',
67000                ),
67001                'cgi' => array(
67002                    'shortopt' => 'c',
67003                    'doc' => 'CGI php executable (needed for tests with POST/GET section)',
67004                    'arg' => 'PHPCGI',
67005                ),
67006                'coverage' => array(
67007                    'shortopt' => 'x',
67008                    'doc'      => 'Generate a code coverage report (requires Xdebug 2.0.0+)',
67009                ),
67010            ),
67011            'doc' => '[testfile|dir ...]
67012Run regression tests with PHP\'s regression testing script (run-tests.php).',
67013            ),
67014        );
67015
67016    var $output;
67017
67018    /**
67019     * PEAR_Command_Test constructor.
67020     *
67021     * @access public
67022     */
67023    function PEAR_Command_Test(&$ui, &$config)
67024    {
67025        parent::PEAR_Command_Common($ui, $config);
67026    }
67027
67028    function doRunTests($command, $options, $params)
67029    {
67030        if (isset($options['phpunit']) && isset($options['tapoutput'])) {
67031            return $this->raiseError('ERROR: cannot use both --phpunit and --tapoutput at the same time');
67032        }
67033
67034        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
67035        require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
67036        $log = new PEAR_Common;
67037        $log->ui = &$this->ui; // slightly hacky, but it will work
67038        $tests = array();
67039        $depth = isset($options['recur']) ? 14 : 1;
67040
67041        if (!count($params)) {
67042            $params[] = '.';
67043        }
67044
67045        if (isset($options['package'])) {
67046            $oldparams = $params;
67047            $params = array();
67048            $reg = &$this->config->getRegistry();
67049            foreach ($oldparams as $param) {
67050                $pname = $reg->parsePackageName($param, $this->config->get('default_channel'));
67051                if (PEAR::isError($pname)) {
67052                    return $this->raiseError($pname);
67053                }
67054
67055                $package = &$reg->getPackage($pname['package'], $pname['channel']);
67056                if (!$package) {
67057                    return PEAR::raiseError('Unknown package "' .
67058                        $reg->parsedPackageNameToString($pname) . '"');
67059                }
67060
67061                $filelist = $package->getFilelist();
67062                foreach ($filelist as $name => $atts) {
67063                    if (isset($atts['role']) && $atts['role'] != 'test') {
67064                        continue;
67065                    }
67066
67067                    if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $name)) {
67068                        $params[] = $atts['installed_as'];
67069                        continue;
67070                    } elseif (!preg_match('/\.phpt\\z/', $name)) {
67071                        continue;
67072                    }
67073                    $params[] = $atts['installed_as'];
67074                }
67075            }
67076        }
67077
67078        foreach ($params as $p) {
67079            if (is_dir($p)) {
67080                if (isset($options['phpunit'])) {
67081                    $dir = System::find(array($p, '-type', 'f',
67082                                                '-maxdepth', $depth,
67083                                                '-name', 'AllTests.php'));
67084                    if (count($dir)) {
67085                        foreach ($dir as $p) {
67086                            $p = realpath($p);
67087                            if (!count($tests) ||
67088                                  (count($tests) && strlen($p) < strlen($tests[0]))) {
67089                                // this is in a higher-level directory, use this one instead.
67090                                $tests = array($p);
67091                            }
67092                        }
67093                    }
67094                    continue;
67095                }
67096
67097                $args  = array($p, '-type', 'f', '-name', '*.phpt');
67098            } else {
67099                if (isset($options['phpunit'])) {
67100                    if (preg_match('/AllTests\.php\\z/i', $p)) {
67101                        $p = realpath($p);
67102                        if (!count($tests) ||
67103                              (count($tests) && strlen($p) < strlen($tests[0]))) {
67104                            // this is in a higher-level directory, use this one instead.
67105                            $tests = array($p);
67106                        }
67107                    }
67108                    continue;
67109                }
67110
67111                if (file_exists($p) && preg_match('/\.phpt$/', $p)) {
67112                    $tests[] = $p;
67113                    continue;
67114                }
67115
67116                if (!preg_match('/\.phpt\\z/', $p)) {
67117                    $p .= '.phpt';
67118                }
67119
67120                $args  = array(dirname($p), '-type', 'f', '-name', $p);
67121            }
67122
67123            if (!isset($options['recur'])) {
67124                $args[] = '-maxdepth';
67125                $args[] = 1;
67126            }
67127
67128            $dir   = System::find($args);
67129            $tests = array_merge($tests, $dir);
67130        }
67131
67132        $ini_settings = '';
67133        if (isset($options['ini'])) {
67134            $ini_settings .= $options['ini'];
67135        }
67136
67137        if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) {
67138            $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}";
67139        }
67140
67141        if ($ini_settings) {
67142            $this->ui->outputData('Using INI settings: "' . $ini_settings . '"');
67143        }
67144
67145        $skipped = $passed = $failed = array();
67146        $tests_count = count($tests);
67147        $this->ui->outputData('Running ' . $tests_count . ' tests', $command);
67148        $start = time();
67149        if (isset($options['realtimelog']) && file_exists('run-tests.log')) {
67150            unlink('run-tests.log');
67151        }
67152
67153        if (isset($options['tapoutput'])) {
67154            $tap = '1..' . $tests_count . "\n";
67155        }
67156
67157        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/RunTest.php';
67158        $run = new PEAR_RunTest($log, $options);
67159        $run->tests_count = $tests_count;
67160
67161        if (isset($options['coverage']) && extension_loaded('xdebug')){
67162            $run->xdebug_loaded = true;
67163        } else {
67164            $run->xdebug_loaded = false;
67165        }
67166
67167        $j = $i = 1;
67168        foreach ($tests as $t) {
67169            if (isset($options['realtimelog'])) {
67170                $fp = @fopen('run-tests.log', 'a');
67171                if ($fp) {
67172                    fwrite($fp, "Running test [$i / $tests_count] $t...");
67173                    fclose($fp);
67174                }
67175            }
67176            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
67177            if (isset($options['phpunit'])) {
67178                $result = $run->runPHPUnit($t, $ini_settings);
67179            } else {
67180                $result = $run->run($t, $ini_settings, $j);
67181            }
67182            PEAR::staticPopErrorHandling();
67183            if (PEAR::isError($result)) {
67184                $this->ui->log($result->getMessage());
67185                continue;
67186            }
67187
67188            if (isset($options['tapoutput'])) {
67189                $tap .= $result[0] . ' ' . $i . $result[1] . "\n";
67190                continue;
67191            }
67192
67193            if (isset($options['realtimelog'])) {
67194                $fp = @fopen('run-tests.log', 'a');
67195                if ($fp) {
67196                    fwrite($fp, "$result\n");
67197                    fclose($fp);
67198                }
67199            }
67200
67201            if ($result == 'FAILED') {
67202                $failed[] = $t;
67203            }
67204            if ($result == 'PASSED') {
67205                $passed[] = $t;
67206            }
67207            if ($result == 'SKIPPED') {
67208                $skipped[] = $t;
67209            }
67210
67211            $j++;
67212        }
67213
67214        $total = date('i:s', time() - $start);
67215        if (isset($options['tapoutput'])) {
67216            $fp = @fopen('run-tests.log', 'w');
67217            if ($fp) {
67218                fwrite($fp, $tap, strlen($tap));
67219                fclose($fp);
67220                $this->ui->outputData('wrote TAP-format log to "' .realpath('run-tests.log') .
67221                    '"', $command);
67222            }
67223        } else {
67224            if (count($failed)) {
67225                $output = "TOTAL TIME: $total\n";
67226                $output .= count($passed) . " PASSED TESTS\n";
67227                $output .= count($skipped) . " SKIPPED TESTS\n";
67228                $output .= count($failed) . " FAILED TESTS:\n";
67229                foreach ($failed as $failure) {
67230                    $output .= $failure . "\n";
67231                }
67232
67233                $mode = isset($options['realtimelog']) ? 'a' : 'w';
67234                $fp   = @fopen('run-tests.log', $mode);
67235
67236                if ($fp) {
67237                    fwrite($fp, $output, strlen($output));
67238                    fclose($fp);
67239                    $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command);
67240                }
67241            } elseif (file_exists('run-tests.log') && !is_dir('run-tests.log')) {
67242                @unlink('run-tests.log');
67243            }
67244        }
67245        $this->ui->outputData('TOTAL TIME: ' . $total);
67246        $this->ui->outputData(count($passed) . ' PASSED TESTS', $command);
67247        $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command);
67248        if (count($failed)) {
67249            $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command);
67250            foreach ($failed as $failure) {
67251                $this->ui->outputData($failure, $command);
67252            }
67253        }
67254
67255        return true;
67256    }
67257}<commands version="1.0">
67258 <run-tests>
67259  <summary>Run Regression Tests</summary>
67260  <function>doRunTests</function>
67261  <shortcut>rt</shortcut>
67262  <options>
67263   <recur>
67264    <shortopt>r</shortopt>
67265    <doc>Run tests in child directories, recursively.  4 dirs deep maximum</doc>
67266   </recur>
67267   <ini>
67268    <shortopt>i</shortopt>
67269    <doc>actual string of settings to pass to php in format &quot; -d setting=blah&quot;</doc>
67270    <arg>SETTINGS</arg>
67271   </ini>
67272   <realtimelog>
67273    <shortopt>l</shortopt>
67274    <doc>Log test runs/results as they are run</doc>
67275   </realtimelog>
67276   <quiet>
67277    <shortopt>q</shortopt>
67278    <doc>Only display detail for failed tests</doc>
67279   </quiet>
67280   <simple>
67281    <shortopt>s</shortopt>
67282    <doc>Display simple output for all tests</doc>
67283   </simple>
67284   <package>
67285    <shortopt>p</shortopt>
67286    <doc>Treat parameters as installed packages from which to run tests</doc>
67287   </package>
67288   <phpunit>
67289    <shortopt>u</shortopt>
67290    <doc>Search parameters for AllTests.php, and use that to run phpunit-based tests
67291If none is found, all .phpt tests will be tried instead.</doc>
67292   </phpunit>
67293   <tapoutput>
67294    <shortopt>t</shortopt>
67295    <doc>Output run-tests.log in TAP-compliant format</doc>
67296   </tapoutput>
67297   <cgi>
67298    <shortopt>c</shortopt>
67299    <doc>CGI php executable (needed for tests with POST/GET section)</doc>
67300    <arg>PHPCGI</arg>
67301   </cgi>
67302   <coverage>
67303    <shortopt>x</shortopt>
67304    <doc>Generate a code coverage report (requires Xdebug 2.0.0+)</doc>
67305   </coverage>
67306  </options>
67307  <doc>[testfile|dir ...]
67308Run regression tests with PHP&#039;s regression testing script (run-tests.php).</doc>
67309 </run-tests>
67310</commands><?php
67311/**
67312 * PEAR_Common, the base class for the PEAR Installer
67313 *
67314 * PHP versions 4 and 5
67315 *
67316 * @category   pear
67317 * @package    PEAR
67318 * @author     Stig Bakken <ssb@php.net>
67319 * @author     Tomas V. V. Cox <cox@idecnet.com>
67320 * @author     Greg Beaver <cellog@php.net>
67321 * @copyright  1997-2009 The Authors
67322 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
67323 * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
67324 * @link       http://pear.php.net/package/PEAR
67325 * @since      File available since Release 0.1.0
67326 * @deprecated File deprecated since Release 1.4.0a1
67327 */
67328
67329/**
67330 * Include error handling
67331 */
67332require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
67333
67334/**
67335 * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
67336 */
67337define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
67338define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
67339define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/');
67340
67341// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
67342define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
67343define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '\\z/i');
67344
67345// XXX far from perfect :-)
67346define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG .
67347    ')(-([.0-9a-zA-Z]+))?');
67348define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG .
67349    '\\z/');
67350
67351define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+');
67352define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '\\z/');
67353
67354// this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED
67355define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*');
67356define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '\\z/i');
67357
67358define('_PEAR_CHANNELS_PACKAGE_PREG',  '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/('
67359         . _PEAR_COMMON_PACKAGE_NAME_PREG . ')');
67360define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '\\z/i');
67361
67362define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::('
67363    . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?');
67364define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '\\z/');
67365
67366/**
67367 * List of temporary files and directories registered by
67368 * PEAR_Common::addTempFile().
67369 * @var array
67370 */
67371$GLOBALS['_PEAR_Common_tempfiles'] = array();
67372
67373/**
67374 * Valid maintainer roles
67375 * @var array
67376 */
67377$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
67378
67379/**
67380 * Valid release states
67381 * @var array
67382 */
67383$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
67384
67385/**
67386 * Valid dependency types
67387 * @var array
67388 */
67389$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
67390
67391/**
67392 * Valid dependency relations
67393 * @var array
67394 */
67395$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');
67396
67397/**
67398 * Valid file roles
67399 * @var array
67400 */
67401$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
67402
67403/**
67404 * Valid replacement types
67405 * @var array
67406 */
67407$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
67408
67409/**
67410 * Valid "provide" types
67411 * @var array
67412 */
67413$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
67414
67415/**
67416 * Valid "provide" types
67417 * @var array
67418 */
67419$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');
67420
67421/**
67422 * Class providing common functionality for PEAR administration classes.
67423 * @category   pear
67424 * @package    PEAR
67425 * @author     Stig Bakken <ssb@php.net>
67426 * @author     Tomas V. V. Cox <cox@idecnet.com>
67427 * @author     Greg Beaver <cellog@php.net>
67428 * @copyright  1997-2009 The Authors
67429 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
67430 * @version    Release: 1.9.4
67431 * @link       http://pear.php.net/package/PEAR
67432 * @since      Class available since Release 1.4.0a1
67433 * @deprecated This class will disappear, and its components will be spread
67434 *             into smaller classes, like the AT&T breakup, as of Release 1.4.0a1
67435 */
67436class PEAR_Common extends PEAR
67437{
67438    /**
67439     * User Interface object (PEAR_Frontend_* class).  If null,
67440     * the log() method uses print.
67441     * @var object
67442     */
67443    var $ui = null;
67444
67445    /**
67446     * Configuration object (PEAR_Config).
67447     * @var PEAR_Config
67448     */
67449    var $config = null;
67450
67451    /** stack of elements, gives some sort of XML context */
67452    var $element_stack = array();
67453
67454    /** name of currently parsed XML element */
67455    var $current_element;
67456
67457    /** array of attributes of the currently parsed XML element */
67458    var $current_attributes = array();
67459
67460    /** assoc with information about a package */
67461    var $pkginfo = array();
67462
67463    var $current_path = null;
67464
67465    /**
67466     * Flag variable used to mark a valid package file
67467     * @var boolean
67468     * @access private
67469     */
67470    var $_validPackageFile;
67471
67472    /**
67473     * PEAR_Common constructor
67474     *
67475     * @access public
67476     */
67477    function PEAR_Common()
67478    {
67479        parent::PEAR();
67480        $this->config = &PEAR_Config::singleton();
67481        $this->debug = $this->config->get('verbose');
67482    }
67483
67484    /**
67485     * PEAR_Common destructor
67486     *
67487     * @access private
67488     */
67489    function _PEAR_Common()
67490    {
67491        // doesn't work due to bug #14744
67492        //$tempfiles = $this->_tempfiles;
67493        $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
67494        while ($file = array_shift($tempfiles)) {
67495            if (@is_dir($file)) {
67496                if (!class_exists('System')) {
67497                    require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
67498                }
67499
67500                System::rm(array('-rf', $file));
67501            } elseif (file_exists($file)) {
67502                unlink($file);
67503            }
67504        }
67505    }
67506
67507    /**
67508     * Register a temporary file or directory.  When the destructor is
67509     * executed, all registered temporary files and directories are
67510     * removed.
67511     *
67512     * @param string  $file  name of file or directory
67513     *
67514     * @return void
67515     *
67516     * @access public
67517     */
67518    function addTempFile($file)
67519    {
67520        if (!class_exists('PEAR_Frontend')) {
67521            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Frontend.php';
67522        }
67523        PEAR_Frontend::addTempFile($file);
67524    }
67525
67526    /**
67527     * Wrapper to System::mkDir(), creates a directory as well as
67528     * any necessary parent directories.
67529     *
67530     * @param string  $dir  directory name
67531     *
67532     * @return bool TRUE on success, or a PEAR error
67533     *
67534     * @access public
67535     */
67536    function mkDirHier($dir)
67537    {
67538        // Only used in Installer - move it there ?
67539        $this->log(2, "+ create dir $dir");
67540        if (!class_exists('System')) {
67541            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
67542        }
67543        return System::mkDir(array('-p', $dir));
67544    }
67545
67546    /**
67547     * Logging method.
67548     *
67549     * @param int    $level  log level (0 is quiet, higher is noisier)
67550     * @param string $msg    message to write to the log
67551     *
67552     * @return void
67553     *
67554     * @access public
67555     * @static
67556     */
67557    function log($level, $msg, $append_crlf = true)
67558    {
67559        if ($this->debug >= $level) {
67560            if (!class_exists('PEAR_Frontend')) {
67561                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Frontend.php';
67562            }
67563
67564            $ui = &PEAR_Frontend::singleton();
67565            if (is_a($ui, 'PEAR_Frontend')) {
67566                $ui->log($msg, $append_crlf);
67567            } else {
67568                print "$msg\n";
67569            }
67570        }
67571    }
67572
67573    /**
67574     * Create and register a temporary directory.
67575     *
67576     * @param string $tmpdir (optional) Directory to use as tmpdir.
67577     *                       Will use system defaults (for example
67578     *                       /tmp or c:\windows\temp) if not specified
67579     *
67580     * @return string name of created directory
67581     *
67582     * @access public
67583     */
67584    function mkTempDir($tmpdir = '')
67585    {
67586        $topt = $tmpdir ? array('-t', $tmpdir) : array();
67587        $topt = array_merge($topt, array('-d', 'pear'));
67588        if (!class_exists('System')) {
67589            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
67590        }
67591
67592        if (!$tmpdir = System::mktemp($topt)) {
67593            return false;
67594        }
67595
67596        $this->addTempFile($tmpdir);
67597        return $tmpdir;
67598    }
67599
67600    /**
67601     * Set object that represents the frontend to be used.
67602     *
67603     * @param  object Reference of the frontend object
67604     * @return void
67605     * @access public
67606     */
67607    function setFrontendObject(&$ui)
67608    {
67609        $this->ui = &$ui;
67610    }
67611
67612    /**
67613     * Return an array containing all of the states that are more stable than
67614     * or equal to the passed in state
67615     *
67616     * @param string Release state
67617     * @param boolean Determines whether to include $state in the list
67618     * @return false|array False if $state is not a valid release state
67619     */
67620    function betterStates($state, $include = false)
67621    {
67622        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
67623        $i = array_search($state, $states);
67624        if ($i === false) {
67625            return false;
67626        }
67627        if ($include) {
67628            $i--;
67629        }
67630        return array_slice($states, $i + 1);
67631    }
67632
67633    /**
67634     * Get the valid roles for a PEAR package maintainer
67635     *
67636     * @return array
67637     * @static
67638     */
67639    function getUserRoles()
67640    {
67641        return $GLOBALS['_PEAR_Common_maintainer_roles'];
67642    }
67643
67644    /**
67645     * Get the valid package release states of packages
67646     *
67647     * @return array
67648     * @static
67649     */
67650    function getReleaseStates()
67651    {
67652        return $GLOBALS['_PEAR_Common_release_states'];
67653    }
67654
67655    /**
67656     * Get the implemented dependency types (php, ext, pkg etc.)
67657     *
67658     * @return array
67659     * @static
67660     */
67661    function getDependencyTypes()
67662    {
67663        return $GLOBALS['_PEAR_Common_dependency_types'];
67664    }
67665
67666    /**
67667     * Get the implemented dependency relations (has, lt, ge etc.)
67668     *
67669     * @return array
67670     * @static
67671     */
67672    function getDependencyRelations()
67673    {
67674        return $GLOBALS['_PEAR_Common_dependency_relations'];
67675    }
67676
67677    /**
67678     * Get the implemented file roles
67679     *
67680     * @return array
67681     * @static
67682     */
67683    function getFileRoles()
67684    {
67685        return $GLOBALS['_PEAR_Common_file_roles'];
67686    }
67687
67688    /**
67689     * Get the implemented file replacement types in
67690     *
67691     * @return array
67692     * @static
67693     */
67694    function getReplacementTypes()
67695    {
67696        return $GLOBALS['_PEAR_Common_replacement_types'];
67697    }
67698
67699    /**
67700     * Get the implemented file replacement types in
67701     *
67702     * @return array
67703     * @static
67704     */
67705    function getProvideTypes()
67706    {
67707        return $GLOBALS['_PEAR_Common_provide_types'];
67708    }
67709
67710    /**
67711     * Get the implemented file replacement types in
67712     *
67713     * @return array
67714     * @static
67715     */
67716    function getScriptPhases()
67717    {
67718        return $GLOBALS['_PEAR_Common_script_phases'];
67719    }
67720
67721    /**
67722     * Test whether a string contains a valid package name.
67723     *
67724     * @param string $name the package name to test
67725     *
67726     * @return bool
67727     *
67728     * @access public
67729     */
67730    function validPackageName($name)
67731    {
67732        return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
67733    }
67734
67735    /**
67736     * Test whether a string contains a valid package version.
67737     *
67738     * @param string $ver the package version to test
67739     *
67740     * @return bool
67741     *
67742     * @access public
67743     */
67744    function validPackageVersion($ver)
67745    {
67746        return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
67747    }
67748
67749    /**
67750     * @param string $path relative or absolute include path
67751     * @return boolean
67752     * @static
67753     */
67754    function isIncludeable($path)
67755    {
67756        if (file_exists($path) && is_readable($path)) {
67757            return true;
67758        }
67759
67760        $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
67761        foreach ($ipath as $include) {
67762            $test = realpath($include . DIRECTORY_SEPARATOR . $path);
67763            if (file_exists($test) && is_readable($test)) {
67764                return true;
67765            }
67766        }
67767
67768        return false;
67769    }
67770
67771    function _postProcessChecks($pf)
67772    {
67773        if (!PEAR::isError($pf)) {
67774            return $this->_postProcessValidPackagexml($pf);
67775        }
67776
67777        $errs = $pf->getUserinfo();
67778        if (is_array($errs)) {
67779            foreach ($errs as $error) {
67780                $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
67781            }
67782        }
67783
67784        return $pf;
67785    }
67786
67787    /**
67788     * Returns information about a package file.  Expects the name of
67789     * a gzipped tar file as input.
67790     *
67791     * @param string  $file  name of .tgz file
67792     *
67793     * @return array  array with package information
67794     *
67795     * @access public
67796     * @deprecated use PEAR_PackageFile->fromTgzFile() instead
67797     *
67798     */
67799    function infoFromTgzFile($file)
67800    {
67801        $packagefile = &new PEAR_PackageFile($this->config);
67802        $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL);
67803        return $this->_postProcessChecks($pf);
67804    }
67805
67806    /**
67807     * Returns information about a package file.  Expects the name of
67808     * a package xml file as input.
67809     *
67810     * @param string  $descfile  name of package xml file
67811     *
67812     * @return array  array with package information
67813     *
67814     * @access public
67815     * @deprecated use PEAR_PackageFile->fromPackageFile() instead
67816     *
67817     */
67818    function infoFromDescriptionFile($descfile)
67819    {
67820        $packagefile = &new PEAR_PackageFile($this->config);
67821        $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
67822        return $this->_postProcessChecks($pf);
67823    }
67824
67825    /**
67826     * Returns information about a package file.  Expects the contents
67827     * of a package xml file as input.
67828     *
67829     * @param string  $data  contents of package.xml file
67830     *
67831     * @return array   array with package information
67832     *
67833     * @access public
67834     * @deprecated use PEAR_PackageFile->fromXmlstring() instead
67835     *
67836     */
67837    function infoFromString($data)
67838    {
67839        $packagefile = &new PEAR_PackageFile($this->config);
67840        $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false);
67841        return $this->_postProcessChecks($pf);
67842    }
67843
67844    /**
67845     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
67846     * @return array
67847     */
67848    function _postProcessValidPackagexml(&$pf)
67849    {
67850        if (!is_a($pf, 'PEAR_PackageFile_v2')) {
67851            $this->pkginfo = $pf->toArray();
67852            return $this->pkginfo;
67853        }
67854
67855        // sort of make this into a package.xml 1.0-style array
67856        // changelog is not converted to old format.
67857        $arr = $pf->toArray(true);
67858        $arr = array_merge($arr, $arr['old']);
67859        unset($arr['old'], $arr['xsdversion'], $arr['contents'], $arr['compatible'],
67860              $arr['channel'], $arr['uri'], $arr['dependencies'], $arr['phprelease'],
67861              $arr['extsrcrelease'], $arr['zendextsrcrelease'], $arr['extbinrelease'],
67862              $arr['zendextbinrelease'], $arr['bundle'], $arr['lead'], $arr['developer'],
67863              $arr['helper'], $arr['contributor']);
67864        $arr['filelist'] = $pf->getFilelist();
67865        $this->pkginfo = $arr;
67866        return $arr;
67867    }
67868
67869    /**
67870     * Returns package information from different sources
67871     *
67872     * This method is able to extract information about a package
67873     * from a .tgz archive or from a XML package definition file.
67874     *
67875     * @access public
67876     * @param  string Filename of the source ('package.xml', '<package>.tgz')
67877     * @return string
67878     * @deprecated use PEAR_PackageFile->fromAnyFile() instead
67879     */
67880    function infoFromAny($info)
67881    {
67882        if (is_string($info) && file_exists($info)) {
67883            $packagefile = &new PEAR_PackageFile($this->config);
67884            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
67885            if (PEAR::isError($pf)) {
67886                $errs = $pf->getUserinfo();
67887                if (is_array($errs)) {
67888                    foreach ($errs as $error) {
67889                        $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
67890                    }
67891                }
67892
67893                return $pf;
67894            }
67895
67896            return $this->_postProcessValidPackagexml($pf);
67897        }
67898
67899        return $info;
67900    }
67901
67902    /**
67903     * Return an XML document based on the package info (as returned
67904     * by the PEAR_Common::infoFrom* methods).
67905     *
67906     * @param array  $pkginfo  package info
67907     *
67908     * @return string XML data
67909     *
67910     * @access public
67911     * @deprecated use a PEAR_PackageFile_v* object's generator instead
67912     */
67913    function xmlFromInfo($pkginfo)
67914    {
67915        $config      = &PEAR_Config::singleton();
67916        $packagefile = &new PEAR_PackageFile($config);
67917        $pf = &$packagefile->fromArray($pkginfo);
67918        $gen = &$pf->getDefaultGenerator();
67919        return $gen->toXml(PEAR_VALIDATE_PACKAGING);
67920    }
67921
67922    /**
67923     * Validate XML package definition file.
67924     *
67925     * @param  string $info Filename of the package archive or of the
67926     *                package definition file
67927     * @param  array $errors Array that will contain the errors
67928     * @param  array $warnings Array that will contain the warnings
67929     * @param  string $dir_prefix (optional) directory where source files
67930     *                may be found, or empty if they are not available
67931     * @access public
67932     * @return boolean
67933     * @deprecated use the validation of PEAR_PackageFile objects
67934     */
67935    function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
67936    {
67937        $config      = &PEAR_Config::singleton();
67938        $packagefile = &new PEAR_PackageFile($config);
67939        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
67940        if (strpos($info, '<?xml') !== false) {
67941            $pf = &$packagefile->fromXmlString($info, PEAR_VALIDATE_NORMAL, '');
67942        } else {
67943            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
67944        }
67945
67946        PEAR::staticPopErrorHandling();
67947        if (PEAR::isError($pf)) {
67948            $errs = $pf->getUserinfo();
67949            if (is_array($errs)) {
67950                foreach ($errs as $error) {
67951                    if ($error['level'] == 'error') {
67952                        $errors[] = $error['message'];
67953                    } else {
67954                        $warnings[] = $error['message'];
67955                    }
67956                }
67957            }
67958
67959            return false;
67960        }
67961
67962        return true;
67963    }
67964
67965    /**
67966     * Build a "provides" array from data returned by
67967     * analyzeSourceCode().  The format of the built array is like
67968     * this:
67969     *
67970     *  array(
67971     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
67972     *    ...
67973     *  )
67974     *
67975     *
67976     * @param array $srcinfo array with information about a source file
67977     * as returned by the analyzeSourceCode() method.
67978     *
67979     * @return void
67980     *
67981     * @access public
67982     *
67983     */
67984    function buildProvidesArray($srcinfo)
67985    {
67986        $file = basename($srcinfo['source_file']);
67987        $pn = '';
67988        if (isset($this->_packageName)) {
67989            $pn = $this->_packageName;
67990        }
67991
67992        $pnl = strlen($pn);
67993        foreach ($srcinfo['declared_classes'] as $class) {
67994            $key = "class;$class";
67995            if (isset($this->pkginfo['provides'][$key])) {
67996                continue;
67997            }
67998
67999            $this->pkginfo['provides'][$key] =
68000                array('file'=> $file, 'type' => 'class', 'name' => $class);
68001            if (isset($srcinfo['inheritance'][$class])) {
68002                $this->pkginfo['provides'][$key]['extends'] =
68003                    $srcinfo['inheritance'][$class];
68004            }
68005        }
68006
68007        foreach ($srcinfo['declared_methods'] as $class => $methods) {
68008            foreach ($methods as $method) {
68009                $function = "$class::$method";
68010                $key = "function;$function";
68011                if ($method{0} == '_' || !strcasecmp($method, $class) ||
68012                    isset($this->pkginfo['provides'][$key])) {
68013                    continue;
68014                }
68015
68016                $this->pkginfo['provides'][$key] =
68017                    array('file'=> $file, 'type' => 'function', 'name' => $function);
68018            }
68019        }
68020
68021        foreach ($srcinfo['declared_functions'] as $function) {
68022            $key = "function;$function";
68023            if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
68024                continue;
68025            }
68026
68027            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
68028                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
68029            }
68030
68031            $this->pkginfo['provides'][$key] =
68032                array('file'=> $file, 'type' => 'function', 'name' => $function);
68033        }
68034    }
68035
68036    /**
68037     * Analyze the source code of the given PHP file
68038     *
68039     * @param  string Filename of the PHP file
68040     * @return mixed
68041     * @access public
68042     */
68043    function analyzeSourceCode($file)
68044    {
68045        if (!class_exists('PEAR_PackageFile_v2_Validator')) {
68046            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2/Validator.php';
68047        }
68048
68049        $a = new PEAR_PackageFile_v2_Validator;
68050        return $a->analyzeSourceCode($file);
68051    }
68052
68053    function detectDependencies($any, $status_callback = null)
68054    {
68055        if (!function_exists("token_get_all")) {
68056            return false;
68057        }
68058
68059        if (PEAR::isError($info = $this->infoFromAny($any))) {
68060            return $this->raiseError($info);
68061        }
68062
68063        if (!is_array($info)) {
68064            return false;
68065        }
68066
68067        $deps = array();
68068        $used_c = $decl_c = $decl_f = $decl_m = array();
68069        foreach ($info['filelist'] as $file => $fa) {
68070            $tmp = $this->analyzeSourceCode($file);
68071            $used_c = @array_merge($used_c, $tmp['used_classes']);
68072            $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
68073            $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
68074            $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
68075            $inheri = @array_merge($inheri, $tmp['inheritance']);
68076        }
68077
68078        $used_c = array_unique($used_c);
68079        $decl_c = array_unique($decl_c);
68080        $undecl_c = array_diff($used_c, $decl_c);
68081
68082        return array('used_classes' => $used_c,
68083                     'declared_classes' => $decl_c,
68084                     'declared_methods' => $decl_m,
68085                     'declared_functions' => $decl_f,
68086                     'undeclared_classes' => $undecl_c,
68087                     'inheritance' => $inheri,
68088                     );
68089    }
68090
68091    /**
68092     * Download a file through HTTP.  Considers suggested file name in
68093     * Content-disposition: header and can run a callback function for
68094     * different events.  The callback will be called with two
68095     * parameters: the callback type, and parameters.  The implemented
68096     * callback types are:
68097     *
68098     *  'setup'       called at the very beginning, parameter is a UI object
68099     *                that should be used for all output
68100     *  'message'     the parameter is a string with an informational message
68101     *  'saveas'      may be used to save with a different file name, the
68102     *                parameter is the filename that is about to be used.
68103     *                If a 'saveas' callback returns a non-empty string,
68104     *                that file name will be used as the filename instead.
68105     *                Note that $save_dir will not be affected by this, only
68106     *                the basename of the file.
68107     *  'start'       download is starting, parameter is number of bytes
68108     *                that are expected, or -1 if unknown
68109     *  'bytesread'   parameter is the number of bytes read so far
68110     *  'done'        download is complete, parameter is the total number
68111     *                of bytes read
68112     *  'connfailed'  if the TCP connection fails, this callback is called
68113     *                with array(host,port,errno,errmsg)
68114     *  'writefailed' if writing to disk fails, this callback is called
68115     *                with array(destfile,errmsg)
68116     *
68117     * If an HTTP proxy has been configured (http_proxy PEAR_Config
68118     * setting), the proxy will be used.
68119     *
68120     * @param string  $url       the URL to download
68121     * @param object  $ui        PEAR_Frontend_* instance
68122     * @param object  $config    PEAR_Config instance
68123     * @param string  $save_dir  (optional) directory to save file in
68124     * @param mixed   $callback  (optional) function/method to call for status
68125     *                           updates
68126     *
68127     * @return string  Returns the full path of the downloaded file or a PEAR
68128     *                 error on failure.  If the error is caused by
68129     *                 socket-related errors, the error object will
68130     *                 have the fsockopen error code available through
68131     *                 getCode().
68132     *
68133     * @access public
68134     * @deprecated in favor of PEAR_Downloader::downloadHttp()
68135     */
68136    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
68137    {
68138        if (!class_exists('PEAR_Downloader')) {
68139            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader.php';
68140        }
68141        return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback);
68142    }
68143}
68144
68145require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Config.php';
68146require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';<?php
68147/**
68148 * PEAR_Config, customized configuration handling for the PEAR Installer
68149 *
68150 * PHP versions 4 and 5
68151 *
68152 * @category   pear
68153 * @package    PEAR
68154 * @author     Stig Bakken <ssb@php.net>
68155 * @author     Greg Beaver <cellog@php.net>
68156 * @copyright  1997-2009 The Authors
68157 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
68158 * @version    CVS: $Id: Config.php 313023 2011-07-06 19:17:11Z dufuz $
68159 * @link       http://pear.php.net/package/PEAR
68160 * @since      File available since Release 0.1
68161 */
68162
68163/**
68164 * Required for error handling
68165 */
68166require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
68167require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Registry.php';
68168require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Installer/Role.php';
68169require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
68170
68171/**
68172 * Last created PEAR_Config instance.
68173 * @var object
68174 */
68175$GLOBALS['_PEAR_Config_instance'] = null;
68176if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) {
68177    $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear';
68178} else {
68179    $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR;
68180}
68181
68182// Below we define constants with default values for all configuration
68183// parameters except username/password.  All of them can have their
68184// defaults set through environment variables.  The reason we use the
68185// PHP_ prefix is for some security, PHP protects environment
68186// variables starting with PHP_*.
68187
68188// default channel and preferred mirror is based on whether we are invoked through
68189// the "pear" or the "pecl" command
68190if (!defined('PEAR_RUNTYPE')) {
68191    define('PEAR_RUNTYPE', 'pear');
68192}
68193
68194if (PEAR_RUNTYPE == 'pear') {
68195    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net');
68196} else {
68197    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net');
68198}
68199
68200if (getenv('PHP_PEAR_SYSCONF_DIR')) {
68201    define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR'));
68202} elseif (getenv('SystemRoot')) {
68203    define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot'));
68204} else {
68205    define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR);
68206}
68207
68208// Default for master_server
68209if (getenv('PHP_PEAR_MASTER_SERVER')) {
68210    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER'));
68211} else {
68212    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net');
68213}
68214
68215// Default for http_proxy
68216if (getenv('PHP_PEAR_HTTP_PROXY')) {
68217    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY'));
68218} elseif (getenv('http_proxy')) {
68219    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy'));
68220} else {
68221    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', '');
68222}
68223
68224// Default for php_dir
68225if (getenv('PHP_PEAR_INSTALL_DIR')) {
68226    define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR'));
68227} else {
68228    if (@file_exists($PEAR_INSTALL_DIR) && is_dir($PEAR_INSTALL_DIR)) {
68229        define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
68230    } else {
68231        define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
68232    }
68233}
68234
68235// Default for ext_dir
68236if (getenv('PHP_PEAR_EXTENSION_DIR')) {
68237    define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR'));
68238} else {
68239    if (ini_get('extension_dir')) {
68240        define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir'));
68241    } elseif (defined('PEAR_EXTENSION_DIR') &&
68242              file_exists(PEAR_EXTENSION_DIR) && is_dir(PEAR_EXTENSION_DIR)) {
68243        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR);
68244    } elseif (defined('PHP_EXTENSION_DIR')) {
68245        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR);
68246    } else {
68247        define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.');
68248    }
68249}
68250
68251// Default for doc_dir
68252if (getenv('PHP_PEAR_DOC_DIR')) {
68253    define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR'));
68254} else {
68255    define('PEAR_CONFIG_DEFAULT_DOC_DIR',
68256           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs');
68257}
68258
68259// Default for bin_dir
68260if (getenv('PHP_PEAR_BIN_DIR')) {
68261    define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR'));
68262} else {
68263    define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR);
68264}
68265
68266// Default for data_dir
68267if (getenv('PHP_PEAR_DATA_DIR')) {
68268    define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR'));
68269} else {
68270    define('PEAR_CONFIG_DEFAULT_DATA_DIR',
68271           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data');
68272}
68273
68274// Default for cfg_dir
68275if (getenv('PHP_PEAR_CFG_DIR')) {
68276    define('PEAR_CONFIG_DEFAULT_CFG_DIR', getenv('PHP_PEAR_CFG_DIR'));
68277} else {
68278    define('PEAR_CONFIG_DEFAULT_CFG_DIR',
68279           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'cfg');
68280}
68281
68282// Default for www_dir
68283if (getenv('PHP_PEAR_WWW_DIR')) {
68284    define('PEAR_CONFIG_DEFAULT_WWW_DIR', getenv('PHP_PEAR_WWW_DIR'));
68285} else {
68286    define('PEAR_CONFIG_DEFAULT_WWW_DIR',
68287           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'www');
68288}
68289
68290// Default for test_dir
68291if (getenv('PHP_PEAR_TEST_DIR')) {
68292    define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR'));
68293} else {
68294    define('PEAR_CONFIG_DEFAULT_TEST_DIR',
68295           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests');
68296}
68297
68298// Default for temp_dir
68299if (getenv('PHP_PEAR_TEMP_DIR')) {
68300    define('PEAR_CONFIG_DEFAULT_TEMP_DIR', getenv('PHP_PEAR_TEMP_DIR'));
68301} else {
68302    define('PEAR_CONFIG_DEFAULT_TEMP_DIR',
68303           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
68304           DIRECTORY_SEPARATOR . 'temp');
68305}
68306
68307// Default for cache_dir
68308if (getenv('PHP_PEAR_CACHE_DIR')) {
68309    define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR'));
68310} else {
68311    define('PEAR_CONFIG_DEFAULT_CACHE_DIR',
68312           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
68313           DIRECTORY_SEPARATOR . 'cache');
68314}
68315
68316// Default for download_dir
68317if (getenv('PHP_PEAR_DOWNLOAD_DIR')) {
68318    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', getenv('PHP_PEAR_DOWNLOAD_DIR'));
68319} else {
68320    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR',
68321           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
68322           DIRECTORY_SEPARATOR . 'download');
68323}
68324
68325// Default for php_bin
68326if (getenv('PHP_PEAR_PHP_BIN')) {
68327    define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN'));
68328} else {
68329    define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR.
68330           DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : ''));
68331}
68332
68333// Default for verbose
68334if (getenv('PHP_PEAR_VERBOSE')) {
68335    define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE'));
68336} else {
68337    define('PEAR_CONFIG_DEFAULT_VERBOSE', 1);
68338}
68339
68340// Default for preferred_state
68341if (getenv('PHP_PEAR_PREFERRED_STATE')) {
68342    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE'));
68343} else {
68344    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable');
68345}
68346
68347// Default for umask
68348if (getenv('PHP_PEAR_UMASK')) {
68349    define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK'));
68350} else {
68351    define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask()));
68352}
68353
68354// Default for cache_ttl
68355if (getenv('PHP_PEAR_CACHE_TTL')) {
68356    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL'));
68357} else {
68358    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600);
68359}
68360
68361// Default for sig_type
68362if (getenv('PHP_PEAR_SIG_TYPE')) {
68363    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE'));
68364} else {
68365    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg');
68366}
68367
68368// Default for sig_bin
68369if (getenv('PHP_PEAR_SIG_BIN')) {
68370    define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN'));
68371} else {
68372    define('PEAR_CONFIG_DEFAULT_SIG_BIN',
68373           System::which(
68374               'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg'));
68375}
68376
68377// Default for sig_keydir
68378if (getenv('PHP_PEAR_SIG_KEYDIR')) {
68379    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR'));
68380} else {
68381    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR',
68382           PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys');
68383}
68384
68385/**
68386 * This is a class for storing configuration data, keeping track of
68387 * which are system-defined, user-defined or defaulted.
68388 * @category   pear
68389 * @package    PEAR
68390 * @author     Stig Bakken <ssb@php.net>
68391 * @author     Greg Beaver <cellog@php.net>
68392 * @copyright  1997-2009 The Authors
68393 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
68394 * @version    Release: 1.9.4
68395 * @link       http://pear.php.net/package/PEAR
68396 * @since      Class available since Release 0.1
68397 */
68398class PEAR_Config extends PEAR
68399{
68400    /**
68401     * Array of config files used.
68402     *
68403     * @var array layer => config file
68404     */
68405    var $files = array(
68406        'system' => '',
68407        'user' => '',
68408        );
68409
68410    var $layers = array();
68411
68412    /**
68413     * Configuration data, two-dimensional array where the first
68414     * dimension is the config layer ('user', 'system' and 'default'),
68415     * and the second dimension is keyname => value.
68416     *
68417     * The order in the first dimension is important!  Earlier
68418     * layers will shadow later ones when a config value is
68419     * requested (if a 'user' value exists, it will be returned first,
68420     * then 'system' and finally 'default').
68421     *
68422     * @var array layer => array(keyname => value, ...)
68423     */
68424    var $configuration = array(
68425        'user' => array(),
68426        'system' => array(),
68427        'default' => array(),
68428        );
68429
68430    /**
68431     * Configuration values that can be set for a channel
68432     *
68433     * All other configuration values can only have a global value
68434     * @var array
68435     * @access private
68436     */
68437    var $_channelConfigInfo = array(
68438        'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'cfg_dir',
68439        'test_dir', 'www_dir', 'php_bin', 'php_prefix', 'php_suffix', 'username',
68440        'password', 'verbose', 'preferred_state', 'umask', 'preferred_mirror', 'php_ini'
68441        );
68442
68443    /**
68444     * Channels that can be accessed
68445     * @see setChannels()
68446     * @var array
68447     * @access private
68448     */
68449    var $_channels = array('pear.php.net', 'pecl.php.net', '__uri');
68450
68451    /**
68452     * This variable is used to control the directory values returned
68453     * @see setInstallRoot();
68454     * @var string|false
68455     * @access private
68456     */
68457    var $_installRoot = false;
68458
68459    /**
68460     * If requested, this will always refer to the registry
68461     * contained in php_dir
68462     * @var PEAR_Registry
68463     */
68464    var $_registry = array();
68465
68466    /**
68467     * @var array
68468     * @access private
68469     */
68470    var $_regInitialized = array();
68471
68472    /**
68473     * @var bool
68474     * @access private
68475     */
68476    var $_noRegistry = false;
68477
68478    /**
68479     * amount of errors found while parsing config
68480     * @var integer
68481     * @access private
68482     */
68483    var $_errorsFound = 0;
68484    var $_lastError = null;
68485
68486    /**
68487     * Information about the configuration data.  Stores the type,
68488     * default value and a documentation string for each configuration
68489     * value.
68490     *
68491     * @var array layer => array(infotype => value, ...)
68492     */
68493    var $configuration_info = array(
68494        // Channels/Internet Access
68495        'default_channel' => array(
68496            'type' => 'string',
68497            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
68498            'doc' => 'the default channel to use for all non explicit commands',
68499            'prompt' => 'Default Channel',
68500            'group' => 'Internet Access',
68501            ),
68502        'preferred_mirror' => array(
68503            'type' => 'string',
68504            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
68505            'doc' => 'the default server or mirror to use for channel actions',
68506            'prompt' => 'Default Channel Mirror',
68507            'group' => 'Internet Access',
68508            ),
68509        'remote_config' => array(
68510            'type' => 'password',
68511            'default' => '',
68512            'doc' => 'ftp url of remote configuration file to use for synchronized install',
68513            'prompt' => 'Remote Configuration File',
68514            'group' => 'Internet Access',
68515            ),
68516        'auto_discover' => array(
68517            'type' => 'integer',
68518            'default' => 0,
68519            'doc' => 'whether to automatically discover new channels',
68520            'prompt' => 'Auto-discover new Channels',
68521            'group' => 'Internet Access',
68522            ),
68523        // Internet Access
68524        'master_server' => array(
68525            'type' => 'string',
68526            'default' => 'pear.php.net',
68527            'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]',
68528            'prompt' => 'PEAR server [DEPRECATED]',
68529            'group' => 'Internet Access',
68530            ),
68531        'http_proxy' => array(
68532            'type' => 'string',
68533            'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY,
68534            'doc' => 'HTTP proxy (host:port) to use when downloading packages',
68535            'prompt' => 'HTTP Proxy Server Address',
68536            'group' => 'Internet Access',
68537            ),
68538        // File Locations
68539        'php_dir' => array(
68540            'type' => 'directory',
68541            'default' => PEAR_CONFIG_DEFAULT_PHP_DIR,
68542            'doc' => 'directory where .php files are installed',
68543            'prompt' => 'PEAR directory',
68544            'group' => 'File Locations',
68545            ),
68546        'ext_dir' => array(
68547            'type' => 'directory',
68548            'default' => PEAR_CONFIG_DEFAULT_EXT_DIR,
68549            'doc' => 'directory where loadable extensions are installed',
68550            'prompt' => 'PHP extension directory',
68551            'group' => 'File Locations',
68552            ),
68553        'doc_dir' => array(
68554            'type' => 'directory',
68555            'default' => PEAR_CONFIG_DEFAULT_DOC_DIR,
68556            'doc' => 'directory where documentation is installed',
68557            'prompt' => 'PEAR documentation directory',
68558            'group' => 'File Locations',
68559            ),
68560        'bin_dir' => array(
68561            'type' => 'directory',
68562            'default' => PEAR_CONFIG_DEFAULT_BIN_DIR,
68563            'doc' => 'directory where executables are installed',
68564            'prompt' => 'PEAR executables directory',
68565            'group' => 'File Locations',
68566            ),
68567        'data_dir' => array(
68568            'type' => 'directory',
68569            'default' => PEAR_CONFIG_DEFAULT_DATA_DIR,
68570            'doc' => 'directory where data files are installed',
68571            'prompt' => 'PEAR data directory',
68572            'group' => 'File Locations (Advanced)',
68573            ),
68574        'cfg_dir' => array(
68575            'type' => 'directory',
68576            'default' => PEAR_CONFIG_DEFAULT_CFG_DIR,
68577            'doc' => 'directory where modifiable configuration files are installed',
68578            'prompt' => 'PEAR configuration file directory',
68579            'group' => 'File Locations (Advanced)',
68580            ),
68581        'www_dir' => array(
68582            'type' => 'directory',
68583            'default' => PEAR_CONFIG_DEFAULT_WWW_DIR,
68584            'doc' => 'directory where www frontend files (html/js) are installed',
68585            'prompt' => 'PEAR www files directory',
68586            'group' => 'File Locations (Advanced)',
68587            ),
68588        'test_dir' => array(
68589            'type' => 'directory',
68590            'default' => PEAR_CONFIG_DEFAULT_TEST_DIR,
68591            'doc' => 'directory where regression tests are installed',
68592            'prompt' => 'PEAR test directory',
68593            'group' => 'File Locations (Advanced)',
68594            ),
68595        'cache_dir' => array(
68596            'type' => 'directory',
68597            'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
68598            'doc' => 'directory which is used for web service cache',
68599            'prompt' => 'PEAR Installer cache directory',
68600            'group' => 'File Locations (Advanced)',
68601            ),
68602        'temp_dir' => array(
68603            'type' => 'directory',
68604            'default' => PEAR_CONFIG_DEFAULT_TEMP_DIR,
68605            'doc' => 'directory which is used for all temp files',
68606            'prompt' => 'PEAR Installer temp directory',
68607            'group' => 'File Locations (Advanced)',
68608            ),
68609        'download_dir' => array(
68610            'type' => 'directory',
68611            'default' => PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR,
68612            'doc' => 'directory which is used for all downloaded files',
68613            'prompt' => 'PEAR Installer download directory',
68614            'group' => 'File Locations (Advanced)',
68615            ),
68616        'php_bin' => array(
68617            'type' => 'file',
68618            'default' => PEAR_CONFIG_DEFAULT_PHP_BIN,
68619            'doc' => 'PHP CLI/CGI binary for executing scripts',
68620            'prompt' => 'PHP CLI/CGI binary',
68621            'group' => 'File Locations (Advanced)',
68622            ),
68623        'php_prefix' => array(
68624            'type' => 'string',
68625            'default' => '',
68626            'doc' => '--program-prefix for php_bin\'s ./configure, used for pecl installs',
68627            'prompt' => '--program-prefix passed to PHP\'s ./configure',
68628            'group' => 'File Locations (Advanced)',
68629            ),
68630        'php_suffix' => array(
68631            'type' => 'string',
68632            'default' => '',
68633            'doc' => '--program-suffix for php_bin\'s ./configure, used for pecl installs',
68634            'prompt' => '--program-suffix passed to PHP\'s ./configure',
68635            'group' => 'File Locations (Advanced)',
68636            ),
68637        'php_ini' => array(
68638            'type' => 'file',
68639            'default' => '',
68640            'doc' => 'location of php.ini in which to enable PECL extensions on install',
68641            'prompt' => 'php.ini location',
68642            'group' => 'File Locations (Advanced)',
68643            ),
68644        // Maintainers
68645        'username' => array(
68646            'type' => 'string',
68647            'default' => '',
68648            'doc' => '(maintainers) your PEAR account name',
68649            'prompt' => 'PEAR username (for maintainers)',
68650            'group' => 'Maintainers',
68651            ),
68652        'password' => array(
68653            'type' => 'password',
68654            'default' => '',
68655            'doc' => '(maintainers) your PEAR account password',
68656            'prompt' => 'PEAR password (for maintainers)',
68657            'group' => 'Maintainers',
68658            ),
68659        // Advanced
68660        'verbose' => array(
68661            'type' => 'integer',
68662            'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
68663            'doc' => 'verbosity level
686640: really quiet
686651: somewhat quiet
686662: verbose
686673: debug',
68668            'prompt' => 'Debug Log Level',
68669            'group' => 'Advanced',
68670            ),
68671        'preferred_state' => array(
68672            'type' => 'set',
68673            'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
68674            'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
68675            'valid_set' => array(
68676                'stable', 'beta', 'alpha', 'devel', 'snapshot'),
68677            'prompt' => 'Preferred Package State',
68678            'group' => 'Advanced',
68679            ),
68680        'umask' => array(
68681            'type' => 'mask',
68682            'default' => PEAR_CONFIG_DEFAULT_UMASK,
68683            'doc' => 'umask used when creating files (Unix-like systems only)',
68684            'prompt' => 'Unix file mask',
68685            'group' => 'Advanced',
68686            ),
68687        'cache_ttl' => array(
68688            'type' => 'integer',
68689            'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL,
68690            'doc' => 'amount of secs where the local cache is used and not updated',
68691            'prompt' => 'Cache TimeToLive',
68692            'group' => 'Advanced',
68693            ),
68694        'sig_type' => array(
68695            'type' => 'set',
68696            'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE,
68697            'doc' => 'which package signature mechanism to use',
68698            'valid_set' => array('gpg'),
68699            'prompt' => 'Package Signature Type',
68700            'group' => 'Maintainers',
68701            ),
68702        'sig_bin' => array(
68703            'type' => 'string',
68704            'default' => PEAR_CONFIG_DEFAULT_SIG_BIN,
68705            'doc' => 'which package signature mechanism to use',
68706            'prompt' => 'Signature Handling Program',
68707            'group' => 'Maintainers',
68708            ),
68709        'sig_keyid' => array(
68710            'type' => 'string',
68711            'default' => '',
68712            'doc' => 'which key to use for signing with',
68713            'prompt' => 'Signature Key Id',
68714            'group' => 'Maintainers',
68715            ),
68716        'sig_keydir' => array(
68717            'type' => 'directory',
68718            'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR,
68719            'doc' => 'directory where signature keys are located',
68720            'prompt' => 'Signature Key Directory',
68721            'group' => 'Maintainers',
68722            ),
68723        // __channels is reserved - used for channel-specific configuration
68724        );
68725
68726    /**
68727     * Constructor.
68728     *
68729     * @param string file to read user-defined options from
68730     * @param string file to read system-wide defaults from
68731     * @param bool   determines whether a registry object "follows"
68732     *               the value of php_dir (is automatically created
68733     *               and moved when php_dir is changed)
68734     * @param bool   if true, fails if configuration files cannot be loaded
68735     *
68736     * @access public
68737     *
68738     * @see PEAR_Config::singleton
68739     */
68740    function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false,
68741                         $strict = true)
68742    {
68743        $this->PEAR();
68744        PEAR_Installer_Role::initializeConfig($this);
68745        $sl = DIRECTORY_SEPARATOR;
68746        if (empty($user_file)) {
68747            if (OS_WINDOWS) {
68748                $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini';
68749            } else {
68750                $user_file = getenv('HOME') . $sl . '.pearrc';
68751            }
68752        }
68753
68754        if (empty($system_file)) {
68755            $system_file = PEAR_CONFIG_SYSCONFDIR . $sl;
68756            if (OS_WINDOWS) {
68757                $system_file .= 'pearsys.ini';
68758            } else {
68759                $system_file .= 'pear.conf';
68760            }
68761        }
68762
68763        $this->layers = array_keys($this->configuration);
68764        $this->files['user']   = $user_file;
68765        $this->files['system'] = $system_file;
68766        if ($user_file && file_exists($user_file)) {
68767            $this->pushErrorHandling(PEAR_ERROR_RETURN);
68768            $this->readConfigFile($user_file, 'user', $strict);
68769            $this->popErrorHandling();
68770            if ($this->_errorsFound > 0) {
68771                return;
68772            }
68773        }
68774
68775        if ($system_file && @file_exists($system_file)) {
68776            $this->mergeConfigFile($system_file, false, 'system', $strict);
68777            if ($this->_errorsFound > 0) {
68778                return;
68779            }
68780
68781        }
68782
68783        if (!$ftp_file) {
68784            $ftp_file = $this->get('remote_config');
68785        }
68786
68787        if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) {
68788            $this->readFTPConfigFile($ftp_file);
68789        }
68790
68791        foreach ($this->configuration_info as $key => $info) {
68792            $this->configuration['default'][$key] = $info['default'];
68793        }
68794
68795        $this->_registry['default'] = &new PEAR_Registry($this->configuration['default']['php_dir']);
68796        $this->_registry['default']->setConfig($this, false);
68797        $this->_regInitialized['default'] = false;
68798        //$GLOBALS['_PEAR_Config_instance'] = &$this;
68799    }
68800
68801    /**
68802     * Return the default locations of user and system configuration files
68803     * @static
68804     */
68805    function getDefaultConfigFiles()
68806    {
68807        $sl = DIRECTORY_SEPARATOR;
68808        if (OS_WINDOWS) {
68809            return array(
68810                'user'   => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini',
68811                'system' =>  PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini'
68812            );
68813        }
68814
68815        return array(
68816            'user'   => getenv('HOME') . $sl . '.pearrc',
68817            'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf'
68818        );
68819    }
68820
68821    /**
68822     * Static singleton method.  If you want to keep only one instance
68823     * of this class in use, this method will give you a reference to
68824     * the last created PEAR_Config object if one exists, or create a
68825     * new object.
68826     *
68827     * @param string (optional) file to read user-defined options from
68828     * @param string (optional) file to read system-wide defaults from
68829     *
68830     * @return object an existing or new PEAR_Config instance
68831     *
68832     * @access public
68833     *
68834     * @see PEAR_Config::PEAR_Config
68835     */
68836    function &singleton($user_file = '', $system_file = '', $strict = true)
68837    {
68838        if (is_object($GLOBALS['_PEAR_Config_instance'])) {
68839            return $GLOBALS['_PEAR_Config_instance'];
68840        }
68841
68842        $t_conf = &new PEAR_Config($user_file, $system_file, false, $strict);
68843        if ($t_conf->_errorsFound > 0) {
68844             return $t_conf->lastError;
68845        }
68846
68847        $GLOBALS['_PEAR_Config_instance'] = &$t_conf;
68848        return $GLOBALS['_PEAR_Config_instance'];
68849    }
68850
68851    /**
68852     * Determine whether any configuration files have been detected, and whether a
68853     * registry object can be retrieved from this configuration.
68854     * @return bool
68855     * @since PEAR 1.4.0a1
68856     */
68857    function validConfiguration()
68858    {
68859        if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) {
68860            return true;
68861        }
68862
68863        return false;
68864    }
68865
68866    /**
68867     * Reads configuration data from a file.  All existing values in
68868     * the config layer are discarded and replaced with data from the
68869     * file.
68870     * @param string file to read from, if NULL or not specified, the
68871     *               last-used file for the same layer (second param) is used
68872     * @param string config layer to insert data into ('user' or 'system')
68873     * @return bool TRUE on success or a PEAR error on failure
68874     */
68875    function readConfigFile($file = null, $layer = 'user', $strict = true)
68876    {
68877        if (empty($this->files[$layer])) {
68878            return $this->raiseError("unknown config layer `$layer'");
68879        }
68880
68881        if ($file === null) {
68882            $file = $this->files[$layer];
68883        }
68884
68885        $data = $this->_readConfigDataFrom($file);
68886        if (PEAR::isError($data)) {
68887            if (!$strict) {
68888                return true;
68889            }
68890
68891            $this->_errorsFound++;
68892            $this->lastError = $data;
68893
68894            return $data;
68895        }
68896
68897        $this->files[$layer] = $file;
68898        $this->_decodeInput($data);
68899        $this->configuration[$layer] = $data;
68900        $this->_setupChannels();
68901        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
68902            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
68903            $this->_registry[$layer]->setConfig($this, false);
68904            $this->_regInitialized[$layer] = false;
68905        } else {
68906            unset($this->_registry[$layer]);
68907        }
68908        return true;
68909    }
68910
68911    /**
68912     * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini
68913     * @return true|PEAR_Error
68914     */
68915    function readFTPConfigFile($path)
68916    {
68917        do { // poor man's try
68918            if (!class_exists('PEAR_FTP')) {
68919                if (!class_exists('PEAR_Common')) {
68920                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
68921                }
68922                if (PEAR_Common::isIncludeable('PEAR/FTP.php')) {
68923                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/FTP.php';
68924                }
68925            }
68926
68927            if (!class_exists('PEAR_FTP')) {
68928                return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config');
68929            }
68930
68931            $this->_ftp = &new PEAR_FTP;
68932            $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN);
68933            $e = $this->_ftp->init($path);
68934            if (PEAR::isError($e)) {
68935                $this->_ftp->popErrorHandling();
68936                return $e;
68937            }
68938
68939            $tmp = System::mktemp('-d');
68940            PEAR_Common::addTempFile($tmp);
68941            $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR .
68942                'pear.ini', false, FTP_BINARY);
68943            if (PEAR::isError($e)) {
68944                $this->_ftp->popErrorHandling();
68945                return $e;
68946            }
68947
68948            PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini');
68949            $this->_ftp->disconnect();
68950            $this->_ftp->popErrorHandling();
68951            $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini';
68952            $e = $this->readConfigFile(null, 'ftp');
68953            if (PEAR::isError($e)) {
68954                return $e;
68955            }
68956
68957            $fail = array();
68958            foreach ($this->configuration_info as $key => $val) {
68959                if (in_array($this->getGroup($key),
68960                      array('File Locations', 'File Locations (Advanced)')) &&
68961                      $this->getType($key) == 'directory') {
68962                    // any directory configs must be set for this to work
68963                    if (!isset($this->configuration['ftp'][$key])) {
68964                        $fail[] = $key;
68965                    }
68966                }
68967            }
68968
68969            if (!count($fail)) {
68970                return true;
68971            }
68972
68973            $fail = '"' . implode('", "', $fail) . '"';
68974            unset($this->files['ftp']);
68975            unset($this->configuration['ftp']);
68976            return PEAR::raiseError('ERROR: Ftp configuration file must set all ' .
68977                'directory configuration variables.  These variables were not set: ' .
68978                $fail);
68979        } while (false); // poor man's catch
68980        unset($this->files['ftp']);
68981        return PEAR::raiseError('no remote host specified');
68982    }
68983
68984    /**
68985     * Reads the existing configurations and creates the _channels array from it
68986     */
68987    function _setupChannels()
68988    {
68989        $set = array_flip(array_values($this->_channels));
68990        foreach ($this->configuration as $layer => $data) {
68991            $i = 1000;
68992            if (isset($data['__channels']) && is_array($data['__channels'])) {
68993                foreach ($data['__channels'] as $channel => $info) {
68994                    $set[$channel] = $i++;
68995                }
68996            }
68997        }
68998        $this->_channels = array_values(array_flip($set));
68999        $this->setChannels($this->_channels);
69000    }
69001
69002    function deleteChannel($channel)
69003    {
69004        $ch = strtolower($channel);
69005        foreach ($this->configuration as $layer => $data) {
69006            if (isset($data['__channels']) && isset($data['__channels'][$ch])) {
69007                unset($this->configuration[$layer]['__channels'][$ch]);
69008            }
69009        }
69010
69011        $this->_channels = array_flip($this->_channels);
69012        unset($this->_channels[$ch]);
69013        $this->_channels = array_flip($this->_channels);
69014    }
69015
69016    /**
69017     * Merges data into a config layer from a file.  Does the same
69018     * thing as readConfigFile, except it does not replace all
69019     * existing values in the config layer.
69020     * @param string file to read from
69021     * @param bool whether to overwrite existing data (default TRUE)
69022     * @param string config layer to insert data into ('user' or 'system')
69023     * @param string if true, errors are returned if file opening fails
69024     * @return bool TRUE on success or a PEAR error on failure
69025     */
69026    function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true)
69027    {
69028        if (empty($this->files[$layer])) {
69029            return $this->raiseError("unknown config layer `$layer'");
69030        }
69031
69032        if ($file === null) {
69033            $file = $this->files[$layer];
69034        }
69035
69036        $data = $this->_readConfigDataFrom($file);
69037        if (PEAR::isError($data)) {
69038            if (!$strict) {
69039                return true;
69040            }
69041
69042            $this->_errorsFound++;
69043            $this->lastError = $data;
69044
69045            return $data;
69046        }
69047
69048        $this->_decodeInput($data);
69049        if ($override) {
69050            $this->configuration[$layer] =
69051                PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data);
69052        } else {
69053            $this->configuration[$layer] =
69054                PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]);
69055        }
69056
69057        $this->_setupChannels();
69058        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
69059            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
69060            $this->_registry[$layer]->setConfig($this, false);
69061            $this->_regInitialized[$layer] = false;
69062        } else {
69063            unset($this->_registry[$layer]);
69064        }
69065        return true;
69066    }
69067
69068    /**
69069     * @param array
69070     * @param array
69071     * @return array
69072     * @static
69073     */
69074    function arrayMergeRecursive($arr2, $arr1)
69075    {
69076        $ret = array();
69077        foreach ($arr2 as $key => $data) {
69078            if (!isset($arr1[$key])) {
69079                $ret[$key] = $data;
69080                unset($arr1[$key]);
69081                continue;
69082            }
69083            if (is_array($data)) {
69084                if (!is_array($arr1[$key])) {
69085                    $ret[$key] = $arr1[$key];
69086                    unset($arr1[$key]);
69087                    continue;
69088                }
69089                $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]);
69090                unset($arr1[$key]);
69091            }
69092        }
69093
69094        return array_merge($ret, $arr1);
69095    }
69096
69097    /**
69098     * Writes data into a config layer from a file.
69099     *
69100     * @param string|null file to read from, or null for default
69101     * @param string config layer to insert data into ('user' or
69102     *               'system')
69103     * @param string|null data to write to config file or null for internal data [DEPRECATED]
69104     * @return bool TRUE on success or a PEAR error on failure
69105     */
69106    function writeConfigFile($file = null, $layer = 'user', $data = null)
69107    {
69108        $this->_lazyChannelSetup($layer);
69109        if ($layer == 'both' || $layer == 'all') {
69110            foreach ($this->files as $type => $file) {
69111                $err = $this->writeConfigFile($file, $type, $data);
69112                if (PEAR::isError($err)) {
69113                    return $err;
69114                }
69115            }
69116            return true;
69117        }
69118
69119        if (empty($this->files[$layer])) {
69120            return $this->raiseError("unknown config file type `$layer'");
69121        }
69122
69123        if ($file === null) {
69124            $file = $this->files[$layer];
69125        }
69126
69127        $data = ($data === null) ? $this->configuration[$layer] : $data;
69128        $this->_encodeOutput($data);
69129        $opt = array('-p', dirname($file));
69130        if (!@System::mkDir($opt)) {
69131            return $this->raiseError("could not create directory: " . dirname($file));
69132        }
69133
69134        if (file_exists($file) && is_file($file) && !is_writeable($file)) {
69135            return $this->raiseError("no write access to $file!");
69136        }
69137
69138        $fp = @fopen($file, "w");
69139        if (!$fp) {
69140            return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed ($php_errormsg)");
69141        }
69142
69143        $contents = "#PEAR_Config 0.9\n" . serialize($data);
69144        if (!@fwrite($fp, $contents)) {
69145            return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed ($php_errormsg)");
69146        }
69147        return true;
69148    }
69149
69150    /**
69151     * Reads configuration data from a file and returns the parsed data
69152     * in an array.
69153     *
69154     * @param string file to read from
69155     * @return array configuration data or a PEAR error on failure
69156     * @access private
69157     */
69158    function _readConfigDataFrom($file)
69159    {
69160        $fp = false;
69161        if (file_exists($file)) {
69162            $fp = @fopen($file, "r");
69163        }
69164
69165        if (!$fp) {
69166            return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed");
69167        }
69168
69169        $size = filesize($file);
69170        $rt = get_magic_quotes_runtime();
69171        set_magic_quotes_runtime(0);
69172        fclose($fp);
69173        $contents = file_get_contents($file);
69174        if (empty($contents)) {
69175            return $this->raiseError('Configuration file "' . $file . '" is empty');
69176        }
69177
69178        set_magic_quotes_runtime($rt);
69179
69180        $version = false;
69181        if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) {
69182            $version = $matches[1];
69183            $contents = substr($contents, strlen($matches[0]));
69184        } else {
69185            // Museum config file
69186            if (substr($contents,0,2) == 'a:') {
69187                $version = '0.1';
69188            }
69189        }
69190
69191        if ($version && version_compare("$version", '1', '<')) {
69192            // no '@', it is possible that unserialize
69193            // raises a notice but it seems to block IO to
69194            // STDOUT if a '@' is used and a notice is raise
69195            $data = unserialize($contents);
69196
69197            if (!is_array($data) && !$data) {
69198                if ($contents == serialize(false)) {
69199                    $data = array();
69200                } else {
69201                    $err = $this->raiseError("PEAR_Config: bad data in $file");
69202                    return $err;
69203                }
69204            }
69205            if (!is_array($data)) {
69206                if (strlen(trim($contents)) > 0) {
69207                    $error = "PEAR_Config: bad data in $file";
69208                    $err = $this->raiseError($error);
69209                    return $err;
69210                }
69211
69212                $data = array();
69213            }
69214        // add parsing of newer formats here...
69215        } else {
69216            $err = $this->raiseError("$file: unknown version `$version'");
69217            return $err;
69218        }
69219
69220        return $data;
69221    }
69222
69223    /**
69224    * Gets the file used for storing the config for a layer
69225    *
69226    * @param string $layer 'user' or 'system'
69227    */
69228    function getConfFile($layer)
69229    {
69230        return $this->files[$layer];
69231    }
69232
69233    /**
69234     * @param string Configuration class name, used for detecting duplicate calls
69235     * @param array information on a role as parsed from its xml file
69236     * @return true|PEAR_Error
69237     * @access private
69238     */
69239    function _addConfigVars($class, $vars)
69240    {
69241        static $called = array();
69242        if (isset($called[$class])) {
69243            return;
69244        }
69245
69246        $called[$class] = 1;
69247        if (count($vars) > 3) {
69248            return $this->raiseError('Roles can only define 3 new config variables or less');
69249        }
69250
69251        foreach ($vars as $name => $var) {
69252            if (!is_array($var)) {
69253                return $this->raiseError('Configuration information must be an array');
69254            }
69255
69256            if (!isset($var['type'])) {
69257                return $this->raiseError('Configuration information must contain a type');
69258            } elseif (!in_array($var['type'],
69259                    array('string', 'mask', 'password', 'directory', 'file', 'set'))) {
69260                  return $this->raiseError(
69261                      'Configuration type must be one of directory, file, string, ' .
69262                      'mask, set, or password');
69263            }
69264            if (!isset($var['default'])) {
69265                return $this->raiseError(
69266                    'Configuration information must contain a default value ("default" index)');
69267            }
69268
69269            if (is_array($var['default'])) {
69270                $real_default = '';
69271                foreach ($var['default'] as $config_var => $val) {
69272                    if (strpos($config_var, 'text') === 0) {
69273                        $real_default .= $val;
69274                    } elseif (strpos($config_var, 'constant') === 0) {
69275                        if (!defined($val)) {
69276                            return $this->raiseError(
69277                                'Unknown constant "' . $val . '" requested in ' .
69278                                'default value for configuration variable "' .
69279                                $name . '"');
69280                        }
69281
69282                        $real_default .= constant($val);
69283                    } elseif (isset($this->configuration_info[$config_var])) {
69284                        $real_default .=
69285                            $this->configuration_info[$config_var]['default'];
69286                    } else {
69287                        return $this->raiseError(
69288                            'Unknown request for "' . $config_var . '" value in ' .
69289                            'default value for configuration variable "' .
69290                            $name . '"');
69291                    }
69292                }
69293                $var['default'] = $real_default;
69294            }
69295
69296            if ($var['type'] == 'integer') {
69297                $var['default'] = (integer) $var['default'];
69298            }
69299
69300            if (!isset($var['doc'])) {
69301                return $this->raiseError(
69302                    'Configuration information must contain a summary ("doc" index)');
69303            }
69304
69305            if (!isset($var['prompt'])) {
69306                return $this->raiseError(
69307                    'Configuration information must contain a simple prompt ("prompt" index)');
69308            }
69309
69310            if (!isset($var['group'])) {
69311                return $this->raiseError(
69312                    'Configuration information must contain a simple group ("group" index)');
69313            }
69314
69315            if (isset($this->configuration_info[$name])) {
69316                return $this->raiseError('Configuration variable "' . $name .
69317                    '" already exists');
69318            }
69319
69320            $this->configuration_info[$name] = $var;
69321            // fix bug #7351: setting custom config variable in a channel fails
69322            $this->_channelConfigInfo[] = $name;
69323        }
69324
69325        return true;
69326    }
69327
69328    /**
69329     * Encodes/scrambles configuration data before writing to files.
69330     * Currently, 'password' values will be base64-encoded as to avoid
69331     * that people spot cleartext passwords by accident.
69332     *
69333     * @param array (reference) array to encode values in
69334     * @return bool TRUE on success
69335     * @access private
69336     */
69337    function _encodeOutput(&$data)
69338    {
69339        foreach ($data as $key => $value) {
69340            if ($key == '__channels') {
69341                foreach ($data['__channels'] as $channel => $blah) {
69342                    $this->_encodeOutput($data['__channels'][$channel]);
69343                }
69344            }
69345
69346            if (!isset($this->configuration_info[$key])) {
69347                continue;
69348            }
69349
69350            $type = $this->configuration_info[$key]['type'];
69351            switch ($type) {
69352                // we base64-encode passwords so they are at least
69353                // not shown in plain by accident
69354                case 'password': {
69355                    $data[$key] = base64_encode($data[$key]);
69356                    break;
69357                }
69358                case 'mask': {
69359                    $data[$key] = octdec($data[$key]);
69360                    break;
69361                }
69362            }
69363        }
69364
69365        return true;
69366    }
69367
69368    /**
69369     * Decodes/unscrambles configuration data after reading from files.
69370     *
69371     * @param array (reference) array to encode values in
69372     * @return bool TRUE on success
69373     * @access private
69374     *
69375     * @see PEAR_Config::_encodeOutput
69376     */
69377    function _decodeInput(&$data)
69378    {
69379        if (!is_array($data)) {
69380            return true;
69381        }
69382
69383        foreach ($data as $key => $value) {
69384            if ($key == '__channels') {
69385                foreach ($data['__channels'] as $channel => $blah) {
69386                    $this->_decodeInput($data['__channels'][$channel]);
69387                }
69388            }
69389
69390            if (!isset($this->configuration_info[$key])) {
69391                continue;
69392            }
69393
69394            $type = $this->configuration_info[$key]['type'];
69395            switch ($type) {
69396                case 'password': {
69397                    $data[$key] = base64_decode($data[$key]);
69398                    break;
69399                }
69400                case 'mask': {
69401                    $data[$key] = decoct($data[$key]);
69402                    break;
69403                }
69404            }
69405        }
69406
69407        return true;
69408    }
69409
69410    /**
69411     * Retrieve the default channel.
69412     *
69413     * On startup, channels are not initialized, so if the default channel is not
69414     * pear.php.net, then initialize the config.
69415     * @param string registry layer
69416     * @return string|false
69417     */
69418    function getDefaultChannel($layer = null)
69419    {
69420        $ret = false;
69421        if ($layer === null) {
69422            foreach ($this->layers as $layer) {
69423                if (isset($this->configuration[$layer]['default_channel'])) {
69424                    $ret = $this->configuration[$layer]['default_channel'];
69425                    break;
69426                }
69427            }
69428        } elseif (isset($this->configuration[$layer]['default_channel'])) {
69429            $ret = $this->configuration[$layer]['default_channel'];
69430        }
69431
69432        if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') {
69433            $ret = 'pecl.php.net';
69434        }
69435
69436        if ($ret) {
69437            if ($ret != 'pear.php.net') {
69438                $this->_lazyChannelSetup();
69439            }
69440
69441            return $ret;
69442        }
69443
69444        return PEAR_CONFIG_DEFAULT_CHANNEL;
69445    }
69446
69447    /**
69448     * Returns a configuration value, prioritizing layers as per the
69449     * layers property.
69450     *
69451     * @param string config key
69452     * @return mixed the config value, or NULL if not found
69453     * @access public
69454     */
69455    function get($key, $layer = null, $channel = false)
69456    {
69457        if (!isset($this->configuration_info[$key])) {
69458            return null;
69459        }
69460
69461        if ($key == '__channels') {
69462            return null;
69463        }
69464
69465        if ($key == 'default_channel') {
69466            return $this->getDefaultChannel($layer);
69467        }
69468
69469        if (!$channel) {
69470            $channel = $this->getDefaultChannel();
69471        } elseif ($channel != 'pear.php.net') {
69472            $this->_lazyChannelSetup();
69473        }
69474        $channel = strtolower($channel);
69475
69476        $test = (in_array($key, $this->_channelConfigInfo)) ?
69477            $this->_getChannelValue($key, $layer, $channel) :
69478            null;
69479        if ($test !== null) {
69480            if ($this->_installRoot) {
69481                if (in_array($this->getGroup($key),
69482                      array('File Locations', 'File Locations (Advanced)')) &&
69483                      $this->getType($key) == 'directory') {
69484                    return $this->_prependPath($test, $this->_installRoot);
69485                }
69486            }
69487            return $test;
69488        }
69489
69490        if ($layer === null) {
69491            foreach ($this->layers as $layer) {
69492                if (isset($this->configuration[$layer][$key])) {
69493                    $test = $this->configuration[$layer][$key];
69494                    if ($this->_installRoot) {
69495                        if (in_array($this->getGroup($key),
69496                              array('File Locations', 'File Locations (Advanced)')) &&
69497                              $this->getType($key) == 'directory') {
69498                            return $this->_prependPath($test, $this->_installRoot);
69499                        }
69500                    }
69501
69502                    if ($key == 'preferred_mirror') {
69503                        $reg = &$this->getRegistry();
69504                        if (is_object($reg)) {
69505                            $chan = &$reg->getChannel($channel);
69506                            if (PEAR::isError($chan)) {
69507                                return $channel;
69508                            }
69509
69510                            if (!$chan->getMirror($test) && $chan->getName() != $test) {
69511                                return $channel; // mirror does not exist
69512                            }
69513                        }
69514                    }
69515                    return $test;
69516                }
69517            }
69518        } elseif (isset($this->configuration[$layer][$key])) {
69519            $test = $this->configuration[$layer][$key];
69520            if ($this->_installRoot) {
69521                if (in_array($this->getGroup($key),
69522                      array('File Locations', 'File Locations (Advanced)')) &&
69523                      $this->getType($key) == 'directory') {
69524                    return $this->_prependPath($test, $this->_installRoot);
69525                }
69526            }
69527
69528            if ($key == 'preferred_mirror') {
69529                $reg = &$this->getRegistry();
69530                if (is_object($reg)) {
69531                    $chan = &$reg->getChannel($channel);
69532                    if (PEAR::isError($chan)) {
69533                        return $channel;
69534                    }
69535
69536                    if (!$chan->getMirror($test) && $chan->getName() != $test) {
69537                        return $channel; // mirror does not exist
69538                    }
69539                }
69540            }
69541
69542            return $test;
69543        }
69544
69545        return null;
69546    }
69547
69548    /**
69549     * Returns a channel-specific configuration value, prioritizing layers as per the
69550     * layers property.
69551     *
69552     * @param string config key
69553     * @return mixed the config value, or NULL if not found
69554     * @access private
69555     */
69556    function _getChannelValue($key, $layer, $channel)
69557    {
69558        if ($key == '__channels' || $channel == 'pear.php.net') {
69559            return null;
69560        }
69561
69562        $ret = null;
69563        if ($layer === null) {
69564            foreach ($this->layers as $ilayer) {
69565                if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) {
69566                    $ret = $this->configuration[$ilayer]['__channels'][$channel][$key];
69567                    break;
69568                }
69569            }
69570        } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
69571            $ret = $this->configuration[$layer]['__channels'][$channel][$key];
69572        }
69573
69574        if ($key != 'preferred_mirror') {
69575            return $ret;
69576        }
69577
69578
69579        if ($ret !== null) {
69580            $reg = &$this->getRegistry($layer);
69581            if (is_object($reg)) {
69582                $chan = &$reg->getChannel($channel);
69583                if (PEAR::isError($chan)) {
69584                    return $channel;
69585                }
69586
69587                if (!$chan->getMirror($ret) && $chan->getName() != $ret) {
69588                    return $channel; // mirror does not exist
69589                }
69590            }
69591
69592            return $ret;
69593        }
69594
69595        if ($channel != $this->getDefaultChannel($layer)) {
69596            return $channel; // we must use the channel name as the preferred mirror
69597                             // if the user has not chosen an alternate
69598        }
69599
69600        return $this->getDefaultChannel($layer);
69601    }
69602
69603    /**
69604     * Set a config value in a specific layer (defaults to 'user').
69605     * Enforces the types defined in the configuration_info array.  An
69606     * integer config variable will be cast to int, and a set config
69607     * variable will be validated against its legal values.
69608     *
69609     * @param string config key
69610     * @param string config value
69611     * @param string (optional) config layer
69612     * @param string channel to set this value for, or null for global value
69613     * @return bool TRUE on success, FALSE on failure
69614     */
69615    function set($key, $value, $layer = 'user', $channel = false)
69616    {
69617        if ($key == '__channels') {
69618            return false;
69619        }
69620
69621        if (!isset($this->configuration[$layer])) {
69622            return false;
69623        }
69624
69625        if ($key == 'default_channel') {
69626            // can only set this value globally
69627            $channel = 'pear.php.net';
69628            if ($value != 'pear.php.net') {
69629                $this->_lazyChannelSetup($layer);
69630            }
69631        }
69632
69633        if ($key == 'preferred_mirror') {
69634            if ($channel == '__uri') {
69635                return false; // can't set the __uri pseudo-channel's mirror
69636            }
69637
69638            $reg = &$this->getRegistry($layer);
69639            if (is_object($reg)) {
69640                $chan = &$reg->getChannel($channel ? $channel : 'pear.php.net');
69641                if (PEAR::isError($chan)) {
69642                    return false;
69643                }
69644
69645                if (!$chan->getMirror($value) && $chan->getName() != $value) {
69646                    return false; // mirror does not exist
69647                }
69648            }
69649        }
69650
69651        if (!isset($this->configuration_info[$key])) {
69652            return false;
69653        }
69654
69655        extract($this->configuration_info[$key]);
69656        switch ($type) {
69657            case 'integer':
69658                $value = (int)$value;
69659                break;
69660            case 'set': {
69661                // If a valid_set is specified, require the value to
69662                // be in the set.  If there is no valid_set, accept
69663                // any value.
69664                if ($valid_set) {
69665                    reset($valid_set);
69666                    if ((key($valid_set) === 0 && !in_array($value, $valid_set)) ||
69667                        (key($valid_set) !== 0 && empty($valid_set[$value])))
69668                    {
69669                        return false;
69670                    }
69671                }
69672                break;
69673            }
69674        }
69675
69676        if (!$channel) {
69677            $channel = $this->get('default_channel', null, 'pear.php.net');
69678        }
69679
69680        if (!in_array($channel, $this->_channels)) {
69681            $this->_lazyChannelSetup($layer);
69682            $reg = &$this->getRegistry($layer);
69683            if ($reg) {
69684                $channel = $reg->channelName($channel);
69685            }
69686
69687            if (!in_array($channel, $this->_channels)) {
69688                return false;
69689            }
69690        }
69691
69692        if ($channel != 'pear.php.net') {
69693            if (in_array($key, $this->_channelConfigInfo)) {
69694                $this->configuration[$layer]['__channels'][$channel][$key] = $value;
69695                return true;
69696            }
69697
69698            return false;
69699        }
69700
69701        if ($key == 'default_channel') {
69702            if (!isset($reg)) {
69703                $reg = &$this->getRegistry($layer);
69704                if (!$reg) {
69705                    $reg = &$this->getRegistry();
69706                }
69707            }
69708
69709            if ($reg) {
69710                $value = $reg->channelName($value);
69711            }
69712
69713            if (!$value) {
69714                return false;
69715            }
69716        }
69717
69718        $this->configuration[$layer][$key] = $value;
69719        if ($key == 'php_dir' && !$this->_noRegistry) {
69720            if (!isset($this->_registry[$layer]) ||
69721                  $value != $this->_registry[$layer]->install_dir) {
69722                $this->_registry[$layer] = &new PEAR_Registry($value);
69723                $this->_regInitialized[$layer] = false;
69724                $this->_registry[$layer]->setConfig($this, false);
69725            }
69726        }
69727
69728        return true;
69729    }
69730
69731    function _lazyChannelSetup($uselayer = false)
69732    {
69733        if ($this->_noRegistry) {
69734            return;
69735        }
69736
69737        $merge = false;
69738        foreach ($this->_registry as $layer => $p) {
69739            if ($uselayer && $uselayer != $layer) {
69740                continue;
69741            }
69742
69743            if (!$this->_regInitialized[$layer]) {
69744                if ($layer == 'default' && isset($this->_registry['user']) ||
69745                      isset($this->_registry['system'])) {
69746                    // only use the default registry if there are no alternatives
69747                    continue;
69748                }
69749
69750                if (!is_object($this->_registry[$layer])) {
69751                    if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) {
69752                        $this->_registry[$layer] = &new PEAR_Registry($phpdir);
69753                        $this->_registry[$layer]->setConfig($this, false);
69754                        $this->_regInitialized[$layer] = false;
69755                    } else {
69756                        unset($this->_registry[$layer]);
69757                        return;
69758                    }
69759                }
69760
69761                $this->setChannels($this->_registry[$layer]->listChannels(), $merge);
69762                $this->_regInitialized[$layer] = true;
69763                $merge = true;
69764            }
69765        }
69766    }
69767
69768    /**
69769     * Set the list of channels.
69770     *
69771     * This should be set via a call to {@link PEAR_Registry::listChannels()}
69772     * @param array
69773     * @param bool
69774     * @return bool success of operation
69775     */
69776    function setChannels($channels, $merge = false)
69777    {
69778        if (!is_array($channels)) {
69779            return false;
69780        }
69781
69782        if ($merge) {
69783            $this->_channels = array_merge($this->_channels, $channels);
69784        } else {
69785            $this->_channels = $channels;
69786        }
69787
69788        foreach ($channels as $channel) {
69789            $channel = strtolower($channel);
69790            if ($channel == 'pear.php.net') {
69791                continue;
69792            }
69793
69794            foreach ($this->layers as $layer) {
69795                if (!isset($this->configuration[$layer]['__channels'])) {
69796                    $this->configuration[$layer]['__channels'] = array();
69797                }
69798                if (!isset($this->configuration[$layer]['__channels'][$channel])
69799                      || !is_array($this->configuration[$layer]['__channels'][$channel])) {
69800                    $this->configuration[$layer]['__channels'][$channel] = array();
69801                }
69802            }
69803        }
69804
69805        return true;
69806    }
69807
69808    /**
69809     * Get the type of a config value.
69810     *
69811     * @param string  config key
69812     *
69813     * @return string type, one of "string", "integer", "file",
69814     * "directory", "set" or "password".
69815     *
69816     * @access public
69817     *
69818     */
69819    function getType($key)
69820    {
69821        if (isset($this->configuration_info[$key])) {
69822            return $this->configuration_info[$key]['type'];
69823        }
69824        return false;
69825    }
69826
69827    /**
69828     * Get the documentation for a config value.
69829     *
69830     * @param string  config key
69831     * @return string documentation string
69832     *
69833     * @access public
69834     *
69835     */
69836    function getDocs($key)
69837    {
69838        if (isset($this->configuration_info[$key])) {
69839            return $this->configuration_info[$key]['doc'];
69840        }
69841
69842        return false;
69843    }
69844
69845    /**
69846     * Get the short documentation for a config value.
69847     *
69848     * @param string  config key
69849     * @return string short documentation string
69850     *
69851     * @access public
69852     *
69853     */
69854    function getPrompt($key)
69855    {
69856        if (isset($this->configuration_info[$key])) {
69857            return $this->configuration_info[$key]['prompt'];
69858        }
69859
69860        return false;
69861    }
69862
69863    /**
69864     * Get the parameter group for a config key.
69865     *
69866     * @param string  config key
69867     * @return string parameter group
69868     *
69869     * @access public
69870     *
69871     */
69872    function getGroup($key)
69873    {
69874        if (isset($this->configuration_info[$key])) {
69875            return $this->configuration_info[$key]['group'];
69876        }
69877
69878        return false;
69879    }
69880
69881    /**
69882     * Get the list of parameter groups.
69883     *
69884     * @return array list of parameter groups
69885     *
69886     * @access public
69887     *
69888     */
69889    function getGroups()
69890    {
69891        $tmp = array();
69892        foreach ($this->configuration_info as $key => $info) {
69893            $tmp[$info['group']] = 1;
69894        }
69895
69896        return array_keys($tmp);
69897    }
69898
69899    /**
69900     * Get the list of the parameters in a group.
69901     *
69902     * @param string $group parameter group
69903     * @return array list of parameters in $group
69904     *
69905     * @access public
69906     *
69907     */
69908    function getGroupKeys($group)
69909    {
69910        $keys = array();
69911        foreach ($this->configuration_info as $key => $info) {
69912            if ($info['group'] == $group) {
69913                $keys[] = $key;
69914            }
69915        }
69916
69917        return $keys;
69918    }
69919
69920    /**
69921     * Get the list of allowed set values for a config value.  Returns
69922     * NULL for config values that are not sets.
69923     *
69924     * @param string  config key
69925     * @return array enumerated array of set values, or NULL if the
69926     *               config key is unknown or not a set
69927     *
69928     * @access public
69929     *
69930     */
69931    function getSetValues($key)
69932    {
69933        if (isset($this->configuration_info[$key]) &&
69934            isset($this->configuration_info[$key]['type']) &&
69935            $this->configuration_info[$key]['type'] == 'set')
69936        {
69937            $valid_set = $this->configuration_info[$key]['valid_set'];
69938            reset($valid_set);
69939            if (key($valid_set) === 0) {
69940                return $valid_set;
69941            }
69942
69943            return array_keys($valid_set);
69944        }
69945
69946        return null;
69947    }
69948
69949    /**
69950     * Get all the current config keys.
69951     *
69952     * @return array simple array of config keys
69953     *
69954     * @access public
69955     */
69956    function getKeys()
69957    {
69958        $keys = array();
69959        foreach ($this->layers as $layer) {
69960            $test = $this->configuration[$layer];
69961            if (isset($test['__channels'])) {
69962                foreach ($test['__channels'] as $channel => $configs) {
69963                    $keys = array_merge($keys, $configs);
69964                }
69965            }
69966
69967            unset($test['__channels']);
69968            $keys = array_merge($keys, $test);
69969
69970        }
69971        return array_keys($keys);
69972    }
69973
69974    /**
69975     * Remove the a config key from a specific config layer.
69976     *
69977     * @param string config key
69978     * @param string (optional) config layer
69979     * @param string (optional) channel (defaults to default channel)
69980     * @return bool TRUE on success, FALSE on failure
69981     *
69982     * @access public
69983     */
69984    function remove($key, $layer = 'user', $channel = null)
69985    {
69986        if ($channel === null) {
69987            $channel = $this->getDefaultChannel();
69988        }
69989
69990        if ($channel !== 'pear.php.net') {
69991            if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
69992                unset($this->configuration[$layer]['__channels'][$channel][$key]);
69993                return true;
69994            }
69995        }
69996
69997        if (isset($this->configuration[$layer][$key])) {
69998            unset($this->configuration[$layer][$key]);
69999            return true;
70000        }
70001
70002        return false;
70003    }
70004
70005    /**
70006     * Temporarily remove an entire config layer.  USE WITH CARE!
70007     *
70008     * @param string config key
70009     * @param string (optional) config layer
70010     * @return bool TRUE on success, FALSE on failure
70011     *
70012     * @access public
70013     */
70014    function removeLayer($layer)
70015    {
70016        if (isset($this->configuration[$layer])) {
70017            $this->configuration[$layer] = array();
70018            return true;
70019        }
70020
70021        return false;
70022    }
70023
70024    /**
70025     * Stores configuration data in a layer.
70026     *
70027     * @param string config layer to store
70028     * @return bool TRUE on success, or PEAR error on failure
70029     *
70030     * @access public
70031     */
70032    function store($layer = 'user', $data = null)
70033    {
70034        return $this->writeConfigFile(null, $layer, $data);
70035    }
70036
70037    /**
70038     * Tells what config layer that gets to define a key.
70039     *
70040     * @param string config key
70041     * @param boolean return the defining channel
70042     *
70043     * @return string|array the config layer, or an empty string if not found.
70044     *
70045     *         if $returnchannel, the return is an array array('layer' => layername,
70046     *         'channel' => channelname), or an empty string if not found
70047     *
70048     * @access public
70049     */
70050    function definedBy($key, $returnchannel = false)
70051    {
70052        foreach ($this->layers as $layer) {
70053            $channel = $this->getDefaultChannel();
70054            if ($channel !== 'pear.php.net') {
70055                if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
70056                    if ($returnchannel) {
70057                        return array('layer' => $layer, 'channel' => $channel);
70058                    }
70059                    return $layer;
70060                }
70061            }
70062
70063            if (isset($this->configuration[$layer][$key])) {
70064                if ($returnchannel) {
70065                    return array('layer' => $layer, 'channel' => 'pear.php.net');
70066                }
70067                return $layer;
70068            }
70069        }
70070
70071        return '';
70072    }
70073
70074    /**
70075     * Tells whether a given key exists as a config value.
70076     *
70077     * @param string config key
70078     * @return bool whether <config key> exists in this object
70079     *
70080     * @access public
70081     */
70082    function isDefined($key)
70083    {
70084        foreach ($this->layers as $layer) {
70085            if (isset($this->configuration[$layer][$key])) {
70086                return true;
70087            }
70088        }
70089
70090        return false;
70091    }
70092
70093    /**
70094     * Tells whether a given config layer exists.
70095     *
70096     * @param string config layer
70097     * @return bool whether <config layer> exists in this object
70098     *
70099     * @access public
70100     */
70101    function isDefinedLayer($layer)
70102    {
70103        return isset($this->configuration[$layer]);
70104    }
70105
70106    /**
70107     * Returns the layers defined (except the 'default' one)
70108     *
70109     * @return array of the defined layers
70110     */
70111    function getLayers()
70112    {
70113        $cf = $this->configuration;
70114        unset($cf['default']);
70115        return array_keys($cf);
70116    }
70117
70118    function apiVersion()
70119    {
70120        return '1.1';
70121    }
70122
70123    /**
70124     * @return PEAR_Registry
70125     */
70126    function &getRegistry($use = null)
70127    {
70128        $layer = $use === null ? 'user' : $use;
70129        if (isset($this->_registry[$layer])) {
70130            return $this->_registry[$layer];
70131        } elseif ($use === null && isset($this->_registry['system'])) {
70132            return $this->_registry['system'];
70133        } elseif ($use === null && isset($this->_registry['default'])) {
70134            return $this->_registry['default'];
70135        } elseif ($use) {
70136            $a = false;
70137            return $a;
70138        }
70139
70140        // only go here if null was passed in
70141        echo "CRITICAL ERROR: Registry could not be initialized from any value";
70142        exit(1);
70143    }
70144
70145    /**
70146     * This is to allow customization like the use of installroot
70147     * @param PEAR_Registry
70148     * @return bool
70149     */
70150    function setRegistry(&$reg, $layer = 'user')
70151    {
70152        if ($this->_noRegistry) {
70153            return false;
70154        }
70155
70156        if (!in_array($layer, array('user', 'system'))) {
70157            return false;
70158        }
70159
70160        $this->_registry[$layer] = &$reg;
70161        if (is_object($reg)) {
70162            $this->_registry[$layer]->setConfig($this, false);
70163        }
70164
70165        return true;
70166    }
70167
70168    function noRegistry()
70169    {
70170        $this->_noRegistry = true;
70171    }
70172
70173    /**
70174     * @return PEAR_REST
70175     */
70176    function &getREST($version, $options = array())
70177    {
70178        $version = str_replace('.', '', $version);
70179        if (!class_exists($class = 'PEAR_REST_' . $version)) {
70180            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/REST/' . $version . '.php';
70181        }
70182
70183        $remote = &new $class($this, $options);
70184        return $remote;
70185    }
70186
70187    /**
70188     * The ftp server is set in {@link readFTPConfigFile()}.  It exists only if a
70189     * remote configuration file has been specified
70190     * @return PEAR_FTP|false
70191     */
70192    function &getFTP()
70193    {
70194        if (isset($this->_ftp)) {
70195            return $this->_ftp;
70196        }
70197
70198        $a = false;
70199        return $a;
70200    }
70201
70202    function _prependPath($path, $prepend)
70203    {
70204        if (strlen($prepend) > 0) {
70205            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
70206                if (preg_match('/^[a-z]:/i', $prepend)) {
70207                    $prepend = substr($prepend, 2);
70208                } elseif ($prepend{0} != '\\') {
70209                    $prepend = "\\$prepend";
70210                }
70211                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
70212            } else {
70213                $path = $prepend . $path;
70214            }
70215        }
70216        return $path;
70217    }
70218
70219    /**
70220     * @param string|false installation directory to prepend to all _dir variables, or false to
70221     *                     disable
70222     */
70223    function setInstallRoot($root)
70224    {
70225        if (substr($root, -1) == DIRECTORY_SEPARATOR) {
70226            $root = substr($root, 0, -1);
70227        }
70228        $old = $this->_installRoot;
70229        $this->_installRoot = $root;
70230        if (($old != $root) && !$this->_noRegistry) {
70231            foreach (array_keys($this->_registry) as $layer) {
70232                if ($layer == 'ftp' || !isset($this->_registry[$layer])) {
70233                    continue;
70234                }
70235                $this->_registry[$layer] =
70236                    &new PEAR_Registry($this->get('php_dir', $layer, 'pear.php.net'));
70237                $this->_registry[$layer]->setConfig($this, false);
70238                $this->_regInitialized[$layer] = false;
70239            }
70240        }
70241    }
70242}
70243<?php
70244/**
70245 * PEAR_Dependency2, advanced dependency validation
70246 *
70247 * PHP versions 4 and 5
70248 *
70249 * @category   pear
70250 * @package    PEAR
70251 * @author     Greg Beaver <cellog@php.net>
70252 * @copyright  1997-2009 The Authors
70253 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
70254 * @version    CVS: $Id: Dependency2.php 313023 2011-07-06 19:17:11Z dufuz $
70255 * @link       http://pear.php.net/package/PEAR
70256 * @since      File available since Release 1.4.0a1
70257 */
70258
70259/**
70260 * Required for the PEAR_VALIDATE_* constants
70261 */
70262require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validate.php';
70263
70264/**
70265 * Dependency check for PEAR packages
70266 *
70267 * This class handles both version 1.0 and 2.0 dependencies
70268 * WARNING: *any* changes to this class must be duplicated in the
70269 * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc,
70270 * or unit tests will not actually validate the changes
70271 * @category   pear
70272 * @package    PEAR
70273 * @author     Greg Beaver <cellog@php.net>
70274 * @copyright  1997-2009 The Authors
70275 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
70276 * @version    Release: 1.9.4
70277 * @link       http://pear.php.net/package/PEAR
70278 * @since      Class available since Release 1.4.0a1
70279 */
70280class PEAR_Dependency2
70281{
70282    /**
70283     * One of the PEAR_VALIDATE_* states
70284     * @see PEAR_VALIDATE_NORMAL
70285     * @var integer
70286     */
70287    var $_state;
70288
70289    /**
70290     * Command-line options to install/upgrade/uninstall commands
70291     * @param array
70292     */
70293    var $_options;
70294
70295    /**
70296     * @var OS_Guess
70297     */
70298    var $_os;
70299
70300    /**
70301     * @var PEAR_Registry
70302     */
70303    var $_registry;
70304
70305    /**
70306     * @var PEAR_Config
70307     */
70308    var $_config;
70309
70310    /**
70311     * @var PEAR_DependencyDB
70312     */
70313    var $_dependencydb;
70314
70315    /**
70316     * Output of PEAR_Registry::parsedPackageName()
70317     * @var array
70318     */
70319    var $_currentPackage;
70320
70321    /**
70322     * @param PEAR_Config
70323     * @param array installation options
70324     * @param array format of PEAR_Registry::parsedPackageName()
70325     * @param int installation state (one of PEAR_VALIDATE_*)
70326     */
70327    function PEAR_Dependency2(&$config, $installoptions, $package,
70328                              $state = PEAR_VALIDATE_INSTALLING)
70329    {
70330        $this->_config = &$config;
70331        if (!class_exists('PEAR_DependencyDB')) {
70332            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/DependencyDB.php';
70333        }
70334
70335        if (isset($installoptions['packagingroot'])) {
70336            // make sure depdb is in the right location
70337            $config->setInstallRoot($installoptions['packagingroot']);
70338        }
70339
70340        $this->_registry = &$config->getRegistry();
70341        $this->_dependencydb = &PEAR_DependencyDB::singleton($config);
70342        if (isset($installoptions['packagingroot'])) {
70343            $config->setInstallRoot(false);
70344        }
70345
70346        $this->_options = $installoptions;
70347        $this->_state = $state;
70348        if (!class_exists('OS_Guess')) {
70349            require_once 'phar://install-pear-nozlib.phar/' . 'OS/Guess.php';
70350        }
70351
70352        $this->_os = new OS_Guess;
70353        $this->_currentPackage = $package;
70354    }
70355
70356    function _getExtraString($dep)
70357    {
70358        $extra = ' (';
70359        if (isset($dep['uri'])) {
70360            return '';
70361        }
70362
70363        if (isset($dep['recommended'])) {
70364            $extra .= 'recommended version ' . $dep['recommended'];
70365        } else {
70366            if (isset($dep['min'])) {
70367                $extra .= 'version >= ' . $dep['min'];
70368            }
70369
70370            if (isset($dep['max'])) {
70371                if ($extra != ' (') {
70372                    $extra .= ', ';
70373                }
70374                $extra .= 'version <= ' . $dep['max'];
70375            }
70376
70377            if (isset($dep['exclude'])) {
70378                if (!is_array($dep['exclude'])) {
70379                    $dep['exclude'] = array($dep['exclude']);
70380                }
70381
70382                if ($extra != ' (') {
70383                    $extra .= ', ';
70384                }
70385
70386                $extra .= 'excluded versions: ';
70387                foreach ($dep['exclude'] as $i => $exclude) {
70388                    if ($i) {
70389                        $extra .= ', ';
70390                    }
70391                    $extra .= $exclude;
70392                }
70393            }
70394        }
70395
70396        $extra .= ')';
70397        if ($extra == ' ()') {
70398            $extra = '';
70399        }
70400
70401        return $extra;
70402    }
70403
70404    /**
70405     * This makes unit-testing a heck of a lot easier
70406     */
70407    function getPHP_OS()
70408    {
70409        return PHP_OS;
70410    }
70411
70412    /**
70413     * This makes unit-testing a heck of a lot easier
70414     */
70415    function getsysname()
70416    {
70417        return $this->_os->getSysname();
70418    }
70419
70420    /**
70421     * Specify a dependency on an OS.  Use arch for detailed os/processor information
70422     *
70423     * There are two generic OS dependencies that will be the most common, unix and windows.
70424     * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix
70425     */
70426    function validateOsDependency($dep)
70427    {
70428        if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) {
70429            return true;
70430        }
70431
70432        if ($dep['name'] == '*') {
70433            return true;
70434        }
70435
70436        $not = isset($dep['conflicts']) ? true : false;
70437        switch (strtolower($dep['name'])) {
70438            case 'windows' :
70439                if ($not) {
70440                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') {
70441                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70442                            return $this->raiseError("Cannot install %s on Windows");
70443                        }
70444
70445                        return $this->warning("warning: Cannot install %s on Windows");
70446                    }
70447                } else {
70448                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') {
70449                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70450                            return $this->raiseError("Can only install %s on Windows");
70451                        }
70452
70453                        return $this->warning("warning: Can only install %s on Windows");
70454                    }
70455                }
70456            break;
70457            case 'unix' :
70458                $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix');
70459                if ($not) {
70460                    if (in_array($this->getSysname(), $unices)) {
70461                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70462                            return $this->raiseError("Cannot install %s on any Unix system");
70463                        }
70464
70465                        return $this->warning( "warning: Cannot install %s on any Unix system");
70466                    }
70467                } else {
70468                    if (!in_array($this->getSysname(), $unices)) {
70469                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70470                            return $this->raiseError("Can only install %s on a Unix system");
70471                        }
70472
70473                        return $this->warning("warning: Can only install %s on a Unix system");
70474                    }
70475                }
70476            break;
70477            default :
70478                if ($not) {
70479                    if (strtolower($dep['name']) == strtolower($this->getSysname())) {
70480                        if (!isset($this->_options['nodeps']) &&
70481                              !isset($this->_options['force'])) {
70482                            return $this->raiseError('Cannot install %s on ' . $dep['name'] .
70483                                ' operating system');
70484                        }
70485
70486                        return $this->warning('warning: Cannot install %s on ' .
70487                            $dep['name'] . ' operating system');
70488                    }
70489                } else {
70490                    if (strtolower($dep['name']) != strtolower($this->getSysname())) {
70491                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70492                            return $this->raiseError('Cannot install %s on ' .
70493                                $this->getSysname() .
70494                                ' operating system, can only install on ' . $dep['name']);
70495                        }
70496
70497                        return $this->warning('warning: Cannot install %s on ' .
70498                            $this->getSysname() .
70499                            ' operating system, can only install on ' . $dep['name']);
70500                    }
70501                }
70502        }
70503        return true;
70504    }
70505
70506    /**
70507     * This makes unit-testing a heck of a lot easier
70508     */
70509    function matchSignature($pattern)
70510    {
70511        return $this->_os->matchSignature($pattern);
70512    }
70513
70514    /**
70515     * Specify a complex dependency on an OS/processor/kernel version,
70516     * Use OS for simple operating system dependency.
70517     *
70518     * This is the only dependency that accepts an eregable pattern.  The pattern
70519     * will be matched against the php_uname() output parsed by OS_Guess
70520     */
70521    function validateArchDependency($dep)
70522    {
70523        if ($this->_state != PEAR_VALIDATE_INSTALLING) {
70524            return true;
70525        }
70526
70527        $not = isset($dep['conflicts']) ? true : false;
70528        if (!$this->matchSignature($dep['pattern'])) {
70529            if (!$not) {
70530                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70531                    return $this->raiseError('%s Architecture dependency failed, does not ' .
70532                        'match "' . $dep['pattern'] . '"');
70533                }
70534
70535                return $this->warning('warning: %s Architecture dependency failed, does ' .
70536                    'not match "' . $dep['pattern'] . '"');
70537            }
70538
70539            return true;
70540        }
70541
70542        if ($not) {
70543            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70544                return $this->raiseError('%s Architecture dependency failed, required "' .
70545                    $dep['pattern'] . '"');
70546            }
70547
70548            return $this->warning('warning: %s Architecture dependency failed, ' .
70549                'required "' . $dep['pattern'] . '"');
70550        }
70551
70552        return true;
70553    }
70554
70555    /**
70556     * This makes unit-testing a heck of a lot easier
70557     */
70558    function extension_loaded($name)
70559    {
70560        return extension_loaded($name);
70561    }
70562
70563    /**
70564     * This makes unit-testing a heck of a lot easier
70565     */
70566    function phpversion($name = null)
70567    {
70568        if ($name !== null) {
70569            return phpversion($name);
70570        }
70571
70572        return phpversion();
70573    }
70574
70575    function validateExtensionDependency($dep, $required = true)
70576    {
70577        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
70578              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
70579            return true;
70580        }
70581
70582        $loaded = $this->extension_loaded($dep['name']);
70583        $extra  = $this->_getExtraString($dep);
70584        if (isset($dep['exclude'])) {
70585            if (!is_array($dep['exclude'])) {
70586                $dep['exclude'] = array($dep['exclude']);
70587            }
70588        }
70589
70590        if (!isset($dep['min']) && !isset($dep['max']) &&
70591            !isset($dep['recommended']) && !isset($dep['exclude'])
70592        ) {
70593            if ($loaded) {
70594                if (isset($dep['conflicts'])) {
70595                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70596                        return $this->raiseError('%s conflicts with PHP extension "' .
70597                            $dep['name'] . '"' . $extra);
70598                    }
70599
70600                    return $this->warning('warning: %s conflicts with PHP extension "' .
70601                        $dep['name'] . '"' . $extra);
70602                }
70603
70604                return true;
70605            }
70606
70607            if (isset($dep['conflicts'])) {
70608                return true;
70609            }
70610
70611            if ($required) {
70612                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70613                    return $this->raiseError('%s requires PHP extension "' .
70614                        $dep['name'] . '"' . $extra);
70615                }
70616
70617                return $this->warning('warning: %s requires PHP extension "' .
70618                    $dep['name'] . '"' . $extra);
70619            }
70620
70621            return $this->warning('%s can optionally use PHP extension "' .
70622                $dep['name'] . '"' . $extra);
70623        }
70624
70625        if (!$loaded) {
70626            if (isset($dep['conflicts'])) {
70627                return true;
70628            }
70629
70630            if (!$required) {
70631                return $this->warning('%s can optionally use PHP extension "' .
70632                    $dep['name'] . '"' . $extra);
70633            }
70634
70635            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70636                return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
70637                    '"' . $extra);
70638            }
70639
70640            return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
70641                    '"' . $extra);
70642        }
70643
70644        $version = (string) $this->phpversion($dep['name']);
70645        if (empty($version)) {
70646            $version = '0';
70647        }
70648
70649        $fail = false;
70650        if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) {
70651            $fail = true;
70652        }
70653
70654        if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) {
70655            $fail = true;
70656        }
70657
70658        if ($fail && !isset($dep['conflicts'])) {
70659            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70660                return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
70661                    '"' . $extra . ', installed version is ' . $version);
70662            }
70663
70664            return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
70665                '"' . $extra . ', installed version is ' . $version);
70666        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) {
70667            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70668                return $this->raiseError('%s conflicts with PHP extension "' .
70669                    $dep['name'] . '"' . $extra . ', installed version is ' . $version);
70670            }
70671
70672            return $this->warning('warning: %s conflicts with PHP extension "' .
70673                $dep['name'] . '"' . $extra . ', installed version is ' . $version);
70674        }
70675
70676        if (isset($dep['exclude'])) {
70677            foreach ($dep['exclude'] as $exclude) {
70678                if (version_compare($version, $exclude, '==')) {
70679                    if (isset($dep['conflicts'])) {
70680                        continue;
70681                    }
70682
70683                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70684                        return $this->raiseError('%s is not compatible with PHP extension "' .
70685                            $dep['name'] . '" version ' .
70686                            $exclude);
70687                    }
70688
70689                    return $this->warning('warning: %s is not compatible with PHP extension "' .
70690                        $dep['name'] . '" version ' .
70691                        $exclude);
70692                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
70693                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70694                        return $this->raiseError('%s conflicts with PHP extension "' .
70695                            $dep['name'] . '"' . $extra . ', installed version is ' . $version);
70696                    }
70697
70698                    return $this->warning('warning: %s conflicts with PHP extension "' .
70699                        $dep['name'] . '"' . $extra . ', installed version is ' . $version);
70700                }
70701            }
70702        }
70703
70704        if (isset($dep['recommended'])) {
70705            if (version_compare($version, $dep['recommended'], '==')) {
70706                return true;
70707            }
70708
70709            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70710                return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] .
70711                    ' version "' . $version . '"' .
70712                    ' is not the recommended version "' . $dep['recommended'] .
70713                    '", but may be compatible, use --force to install');
70714            }
70715
70716            return $this->warning('warning: %s dependency: PHP extension ' .
70717                $dep['name'] . ' version "' . $version . '"' .
70718                ' is not the recommended version "' . $dep['recommended'].'"');
70719        }
70720
70721        return true;
70722    }
70723
70724    function validatePhpDependency($dep)
70725    {
70726        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
70727              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
70728            return true;
70729        }
70730
70731        $version = $this->phpversion();
70732        $extra   = $this->_getExtraString($dep);
70733        if (isset($dep['exclude'])) {
70734            if (!is_array($dep['exclude'])) {
70735                $dep['exclude'] = array($dep['exclude']);
70736            }
70737        }
70738
70739        if (isset($dep['min'])) {
70740            if (!version_compare($version, $dep['min'], '>=')) {
70741                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70742                    return $this->raiseError('%s requires PHP' .
70743                        $extra . ', installed version is ' . $version);
70744                }
70745
70746                return $this->warning('warning: %s requires PHP' .
70747                    $extra . ', installed version is ' . $version);
70748            }
70749        }
70750
70751        if (isset($dep['max'])) {
70752            if (!version_compare($version, $dep['max'], '<=')) {
70753                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70754                    return $this->raiseError('%s requires PHP' .
70755                        $extra . ', installed version is ' . $version);
70756                }
70757
70758                return $this->warning('warning: %s requires PHP' .
70759                    $extra . ', installed version is ' . $version);
70760            }
70761        }
70762
70763        if (isset($dep['exclude'])) {
70764            foreach ($dep['exclude'] as $exclude) {
70765                if (version_compare($version, $exclude, '==')) {
70766                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70767                        return $this->raiseError('%s is not compatible with PHP version ' .
70768                            $exclude);
70769                    }
70770
70771                    return $this->warning(
70772                        'warning: %s is not compatible with PHP version ' .
70773                        $exclude);
70774                }
70775            }
70776        }
70777
70778        return true;
70779    }
70780
70781    /**
70782     * This makes unit-testing a heck of a lot easier
70783     */
70784    function getPEARVersion()
70785    {
70786        return '1.9.4';
70787    }
70788
70789    function validatePearinstallerDependency($dep)
70790    {
70791        $pearversion = $this->getPEARVersion();
70792        $extra = $this->_getExtraString($dep);
70793        if (isset($dep['exclude'])) {
70794            if (!is_array($dep['exclude'])) {
70795                $dep['exclude'] = array($dep['exclude']);
70796            }
70797        }
70798
70799        if (version_compare($pearversion, $dep['min'], '<')) {
70800            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70801                return $this->raiseError('%s requires PEAR Installer' . $extra .
70802                    ', installed version is ' . $pearversion);
70803            }
70804
70805            return $this->warning('warning: %s requires PEAR Installer' . $extra .
70806                ', installed version is ' . $pearversion);
70807        }
70808
70809        if (isset($dep['max'])) {
70810            if (version_compare($pearversion, $dep['max'], '>')) {
70811                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70812                    return $this->raiseError('%s requires PEAR Installer' . $extra .
70813                        ', installed version is ' . $pearversion);
70814                }
70815
70816                return $this->warning('warning: %s requires PEAR Installer' . $extra .
70817                    ', installed version is ' . $pearversion);
70818            }
70819        }
70820
70821        if (isset($dep['exclude'])) {
70822            if (!isset($dep['exclude'][0])) {
70823                $dep['exclude'] = array($dep['exclude']);
70824            }
70825
70826            foreach ($dep['exclude'] as $exclude) {
70827                if (version_compare($exclude, $pearversion, '==')) {
70828                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70829                        return $this->raiseError('%s is not compatible with PEAR Installer ' .
70830                            'version ' . $exclude);
70831                    }
70832
70833                    return $this->warning('warning: %s is not compatible with PEAR ' .
70834                        'Installer version ' . $exclude);
70835                }
70836            }
70837        }
70838
70839        return true;
70840    }
70841
70842    function validateSubpackageDependency($dep, $required, $params)
70843    {
70844        return $this->validatePackageDependency($dep, $required, $params);
70845    }
70846
70847    /**
70848     * @param array dependency information (2.0 format)
70849     * @param boolean whether this is a required dependency
70850     * @param array a list of downloaded packages to be installed, if any
70851     * @param boolean if true, then deps on pear.php.net that fail will also check
70852     *                against pecl.php.net packages to accomodate extensions that have
70853     *                moved to pecl.php.net from pear.php.net
70854     */
70855    function validatePackageDependency($dep, $required, $params, $depv1 = false)
70856    {
70857        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
70858              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
70859            return true;
70860        }
70861
70862        if (isset($dep['providesextension'])) {
70863            if ($this->extension_loaded($dep['providesextension'])) {
70864                $save = $dep;
70865                $subdep = $dep;
70866                $subdep['name'] = $subdep['providesextension'];
70867                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
70868                $ret = $this->validateExtensionDependency($subdep, $required);
70869                PEAR::popErrorHandling();
70870                if (!PEAR::isError($ret)) {
70871                    return true;
70872                }
70873            }
70874        }
70875
70876        if ($this->_state == PEAR_VALIDATE_INSTALLING) {
70877            return $this->_validatePackageInstall($dep, $required, $depv1);
70878        }
70879
70880        if ($this->_state == PEAR_VALIDATE_DOWNLOADING) {
70881            return $this->_validatePackageDownload($dep, $required, $params, $depv1);
70882        }
70883    }
70884
70885    function _validatePackageDownload($dep, $required, $params, $depv1 = false)
70886    {
70887        $dep['package'] = $dep['name'];
70888        if (isset($dep['uri'])) {
70889            $dep['channel'] = '__uri';
70890        }
70891
70892        $depname = $this->_registry->parsedPackageNameToString($dep, true);
70893        $found = false;
70894        foreach ($params as $param) {
70895            if ($param->isEqual(
70896                  array('package' => $dep['name'],
70897                        'channel' => $dep['channel']))) {
70898                $found = true;
70899                break;
70900            }
70901
70902            if ($depv1 && $dep['channel'] == 'pear.php.net') {
70903                if ($param->isEqual(
70904                  array('package' => $dep['name'],
70905                        'channel' => 'pecl.php.net'))) {
70906                    $found = true;
70907                    break;
70908                }
70909            }
70910        }
70911
70912        if (!$found && isset($dep['providesextension'])) {
70913            foreach ($params as $param) {
70914                if ($param->isExtension($dep['providesextension'])) {
70915                    $found = true;
70916                    break;
70917                }
70918            }
70919        }
70920
70921        if ($found) {
70922            $version = $param->getVersion();
70923            $installed = false;
70924            $downloaded = true;
70925        } else {
70926            if ($this->_registry->packageExists($dep['name'], $dep['channel'])) {
70927                $installed = true;
70928                $downloaded = false;
70929                $version = $this->_registry->packageinfo($dep['name'], 'version',
70930                    $dep['channel']);
70931            } else {
70932                if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'],
70933                      'pear.php.net')) {
70934                    $installed = true;
70935                    $downloaded = false;
70936                    $version = $this->_registry->packageinfo($dep['name'], 'version',
70937                        'pear.php.net');
70938                } else {
70939                    $version = 'not installed or downloaded';
70940                    $installed = false;
70941                    $downloaded = false;
70942                }
70943            }
70944        }
70945
70946        $extra = $this->_getExtraString($dep);
70947        if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
70948            $dep['exclude'] = array($dep['exclude']);
70949        }
70950
70951        if (!isset($dep['min']) && !isset($dep['max']) &&
70952              !isset($dep['recommended']) && !isset($dep['exclude'])
70953        ) {
70954            if ($installed || $downloaded) {
70955                $installed = $installed ? 'installed' : 'downloaded';
70956                if (isset($dep['conflicts'])) {
70957                    $rest = '';
70958                    if ($version) {
70959                        $rest = ", $installed version is " . $version;
70960                    }
70961
70962                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70963                        return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest);
70964                    }
70965
70966                    return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest);
70967                }
70968
70969                return true;
70970            }
70971
70972            if (isset($dep['conflicts'])) {
70973                return true;
70974            }
70975
70976            if ($required) {
70977                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70978                    return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
70979                }
70980
70981                return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
70982            }
70983
70984            return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
70985        }
70986
70987        if (!$installed && !$downloaded) {
70988            if (isset($dep['conflicts'])) {
70989                return true;
70990            }
70991
70992            if ($required) {
70993                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
70994                    return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
70995                }
70996
70997                return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
70998            }
70999
71000            return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
71001        }
71002
71003        $fail = false;
71004        if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) {
71005            $fail = true;
71006        }
71007
71008        if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) {
71009            $fail = true;
71010        }
71011
71012        if ($fail && !isset($dep['conflicts'])) {
71013            $installed = $installed ? 'installed' : 'downloaded';
71014            $dep['package'] = $dep['name'];
71015            $dep = $this->_registry->parsedPackageNameToString($dep, true);
71016            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
71017                return $this->raiseError('%s requires package "' . $depname . '"' .
71018                    $extra . ", $installed version is " . $version);
71019            }
71020
71021            return $this->warning('warning: %s requires package "' . $depname . '"' .
71022                $extra . ", $installed version is " . $version);
71023        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail &&
71024              isset($dep['conflicts']) && !isset($dep['exclude'])) {
71025            $installed = $installed ? 'installed' : 'downloaded';
71026            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
71027                return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra .
71028                    ", $installed version is " . $version);
71029            }
71030
71031            return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
71032                $extra . ", $installed version is " . $version);
71033        }
71034
71035        if (isset($dep['exclude'])) {
71036            $installed = $installed ? 'installed' : 'downloaded';
71037            foreach ($dep['exclude'] as $exclude) {
71038                if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) {
71039                    if (!isset($this->_options['nodeps']) &&
71040                          !isset($this->_options['force'])
71041                    ) {
71042                        return $this->raiseError('%s is not compatible with ' .
71043                            $installed . ' package "' .
71044                            $depname . '" version ' .
71045                            $exclude);
71046                    }
71047
71048                    return $this->warning('warning: %s is not compatible with ' .
71049                        $installed . ' package "' .
71050                        $depname . '" version ' .
71051                        $exclude);
71052                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
71053                    $installed = $installed ? 'installed' : 'downloaded';
71054                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
71055                        return $this->raiseError('%s conflicts with package "' . $depname . '"' .
71056                            $extra . ", $installed version is " . $version);
71057                    }
71058
71059                    return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
71060                        $extra . ", $installed version is " . $version);
71061                }
71062            }
71063        }
71064
71065        if (isset($dep['recommended'])) {
71066            $installed = $installed ? 'installed' : 'downloaded';
71067            if (version_compare($version, $dep['recommended'], '==')) {
71068                return true;
71069            }
71070
71071            if (!$found && $installed) {
71072                $param = $this->_registry->getPackage($dep['name'], $dep['channel']);
71073            }
71074
71075            if ($param) {
71076                $found = false;
71077                foreach ($params as $parent) {
71078                    if ($parent->isEqual($this->_currentPackage)) {
71079                        $found = true;
71080                        break;
71081                    }
71082                }
71083
71084                if ($found) {
71085                    if ($param->isCompatible($parent)) {
71086                        return true;
71087                    }
71088                } else { // this is for validPackage() calls
71089                    $parent = $this->_registry->getPackage($this->_currentPackage['package'],
71090                        $this->_currentPackage['channel']);
71091                    if ($parent !== null && $param->isCompatible($parent)) {
71092                        return true;
71093                    }
71094                }
71095            }
71096
71097            if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) &&
71098                  !isset($this->_options['loose'])
71099            ) {
71100                return $this->raiseError('%s dependency package "' . $depname .
71101                    '" ' . $installed . ' version ' . $version .
71102                    ' is not the recommended version ' . $dep['recommended'] .
71103                    ', but may be compatible, use --force to install');
71104            }
71105
71106            return $this->warning('warning: %s dependency package "' . $depname .
71107                '" ' . $installed . ' version ' . $version .
71108                ' is not the recommended version ' . $dep['recommended']);
71109        }
71110
71111        return true;
71112    }
71113
71114    function _validatePackageInstall($dep, $required, $depv1 = false)
71115    {
71116        return $this->_validatePackageDownload($dep, $required, array(), $depv1);
71117    }
71118
71119    /**
71120     * Verify that uninstalling packages passed in to command line is OK.
71121     *
71122     * @param PEAR_Installer $dl
71123     * @return PEAR_Error|true
71124     */
71125    function validatePackageUninstall(&$dl)
71126    {
71127        if (PEAR::isError($this->_dependencydb)) {
71128            return $this->_dependencydb;
71129        }
71130
71131        $params = array();
71132        // construct an array of "downloaded" packages to fool the package dependency checker
71133        // into using these to validate uninstalls of circular dependencies
71134        $downloaded = &$dl->getUninstallPackages();
71135        foreach ($downloaded as $i => $pf) {
71136            if (!class_exists('PEAR_Downloader_Package')) {
71137                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader/Package.php';
71138            }
71139            $dp = &new PEAR_Downloader_Package($dl);
71140            $dp->setPackageFile($downloaded[$i]);
71141            $params[$i] = &$dp;
71142        }
71143
71144        // check cache
71145        $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' .
71146            strtolower($this->_currentPackage['package']);
71147        if (isset($dl->___uninstall_package_cache)) {
71148            $badpackages = $dl->___uninstall_package_cache;
71149            if (isset($badpackages[$memyselfandI]['warnings'])) {
71150                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
71151                    $dl->log(0, $warning[0]);
71152                }
71153            }
71154
71155            if (isset($badpackages[$memyselfandI]['errors'])) {
71156                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
71157                    if (is_array($error)) {
71158                        $dl->log(0, $error[0]);
71159                    } else {
71160                        $dl->log(0, $error->getMessage());
71161                    }
71162                }
71163
71164                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
71165                    return $this->warning(
71166                        'warning: %s should not be uninstalled, other installed packages depend ' .
71167                        'on this package');
71168                }
71169
71170                return $this->raiseError(
71171                    '%s cannot be uninstalled, other installed packages depend on this package');
71172            }
71173
71174            return true;
71175        }
71176
71177        // first, list the immediate parents of each package to be uninstalled
71178        $perpackagelist = array();
71179        $allparents = array();
71180        foreach ($params as $i => $param) {
71181            $a = array(
71182                'channel' => strtolower($param->getChannel()),
71183                'package' => strtolower($param->getPackage())
71184            );
71185
71186            $deps = $this->_dependencydb->getDependentPackages($a);
71187            if ($deps) {
71188                foreach ($deps as $d) {
71189                    $pardeps = $this->_dependencydb->getDependencies($d);
71190                    foreach ($pardeps as $dep) {
71191                        if (strtolower($dep['dep']['channel']) == $a['channel'] &&
71192                              strtolower($dep['dep']['name']) == $a['package']) {
71193                            if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) {
71194                                $perpackagelist[$a['channel'] . '/' . $a['package']] = array();
71195                            }
71196                            $perpackagelist[$a['channel'] . '/' . $a['package']][]
71197                                = array($d['channel'] . '/' . $d['package'], $dep);
71198                            if (!isset($allparents[$d['channel'] . '/' . $d['package']])) {
71199                                $allparents[$d['channel'] . '/' . $d['package']] = array();
71200                            }
71201                            if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) {
71202                                $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array();
71203                            }
71204                            $allparents[$d['channel'] . '/' . $d['package']]
71205                                       [$a['channel'] . '/' . $a['package']][]
71206                                = array($d, $dep);
71207                        }
71208                    }
71209                }
71210            }
71211        }
71212
71213        // next, remove any packages from the parents list that are not installed
71214        $remove = array();
71215        foreach ($allparents as $parent => $d1) {
71216            foreach ($d1 as $d) {
71217                if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) {
71218                    continue;
71219                }
71220                $remove[$parent] = true;
71221            }
71222        }
71223
71224        // next remove any packages from the parents list that are not passed in for
71225        // uninstallation
71226        foreach ($allparents as $parent => $d1) {
71227            foreach ($d1 as $d) {
71228                foreach ($params as $param) {
71229                    if (strtolower($param->getChannel()) == $d[0][0]['channel'] &&
71230                          strtolower($param->getPackage()) == $d[0][0]['package']) {
71231                        // found it
71232                        continue 3;
71233                    }
71234                }
71235                $remove[$parent] = true;
71236            }
71237        }
71238
71239        // remove all packages whose dependencies fail
71240        // save which ones failed for error reporting
71241        $badchildren = array();
71242        do {
71243            $fail = false;
71244            foreach ($remove as $package => $unused) {
71245                if (!isset($allparents[$package])) {
71246                    continue;
71247                }
71248
71249                foreach ($allparents[$package] as $kid => $d1) {
71250                    foreach ($d1 as $depinfo) {
71251                        if ($depinfo[1]['type'] != 'optional') {
71252                            if (isset($badchildren[$kid])) {
71253                                continue;
71254                            }
71255                            $badchildren[$kid] = true;
71256                            $remove[$kid] = true;
71257                            $fail = true;
71258                            continue 2;
71259                        }
71260                    }
71261                }
71262                if ($fail) {
71263                    // start over, we removed some children
71264                    continue 2;
71265                }
71266            }
71267        } while ($fail);
71268
71269        // next, construct the list of packages that can't be uninstalled
71270        $badpackages = array();
71271        $save = $this->_currentPackage;
71272        foreach ($perpackagelist as $package => $packagedeps) {
71273            foreach ($packagedeps as $parent) {
71274                if (!isset($remove[$parent[0]])) {
71275                    continue;
71276                }
71277
71278                $packagename = $this->_registry->parsePackageName($parent[0]);
71279                $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']);
71280                $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']);
71281                $packagename['package'] = $pa->getPackage();
71282                $this->_currentPackage = $packagename;
71283                // parent is not present in uninstall list, make sure we can actually
71284                // uninstall it (parent dep is optional)
71285                $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']);
71286                $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']);
71287                $parentname['package'] = $pa->getPackage();
71288                $parent[1]['dep']['package'] = $parentname['package'];
71289                $parent[1]['dep']['channel'] = $parentname['channel'];
71290                if ($parent[1]['type'] == 'optional') {
71291                    $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl);
71292                    if ($test !== true) {
71293                        $badpackages[$package]['warnings'][] = $test;
71294                    }
71295                } else {
71296                    $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl);
71297                    if ($test !== true) {
71298                        $badpackages[$package]['errors'][] = $test;
71299                    }
71300                }
71301            }
71302        }
71303
71304        $this->_currentPackage          = $save;
71305        $dl->___uninstall_package_cache = $badpackages;
71306        if (isset($badpackages[$memyselfandI])) {
71307            if (isset($badpackages[$memyselfandI]['warnings'])) {
71308                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
71309                    $dl->log(0, $warning[0]);
71310                }
71311            }
71312
71313            if (isset($badpackages[$memyselfandI]['errors'])) {
71314                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
71315                    if (is_array($error)) {
71316                        $dl->log(0, $error[0]);
71317                    } else {
71318                        $dl->log(0, $error->getMessage());
71319                    }
71320                }
71321
71322                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
71323                    return $this->warning(
71324                        'warning: %s should not be uninstalled, other installed packages depend ' .
71325                        'on this package');
71326                }
71327
71328                return $this->raiseError(
71329                    '%s cannot be uninstalled, other installed packages depend on this package');
71330            }
71331        }
71332
71333        return true;
71334    }
71335
71336    function _validatePackageUninstall($dep, $required, $dl)
71337    {
71338        $depname = $this->_registry->parsedPackageNameToString($dep, true);
71339        $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']);
71340        if (!$version) {
71341            return true;
71342        }
71343
71344        $extra = $this->_getExtraString($dep);
71345        if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
71346            $dep['exclude'] = array($dep['exclude']);
71347        }
71348
71349        if (isset($dep['conflicts'])) {
71350            return true; // uninstall OK - these packages conflict (probably installed with --force)
71351        }
71352
71353        if (!isset($dep['min']) && !isset($dep['max'])) {
71354            if (!$required) {
71355                return $this->warning('"' . $depname . '" can be optionally used by ' .
71356                        'installed package %s' . $extra);
71357            }
71358
71359            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
71360                return $this->raiseError('"' . $depname . '" is required by ' .
71361                    'installed package %s' . $extra);
71362            }
71363
71364            return $this->warning('warning: "' . $depname . '" is required by ' .
71365                'installed package %s' . $extra);
71366        }
71367
71368        $fail = false;
71369        if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) {
71370            $fail = true;
71371        }
71372
71373        if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) {
71374            $fail = true;
71375        }
71376
71377        // we re-use this variable, preserve the original value
71378        $saverequired = $required;
71379        if (!$required) {
71380            return $this->warning($depname . $extra . ' can be optionally used by installed package' .
71381                    ' "%s"');
71382        }
71383
71384        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
71385            return $this->raiseError($depname . $extra . ' is required by installed package' .
71386                ' "%s"');
71387        }
71388
71389        return $this->raiseError('warning: ' . $depname . $extra .
71390            ' is required by installed package "%s"');
71391    }
71392
71393    /**
71394     * validate a downloaded package against installed packages
71395     *
71396     * As of PEAR 1.4.3, this will only validate
71397     *
71398     * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2
71399     *              $pkg package identifier (either
71400     *                   array('package' => blah, 'channel' => blah) or an array with
71401     *                   index 'info' referencing an object)
71402     * @param PEAR_Downloader $dl
71403     * @param array $params full list of packages to install
71404     * @return true|PEAR_Error
71405     */
71406    function validatePackage($pkg, &$dl, $params = array())
71407    {
71408        if (is_array($pkg) && isset($pkg['info'])) {
71409            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']);
71410        } else {
71411            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg);
71412        }
71413
71414        $fail = false;
71415        if ($deps) {
71416            if (!class_exists('PEAR_Downloader_Package')) {
71417                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader/Package.php';
71418            }
71419
71420            $dp = &new PEAR_Downloader_Package($dl);
71421            if (is_object($pkg)) {
71422                $dp->setPackageFile($pkg);
71423            } else {
71424                $dp->setDownloadURL($pkg);
71425            }
71426
71427            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
71428            foreach ($deps as $channel => $info) {
71429                foreach ($info as $package => $ds) {
71430                    foreach ($params as $packd) {
71431                        if (strtolower($packd->getPackage()) == strtolower($package) &&
71432                              $packd->getChannel() == $channel) {
71433                            $dl->log(3, 'skipping installed package check of "' .
71434                                        $this->_registry->parsedPackageNameToString(
71435                                            array('channel' => $channel, 'package' => $package),
71436                                            true) .
71437                                        '", version "' . $packd->getVersion() . '" will be ' .
71438                                        'downloaded and installed');
71439                            continue 2; // jump to next package
71440                        }
71441                    }
71442
71443                    foreach ($ds as $d) {
71444                        $checker = &new PEAR_Dependency2($this->_config, $this->_options,
71445                            array('channel' => $channel, 'package' => $package), $this->_state);
71446                        $dep = $d['dep'];
71447                        $required = $d['type'] == 'required';
71448                        $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp));
71449                        if (is_array($ret)) {
71450                            $dl->log(0, $ret[0]);
71451                        } elseif (PEAR::isError($ret)) {
71452                            $dl->log(0, $ret->getMessage());
71453                            $fail = true;
71454                        }
71455                    }
71456                }
71457            }
71458            PEAR::popErrorHandling();
71459        }
71460
71461        if ($fail) {
71462            return $this->raiseError(
71463                '%s cannot be installed, conflicts with installed packages');
71464        }
71465
71466        return true;
71467    }
71468
71469    /**
71470     * validate a package.xml 1.0 dependency
71471     */
71472    function validateDependency1($dep, $params = array())
71473    {
71474        if (!isset($dep['optional'])) {
71475            $dep['optional'] = 'no';
71476        }
71477
71478        list($newdep, $type) = $this->normalizeDep($dep);
71479        if (!$newdep) {
71480            return $this->raiseError("Invalid Dependency");
71481        }
71482
71483        if (method_exists($this, "validate{$type}Dependency")) {
71484            return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no',
71485                $params, true);
71486        }
71487    }
71488
71489    /**
71490     * Convert a 1.0 dep into a 2.0 dep
71491     */
71492    function normalizeDep($dep)
71493    {
71494        $types = array(
71495            'pkg' => 'Package',
71496            'ext' => 'Extension',
71497            'os' => 'Os',
71498            'php' => 'Php'
71499        );
71500
71501        if (!isset($types[$dep['type']])) {
71502            return array(false, false);
71503        }
71504
71505        $type = $types[$dep['type']];
71506
71507        $newdep = array();
71508        switch ($type) {
71509            case 'Package' :
71510                $newdep['channel'] = 'pear.php.net';
71511            case 'Extension' :
71512            case 'Os' :
71513                $newdep['name'] = $dep['name'];
71514            break;
71515        }
71516
71517        $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']);
71518        switch ($dep['rel']) {
71519            case 'has' :
71520                return array($newdep, $type);
71521            break;
71522            case 'not' :
71523                $newdep['conflicts'] = true;
71524            break;
71525            case '>=' :
71526            case '>' :
71527                $newdep['min'] = $dep['version'];
71528                if ($dep['rel'] == '>') {
71529                    $newdep['exclude'] = $dep['version'];
71530                }
71531            break;
71532            case '<=' :
71533            case '<' :
71534                $newdep['max'] = $dep['version'];
71535                if ($dep['rel'] == '<') {
71536                    $newdep['exclude'] = $dep['version'];
71537                }
71538            break;
71539            case 'ne' :
71540            case '!=' :
71541                $newdep['min'] = '0';
71542                $newdep['max'] = '100000';
71543                $newdep['exclude'] = $dep['version'];
71544            break;
71545            case '==' :
71546                $newdep['min'] = $dep['version'];
71547                $newdep['max'] = $dep['version'];
71548            break;
71549        }
71550        if ($type == 'Php') {
71551            if (!isset($newdep['min'])) {
71552                $newdep['min'] = '4.4.0';
71553            }
71554
71555            if (!isset($newdep['max'])) {
71556                $newdep['max'] = '6.0.0';
71557            }
71558        }
71559        return array($newdep, $type);
71560    }
71561
71562    /**
71563     * Converts text comparing operators to them sign equivalents
71564     *
71565     * Example: 'ge' to '>='
71566     *
71567     * @access public
71568     * @param  string Operator
71569     * @return string Sign equivalent
71570     */
71571    function signOperator($operator)
71572    {
71573        switch($operator) {
71574            case 'lt': return '<';
71575            case 'le': return '<=';
71576            case 'gt': return '>';
71577            case 'ge': return '>=';
71578            case 'eq': return '==';
71579            case 'ne': return '!=';
71580            default:
71581                return $operator;
71582        }
71583    }
71584
71585    function raiseError($msg)
71586    {
71587        if (isset($this->_options['ignore-errors'])) {
71588            return $this->warning($msg);
71589        }
71590
71591        return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString(
71592            $this->_currentPackage, true)));
71593    }
71594
71595    function warning($msg)
71596    {
71597        return array(sprintf($msg, $this->_registry->parsedPackageNameToString(
71598            $this->_currentPackage, true)));
71599    }
71600}<?php
71601/**
71602 * PEAR_DependencyDB, advanced installed packages dependency database
71603 *
71604 * PHP versions 4 and 5
71605 *
71606 * @category   pear
71607 * @package    PEAR
71608 * @author     Tomas V. V. Cox <cox@idecnet.com>
71609 * @author     Greg Beaver <cellog@php.net>
71610 * @copyright  1997-2009 The Authors
71611 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
71612 * @version    CVS: $Id: DependencyDB.php 313023 2011-07-06 19:17:11Z dufuz $
71613 * @link       http://pear.php.net/package/PEAR
71614 * @since      File available since Release 1.4.0a1
71615 */
71616
71617/**
71618 * Needed for error handling
71619 */
71620require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
71621require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Config.php';
71622
71623$GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array();
71624/**
71625 * Track dependency relationships between installed packages
71626 * @category   pear
71627 * @package    PEAR
71628 * @author     Greg Beaver <cellog@php.net>
71629 * @author     Tomas V.V.Cox <cox@idec.net.com>
71630 * @copyright  1997-2009 The Authors
71631 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
71632 * @version    Release: 1.9.4
71633 * @link       http://pear.php.net/package/PEAR
71634 * @since      Class available since Release 1.4.0a1
71635 */
71636class PEAR_DependencyDB
71637{
71638    // {{{ properties
71639
71640    /**
71641     * This is initialized by {@link setConfig()}
71642     * @var PEAR_Config
71643     * @access private
71644     */
71645    var $_config;
71646    /**
71647     * This is initialized by {@link setConfig()}
71648     * @var PEAR_Registry
71649     * @access private
71650     */
71651    var $_registry;
71652    /**
71653     * Filename of the dependency DB (usually .depdb)
71654     * @var string
71655     * @access private
71656     */
71657    var $_depdb = false;
71658    /**
71659     * File name of the lockfile (usually .depdblock)
71660     * @var string
71661     * @access private
71662     */
71663    var $_lockfile = false;
71664    /**
71665     * Open file resource for locking the lockfile
71666     * @var resource|false
71667     * @access private
71668     */
71669    var $_lockFp = false;
71670    /**
71671     * API version of this class, used to validate a file on-disk
71672     * @var string
71673     * @access private
71674     */
71675    var $_version = '1.0';
71676    /**
71677     * Cached dependency database file
71678     * @var array|null
71679     * @access private
71680     */
71681    var $_cache;
71682
71683    // }}}
71684    // {{{ & singleton()
71685
71686    /**
71687     * Get a raw dependency database.  Calls setConfig() and assertDepsDB()
71688     * @param PEAR_Config
71689     * @param string|false full path to the dependency database, or false to use default
71690     * @return PEAR_DependencyDB|PEAR_Error
71691     * @static
71692     */
71693    function &singleton(&$config, $depdb = false)
71694    {
71695        $phpdir = $config->get('php_dir', null, 'pear.php.net');
71696        if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir])) {
71697            $a = new PEAR_DependencyDB;
71698            $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir] = &$a;
71699            $a->setConfig($config, $depdb);
71700            $e = $a->assertDepsDB();
71701            if (PEAR::isError($e)) {
71702                return $e;
71703            }
71704        }
71705
71706        return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir];
71707    }
71708
71709    /**
71710     * Set up the registry/location of dependency DB
71711     * @param PEAR_Config|false
71712     * @param string|false full path to the dependency database, or false to use default
71713     */
71714    function setConfig(&$config, $depdb = false)
71715    {
71716        if (!$config) {
71717            $this->_config = &PEAR_Config::singleton();
71718        } else {
71719            $this->_config = &$config;
71720        }
71721
71722        $this->_registry = &$this->_config->getRegistry();
71723        if (!$depdb) {
71724            $this->_depdb = $this->_config->get('php_dir', null, 'pear.php.net') .
71725                DIRECTORY_SEPARATOR . '.depdb';
71726        } else {
71727            $this->_depdb = $depdb;
71728        }
71729
71730        $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock';
71731    }
71732    // }}}
71733
71734    function hasWriteAccess()
71735    {
71736        if (!file_exists($this->_depdb)) {
71737            $dir = $this->_depdb;
71738            while ($dir && $dir != '.') {
71739                $dir = dirname($dir); // cd ..
71740                if ($dir != '.' && file_exists($dir)) {
71741                    if (is_writeable($dir)) {
71742                        return true;
71743                    }
71744
71745                    return false;
71746                }
71747            }
71748
71749            return false;
71750        }
71751
71752        return is_writeable($this->_depdb);
71753    }
71754
71755    // {{{ assertDepsDB()
71756
71757    /**
71758     * Create the dependency database, if it doesn't exist.  Error if the database is
71759     * newer than the code reading it.
71760     * @return void|PEAR_Error
71761     */
71762    function assertDepsDB()
71763    {
71764        if (!is_file($this->_depdb)) {
71765            $this->rebuildDB();
71766            return;
71767        }
71768
71769        $depdb = $this->_getDepDB();
71770        // Datatype format has been changed, rebuild the Deps DB
71771        if ($depdb['_version'] < $this->_version) {
71772            $this->rebuildDB();
71773        }
71774
71775        if ($depdb['_version']{0} > $this->_version{0}) {
71776            return PEAR::raiseError('Dependency database is version ' .
71777                $depdb['_version'] . ', and we are version ' .
71778                $this->_version . ', cannot continue');
71779        }
71780    }
71781
71782    /**
71783     * Get a list of installed packages that depend on this package
71784     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
71785     * @return array|false
71786     */
71787    function getDependentPackages(&$pkg)
71788    {
71789        $data = $this->_getDepDB();
71790        if (is_object($pkg)) {
71791            $channel = strtolower($pkg->getChannel());
71792            $package = strtolower($pkg->getPackage());
71793        } else {
71794            $channel = strtolower($pkg['channel']);
71795            $package = strtolower($pkg['package']);
71796        }
71797
71798        if (isset($data['packages'][$channel][$package])) {
71799            return $data['packages'][$channel][$package];
71800        }
71801
71802        return false;
71803    }
71804
71805    /**
71806     * Get a list of the actual dependencies of installed packages that depend on
71807     * a package.
71808     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
71809     * @return array|false
71810     */
71811    function getDependentPackageDependencies(&$pkg)
71812    {
71813        $data = $this->_getDepDB();
71814        if (is_object($pkg)) {
71815            $channel = strtolower($pkg->getChannel());
71816            $package = strtolower($pkg->getPackage());
71817        } else {
71818            $channel = strtolower($pkg['channel']);
71819            $package = strtolower($pkg['package']);
71820        }
71821
71822        $depend = $this->getDependentPackages($pkg);
71823        if (!$depend) {
71824            return false;
71825        }
71826
71827        $dependencies = array();
71828        foreach ($depend as $info) {
71829            $temp = $this->getDependencies($info);
71830            foreach ($temp as $dep) {
71831                if (
71832                    isset($dep['dep'], $dep['dep']['channel'], $dep['dep']['name']) &&
71833                    strtolower($dep['dep']['channel']) == $channel &&
71834                    strtolower($dep['dep']['name']) == $package
71835                ) {
71836                    if (!isset($dependencies[$info['channel']])) {
71837                        $dependencies[$info['channel']] = array();
71838                    }
71839
71840                    if (!isset($dependencies[$info['channel']][$info['package']])) {
71841                        $dependencies[$info['channel']][$info['package']] = array();
71842                    }
71843                    $dependencies[$info['channel']][$info['package']][] = $dep;
71844                }
71845            }
71846        }
71847
71848        return $dependencies;
71849    }
71850
71851    /**
71852     * Get a list of dependencies of this installed package
71853     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
71854     * @return array|false
71855     */
71856    function getDependencies(&$pkg)
71857    {
71858        if (is_object($pkg)) {
71859            $channel = strtolower($pkg->getChannel());
71860            $package = strtolower($pkg->getPackage());
71861        } else {
71862            $channel = strtolower($pkg['channel']);
71863            $package = strtolower($pkg['package']);
71864        }
71865
71866        $data = $this->_getDepDB();
71867        if (isset($data['dependencies'][$channel][$package])) {
71868            return $data['dependencies'][$channel][$package];
71869        }
71870
71871        return false;
71872    }
71873
71874    /**
71875     * Determine whether $parent depends on $child, near or deep
71876     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
71877     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
71878     */
71879    function dependsOn($parent, $child)
71880    {
71881        $c = array();
71882        $this->_getDepDB();
71883        return $this->_dependsOn($parent, $child, $c);
71884    }
71885
71886    function _dependsOn($parent, $child, &$checked)
71887    {
71888        if (is_object($parent)) {
71889            $channel = strtolower($parent->getChannel());
71890            $package = strtolower($parent->getPackage());
71891        } else {
71892            $channel = strtolower($parent['channel']);
71893            $package = strtolower($parent['package']);
71894        }
71895
71896        if (is_object($child)) {
71897            $depchannel = strtolower($child->getChannel());
71898            $deppackage = strtolower($child->getPackage());
71899        } else {
71900            $depchannel = strtolower($child['channel']);
71901            $deppackage = strtolower($child['package']);
71902        }
71903
71904        if (isset($checked[$channel][$package][$depchannel][$deppackage])) {
71905            return false; // avoid endless recursion
71906        }
71907
71908        $checked[$channel][$package][$depchannel][$deppackage] = true;
71909        if (!isset($this->_cache['dependencies'][$channel][$package])) {
71910            return false;
71911        }
71912
71913        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
71914            if (isset($info['dep']['uri'])) {
71915                if (is_object($child)) {
71916                    if ($info['dep']['uri'] == $child->getURI()) {
71917                        return true;
71918                    }
71919                } elseif (isset($child['uri'])) {
71920                    if ($info['dep']['uri'] == $child['uri']) {
71921                        return true;
71922                    }
71923                }
71924                return false;
71925            }
71926
71927            if (strtolower($info['dep']['channel']) == $depchannel &&
71928                  strtolower($info['dep']['name']) == $deppackage) {
71929                return true;
71930            }
71931        }
71932
71933        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
71934            if (isset($info['dep']['uri'])) {
71935                if ($this->_dependsOn(array(
71936                        'uri' => $info['dep']['uri'],
71937                        'package' => $info['dep']['name']), $child, $checked)) {
71938                    return true;
71939                }
71940            } else {
71941                if ($this->_dependsOn(array(
71942                        'channel' => $info['dep']['channel'],
71943                        'package' => $info['dep']['name']), $child, $checked)) {
71944                    return true;
71945                }
71946            }
71947        }
71948
71949        return false;
71950    }
71951
71952    /**
71953     * Register dependencies of a package that is being installed or upgraded
71954     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2
71955     */
71956    function installPackage(&$package)
71957    {
71958        $data = $this->_getDepDB();
71959        unset($this->_cache);
71960        $this->_setPackageDeps($data, $package);
71961        $this->_writeDepDB($data);
71962    }
71963
71964    /**
71965     * Remove dependencies of a package that is being uninstalled, or upgraded.
71966     *
71967     * Upgraded packages first uninstall, then install
71968     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have
71969     *        indices 'channel' and 'package'
71970     */
71971    function uninstallPackage(&$pkg)
71972    {
71973        $data = $this->_getDepDB();
71974        unset($this->_cache);
71975        if (is_object($pkg)) {
71976            $channel = strtolower($pkg->getChannel());
71977            $package = strtolower($pkg->getPackage());
71978        } else {
71979            $channel = strtolower($pkg['channel']);
71980            $package = strtolower($pkg['package']);
71981        }
71982
71983        if (!isset($data['dependencies'][$channel][$package])) {
71984            return true;
71985        }
71986
71987        foreach ($data['dependencies'][$channel][$package] as $dep) {
71988            $found      = false;
71989            $depchannel = isset($dep['dep']['uri']) ? '__uri' : strtolower($dep['dep']['channel']);
71990            $depname    = strtolower($dep['dep']['name']);
71991            if (isset($data['packages'][$depchannel][$depname])) {
71992                foreach ($data['packages'][$depchannel][$depname] as $i => $info) {
71993                    if ($info['channel'] == $channel && $info['package'] == $package) {
71994                        $found = true;
71995                        break;
71996                    }
71997                }
71998            }
71999
72000            if ($found) {
72001                unset($data['packages'][$depchannel][$depname][$i]);
72002                if (!count($data['packages'][$depchannel][$depname])) {
72003                    unset($data['packages'][$depchannel][$depname]);
72004                    if (!count($data['packages'][$depchannel])) {
72005                        unset($data['packages'][$depchannel]);
72006                    }
72007                } else {
72008                    $data['packages'][$depchannel][$depname] =
72009                        array_values($data['packages'][$depchannel][$depname]);
72010                }
72011            }
72012        }
72013
72014        unset($data['dependencies'][$channel][$package]);
72015        if (!count($data['dependencies'][$channel])) {
72016            unset($data['dependencies'][$channel]);
72017        }
72018
72019        if (!count($data['dependencies'])) {
72020            unset($data['dependencies']);
72021        }
72022
72023        if (!count($data['packages'])) {
72024            unset($data['packages']);
72025        }
72026
72027        $this->_writeDepDB($data);
72028    }
72029
72030    /**
72031     * Rebuild the dependency DB by reading registry entries.
72032     * @return true|PEAR_Error
72033     */
72034    function rebuildDB()
72035    {
72036        $depdb = array('_version' => $this->_version);
72037        if (!$this->hasWriteAccess()) {
72038            // allow startup for read-only with older Registry
72039            return $depdb;
72040        }
72041
72042        $packages = $this->_registry->listAllPackages();
72043        if (PEAR::isError($packages)) {
72044            return $packages;
72045        }
72046
72047        foreach ($packages as $channel => $ps) {
72048            foreach ($ps as $package) {
72049                $package = $this->_registry->getPackage($package, $channel);
72050                if (PEAR::isError($package)) {
72051                    return $package;
72052                }
72053                $this->_setPackageDeps($depdb, $package);
72054            }
72055        }
72056
72057        $error = $this->_writeDepDB($depdb);
72058        if (PEAR::isError($error)) {
72059            return $error;
72060        }
72061
72062        $this->_cache = $depdb;
72063        return true;
72064    }
72065
72066    /**
72067     * Register usage of the dependency DB to prevent race conditions
72068     * @param int one of the LOCK_* constants
72069     * @return true|PEAR_Error
72070     * @access private
72071     */
72072    function _lock($mode = LOCK_EX)
72073    {
72074        if (stristr(php_uname(), 'Windows 9')) {
72075            return true;
72076        }
72077
72078        if ($mode != LOCK_UN && is_resource($this->_lockFp)) {
72079            // XXX does not check type of lock (LOCK_SH/LOCK_EX)
72080            return true;
72081        }
72082
72083        $open_mode = 'w';
72084        // XXX People reported problems with LOCK_SH and 'w'
72085        if ($mode === LOCK_SH) {
72086            if (!file_exists($this->_lockfile)) {
72087                touch($this->_lockfile);
72088            } elseif (!is_file($this->_lockfile)) {
72089                return PEAR::raiseError('could not create Dependency lock file, ' .
72090                    'it exists and is not a regular file');
72091            }
72092            $open_mode = 'r';
72093        }
72094
72095        if (!is_resource($this->_lockFp)) {
72096            $this->_lockFp = @fopen($this->_lockfile, $open_mode);
72097        }
72098
72099        if (!is_resource($this->_lockFp)) {
72100            return PEAR::raiseError("could not create Dependency lock file" .
72101                                     (isset($php_errormsg) ? ": " . $php_errormsg : ""));
72102        }
72103
72104        if (!(int)flock($this->_lockFp, $mode)) {
72105            switch ($mode) {
72106                case LOCK_SH: $str = 'shared';    break;
72107                case LOCK_EX: $str = 'exclusive'; break;
72108                case LOCK_UN: $str = 'unlock';    break;
72109                default:      $str = 'unknown';   break;
72110            }
72111
72112            return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)");
72113        }
72114
72115        return true;
72116    }
72117
72118    /**
72119     * Release usage of dependency DB
72120     * @return true|PEAR_Error
72121     * @access private
72122     */
72123    function _unlock()
72124    {
72125        $ret = $this->_lock(LOCK_UN);
72126        if (is_resource($this->_lockFp)) {
72127            fclose($this->_lockFp);
72128        }
72129        $this->_lockFp = null;
72130        return $ret;
72131    }
72132
72133    /**
72134     * Load the dependency database from disk, or return the cache
72135     * @return array|PEAR_Error
72136     */
72137    function _getDepDB()
72138    {
72139        if (!$this->hasWriteAccess()) {
72140            return array('_version' => $this->_version);
72141        }
72142
72143        if (isset($this->_cache)) {
72144            return $this->_cache;
72145        }
72146
72147        if (!$fp = fopen($this->_depdb, 'r')) {
72148            $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'");
72149            return $err;
72150        }
72151
72152        $rt = get_magic_quotes_runtime();
72153        set_magic_quotes_runtime(0);
72154        clearstatcache();
72155        fclose($fp);
72156        $data = unserialize(file_get_contents($this->_depdb));
72157        set_magic_quotes_runtime($rt);
72158        $this->_cache = $data;
72159        return $data;
72160    }
72161
72162    /**
72163     * Write out the dependency database to disk
72164     * @param array the database
72165     * @return true|PEAR_Error
72166     * @access private
72167     */
72168    function _writeDepDB(&$deps)
72169    {
72170        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
72171            return $e;
72172        }
72173
72174        if (!$fp = fopen($this->_depdb, 'wb')) {
72175            $this->_unlock();
72176            return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing");
72177        }
72178
72179        $rt = get_magic_quotes_runtime();
72180        set_magic_quotes_runtime(0);
72181        fwrite($fp, serialize($deps));
72182        set_magic_quotes_runtime($rt);
72183        fclose($fp);
72184        $this->_unlock();
72185        $this->_cache = $deps;
72186        return true;
72187    }
72188
72189    /**
72190     * Register all dependencies from a package in the dependencies database, in essence
72191     * "installing" the package's dependency information
72192     * @param array the database
72193     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
72194     * @access private
72195     */
72196    function _setPackageDeps(&$data, &$pkg)
72197    {
72198        $pkg->setConfig($this->_config);
72199        if ($pkg->getPackagexmlVersion() == '1.0') {
72200            $gen = &$pkg->getDefaultGenerator();
72201            $deps = $gen->dependenciesToV2();
72202        } else {
72203            $deps = $pkg->getDeps(true);
72204        }
72205
72206        if (!$deps) {
72207            return;
72208        }
72209
72210        if (!is_array($data)) {
72211            $data = array();
72212        }
72213
72214        if (!isset($data['dependencies'])) {
72215            $data['dependencies'] = array();
72216        }
72217
72218        $channel = strtolower($pkg->getChannel());
72219        $package = strtolower($pkg->getPackage());
72220
72221        if (!isset($data['dependencies'][$channel])) {
72222            $data['dependencies'][$channel] = array();
72223        }
72224
72225        $data['dependencies'][$channel][$package] = array();
72226        if (isset($deps['required']['package'])) {
72227            if (!isset($deps['required']['package'][0])) {
72228                $deps['required']['package'] = array($deps['required']['package']);
72229            }
72230
72231            foreach ($deps['required']['package'] as $dep) {
72232                $this->_registerDep($data, $pkg, $dep, 'required');
72233            }
72234        }
72235
72236        if (isset($deps['optional']['package'])) {
72237            if (!isset($deps['optional']['package'][0])) {
72238                $deps['optional']['package'] = array($deps['optional']['package']);
72239            }
72240
72241            foreach ($deps['optional']['package'] as $dep) {
72242                $this->_registerDep($data, $pkg, $dep, 'optional');
72243            }
72244        }
72245
72246        if (isset($deps['required']['subpackage'])) {
72247            if (!isset($deps['required']['subpackage'][0])) {
72248                $deps['required']['subpackage'] = array($deps['required']['subpackage']);
72249            }
72250
72251            foreach ($deps['required']['subpackage'] as $dep) {
72252                $this->_registerDep($data, $pkg, $dep, 'required');
72253            }
72254        }
72255
72256        if (isset($deps['optional']['subpackage'])) {
72257            if (!isset($deps['optional']['subpackage'][0])) {
72258                $deps['optional']['subpackage'] = array($deps['optional']['subpackage']);
72259            }
72260
72261            foreach ($deps['optional']['subpackage'] as $dep) {
72262                $this->_registerDep($data, $pkg, $dep, 'optional');
72263            }
72264        }
72265
72266        if (isset($deps['group'])) {
72267            if (!isset($deps['group'][0])) {
72268                $deps['group'] = array($deps['group']);
72269            }
72270
72271            foreach ($deps['group'] as $group) {
72272                if (isset($group['package'])) {
72273                    if (!isset($group['package'][0])) {
72274                        $group['package'] = array($group['package']);
72275                    }
72276
72277                    foreach ($group['package'] as $dep) {
72278                        $this->_registerDep($data, $pkg, $dep, 'optional',
72279                            $group['attribs']['name']);
72280                    }
72281                }
72282
72283                if (isset($group['subpackage'])) {
72284                    if (!isset($group['subpackage'][0])) {
72285                        $group['subpackage'] = array($group['subpackage']);
72286                    }
72287
72288                    foreach ($group['subpackage'] as $dep) {
72289                        $this->_registerDep($data, $pkg, $dep, 'optional',
72290                            $group['attribs']['name']);
72291                    }
72292                }
72293            }
72294        }
72295
72296        if ($data['dependencies'][$channel][$package] == array()) {
72297            unset($data['dependencies'][$channel][$package]);
72298            if (!count($data['dependencies'][$channel])) {
72299                unset($data['dependencies'][$channel]);
72300            }
72301        }
72302    }
72303
72304    /**
72305     * @param array the database
72306     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
72307     * @param array the specific dependency
72308     * @param required|optional whether this is a required or an optional dep
72309     * @param string|false dependency group this dependency is from, or false for ordinary dep
72310     */
72311    function _registerDep(&$data, &$pkg, $dep, $type, $group = false)
72312    {
72313        $info = array(
72314            'dep'   => $dep,
72315            'type'  => $type,
72316            'group' => $group
72317        );
72318
72319        $dep  = array_map('strtolower', $dep);
72320        $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
72321        if (!isset($data['dependencies'])) {
72322            $data['dependencies'] = array();
72323        }
72324
72325        $channel = strtolower($pkg->getChannel());
72326        $package = strtolower($pkg->getPackage());
72327
72328        if (!isset($data['dependencies'][$channel])) {
72329            $data['dependencies'][$channel] = array();
72330        }
72331
72332        if (!isset($data['dependencies'][$channel][$package])) {
72333            $data['dependencies'][$channel][$package] = array();
72334        }
72335
72336        $data['dependencies'][$channel][$package][] = $info;
72337        if (isset($data['packages'][$depchannel][$dep['name']])) {
72338            $found = false;
72339            foreach ($data['packages'][$depchannel][$dep['name']] as $i => $p) {
72340                if ($p['channel'] == $channel && $p['package'] == $package) {
72341                    $found = true;
72342                    break;
72343                }
72344            }
72345        } else {
72346            if (!isset($data['packages'])) {
72347                $data['packages'] = array();
72348            }
72349
72350            if (!isset($data['packages'][$depchannel])) {
72351                $data['packages'][$depchannel] = array();
72352            }
72353
72354            if (!isset($data['packages'][$depchannel][$dep['name']])) {
72355                $data['packages'][$depchannel][$dep['name']] = array();
72356            }
72357
72358            $found = false;
72359        }
72360
72361        if (!$found) {
72362            $data['packages'][$depchannel][$dep['name']][] = array(
72363                'channel' => $channel,
72364                'package' => $package
72365            );
72366        }
72367    }
72368}<?php
72369/**
72370 * PEAR_Downloader, the PEAR Installer's download utility class
72371 *
72372 * PHP versions 4 and 5
72373 *
72374 * @category   pear
72375 * @package    PEAR
72376 * @author     Greg Beaver <cellog@php.net>
72377 * @author     Stig Bakken <ssb@php.net>
72378 * @author     Tomas V. V. Cox <cox@idecnet.com>
72379 * @author     Martin Jansen <mj@php.net>
72380 * @copyright  1997-2009 The Authors
72381 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
72382 * @version    CVS: $Id: Downloader.php 313024 2011-07-06 19:51:24Z dufuz $
72383 * @link       http://pear.php.net/package/PEAR
72384 * @since      File available since Release 1.3.0
72385 */
72386
72387/**
72388 * Needed for constants, extending
72389 */
72390require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
72391
72392define('PEAR_INSTALLER_OK',       1);
72393define('PEAR_INSTALLER_FAILED',   0);
72394define('PEAR_INSTALLER_SKIPPED', -1);
72395define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
72396
72397/**
72398 * Administration class used to download anything from the internet (PEAR Packages,
72399 * static URLs, xml files)
72400 *
72401 * @category   pear
72402 * @package    PEAR
72403 * @author     Greg Beaver <cellog@php.net>
72404 * @author     Stig Bakken <ssb@php.net>
72405 * @author     Tomas V. V. Cox <cox@idecnet.com>
72406 * @author     Martin Jansen <mj@php.net>
72407 * @copyright  1997-2009 The Authors
72408 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
72409 * @version    Release: 1.9.4
72410 * @link       http://pear.php.net/package/PEAR
72411 * @since      Class available since Release 1.3.0
72412 */
72413class PEAR_Downloader extends PEAR_Common
72414{
72415    /**
72416     * @var PEAR_Registry
72417     * @access private
72418     */
72419    var $_registry;
72420
72421    /**
72422     * Preferred Installation State (snapshot, devel, alpha, beta, stable)
72423     * @var string|null
72424     * @access private
72425     */
72426    var $_preferredState;
72427
72428    /**
72429     * Options from command-line passed to Install.
72430     *
72431     * Recognized options:<br />
72432     *  - onlyreqdeps   : install all required dependencies as well
72433     *  - alldeps       : install all dependencies, including optional
72434     *  - installroot   : base relative path to install files in
72435     *  - force         : force a download even if warnings would prevent it
72436     *  - nocompress    : download uncompressed tarballs
72437     * @see PEAR_Command_Install
72438     * @access private
72439     * @var array
72440     */
72441    var $_options;
72442
72443    /**
72444     * Downloaded Packages after a call to download().
72445     *
72446     * Format of each entry:
72447     *
72448     * <code>
72449     * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
72450     *    'info' => array() // parsed package.xml
72451     * );
72452     * </code>
72453     * @access private
72454     * @var array
72455     */
72456    var $_downloadedPackages = array();
72457
72458    /**
72459     * Packages slated for download.
72460     *
72461     * This is used to prevent downloading a package more than once should it be a dependency
72462     * for two packages to be installed.
72463     * Format of each entry:
72464     *
72465     * <pre>
72466     * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
72467     * );
72468     * </pre>
72469     * @access private
72470     * @var array
72471     */
72472    var $_toDownload = array();
72473
72474    /**
72475     * Array of every package installed, with names lower-cased.
72476     *
72477     * Format:
72478     * <code>
72479     * array('package1' => 0, 'package2' => 1, );
72480     * </code>
72481     * @var array
72482     */
72483    var $_installed = array();
72484
72485    /**
72486     * @var array
72487     * @access private
72488     */
72489    var $_errorStack = array();
72490
72491    /**
72492     * @var boolean
72493     * @access private
72494     */
72495    var $_internalDownload = false;
72496
72497    /**
72498     * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()}
72499     * @var array
72500     * @access private
72501     */
72502    var $_packageSortTree;
72503
72504    /**
72505     * Temporary directory, or configuration value where downloads will occur
72506     * @var string
72507     */
72508    var $_downloadDir;
72509
72510    /**
72511     * @param PEAR_Frontend_*
72512     * @param array
72513     * @param PEAR_Config
72514     */
72515    function PEAR_Downloader(&$ui, $options, &$config)
72516    {
72517        parent::PEAR_Common();
72518        $this->_options = $options;
72519        $this->config = &$config;
72520        $this->_preferredState = $this->config->get('preferred_state');
72521        $this->ui = &$ui;
72522        if (!$this->_preferredState) {
72523            // don't inadvertantly use a non-set preferred_state
72524            $this->_preferredState = null;
72525        }
72526
72527        if (isset($this->_options['installroot'])) {
72528            $this->config->setInstallRoot($this->_options['installroot']);
72529        }
72530        $this->_registry = &$config->getRegistry();
72531
72532        if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
72533            $this->_installed = $this->_registry->listAllPackages();
72534            foreach ($this->_installed as $key => $unused) {
72535                if (!count($unused)) {
72536                    continue;
72537                }
72538                $strtolower = create_function('$a','return strtolower($a);');
72539                array_walk($this->_installed[$key], $strtolower);
72540            }
72541        }
72542    }
72543
72544    /**
72545     * Attempt to discover a channel's remote capabilities from
72546     * its server name
72547     * @param string
72548     * @return boolean
72549     */
72550    function discover($channel)
72551    {
72552        $this->log(1, 'Attempting to discover channel "' . $channel . '"...');
72553        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
72554        $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
72555        if (!class_exists('System')) {
72556            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
72557        }
72558
72559        $tmpdir = $this->config->get('temp_dir');
72560        $tmp = System::mktemp('-d -t "' . $tmpdir . '"');
72561        $a   = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
72562        PEAR::popErrorHandling();
72563        if (PEAR::isError($a)) {
72564            // Attempt to fallback to https automatically.
72565            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
72566            $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...');
72567            $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
72568            PEAR::popErrorHandling();
72569            if (PEAR::isError($a)) {
72570                return false;
72571            }
72572        }
72573
72574        list($a, $lastmodified) = $a;
72575        if (!class_exists('PEAR_ChannelFile')) {
72576            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
72577        }
72578
72579        $b = new PEAR_ChannelFile;
72580        if ($b->fromXmlFile($a)) {
72581            unlink($a);
72582            if ($this->config->get('auto_discover')) {
72583                $this->_registry->addChannel($b, $lastmodified);
72584                $alias = $b->getName();
72585                if ($b->getName() == $this->_registry->channelName($b->getAlias())) {
72586                    $alias = $b->getAlias();
72587                }
72588
72589                $this->log(1, 'Auto-discovered channel "' . $channel .
72590                    '", alias "' . $alias . '", adding to registry');
72591            }
72592
72593            return true;
72594        }
72595
72596        unlink($a);
72597        return false;
72598    }
72599
72600    /**
72601     * For simpler unit-testing
72602     * @param PEAR_Downloader
72603     * @return PEAR_Downloader_Package
72604     */
72605    function &newDownloaderPackage(&$t)
72606    {
72607        if (!class_exists('PEAR_Downloader_Package')) {
72608            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader/Package.php';
72609        }
72610        $a = &new PEAR_Downloader_Package($t);
72611        return $a;
72612    }
72613
72614    /**
72615     * For simpler unit-testing
72616     * @param PEAR_Config
72617     * @param array
72618     * @param array
72619     * @param int
72620     */
72621    function &getDependency2Object(&$c, $i, $p, $s)
72622    {
72623        if (!class_exists('PEAR_Dependency2')) {
72624            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
72625        }
72626        $z = &new PEAR_Dependency2($c, $i, $p, $s);
72627        return $z;
72628    }
72629
72630    function &download($params)
72631    {
72632        if (!count($params)) {
72633            $a = array();
72634            return $a;
72635        }
72636
72637        if (!isset($this->_registry)) {
72638            $this->_registry = &$this->config->getRegistry();
72639        }
72640
72641        $channelschecked = array();
72642        // convert all parameters into PEAR_Downloader_Package objects
72643        foreach ($params as $i => $param) {
72644            $params[$i] = &$this->newDownloaderPackage($this);
72645            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
72646            $err = $params[$i]->initialize($param);
72647            PEAR::staticPopErrorHandling();
72648            if (!$err) {
72649                // skip parameters that were missed by preferred_state
72650                continue;
72651            }
72652
72653            if (PEAR::isError($err)) {
72654                if (!isset($this->_options['soft']) && $err->getMessage() !== '') {
72655                    $this->log(0, $err->getMessage());
72656                }
72657
72658                $params[$i] = false;
72659                if (is_object($param)) {
72660                    $param = $param->getChannel() . '/' . $param->getPackage();
72661                }
72662
72663                if (!isset($this->_options['soft'])) {
72664                    $this->log(2, 'Package "' . $param . '" is not valid');
72665                }
72666
72667                // Message logged above in a specific verbose mode, passing null to not show up on CLI
72668                $this->pushError(null, PEAR_INSTALLER_SKIPPED);
72669            } else {
72670                do {
72671                    if ($params[$i] && $params[$i]->getType() == 'local') {
72672                        // bug #7090 skip channel.xml check for local packages
72673                        break;
72674                    }
72675
72676                    if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) &&
72677                          !isset($this->_options['offline'])
72678                    ) {
72679                        $channelschecked[$params[$i]->getChannel()] = true;
72680                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
72681                        if (!class_exists('System')) {
72682                            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
72683                        }
72684
72685                        $curchannel = &$this->_registry->getChannel($params[$i]->getChannel());
72686                        if (PEAR::isError($curchannel)) {
72687                            PEAR::staticPopErrorHandling();
72688                            return $this->raiseError($curchannel);
72689                        }
72690
72691                        if (PEAR::isError($dir = $this->getDownloadDir())) {
72692                            PEAR::staticPopErrorHandling();
72693                            break;
72694                        }
72695
72696                        $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel());
72697                        $url    = 'http://' . $mirror . '/channel.xml';
72698                        $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified());
72699
72700                        PEAR::staticPopErrorHandling();
72701                        if (PEAR::isError($a) || !$a) {
72702                            // Attempt fallback to https automatically
72703                            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
72704                            $a = $this->downloadHttp('https://' . $mirror .
72705                                '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified());
72706
72707                            PEAR::staticPopErrorHandling();
72708                            if (PEAR::isError($a) || !$a) {
72709                                break;
72710                            }
72711                        }
72712                        $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' .
72713                            'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() .
72714                            '" to update');
72715                    }
72716                } while (false);
72717
72718                if ($params[$i] && !isset($this->_options['downloadonly'])) {
72719                    if (isset($this->_options['packagingroot'])) {
72720                        $checkdir = $this->_prependPath(
72721                            $this->config->get('php_dir', null, $params[$i]->getChannel()),
72722                            $this->_options['packagingroot']);
72723                    } else {
72724                        $checkdir = $this->config->get('php_dir',
72725                            null, $params[$i]->getChannel());
72726                    }
72727
72728                    while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) {
72729                        $checkdir = dirname($checkdir);
72730                    }
72731
72732                    if ($checkdir == '.') {
72733                        $checkdir = '/';
72734                    }
72735
72736                    if (!is_writeable($checkdir)) {
72737                        return PEAR::raiseError('Cannot install, php_dir for channel "' .
72738                            $params[$i]->getChannel() . '" is not writeable by the current user');
72739                    }
72740                }
72741            }
72742        }
72743
72744        unset($channelschecked);
72745        PEAR_Downloader_Package::removeDuplicates($params);
72746        if (!count($params)) {
72747            $a = array();
72748            return $a;
72749        }
72750
72751        if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) {
72752            $reverify = true;
72753            while ($reverify) {
72754                $reverify = false;
72755                foreach ($params as $i => $param) {
72756                    //PHP Bug 40768 / PEAR Bug #10944
72757                    //Nested foreaches fail in PHP 5.2.1
72758                    key($params);
72759                    $ret = $params[$i]->detectDependencies($params);
72760                    if (PEAR::isError($ret)) {
72761                        $reverify = true;
72762                        $params[$i] = false;
72763                        PEAR_Downloader_Package::removeDuplicates($params);
72764                        if (!isset($this->_options['soft'])) {
72765                            $this->log(0, $ret->getMessage());
72766                        }
72767                        continue 2;
72768                    }
72769                }
72770            }
72771        }
72772
72773        if (isset($this->_options['offline'])) {
72774            $this->log(3, 'Skipping dependency download check, --offline specified');
72775        }
72776
72777        if (!count($params)) {
72778            $a = array();
72779            return $a;
72780        }
72781
72782        while (PEAR_Downloader_Package::mergeDependencies($params));
72783        PEAR_Downloader_Package::removeDuplicates($params, true);
72784        $errorparams = array();
72785        if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) {
72786            if (count($errorparams)) {
72787                foreach ($errorparams as $param) {
72788                    $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage());
72789                    $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED);
72790                }
72791                $a = array();
72792                return $a;
72793            }
72794        }
72795
72796        PEAR_Downloader_Package::removeInstalled($params);
72797        if (!count($params)) {
72798            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
72799            $a = array();
72800            return $a;
72801        }
72802
72803        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
72804        $err = $this->analyzeDependencies($params);
72805        PEAR::popErrorHandling();
72806        if (!count($params)) {
72807            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
72808            $a = array();
72809            return $a;
72810        }
72811
72812        $ret = array();
72813        $newparams = array();
72814        if (isset($this->_options['pretend'])) {
72815            return $params;
72816        }
72817
72818        $somefailed = false;
72819        foreach ($params as $i => $package) {
72820            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
72821            $pf = &$params[$i]->download();
72822            PEAR::staticPopErrorHandling();
72823            if (PEAR::isError($pf)) {
72824                if (!isset($this->_options['soft'])) {
72825                    $this->log(1, $pf->getMessage());
72826                    $this->log(0, 'Error: cannot download "' .
72827                        $this->_registry->parsedPackageNameToString($package->getParsedPackage(),
72828                            true) .
72829                        '"');
72830                }
72831                $somefailed = true;
72832                continue;
72833            }
72834
72835            $newparams[] = &$params[$i];
72836            $ret[] = array(
72837                'file' => $pf->getArchiveFile(),
72838                'info' => &$pf,
72839                'pkg'  => $pf->getPackage()
72840            );
72841        }
72842
72843        if ($somefailed) {
72844            // remove params that did not download successfully
72845            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
72846            $err = $this->analyzeDependencies($newparams, true);
72847            PEAR::popErrorHandling();
72848            if (!count($newparams)) {
72849                $this->pushError('Download failed', PEAR_INSTALLER_FAILED);
72850                $a = array();
72851                return $a;
72852            }
72853        }
72854
72855        $this->_downloadedPackages = $ret;
72856        return $newparams;
72857    }
72858
72859    /**
72860     * @param array all packages to be installed
72861     */
72862    function analyzeDependencies(&$params, $force = false)
72863    {
72864        if (isset($this->_options['downloadonly'])) {
72865            return;
72866        }
72867
72868        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
72869        $redo  = true;
72870        $reset = $hasfailed = $failed = false;
72871        while ($redo) {
72872            $redo = false;
72873            foreach ($params as $i => $param) {
72874                $deps = $param->getDeps();
72875                if (!$deps) {
72876                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
72877                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
72878                    $send = $param->getPackageFile();
72879
72880                    $installcheck = $depchecker->validatePackage($send, $this, $params);
72881                    if (PEAR::isError($installcheck)) {
72882                        if (!isset($this->_options['soft'])) {
72883                            $this->log(0, $installcheck->getMessage());
72884                        }
72885                        $hasfailed  = true;
72886                        $params[$i] = false;
72887                        $reset      = true;
72888                        $redo       = true;
72889                        $failed     = false;
72890                        PEAR_Downloader_Package::removeDuplicates($params);
72891                        continue 2;
72892                    }
72893                    continue;
72894                }
72895
72896                if (!$reset && $param->alreadyValidated() && !$force) {
72897                    continue;
72898                }
72899
72900                if (count($deps)) {
72901                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
72902                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
72903                    $send = $param->getPackageFile();
72904                    if ($send === null) {
72905                        $send = $param->getDownloadURL();
72906                    }
72907
72908                    $installcheck = $depchecker->validatePackage($send, $this, $params);
72909                    if (PEAR::isError($installcheck)) {
72910                        if (!isset($this->_options['soft'])) {
72911                            $this->log(0, $installcheck->getMessage());
72912                        }
72913                        $hasfailed  = true;
72914                        $params[$i] = false;
72915                        $reset      = true;
72916                        $redo       = true;
72917                        $failed     = false;
72918                        PEAR_Downloader_Package::removeDuplicates($params);
72919                        continue 2;
72920                    }
72921
72922                    $failed = false;
72923                    if (isset($deps['required']) && is_array($deps['required'])) {
72924                        foreach ($deps['required'] as $type => $dep) {
72925                            // note: Dependency2 will never return a PEAR_Error if ignore-errors
72926                            // is specified, so soft is needed to turn off logging
72927                            if (!isset($dep[0])) {
72928                                if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep,
72929                                      true, $params))) {
72930                                    $failed = true;
72931                                    if (!isset($this->_options['soft'])) {
72932                                        $this->log(0, $e->getMessage());
72933                                    }
72934                                } elseif (is_array($e) && !$param->alreadyValidated()) {
72935                                    if (!isset($this->_options['soft'])) {
72936                                        $this->log(0, $e[0]);
72937                                    }
72938                                }
72939                            } else {
72940                                foreach ($dep as $d) {
72941                                    if (PEAR::isError($e =
72942                                          $depchecker->{"validate{$type}Dependency"}($d,
72943                                          true, $params))) {
72944                                        $failed = true;
72945                                        if (!isset($this->_options['soft'])) {
72946                                            $this->log(0, $e->getMessage());
72947                                        }
72948                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
72949                                        if (!isset($this->_options['soft'])) {
72950                                            $this->log(0, $e[0]);
72951                                        }
72952                                    }
72953                                }
72954                            }
72955                        }
72956
72957                        if (isset($deps['optional']) && is_array($deps['optional'])) {
72958                            foreach ($deps['optional'] as $type => $dep) {
72959                                if (!isset($dep[0])) {
72960                                    if (PEAR::isError($e =
72961                                          $depchecker->{"validate{$type}Dependency"}($dep,
72962                                          false, $params))) {
72963                                        $failed = true;
72964                                        if (!isset($this->_options['soft'])) {
72965                                            $this->log(0, $e->getMessage());
72966                                        }
72967                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
72968                                        if (!isset($this->_options['soft'])) {
72969                                            $this->log(0, $e[0]);
72970                                        }
72971                                    }
72972                                } else {
72973                                    foreach ($dep as $d) {
72974                                        if (PEAR::isError($e =
72975                                              $depchecker->{"validate{$type}Dependency"}($d,
72976                                              false, $params))) {
72977                                            $failed = true;
72978                                            if (!isset($this->_options['soft'])) {
72979                                                $this->log(0, $e->getMessage());
72980                                            }
72981                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
72982                                            if (!isset($this->_options['soft'])) {
72983                                                $this->log(0, $e[0]);
72984                                            }
72985                                        }
72986                                    }
72987                                }
72988                            }
72989                        }
72990
72991                        $groupname = $param->getGroup();
72992                        if (isset($deps['group']) && $groupname) {
72993                            if (!isset($deps['group'][0])) {
72994                                $deps['group'] = array($deps['group']);
72995                            }
72996
72997                            $found = false;
72998                            foreach ($deps['group'] as $group) {
72999                                if ($group['attribs']['name'] == $groupname) {
73000                                    $found = true;
73001                                    break;
73002                                }
73003                            }
73004
73005                            if ($found) {
73006                                unset($group['attribs']);
73007                                foreach ($group as $type => $dep) {
73008                                    if (!isset($dep[0])) {
73009                                        if (PEAR::isError($e =
73010                                              $depchecker->{"validate{$type}Dependency"}($dep,
73011                                              false, $params))) {
73012                                            $failed = true;
73013                                            if (!isset($this->_options['soft'])) {
73014                                                $this->log(0, $e->getMessage());
73015                                            }
73016                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
73017                                            if (!isset($this->_options['soft'])) {
73018                                                $this->log(0, $e[0]);
73019                                            }
73020                                        }
73021                                    } else {
73022                                        foreach ($dep as $d) {
73023                                            if (PEAR::isError($e =
73024                                                  $depchecker->{"validate{$type}Dependency"}($d,
73025                                                  false, $params))) {
73026                                                $failed = true;
73027                                                if (!isset($this->_options['soft'])) {
73028                                                    $this->log(0, $e->getMessage());
73029                                                }
73030                                            } elseif (is_array($e) && !$param->alreadyValidated()) {
73031                                                if (!isset($this->_options['soft'])) {
73032                                                    $this->log(0, $e[0]);
73033                                                }
73034                                            }
73035                                        }
73036                                    }
73037                                }
73038                            }
73039                        }
73040                    } else {
73041                        foreach ($deps as $dep) {
73042                            if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) {
73043                                $failed = true;
73044                                if (!isset($this->_options['soft'])) {
73045                                    $this->log(0, $e->getMessage());
73046                                }
73047                            } elseif (is_array($e) && !$param->alreadyValidated()) {
73048                                if (!isset($this->_options['soft'])) {
73049                                    $this->log(0, $e[0]);
73050                                }
73051                            }
73052                        }
73053                    }
73054                    $params[$i]->setValidated();
73055                }
73056
73057                if ($failed) {
73058                    $hasfailed  = true;
73059                    $params[$i] = false;
73060                    $reset      = true;
73061                    $redo       = true;
73062                    $failed     = false;
73063                    PEAR_Downloader_Package::removeDuplicates($params);
73064                    continue 2;
73065                }
73066            }
73067        }
73068
73069        PEAR::staticPopErrorHandling();
73070        if ($hasfailed && (isset($this->_options['ignore-errors']) ||
73071              isset($this->_options['nodeps']))) {
73072            // this is probably not needed, but just in case
73073            if (!isset($this->_options['soft'])) {
73074                $this->log(0, 'WARNING: dependencies failed');
73075            }
73076        }
73077    }
73078
73079    /**
73080     * Retrieve the directory that downloads will happen in
73081     * @access private
73082     * @return string
73083     */
73084    function getDownloadDir()
73085    {
73086        if (isset($this->_downloadDir)) {
73087            return $this->_downloadDir;
73088        }
73089
73090        $downloaddir = $this->config->get('download_dir');
73091        if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) {
73092            if  (is_dir($downloaddir) && !is_writable($downloaddir)) {
73093                $this->log(0, 'WARNING: configuration download directory "' . $downloaddir .
73094                    '" is not writeable.  Change download_dir config variable to ' .
73095                    'a writeable dir to avoid this warning');
73096            }
73097
73098            if (!class_exists('System')) {
73099                require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
73100            }
73101
73102            if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
73103                return $downloaddir;
73104            }
73105            $this->log(3, '+ tmp dir created at ' . $downloaddir);
73106        }
73107
73108        if (!is_writable($downloaddir)) {
73109            if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) ||
73110                  !is_writable($downloaddir)) {
73111                return PEAR::raiseError('download directory "' . $downloaddir .
73112                    '" is not writeable.  Change download_dir config variable to ' .
73113                    'a writeable dir');
73114            }
73115        }
73116
73117        return $this->_downloadDir = $downloaddir;
73118    }
73119
73120    function setDownloadDir($dir)
73121    {
73122        if (!@is_writable($dir)) {
73123            if (PEAR::isError(System::mkdir(array('-p', $dir)))) {
73124                return PEAR::raiseError('download directory "' . $dir .
73125                    '" is not writeable.  Change download_dir config variable to ' .
73126                    'a writeable dir');
73127            }
73128        }
73129        $this->_downloadDir = $dir;
73130    }
73131
73132    function configSet($key, $value, $layer = 'user', $channel = false)
73133    {
73134        $this->config->set($key, $value, $layer, $channel);
73135        $this->_preferredState = $this->config->get('preferred_state', null, $channel);
73136        if (!$this->_preferredState) {
73137            // don't inadvertantly use a non-set preferred_state
73138            $this->_preferredState = null;
73139        }
73140    }
73141
73142    function setOptions($options)
73143    {
73144        $this->_options = $options;
73145    }
73146
73147    function getOptions()
73148    {
73149        return $this->_options;
73150    }
73151
73152
73153    /**
73154     * @param array output of {@link parsePackageName()}
73155     * @access private
73156     */
73157    function _getPackageDownloadUrl($parr)
73158    {
73159        $curchannel = $this->config->get('default_channel');
73160        $this->configSet('default_channel', $parr['channel']);
73161        // getDownloadURL returns an array.  On error, it only contains information
73162        // on the latest release as array(version, info).  On success it contains
73163        // array(version, info, download url string)
73164        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
73165        if (!$this->_registry->channelExists($parr['channel'])) {
73166            do {
73167                if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) {
73168                    break;
73169                }
73170
73171                $this->configSet('default_channel', $curchannel);
73172                return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']);
73173            } while (false);
73174        }
73175
73176        $chan = &$this->_registry->getChannel($parr['channel']);
73177        if (PEAR::isError($chan)) {
73178            return $chan;
73179        }
73180
73181        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
73182        $version   = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']);
73183        $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']);
73184        // package is installed - use the installed release stability level
73185        if (!isset($parr['state']) && $stability !== null) {
73186            $state = $stability['release'];
73187        }
73188        PEAR::staticPopErrorHandling();
73189        $base2 = false;
73190
73191        $preferred_mirror = $this->config->get('preferred_mirror');
73192        if (!$chan->supportsREST($preferred_mirror) ||
73193              (
73194               !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror))
73195               &&
73196               !($base = $chan->getBaseURL('REST1.0', $preferred_mirror))
73197              )
73198        ) {
73199            return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
73200        }
73201
73202        if ($base2) {
73203            $rest = &$this->config->getREST('1.3', $this->_options);
73204            $base = $base2;
73205        } else {
73206            $rest = &$this->config->getREST('1.0', $this->_options);
73207        }
73208
73209        $downloadVersion = false;
73210        if (!isset($parr['version']) && !isset($parr['state']) && $version
73211              && !PEAR::isError($version)
73212              && !isset($this->_options['downloadonly'])
73213        ) {
73214            $downloadVersion = $version;
73215        }
73216
73217        $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName());
73218        if (PEAR::isError($url)) {
73219            $this->configSet('default_channel', $curchannel);
73220            return $url;
73221        }
73222
73223        if ($parr['channel'] != $curchannel) {
73224            $this->configSet('default_channel', $curchannel);
73225        }
73226
73227        if (!is_array($url)) {
73228            return $url;
73229        }
73230
73231        $url['raw'] = false; // no checking is necessary for REST
73232        if (!is_array($url['info'])) {
73233            return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
73234                'this should never happen');
73235        }
73236
73237        if (!isset($this->_options['force']) &&
73238              !isset($this->_options['downloadonly']) &&
73239              $version &&
73240              !PEAR::isError($version) &&
73241              !isset($parr['group'])
73242        ) {
73243            if (version_compare($version, $url['version'], '=')) {
73244                return PEAR::raiseError($this->_registry->parsedPackageNameToString(
73245                    $parr, true) . ' is already installed and is the same as the ' .
73246                    'released version ' . $url['version'], -976);
73247            }
73248
73249            if (version_compare($version, $url['version'], '>')) {
73250                return PEAR::raiseError($this->_registry->parsedPackageNameToString(
73251                    $parr, true) . ' is already installed and is newer than detected ' .
73252                    'released version ' . $url['version'], -976);
73253            }
73254        }
73255
73256        if (isset($url['info']['required']) || $url['compatible']) {
73257            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
73258            $pf = new PEAR_PackageFile_v2;
73259            $pf->setRawChannel($parr['channel']);
73260            if ($url['compatible']) {
73261                $pf->setRawCompatible($url['compatible']);
73262            }
73263        } else {
73264            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
73265            $pf = new PEAR_PackageFile_v1;
73266        }
73267
73268        $pf->setRawPackage($url['package']);
73269        $pf->setDeps($url['info']);
73270        if ($url['compatible']) {
73271            $pf->setCompatible($url['compatible']);
73272        }
73273
73274        $pf->setRawState($url['stability']);
73275        $url['info'] = &$pf;
73276        if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
73277            $ext = '.tar';
73278        } else {
73279            $ext = '.tgz';
73280        }
73281
73282        if (is_array($url) && isset($url['url'])) {
73283            $url['url'] .= $ext;
73284        }
73285
73286        return $url;
73287    }
73288
73289    /**
73290     * @param array dependency array
73291     * @access private
73292     */
73293    function _getDepPackageDownloadUrl($dep, $parr)
73294    {
73295        $xsdversion = isset($dep['rel']) ? '1.0' : '2.0';
73296        $curchannel = $this->config->get('default_channel');
73297        if (isset($dep['uri'])) {
73298            $xsdversion = '2.0';
73299            $chan = &$this->_registry->getChannel('__uri');
73300            if (PEAR::isError($chan)) {
73301                return $chan;
73302            }
73303
73304            $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri');
73305            $this->configSet('default_channel', '__uri');
73306        } else {
73307            if (isset($dep['channel'])) {
73308                $remotechannel = $dep['channel'];
73309            } else {
73310                $remotechannel = 'pear.php.net';
73311            }
73312
73313            if (!$this->_registry->channelExists($remotechannel)) {
73314                do {
73315                    if ($this->config->get('auto_discover')) {
73316                        if ($this->discover($remotechannel)) {
73317                            break;
73318                        }
73319                    }
73320                    return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
73321                } while (false);
73322            }
73323
73324            $chan = &$this->_registry->getChannel($remotechannel);
73325            if (PEAR::isError($chan)) {
73326                return $chan;
73327            }
73328
73329            $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel);
73330            $this->configSet('default_channel', $remotechannel);
73331        }
73332
73333        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
73334        if (isset($parr['state']) && isset($parr['version'])) {
73335            unset($parr['state']);
73336        }
73337
73338        if (isset($dep['uri'])) {
73339            $info = &$this->newDownloaderPackage($this);
73340            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
73341            $err = $info->initialize($dep);
73342            PEAR::staticPopErrorHandling();
73343            if (!$err) {
73344                // skip parameters that were missed by preferred_state
73345                return PEAR::raiseError('Cannot initialize dependency');
73346            }
73347
73348            if (PEAR::isError($err)) {
73349                if (!isset($this->_options['soft'])) {
73350                    $this->log(0, $err->getMessage());
73351                }
73352
73353                if (is_object($info)) {
73354                    $param = $info->getChannel() . '/' . $info->getPackage();
73355                }
73356                return PEAR::raiseError('Package "' . $param . '" is not valid');
73357            }
73358            return $info;
73359        } elseif ($chan->supportsREST($this->config->get('preferred_mirror'))
73360              &&
73361                (
73362                  ($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror')))
73363                    ||
73364                  ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror')))
73365                )
73366        ) {
73367            if ($base2) {
73368                $base = $base2;
73369                $rest = &$this->config->getREST('1.3', $this->_options);
73370            } else {
73371                $rest = &$this->config->getREST('1.0', $this->_options);
73372            }
73373
73374            $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr,
73375                    $state, $version, $chan->getName());
73376            if (PEAR::isError($url)) {
73377                return $url;
73378            }
73379
73380            if ($parr['channel'] != $curchannel) {
73381                $this->configSet('default_channel', $curchannel);
73382            }
73383
73384            if (!is_array($url)) {
73385                return $url;
73386            }
73387
73388            $url['raw'] = false; // no checking is necessary for REST
73389            if (!is_array($url['info'])) {
73390                return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
73391                    'this should never happen');
73392            }
73393
73394            if (isset($url['info']['required'])) {
73395                if (!class_exists('PEAR_PackageFile_v2')) {
73396                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
73397                }
73398                $pf = new PEAR_PackageFile_v2;
73399                $pf->setRawChannel($remotechannel);
73400            } else {
73401                if (!class_exists('PEAR_PackageFile_v1')) {
73402                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
73403                }
73404                $pf = new PEAR_PackageFile_v1;
73405
73406            }
73407            $pf->setRawPackage($url['package']);
73408            $pf->setDeps($url['info']);
73409            if ($url['compatible']) {
73410                $pf->setCompatible($url['compatible']);
73411            }
73412
73413            $pf->setRawState($url['stability']);
73414            $url['info'] = &$pf;
73415            if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
73416                $ext = '.tar';
73417            } else {
73418                $ext = '.tgz';
73419            }
73420
73421            if (is_array($url) && isset($url['url'])) {
73422                $url['url'] .= $ext;
73423            }
73424
73425            return $url;
73426        }
73427
73428        return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
73429    }
73430
73431    /**
73432     * @deprecated in favor of _getPackageDownloadUrl
73433     */
73434    function getPackageDownloadUrl($package, $version = null, $channel = false)
73435    {
73436        if ($version) {
73437            $package .= "-$version";
73438        }
73439        if ($this === null || $this->_registry === null) {
73440            $package = "http://pear.php.net/get/$package";
73441        } else {
73442            $chan = $this->_registry->getChannel($channel);
73443            if (PEAR::isError($chan)) {
73444                return '';
73445            }
73446            $package = "http://" . $chan->getServer() . "/get/$package";
73447        }
73448        if (!extension_loaded("zlib")) {
73449            $package .= '?uncompress=yes';
73450        }
73451        return $package;
73452    }
73453
73454    /**
73455     * Retrieve a list of downloaded packages after a call to {@link download()}.
73456     *
73457     * Also resets the list of downloaded packages.
73458     * @return array
73459     */
73460    function getDownloadedPackages()
73461    {
73462        $ret = $this->_downloadedPackages;
73463        $this->_downloadedPackages = array();
73464        $this->_toDownload = array();
73465        return $ret;
73466    }
73467
73468    function _downloadCallback($msg, $params = null)
73469    {
73470        switch ($msg) {
73471            case 'saveas':
73472                $this->log(1, "downloading $params ...");
73473                break;
73474            case 'done':
73475                $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
73476                break;
73477            case 'bytesread':
73478                static $bytes;
73479                if (empty($bytes)) {
73480                    $bytes = 0;
73481                }
73482                if (!($bytes % 10240)) {
73483                    $this->log(1, '.', false);
73484                }
73485                $bytes += $params;
73486                break;
73487            case 'start':
73488                if($params[1] == -1) {
73489                    $length = "Unknown size";
73490                } else {
73491                    $length = number_format($params[1], 0, '', ',')." bytes";
73492                }
73493                $this->log(1, "Starting to download {$params[0]} ($length)");
73494                break;
73495        }
73496        if (method_exists($this->ui, '_downloadCallback'))
73497            $this->ui->_downloadCallback($msg, $params);
73498    }
73499
73500    function _prependPath($path, $prepend)
73501    {
73502        if (strlen($prepend) > 0) {
73503            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
73504                if (preg_match('/^[a-z]:/i', $prepend)) {
73505                    $prepend = substr($prepend, 2);
73506                } elseif ($prepend{0} != '\\') {
73507                    $prepend = "\\$prepend";
73508                }
73509                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
73510            } else {
73511                $path = $prepend . $path;
73512            }
73513        }
73514        return $path;
73515    }
73516
73517    /**
73518     * @param string
73519     * @param integer
73520     */
73521    function pushError($errmsg, $code = -1)
73522    {
73523        array_push($this->_errorStack, array($errmsg, $code));
73524    }
73525
73526    function getErrorMsgs()
73527    {
73528        $msgs = array();
73529        $errs = $this->_errorStack;
73530        foreach ($errs as $err) {
73531            $msgs[] = $err[0];
73532        }
73533        $this->_errorStack = array();
73534        return $msgs;
73535    }
73536
73537    /**
73538     * for BC
73539     *
73540     * @deprecated
73541     */
73542    function sortPkgDeps(&$packages, $uninstall = false)
73543    {
73544        $uninstall ?
73545            $this->sortPackagesForUninstall($packages) :
73546            $this->sortPackagesForInstall($packages);
73547    }
73548
73549    /**
73550     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
73551     *
73552     * This uses the topological sort method from graph theory, and the
73553     * Structures_Graph package to properly sort dependencies for installation.
73554     * @param array an array of downloaded PEAR_Downloader_Packages
73555     * @return array array of array(packagefilename, package.xml contents)
73556     */
73557    function sortPackagesForInstall(&$packages)
73558    {
73559        require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph.php';
73560        require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Node.php';
73561        require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Manipulator/TopologicalSorter.php';
73562        $depgraph = new Structures_Graph(true);
73563        $nodes = array();
73564        $reg = &$this->config->getRegistry();
73565        foreach ($packages as $i => $package) {
73566            $pname = $reg->parsedPackageNameToString(
73567                array(
73568                    'channel' => $package->getChannel(),
73569                    'package' => strtolower($package->getPackage()),
73570                ));
73571            $nodes[$pname] = new Structures_Graph_Node;
73572            $nodes[$pname]->setData($packages[$i]);
73573            $depgraph->addNode($nodes[$pname]);
73574        }
73575
73576        $deplinks = array();
73577        foreach ($nodes as $package => $node) {
73578            $pf = &$node->getData();
73579            $pdeps = $pf->getDeps(true);
73580            if (!$pdeps) {
73581                continue;
73582            }
73583
73584            if ($pf->getPackagexmlVersion() == '1.0') {
73585                foreach ($pdeps as $dep) {
73586                    if ($dep['type'] != 'pkg' ||
73587                          (isset($dep['optional']) && $dep['optional'] == 'yes')) {
73588                        continue;
73589                    }
73590
73591                    $dname = $reg->parsedPackageNameToString(
73592                          array(
73593                              'channel' => 'pear.php.net',
73594                              'package' => strtolower($dep['name']),
73595                          ));
73596
73597                    if (isset($nodes[$dname])) {
73598                        if (!isset($deplinks[$dname])) {
73599                            $deplinks[$dname] = array();
73600                        }
73601
73602                        $deplinks[$dname][$package] = 1;
73603                        // dependency is in installed packages
73604                        continue;
73605                    }
73606
73607                    $dname = $reg->parsedPackageNameToString(
73608                          array(
73609                              'channel' => 'pecl.php.net',
73610                              'package' => strtolower($dep['name']),
73611                          ));
73612
73613                    if (isset($nodes[$dname])) {
73614                        if (!isset($deplinks[$dname])) {
73615                            $deplinks[$dname] = array();
73616                        }
73617
73618                        $deplinks[$dname][$package] = 1;
73619                        // dependency is in installed packages
73620                        continue;
73621                    }
73622                }
73623            } else {
73624                // the only ordering we care about is:
73625                // 1) subpackages must be installed before packages that depend on them
73626                // 2) required deps must be installed before packages that depend on them
73627                if (isset($pdeps['required']['subpackage'])) {
73628                    $t = $pdeps['required']['subpackage'];
73629                    if (!isset($t[0])) {
73630                        $t = array($t);
73631                    }
73632
73633                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
73634                }
73635
73636                if (isset($pdeps['group'])) {
73637                    if (!isset($pdeps['group'][0])) {
73638                        $pdeps['group'] = array($pdeps['group']);
73639                    }
73640
73641                    foreach ($pdeps['group'] as $group) {
73642                        if (isset($group['subpackage'])) {
73643                            $t = $group['subpackage'];
73644                            if (!isset($t[0])) {
73645                                $t = array($t);
73646                            }
73647
73648                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
73649                        }
73650                    }
73651                }
73652
73653                if (isset($pdeps['optional']['subpackage'])) {
73654                    $t = $pdeps['optional']['subpackage'];
73655                    if (!isset($t[0])) {
73656                        $t = array($t);
73657                    }
73658
73659                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
73660                }
73661
73662                if (isset($pdeps['required']['package'])) {
73663                    $t = $pdeps['required']['package'];
73664                    if (!isset($t[0])) {
73665                        $t = array($t);
73666                    }
73667
73668                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
73669                }
73670
73671                if (isset($pdeps['group'])) {
73672                    if (!isset($pdeps['group'][0])) {
73673                        $pdeps['group'] = array($pdeps['group']);
73674                    }
73675
73676                    foreach ($pdeps['group'] as $group) {
73677                        if (isset($group['package'])) {
73678                            $t = $group['package'];
73679                            if (!isset($t[0])) {
73680                                $t = array($t);
73681                            }
73682
73683                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
73684                        }
73685                    }
73686                }
73687            }
73688        }
73689
73690        $this->_detectDepCycle($deplinks);
73691        foreach ($deplinks as $dependent => $parents) {
73692            foreach ($parents as $parent => $unused) {
73693                $nodes[$dependent]->connectTo($nodes[$parent]);
73694            }
73695        }
73696
73697        $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph);
73698        $ret = array();
73699        for ($i = 0, $count = count($installOrder); $i < $count; $i++) {
73700            foreach ($installOrder[$i] as $index => $sortedpackage) {
73701                $data = &$installOrder[$i][$index]->getData();
73702                $ret[] = &$nodes[$reg->parsedPackageNameToString(
73703                          array(
73704                              'channel' => $data->getChannel(),
73705                              'package' => strtolower($data->getPackage()),
73706                          ))]->getData();
73707            }
73708        }
73709
73710        $packages = $ret;
73711        return;
73712    }
73713
73714    /**
73715     * Detect recursive links between dependencies and break the cycles
73716     *
73717     * @param array
73718     * @access private
73719     */
73720    function _detectDepCycle(&$deplinks)
73721    {
73722        do {
73723            $keepgoing = false;
73724            foreach ($deplinks as $dep => $parents) {
73725                foreach ($parents as $parent => $unused) {
73726                    // reset the parent cycle detector
73727                    $this->_testCycle(null, null, null);
73728                    if ($this->_testCycle($dep, $deplinks, $parent)) {
73729                        $keepgoing = true;
73730                        unset($deplinks[$dep][$parent]);
73731                        if (count($deplinks[$dep]) == 0) {
73732                            unset($deplinks[$dep]);
73733                        }
73734
73735                        continue 3;
73736                    }
73737                }
73738            }
73739        } while ($keepgoing);
73740    }
73741
73742    function _testCycle($test, $deplinks, $dep)
73743    {
73744        static $visited = array();
73745        if ($test === null) {
73746            $visited = array();
73747            return;
73748        }
73749
73750        // this happens when a parent has a dep cycle on another dependency
73751        // but the child is not part of the cycle
73752        if (isset($visited[$dep])) {
73753            return false;
73754        }
73755
73756        $visited[$dep] = 1;
73757        if ($test == $dep) {
73758            return true;
73759        }
73760
73761        if (isset($deplinks[$dep])) {
73762            if (in_array($test, array_keys($deplinks[$dep]), true)) {
73763                return true;
73764            }
73765
73766            foreach ($deplinks[$dep] as $parent => $unused) {
73767                if ($this->_testCycle($test, $deplinks, $parent)) {
73768                    return true;
73769                }
73770            }
73771        }
73772
73773        return false;
73774    }
73775
73776    /**
73777     * Set up the dependency for installation parsing
73778     *
73779     * @param array $t dependency information
73780     * @param PEAR_Registry $reg
73781     * @param array $deplinks list of dependency links already established
73782     * @param array $nodes all existing package nodes
73783     * @param string $package parent package name
73784     * @access private
73785     */
73786    function _setupGraph($t, $reg, &$deplinks, &$nodes, $package)
73787    {
73788        foreach ($t as $dep) {
73789            $depchannel = !isset($dep['channel']) ? '__uri': $dep['channel'];
73790            $dname = $reg->parsedPackageNameToString(
73791                  array(
73792                      'channel' => $depchannel,
73793                      'package' => strtolower($dep['name']),
73794                  ));
73795
73796            if (isset($nodes[$dname])) {
73797                if (!isset($deplinks[$dname])) {
73798                    $deplinks[$dname] = array();
73799                }
73800                $deplinks[$dname][$package] = 1;
73801            }
73802        }
73803    }
73804
73805    function _dependsOn($a, $b)
73806    {
73807        return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), $b);
73808    }
73809
73810    function _checkDepTree($channel, $package, $b, $checked = array())
73811    {
73812        $checked[$channel][$package] = true;
73813        if (!isset($this->_depTree[$channel][$package])) {
73814            return false;
73815        }
73816
73817        if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())]
73818              [strtolower($b->getPackage())])) {
73819            return true;
73820        }
73821
73822        foreach ($this->_depTree[$channel][$package] as $ch => $packages) {
73823            foreach ($packages as $pa => $true) {
73824                if ($this->_checkDepTree($ch, $pa, $b, $checked)) {
73825                    return true;
73826                }
73827            }
73828        }
73829
73830        return false;
73831    }
73832
73833    function _sortInstall($a, $b)
73834    {
73835        if (!$a->getDeps() && !$b->getDeps()) {
73836            return 0; // neither package has dependencies, order is insignificant
73837        }
73838        if ($a->getDeps() && !$b->getDeps()) {
73839            return 1; // $a must be installed after $b because $a has dependencies
73840        }
73841        if (!$a->getDeps() && $b->getDeps()) {
73842            return -1; // $b must be installed after $a because $b has dependencies
73843        }
73844        // both packages have dependencies
73845        if ($this->_dependsOn($a, $b)) {
73846            return 1;
73847        }
73848        if ($this->_dependsOn($b, $a)) {
73849            return -1;
73850        }
73851        return 0;
73852    }
73853
73854    /**
73855     * Download a file through HTTP.  Considers suggested file name in
73856     * Content-disposition: header and can run a callback function for
73857     * different events.  The callback will be called with two
73858     * parameters: the callback type, and parameters.  The implemented
73859     * callback types are:
73860     *
73861     *  'setup'       called at the very beginning, parameter is a UI object
73862     *                that should be used for all output
73863     *  'message'     the parameter is a string with an informational message
73864     *  'saveas'      may be used to save with a different file name, the
73865     *                parameter is the filename that is about to be used.
73866     *                If a 'saveas' callback returns a non-empty string,
73867     *                that file name will be used as the filename instead.
73868     *                Note that $save_dir will not be affected by this, only
73869     *                the basename of the file.
73870     *  'start'       download is starting, parameter is number of bytes
73871     *                that are expected, or -1 if unknown
73872     *  'bytesread'   parameter is the number of bytes read so far
73873     *  'done'        download is complete, parameter is the total number
73874     *                of bytes read
73875     *  'connfailed'  if the TCP/SSL connection fails, this callback is called
73876     *                with array(host,port,errno,errmsg)
73877     *  'writefailed' if writing to disk fails, this callback is called
73878     *                with array(destfile,errmsg)
73879     *
73880     * If an HTTP proxy has been configured (http_proxy PEAR_Config
73881     * setting), the proxy will be used.
73882     *
73883     * @param string  $url       the URL to download
73884     * @param object  $ui        PEAR_Frontend_* instance
73885     * @param object  $config    PEAR_Config instance
73886     * @param string  $save_dir  directory to save file in
73887     * @param mixed   $callback  function/method to call for status
73888     *                           updates
73889     * @param false|string|array $lastmodified header values to check against for caching
73890     *                           use false to return the header values from this download
73891     * @param false|array $accept Accept headers to send
73892     * @param false|string $channel Channel to use for retrieving authentication
73893     * @return string|array  Returns the full path of the downloaded file or a PEAR
73894     *                       error on failure.  If the error is caused by
73895     *                       socket-related errors, the error object will
73896     *                       have the fsockopen error code available through
73897     *                       getCode().  If caching is requested, then return the header
73898     *                       values.
73899     *
73900     * @access public
73901     */
73902    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null,
73903                          $accept = false, $channel = false)
73904    {
73905        static $redirect = 0;
73906        // always reset , so we are clean case of error
73907        $wasredirect = $redirect;
73908        $redirect = 0;
73909        if ($callback) {
73910            call_user_func($callback, 'setup', array(&$ui));
73911        }
73912
73913        $info = parse_url($url);
73914        if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
73915            return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
73916        }
73917
73918        if (!isset($info['host'])) {
73919            return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
73920        }
73921
73922        $host = isset($info['host']) ? $info['host'] : null;
73923        $port = isset($info['port']) ? $info['port'] : null;
73924        $path = isset($info['path']) ? $info['path'] : null;
73925
73926        if (isset($this)) {
73927            $config = &$this->config;
73928        } else {
73929            $config = &PEAR_Config::singleton();
73930        }
73931
73932        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
73933        if ($config->get('http_proxy') &&
73934              $proxy = parse_url($config->get('http_proxy'))) {
73935            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
73936            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
73937                $proxy_host = 'ssl://' . $proxy_host;
73938            }
73939            $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
73940            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
73941            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
73942
73943            if ($callback) {
73944                call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
73945            }
73946        }
73947
73948        if (empty($port)) {
73949            $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80;
73950        }
73951
73952        $scheme = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http';
73953
73954        if ($proxy_host != '') {
73955            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr);
73956            if (!$fp) {
73957                if ($callback) {
73958                    call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port,
73959                                                                  $errno, $errstr));
73960                }
73961                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno);
73962            }
73963
73964            if ($lastmodified === false || $lastmodified) {
73965                $request  = "GET $url HTTP/1.1\r\n";
73966                $request .= "Host: $host\r\n";
73967            } else {
73968                $request  = "GET $url HTTP/1.0\r\n";
73969                $request .= "Host: $host\r\n";
73970            }
73971        } else {
73972            $network_host = $host;
73973            if (isset($info['scheme']) && $info['scheme'] == 'https') {
73974                $network_host = 'ssl://' . $host;
73975            }
73976
73977            $fp = @fsockopen($network_host, $port, $errno, $errstr);
73978            if (!$fp) {
73979                if ($callback) {
73980                    call_user_func($callback, 'connfailed', array($host, $port,
73981                                                                  $errno, $errstr));
73982                }
73983                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
73984            }
73985
73986            if ($lastmodified === false || $lastmodified) {
73987                $request = "GET $path HTTP/1.1\r\n";
73988                $request .= "Host: $host\r\n";
73989            } else {
73990                $request = "GET $path HTTP/1.0\r\n";
73991                $request .= "Host: $host\r\n";
73992            }
73993        }
73994
73995        $ifmodifiedsince = '';
73996        if (is_array($lastmodified)) {
73997            if (isset($lastmodified['Last-Modified'])) {
73998                $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
73999            }
74000
74001            if (isset($lastmodified['ETag'])) {
74002                $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
74003            }
74004        } else {
74005            $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
74006        }
74007
74008        $request .= $ifmodifiedsince .
74009            "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n";
74010
74011        if (isset($this)) { // only pass in authentication for non-static calls
74012            $username = $config->get('username', null, $channel);
74013            $password = $config->get('password', null, $channel);
74014            if ($username && $password) {
74015                $tmp = base64_encode("$username:$password");
74016                $request .= "Authorization: Basic $tmp\r\n";
74017            }
74018        }
74019
74020        if ($proxy_host != '' && $proxy_user != '') {
74021            $request .= 'Proxy-Authorization: Basic ' .
74022                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
74023        }
74024
74025        if ($accept) {
74026            $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
74027        }
74028
74029        $request .= "Connection: close\r\n";
74030        $request .= "\r\n";
74031        fwrite($fp, $request);
74032        $headers = array();
74033        $reply = 0;
74034        while (trim($line = fgets($fp, 1024))) {
74035            if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
74036                $headers[strtolower($matches[1])] = trim($matches[2]);
74037            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
74038                $reply = (int)$matches[1];
74039                if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
74040                    return false;
74041                }
74042
74043                if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) {
74044                    return PEAR::raiseError("File $scheme://$host:$port$path not valid (received: $line)");
74045                }
74046            }
74047        }
74048
74049        if ($reply != 200) {
74050            if (!isset($headers['location'])) {
74051                return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirected but no location)");
74052            }
74053
74054            if ($wasredirect > 4) {
74055                return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirection looped more than 5 times)");
74056            }
74057
74058            $redirect = $wasredirect + 1;
74059            return $this->downloadHttp($headers['location'],
74060                    $ui, $save_dir, $callback, $lastmodified, $accept);
74061        }
74062
74063        if (isset($headers['content-disposition']) &&
74064            preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) {
74065            $save_as = basename($matches[1]);
74066        } else {
74067            $save_as = basename($url);
74068        }
74069
74070        if ($callback) {
74071            $tmp = call_user_func($callback, 'saveas', $save_as);
74072            if ($tmp) {
74073                $save_as = $tmp;
74074            }
74075        }
74076
74077        $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
74078        if (is_link($dest_file)) {
74079            return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $dest_file . ' as it is symlinked to ' . readlink($dest_file) . ' - Possible symlink attack');
74080        }
74081
74082        if (!$wp = @fopen($dest_file, 'wb')) {
74083            fclose($fp);
74084            if ($callback) {
74085                call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
74086            }
74087            return PEAR::raiseError("could not open $dest_file for writing");
74088        }
74089
74090        $length = isset($headers['content-length']) ? $headers['content-length'] : -1;
74091
74092        $bytes = 0;
74093        if ($callback) {
74094            call_user_func($callback, 'start', array(basename($dest_file), $length));
74095        }
74096
74097        while ($data = fread($fp, 1024)) {
74098            $bytes += strlen($data);
74099            if ($callback) {
74100                call_user_func($callback, 'bytesread', $bytes);
74101            }
74102            if (!@fwrite($wp, $data)) {
74103                fclose($fp);
74104                if ($callback) {
74105                    call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
74106                }
74107                return PEAR::raiseError("$dest_file: write failed ($php_errormsg)");
74108            }
74109        }
74110
74111        fclose($fp);
74112        fclose($wp);
74113        if ($callback) {
74114            call_user_func($callback, 'done', $bytes);
74115        }
74116
74117        if ($lastmodified === false || $lastmodified) {
74118            if (isset($headers['etag'])) {
74119                $lastmodified = array('ETag' => $headers['etag']);
74120            }
74121
74122            if (isset($headers['last-modified'])) {
74123                if (is_array($lastmodified)) {
74124                    $lastmodified['Last-Modified'] = $headers['last-modified'];
74125                } else {
74126                    $lastmodified = $headers['last-modified'];
74127                }
74128            }
74129            return array($dest_file, $lastmodified, $headers);
74130        }
74131        return $dest_file;
74132    }
74133}<?php
74134/**
74135 * PEAR_Downloader_Package
74136 *
74137 * PHP versions 4 and 5
74138 *
74139 * @category   pear
74140 * @package    PEAR
74141 * @author     Greg Beaver <cellog@php.net>
74142 * @copyright  1997-2009 The Authors
74143 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
74144 * @version    CVS: $Id: Package.php 313023 2011-07-06 19:17:11Z dufuz $
74145 * @link       http://pear.php.net/package/PEAR
74146 * @since      File available since Release 1.4.0a1
74147 */
74148
74149/**
74150 * Error code when parameter initialization fails because no releases
74151 * exist within preferred_state, but releases do exist
74152 */
74153define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003);
74154/**
74155 * Error code when parameter initialization fails because no releases
74156 * exist that will work with the existing PHP version
74157 */
74158define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004);
74159
74160/**
74161 * Coordinates download parameters and manages their dependencies
74162 * prior to downloading them.
74163 *
74164 * Input can come from three sources:
74165 *
74166 * - local files (archives or package.xml)
74167 * - remote files (downloadable urls)
74168 * - abstract package names
74169 *
74170 * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires
74171 * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the
74172 * format returned of dependencies is slightly different from that used in package.xml.
74173 *
74174 * This class hides the differences between these elements, and makes automatic
74175 * dependency resolution a piece of cake.  It also manages conflicts when
74176 * two classes depend on incompatible dependencies, or differing versions of the same
74177 * package dependency.  In addition, download will not be attempted if the php version is
74178 * not supported, PEAR installer version is not supported, or non-PECL extensions are not
74179 * installed.
74180 * @category   pear
74181 * @package    PEAR
74182 * @author     Greg Beaver <cellog@php.net>
74183 * @copyright  1997-2009 The Authors
74184 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
74185 * @version    Release: 1.9.4
74186 * @link       http://pear.php.net/package/PEAR
74187 * @since      Class available since Release 1.4.0a1
74188 */
74189class PEAR_Downloader_Package
74190{
74191    /**
74192     * @var PEAR_Downloader
74193     */
74194    var $_downloader;
74195    /**
74196     * @var PEAR_Config
74197     */
74198    var $_config;
74199    /**
74200     * @var PEAR_Registry
74201     */
74202    var $_registry;
74203    /**
74204     * Used to implement packagingroot properly
74205     * @var PEAR_Registry
74206     */
74207    var $_installRegistry;
74208    /**
74209     * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2
74210     */
74211    var $_packagefile;
74212    /**
74213     * @var array
74214     */
74215    var $_parsedname;
74216    /**
74217     * @var array
74218     */
74219    var $_downloadURL;
74220    /**
74221     * @var array
74222     */
74223    var $_downloadDeps = array();
74224    /**
74225     * @var boolean
74226     */
74227    var $_valid = false;
74228    /**
74229     * @var boolean
74230     */
74231    var $_analyzed = false;
74232    /**
74233     * if this or a parent package was invoked with Package-state, this is set to the
74234     * state variable.
74235     *
74236     * This allows temporary reassignment of preferred_state for a parent package and all of
74237     * its dependencies.
74238     * @var string|false
74239     */
74240    var $_explicitState = false;
74241    /**
74242     * If this package is invoked with Package#group, this variable will be true
74243     */
74244    var $_explicitGroup = false;
74245    /**
74246     * Package type local|url
74247     * @var string
74248     */
74249    var $_type;
74250    /**
74251     * Contents of package.xml, if downloaded from a remote channel
74252     * @var string|false
74253     * @access private
74254     */
74255    var $_rawpackagefile;
74256    /**
74257     * @var boolean
74258     * @access private
74259     */
74260    var $_validated = false;
74261
74262    /**
74263     * @param PEAR_Downloader
74264     */
74265    function PEAR_Downloader_Package(&$downloader)
74266    {
74267        $this->_downloader = &$downloader;
74268        $this->_config = &$this->_downloader->config;
74269        $this->_registry = &$this->_config->getRegistry();
74270        $options = $downloader->getOptions();
74271        if (isset($options['packagingroot'])) {
74272            $this->_config->setInstallRoot($options['packagingroot']);
74273            $this->_installRegistry = &$this->_config->getRegistry();
74274            $this->_config->setInstallRoot(false);
74275        } else {
74276            $this->_installRegistry = &$this->_registry;
74277        }
74278        $this->_valid = $this->_analyzed = false;
74279    }
74280
74281    /**
74282     * Parse the input and determine whether this is a local file, a remote uri, or an
74283     * abstract package name.
74284     *
74285     * This is the heart of the PEAR_Downloader_Package(), and is used in
74286     * {@link PEAR_Downloader::download()}
74287     * @param string
74288     * @return bool|PEAR_Error
74289     */
74290    function initialize($param)
74291    {
74292        $origErr = $this->_fromFile($param);
74293        if ($this->_valid) {
74294            return true;
74295        }
74296
74297        $options = $this->_downloader->getOptions();
74298        if (isset($options['offline'])) {
74299            if (PEAR::isError($origErr) && !isset($options['soft'])) {
74300                foreach ($origErr->getUserInfo() as $userInfo) {
74301                    if (isset($userInfo['message'])) {
74302                        $this->_downloader->log(0, $userInfo['message']);
74303                    }
74304                }
74305
74306                $this->_downloader->log(0, $origErr->getMessage());
74307            }
74308
74309            return PEAR::raiseError('Cannot download non-local package "' . $param . '"');
74310        }
74311
74312        $err = $this->_fromUrl($param);
74313        if (PEAR::isError($err) || !$this->_valid) {
74314            if ($this->_type == 'url') {
74315                if (PEAR::isError($err) && !isset($options['soft'])) {
74316                    $this->_downloader->log(0, $err->getMessage());
74317                }
74318
74319                return PEAR::raiseError("Invalid or missing remote package file");
74320            }
74321
74322            $err = $this->_fromString($param);
74323            if (PEAR::isError($err) || !$this->_valid) {
74324                if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) {
74325                    return false; // instruct the downloader to silently skip
74326                }
74327
74328                if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) {
74329                    if (is_array($origErr->getUserInfo())) {
74330                        foreach ($origErr->getUserInfo() as $err) {
74331                            if (is_array($err)) {
74332                                $err = $err['message'];
74333                            }
74334
74335                            if (!isset($options['soft'])) {
74336                                $this->_downloader->log(0, $err);
74337                            }
74338                        }
74339                    }
74340
74341                    if (!isset($options['soft'])) {
74342                        $this->_downloader->log(0, $origErr->getMessage());
74343                    }
74344
74345                    if (is_array($param)) {
74346                        $param = $this->_registry->parsedPackageNameToString($param, true);
74347                    }
74348
74349                    if (!isset($options['soft'])) {
74350                        $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
74351                    }
74352
74353                    // Passing no message back - already logged above
74354                    return PEAR::raiseError();
74355                }
74356
74357                if (PEAR::isError($err) && !isset($options['soft'])) {
74358                    $this->_downloader->log(0, $err->getMessage());
74359                }
74360
74361                if (is_array($param)) {
74362                    $param = $this->_registry->parsedPackageNameToString($param, true);
74363                }
74364
74365                if (!isset($options['soft'])) {
74366                    $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
74367                }
74368
74369                // Passing no message back - already logged above
74370                return PEAR::raiseError();
74371            }
74372        }
74373
74374        return true;
74375    }
74376
74377    /**
74378     * Retrieve any non-local packages
74379     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error
74380     */
74381    function &download()
74382    {
74383        if (isset($this->_packagefile)) {
74384            return $this->_packagefile;
74385        }
74386
74387        if (isset($this->_downloadURL['url'])) {
74388            $this->_isvalid = false;
74389            $info = $this->getParsedPackage();
74390            foreach ($info as $i => $p) {
74391                $info[$i] = strtolower($p);
74392            }
74393
74394            $err = $this->_fromUrl($this->_downloadURL['url'],
74395                $this->_registry->parsedPackageNameToString($this->_parsedname, true));
74396            $newinfo = $this->getParsedPackage();
74397            foreach ($newinfo as $i => $p) {
74398                $newinfo[$i] = strtolower($p);
74399            }
74400
74401            if ($info != $newinfo) {
74402                do {
74403                    if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') {
74404                        $info['channel'] = 'pear.php.net';
74405                        if ($info == $newinfo) {
74406                            // skip the channel check if a pecl package says it's a PEAR package
74407                            break;
74408                        }
74409                    }
74410                    if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') {
74411                        $info['channel'] = 'pecl.php.net';
74412                        if ($info == $newinfo) {
74413                            // skip the channel check if a pecl package says it's a PEAR package
74414                            break;
74415                        }
74416                    }
74417
74418                    return PEAR::raiseError('CRITICAL ERROR: We are ' .
74419                        $this->_registry->parsedPackageNameToString($info) . ', but the file ' .
74420                        'downloaded claims to be ' .
74421                        $this->_registry->parsedPackageNameToString($this->getParsedPackage()));
74422                } while (false);
74423            }
74424
74425            if (PEAR::isError($err) || !$this->_valid) {
74426                return $err;
74427            }
74428        }
74429
74430        $this->_type = 'local';
74431        return $this->_packagefile;
74432    }
74433
74434    function &getPackageFile()
74435    {
74436        return $this->_packagefile;
74437    }
74438
74439    function &getDownloader()
74440    {
74441        return $this->_downloader;
74442    }
74443
74444    function getType()
74445    {
74446        return $this->_type;
74447    }
74448
74449    /**
74450     * Like {@link initialize()}, but operates on a dependency
74451     */
74452    function fromDepURL($dep)
74453    {
74454        $this->_downloadURL = $dep;
74455        if (isset($dep['uri'])) {
74456            $options = $this->_downloader->getOptions();
74457            if (!extension_loaded("zlib") || isset($options['nocompress'])) {
74458                $ext = '.tar';
74459            } else {
74460                $ext = '.tgz';
74461            }
74462
74463            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
74464            $err = $this->_fromUrl($dep['uri'] . $ext);
74465            PEAR::popErrorHandling();
74466            if (PEAR::isError($err)) {
74467                if (!isset($options['soft'])) {
74468                    $this->_downloader->log(0, $err->getMessage());
74469                }
74470
74471                return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' .
74472                    'cannot download');
74473            }
74474        } else {
74475            $this->_parsedname =
74476                array(
74477                    'package' => $dep['info']->getPackage(),
74478                    'channel' => $dep['info']->getChannel(),
74479                    'version' => $dep['version']
74480                );
74481            if (!isset($dep['nodefault'])) {
74482                $this->_parsedname['group'] = 'default'; // download the default dependency group
74483                $this->_explicitGroup = false;
74484            }
74485
74486            $this->_rawpackagefile = $dep['raw'];
74487        }
74488    }
74489
74490    function detectDependencies($params)
74491    {
74492        $options = $this->_downloader->getOptions();
74493        if (isset($options['downloadonly'])) {
74494            return;
74495        }
74496
74497        if (isset($options['offline'])) {
74498            $this->_downloader->log(3, 'Skipping dependency download check, --offline specified');
74499            return;
74500        }
74501
74502        $pname = $this->getParsedPackage();
74503        if (!$pname) {
74504            return;
74505        }
74506
74507        $deps = $this->getDeps();
74508        if (!$deps) {
74509            return;
74510        }
74511
74512        if (isset($deps['required'])) { // package.xml 2.0
74513            return $this->_detect2($deps, $pname, $options, $params);
74514        }
74515
74516        return $this->_detect1($deps, $pname, $options, $params);
74517    }
74518
74519    function setValidated()
74520    {
74521        $this->_validated = true;
74522    }
74523
74524    function alreadyValidated()
74525    {
74526        return $this->_validated;
74527    }
74528
74529    /**
74530     * Remove packages to be downloaded that are already installed
74531     * @param array of PEAR_Downloader_Package objects
74532     * @static
74533     */
74534    function removeInstalled(&$params)
74535    {
74536        if (!isset($params[0])) {
74537            return;
74538        }
74539
74540        $options = $params[0]->_downloader->getOptions();
74541        if (!isset($options['downloadonly'])) {
74542            foreach ($params as $i => $param) {
74543                $package = $param->getPackage();
74544                $channel = $param->getChannel();
74545                // remove self if already installed with this version
74546                // this does not need any pecl magic - we only remove exact matches
74547                if ($param->_installRegistry->packageExists($package, $channel)) {
74548                    $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel);
74549                    if (version_compare($packageVersion, $param->getVersion(), '==')) {
74550                        if (!isset($options['force'])) {
74551                            $info = $param->getParsedPackage();
74552                            unset($info['version']);
74553                            unset($info['state']);
74554                            if (!isset($options['soft'])) {
74555                                $param->_downloader->log(1, 'Skipping package "' .
74556                                    $param->getShortName() .
74557                                    '", already installed as version ' . $packageVersion);
74558                            }
74559                            $params[$i] = false;
74560                        }
74561                    } elseif (!isset($options['force']) && !isset($options['upgrade']) &&
74562                          !isset($options['soft'])) {
74563                        $info = $param->getParsedPackage();
74564                        $param->_downloader->log(1, 'Skipping package "' .
74565                            $param->getShortName() .
74566                            '", already installed as version ' . $packageVersion);
74567                        $params[$i] = false;
74568                    }
74569                }
74570            }
74571        }
74572
74573        PEAR_Downloader_Package::removeDuplicates($params);
74574    }
74575
74576    function _detect2($deps, $pname, $options, $params)
74577    {
74578        $this->_downloadDeps = array();
74579        $groupnotfound = false;
74580        foreach (array('package', 'subpackage') as $packagetype) {
74581            // get required dependency group
74582            if (isset($deps['required'][$packagetype])) {
74583                if (isset($deps['required'][$packagetype][0])) {
74584                    foreach ($deps['required'][$packagetype] as $dep) {
74585                        if (isset($dep['conflicts'])) {
74586                            // skip any package that this package conflicts with
74587                            continue;
74588                        }
74589                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
74590                        if (is_array($ret)) {
74591                            $this->_downloadDeps[] = $ret;
74592                        } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
74593                            $this->_downloader->log(0, $ret->getMessage());
74594                        }
74595                    }
74596                } else {
74597                    $dep = $deps['required'][$packagetype];
74598                    if (!isset($dep['conflicts'])) {
74599                        // skip any package that this package conflicts with
74600                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
74601                        if (is_array($ret)) {
74602                            $this->_downloadDeps[] = $ret;
74603                        } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
74604                            $this->_downloader->log(0, $ret->getMessage());
74605                        }
74606                    }
74607                }
74608            }
74609
74610            // get optional dependency group, if any
74611            if (isset($deps['optional'][$packagetype])) {
74612                $skipnames = array();
74613                if (!isset($deps['optional'][$packagetype][0])) {
74614                    $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]);
74615                }
74616
74617                foreach ($deps['optional'][$packagetype] as $dep) {
74618                    $skip = false;
74619                    if (!isset($options['alldeps'])) {
74620                        $dep['package'] = $dep['name'];
74621                        if (!isset($options['soft'])) {
74622                            $this->_downloader->log(3, 'Notice: package "' .
74623                              $this->_registry->parsedPackageNameToString($this->getParsedPackage(),
74624                                    true) . '" optional dependency "' .
74625                                $this->_registry->parsedPackageNameToString(array('package' =>
74626                                    $dep['name'], 'channel' => 'pear.php.net'), true) .
74627                                '" will not be automatically downloaded');
74628                        }
74629                        $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true);
74630                        $skip = true;
74631                        unset($dep['package']);
74632                    }
74633
74634                    $ret = $this->_detect2Dep($dep, $pname, 'optional', $params);
74635                    if (PEAR::isError($ret) && !isset($options['soft'])) {
74636                        $this->_downloader->log(0, $ret->getMessage());
74637                    }
74638
74639                    if (!$ret) {
74640                        $dep['package'] = $dep['name'];
74641                        $skip = count($skipnames) ?
74642                            $skipnames[count($skipnames) - 1] : '';
74643                        if ($skip ==
74644                              $this->_registry->parsedPackageNameToString($dep, true)) {
74645                            array_pop($skipnames);
74646                        }
74647                    }
74648
74649                    if (!$skip && is_array($ret)) {
74650                        $this->_downloadDeps[] = $ret;
74651                    }
74652                }
74653
74654                if (count($skipnames)) {
74655                    if (!isset($options['soft'])) {
74656                        $this->_downloader->log(1, 'Did not download optional dependencies: ' .
74657                            implode(', ', $skipnames) .
74658                            ', use --alldeps to download automatically');
74659                    }
74660                }
74661            }
74662
74663            // get requested dependency group, if any
74664            $groupname = $this->getGroup();
74665            $explicit  = $this->_explicitGroup;
74666            if (!$groupname) {
74667                if (!$this->canDefault()) {
74668                    continue;
74669                }
74670
74671                $groupname = 'default'; // try the default dependency group
74672            }
74673
74674            if ($groupnotfound) {
74675                continue;
74676            }
74677
74678            if (isset($deps['group'])) {
74679                if (isset($deps['group']['attribs'])) {
74680                    if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) {
74681                        $group = $deps['group'];
74682                    } elseif ($explicit) {
74683                        if (!isset($options['soft'])) {
74684                            $this->_downloader->log(0, 'Warning: package "' .
74685                                $this->_registry->parsedPackageNameToString($pname, true) .
74686                                '" has no dependency ' . 'group named "' . $groupname . '"');
74687                        }
74688
74689                        $groupnotfound = true;
74690                        continue;
74691                    }
74692                } else {
74693                    $found = false;
74694                    foreach ($deps['group'] as $group) {
74695                        if (strtolower($group['attribs']['name']) == strtolower($groupname)) {
74696                            $found = true;
74697                            break;
74698                        }
74699                    }
74700
74701                    if (!$found) {
74702                        if ($explicit) {
74703                            if (!isset($options['soft'])) {
74704                                $this->_downloader->log(0, 'Warning: package "' .
74705                                    $this->_registry->parsedPackageNameToString($pname, true) .
74706                                    '" has no dependency ' . 'group named "' . $groupname . '"');
74707                            }
74708                        }
74709
74710                        $groupnotfound = true;
74711                        continue;
74712                    }
74713                }
74714            }
74715
74716            if (isset($group) && isset($group[$packagetype])) {
74717                if (isset($group[$packagetype][0])) {
74718                    foreach ($group[$packagetype] as $dep) {
74719                        $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' .
74720                            $group['attribs']['name'] . '"', $params);
74721                        if (is_array($ret)) {
74722                            $this->_downloadDeps[] = $ret;
74723                        } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
74724                            $this->_downloader->log(0, $ret->getMessage());
74725                        }
74726                    }
74727                } else {
74728                    $ret = $this->_detect2Dep($group[$packagetype], $pname,
74729                        'dependency group "' .
74730                        $group['attribs']['name'] . '"', $params);
74731                    if (is_array($ret)) {
74732                        $this->_downloadDeps[] = $ret;
74733                    } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
74734                        $this->_downloader->log(0, $ret->getMessage());
74735                    }
74736                }
74737            }
74738        }
74739    }
74740
74741    function _detect2Dep($dep, $pname, $group, $params)
74742    {
74743        if (isset($dep['conflicts'])) {
74744            return true;
74745        }
74746
74747        $options = $this->_downloader->getOptions();
74748        if (isset($dep['uri'])) {
74749            return array('uri' => $dep['uri'], 'dep' => $dep);;
74750        }
74751
74752        $testdep = $dep;
74753        $testdep['package'] = $dep['name'];
74754        if (PEAR_Downloader_Package::willDownload($testdep, $params)) {
74755            $dep['package'] = $dep['name'];
74756            if (!isset($options['soft'])) {
74757                $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group .
74758                    ' dependency "' .
74759                    $this->_registry->parsedPackageNameToString($dep, true) .
74760                    '", will be installed');
74761            }
74762            return false;
74763        }
74764
74765        $options = $this->_downloader->getOptions();
74766        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
74767        if ($this->_explicitState) {
74768            $pname['state'] = $this->_explicitState;
74769        }
74770
74771        $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
74772        if (PEAR::isError($url)) {
74773            PEAR::popErrorHandling();
74774            return $url;
74775        }
74776
74777        $dep['package'] = $dep['name'];
74778        $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' &&
74779            !isset($options['alldeps']), true);
74780        PEAR::popErrorHandling();
74781        if (PEAR::isError($ret)) {
74782            if (!isset($options['soft'])) {
74783                $this->_downloader->log(0, $ret->getMessage());
74784            }
74785
74786            return false;
74787        }
74788
74789        // check to see if a dep is already installed and is the same or newer
74790        if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) {
74791            $oper = 'has';
74792        } else {
74793            $oper = 'gt';
74794        }
74795
74796        // do not try to move this before getDepPackageDownloadURL
74797        // we can't determine whether upgrade is necessary until we know what
74798        // version would be downloaded
74799        if (!isset($options['force']) && $this->isInstalled($ret, $oper)) {
74800            $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']);
74801            $dep['package'] = $dep['name'];
74802            if (!isset($options['soft'])) {
74803                $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
74804                    ' dependency "' .
74805                $this->_registry->parsedPackageNameToString($dep, true) .
74806                    '" version ' . $url['version'] . ', already installed as version ' .
74807                    $version);
74808            }
74809
74810            return false;
74811        }
74812
74813        if (isset($dep['nodefault'])) {
74814            $ret['nodefault'] = true;
74815        }
74816
74817        return $ret;
74818    }
74819
74820    function _detect1($deps, $pname, $options, $params)
74821    {
74822        $this->_downloadDeps = array();
74823        $skipnames = array();
74824        foreach ($deps as $dep) {
74825            $nodownload = false;
74826            if (isset ($dep['type']) && $dep['type'] === 'pkg') {
74827                $dep['channel'] = 'pear.php.net';
74828                $dep['package'] = $dep['name'];
74829                switch ($dep['rel']) {
74830                    case 'not' :
74831                        continue 2;
74832                    case 'ge' :
74833                    case 'eq' :
74834                    case 'gt' :
74835                    case 'has' :
74836                        $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
74837                            'required' :
74838                            'optional';
74839                        if (PEAR_Downloader_Package::willDownload($dep, $params)) {
74840                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
74841                                . ' dependency "' .
74842                                $this->_registry->parsedPackageNameToString($dep, true) .
74843                                '", will be installed');
74844                            continue 2;
74845                        }
74846                        $fakedp = new PEAR_PackageFile_v1;
74847                        $fakedp->setPackage($dep['name']);
74848                        // skip internet check if we are not upgrading (bug #5810)
74849                        if (!isset($options['upgrade']) && $this->isInstalled(
74850                              $fakedp, $dep['rel'])) {
74851                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
74852                                . ' dependency "' .
74853                                $this->_registry->parsedPackageNameToString($dep, true) .
74854                                '", is already installed');
74855                            continue 2;
74856                        }
74857                }
74858
74859                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
74860                if ($this->_explicitState) {
74861                    $pname['state'] = $this->_explicitState;
74862                }
74863
74864                $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
74865                $chan = 'pear.php.net';
74866                if (PEAR::isError($url)) {
74867                    // check to see if this is a pecl package that has jumped
74868                    // from pear.php.net to pecl.php.net channel
74869                    if (!class_exists('PEAR_Dependency2')) {
74870                        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
74871                    }
74872
74873                    $newdep = PEAR_Dependency2::normalizeDep($dep);
74874                    $newdep = $newdep[0];
74875                    $newdep['channel'] = 'pecl.php.net';
74876                    $chan = 'pecl.php.net';
74877                    $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname);
74878                    $obj = &$this->_installRegistry->getPackage($dep['name']);
74879                    if (PEAR::isError($url)) {
74880                        PEAR::popErrorHandling();
74881                        if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) {
74882                            $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
74883                                'required' :
74884                                'optional';
74885                            $dep['package'] = $dep['name'];
74886                            if (!isset($options['soft'])) {
74887                                $this->_downloader->log(3, $this->getShortName() .
74888                                    ': Skipping ' . $group . ' dependency "' .
74889                                    $this->_registry->parsedPackageNameToString($dep, true) .
74890                                    '", already installed as version ' . $obj->getVersion());
74891                            }
74892                            $skip = count($skipnames) ?
74893                                $skipnames[count($skipnames) - 1] : '';
74894                            if ($skip ==
74895                                  $this->_registry->parsedPackageNameToString($dep, true)) {
74896                                array_pop($skipnames);
74897                            }
74898                            continue;
74899                        } else {
74900                            if (isset($dep['optional']) && $dep['optional'] == 'yes') {
74901                                $this->_downloader->log(2, $this->getShortName() .
74902                                    ': Skipping optional dependency "' .
74903                                    $this->_registry->parsedPackageNameToString($dep, true) .
74904                                    '", no releases exist');
74905                                continue;
74906                            } else {
74907                                return $url;
74908                            }
74909                        }
74910                    }
74911                }
74912
74913                PEAR::popErrorHandling();
74914                if (!isset($options['alldeps'])) {
74915                    if (isset($dep['optional']) && $dep['optional'] == 'yes') {
74916                        if (!isset($options['soft'])) {
74917                            $this->_downloader->log(3, 'Notice: package "' .
74918                                $this->getShortName() .
74919                                '" optional dependency "' .
74920                                $this->_registry->parsedPackageNameToString(
74921                                    array('channel' => $chan, 'package' =>
74922                                    $dep['name']), true) .
74923                                '" will not be automatically downloaded');
74924                        }
74925                        $skipnames[] = $this->_registry->parsedPackageNameToString(
74926                                array('channel' => $chan, 'package' =>
74927                                $dep['name']), true);
74928                        $nodownload = true;
74929                    }
74930                }
74931
74932                if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) {
74933                    if (!isset($dep['optional']) || $dep['optional'] == 'no') {
74934                        if (!isset($options['soft'])) {
74935                            $this->_downloader->log(3, 'Notice: package "' .
74936                                $this->getShortName() .
74937                                '" required dependency "' .
74938                                $this->_registry->parsedPackageNameToString(
74939                                    array('channel' => $chan, 'package' =>
74940                                    $dep['name']), true) .
74941                                '" will not be automatically downloaded');
74942                        }
74943                        $skipnames[] = $this->_registry->parsedPackageNameToString(
74944                                array('channel' => $chan, 'package' =>
74945                                $dep['name']), true);
74946                        $nodownload = true;
74947                    }
74948                }
74949
74950                // check to see if a dep is already installed
74951                // do not try to move this before getDepPackageDownloadURL
74952                // we can't determine whether upgrade is necessary until we know what
74953                // version would be downloaded
74954                if (!isset($options['force']) && $this->isInstalled(
74955                        $url, $dep['rel'])) {
74956                    $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
74957                        'required' :
74958                        'optional';
74959                    $dep['package'] = $dep['name'];
74960                    if (isset($newdep)) {
74961                        $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']);
74962                    } else {
74963                        $version = $this->_installRegistry->packageInfo($dep['name'], 'version');
74964                    }
74965
74966                    $dep['version'] = $url['version'];
74967                    if (!isset($options['soft'])) {
74968                        $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
74969                            ' dependency "' .
74970                            $this->_registry->parsedPackageNameToString($dep, true) .
74971                            '", already installed as version ' . $version);
74972                    }
74973
74974                    $skip = count($skipnames) ?
74975                        $skipnames[count($skipnames) - 1] : '';
74976                    if ($skip ==
74977                          $this->_registry->parsedPackageNameToString($dep, true)) {
74978                        array_pop($skipnames);
74979                    }
74980
74981                    continue;
74982                }
74983
74984                if ($nodownload) {
74985                    continue;
74986                }
74987
74988                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
74989                if (isset($newdep)) {
74990                    $dep = $newdep;
74991                }
74992
74993                $dep['package'] = $dep['name'];
74994                $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params,
74995                    isset($dep['optional']) && $dep['optional'] == 'yes' &&
74996                    !isset($options['alldeps']), true);
74997                PEAR::popErrorHandling();
74998                if (PEAR::isError($ret)) {
74999                    if (!isset($options['soft'])) {
75000                        $this->_downloader->log(0, $ret->getMessage());
75001                    }
75002                    continue;
75003                }
75004
75005                $this->_downloadDeps[] = $ret;
75006            }
75007        }
75008
75009        if (count($skipnames)) {
75010            if (!isset($options['soft'])) {
75011                $this->_downloader->log(1, 'Did not download dependencies: ' .
75012                    implode(', ', $skipnames) .
75013                    ', use --alldeps or --onlyreqdeps to download automatically');
75014            }
75015        }
75016    }
75017
75018    function setDownloadURL($pkg)
75019    {
75020        $this->_downloadURL = $pkg;
75021    }
75022
75023    /**
75024     * Set the package.xml object for this downloaded package
75025     *
75026     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg
75027     */
75028    function setPackageFile(&$pkg)
75029    {
75030        $this->_packagefile = &$pkg;
75031    }
75032
75033    function getShortName()
75034    {
75035        return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(),
75036            'package' => $this->getPackage()), true);
75037    }
75038
75039    function getParsedPackage()
75040    {
75041        if (isset($this->_packagefile) || isset($this->_parsedname)) {
75042            return array('channel' => $this->getChannel(),
75043                'package' => $this->getPackage(),
75044                'version' => $this->getVersion());
75045        }
75046
75047        return false;
75048    }
75049
75050    function getDownloadURL()
75051    {
75052        return $this->_downloadURL;
75053    }
75054
75055    function canDefault()
75056    {
75057        if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) {
75058            return false;
75059        }
75060
75061        return true;
75062    }
75063
75064    function getPackage()
75065    {
75066        if (isset($this->_packagefile)) {
75067            return $this->_packagefile->getPackage();
75068        } elseif (isset($this->_downloadURL['info'])) {
75069            return $this->_downloadURL['info']->getPackage();
75070        }
75071
75072        return false;
75073    }
75074
75075    /**
75076     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
75077     */
75078    function isSubpackage(&$pf)
75079    {
75080        if (isset($this->_packagefile)) {
75081            return $this->_packagefile->isSubpackage($pf);
75082        } elseif (isset($this->_downloadURL['info'])) {
75083            return $this->_downloadURL['info']->isSubpackage($pf);
75084        }
75085
75086        return false;
75087    }
75088
75089    function getPackageType()
75090    {
75091        if (isset($this->_packagefile)) {
75092            return $this->_packagefile->getPackageType();
75093        } elseif (isset($this->_downloadURL['info'])) {
75094            return $this->_downloadURL['info']->getPackageType();
75095        }
75096
75097        return false;
75098    }
75099
75100    function isBundle()
75101    {
75102        if (isset($this->_packagefile)) {
75103            return $this->_packagefile->getPackageType() == 'bundle';
75104        }
75105
75106        return false;
75107    }
75108
75109    function getPackageXmlVersion()
75110    {
75111        if (isset($this->_packagefile)) {
75112            return $this->_packagefile->getPackagexmlVersion();
75113        } elseif (isset($this->_downloadURL['info'])) {
75114            return $this->_downloadURL['info']->getPackagexmlVersion();
75115        }
75116
75117        return '1.0';
75118    }
75119
75120    function getChannel()
75121    {
75122        if (isset($this->_packagefile)) {
75123            return $this->_packagefile->getChannel();
75124        } elseif (isset($this->_downloadURL['info'])) {
75125            return $this->_downloadURL['info']->getChannel();
75126        }
75127
75128        return false;
75129    }
75130
75131    function getURI()
75132    {
75133        if (isset($this->_packagefile)) {
75134            return $this->_packagefile->getURI();
75135        } elseif (isset($this->_downloadURL['info'])) {
75136            return $this->_downloadURL['info']->getURI();
75137        }
75138
75139        return false;
75140    }
75141
75142    function getVersion()
75143    {
75144        if (isset($this->_packagefile)) {
75145            return $this->_packagefile->getVersion();
75146        } elseif (isset($this->_downloadURL['version'])) {
75147            return $this->_downloadURL['version'];
75148        }
75149
75150        return false;
75151    }
75152
75153    function isCompatible($pf)
75154    {
75155        if (isset($this->_packagefile)) {
75156            return $this->_packagefile->isCompatible($pf);
75157        } elseif (isset($this->_downloadURL['info'])) {
75158            return $this->_downloadURL['info']->isCompatible($pf);
75159        }
75160
75161        return true;
75162    }
75163
75164    function setGroup($group)
75165    {
75166        $this->_parsedname['group'] = $group;
75167    }
75168
75169    function getGroup()
75170    {
75171        if (isset($this->_parsedname['group'])) {
75172            return $this->_parsedname['group'];
75173        }
75174
75175        return '';
75176    }
75177
75178    function isExtension($name)
75179    {
75180        if (isset($this->_packagefile)) {
75181            return $this->_packagefile->isExtension($name);
75182        } elseif (isset($this->_downloadURL['info'])) {
75183            if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') {
75184                return $this->_downloadURL['info']->getProvidesExtension() == $name;
75185            }
75186
75187            return false;
75188        }
75189
75190        return false;
75191    }
75192
75193    function getDeps()
75194    {
75195        if (isset($this->_packagefile)) {
75196            $ver = $this->_packagefile->getPackagexmlVersion();
75197            if (version_compare($ver, '2.0', '>=')) {
75198                return $this->_packagefile->getDeps(true);
75199            }
75200
75201            return $this->_packagefile->getDeps();
75202        } elseif (isset($this->_downloadURL['info'])) {
75203            $ver = $this->_downloadURL['info']->getPackagexmlVersion();
75204            if (version_compare($ver, '2.0', '>=')) {
75205                return $this->_downloadURL['info']->getDeps(true);
75206            }
75207
75208            return $this->_downloadURL['info']->getDeps();
75209        }
75210
75211        return array();
75212    }
75213
75214    /**
75215     * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency
75216     *                     returned from getDepDownloadURL()
75217     */
75218    function isEqual($param)
75219    {
75220        if (is_object($param)) {
75221            $channel = $param->getChannel();
75222            $package = $param->getPackage();
75223            if ($param->getURI()) {
75224                $param = array(
75225                    'channel' => $param->getChannel(),
75226                    'package' => $param->getPackage(),
75227                    'version' => $param->getVersion(),
75228                    'uri' => $param->getURI(),
75229                );
75230            } else {
75231                $param = array(
75232                    'channel' => $param->getChannel(),
75233                    'package' => $param->getPackage(),
75234                    'version' => $param->getVersion(),
75235                );
75236            }
75237        } else {
75238            if (isset($param['uri'])) {
75239                if ($this->getChannel() != '__uri') {
75240                    return false;
75241                }
75242                return $param['uri'] == $this->getURI();
75243            }
75244
75245            $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage();
75246            $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel();
75247            if (isset($param['rel'])) {
75248                if (!class_exists('PEAR_Dependency2')) {
75249                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
75250                }
75251
75252                $newdep = PEAR_Dependency2::normalizeDep($param);
75253                $newdep = $newdep[0];
75254            } elseif (isset($param['min'])) {
75255                $newdep = $param;
75256            }
75257        }
75258
75259        if (isset($newdep)) {
75260            if (!isset($newdep['min'])) {
75261                $newdep['min'] = '0';
75262            }
75263
75264            if (!isset($newdep['max'])) {
75265                $newdep['max'] = '100000000000000000000';
75266            }
75267
75268            // use magic to support pecl packages suddenly jumping to the pecl channel
75269            // we need to support both dependency possibilities
75270            if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') {
75271                if ($package == $this->getPackage()) {
75272                    $channel = 'pecl.php.net';
75273                }
75274            }
75275            if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
75276                if ($package == $this->getPackage()) {
75277                    $channel = 'pear.php.net';
75278                }
75279            }
75280
75281            return (strtolower($package) == strtolower($this->getPackage()) &&
75282                $channel == $this->getChannel() &&
75283                version_compare($newdep['min'], $this->getVersion(), '<=') &&
75284                version_compare($newdep['max'], $this->getVersion(), '>='));
75285        }
75286
75287        // use magic to support pecl packages suddenly jumping to the pecl channel
75288        if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
75289            if (strtolower($package) == strtolower($this->getPackage())) {
75290                $channel = 'pear.php.net';
75291            }
75292        }
75293
75294        if (isset($param['version'])) {
75295            return (strtolower($package) == strtolower($this->getPackage()) &&
75296                $channel == $this->getChannel() &&
75297                $param['version'] == $this->getVersion());
75298        }
75299
75300        return strtolower($package) == strtolower($this->getPackage()) &&
75301            $channel == $this->getChannel();
75302    }
75303
75304    function isInstalled($dep, $oper = '==')
75305    {
75306        if (!$dep) {
75307            return false;
75308        }
75309
75310        if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') {
75311            return false;
75312        }
75313
75314        if (is_object($dep)) {
75315            $package = $dep->getPackage();
75316            $channel = $dep->getChannel();
75317            if ($dep->getURI()) {
75318                $dep = array(
75319                    'uri' => $dep->getURI(),
75320                    'version' => $dep->getVersion(),
75321                );
75322            } else {
75323                $dep = array(
75324                    'version' => $dep->getVersion(),
75325                );
75326            }
75327        } else {
75328            if (isset($dep['uri'])) {
75329                $channel = '__uri';
75330                $package = $dep['dep']['name'];
75331            } else {
75332                $channel = $dep['info']->getChannel();
75333                $package = $dep['info']->getPackage();
75334            }
75335        }
75336
75337        $options = $this->_downloader->getOptions();
75338        $test    = $this->_installRegistry->packageExists($package, $channel);
75339        if (!$test && $channel == 'pecl.php.net') {
75340            // do magic to allow upgrading from old pecl packages to new ones
75341            $test = $this->_installRegistry->packageExists($package, 'pear.php.net');
75342            $channel = 'pear.php.net';
75343        }
75344
75345        if ($test) {
75346            if (isset($dep['uri'])) {
75347                if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) {
75348                    return true;
75349                }
75350            }
75351
75352            if (isset($options['upgrade'])) {
75353                $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel);
75354                if (version_compare($packageVersion, $dep['version'], '>=')) {
75355                    return true;
75356                }
75357
75358                return false;
75359            }
75360
75361            return true;
75362        }
75363
75364        return false;
75365    }
75366
75367    /**
75368     * Detect duplicate package names with differing versions
75369     *
75370     * If a user requests to install Date 1.4.6 and Date 1.4.7,
75371     * for instance, this is a logic error.  This method
75372     * detects this situation.
75373     *
75374     * @param array $params array of PEAR_Downloader_Package objects
75375     * @param array $errorparams empty array
75376     * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts
75377     */
75378    function detectStupidDuplicates($params, &$errorparams)
75379    {
75380        $existing = array();
75381        foreach ($params as $i => $param) {
75382            $package = $param->getPackage();
75383            $channel = $param->getChannel();
75384            $group   = $param->getGroup();
75385            if (!isset($existing[$channel . '/' . $package])) {
75386                $existing[$channel . '/' . $package] = array();
75387            }
75388
75389            if (!isset($existing[$channel . '/' . $package][$group])) {
75390                $existing[$channel . '/' . $package][$group] = array();
75391            }
75392
75393            $existing[$channel . '/' . $package][$group][] = $i;
75394        }
75395
75396        $indices = array();
75397        foreach ($existing as $package => $groups) {
75398            foreach ($groups as $group => $dupes) {
75399                if (count($dupes) > 1) {
75400                    $indices = $indices + $dupes;
75401                }
75402            }
75403        }
75404
75405        $indices = array_unique($indices);
75406        foreach ($indices as $index) {
75407            $errorparams[] = $params[$index];
75408        }
75409
75410        return count($errorparams);
75411    }
75412
75413    /**
75414     * @param array
75415     * @param bool ignore install groups - for final removal of dupe packages
75416     * @static
75417     */
75418    function removeDuplicates(&$params, $ignoreGroups = false)
75419    {
75420        $pnames = array();
75421        foreach ($params as $i => $param) {
75422            if (!$param) {
75423                continue;
75424            }
75425
75426            if ($param->getPackage()) {
75427                $group = $ignoreGroups ? '' : $param->getGroup();
75428                $pnames[$i] = $param->getChannel() . '/' .
75429                    $param->getPackage() . '-' . $param->getVersion() . '#' . $group;
75430            }
75431        }
75432
75433        $pnames = array_unique($pnames);
75434        $unset  = array_diff(array_keys($params), array_keys($pnames));
75435        $testp  = array_flip($pnames);
75436        foreach ($params as $i => $param) {
75437            if (!$param) {
75438                $unset[] = $i;
75439                continue;
75440            }
75441
75442            if (!is_a($param, 'PEAR_Downloader_Package')) {
75443                $unset[] = $i;
75444                continue;
75445            }
75446
75447            $group = $ignoreGroups ? '' : $param->getGroup();
75448            if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' .
75449                  $param->getVersion() . '#' . $group])) {
75450                $unset[] = $i;
75451            }
75452        }
75453
75454        foreach ($unset as $i) {
75455            unset($params[$i]);
75456        }
75457
75458        $ret = array();
75459        foreach ($params as $i => $param) {
75460            $ret[] = &$params[$i];
75461        }
75462
75463        $params = array();
75464        foreach ($ret as $i => $param) {
75465            $params[] = &$ret[$i];
75466        }
75467    }
75468
75469    function explicitState()
75470    {
75471        return $this->_explicitState;
75472    }
75473
75474    function setExplicitState($s)
75475    {
75476        $this->_explicitState = $s;
75477    }
75478
75479    /**
75480     * @static
75481     */
75482    function mergeDependencies(&$params)
75483    {
75484        $bundles = $newparams = array();
75485        foreach ($params as $i => $param) {
75486            if (!$param->isBundle()) {
75487                continue;
75488            }
75489
75490            $bundles[] = $i;
75491            $pf = &$param->getPackageFile();
75492            $newdeps = array();
75493            $contents = $pf->getBundledPackages();
75494            if (!is_array($contents)) {
75495                $contents = array($contents);
75496            }
75497
75498            foreach ($contents as $file) {
75499                $filecontents = $pf->getFileContents($file);
75500                $dl = &$param->getDownloader();
75501                $options = $dl->getOptions();
75502                if (PEAR::isError($dir = $dl->getDownloadDir())) {
75503                    return $dir;
75504                }
75505
75506                $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb');
75507                if (!$fp) {
75508                    continue;
75509                }
75510
75511                // FIXME do symlink check
75512
75513                fwrite($fp, $filecontents, strlen($filecontents));
75514                fclose($fp);
75515                if ($s = $params[$i]->explicitState()) {
75516                    $obj->setExplicitState($s);
75517                }
75518
75519                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
75520                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
75521                if (PEAR::isError($dir = $dl->getDownloadDir())) {
75522                    PEAR::popErrorHandling();
75523                    return $dir;
75524                }
75525
75526                $e = $obj->_fromFile($a = $dir . DIRECTORY_SEPARATOR . $file);
75527                PEAR::popErrorHandling();
75528                if (PEAR::isError($e)) {
75529                    if (!isset($options['soft'])) {
75530                        $dl->log(0, $e->getMessage());
75531                    }
75532                    continue;
75533                }
75534
75535                $j = &$obj;
75536                if (!PEAR_Downloader_Package::willDownload($j,
75537                      array_merge($params, $newparams)) && !$param->isInstalled($j)) {
75538                    $newparams[] = &$j;
75539                }
75540            }
75541        }
75542
75543        foreach ($bundles as $i) {
75544            unset($params[$i]); // remove bundles - only their contents matter for installation
75545        }
75546
75547        PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices
75548        if (count($newparams)) { // add in bundled packages for install
75549            foreach ($newparams as $i => $unused) {
75550                $params[] = &$newparams[$i];
75551            }
75552            $newparams = array();
75553        }
75554
75555        foreach ($params as $i => $param) {
75556            $newdeps = array();
75557            foreach ($param->_downloadDeps as $dep) {
75558                $merge = array_merge($params, $newparams);
75559                if (!PEAR_Downloader_Package::willDownload($dep, $merge)
75560                    && !$param->isInstalled($dep)
75561                ) {
75562                    $newdeps[] = $dep;
75563                } else {
75564                    //var_dump($dep);
75565                    // detect versioning conflicts here
75566                }
75567            }
75568
75569            // convert the dependencies into PEAR_Downloader_Package objects for the next time around
75570            $params[$i]->_downloadDeps = array();
75571            foreach ($newdeps as $dep) {
75572                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
75573                if ($s = $params[$i]->explicitState()) {
75574                    $obj->setExplicitState($s);
75575                }
75576
75577                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
75578                $e = $obj->fromDepURL($dep);
75579                PEAR::popErrorHandling();
75580                if (PEAR::isError($e)) {
75581                    if (!isset($options['soft'])) {
75582                        $obj->_downloader->log(0, $e->getMessage());
75583                    }
75584                    continue;
75585                }
75586
75587                $e = $obj->detectDependencies($params);
75588                if (PEAR::isError($e)) {
75589                    if (!isset($options['soft'])) {
75590                        $obj->_downloader->log(0, $e->getMessage());
75591                    }
75592                }
75593
75594                $j = &$obj;
75595                $newparams[] = &$j;
75596            }
75597        }
75598
75599        if (count($newparams)) {
75600            foreach ($newparams as $i => $unused) {
75601                $params[] = &$newparams[$i];
75602            }
75603            return true;
75604        }
75605
75606        return false;
75607    }
75608
75609
75610    /**
75611     * @static
75612     */
75613    function willDownload($param, $params)
75614    {
75615        if (!is_array($params)) {
75616            return false;
75617        }
75618
75619        foreach ($params as $obj) {
75620            if ($obj->isEqual($param)) {
75621                return true;
75622            }
75623        }
75624
75625        return false;
75626    }
75627
75628    /**
75629     * For simpler unit-testing
75630     * @param PEAR_Config
75631     * @param int
75632     * @param string
75633     */
75634    function &getPackagefileObject(&$c, $d)
75635    {
75636        $a = &new PEAR_PackageFile($c, $d);
75637        return $a;
75638    }
75639
75640    /**
75641     * This will retrieve from a local file if possible, and parse out
75642     * a group name as well.  The original parameter will be modified to reflect this.
75643     * @param string|array can be a parsed package name as well
75644     * @access private
75645     */
75646    function _fromFile(&$param)
75647    {
75648        $saveparam = $param;
75649        if (is_string($param)) {
75650            if (!@file_exists($param)) {
75651                $test = explode('#', $param);
75652                $group = array_pop($test);
75653                if (@file_exists(implode('#', $test))) {
75654                    $this->setGroup($group);
75655                    $param = implode('#', $test);
75656                    $this->_explicitGroup = true;
75657                }
75658            }
75659
75660            if (@is_file($param)) {
75661                $this->_type = 'local';
75662                $options = $this->_downloader->getOptions();
75663                $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug);
75664                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
75665                $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING);
75666                PEAR::popErrorHandling();
75667                if (PEAR::isError($pf)) {
75668                    $this->_valid = false;
75669                    $param = $saveparam;
75670                    return $pf;
75671                }
75672                $this->_packagefile = &$pf;
75673                if (!$this->getGroup()) {
75674                    $this->setGroup('default'); // install the default dependency group
75675                }
75676                return $this->_valid = true;
75677            }
75678        }
75679        $param = $saveparam;
75680        return $this->_valid = false;
75681    }
75682
75683    function _fromUrl($param, $saveparam = '')
75684    {
75685        if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) {
75686            $options = $this->_downloader->getOptions();
75687            $this->_type = 'url';
75688            $callback = $this->_downloader->ui ?
75689                array(&$this->_downloader, '_downloadCallback') : null;
75690            $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN);
75691            if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
75692                $this->_downloader->popErrorHandling();
75693                return $dir;
75694            }
75695
75696            $this->_downloader->log(3, 'Downloading "' . $param . '"');
75697            $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui,
75698                $dir, $callback, null, false, $this->getChannel());
75699            $this->_downloader->popErrorHandling();
75700            if (PEAR::isError($file)) {
75701                if (!empty($saveparam)) {
75702                    $saveparam = ", cannot download \"$saveparam\"";
75703                }
75704                $err = PEAR::raiseError('Could not download from "' . $param .
75705                    '"' . $saveparam . ' (' . $file->getMessage() . ')');
75706                    return $err;
75707            }
75708
75709            if ($this->_rawpackagefile) {
75710                require_once 'phar://install-pear-nozlib.phar/' . 'Archive/Tar.php';
75711                $tar = &new Archive_Tar($file);
75712                $packagexml = $tar->extractInString('package2.xml');
75713                if (!$packagexml) {
75714                    $packagexml = $tar->extractInString('package.xml');
75715                }
75716
75717                if (str_replace(array("\n", "\r"), array('',''), $packagexml) !=
75718                      str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) {
75719                    if ($this->getChannel() != 'pear.php.net') {
75720                        return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' .
75721                            'not match value returned from xml-rpc');
75722                    }
75723
75724                    // be more lax for the existing PEAR packages that have not-ok
75725                    // characters in their package.xml
75726                    $this->_downloader->log(0, 'CRITICAL WARNING: The "' .
75727                        $this->getPackage() . '" package has invalid characters in its ' .
75728                        'package.xml.  The next version of PEAR may not be able to install ' .
75729                        'this package for security reasons.  Please open a bug report at ' .
75730                        'http://pear.php.net/package/' . $this->getPackage() . '/bugs');
75731                }
75732            }
75733
75734            // whew, download worked!
75735            $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug);
75736
75737            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
75738            $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING);
75739            PEAR::popErrorHandling();
75740            if (PEAR::isError($pf)) {
75741                if (is_array($pf->getUserInfo())) {
75742                    foreach ($pf->getUserInfo() as $err) {
75743                        if (is_array($err)) {
75744                            $err = $err['message'];
75745                        }
75746
75747                        if (!isset($options['soft'])) {
75748                            $this->_downloader->log(0, "Validation Error: $err");
75749                        }
75750                    }
75751                }
75752
75753                if (!isset($options['soft'])) {
75754                    $this->_downloader->log(0, $pf->getMessage());
75755                }
75756
75757                ///FIXME need to pass back some error code that we can use to match with to cancel all further operations
75758                /// At least stop all deps of this package from being installed
75759                $out = $saveparam ? $saveparam : $param;
75760                $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive');
75761                $this->_valid = false;
75762                return $err;
75763            }
75764
75765            $this->_packagefile = &$pf;
75766            $this->setGroup('default'); // install the default dependency group
75767            return $this->_valid = true;
75768        }
75769
75770        return $this->_valid = false;
75771    }
75772
75773    /**
75774     *
75775     * @param string|array pass in an array of format
75776     *                     array(
75777     *                      'package' => 'pname',
75778     *                     ['channel' => 'channame',]
75779     *                     ['version' => 'version',]
75780     *                     ['state' => 'state',])
75781     *                     or a string of format [channame/]pname[-version|-state]
75782     */
75783    function _fromString($param)
75784    {
75785        $options = $this->_downloader->getOptions();
75786        $channel = $this->_config->get('default_channel');
75787        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
75788        $pname = $this->_registry->parsePackageName($param, $channel);
75789        PEAR::popErrorHandling();
75790        if (PEAR::isError($pname)) {
75791            if ($pname->getCode() == 'invalid') {
75792                $this->_valid = false;
75793                return false;
75794            }
75795
75796            if ($pname->getCode() == 'channel') {
75797                $parsed = $pname->getUserInfo();
75798                if ($this->_downloader->discover($parsed['channel'])) {
75799                    if ($this->_config->get('auto_discover')) {
75800                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
75801                        $pname = $this->_registry->parsePackageName($param, $channel);
75802                        PEAR::popErrorHandling();
75803                    } else {
75804                        if (!isset($options['soft'])) {
75805                            $this->_downloader->log(0, 'Channel "' . $parsed['channel'] .
75806                                '" is not initialized, use ' .
75807                                '"pear channel-discover ' . $parsed['channel'] . '" to initialize' .
75808                                'or pear config-set auto_discover 1');
75809                        }
75810                    }
75811                }
75812
75813                if (PEAR::isError($pname)) {
75814                    if (!isset($options['soft'])) {
75815                        $this->_downloader->log(0, $pname->getMessage());
75816                    }
75817
75818                    if (is_array($param)) {
75819                        $param = $this->_registry->parsedPackageNameToString($param);
75820                    }
75821
75822                    $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
75823                    $this->_valid = false;
75824                    return $err;
75825                }
75826            } else {
75827                if (!isset($options['soft'])) {
75828                    $this->_downloader->log(0, $pname->getMessage());
75829                }
75830
75831                $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
75832                $this->_valid = false;
75833                return $err;
75834            }
75835        }
75836
75837        if (!isset($this->_type)) {
75838            $this->_type = 'rest';
75839        }
75840
75841        $this->_parsedname    = $pname;
75842        $this->_explicitState = isset($pname['state']) ? $pname['state'] : false;
75843        $this->_explicitGroup = isset($pname['group']) ? true : false;
75844
75845        $info = $this->_downloader->_getPackageDownloadUrl($pname);
75846        if (PEAR::isError($info)) {
75847            if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') {
75848                // try pecl
75849                $pname['channel'] = 'pecl.php.net';
75850                if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) {
75851                    if (!PEAR::isError($test)) {
75852                        $info = PEAR::raiseError($info->getMessage() . ' - package ' .
75853                            $this->_registry->parsedPackageNameToString($pname, true) .
75854                            ' can be installed with "pecl install ' . $pname['package'] .
75855                            '"');
75856                    } else {
75857                        $pname['channel'] = 'pear.php.net';
75858                    }
75859                } else {
75860                    $pname['channel'] = 'pear.php.net';
75861                }
75862            }
75863
75864            return $info;
75865        }
75866
75867        $this->_rawpackagefile = $info['raw'];
75868        $ret = $this->_analyzeDownloadURL($info, $param, $pname);
75869        if (PEAR::isError($ret)) {
75870            return $ret;
75871        }
75872
75873        if ($ret) {
75874            $this->_downloadURL = $ret;
75875            return $this->_valid = (bool) $ret;
75876        }
75877    }
75878
75879    /**
75880     * @param array output of package.getDownloadURL
75881     * @param string|array|object information for detecting packages to be downloaded, and
75882     *                            for errors
75883     * @param array name information of the package
75884     * @param array|null packages to be downloaded
75885     * @param bool is this an optional dependency?
75886     * @param bool is this any kind of dependency?
75887     * @access private
75888     */
75889    function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false,
75890                                 $isdependency = false)
75891    {
75892        if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) {
75893            return false;
75894        }
75895
75896        if ($info === false) {
75897            $saveparam = !is_string($param) ? ", cannot download \"$param\"" : '';
75898
75899            // no releases exist
75900            return PEAR::raiseError('No releases for package "' .
75901                $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam);
75902        }
75903
75904        if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) {
75905            $err = false;
75906            if ($pname['channel'] == 'pecl.php.net') {
75907                if ($info['info']->getChannel() != 'pear.php.net') {
75908                    $err = true;
75909                }
75910            } elseif ($info['info']->getChannel() == 'pecl.php.net') {
75911                if ($pname['channel'] != 'pear.php.net') {
75912                    $err = true;
75913                }
75914            } else {
75915                $err = true;
75916            }
75917
75918            if ($err) {
75919                return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] .
75920                    '" retrieved another channel\'s name for download! ("' .
75921                    $info['info']->getChannel() . '")');
75922            }
75923        }
75924
75925        $preferred_state = $this->_config->get('preferred_state');
75926        if (!isset($info['url'])) {
75927            $package_version = $this->_registry->packageInfo($info['info']->getPackage(),
75928            'version', $info['info']->getChannel());
75929            if ($this->isInstalled($info)) {
75930                if ($isdependency && version_compare($info['version'], $package_version, '<=')) {
75931                    // ignore bogus errors of "failed to download dependency"
75932                    // if it is already installed and the one that would be
75933                    // downloaded is older or the same version (Bug #7219)
75934                    return false;
75935                }
75936            }
75937
75938            if ($info['version'] === $package_version) {
75939                if (!isset($options['soft'])) {
75940                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
75941                        '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' .
75942                        ' (' . $package_version . ') is the same as the locally installed one.');
75943                }
75944
75945                return false;
75946            }
75947
75948            if (version_compare($info['version'], $package_version, '<=')) {
75949                if (!isset($options['soft'])) {
75950                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
75951                        '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' .
75952                        ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').');
75953                }
75954
75955                return false;
75956            }
75957
75958            $instead =  ', will instead download version ' . $info['version'] .
75959                        ', stability "' . $info['info']->getState() . '"';
75960            // releases exist, but we failed to get any
75961            if (isset($this->_downloader->_options['force'])) {
75962                if (isset($pname['version'])) {
75963                    $vs = ', version "' . $pname['version'] . '"';
75964                } elseif (isset($pname['state'])) {
75965                    $vs = ', stability "' . $pname['state'] . '"';
75966                } elseif ($param == 'dependency') {
75967                    if (!class_exists('PEAR_Common')) {
75968                        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
75969                    }
75970
75971                    if (!in_array($info['info']->getState(),
75972                          PEAR_Common::betterStates($preferred_state, true))) {
75973                        if ($optional) {
75974                            // don't spit out confusing error message
75975                            return $this->_downloader->_getPackageDownloadUrl(
75976                                array('package' => $pname['package'],
75977                                      'channel' => $pname['channel'],
75978                                      'version' => $info['version']));
75979                        }
75980                        $vs = ' within preferred state "' . $preferred_state .
75981                            '"';
75982                    } else {
75983                        if (!class_exists('PEAR_Dependency2')) {
75984                            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
75985                        }
75986
75987                        if ($optional) {
75988                            // don't spit out confusing error message
75989                            return $this->_downloader->_getPackageDownloadUrl(
75990                                array('package' => $pname['package'],
75991                                      'channel' => $pname['channel'],
75992                                      'version' => $info['version']));
75993                        }
75994                        $vs = PEAR_Dependency2::_getExtraString($pname);
75995                        $instead = '';
75996                    }
75997                } else {
75998                    $vs = ' within preferred state "' . $preferred_state . '"';
75999                }
76000
76001                if (!isset($options['soft'])) {
76002                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
76003                        '/' . $pname['package'] . $vs . $instead);
76004                }
76005
76006                // download the latest release
76007                return $this->_downloader->_getPackageDownloadUrl(
76008                    array('package' => $pname['package'],
76009                          'channel' => $pname['channel'],
76010                          'version' => $info['version']));
76011            } else {
76012                if (isset($info['php']) && $info['php']) {
76013                    $err = PEAR::raiseError('Failed to download ' .
76014                        $this->_registry->parsedPackageNameToString(
76015                            array('channel' => $pname['channel'],
76016                                  'package' => $pname['package']),
76017                                true) .
76018                        ', latest release is version ' . $info['php']['v'] .
76019                        ', but it requires PHP version "' .
76020                        $info['php']['m'] . '", use "' .
76021                        $this->_registry->parsedPackageNameToString(
76022                            array('channel' => $pname['channel'], 'package' => $pname['package'],
76023                            'version' => $info['php']['v'])) . '" to install',
76024                            PEAR_DOWNLOADER_PACKAGE_PHPVERSION);
76025                    return $err;
76026                }
76027
76028                // construct helpful error message
76029                if (isset($pname['version'])) {
76030                    $vs = ', version "' . $pname['version'] . '"';
76031                } elseif (isset($pname['state'])) {
76032                    $vs = ', stability "' . $pname['state'] . '"';
76033                } elseif ($param == 'dependency') {
76034                    if (!class_exists('PEAR_Common')) {
76035                        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
76036                    }
76037
76038                    if (!in_array($info['info']->getState(),
76039                          PEAR_Common::betterStates($preferred_state, true))) {
76040                        if ($optional) {
76041                            // don't spit out confusing error message, and don't die on
76042                            // optional dep failure!
76043                            return $this->_downloader->_getPackageDownloadUrl(
76044                                array('package' => $pname['package'],
76045                                      'channel' => $pname['channel'],
76046                                      'version' => $info['version']));
76047                        }
76048                        $vs = ' within preferred state "' . $preferred_state . '"';
76049                    } else {
76050                        if (!class_exists('PEAR_Dependency2')) {
76051                            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
76052                        }
76053
76054                        if ($optional) {
76055                            // don't spit out confusing error message, and don't die on
76056                            // optional dep failure!
76057                            return $this->_downloader->_getPackageDownloadUrl(
76058                                array('package' => $pname['package'],
76059                                      'channel' => $pname['channel'],
76060                                      'version' => $info['version']));
76061                        }
76062                        $vs = PEAR_Dependency2::_getExtraString($pname);
76063                    }
76064                } else {
76065                    $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"';
76066                }
76067
76068                $options = $this->_downloader->getOptions();
76069                // this is only set by the "download-all" command
76070                if (isset($options['ignorepreferred_state'])) {
76071                    $err = PEAR::raiseError(
76072                        'Failed to download ' . $this->_registry->parsedPackageNameToString(
76073                            array('channel' => $pname['channel'], 'package' => $pname['package']),
76074                                true)
76075                         . $vs .
76076                        ', latest release is version ' . $info['version'] .
76077                        ', stability "' . $info['info']->getState() . '", use "' .
76078                        $this->_registry->parsedPackageNameToString(
76079                            array('channel' => $pname['channel'], 'package' => $pname['package'],
76080                            'version' => $info['version'])) . '" to install',
76081                            PEAR_DOWNLOADER_PACKAGE_STATE);
76082                    return $err;
76083                }
76084
76085                // Checks if the user has a package installed already and checks the release against
76086                // the state against the installed package, this allows upgrades for packages
76087                // with lower stability than the preferred_state
76088                $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']);
76089                if (!$this->isInstalled($info)
76090                    || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true))
76091                ) {
76092                    $err = PEAR::raiseError(
76093                        'Failed to download ' . $this->_registry->parsedPackageNameToString(
76094                            array('channel' => $pname['channel'], 'package' => $pname['package']),
76095                                true)
76096                         . $vs .
76097                        ', latest release is version ' . $info['version'] .
76098                        ', stability "' . $info['info']->getState() . '", use "' .
76099                        $this->_registry->parsedPackageNameToString(
76100                            array('channel' => $pname['channel'], 'package' => $pname['package'],
76101                            'version' => $info['version'])) . '" to install');
76102                    return $err;
76103                }
76104            }
76105        }
76106
76107        if (isset($info['deprecated']) && $info['deprecated']) {
76108            $this->_downloader->log(0,
76109                'WARNING: "' .
76110                    $this->_registry->parsedPackageNameToString(
76111                            array('channel' => $info['info']->getChannel(),
76112                                  'package' => $info['info']->getPackage()), true) .
76113                '" is deprecated in favor of "' .
76114                    $this->_registry->parsedPackageNameToString($info['deprecated'], true) .
76115                '"');
76116        }
76117
76118        return $info;
76119    }
76120}<?php
76121/**
76122 * Error Stack Implementation
76123 * 
76124 * This is an incredibly simple implementation of a very complex error handling
76125 * facility.  It contains the ability
76126 * to track multiple errors from multiple packages simultaneously.  In addition,
76127 * it can track errors of many levels, save data along with the error, context
76128 * information such as the exact file, line number, class and function that
76129 * generated the error, and if necessary, it can raise a traditional PEAR_Error.
76130 * It has built-in support for PEAR::Log, to log errors as they occur
76131 * 
76132 * Since version 0.2alpha, it is also possible to selectively ignore errors,
76133 * through the use of an error callback, see {@link pushCallback()}
76134 * 
76135 * Since version 0.3alpha, it is possible to specify the exception class
76136 * returned from {@link push()}
76137 *
76138 * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class.  This can
76139 * still be done quite handily in an error callback or by manipulating the returned array
76140 * @category   Debugging
76141 * @package    PEAR_ErrorStack
76142 * @author     Greg Beaver <cellog@php.net>
76143 * @copyright  2004-2008 Greg Beaver
76144 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
76145 * @version    CVS: $Id: ErrorStack.php 313023 2011-07-06 19:17:11Z dufuz $
76146 * @link       http://pear.php.net/package/PEAR_ErrorStack
76147 */
76148
76149/**
76150 * Singleton storage
76151 * 
76152 * Format:
76153 * <pre>
76154 * array(
76155 *  'package1' => PEAR_ErrorStack object,
76156 *  'package2' => PEAR_ErrorStack object,
76157 *  ...
76158 * )
76159 * </pre>
76160 * @access private
76161 * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
76162 */
76163$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
76164
76165/**
76166 * Global error callback (default)
76167 * 
76168 * This is only used if set to non-false.  * is the default callback for
76169 * all packages, whereas specific packages may set a default callback
76170 * for all instances, regardless of whether they are a singleton or not.
76171 *
76172 * To exclude non-singletons, only set the local callback for the singleton
76173 * @see PEAR_ErrorStack::setDefaultCallback()
76174 * @access private
76175 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
76176 */
76177$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
76178    '*' => false,
76179);
76180
76181/**
76182 * Global Log object (default)
76183 * 
76184 * This is only used if set to non-false.  Use to set a default log object for
76185 * all stacks, regardless of instantiation order or location
76186 * @see PEAR_ErrorStack::setDefaultLogger()
76187 * @access private
76188 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
76189 */
76190$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;
76191
76192/**
76193 * Global Overriding Callback
76194 * 
76195 * This callback will override any error callbacks that specific loggers have set.
76196 * Use with EXTREME caution
76197 * @see PEAR_ErrorStack::staticPushCallback()
76198 * @access private
76199 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
76200 */
76201$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
76202
76203/**#@+
76204 * One of four possible return values from the error Callback
76205 * @see PEAR_ErrorStack::_errorCallback()
76206 */
76207/**
76208 * If this is returned, then the error will be both pushed onto the stack
76209 * and logged.
76210 */
76211define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
76212/**
76213 * If this is returned, then the error will only be pushed onto the stack,
76214 * and not logged.
76215 */
76216define('PEAR_ERRORSTACK_PUSH', 2);
76217/**
76218 * If this is returned, then the error will only be logged, but not pushed
76219 * onto the error stack.
76220 */
76221define('PEAR_ERRORSTACK_LOG', 3);
76222/**
76223 * If this is returned, then the error is completely ignored.
76224 */
76225define('PEAR_ERRORSTACK_IGNORE', 4);
76226/**
76227 * If this is returned, then the error is logged and die() is called.
76228 */
76229define('PEAR_ERRORSTACK_DIE', 5);
76230/**#@-*/
76231
76232/**
76233 * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
76234 * the singleton method.
76235 */
76236define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);
76237
76238/**
76239 * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
76240 * that has no __toString() method
76241 */
76242define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
76243/**
76244 * Error Stack Implementation
76245 *
76246 * Usage:
76247 * <code>
76248 * // global error stack
76249 * $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
76250 * // local error stack
76251 * $local_stack = new PEAR_ErrorStack('MyPackage');
76252 * </code>
76253 * @author     Greg Beaver <cellog@php.net>
76254 * @version    1.9.4
76255 * @package    PEAR_ErrorStack
76256 * @category   Debugging
76257 * @copyright  2004-2008 Greg Beaver
76258 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
76259 * @version    CVS: $Id: ErrorStack.php 313023 2011-07-06 19:17:11Z dufuz $
76260 * @link       http://pear.php.net/package/PEAR_ErrorStack
76261 */
76262class PEAR_ErrorStack {
76263    /**
76264     * Errors are stored in the order that they are pushed on the stack.
76265     * @since 0.4alpha Errors are no longer organized by error level.
76266     * This renders pop() nearly unusable, and levels could be more easily
76267     * handled in a callback anyway
76268     * @var array
76269     * @access private
76270     */
76271    var $_errors = array();
76272
76273    /**
76274     * Storage of errors by level.
76275     *
76276     * Allows easy retrieval and deletion of only errors from a particular level
76277     * @since PEAR 1.4.0dev
76278     * @var array
76279     * @access private
76280     */
76281    var $_errorsByLevel = array();
76282
76283    /**
76284     * Package name this error stack represents
76285     * @var string
76286     * @access protected
76287     */
76288    var $_package;
76289    
76290    /**
76291     * Determines whether a PEAR_Error is thrown upon every error addition
76292     * @var boolean
76293     * @access private
76294     */
76295    var $_compat = false;
76296    
76297    /**
76298     * If set to a valid callback, this will be used to generate the error
76299     * message from the error code, otherwise the message passed in will be
76300     * used
76301     * @var false|string|array
76302     * @access private
76303     */
76304    var $_msgCallback = false;
76305    
76306    /**
76307     * If set to a valid callback, this will be used to generate the error
76308     * context for an error.  For PHP-related errors, this will be a file
76309     * and line number as retrieved from debug_backtrace(), but can be
76310     * customized for other purposes.  The error might actually be in a separate
76311     * configuration file, or in a database query.
76312     * @var false|string|array
76313     * @access protected
76314     */
76315    var $_contextCallback = false;
76316    
76317    /**
76318     * If set to a valid callback, this will be called every time an error
76319     * is pushed onto the stack.  The return value will be used to determine
76320     * whether to allow an error to be pushed or logged.
76321     * 
76322     * The return value must be one an PEAR_ERRORSTACK_* constant
76323     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
76324     * @var false|string|array
76325     * @access protected
76326     */
76327    var $_errorCallback = array();
76328    
76329    /**
76330     * PEAR::Log object for logging errors
76331     * @var false|Log
76332     * @access protected
76333     */
76334    var $_logger = false;
76335    
76336    /**
76337     * Error messages - designed to be overridden
76338     * @var array
76339     * @abstract
76340     */
76341    var $_errorMsgs = array();
76342    
76343    /**
76344     * Set up a new error stack
76345     * 
76346     * @param string   $package name of the package this error stack represents
76347     * @param callback $msgCallback callback used for error message generation
76348     * @param callback $contextCallback callback used for context generation,
76349     *                 defaults to {@link getFileLine()}
76350     * @param boolean  $throwPEAR_Error
76351     */
76352    function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false,
76353                         $throwPEAR_Error = false)
76354    {
76355        $this->_package = $package;
76356        $this->setMessageCallback($msgCallback);
76357        $this->setContextCallback($contextCallback);
76358        $this->_compat = $throwPEAR_Error;
76359    }
76360    
76361    /**
76362     * Return a single error stack for this package.
76363     * 
76364     * Note that all parameters are ignored if the stack for package $package
76365     * has already been instantiated
76366     * @param string   $package name of the package this error stack represents
76367     * @param callback $msgCallback callback used for error message generation
76368     * @param callback $contextCallback callback used for context generation,
76369     *                 defaults to {@link getFileLine()}
76370     * @param boolean  $throwPEAR_Error
76371     * @param string   $stackClass class to instantiate
76372     * @static
76373     * @return PEAR_ErrorStack
76374     */
76375    function &singleton($package, $msgCallback = false, $contextCallback = false,
76376                         $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack')
76377    {
76378        if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
76379            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
76380        }
76381        if (!class_exists($stackClass)) {
76382            if (function_exists('debug_backtrace')) {
76383                $trace = debug_backtrace();
76384            }
76385            PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
76386                'exception', array('stackclass' => $stackClass),
76387                'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
76388                false, $trace);
76389        }
76390        $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
76391            new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);
76392
76393        return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
76394    }
76395
76396    /**
76397     * Internal error handler for PEAR_ErrorStack class
76398     * 
76399     * Dies if the error is an exception (and would have died anyway)
76400     * @access private
76401     */
76402    function _handleError($err)
76403    {
76404        if ($err['level'] == 'exception') {
76405            $message = $err['message'];
76406            if (isset($_SERVER['REQUEST_URI'])) {
76407                echo '<br />';
76408            } else {
76409                echo "\n";
76410            }
76411            var_dump($err['context']);
76412            die($message);
76413        }
76414    }
76415    
76416    /**
76417     * Set up a PEAR::Log object for all error stacks that don't have one
76418     * @param Log $log 
76419     * @static
76420     */
76421    function setDefaultLogger(&$log)
76422    {
76423        if (is_object($log) && method_exists($log, 'log') ) {
76424            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
76425        } elseif (is_callable($log)) {
76426            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
76427	}
76428    }
76429    
76430    /**
76431     * Set up a PEAR::Log object for this error stack
76432     * @param Log $log 
76433     */
76434    function setLogger(&$log)
76435    {
76436        if (is_object($log) && method_exists($log, 'log') ) {
76437            $this->_logger = &$log;
76438        } elseif (is_callable($log)) {
76439            $this->_logger = &$log;
76440        }
76441    }
76442    
76443    /**
76444     * Set an error code => error message mapping callback
76445     * 
76446     * This method sets the callback that can be used to generate error
76447     * messages for any instance
76448     * @param array|string Callback function/method
76449     */
76450    function setMessageCallback($msgCallback)
76451    {
76452        if (!$msgCallback) {
76453            $this->_msgCallback = array(&$this, 'getErrorMessage');
76454        } else {
76455            if (is_callable($msgCallback)) {
76456                $this->_msgCallback = $msgCallback;
76457            }
76458        }
76459    }
76460    
76461    /**
76462     * Get an error code => error message mapping callback
76463     * 
76464     * This method returns the current callback that can be used to generate error
76465     * messages
76466     * @return array|string|false Callback function/method or false if none
76467     */
76468    function getMessageCallback()
76469    {
76470        return $this->_msgCallback;
76471    }
76472    
76473    /**
76474     * Sets a default callback to be used by all error stacks
76475     * 
76476     * This method sets the callback that can be used to generate error
76477     * messages for a singleton
76478     * @param array|string Callback function/method
76479     * @param string Package name, or false for all packages
76480     * @static
76481     */
76482    function setDefaultCallback($callback = false, $package = false)
76483    {
76484        if (!is_callable($callback)) {
76485            $callback = false;
76486        }
76487        $package = $package ? $package : '*';
76488        $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
76489    }
76490    
76491    /**
76492     * Set a callback that generates context information (location of error) for an error stack
76493     * 
76494     * This method sets the callback that can be used to generate context
76495     * information for an error.  Passing in NULL will disable context generation
76496     * and remove the expensive call to debug_backtrace()
76497     * @param array|string|null Callback function/method
76498     */
76499    function setContextCallback($contextCallback)
76500    {
76501        if ($contextCallback === null) {
76502            return $this->_contextCallback = false;
76503        }
76504        if (!$contextCallback) {
76505            $this->_contextCallback = array(&$this, 'getFileLine');
76506        } else {
76507            if (is_callable($contextCallback)) {
76508                $this->_contextCallback = $contextCallback;
76509            }
76510        }
76511    }
76512    
76513    /**
76514     * Set an error Callback
76515     * If set to a valid callback, this will be called every time an error
76516     * is pushed onto the stack.  The return value will be used to determine
76517     * whether to allow an error to be pushed or logged.
76518     * 
76519     * The return value must be one of the ERRORSTACK_* constants.
76520     * 
76521     * This functionality can be used to emulate PEAR's pushErrorHandling, and
76522     * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
76523     * the error stack or logging
76524     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
76525     * @see popCallback()
76526     * @param string|array $cb
76527     */
76528    function pushCallback($cb)
76529    {
76530        array_push($this->_errorCallback, $cb);
76531    }
76532    
76533    /**
76534     * Remove a callback from the error callback stack
76535     * @see pushCallback()
76536     * @return array|string|false
76537     */
76538    function popCallback()
76539    {
76540        if (!count($this->_errorCallback)) {
76541            return false;
76542        }
76543        return array_pop($this->_errorCallback);
76544    }
76545    
76546    /**
76547     * Set a temporary overriding error callback for every package error stack
76548     *
76549     * Use this to temporarily disable all existing callbacks (can be used
76550     * to emulate the @ operator, for instance)
76551     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
76552     * @see staticPopCallback(), pushCallback()
76553     * @param string|array $cb
76554     * @static
76555     */
76556    function staticPushCallback($cb)
76557    {
76558        array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
76559    }
76560    
76561    /**
76562     * Remove a temporary overriding error callback
76563     * @see staticPushCallback()
76564     * @return array|string|false
76565     * @static
76566     */
76567    function staticPopCallback()
76568    {
76569        $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
76570        if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
76571            $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
76572        }
76573        return $ret;
76574    }
76575    
76576    /**
76577     * Add an error to the stack
76578     * 
76579     * If the message generator exists, it is called with 2 parameters.
76580     *  - the current Error Stack object
76581     *  - an array that is in the same format as an error.  Available indices
76582     *    are 'code', 'package', 'time', 'params', 'level', and 'context'
76583     * 
76584     * Next, if the error should contain context information, this is
76585     * handled by the context grabbing method.
76586     * Finally, the error is pushed onto the proper error stack
76587     * @param int    $code      Package-specific error code
76588     * @param string $level     Error level.  This is NOT spell-checked
76589     * @param array  $params    associative array of error parameters
76590     * @param string $msg       Error message, or a portion of it if the message
76591     *                          is to be generated
76592     * @param array  $repackage If this error re-packages an error pushed by
76593     *                          another package, place the array returned from
76594     *                          {@link pop()} in this parameter
76595     * @param array  $backtrace Protected parameter: use this to pass in the
76596     *                          {@link debug_backtrace()} that should be used
76597     *                          to find error context
76598     * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
76599     * thrown.  If a PEAR_Error is returned, the userinfo
76600     * property is set to the following array:
76601     * 
76602     * <code>
76603     * array(
76604     *    'code' => $code,
76605     *    'params' => $params,
76606     *    'package' => $this->_package,
76607     *    'level' => $level,
76608     *    'time' => time(),
76609     *    'context' => $context,
76610     *    'message' => $msg,
76611     * //['repackage' => $err] repackaged error array/Exception class
76612     * );
76613     * </code>
76614     * 
76615     * Normally, the previous array is returned.
76616     */
76617    function push($code, $level = 'error', $params = array(), $msg = false,
76618                  $repackage = false, $backtrace = false)
76619    {
76620        $context = false;
76621        // grab error context
76622        if ($this->_contextCallback) {
76623            if (!$backtrace) {
76624                $backtrace = debug_backtrace();
76625            }
76626            $context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
76627        }
76628        
76629        // save error
76630        $time = explode(' ', microtime());
76631        $time = $time[1] + $time[0];
76632        $err = array(
76633                'code' => $code,
76634                'params' => $params,
76635                'package' => $this->_package,
76636                'level' => $level,
76637                'time' => $time,
76638                'context' => $context,
76639                'message' => $msg,
76640               );
76641
76642        if ($repackage) {
76643            $err['repackage'] = $repackage;
76644        }
76645
76646        // set up the error message, if necessary
76647        if ($this->_msgCallback) {
76648            $msg = call_user_func_array($this->_msgCallback,
76649                                        array(&$this, $err));
76650            $err['message'] = $msg;
76651        }        
76652        $push = $log = true;
76653        $die = false;
76654        // try the overriding callback first
76655        $callback = $this->staticPopCallback();
76656        if ($callback) {
76657            $this->staticPushCallback($callback);
76658        }
76659        if (!is_callable($callback)) {
76660            // try the local callback next
76661            $callback = $this->popCallback();
76662            if (is_callable($callback)) {
76663                $this->pushCallback($callback);
76664            } else {
76665                // try the default callback
76666                $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
76667                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
76668                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
76669            }
76670        }
76671        if (is_callable($callback)) {
76672            switch(call_user_func($callback, $err)){
76673            	case PEAR_ERRORSTACK_IGNORE: 
76674            		return $err;
76675        		break;
76676            	case PEAR_ERRORSTACK_PUSH: 
76677            		$log = false;
76678        		break;
76679            	case PEAR_ERRORSTACK_LOG: 
76680            		$push = false;
76681        		break;
76682            	case PEAR_ERRORSTACK_DIE: 
76683            		$die = true;
76684        		break;
76685                // anything else returned has the same effect as pushandlog
76686            }
76687        }
76688        if ($push) {
76689            array_unshift($this->_errors, $err);
76690            if (!isset($this->_errorsByLevel[$err['level']])) {
76691                $this->_errorsByLevel[$err['level']] = array();
76692            }
76693            $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
76694        }
76695        if ($log) {
76696            if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
76697                $this->_log($err);
76698            }
76699        }
76700        if ($die) {
76701            die();
76702        }
76703        if ($this->_compat && $push) {
76704            return $this->raiseError($msg, $code, null, null, $err);
76705        }
76706        return $err;
76707    }
76708    
76709    /**
76710     * Static version of {@link push()}
76711     * 
76712     * @param string $package   Package name this error belongs to
76713     * @param int    $code      Package-specific error code
76714     * @param string $level     Error level.  This is NOT spell-checked
76715     * @param array  $params    associative array of error parameters
76716     * @param string $msg       Error message, or a portion of it if the message
76717     *                          is to be generated
76718     * @param array  $repackage If this error re-packages an error pushed by
76719     *                          another package, place the array returned from
76720     *                          {@link pop()} in this parameter
76721     * @param array  $backtrace Protected parameter: use this to pass in the
76722     *                          {@link debug_backtrace()} that should be used
76723     *                          to find error context
76724     * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
76725     *                          thrown.  see docs for {@link push()}
76726     * @static
76727     */
76728    function staticPush($package, $code, $level = 'error', $params = array(),
76729                        $msg = false, $repackage = false, $backtrace = false)
76730    {
76731        $s = &PEAR_ErrorStack::singleton($package);
76732        if ($s->_contextCallback) {
76733            if (!$backtrace) {
76734                if (function_exists('debug_backtrace')) {
76735                    $backtrace = debug_backtrace();
76736                }
76737            }
76738        }
76739        return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
76740    }
76741    
76742    /**
76743     * Log an error using PEAR::Log
76744     * @param array $err Error array
76745     * @param array $levels Error level => Log constant map
76746     * @access protected
76747     */
76748    function _log($err)
76749    {
76750        if ($this->_logger) {
76751            $logger = &$this->_logger;
76752        } else {
76753            $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
76754        }
76755        if (is_a($logger, 'Log')) {
76756            $levels = array(
76757                'exception' => PEAR_LOG_CRIT,
76758                'alert' => PEAR_LOG_ALERT,
76759                'critical' => PEAR_LOG_CRIT,
76760                'error' => PEAR_LOG_ERR,
76761                'warning' => PEAR_LOG_WARNING,
76762                'notice' => PEAR_LOG_NOTICE,
76763                'info' => PEAR_LOG_INFO,
76764                'debug' => PEAR_LOG_DEBUG);
76765            if (isset($levels[$err['level']])) {
76766                $level = $levels[$err['level']];
76767            } else {
76768                $level = PEAR_LOG_INFO;
76769            }
76770            $logger->log($err['message'], $level, $err);
76771        } else { // support non-standard logs
76772            call_user_func($logger, $err);
76773        }
76774    }
76775
76776    
76777    /**
76778     * Pop an error off of the error stack
76779     * 
76780     * @return false|array
76781     * @since 0.4alpha it is no longer possible to specify a specific error
76782     * level to return - the last error pushed will be returned, instead
76783     */
76784    function pop()
76785    {
76786        $err = @array_shift($this->_errors);
76787        if (!is_null($err)) {
76788            @array_pop($this->_errorsByLevel[$err['level']]);
76789            if (!count($this->_errorsByLevel[$err['level']])) {
76790                unset($this->_errorsByLevel[$err['level']]);
76791            }
76792        }
76793        return $err;
76794    }
76795
76796    /**
76797     * Pop an error off of the error stack, static method
76798     *
76799     * @param string package name
76800     * @return boolean
76801     * @since PEAR1.5.0a1
76802     */
76803    function staticPop($package)
76804    {
76805        if ($package) {
76806            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
76807                return false;
76808            }
76809            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop();
76810        }
76811    }
76812
76813    /**
76814     * Determine whether there are any errors on the stack
76815     * @param string|array Level name.  Use to determine if any errors
76816     * of level (string), or levels (array) have been pushed
76817     * @return boolean
76818     */
76819    function hasErrors($level = false)
76820    {
76821        if ($level) {
76822            return isset($this->_errorsByLevel[$level]);
76823        }
76824        return count($this->_errors);
76825    }
76826    
76827    /**
76828     * Retrieve all errors since last purge
76829     * 
76830     * @param boolean set in order to empty the error stack
76831     * @param string level name, to return only errors of a particular severity
76832     * @return array
76833     */
76834    function getErrors($purge = false, $level = false)
76835    {
76836        if (!$purge) {
76837            if ($level) {
76838                if (!isset($this->_errorsByLevel[$level])) {
76839                    return array();
76840                } else {
76841                    return $this->_errorsByLevel[$level];
76842                }
76843            } else {
76844                return $this->_errors;
76845            }
76846        }
76847        if ($level) {
76848            $ret = $this->_errorsByLevel[$level];
76849            foreach ($this->_errorsByLevel[$level] as $i => $unused) {
76850                // entries are references to the $_errors array
76851                $this->_errorsByLevel[$level][$i] = false;
76852            }
76853            // array_filter removes all entries === false
76854            $this->_errors = array_filter($this->_errors);
76855            unset($this->_errorsByLevel[$level]);
76856            return $ret;
76857        }
76858        $ret = $this->_errors;
76859        $this->_errors = array();
76860        $this->_errorsByLevel = array();
76861        return $ret;
76862    }
76863    
76864    /**
76865     * Determine whether there are any errors on a single error stack, or on any error stack
76866     *
76867     * The optional parameter can be used to test the existence of any errors without the need of
76868     * singleton instantiation
76869     * @param string|false Package name to check for errors
76870     * @param string Level name to check for a particular severity
76871     * @return boolean
76872     * @static
76873     */
76874    function staticHasErrors($package = false, $level = false)
76875    {
76876        if ($package) {
76877            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
76878                return false;
76879            }
76880            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
76881        }
76882        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
76883            if ($obj->hasErrors($level)) {
76884                return true;
76885            }
76886        }
76887        return false;
76888    }
76889    
76890    /**
76891     * Get a list of all errors since last purge, organized by package
76892     * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
76893     * @param boolean $purge Set to purge the error stack of existing errors
76894     * @param string  $level Set to a level name in order to retrieve only errors of a particular level
76895     * @param boolean $merge Set to return a flat array, not organized by package
76896     * @param array   $sortfunc Function used to sort a merged array - default
76897     *        sorts by time, and should be good for most cases
76898     * @static
76899     * @return array 
76900     */
76901    function staticGetErrors($purge = false, $level = false, $merge = false,
76902                             $sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
76903    {
76904        $ret = array();
76905        if (!is_callable($sortfunc)) {
76906            $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
76907        }
76908        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
76909            $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
76910            if ($test) {
76911                if ($merge) {
76912                    $ret = array_merge($ret, $test);
76913                } else {
76914                    $ret[$package] = $test;
76915                }
76916            }
76917        }
76918        if ($merge) {
76919            usort($ret, $sortfunc);
76920        }
76921        return $ret;
76922    }
76923    
76924    /**
76925     * Error sorting function, sorts by time
76926     * @access private
76927     */
76928    function _sortErrors($a, $b)
76929    {
76930        if ($a['time'] == $b['time']) {
76931            return 0;
76932        }
76933        if ($a['time'] < $b['time']) {
76934            return 1;
76935        }
76936        return -1;
76937    }
76938
76939    /**
76940     * Standard file/line number/function/class context callback
76941     *
76942     * This function uses a backtrace generated from {@link debug_backtrace()}
76943     * and so will not work at all in PHP < 4.3.0.  The frame should
76944     * reference the frame that contains the source of the error.
76945     * @return array|false either array('file' => file, 'line' => line,
76946     *         'function' => function name, 'class' => class name) or
76947     *         if this doesn't work, then false
76948     * @param unused
76949     * @param integer backtrace frame.
76950     * @param array Results of debug_backtrace()
76951     * @static
76952     */
76953    function getFileLine($code, $params, $backtrace = null)
76954    {
76955        if ($backtrace === null) {
76956            return false;
76957        }
76958        $frame = 0;
76959        $functionframe = 1;
76960        if (!isset($backtrace[1])) {
76961            $functionframe = 0;
76962        } else {
76963            while (isset($backtrace[$functionframe]['function']) &&
76964                  $backtrace[$functionframe]['function'] == 'eval' &&
76965                  isset($backtrace[$functionframe + 1])) {
76966                $functionframe++;
76967            }
76968        }
76969        if (isset($backtrace[$frame])) {
76970            if (!isset($backtrace[$frame]['file'])) {
76971                $frame++;
76972            }
76973            $funcbacktrace = $backtrace[$functionframe];
76974            $filebacktrace = $backtrace[$frame];
76975            $ret = array('file' => $filebacktrace['file'],
76976                         'line' => $filebacktrace['line']);
76977            // rearrange for eval'd code or create function errors
76978            if (strpos($filebacktrace['file'], '(') && 
76979            	  preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'],
76980                  $matches)) {
76981                $ret['file'] = $matches[1];
76982                $ret['line'] = $matches[2] + 0;
76983            }
76984            if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
76985                if ($funcbacktrace['function'] != 'eval') {
76986                    if ($funcbacktrace['function'] == '__lambda_func') {
76987                        $ret['function'] = 'create_function() code';
76988                    } else {
76989                        $ret['function'] = $funcbacktrace['function'];
76990                    }
76991                }
76992            }
76993            if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
76994                $ret['class'] = $funcbacktrace['class'];
76995            }
76996            return $ret;
76997        }
76998        return false;
76999    }
77000    
77001    /**
77002     * Standard error message generation callback
77003     * 
77004     * This method may also be called by a custom error message generator
77005     * to fill in template values from the params array, simply
77006     * set the third parameter to the error message template string to use
77007     * 
77008     * The special variable %__msg% is reserved: use it only to specify
77009     * where a message passed in by the user should be placed in the template,
77010     * like so:
77011     * 
77012     * Error message: %msg% - internal error
77013     * 
77014     * If the message passed like so:
77015     * 
77016     * <code>
77017     * $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
77018     * </code>
77019     * 
77020     * The returned error message will be "Error message: server error 500 -
77021     * internal error"
77022     * @param PEAR_ErrorStack
77023     * @param array
77024     * @param string|false Pre-generated error message template
77025     * @static
77026     * @return string
77027     */
77028    function getErrorMessage(&$stack, $err, $template = false)
77029    {
77030        if ($template) {
77031            $mainmsg = $template;
77032        } else {
77033            $mainmsg = $stack->getErrorMessageTemplate($err['code']);
77034        }
77035        $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
77036        if (is_array($err['params']) && count($err['params'])) {
77037            foreach ($err['params'] as $name => $val) {
77038                if (is_array($val)) {
77039                    // @ is needed in case $val is a multi-dimensional array
77040                    $val = @implode(', ', $val);
77041                }
77042                if (is_object($val)) {
77043                    if (method_exists($val, '__toString')) {
77044                        $val = $val->__toString();
77045                    } else {
77046                        PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
77047                            'warning', array('obj' => get_class($val)),
77048                            'object %obj% passed into getErrorMessage, but has no __toString() method');
77049                        $val = 'Object';
77050                    }
77051                }
77052                $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
77053            }
77054        }
77055        return $mainmsg;
77056    }
77057    
77058    /**
77059     * Standard Error Message Template generator from code
77060     * @return string
77061     */
77062    function getErrorMessageTemplate($code)
77063    {
77064        if (!isset($this->_errorMsgs[$code])) {
77065            return '%__msg%';
77066        }
77067        return $this->_errorMsgs[$code];
77068    }
77069    
77070    /**
77071     * Set the Error Message Template array
77072     * 
77073     * The array format must be:
77074     * <pre>
77075     * array(error code => 'message template',...)
77076     * </pre>
77077     * 
77078     * Error message parameters passed into {@link push()} will be used as input
77079     * for the error message.  If the template is 'message %foo% was %bar%', and the
77080     * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
77081     * be 'message one was six'
77082     * @return string
77083     */
77084    function setErrorMessageTemplate($template)
77085    {
77086        $this->_errorMsgs = $template;
77087    }
77088    
77089    
77090    /**
77091     * emulate PEAR::raiseError()
77092     * 
77093     * @return PEAR_Error
77094     */
77095    function raiseError()
77096    {
77097        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
77098        $args = func_get_args();
77099        return call_user_func_array(array('PEAR', 'raiseError'), $args);
77100    }
77101}
77102$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
77103$stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
77104?>
77105<?php
77106/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
77107/**
77108 * PEAR_Exception
77109 *
77110 * PHP versions 4 and 5
77111 *
77112 * @category   pear
77113 * @package    PEAR
77114 * @author     Tomas V. V. Cox <cox@idecnet.com>
77115 * @author     Hans Lellelid <hans@velum.net>
77116 * @author     Bertrand Mansion <bmansion@mamasam.com>
77117 * @author     Greg Beaver <cellog@php.net>
77118 * @copyright  1997-2009 The Authors
77119 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
77120 * @version    CVS: $Id: Exception.php 313023 2011-07-06 19:17:11Z dufuz $
77121 * @link       http://pear.php.net/package/PEAR
77122 * @since      File available since Release 1.3.3
77123 */
77124
77125
77126/**
77127 * Base PEAR_Exception Class
77128 *
77129 * 1) Features:
77130 *
77131 * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
77132 * - Definable triggers, shot when exceptions occur
77133 * - Pretty and informative error messages
77134 * - Added more context info available (like class, method or cause)
77135 * - cause can be a PEAR_Exception or an array of mixed
77136 *   PEAR_Exceptions/PEAR_ErrorStack warnings
77137 * - callbacks for specific exception classes and their children
77138 *
77139 * 2) Ideas:
77140 *
77141 * - Maybe a way to define a 'template' for the output
77142 *
77143 * 3) Inherited properties from PHP Exception Class:
77144 *
77145 * protected $message
77146 * protected $code
77147 * protected $line
77148 * protected $file
77149 * private   $trace
77150 *
77151 * 4) Inherited methods from PHP Exception Class:
77152 *
77153 * __clone
77154 * __construct
77155 * getMessage
77156 * getCode
77157 * getFile
77158 * getLine
77159 * getTraceSafe
77160 * getTraceSafeAsString
77161 * __toString
77162 *
77163 * 5) Usage example
77164 *
77165 * <code>
77166 *  require_once 'PEAR/Exception.php';
77167 *
77168 *  class Test {
77169 *     function foo() {
77170 *         throw new PEAR_Exception('Error Message', ERROR_CODE);
77171 *     }
77172 *  }
77173 *
77174 *  function myLogger($pear_exception) {
77175 *     echo $pear_exception->getMessage();
77176 *  }
77177 *  // each time a exception is thrown the 'myLogger' will be called
77178 *  // (its use is completely optional)
77179 *  PEAR_Exception::addObserver('myLogger');
77180 *  $test = new Test;
77181 *  try {
77182 *     $test->foo();
77183 *  } catch (PEAR_Exception $e) {
77184 *     print $e;
77185 *  }
77186 * </code>
77187 *
77188 * @category   pear
77189 * @package    PEAR
77190 * @author     Tomas V.V.Cox <cox@idecnet.com>
77191 * @author     Hans Lellelid <hans@velum.net>
77192 * @author     Bertrand Mansion <bmansion@mamasam.com>
77193 * @author     Greg Beaver <cellog@php.net>
77194 * @copyright  1997-2009 The Authors
77195 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
77196 * @version    Release: 1.9.4
77197 * @link       http://pear.php.net/package/PEAR
77198 * @since      Class available since Release 1.3.3
77199 *
77200 */
77201class PEAR_Exception extends Exception
77202{
77203    const OBSERVER_PRINT = -2;
77204    const OBSERVER_TRIGGER = -4;
77205    const OBSERVER_DIE = -8;
77206    protected $cause;
77207    private static $_observers = array();
77208    private static $_uniqueid = 0;
77209    private $_trace;
77210
77211    /**
77212     * Supported signatures:
77213     *  - PEAR_Exception(string $message);
77214     *  - PEAR_Exception(string $message, int $code);
77215     *  - PEAR_Exception(string $message, Exception $cause);
77216     *  - PEAR_Exception(string $message, Exception $cause, int $code);
77217     *  - PEAR_Exception(string $message, PEAR_Error $cause);
77218     *  - PEAR_Exception(string $message, PEAR_Error $cause, int $code);
77219     *  - PEAR_Exception(string $message, array $causes);
77220     *  - PEAR_Exception(string $message, array $causes, int $code);
77221     * @param string exception message
77222     * @param int|Exception|PEAR_Error|array|null exception cause
77223     * @param int|null exception code or null
77224     */
77225    public function __construct($message, $p2 = null, $p3 = null)
77226    {
77227        if (is_int($p2)) {
77228            $code = $p2;
77229            $this->cause = null;
77230        } elseif (is_object($p2) || is_array($p2)) {
77231            // using is_object allows both Exception and PEAR_Error
77232            if (is_object($p2) && !($p2 instanceof Exception)) {
77233                if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
77234                    throw new PEAR_Exception('exception cause must be Exception, ' .
77235                        'array, or PEAR_Error');
77236                }
77237            }
77238            $code = $p3;
77239            if (is_array($p2) && isset($p2['message'])) {
77240                // fix potential problem of passing in a single warning
77241                $p2 = array($p2);
77242            }
77243            $this->cause = $p2;
77244        } else {
77245            $code = null;
77246            $this->cause = null;
77247        }
77248        parent::__construct($message, $code);
77249        $this->signal();
77250    }
77251
77252    /**
77253     * @param mixed $callback  - A valid php callback, see php func is_callable()
77254     *                         - A PEAR_Exception::OBSERVER_* constant
77255     *                         - An array(const PEAR_Exception::OBSERVER_*,
77256     *                           mixed $options)
77257     * @param string $label    The name of the observer. Use this if you want
77258     *                         to remove it later with removeObserver()
77259     */
77260    public static function addObserver($callback, $label = 'default')
77261    {
77262        self::$_observers[$label] = $callback;
77263    }
77264
77265    public static function removeObserver($label = 'default')
77266    {
77267        unset(self::$_observers[$label]);
77268    }
77269
77270    /**
77271     * @return int unique identifier for an observer
77272     */
77273    public static function getUniqueId()
77274    {
77275        return self::$_uniqueid++;
77276    }
77277
77278    private function signal()
77279    {
77280        foreach (self::$_observers as $func) {
77281            if (is_callable($func)) {
77282                call_user_func($func, $this);
77283                continue;
77284            }
77285            settype($func, 'array');
77286            switch ($func[0]) {
77287                case self::OBSERVER_PRINT :
77288                    $f = (isset($func[1])) ? $func[1] : '%s';
77289                    printf($f, $this->getMessage());
77290                    break;
77291                case self::OBSERVER_TRIGGER :
77292                    $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
77293                    trigger_error($this->getMessage(), $f);
77294                    break;
77295                case self::OBSERVER_DIE :
77296                    $f = (isset($func[1])) ? $func[1] : '%s';
77297                    die(printf($f, $this->getMessage()));
77298                    break;
77299                default:
77300                    trigger_error('invalid observer type', E_USER_WARNING);
77301            }
77302        }
77303    }
77304
77305    /**
77306     * Return specific error information that can be used for more detailed
77307     * error messages or translation.
77308     *
77309     * This method may be overridden in child exception classes in order
77310     * to add functionality not present in PEAR_Exception and is a placeholder
77311     * to define API
77312     *
77313     * The returned array must be an associative array of parameter => value like so:
77314     * <pre>
77315     * array('name' => $name, 'context' => array(...))
77316     * </pre>
77317     * @return array
77318     */
77319    public function getErrorData()
77320    {
77321        return array();
77322    }
77323
77324    /**
77325     * Returns the exception that caused this exception to be thrown
77326     * @access public
77327     * @return Exception|array The context of the exception
77328     */
77329    public function getCause()
77330    {
77331        return $this->cause;
77332    }
77333
77334    /**
77335     * Function must be public to call on caused exceptions
77336     * @param array
77337     */
77338    public function getCauseMessage(&$causes)
77339    {
77340        $trace = $this->getTraceSafe();
77341        $cause = array('class'   => get_class($this),
77342                       'message' => $this->message,
77343                       'file' => 'unknown',
77344                       'line' => 'unknown');
77345        if (isset($trace[0])) {
77346            if (isset($trace[0]['file'])) {
77347                $cause['file'] = $trace[0]['file'];
77348                $cause['line'] = $trace[0]['line'];
77349            }
77350        }
77351        $causes[] = $cause;
77352        if ($this->cause instanceof PEAR_Exception) {
77353            $this->cause->getCauseMessage($causes);
77354        } elseif ($this->cause instanceof Exception) {
77355            $causes[] = array('class'   => get_class($this->cause),
77356                              'message' => $this->cause->getMessage(),
77357                              'file' => $this->cause->getFile(),
77358                              'line' => $this->cause->getLine());
77359        } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
77360            $causes[] = array('class' => get_class($this->cause),
77361                              'message' => $this->cause->getMessage(),
77362                              'file' => 'unknown',
77363                              'line' => 'unknown');
77364        } elseif (is_array($this->cause)) {
77365            foreach ($this->cause as $cause) {
77366                if ($cause instanceof PEAR_Exception) {
77367                    $cause->getCauseMessage($causes);
77368                } elseif ($cause instanceof Exception) {
77369                    $causes[] = array('class'   => get_class($cause),
77370                                   'message' => $cause->getMessage(),
77371                                   'file' => $cause->getFile(),
77372                                   'line' => $cause->getLine());
77373                } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
77374                    $causes[] = array('class' => get_class($cause),
77375                                      'message' => $cause->getMessage(),
77376                                      'file' => 'unknown',
77377                                      'line' => 'unknown');
77378                } elseif (is_array($cause) && isset($cause['message'])) {
77379                    // PEAR_ErrorStack warning
77380                    $causes[] = array(
77381                        'class' => $cause['package'],
77382                        'message' => $cause['message'],
77383                        'file' => isset($cause['context']['file']) ?
77384                                            $cause['context']['file'] :
77385                                            'unknown',
77386                        'line' => isset($cause['context']['line']) ?
77387                                            $cause['context']['line'] :
77388                                            'unknown',
77389                    );
77390                }
77391            }
77392        }
77393    }
77394
77395    public function getTraceSafe()
77396    {
77397        if (!isset($this->_trace)) {
77398            $this->_trace = $this->getTrace();
77399            if (empty($this->_trace)) {
77400                $backtrace = debug_backtrace();
77401                $this->_trace = array($backtrace[count($backtrace)-1]);
77402            }
77403        }
77404        return $this->_trace;
77405    }
77406
77407    public function getErrorClass()
77408    {
77409        $trace = $this->getTraceSafe();
77410        return $trace[0]['class'];
77411    }
77412
77413    public function getErrorMethod()
77414    {
77415        $trace = $this->getTraceSafe();
77416        return $trace[0]['function'];
77417    }
77418
77419    public function __toString()
77420    {
77421        if (isset($_SERVER['REQUEST_URI'])) {
77422            return $this->toHtml();
77423        }
77424        return $this->toText();
77425    }
77426
77427    public function toHtml()
77428    {
77429        $trace = $this->getTraceSafe();
77430        $causes = array();
77431        $this->getCauseMessage($causes);
77432        $html =  '<table style="border: 1px" cellspacing="0">' . "\n";
77433        foreach ($causes as $i => $cause) {
77434            $html .= '<tr><td colspan="3" style="background: #ff9999">'
77435               . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
77436               . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
77437               . 'on line <b>' . $cause['line'] . '</b>'
77438               . "</td></tr>\n";
77439        }
77440        $html .= '<tr><td colspan="3" style="background-color: #aaaaaa; text-align: center; font-weight: bold;">Exception trace</td></tr>' . "\n"
77441               . '<tr><td style="text-align: center; background: #cccccc; width:20px; font-weight: bold;">#</td>'
77442               . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Function</td>'
77443               . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Location</td></tr>' . "\n";
77444
77445        foreach ($trace as $k => $v) {
77446            $html .= '<tr><td style="text-align: center;">' . $k . '</td>'
77447                   . '<td>';
77448            if (!empty($v['class'])) {
77449                $html .= $v['class'] . $v['type'];
77450            }
77451            $html .= $v['function'];
77452            $args = array();
77453            if (!empty($v['args'])) {
77454                foreach ($v['args'] as $arg) {
77455                    if (is_null($arg)) $args[] = 'null';
77456                    elseif (is_array($arg)) $args[] = 'Array';
77457                    elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
77458                    elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
77459                    elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
77460                    else {
77461                        $arg = (string)$arg;
77462                        $str = htmlspecialchars(substr($arg, 0, 16));
77463                        if (strlen($arg) > 16) $str .= '&hellip;';
77464                        $args[] = "'" . $str . "'";
77465                    }
77466                }
77467            }
77468            $html .= '(' . implode(', ',$args) . ')'
77469                   . '</td>'
77470                   . '<td>' . (isset($v['file']) ? $v['file'] : 'unknown')
77471                   . ':' . (isset($v['line']) ? $v['line'] : 'unknown')
77472                   . '</td></tr>' . "\n";
77473        }
77474        $html .= '<tr><td style="text-align: center;">' . ($k+1) . '</td>'
77475               . '<td>{main}</td>'
77476               . '<td>&nbsp;</td></tr>' . "\n"
77477               . '</table>';
77478        return $html;
77479    }
77480
77481    public function toText()
77482    {
77483        $causes = array();
77484        $this->getCauseMessage($causes);
77485        $causeMsg = '';
77486        foreach ($causes as $i => $cause) {
77487            $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
77488                   . $cause['message'] . ' in ' . $cause['file']
77489                   . ' on line ' . $cause['line'] . "\n";
77490        }
77491        return $causeMsg . $this->getTraceAsString();
77492    }
77493}<?php
77494if ($skipmsg) {
77495    $a = &new $ec($code, $mode, $options, $userinfo);
77496} else {
77497    $a = &new $ec($message, $code, $mode, $options, $userinfo);
77498}
77499?><?php
77500/**
77501 * PEAR_Frontend, the singleton-based frontend for user input/output
77502 *
77503 * PHP versions 4 and 5
77504 *
77505 * @category   pear
77506 * @package    PEAR
77507 * @author     Greg Beaver <cellog@php.net>
77508 * @copyright  1997-2009 The Authors
77509 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
77510 * @version    CVS: $Id: Frontend.php 313023 2011-07-06 19:17:11Z dufuz $
77511 * @link       http://pear.php.net/package/PEAR
77512 * @since      File available since Release 1.4.0a1
77513 */
77514
77515/**
77516 * Include error handling
77517 */
77518//require_once 'PEAR.php';
77519
77520/**
77521 * Which user interface class is being used.
77522 * @var string class name
77523 */
77524$GLOBALS['_PEAR_FRONTEND_CLASS'] = 'PEAR_Frontend_CLI';
77525
77526/**
77527 * Instance of $_PEAR_Command_uiclass.
77528 * @var object
77529 */
77530$GLOBALS['_PEAR_FRONTEND_SINGLETON'] = null;
77531
77532/**
77533 * Singleton-based frontend for PEAR user input/output
77534 *
77535 * @category   pear
77536 * @package    PEAR
77537 * @author     Greg Beaver <cellog@php.net>
77538 * @copyright  1997-2009 The Authors
77539 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
77540 * @version    Release: 1.9.4
77541 * @link       http://pear.php.net/package/PEAR
77542 * @since      Class available since Release 1.4.0a1
77543 */
77544class PEAR_Frontend extends PEAR
77545{
77546    /**
77547     * Retrieve the frontend object
77548     * @return PEAR_Frontend_CLI|PEAR_Frontend_Web|PEAR_Frontend_Gtk
77549     * @static
77550     */
77551    function &singleton($type = null)
77552    {
77553        if ($type === null) {
77554            if (!isset($GLOBALS['_PEAR_FRONTEND_SINGLETON'])) {
77555                $a = false;
77556                return $a;
77557            }
77558            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
77559        }
77560
77561        $a = PEAR_Frontend::setFrontendClass($type);
77562        return $a;
77563    }
77564
77565    /**
77566     * Set the frontend class that will be used by calls to {@link singleton()}
77567     *
77568     * Frontends are expected to conform to the PEAR naming standard of
77569     * _ => DIRECTORY_SEPARATOR (PEAR_Frontend_CLI is in PEAR/Frontend/CLI.php)
77570     * @param string $uiclass full class name
77571     * @return PEAR_Frontend
77572     * @static
77573     */
77574    function &setFrontendClass($uiclass)
77575    {
77576        if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
77577              is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], $uiclass)) {
77578            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
77579        }
77580
77581        if (!class_exists($uiclass)) {
77582            $file = 'phar://install-pear-nozlib.phar/' . str_replace('_', '/', $uiclass) . '.php';
77583            if (PEAR_Frontend::isIncludeable($file)) {
77584                include_once $file;
77585            }
77586        }
77587
77588        if (class_exists($uiclass)) {
77589            $obj = &new $uiclass;
77590            // quick test to see if this class implements a few of the most
77591            // important frontend methods
77592            if (is_a($obj, 'PEAR_Frontend')) {
77593                $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$obj;
77594                $GLOBALS['_PEAR_FRONTEND_CLASS'] = $uiclass;
77595                return $obj;
77596            }
77597
77598            $err = PEAR::raiseError("not a frontend class: $uiclass");
77599            return $err;
77600        }
77601
77602        $err = PEAR::raiseError("no such class: $uiclass");
77603        return $err;
77604    }
77605
77606    /**
77607     * Set the frontend class that will be used by calls to {@link singleton()}
77608     *
77609     * Frontends are expected to be a descendant of PEAR_Frontend
77610     * @param PEAR_Frontend
77611     * @return PEAR_Frontend
77612     * @static
77613     */
77614    function &setFrontendObject($uiobject)
77615    {
77616        if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
77617              is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], get_class($uiobject))) {
77618            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
77619        }
77620
77621        if (!is_a($uiobject, 'PEAR_Frontend')) {
77622            $err = PEAR::raiseError('not a valid frontend class: (' .
77623                get_class($uiobject) . ')');
77624            return $err;
77625        }
77626
77627        $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$uiobject;
77628        $GLOBALS['_PEAR_FRONTEND_CLASS'] = get_class($uiobject);
77629        return $uiobject;
77630    }
77631
77632    /**
77633     * @param string $path relative or absolute include path
77634     * @return boolean
77635     * @static
77636     */
77637    function isIncludeable($path)
77638    {
77639        if (file_exists($path) && is_readable($path)) {
77640            return true;
77641        }
77642
77643        $fp = @fopen($path, 'r', true);
77644        if ($fp) {
77645            fclose($fp);
77646            return true;
77647        }
77648
77649        return false;
77650    }
77651
77652    /**
77653     * @param PEAR_Config
77654     */
77655    function setConfig(&$config)
77656    {
77657    }
77658
77659    /**
77660     * This can be overridden to allow session-based temporary file management
77661     *
77662     * By default, all files are deleted at the end of a session.  The web installer
77663     * needs to be able to sustain a list over many sessions in order to support
77664     * user interaction with install scripts
77665     */
77666    function addTempFile($file)
77667    {
77668        $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
77669    }
77670
77671    /**
77672     * Log an action
77673     *
77674     * @param string $msg the message to log
77675     * @param boolean $append_crlf
77676     * @return boolean true
77677     * @abstract
77678     */
77679    function log($msg, $append_crlf = true)
77680    {
77681    }
77682
77683    /**
77684     * Run a post-installation script
77685     *
77686     * @param array $scripts array of post-install scripts
77687     * @abstract
77688     */
77689    function runPostinstallScripts(&$scripts)
77690    {
77691    }
77692
77693    /**
77694     * Display human-friendly output formatted depending on the
77695     * $command parameter.
77696     *
77697     * This should be able to handle basic output data with no command
77698     * @param mixed  $data    data structure containing the information to display
77699     * @param string $command command from which this method was called
77700     * @abstract
77701     */
77702    function outputData($data, $command = '_default')
77703    {
77704    }
77705
77706    /**
77707     * Display a modal form dialog and return the given input
77708     *
77709     * A frontend that requires multiple requests to retrieve and process
77710     * data must take these needs into account, and implement the request
77711     * handling code.
77712     * @param string $command  command from which this method was called
77713     * @param array  $prompts  associative array. keys are the input field names
77714     *                         and values are the description
77715     * @param array  $types    array of input field types (text, password,
77716     *                         etc.) keys have to be the same like in $prompts
77717     * @param array  $defaults array of default values. again keys have
77718     *                         to be the same like in $prompts.  Do not depend
77719     *                         on a default value being set.
77720     * @return array input sent by the user
77721     * @abstract
77722     */
77723    function userDialog($command, $prompts, $types = array(), $defaults = array())
77724    {
77725    }
77726}<?php
77727/**
77728 * PEAR_Frontend_CLI
77729 *
77730 * PHP versions 4 and 5
77731 *
77732 * @category   pear
77733 * @package    PEAR
77734 * @author     Stig Bakken <ssb@php.net>
77735 * @author     Greg Beaver <cellog@php.net>
77736 * @copyright  1997-2009 The Authors
77737 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
77738 * @version    CVS: $Id: CLI.php 313023 2011-07-06 19:17:11Z dufuz $
77739 * @link       http://pear.php.net/package/PEAR
77740 * @since      File available since Release 0.1
77741 */
77742/**
77743 * base class
77744 */
77745require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Frontend.php';
77746
77747/**
77748 * Command-line Frontend for the PEAR Installer
77749 * @category   pear
77750 * @package    PEAR
77751 * @author     Stig Bakken <ssb@php.net>
77752 * @author     Greg Beaver <cellog@php.net>
77753 * @copyright  1997-2009 The Authors
77754 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
77755 * @version    Release: 1.9.4
77756 * @link       http://pear.php.net/package/PEAR
77757 * @since      Class available since Release 0.1
77758 */
77759class PEAR_Frontend_CLI extends PEAR_Frontend
77760{
77761    /**
77762     * What type of user interface this frontend is for.
77763     * @var string
77764     * @access public
77765     */
77766    var $type = 'CLI';
77767    var $lp = ''; // line prefix
77768
77769    var $params = array();
77770    var $term = array(
77771        'bold'   => '',
77772        'normal' => '',
77773    );
77774
77775    function PEAR_Frontend_CLI()
77776    {
77777        parent::PEAR();
77778        $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1
77779        if (function_exists('posix_isatty') && !posix_isatty(1)) {
77780            // output is being redirected to a file or through a pipe
77781        } elseif ($term) {
77782            if (preg_match('/^(xterm|vt220|linux)/', $term)) {
77783                $this->term['bold']   = sprintf("%c%c%c%c", 27, 91, 49, 109);
77784                $this->term['normal'] = sprintf("%c%c%c", 27, 91, 109);
77785            } elseif (preg_match('/^vt100/', $term)) {
77786                $this->term['bold']   = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0);
77787                $this->term['normal'] = sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0);
77788            }
77789        } elseif (OS_WINDOWS) {
77790            // XXX add ANSI codes here
77791        }
77792    }
77793
77794    /**
77795     * @param object PEAR_Error object
77796     */
77797    function displayError($e)
77798    {
77799        return $this->_displayLine($e->getMessage());
77800    }
77801
77802    /**
77803     * @param object PEAR_Error object
77804     */
77805    function displayFatalError($eobj)
77806    {
77807        $this->displayError($eobj);
77808        if (class_exists('PEAR_Config')) {
77809            $config = &PEAR_Config::singleton();
77810            if ($config->get('verbose') > 5) {
77811                if (function_exists('debug_print_backtrace')) {
77812                    debug_print_backtrace();
77813                    exit(1);
77814                }
77815
77816                $raised = false;
77817                foreach (debug_backtrace() as $i => $frame) {
77818                    if (!$raised) {
77819                        if (isset($frame['class'])
77820                            && strtolower($frame['class']) == 'pear'
77821                            && strtolower($frame['function']) == 'raiseerror'
77822                        ) {
77823                            $raised = true;
77824                        } else {
77825                            continue;
77826                        }
77827                    }
77828
77829                    $frame['class']    = !isset($frame['class'])    ? '' : $frame['class'];
77830                    $frame['type']     = !isset($frame['type'])     ? '' : $frame['type'];
77831                    $frame['function'] = !isset($frame['function']) ? '' : $frame['function'];
77832                    $frame['line']     = !isset($frame['line'])     ? '' : $frame['line'];
77833                    $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]");
77834                }
77835            }
77836        }
77837
77838        exit(1);
77839    }
77840
77841    /**
77842     * Instruct the runInstallScript method to skip a paramgroup that matches the
77843     * id value passed in.
77844     *
77845     * This method is useful for dynamically configuring which sections of a post-install script
77846     * will be run based on the user's setup, which is very useful for making flexible
77847     * post-install scripts without losing the cross-Frontend ability to retrieve user input
77848     * @param string
77849     */
77850    function skipParamgroup($id)
77851    {
77852        $this->_skipSections[$id] = true;
77853    }
77854
77855    function runPostinstallScripts(&$scripts)
77856    {
77857        foreach ($scripts as $i => $script) {
77858            $this->runInstallScript($scripts[$i]->_params, $scripts[$i]->_obj);
77859        }
77860    }
77861
77862    /**
77863     * @param array $xml contents of postinstallscript tag
77864     * @param object $script post-installation script
77865     * @param string install|upgrade
77866     */
77867    function runInstallScript($xml, &$script)
77868    {
77869        $this->_skipSections = array();
77870        if (!is_array($xml) || !isset($xml['paramgroup'])) {
77871            $script->run(array(), '_default');
77872            return;
77873        }
77874
77875        $completedPhases = array();
77876        if (!isset($xml['paramgroup'][0])) {
77877            $xml['paramgroup'] = array($xml['paramgroup']);
77878        }
77879
77880        foreach ($xml['paramgroup'] as $group) {
77881            if (isset($this->_skipSections[$group['id']])) {
77882                // the post-install script chose to skip this section dynamically
77883                continue;
77884            }
77885
77886            if (isset($group['name'])) {
77887                $paramname = explode('::', $group['name']);
77888                if ($lastgroup['id'] != $paramname[0]) {
77889                    continue;
77890                }
77891
77892                $group['name'] = $paramname[1];
77893                if (!isset($answers)) {
77894                    return;
77895                }
77896
77897                if (isset($answers[$group['name']])) {
77898                    switch ($group['conditiontype']) {
77899                        case '=' :
77900                            if ($answers[$group['name']] != $group['value']) {
77901                                continue 2;
77902                            }
77903                        break;
77904                        case '!=' :
77905                            if ($answers[$group['name']] == $group['value']) {
77906                                continue 2;
77907                            }
77908                        break;
77909                        case 'preg_match' :
77910                            if (!@preg_match('/' . $group['value'] . '/',
77911                                  $answers[$group['name']])) {
77912                                continue 2;
77913                            }
77914                        break;
77915                        default :
77916                        return;
77917                    }
77918                }
77919            }
77920
77921            $lastgroup = $group;
77922            if (isset($group['instructions'])) {
77923                $this->_display($group['instructions']);
77924            }
77925
77926            if (!isset($group['param'][0])) {
77927                $group['param'] = array($group['param']);
77928            }
77929
77930            if (isset($group['param'])) {
77931                if (method_exists($script, 'postProcessPrompts')) {
77932                    $prompts = $script->postProcessPrompts($group['param'], $group['id']);
77933                    if (!is_array($prompts) || count($prompts) != count($group['param'])) {
77934                        $this->outputData('postinstall', 'Error: post-install script did not ' .
77935                            'return proper post-processed prompts');
77936                        $prompts = $group['param'];
77937                    } else {
77938                        foreach ($prompts as $i => $var) {
77939                            if (!is_array($var) || !isset($var['prompt']) ||
77940                                  !isset($var['name']) ||
77941                                  ($var['name'] != $group['param'][$i]['name']) ||
77942                                  ($var['type'] != $group['param'][$i]['type'])
77943                            ) {
77944                                $this->outputData('postinstall', 'Error: post-install script ' .
77945                                    'modified the variables or prompts, severe security risk. ' .
77946                                    'Will instead use the defaults from the package.xml');
77947                                $prompts = $group['param'];
77948                            }
77949                        }
77950                    }
77951
77952                    $answers = $this->confirmDialog($prompts);
77953                } else {
77954                    $answers = $this->confirmDialog($group['param']);
77955                }
77956            }
77957
77958            if ((isset($answers) && $answers) || !isset($group['param'])) {
77959                if (!isset($answers)) {
77960                    $answers = array();
77961                }
77962
77963                array_unshift($completedPhases, $group['id']);
77964                if (!$script->run($answers, $group['id'])) {
77965                    $script->run($completedPhases, '_undoOnError');
77966                    return;
77967                }
77968            } else {
77969                $script->run($completedPhases, '_undoOnError');
77970                return;
77971            }
77972        }
77973    }
77974
77975    /**
77976     * Ask for user input, confirm the answers and continue until the user is satisfied
77977     * @param array an array of arrays, format array('name' => 'paramname', 'prompt' =>
77978     *              'text to display', 'type' => 'string'[, default => 'default value'])
77979     * @return array
77980     */
77981    function confirmDialog($params)
77982    {
77983        $answers = $prompts = $types = array();
77984        foreach ($params as $param) {
77985            $prompts[$param['name']] = $param['prompt'];
77986            $types[$param['name']]   = $param['type'];
77987            $answers[$param['name']] = isset($param['default']) ? $param['default'] : '';
77988        }
77989
77990        $tried = false;
77991        do {
77992            if ($tried) {
77993                $i = 1;
77994                foreach ($answers as $var => $value) {
77995                    if (!strlen($value)) {
77996                        echo $this->bold("* Enter an answer for #" . $i . ": ({$prompts[$var]})\n");
77997                    }
77998                    $i++;
77999                }
78000            }
78001
78002            $answers = $this->userDialog('', $prompts, $types, $answers);
78003            $tried   = true;
78004        } while (is_array($answers) && count(array_filter($answers)) != count($prompts));
78005
78006        return $answers;
78007    }
78008
78009    function userDialog($command, $prompts, $types = array(), $defaults = array(), $screensize = 20)
78010    {
78011        if (!is_array($prompts)) {
78012            return array();
78013        }
78014
78015        $testprompts = array_keys($prompts);
78016        $result      = $defaults;
78017
78018        reset($prompts);
78019        if (count($prompts) === 1) {
78020            foreach ($prompts as $key => $prompt) {
78021                $type    = $types[$key];
78022                $default = @$defaults[$key];
78023                print "$prompt ";
78024                if ($default) {
78025                    print "[$default] ";
78026                }
78027                print ": ";
78028
78029                $line         = fgets(STDIN, 2048);
78030                $result[$key] =  ($default && trim($line) == '') ? $default : trim($line);
78031            }
78032
78033            return $result;
78034        }
78035
78036        $first_run = true;
78037        while (true) {
78038            $descLength = max(array_map('strlen', $prompts));
78039            $descFormat = "%-{$descLength}s";
78040            $last       = count($prompts);
78041
78042            $i = 0;
78043            foreach ($prompts as $n => $var) {
78044                $res = isset($result[$n]) ? $result[$n] : null;
78045                printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], $res);
78046            }
78047            print "\n1-$last, 'all', 'abort', or Enter to continue: ";
78048
78049            $tmp = trim(fgets(STDIN, 1024));
78050            if (empty($tmp)) {
78051                break;
78052            }
78053
78054            if ($tmp == 'abort') {
78055                return false;
78056            }
78057
78058            if (isset($testprompts[(int)$tmp - 1])) {
78059                $var     = $testprompts[(int)$tmp - 1];
78060                $desc    = $prompts[$var];
78061                $current = @$result[$var];
78062                print "$desc [$current] : ";
78063                $tmp = trim(fgets(STDIN, 1024));
78064                if ($tmp !== '') {
78065                    $result[$var] = $tmp;
78066                }
78067            } elseif ($tmp == 'all') {
78068                foreach ($prompts as $var => $desc) {
78069                    $current = $result[$var];
78070                    print "$desc [$current] : ";
78071                    $tmp = trim(fgets(STDIN, 1024));
78072                    if (trim($tmp) !== '') {
78073                        $result[$var] = trim($tmp);
78074                    }
78075                }
78076            }
78077
78078            $first_run = false;
78079        }
78080
78081        return $result;
78082    }
78083
78084    function userConfirm($prompt, $default = 'yes')
78085    {
78086        trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR);
78087        static $positives = array('y', 'yes', 'on', '1');
78088        static $negatives = array('n', 'no', 'off', '0');
78089        print "$this->lp$prompt [$default] : ";
78090        $fp = fopen("php://stdin", "r");
78091        $line = fgets($fp, 2048);
78092        fclose($fp);
78093        $answer = strtolower(trim($line));
78094        if (empty($answer)) {
78095            $answer = $default;
78096        }
78097        if (in_array($answer, $positives)) {
78098            return true;
78099        }
78100        if (in_array($answer, $negatives)) {
78101            return false;
78102        }
78103        if (in_array($default, $positives)) {
78104            return true;
78105        }
78106        return false;
78107    }
78108
78109    function outputData($data, $command = '_default')
78110    {
78111        switch ($command) {
78112            case 'channel-info':
78113                foreach ($data as $type => $section) {
78114                    if ($type == 'main') {
78115                        $section['data'] = array_values($section['data']);
78116                    }
78117
78118                    $this->outputData($section);
78119                }
78120                break;
78121            case 'install':
78122            case 'upgrade':
78123            case 'upgrade-all':
78124                if (is_array($data) && isset($data['release_warnings'])) {
78125                    $this->_displayLine('');
78126                    $this->_startTable(array(
78127                        'border' => false,
78128                        'caption' => 'Release Warnings'
78129                    ));
78130                    $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55)));
78131                    $this->_endTable();
78132                    $this->_displayLine('');
78133                }
78134
78135                $this->_displayLine(is_array($data) ? $data['data'] : $data);
78136                break;
78137            case 'search':
78138                $this->_startTable($data);
78139                if (isset($data['headline']) && is_array($data['headline'])) {
78140                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
78141                }
78142
78143                $packages = array();
78144                foreach($data['data'] as $category) {
78145                    foreach($category as $name => $pkg) {
78146                        $packages[$pkg[0]] = $pkg;
78147                    }
78148                }
78149
78150                $p = array_keys($packages);
78151                natcasesort($p);
78152                foreach ($p as $name) {
78153                    $this->_tableRow($packages[$name], null, array(1 => array('wrap' => 55)));
78154                }
78155
78156                $this->_endTable();
78157                break;
78158            case 'list-all':
78159                if (!isset($data['data'])) {
78160                      $this->_displayLine('No packages in channel');
78161                      break;
78162                }
78163
78164                $this->_startTable($data);
78165                if (isset($data['headline']) && is_array($data['headline'])) {
78166                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
78167                }
78168
78169                $packages = array();
78170                foreach($data['data'] as $category) {
78171                    foreach($category as $name => $pkg) {
78172                        $packages[$pkg[0]] = $pkg;
78173                    }
78174                }
78175
78176                $p = array_keys($packages);
78177                natcasesort($p);
78178                foreach ($p as $name) {
78179                    $pkg = $packages[$name];
78180                    unset($pkg[4], $pkg[5]);
78181                    $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
78182                }
78183
78184                $this->_endTable();
78185                break;
78186            case 'config-show':
78187                $data['border'] = false;
78188                $opts = array(
78189                    0 => array('wrap' => 30),
78190                    1 => array('wrap' => 20),
78191                    2 => array('wrap' => 35)
78192                );
78193
78194                $this->_startTable($data);
78195                if (isset($data['headline']) && is_array($data['headline'])) {
78196                    $this->_tableRow($data['headline'], array('bold' => true), $opts);
78197                }
78198
78199                foreach ($data['data'] as $group) {
78200                    foreach ($group as $value) {
78201                        if ($value[2] == '') {
78202                            $value[2] = "<not set>";
78203                        }
78204
78205                        $this->_tableRow($value, null, $opts);
78206                    }
78207                }
78208
78209                $this->_endTable();
78210                break;
78211            case 'remote-info':
78212                $d = $data;
78213                $data = array(
78214                    'caption' => 'Package details:',
78215                    'border'  => false,
78216                    'data'    => array(
78217                        array("Latest",      $data['stable']),
78218                        array("Installed",   $data['installed']),
78219                        array("Package",     $data['name']),
78220                        array("License",     $data['license']),
78221                        array("Category",    $data['category']),
78222                        array("Summary",     $data['summary']),
78223                        array("Description", $data['description']),
78224                    ),
78225                );
78226
78227                if (isset($d['deprecated']) && $d['deprecated']) {
78228                    $conf = &PEAR_Config::singleton();
78229                    $reg = $conf->getRegistry();
78230                    $name = $reg->parsedPackageNameToString($d['deprecated'], true);
78231                    $data['data'][] = array('Deprecated! use', $name);
78232                }
78233            default: {
78234                if (is_array($data)) {
78235                    $this->_startTable($data);
78236                    $count = count($data['data'][0]);
78237                    if ($count == 2) {
78238                        $opts = array(0 => array('wrap' => 25),
78239                                      1 => array('wrap' => 48)
78240                        );
78241                    } elseif ($count == 3) {
78242                        $opts = array(0 => array('wrap' => 30),
78243                                      1 => array('wrap' => 20),
78244                                      2 => array('wrap' => 35)
78245                        );
78246                    } else {
78247                        $opts = null;
78248                    }
78249                    if (isset($data['headline']) && is_array($data['headline'])) {
78250                        $this->_tableRow($data['headline'],
78251                                         array('bold' => true),
78252                                         $opts);
78253                    }
78254
78255                    if (is_array($data['data'])) {
78256                        foreach($data['data'] as $row) {
78257                            $this->_tableRow($row, null, $opts);
78258                        }
78259                    } else {
78260                        $this->_tableRow(array($data['data']), null, $opts);
78261                     }
78262                    $this->_endTable();
78263                } else {
78264                    $this->_displayLine($data);
78265                }
78266            }
78267        }
78268    }
78269
78270    function log($text, $append_crlf = true)
78271    {
78272        if ($append_crlf) {
78273            return $this->_displayLine($text);
78274        }
78275
78276        return $this->_display($text);
78277    }
78278
78279    function bold($text)
78280    {
78281        if (empty($this->term['bold'])) {
78282            return strtoupper($text);
78283        }
78284
78285        return $this->term['bold'] . $text . $this->term['normal'];
78286    }
78287
78288    function _displayHeading($title)
78289    {
78290        print $this->lp.$this->bold($title)."\n";
78291        print $this->lp.str_repeat("=", strlen($title))."\n";
78292    }
78293
78294    function _startTable($params = array())
78295    {
78296        $params['table_data'] = array();
78297        $params['widest']     = array();  // indexed by column
78298        $params['highest']    = array(); // indexed by row
78299        $params['ncols']      = 0;
78300        $this->params         = $params;
78301    }
78302
78303    function _tableRow($columns, $rowparams = array(), $colparams = array())
78304    {
78305        $highest = 1;
78306        for ($i = 0; $i < count($columns); $i++) {
78307            $col = &$columns[$i];
78308            if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) {
78309                $col = wordwrap($col, $colparams[$i]['wrap']);
78310            }
78311
78312            if (strpos($col, "\n") !== false) {
78313                $multiline = explode("\n", $col);
78314                $w = 0;
78315                foreach ($multiline as $n => $line) {
78316                    $len = strlen($line);
78317                    if ($len > $w) {
78318                        $w = $len;
78319                    }
78320                }
78321                $lines = count($multiline);
78322            } else {
78323                $w = strlen($col);
78324            }
78325
78326            if (isset($this->params['widest'][$i])) {
78327                if ($w > $this->params['widest'][$i]) {
78328                    $this->params['widest'][$i] = $w;
78329                }
78330            } else {
78331                $this->params['widest'][$i] = $w;
78332            }
78333
78334            $tmp = count_chars($columns[$i], 1);
78335            // handle unix, mac and windows formats
78336            $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1;
78337            if ($lines > $highest) {
78338                $highest = $lines;
78339            }
78340        }
78341
78342        if (count($columns) > $this->params['ncols']) {
78343            $this->params['ncols'] = count($columns);
78344        }
78345
78346        $new_row = array(
78347            'data'      => $columns,
78348            'height'    => $highest,
78349            'rowparams' => $rowparams,
78350            'colparams' => $colparams,
78351        );
78352        $this->params['table_data'][] = $new_row;
78353    }
78354
78355    function _endTable()
78356    {
78357        extract($this->params);
78358        if (!empty($caption)) {
78359            $this->_displayHeading($caption);
78360        }
78361
78362        if (count($table_data) === 0) {
78363            return;
78364        }
78365
78366        if (!isset($width)) {
78367            $width = $widest;
78368        } else {
78369            for ($i = 0; $i < $ncols; $i++) {
78370                if (!isset($width[$i])) {
78371                    $width[$i] = $widest[$i];
78372                }
78373            }
78374        }
78375
78376        $border = false;
78377        if (empty($border)) {
78378            $cellstart  = '';
78379            $cellend    = ' ';
78380            $rowend     = '';
78381            $padrowend  = false;
78382            $borderline = '';
78383        } else {
78384            $cellstart  = '| ';
78385            $cellend    = ' ';
78386            $rowend     = '|';
78387            $padrowend  = true;
78388            $borderline = '+';
78389            foreach ($width as $w) {
78390                $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1);
78391                $borderline .= '+';
78392            }
78393        }
78394
78395        if ($borderline) {
78396            $this->_displayLine($borderline);
78397        }
78398
78399        for ($i = 0; $i < count($table_data); $i++) {
78400            extract($table_data[$i]);
78401            if (!is_array($rowparams)) {
78402                $rowparams = array();
78403            }
78404
78405            if (!is_array($colparams)) {
78406                $colparams = array();
78407            }
78408
78409            $rowlines = array();
78410            if ($height > 1) {
78411                for ($c = 0; $c < count($data); $c++) {
78412                    $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]);
78413                    if (count($rowlines[$c]) < $height) {
78414                        $rowlines[$c] = array_pad($rowlines[$c], $height, '');
78415                    }
78416                }
78417            } else {
78418                for ($c = 0; $c < count($data); $c++) {
78419                    $rowlines[$c] = array($data[$c]);
78420                }
78421            }
78422
78423            for ($r = 0; $r < $height; $r++) {
78424                $rowtext = '';
78425                for ($c = 0; $c < count($data); $c++) {
78426                    if (isset($colparams[$c])) {
78427                        $attribs = array_merge($rowparams, $colparams);
78428                    } else {
78429                        $attribs = $rowparams;
78430                    }
78431
78432                    $w = isset($width[$c]) ? $width[$c] : 0;
78433                    //$cell = $data[$c];
78434                    $cell = $rowlines[$c][$r];
78435                    $l = strlen($cell);
78436                    if ($l > $w) {
78437                        $cell = substr($cell, 0, $w);
78438                    }
78439
78440                    if (isset($attribs['bold'])) {
78441                        $cell = $this->bold($cell);
78442                    }
78443
78444                    if ($l < $w) {
78445                        // not using str_pad here because we may
78446                        // add bold escape characters to $cell
78447                        $cell .= str_repeat(' ', $w - $l);
78448                    }
78449
78450                    $rowtext .= $cellstart . $cell . $cellend;
78451                }
78452
78453                if (!$border) {
78454                    $rowtext = rtrim($rowtext);
78455                }
78456
78457                $rowtext .= $rowend;
78458                $this->_displayLine($rowtext);
78459            }
78460        }
78461
78462        if ($borderline) {
78463            $this->_displayLine($borderline);
78464        }
78465    }
78466
78467    function _displayLine($text)
78468    {
78469        print "$this->lp$text\n";
78470    }
78471
78472    function _display($text)
78473    {
78474        print $text;
78475    }
78476}<?php
78477/**
78478 * PEAR_Installer
78479 *
78480 * PHP versions 4 and 5
78481 *
78482 * @category   pear
78483 * @package    PEAR
78484 * @author     Stig Bakken <ssb@php.net>
78485 * @author     Tomas V.V. Cox <cox@idecnet.com>
78486 * @author     Martin Jansen <mj@php.net>
78487 * @author     Greg Beaver <cellog@php.net>
78488 * @copyright  1997-2009 The Authors
78489 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
78490 * @version    CVS: $Id: Installer.php 313024 2011-07-06 19:51:24Z dufuz $
78491 * @link       http://pear.php.net/package/PEAR
78492 * @since      File available since Release 0.1
78493 */
78494
78495/**
78496 * Used for installation groups in package.xml 2.0 and platform exceptions
78497 */
78498require_once 'phar://install-pear-nozlib.phar/' . 'OS/Guess.php';
78499require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Downloader.php';
78500
78501define('PEAR_INSTALLER_NOBINARY', -240);
78502/**
78503 * Administration class used to install PEAR packages and maintain the
78504 * installed package database.
78505 *
78506 * @category   pear
78507 * @package    PEAR
78508 * @author     Stig Bakken <ssb@php.net>
78509 * @author     Tomas V.V. Cox <cox@idecnet.com>
78510 * @author     Martin Jansen <mj@php.net>
78511 * @author     Greg Beaver <cellog@php.net>
78512 * @copyright  1997-2009 The Authors
78513 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
78514 * @version    Release: 1.9.4
78515 * @link       http://pear.php.net/package/PEAR
78516 * @since      Class available since Release 0.1
78517 */
78518class PEAR_Installer extends PEAR_Downloader
78519{
78520    // {{{ properties
78521
78522    /** name of the package directory, for example Foo-1.0
78523     * @var string
78524     */
78525    var $pkgdir;
78526
78527    /** directory where PHP code files go
78528     * @var string
78529     */
78530    var $phpdir;
78531
78532    /** directory where PHP extension files go
78533     * @var string
78534     */
78535    var $extdir;
78536
78537    /** directory where documentation goes
78538     * @var string
78539     */
78540    var $docdir;
78541
78542    /** installation root directory (ala PHP's INSTALL_ROOT or
78543     * automake's DESTDIR
78544     * @var string
78545     */
78546    var $installroot = '';
78547
78548    /** debug level
78549     * @var int
78550     */
78551    var $debug = 1;
78552
78553    /** temporary directory
78554     * @var string
78555     */
78556    var $tmpdir;
78557
78558    /**
78559     * PEAR_Registry object used by the installer
78560     * @var PEAR_Registry
78561     */
78562    var $registry;
78563
78564    /**
78565     * array of PEAR_Downloader_Packages
78566     * @var array
78567     */
78568    var $_downloadedPackages;
78569
78570    /** List of file transactions queued for an install/upgrade/uninstall.
78571     *
78572     *  Format:
78573     *    array(
78574     *      0 => array("rename => array("from-file", "to-file")),
78575     *      1 => array("delete" => array("file-to-delete")),
78576     *      ...
78577     *    )
78578     *
78579     * @var array
78580     */
78581    var $file_operations = array();
78582
78583    // }}}
78584
78585    // {{{ constructor
78586
78587    /**
78588     * PEAR_Installer constructor.
78589     *
78590     * @param object $ui user interface object (instance of PEAR_Frontend_*)
78591     *
78592     * @access public
78593     */
78594    function PEAR_Installer(&$ui)
78595    {
78596        parent::PEAR_Common();
78597        $this->setFrontendObject($ui);
78598        $this->debug = $this->config->get('verbose');
78599    }
78600
78601    function setOptions($options)
78602    {
78603        $this->_options = $options;
78604    }
78605
78606    function setConfig(&$config)
78607    {
78608        $this->config    = &$config;
78609        $this->_registry = &$config->getRegistry();
78610    }
78611
78612    // }}}
78613
78614    function _removeBackups($files)
78615    {
78616        foreach ($files as $path) {
78617            $this->addFileOperation('removebackup', array($path));
78618        }
78619    }
78620
78621    // {{{ _deletePackageFiles()
78622
78623    /**
78624     * Delete a package's installed files, does not remove empty directories.
78625     *
78626     * @param string package name
78627     * @param string channel name
78628     * @param bool if true, then files are backed up first
78629     * @return bool TRUE on success, or a PEAR error on failure
78630     * @access protected
78631     */
78632    function _deletePackageFiles($package, $channel = false, $backup = false)
78633    {
78634        if (!$channel) {
78635            $channel = 'pear.php.net';
78636        }
78637
78638        if (!strlen($package)) {
78639            return $this->raiseError("No package to uninstall given");
78640        }
78641
78642        if (strtolower($package) == 'pear' && $channel == 'pear.php.net') {
78643            // to avoid race conditions, include all possible needed files
78644            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Common.php';
78645            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Replace.php';
78646            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Unixeol.php';
78647            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Windowseol.php';
78648            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
78649            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
78650            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/Generator/v1.php';
78651            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/Generator/v2.php';
78652        }
78653
78654        $filelist = $this->_registry->packageInfo($package, 'filelist', $channel);
78655        if ($filelist == null) {
78656            return $this->raiseError("$channel/$package not installed");
78657        }
78658
78659        $ret = array();
78660        foreach ($filelist as $file => $props) {
78661            if (empty($props['installed_as'])) {
78662                continue;
78663            }
78664
78665            $path = $props['installed_as'];
78666            if ($backup) {
78667                $this->addFileOperation('backup', array($path));
78668                $ret[] = $path;
78669            }
78670
78671            $this->addFileOperation('delete', array($path));
78672        }
78673
78674        if ($backup) {
78675            return $ret;
78676        }
78677
78678        return true;
78679    }
78680
78681    // }}}
78682    // {{{ _installFile()
78683
78684    /**
78685     * @param string filename
78686     * @param array attributes from <file> tag in package.xml
78687     * @param string path to install the file in
78688     * @param array options from command-line
78689     * @access private
78690     */
78691    function _installFile($file, $atts, $tmp_path, $options)
78692    {
78693        // {{{ return if this file is meant for another platform
78694        static $os;
78695        if (!isset($this->_registry)) {
78696            $this->_registry = &$this->config->getRegistry();
78697        }
78698
78699        if (isset($atts['platform'])) {
78700            if (empty($os)) {
78701                $os = new OS_Guess();
78702            }
78703
78704            if (strlen($atts['platform']) && $atts['platform']{0} == '!') {
78705                $negate   = true;
78706                $platform = substr($atts['platform'], 1);
78707            } else {
78708                $negate    = false;
78709                $platform = $atts['platform'];
78710            }
78711
78712            if ((bool) $os->matchSignature($platform) === $negate) {
78713                $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
78714                return PEAR_INSTALLER_SKIPPED;
78715            }
78716        }
78717        // }}}
78718
78719        $channel = $this->pkginfo->getChannel();
78720        // {{{ assemble the destination paths
78721        switch ($atts['role']) {
78722            case 'src':
78723            case 'extsrc':
78724                $this->source_files++;
78725                return;
78726            case 'doc':
78727            case 'data':
78728            case 'test':
78729                $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) .
78730                            DIRECTORY_SEPARATOR . $this->pkginfo->getPackage();
78731                unset($atts['baseinstalldir']);
78732                break;
78733            case 'ext':
78734            case 'php':
78735                $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel);
78736                break;
78737            case 'script':
78738                $dest_dir = $this->config->get('bin_dir', null, $channel);
78739                break;
78740            default:
78741                return $this->raiseError("Invalid role `$atts[role]' for file $file");
78742        }
78743
78744        $save_destdir = $dest_dir;
78745        if (!empty($atts['baseinstalldir'])) {
78746            $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
78747        }
78748
78749        if (dirname($file) != '.' && empty($atts['install-as'])) {
78750            $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
78751        }
78752
78753        if (empty($atts['install-as'])) {
78754            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
78755        } else {
78756            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
78757        }
78758        $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
78759
78760        // Clean up the DIRECTORY_SEPARATOR mess
78761        $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
78762        list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
78763                                                    array(DIRECTORY_SEPARATOR,
78764                                                          DIRECTORY_SEPARATOR,
78765                                                          DIRECTORY_SEPARATOR),
78766                                                    array($dest_file, $orig_file));
78767        $final_dest_file = $installed_as = $dest_file;
78768        if (isset($this->_options['packagingroot'])) {
78769            $installedas_dest_dir  = dirname($final_dest_file);
78770            $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
78771            $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']);
78772        } else {
78773            $installedas_dest_dir  = dirname($final_dest_file);
78774            $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
78775        }
78776
78777        $dest_dir  = dirname($final_dest_file);
78778        $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
78779        if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
78780            return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
78781        }
78782        // }}}
78783
78784        if (empty($this->_options['register-only']) &&
78785              (!file_exists($dest_dir) || !is_dir($dest_dir))) {
78786            if (!$this->mkDirHier($dest_dir)) {
78787                return $this->raiseError("failed to mkdir $dest_dir",
78788                                         PEAR_INSTALLER_FAILED);
78789            }
78790            $this->log(3, "+ mkdir $dest_dir");
78791        }
78792
78793        // pretty much nothing happens if we are only registering the install
78794        if (empty($this->_options['register-only'])) {
78795            if (empty($atts['replacements'])) {
78796                if (!file_exists($orig_file)) {
78797                    return $this->raiseError("file $orig_file does not exist",
78798                                             PEAR_INSTALLER_FAILED);
78799                }
78800
78801                if (!@copy($orig_file, $dest_file)) {
78802                    return $this->raiseError("failed to write $dest_file: $php_errormsg",
78803                                             PEAR_INSTALLER_FAILED);
78804                }
78805
78806                $this->log(3, "+ cp $orig_file $dest_file");
78807                if (isset($atts['md5sum'])) {
78808                    $md5sum = md5_file($dest_file);
78809                }
78810            } else {
78811                // {{{ file with replacements
78812                if (!file_exists($orig_file)) {
78813                    return $this->raiseError("file does not exist",
78814                                             PEAR_INSTALLER_FAILED);
78815                }
78816
78817                $contents = file_get_contents($orig_file);
78818                if ($contents === false) {
78819                    $contents = '';
78820                }
78821
78822                if (isset($atts['md5sum'])) {
78823                    $md5sum = md5($contents);
78824                }
78825
78826                $subst_from = $subst_to = array();
78827                foreach ($atts['replacements'] as $a) {
78828                    $to = '';
78829                    if ($a['type'] == 'php-const') {
78830                        if (preg_match('/^[a-z0-9_]+\\z/i', $a['to'])) {
78831                            eval("\$to = $a[to];");
78832                        } else {
78833                            if (!isset($options['soft'])) {
78834                                $this->log(0, "invalid php-const replacement: $a[to]");
78835                            }
78836                            continue;
78837                        }
78838                    } elseif ($a['type'] == 'pear-config') {
78839                        if ($a['to'] == 'master_server') {
78840                            $chan = $this->_registry->getChannel($channel);
78841                            if (!PEAR::isError($chan)) {
78842                                $to = $chan->getServer();
78843                            } else {
78844                                $to = $this->config->get($a['to'], null, $channel);
78845                            }
78846                        } else {
78847                            $to = $this->config->get($a['to'], null, $channel);
78848                        }
78849                        if (is_null($to)) {
78850                            if (!isset($options['soft'])) {
78851                                $this->log(0, "invalid pear-config replacement: $a[to]");
78852                            }
78853                            continue;
78854                        }
78855                    } elseif ($a['type'] == 'package-info') {
78856                        if ($t = $this->pkginfo->packageInfo($a['to'])) {
78857                            $to = $t;
78858                        } else {
78859                            if (!isset($options['soft'])) {
78860                                $this->log(0, "invalid package-info replacement: $a[to]");
78861                            }
78862                            continue;
78863                        }
78864                    }
78865                    if (!is_null($to)) {
78866                        $subst_from[] = $a['from'];
78867                        $subst_to[] = $to;
78868                    }
78869                }
78870
78871                $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
78872                if (sizeof($subst_from)) {
78873                    $contents = str_replace($subst_from, $subst_to, $contents);
78874                }
78875
78876                $wp = @fopen($dest_file, "wb");
78877                if (!is_resource($wp)) {
78878                    return $this->raiseError("failed to create $dest_file: $php_errormsg",
78879                                             PEAR_INSTALLER_FAILED);
78880                }
78881
78882                if (@fwrite($wp, $contents) === false) {
78883                    return $this->raiseError("failed writing to $dest_file: $php_errormsg",
78884                                             PEAR_INSTALLER_FAILED);
78885                }
78886
78887                fclose($wp);
78888                // }}}
78889            }
78890
78891            // {{{ check the md5
78892            if (isset($md5sum)) {
78893                if (strtolower($md5sum) === strtolower($atts['md5sum'])) {
78894                    $this->log(2, "md5sum ok: $final_dest_file");
78895                } else {
78896                    if (empty($options['force'])) {
78897                        // delete the file
78898                        if (file_exists($dest_file)) {
78899                            unlink($dest_file);
78900                        }
78901
78902                        if (!isset($options['ignore-errors'])) {
78903                            return $this->raiseError("bad md5sum for file $final_dest_file",
78904                                                 PEAR_INSTALLER_FAILED);
78905                        }
78906
78907                        if (!isset($options['soft'])) {
78908                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
78909                        }
78910                    } else {
78911                        if (!isset($options['soft'])) {
78912                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
78913                        }
78914                    }
78915                }
78916            }
78917            // }}}
78918            // {{{ set file permissions
78919            if (!OS_WINDOWS) {
78920                if ($atts['role'] == 'script') {
78921                    $mode = 0777 & ~(int)octdec($this->config->get('umask'));
78922                    $this->log(3, "+ chmod +x $dest_file");
78923                } else {
78924                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
78925                }
78926
78927                if ($atts['role'] != 'src') {
78928                    $this->addFileOperation("chmod", array($mode, $dest_file));
78929                    if (!@chmod($dest_file, $mode)) {
78930                        if (!isset($options['soft'])) {
78931                            $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
78932                        }
78933                    }
78934                }
78935            }
78936            // }}}
78937
78938            if ($atts['role'] == 'src') {
78939                rename($dest_file, $final_dest_file);
78940                $this->log(2, "renamed source file $dest_file to $final_dest_file");
78941            } else {
78942                $this->addFileOperation("rename", array($dest_file, $final_dest_file,
78943                    $atts['role'] == 'ext'));
78944            }
78945        }
78946
78947        // Store the full path where the file was installed for easy unistall
78948        if ($atts['role'] != 'script') {
78949            $loc = $this->config->get($atts['role'] . '_dir');
78950        } else {
78951            $loc = $this->config->get('bin_dir');
78952        }
78953
78954        if ($atts['role'] != 'src') {
78955            $this->addFileOperation("installed_as", array($file, $installed_as,
78956                                    $loc,
78957                                    dirname(substr($installedas_dest_file, strlen($loc)))));
78958        }
78959
78960        //$this->log(2, "installed: $dest_file");
78961        return PEAR_INSTALLER_OK;
78962    }
78963
78964    // }}}
78965    // {{{ _installFile2()
78966
78967    /**
78968     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
78969     * @param string filename
78970     * @param array attributes from <file> tag in package.xml
78971     * @param string path to install the file in
78972     * @param array options from command-line
78973     * @access private
78974     */
78975    function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options)
78976    {
78977        $atts = $real_atts;
78978        if (!isset($this->_registry)) {
78979            $this->_registry = &$this->config->getRegistry();
78980        }
78981
78982        $channel = $pkg->getChannel();
78983        // {{{ assemble the destination paths
78984        if (!in_array($atts['attribs']['role'],
78985              PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
78986            return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
78987                    "' for file $file");
78988        }
78989
78990        $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config);
78991        $err  = $role->setup($this, $pkg, $atts['attribs'], $file);
78992        if (PEAR::isError($err)) {
78993            return $err;
78994        }
78995
78996        if (!$role->isInstallable()) {
78997            return;
78998        }
78999
79000        $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path);
79001        if (PEAR::isError($info)) {
79002            return $info;
79003        }
79004
79005        list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info;
79006        if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
79007            return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
79008        }
79009
79010        $final_dest_file = $installed_as = $dest_file;
79011        if (isset($this->_options['packagingroot'])) {
79012            $final_dest_file = $this->_prependPath($final_dest_file,
79013                $this->_options['packagingroot']);
79014        }
79015
79016        $dest_dir  = dirname($final_dest_file);
79017        $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
79018        // }}}
79019
79020        if (empty($this->_options['register-only'])) {
79021            if (!file_exists($dest_dir) || !is_dir($dest_dir)) {
79022                if (!$this->mkDirHier($dest_dir)) {
79023                    return $this->raiseError("failed to mkdir $dest_dir",
79024                                             PEAR_INSTALLER_FAILED);
79025                }
79026                $this->log(3, "+ mkdir $dest_dir");
79027            }
79028        }
79029
79030        $attribs = $atts['attribs'];
79031        unset($atts['attribs']);
79032        // pretty much nothing happens if we are only registering the install
79033        if (empty($this->_options['register-only'])) {
79034            if (!count($atts)) { // no tasks
79035                if (!file_exists($orig_file)) {
79036                    return $this->raiseError("file $orig_file does not exist",
79037                                             PEAR_INSTALLER_FAILED);
79038                }
79039
79040                if (!@copy($orig_file, $dest_file)) {
79041                    return $this->raiseError("failed to write $dest_file: $php_errormsg",
79042                                             PEAR_INSTALLER_FAILED);
79043                }
79044
79045                $this->log(3, "+ cp $orig_file $dest_file");
79046                if (isset($attribs['md5sum'])) {
79047                    $md5sum = md5_file($dest_file);
79048                }
79049            } else { // file with tasks
79050                if (!file_exists($orig_file)) {
79051                    return $this->raiseError("file $orig_file does not exist",
79052                                             PEAR_INSTALLER_FAILED);
79053                }
79054
79055                $contents = file_get_contents($orig_file);
79056                if ($contents === false) {
79057                    $contents = '';
79058                }
79059
79060                if (isset($attribs['md5sum'])) {
79061                    $md5sum = md5($contents);
79062                }
79063
79064                foreach ($atts as $tag => $raw) {
79065                    $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), array('', '_'), $tag);
79066                    $task = "PEAR_Task_$tag";
79067                    $task = &new $task($this->config, $this, PEAR_TASK_INSTALL);
79068                    if (!$task->isScript()) { // scripts are only handled after installation
79069                        $task->init($raw, $attribs, $pkg->getLastInstalledVersion());
79070                        $res = $task->startSession($pkg, $contents, $final_dest_file);
79071                        if ($res === false) {
79072                            continue; // skip this file
79073                        }
79074
79075                        if (PEAR::isError($res)) {
79076                            return $res;
79077                        }
79078
79079                        $contents = $res; // save changes
79080                    }
79081
79082                    $wp = @fopen($dest_file, "wb");
79083                    if (!is_resource($wp)) {
79084                        return $this->raiseError("failed to create $dest_file: $php_errormsg",
79085                                                 PEAR_INSTALLER_FAILED);
79086                    }
79087
79088                    if (fwrite($wp, $contents) === false) {
79089                        return $this->raiseError("failed writing to $dest_file: $php_errormsg",
79090                                                 PEAR_INSTALLER_FAILED);
79091                    }
79092
79093                    fclose($wp);
79094                }
79095            }
79096
79097            // {{{ check the md5
79098            if (isset($md5sum)) {
79099                // Make sure the original md5 sum matches with expected
79100                if (strtolower($md5sum) === strtolower($attribs['md5sum'])) {
79101                    $this->log(2, "md5sum ok: $final_dest_file");
79102
79103                    if (isset($contents)) {
79104                        // set md5 sum based on $content in case any tasks were run.
79105                        $real_atts['attribs']['md5sum'] = md5($contents);
79106                    }
79107                } else {
79108                    if (empty($options['force'])) {
79109                        // delete the file
79110                        if (file_exists($dest_file)) {
79111                            unlink($dest_file);
79112                        }
79113
79114                        if (!isset($options['ignore-errors'])) {
79115                            return $this->raiseError("bad md5sum for file $final_dest_file",
79116                                                     PEAR_INSTALLER_FAILED);
79117                        }
79118
79119                        if (!isset($options['soft'])) {
79120                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
79121                        }
79122                    } else {
79123                        if (!isset($options['soft'])) {
79124                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
79125                        }
79126                    }
79127                }
79128            } else {
79129                $real_atts['attribs']['md5sum'] = md5_file($dest_file);
79130            }
79131
79132            // }}}
79133            // {{{ set file permissions
79134            if (!OS_WINDOWS) {
79135                if ($role->isExecutable()) {
79136                    $mode = 0777 & ~(int)octdec($this->config->get('umask'));
79137                    $this->log(3, "+ chmod +x $dest_file");
79138                } else {
79139                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
79140                }
79141
79142                if ($attribs['role'] != 'src') {
79143                    $this->addFileOperation("chmod", array($mode, $dest_file));
79144                    if (!@chmod($dest_file, $mode)) {
79145                        if (!isset($options['soft'])) {
79146                            $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
79147                        }
79148                    }
79149                }
79150            }
79151            // }}}
79152
79153            if ($attribs['role'] == 'src') {
79154                rename($dest_file, $final_dest_file);
79155                $this->log(2, "renamed source file $dest_file to $final_dest_file");
79156            } else {
79157                $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension()));
79158            }
79159        }
79160
79161        // Store the full path where the file was installed for easy uninstall
79162        if ($attribs['role'] != 'src') {
79163            $loc = $this->config->get($role->getLocationConfig(), null, $channel);
79164            $this->addFileOperation('installed_as', array($file, $installed_as,
79165                                $loc,
79166                                dirname(substr($installed_as, strlen($loc)))));
79167        }
79168
79169        //$this->log(2, "installed: $dest_file");
79170        return PEAR_INSTALLER_OK;
79171    }
79172
79173    // }}}
79174    // {{{ addFileOperation()
79175
79176    /**
79177     * Add a file operation to the current file transaction.
79178     *
79179     * @see startFileTransaction()
79180     * @param string $type This can be one of:
79181     *    - rename:  rename a file ($data has 3 values)
79182     *    - backup:  backup an existing file ($data has 1 value)
79183     *    - removebackup:  clean up backups created during install ($data has 1 value)
79184     *    - chmod:   change permissions on a file ($data has 2 values)
79185     *    - delete:  delete a file ($data has 1 value)
79186     *    - rmdir:   delete a directory if empty ($data has 1 value)
79187     *    - installed_as: mark a file as installed ($data has 4 values).
79188     * @param array $data For all file operations, this array must contain the
79189     *    full path to the file or directory that is being operated on.  For
79190     *    the rename command, the first parameter must be the file to rename,
79191     *    the second its new name, the third whether this is a PHP extension.
79192     *
79193     *    The installed_as operation contains 4 elements in this order:
79194     *    1. Filename as listed in the filelist element from package.xml
79195     *    2. Full path to the installed file
79196     *    3. Full path from the php_dir configuration variable used in this
79197     *       installation
79198     *    4. Relative path from the php_dir that this file is installed in
79199     */
79200    function addFileOperation($type, $data)
79201    {
79202        if (!is_array($data)) {
79203            return $this->raiseError('Internal Error: $data in addFileOperation'
79204                . ' must be an array, was ' . gettype($data));
79205        }
79206
79207        if ($type == 'chmod') {
79208            $octmode = decoct($data[0]);
79209            $this->log(3, "adding to transaction: $type $octmode $data[1]");
79210        } else {
79211            $this->log(3, "adding to transaction: $type " . implode(" ", $data));
79212        }
79213        $this->file_operations[] = array($type, $data);
79214    }
79215
79216    // }}}
79217    // {{{ startFileTransaction()
79218
79219    function startFileTransaction($rollback_in_case = false)
79220    {
79221        if (count($this->file_operations) && $rollback_in_case) {
79222            $this->rollbackFileTransaction();
79223        }
79224        $this->file_operations = array();
79225    }
79226
79227    // }}}
79228    // {{{ commitFileTransaction()
79229
79230    function commitFileTransaction()
79231    {
79232        // {{{ first, check permissions and such manually
79233        $errors = array();
79234        foreach ($this->file_operations as $key => $tr) {
79235            list($type, $data) = $tr;
79236            switch ($type) {
79237                case 'rename':
79238                    if (!file_exists($data[0])) {
79239                        $errors[] = "cannot rename file $data[0], doesn't exist";
79240                    }
79241
79242                    // check that dest dir. is writable
79243                    if (!is_writable(dirname($data[1]))) {
79244                        $errors[] = "permission denied ($type): $data[1]";
79245                    }
79246                    break;
79247                case 'chmod':
79248                    // check that file is writable
79249                    if (!is_writable($data[1])) {
79250                        $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
79251                    }
79252                    break;
79253                case 'delete':
79254                    if (!file_exists($data[0])) {
79255                        $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
79256                    }
79257                    // check that directory is writable
79258                    if (file_exists($data[0])) {
79259                        if (!is_writable(dirname($data[0]))) {
79260                            $errors[] = "permission denied ($type): $data[0]";
79261                        } else {
79262                            // make sure the file to be deleted can be opened for writing
79263                            $fp = false;
79264                            if (!is_dir($data[0]) &&
79265                                  (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) {
79266                                $errors[] = "permission denied ($type): $data[0]";
79267                            } elseif ($fp) {
79268                                fclose($fp);
79269                            }
79270                        }
79271
79272                        /* Verify we are not deleting a file owned by another package
79273                         * This can happen when a file moves from package A to B in
79274                         * an upgrade ala http://pear.php.net/17986
79275                         */
79276                        $info = array(
79277                            'package' => strtolower($this->pkginfo->getName()),
79278                            'channel' => strtolower($this->pkginfo->getChannel()),
79279                        );
79280                        $result = $this->_registry->checkFileMap($data[0], $info, '1.1');
79281                        if (is_array($result)) {
79282                            $res = array_diff($result, $info);
79283                            if (!empty($res)) {
79284                                $new = $this->_registry->getPackage($result[1], $result[0]);
79285                                $this->file_operations[$key] = false;
79286                                $this->log(3, "file $data[0] was scheduled for removal from {$this->pkginfo->getName()} but is owned by {$new->getChannel()}/{$new->getName()}, removal has been cancelled.");
79287                            }
79288                        }
79289                    }
79290                    break;
79291            }
79292
79293        }
79294        // }}}
79295
79296        $n = count($this->file_operations);
79297        $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName());
79298
79299        $m = count($errors);
79300        if ($m > 0) {
79301            foreach ($errors as $error) {
79302                if (!isset($this->_options['soft'])) {
79303                    $this->log(1, $error);
79304                }
79305            }
79306
79307            if (!isset($this->_options['ignore-errors'])) {
79308                return false;
79309            }
79310        }
79311
79312        $this->_dirtree = array();
79313        // {{{ really commit the transaction
79314        foreach ($this->file_operations as $i => $tr) {
79315            if (!$tr) {
79316                // support removal of non-existing backups
79317                continue;
79318            }
79319
79320            list($type, $data) = $tr;
79321            switch ($type) {
79322                case 'backup':
79323                    if (!file_exists($data[0])) {
79324                        $this->file_operations[$i] = false;
79325                        break;
79326                    }
79327
79328                    if (!@copy($data[0], $data[0] . '.bak')) {
79329                        $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] .
79330                            '.bak ' . $php_errormsg);
79331                        return false;
79332                    }
79333                    $this->log(3, "+ backup $data[0] to $data[0].bak");
79334                    break;
79335                case 'removebackup':
79336                    if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
79337                        unlink($data[0] . '.bak');
79338                        $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
79339                    }
79340                    break;
79341                case 'rename':
79342                    $test = file_exists($data[1]) ? @unlink($data[1]) : null;
79343                    if (!$test && file_exists($data[1])) {
79344                        if ($data[2]) {
79345                            $extra = ', this extension must be installed manually.  Rename to "' .
79346                                basename($data[1]) . '"';
79347                        } else {
79348                            $extra = '';
79349                        }
79350
79351                        if (!isset($this->_options['soft'])) {
79352                            $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' .
79353                                $data[0] . $extra);
79354                        }
79355
79356                        if (!isset($this->_options['ignore-errors'])) {
79357                            return false;
79358                        }
79359                    }
79360
79361                    // permissions issues with rename - copy() is far superior
79362                    $perms = @fileperms($data[0]);
79363                    if (!@copy($data[0], $data[1])) {
79364                        $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] .
79365                            ' ' . $php_errormsg);
79366                        return false;
79367                    }
79368
79369                    // copy over permissions, otherwise they are lost
79370                    @chmod($data[1], $perms);
79371                    @unlink($data[0]);
79372                    $this->log(3, "+ mv $data[0] $data[1]");
79373                    break;
79374                case 'chmod':
79375                    if (!@chmod($data[1], $data[0])) {
79376                        $this->log(1, 'Could not chmod ' . $data[1] . ' to ' .
79377                            decoct($data[0]) . ' ' . $php_errormsg);
79378                        return false;
79379                    }
79380
79381                    $octmode = decoct($data[0]);
79382                    $this->log(3, "+ chmod $octmode $data[1]");
79383                    break;
79384                case 'delete':
79385                    if (file_exists($data[0])) {
79386                        if (!@unlink($data[0])) {
79387                            $this->log(1, 'Could not delete ' . $data[0] . ' ' .
79388                                $php_errormsg);
79389                            return false;
79390                        }
79391                        $this->log(3, "+ rm $data[0]");
79392                    }
79393                    break;
79394                case 'rmdir':
79395                    if (file_exists($data[0])) {
79396                        do {
79397                            $testme = opendir($data[0]);
79398                            while (false !== ($entry = readdir($testme))) {
79399                                if ($entry == '.' || $entry == '..') {
79400                                    continue;
79401                                }
79402                                closedir($testme);
79403                                break 2; // this directory is not empty and can't be
79404                                         // deleted
79405                            }
79406
79407                            closedir($testme);
79408                            if (!@rmdir($data[0])) {
79409                                $this->log(1, 'Could not rmdir ' . $data[0] . ' ' .
79410                                    $php_errormsg);
79411                                return false;
79412                            }
79413                            $this->log(3, "+ rmdir $data[0]");
79414                        } while (false);
79415                    }
79416                    break;
79417                case 'installed_as':
79418                    $this->pkginfo->setInstalledAs($data[0], $data[1]);
79419                    if (!isset($this->_dirtree[dirname($data[1])])) {
79420                        $this->_dirtree[dirname($data[1])] = true;
79421                        $this->pkginfo->setDirtree(dirname($data[1]));
79422
79423                        while(!empty($data[3]) && dirname($data[3]) != $data[3] &&
79424                                $data[3] != '/' && $data[3] != '\\') {
79425                            $this->pkginfo->setDirtree($pp =
79426                                $this->_prependPath($data[3], $data[2]));
79427                            $this->_dirtree[$pp] = true;
79428                            $data[3] = dirname($data[3]);
79429                        }
79430                    }
79431                    break;
79432            }
79433        }
79434        // }}}
79435        $this->log(2, "successfully committed $n file operations");
79436        $this->file_operations = array();
79437        return true;
79438    }
79439
79440    // }}}
79441    // {{{ rollbackFileTransaction()
79442
79443    function rollbackFileTransaction()
79444    {
79445        $n = count($this->file_operations);
79446        $this->log(2, "rolling back $n file operations");
79447        foreach ($this->file_operations as $tr) {
79448            list($type, $data) = $tr;
79449            switch ($type) {
79450                case 'backup':
79451                    if (file_exists($data[0] . '.bak')) {
79452                        if (file_exists($data[0] && is_writable($data[0]))) {
79453                            unlink($data[0]);
79454                        }
79455                        @copy($data[0] . '.bak', $data[0]);
79456                        $this->log(3, "+ restore $data[0] from $data[0].bak");
79457                    }
79458                    break;
79459                case 'removebackup':
79460                    if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
79461                        unlink($data[0] . '.bak');
79462                        $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
79463                    }
79464                    break;
79465                case 'rename':
79466                    @unlink($data[0]);
79467                    $this->log(3, "+ rm $data[0]");
79468                    break;
79469                case 'mkdir':
79470                    @rmdir($data[0]);
79471                    $this->log(3, "+ rmdir $data[0]");
79472                    break;
79473                case 'chmod':
79474                    break;
79475                case 'delete':
79476                    break;
79477                case 'installed_as':
79478                    $this->pkginfo->setInstalledAs($data[0], false);
79479                    break;
79480            }
79481        }
79482        $this->pkginfo->resetDirtree();
79483        $this->file_operations = array();
79484    }
79485
79486    // }}}
79487    // {{{ mkDirHier($dir)
79488
79489    function mkDirHier($dir)
79490    {
79491        $this->addFileOperation('mkdir', array($dir));
79492        return parent::mkDirHier($dir);
79493    }
79494
79495    // }}}
79496    // {{{ download()
79497
79498    /**
79499     * Download any files and their dependencies, if necessary
79500     *
79501     * @param array a mixed list of package names, local files, or package.xml
79502     * @param PEAR_Config
79503     * @param array options from the command line
79504     * @param array this is the array that will be populated with packages to
79505     *              install.  Format of each entry:
79506     *
79507     * <code>
79508     * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
79509     *    'info' => array() // parsed package.xml
79510     * );
79511     * </code>
79512     * @param array this will be populated with any error messages
79513     * @param false private recursion variable
79514     * @param false private recursion variable
79515     * @param false private recursion variable
79516     * @deprecated in favor of PEAR_Downloader
79517     */
79518    function download($packages, $options, &$config, &$installpackages,
79519                      &$errors, $installed = false, $willinstall = false, $state = false)
79520    {
79521        // trickiness: initialize here
79522        parent::PEAR_Downloader($this->ui, $options, $config);
79523        $ret             = parent::download($packages);
79524        $errors          = $this->getErrorMsgs();
79525        $installpackages = $this->getDownloadedPackages();
79526        trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " .
79527                      "in favor of PEAR_Downloader class", E_USER_WARNING);
79528        return $ret;
79529    }
79530
79531    // }}}
79532    // {{{ _parsePackageXml()
79533
79534    function _parsePackageXml(&$descfile)
79535    {
79536        // Parse xml file -----------------------------------------------
79537        $pkg = new PEAR_PackageFile($this->config, $this->debug);
79538        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
79539        $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
79540        PEAR::staticPopErrorHandling();
79541        if (PEAR::isError($p)) {
79542            if (is_array($p->getUserInfo())) {
79543                foreach ($p->getUserInfo() as $err) {
79544                    $loglevel = $err['level'] == 'error' ? 0 : 1;
79545                    if (!isset($this->_options['soft'])) {
79546                        $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']);
79547                    }
79548                }
79549            }
79550            return $this->raiseError('Installation failed: invalid package file');
79551        }
79552
79553        $descfile = $p->getPackageFile();
79554        return $p;
79555    }
79556
79557    // }}}
79558    /**
79559     * Set the list of PEAR_Downloader_Package objects to allow more sane
79560     * dependency validation
79561     * @param array
79562     */
79563    function setDownloadedPackages(&$pkgs)
79564    {
79565        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
79566        $err = $this->analyzeDependencies($pkgs);
79567        PEAR::popErrorHandling();
79568        if (PEAR::isError($err)) {
79569            return $err;
79570        }
79571        $this->_downloadedPackages = &$pkgs;
79572    }
79573
79574    /**
79575     * Set the list of PEAR_Downloader_Package objects to allow more sane
79576     * dependency validation
79577     * @param array
79578     */
79579    function setUninstallPackages(&$pkgs)
79580    {
79581        $this->_downloadedPackages = &$pkgs;
79582    }
79583
79584    function getInstallPackages()
79585    {
79586        return $this->_downloadedPackages;
79587    }
79588
79589    // {{{ install()
79590
79591    /**
79592     * Installs the files within the package file specified.
79593     *
79594     * @param string|PEAR_Downloader_Package $pkgfile path to the package file,
79595     *        or a pre-initialized packagefile object
79596     * @param array $options
79597     * recognized options:
79598     * - installroot   : optional prefix directory for installation
79599     * - force         : force installation
79600     * - register-only : update registry but don't install files
79601     * - upgrade       : upgrade existing install
79602     * - soft          : fail silently
79603     * - nodeps        : ignore dependency conflicts/missing dependencies
79604     * - alldeps       : install all dependencies
79605     * - onlyreqdeps   : install only required dependencies
79606     *
79607     * @return array|PEAR_Error package info if successful
79608     */
79609    function install($pkgfile, $options = array())
79610    {
79611        $this->_options = $options;
79612        $this->_registry = &$this->config->getRegistry();
79613        if (is_object($pkgfile)) {
79614            $dlpkg    = &$pkgfile;
79615            $pkg      = $pkgfile->getPackageFile();
79616            $pkgfile  = $pkg->getArchiveFile();
79617            $descfile = $pkg->getPackageFile();
79618        } else {
79619            $descfile = $pkgfile;
79620            $pkg      = $this->_parsePackageXml($descfile);
79621            if (PEAR::isError($pkg)) {
79622                return $pkg;
79623            }
79624        }
79625
79626        $tmpdir = dirname($descfile);
79627        if (realpath($descfile) != realpath($pkgfile)) {
79628            // Use the temp_dir since $descfile can contain the download dir path
79629            $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net');
79630            $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"');
79631
79632            $tar = new Archive_Tar($pkgfile);
79633            if (!$tar->extract($tmpdir)) {
79634                return $this->raiseError("unable to unpack $pkgfile");
79635            }
79636        }
79637
79638        $pkgname = $pkg->getName();
79639        $channel = $pkg->getChannel();
79640        if (isset($this->_options['packagingroot'])) {
79641            $regdir = $this->_prependPath(
79642                $this->config->get('php_dir', null, 'pear.php.net'),
79643                $this->_options['packagingroot']);
79644
79645            $packrootphp_dir = $this->_prependPath(
79646                $this->config->get('php_dir', null, $channel),
79647                $this->_options['packagingroot']);
79648        }
79649
79650        if (isset($options['installroot'])) {
79651            $this->config->setInstallRoot($options['installroot']);
79652            $this->_registry = &$this->config->getRegistry();
79653            $installregistry = &$this->_registry;
79654            $this->installroot = ''; // all done automagically now
79655            $php_dir = $this->config->get('php_dir', null, $channel);
79656        } else {
79657            $this->config->setInstallRoot(false);
79658            $this->_registry = &$this->config->getRegistry();
79659            if (isset($this->_options['packagingroot'])) {
79660                $installregistry = &new PEAR_Registry($regdir);
79661                if (!$installregistry->channelExists($channel, true)) {
79662                    // we need to fake a channel-discover of this channel
79663                    $chanobj = $this->_registry->getChannel($channel, true);
79664                    $installregistry->addChannel($chanobj);
79665                }
79666                $php_dir = $packrootphp_dir;
79667            } else {
79668                $installregistry = &$this->_registry;
79669                $php_dir = $this->config->get('php_dir', null, $channel);
79670            }
79671            $this->installroot = '';
79672        }
79673
79674        // {{{ checks to do when not in "force" mode
79675        if (empty($options['force']) &&
79676              (file_exists($this->config->get('php_dir')) &&
79677               is_dir($this->config->get('php_dir')))) {
79678            $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname);
79679            $instfilelist = $pkg->getInstallationFileList(true);
79680            if (PEAR::isError($instfilelist)) {
79681                return $instfilelist;
79682            }
79683
79684            // ensure we have the most accurate registry
79685            $installregistry->flushFileMap();
79686            $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1');
79687            if (PEAR::isError($test)) {
79688                return $test;
79689            }
79690
79691            if (sizeof($test)) {
79692                $pkgs = $this->getInstallPackages();
79693                $found = false;
79694                foreach ($pkgs as $param) {
79695                    if ($pkg->isSubpackageOf($param)) {
79696                        $found = true;
79697                        break;
79698                    }
79699                }
79700
79701                if ($found) {
79702                    // subpackages can conflict with earlier versions of parent packages
79703                    $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel());
79704                    $tmp = $test;
79705                    foreach ($tmp as $file => $info) {
79706                        if (is_array($info)) {
79707                            if (strtolower($info[1]) == strtolower($param->getPackage()) &&
79708                                  strtolower($info[0]) == strtolower($param->getChannel())
79709                            ) {
79710                                if (isset($parentreg['filelist'][$file])) {
79711                                    unset($parentreg['filelist'][$file]);
79712                                } else{
79713                                    $pos     = strpos($file, '/');
79714                                    $basedir = substr($file, 0, $pos);
79715                                    $file2   = substr($file, $pos + 1);
79716                                    if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
79717                                        && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
79718                                    ) {
79719                                        unset($parentreg['filelist'][$file2]);
79720                                    }
79721                                }
79722
79723                                unset($test[$file]);
79724                            }
79725                        } else {
79726                            if (strtolower($param->getChannel()) != 'pear.php.net') {
79727                                continue;
79728                            }
79729
79730                            if (strtolower($info) == strtolower($param->getPackage())) {
79731                                if (isset($parentreg['filelist'][$file])) {
79732                                    unset($parentreg['filelist'][$file]);
79733                                } else{
79734                                    $pos     = strpos($file, '/');
79735                                    $basedir = substr($file, 0, $pos);
79736                                    $file2   = substr($file, $pos + 1);
79737                                    if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
79738                                        && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
79739                                    ) {
79740                                        unset($parentreg['filelist'][$file2]);
79741                                    }
79742                                }
79743
79744                                unset($test[$file]);
79745                            }
79746                        }
79747                    }
79748
79749                    $pfk = &new PEAR_PackageFile($this->config);
79750                    $parentpkg = &$pfk->fromArray($parentreg);
79751                    $installregistry->updatePackage2($parentpkg);
79752                }
79753
79754                if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) {
79755                    $tmp = $test;
79756                    foreach ($tmp as $file => $info) {
79757                        if (is_string($info)) {
79758                            // pear.php.net packages are always stored as strings
79759                            if (strtolower($info) == strtolower($param->getPackage())) {
79760                                // upgrading existing package
79761                                unset($test[$file]);
79762                            }
79763                        }
79764                    }
79765                }
79766
79767                if (count($test)) {
79768                    $msg = "$channel/$pkgname: conflicting files found:\n";
79769                    $longest = max(array_map("strlen", array_keys($test)));
79770                    $fmt = "%${longest}s (%s)\n";
79771                    foreach ($test as $file => $info) {
79772                        if (!is_array($info)) {
79773                            $info = array('pear.php.net', $info);
79774                        }
79775                        $info = $info[0] . '/' . $info[1];
79776                        $msg .= sprintf($fmt, $file, $info);
79777                    }
79778
79779                    if (!isset($options['ignore-errors'])) {
79780                        return $this->raiseError($msg);
79781                    }
79782
79783                    if (!isset($options['soft'])) {
79784                        $this->log(0, "WARNING: $msg");
79785                    }
79786                }
79787            }
79788        }
79789        // }}}
79790
79791        $this->startFileTransaction();
79792
79793        $usechannel = $channel;
79794        if ($channel == 'pecl.php.net') {
79795            $test = $installregistry->packageExists($pkgname, $channel);
79796            if (!$test) {
79797                $test = $installregistry->packageExists($pkgname, 'pear.php.net');
79798                $usechannel = 'pear.php.net';
79799            }
79800        } else {
79801            $test = $installregistry->packageExists($pkgname, $channel);
79802        }
79803
79804        if (empty($options['upgrade']) && empty($options['soft'])) {
79805            // checks to do only when installing new packages
79806            if (empty($options['force']) && $test) {
79807                return $this->raiseError("$channel/$pkgname is already installed");
79808            }
79809        } else {
79810            // Upgrade
79811            if ($test) {
79812                $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel);
79813                $v2 = $pkg->getVersion();
79814                $cmp = version_compare("$v1", "$v2", 'gt');
79815                if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
79816                    return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
79817                }
79818            }
79819        }
79820
79821        // Do cleanups for upgrade and install, remove old release's files first
79822        if ($test && empty($options['register-only'])) {
79823            // when upgrading, remove old release's files first:
79824            if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel,
79825                  true))) {
79826                if (!isset($options['ignore-errors'])) {
79827                    return $this->raiseError($err);
79828                }
79829
79830                if (!isset($options['soft'])) {
79831                    $this->log(0, 'WARNING: ' . $err->getMessage());
79832                }
79833            } else {
79834                $backedup = $err;
79835            }
79836        }
79837
79838        // {{{ Copy files to dest dir ---------------------------------------
79839
79840        // info from the package it self we want to access from _installFile
79841        $this->pkginfo = &$pkg;
79842        // used to determine whether we should build any C code
79843        $this->source_files = 0;
79844
79845        $savechannel = $this->config->get('default_channel');
79846        if (empty($options['register-only']) && !is_dir($php_dir)) {
79847            if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) {
79848                return $this->raiseError("no installation destination directory '$php_dir'\n");
79849            }
79850        }
79851
79852        if (substr($pkgfile, -4) != '.xml') {
79853            $tmpdir .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion();
79854        }
79855
79856        $this->configSet('default_channel', $channel);
79857        // {{{ install files
79858
79859        $ver = $pkg->getPackagexmlVersion();
79860        if (version_compare($ver, '2.0', '>=')) {
79861            $filelist = $pkg->getInstallationFilelist();
79862        } else {
79863            $filelist = $pkg->getFileList();
79864        }
79865
79866        if (PEAR::isError($filelist)) {
79867            return $filelist;
79868        }
79869
79870        $p = &$installregistry->getPackage($pkgname, $channel);
79871        $dirtree = (empty($options['register-only']) && $p) ? $p->getDirTree() : false;
79872
79873        $pkg->resetFilelist();
79874        $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(),
79875            'version', $pkg->getChannel()));
79876        foreach ($filelist as $file => $atts) {
79877            $this->expectError(PEAR_INSTALLER_FAILED);
79878            if ($pkg->getPackagexmlVersion() == '1.0') {
79879                $res = $this->_installFile($file, $atts, $tmpdir, $options);
79880            } else {
79881                $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options);
79882            }
79883            $this->popExpect();
79884
79885            if (PEAR::isError($res)) {
79886                if (empty($options['ignore-errors'])) {
79887                    $this->rollbackFileTransaction();
79888                    if ($res->getMessage() == "file does not exist") {
79889                        $this->raiseError("file $file in package.xml does not exist");
79890                    }
79891
79892                    return $this->raiseError($res);
79893                }
79894
79895                if (!isset($options['soft'])) {
79896                    $this->log(0, "Warning: " . $res->getMessage());
79897                }
79898            }
79899
79900            $real = isset($atts['attribs']) ? $atts['attribs'] : $atts;
79901            if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') {
79902                // Register files that were installed
79903                $pkg->installedFile($file, $atts);
79904            }
79905        }
79906        // }}}
79907
79908        // {{{ compile and install source files
79909        if ($this->source_files > 0 && empty($options['nobuild'])) {
79910            if (PEAR::isError($err =
79911                  $this->_compileSourceFiles($savechannel, $pkg))) {
79912                return $err;
79913            }
79914        }
79915        // }}}
79916
79917        if (isset($backedup)) {
79918            $this->_removeBackups($backedup);
79919        }
79920
79921        if (!$this->commitFileTransaction()) {
79922            $this->rollbackFileTransaction();
79923            $this->configSet('default_channel', $savechannel);
79924            return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
79925        }
79926        // }}}
79927
79928        $ret          = false;
79929        $installphase = 'install';
79930        $oldversion   = false;
79931        // {{{ Register that the package is installed -----------------------
79932        if (empty($options['upgrade'])) {
79933            // if 'force' is used, replace the info in registry
79934            $usechannel = $channel;
79935            if ($channel == 'pecl.php.net') {
79936                $test = $installregistry->packageExists($pkgname, $channel);
79937                if (!$test) {
79938                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
79939                    $usechannel = 'pear.php.net';
79940                }
79941            } else {
79942                $test = $installregistry->packageExists($pkgname, $channel);
79943            }
79944
79945            if (!empty($options['force']) && $test) {
79946                $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel);
79947                $installregistry->deletePackage($pkgname, $usechannel);
79948            }
79949            $ret = $installregistry->addPackage2($pkg);
79950        } else {
79951            if ($dirtree) {
79952                $this->startFileTransaction();
79953                // attempt to delete empty directories
79954                uksort($dirtree, array($this, '_sortDirs'));
79955                foreach($dirtree as $dir => $notused) {
79956                    $this->addFileOperation('rmdir', array($dir));
79957                }
79958                $this->commitFileTransaction();
79959            }
79960
79961            $usechannel = $channel;
79962            if ($channel == 'pecl.php.net') {
79963                $test = $installregistry->packageExists($pkgname, $channel);
79964                if (!$test) {
79965                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
79966                    $usechannel = 'pear.php.net';
79967                }
79968            } else {
79969                $test = $installregistry->packageExists($pkgname, $channel);
79970            }
79971
79972            // new: upgrade installs a package if it isn't installed
79973            if (!$test) {
79974                $ret = $installregistry->addPackage2($pkg);
79975            } else {
79976                if ($usechannel != $channel) {
79977                    $installregistry->deletePackage($pkgname, $usechannel);
79978                    $ret = $installregistry->addPackage2($pkg);
79979                } else {
79980                    $ret = $installregistry->updatePackage2($pkg);
79981                }
79982                $installphase = 'upgrade';
79983            }
79984        }
79985
79986        if (!$ret) {
79987            $this->configSet('default_channel', $savechannel);
79988            return $this->raiseError("Adding package $channel/$pkgname to registry failed");
79989        }
79990        // }}}
79991
79992        $this->configSet('default_channel', $savechannel);
79993        if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist
79994            if (PEAR_Task_Common::hasPostinstallTasks()) {
79995                PEAR_Task_Common::runPostinstallTasks($installphase);
79996            }
79997        }
79998
79999        return $pkg->toArray(true);
80000    }
80001
80002    // }}}
80003
80004    // {{{ _compileSourceFiles()
80005    /**
80006     * @param string
80007     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
80008     */
80009    function _compileSourceFiles($savechannel, &$filelist)
80010    {
80011        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Builder.php';
80012        $this->log(1, "$this->source_files source files, building");
80013        $bob = &new PEAR_Builder($this->ui);
80014        $bob->debug = $this->debug;
80015        $built = $bob->build($filelist, array(&$this, '_buildCallback'));
80016        if (PEAR::isError($built)) {
80017            $this->rollbackFileTransaction();
80018            $this->configSet('default_channel', $savechannel);
80019            return $built;
80020        }
80021
80022        $this->log(1, "\nBuild process completed successfully");
80023        foreach ($built as $ext) {
80024            $bn = basename($ext['file']);
80025            list($_ext_name, $_ext_suff) = explode('.', $bn);
80026            if ($_ext_suff == '.so' || $_ext_suff == '.dll') {
80027                if (extension_loaded($_ext_name)) {
80028                    $this->raiseError("Extension '$_ext_name' already loaded. " .
80029                                      'Please unload it in your php.ini file ' .
80030                                      'prior to install or upgrade');
80031                }
80032                $role = 'ext';
80033            } else {
80034                $role = 'src';
80035            }
80036
80037            $dest = $ext['dest'];
80038            $packagingroot = '';
80039            if (isset($this->_options['packagingroot'])) {
80040                $packagingroot = $this->_options['packagingroot'];
80041            }
80042
80043            $copyto = $this->_prependPath($dest, $packagingroot);
80044            $extra  = $copyto != $dest ? " as '$copyto'" : '';
80045            $this->log(1, "Installing '$dest'$extra");
80046
80047            $copydir = dirname($copyto);
80048            // pretty much nothing happens if we are only registering the install
80049            if (empty($this->_options['register-only'])) {
80050                if (!file_exists($copydir) || !is_dir($copydir)) {
80051                    if (!$this->mkDirHier($copydir)) {
80052                        return $this->raiseError("failed to mkdir $copydir",
80053                            PEAR_INSTALLER_FAILED);
80054                    }
80055
80056                    $this->log(3, "+ mkdir $copydir");
80057                }
80058
80059                if (!@copy($ext['file'], $copyto)) {
80060                    return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED);
80061                }
80062
80063                $this->log(3, "+ cp $ext[file] $copyto");
80064                $this->addFileOperation('rename', array($ext['file'], $copyto));
80065                if (!OS_WINDOWS) {
80066                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
80067                    $this->addFileOperation('chmod', array($mode, $copyto));
80068                    if (!@chmod($copyto, $mode)) {
80069                        $this->log(0, "failed to change mode of $copyto ($php_errormsg)");
80070                    }
80071                }
80072            }
80073
80074
80075            $data = array(
80076                'role'         => $role,
80077                'name'         => $bn,
80078                'installed_as' => $dest,
80079                'php_api'      => $ext['php_api'],
80080                'zend_mod_api' => $ext['zend_mod_api'],
80081                'zend_ext_api' => $ext['zend_ext_api'],
80082            );
80083
80084            if ($filelist->getPackageXmlVersion() == '1.0') {
80085                $filelist->installedFile($bn, $data);
80086            } else {
80087                $filelist->installedFile($bn, array('attribs' => $data));
80088            }
80089        }
80090    }
80091
80092    // }}}
80093    function &getUninstallPackages()
80094    {
80095        return $this->_downloadedPackages;
80096    }
80097    // {{{ uninstall()
80098
80099    /**
80100     * Uninstall a package
80101     *
80102     * This method removes all files installed by the application, and then
80103     * removes any empty directories.
80104     * @param string package name
80105     * @param array Command-line options.  Possibilities include:
80106     *
80107     *              - installroot: base installation dir, if not the default
80108     *              - register-only : update registry but don't remove files
80109     *              - nodeps: do not process dependencies of other packages to ensure
80110     *                        uninstallation does not break things
80111     */
80112    function uninstall($package, $options = array())
80113    {
80114        $installRoot = isset($options['installroot']) ? $options['installroot'] : '';
80115        $this->config->setInstallRoot($installRoot);
80116
80117        $this->installroot = '';
80118        $this->_registry = &$this->config->getRegistry();
80119        if (is_object($package)) {
80120            $channel = $package->getChannel();
80121            $pkg     = $package;
80122            $package = $pkg->getPackage();
80123        } else {
80124            $pkg = false;
80125            $info = $this->_registry->parsePackageName($package,
80126                $this->config->get('default_channel'));
80127            $channel = $info['channel'];
80128            $package = $info['package'];
80129        }
80130
80131        $savechannel = $this->config->get('default_channel');
80132        $this->configSet('default_channel', $channel);
80133        if (!is_object($pkg)) {
80134            $pkg = $this->_registry->getPackage($package, $channel);
80135        }
80136
80137        if (!$pkg) {
80138            $this->configSet('default_channel', $savechannel);
80139            return $this->raiseError($this->_registry->parsedPackageNameToString(
80140                array(
80141                    'channel' => $channel,
80142                    'package' => $package
80143                ), true) . ' not installed');
80144        }
80145
80146        if ($pkg->getInstalledBinary()) {
80147            // this is just an alias for a binary package
80148            return $this->_registry->deletePackage($package, $channel);
80149        }
80150
80151        $filelist = $pkg->getFilelist();
80152        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
80153        if (!class_exists('PEAR_Dependency2')) {
80154            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
80155        }
80156
80157        $depchecker = &new PEAR_Dependency2($this->config, $options,
80158            array('channel' => $channel, 'package' => $package),
80159            PEAR_VALIDATE_UNINSTALLING);
80160        $e = $depchecker->validatePackageUninstall($this);
80161        PEAR::staticPopErrorHandling();
80162        if (PEAR::isError($e)) {
80163            if (!isset($options['ignore-errors'])) {
80164                return $this->raiseError($e);
80165            }
80166
80167            if (!isset($options['soft'])) {
80168                $this->log(0, 'WARNING: ' . $e->getMessage());
80169            }
80170        } elseif (is_array($e)) {
80171            if (!isset($options['soft'])) {
80172                $this->log(0, $e[0]);
80173            }
80174        }
80175
80176        $this->pkginfo = &$pkg;
80177        // pretty much nothing happens if we are only registering the uninstall
80178        if (empty($options['register-only'])) {
80179            // {{{ Delete the files
80180            $this->startFileTransaction();
80181            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
80182            if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
80183                PEAR::popErrorHandling();
80184                $this->rollbackFileTransaction();
80185                $this->configSet('default_channel', $savechannel);
80186                if (!isset($options['ignore-errors'])) {
80187                    return $this->raiseError($err);
80188                }
80189
80190                if (!isset($options['soft'])) {
80191                    $this->log(0, 'WARNING: ' . $err->getMessage());
80192                }
80193            } else {
80194                PEAR::popErrorHandling();
80195            }
80196
80197            if (!$this->commitFileTransaction()) {
80198                $this->rollbackFileTransaction();
80199                if (!isset($options['ignore-errors'])) {
80200                    return $this->raiseError("uninstall failed");
80201                }
80202
80203                if (!isset($options['soft'])) {
80204                    $this->log(0, 'WARNING: uninstall failed');
80205                }
80206            } else {
80207                $this->startFileTransaction();
80208                $dirtree = $pkg->getDirTree();
80209                if ($dirtree === false) {
80210                    $this->configSet('default_channel', $savechannel);
80211                    return $this->_registry->deletePackage($package, $channel);
80212                }
80213
80214                // attempt to delete empty directories
80215                uksort($dirtree, array($this, '_sortDirs'));
80216                foreach($dirtree as $dir => $notused) {
80217                    $this->addFileOperation('rmdir', array($dir));
80218                }
80219
80220                if (!$this->commitFileTransaction()) {
80221                    $this->rollbackFileTransaction();
80222                    if (!isset($options['ignore-errors'])) {
80223                        return $this->raiseError("uninstall failed");
80224                    }
80225
80226                    if (!isset($options['soft'])) {
80227                        $this->log(0, 'WARNING: uninstall failed');
80228                    }
80229                }
80230            }
80231            // }}}
80232        }
80233
80234        $this->configSet('default_channel', $savechannel);
80235        // Register that the package is no longer installed
80236        return $this->_registry->deletePackage($package, $channel);
80237    }
80238
80239    /**
80240     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
80241     *
80242     * It also removes duplicate dependencies
80243     * @param array an array of PEAR_PackageFile_v[1/2] objects
80244     * @return array|PEAR_Error array of array(packagefilename, package.xml contents)
80245     */
80246    function sortPackagesForUninstall(&$packages)
80247    {
80248        $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config);
80249        if (PEAR::isError($this->_dependencyDB)) {
80250            return $this->_dependencyDB;
80251        }
80252        usort($packages, array(&$this, '_sortUninstall'));
80253    }
80254
80255    function _sortUninstall($a, $b)
80256    {
80257        if (!$a->getDeps() && !$b->getDeps()) {
80258            return 0; // neither package has dependencies, order is insignificant
80259        }
80260        if ($a->getDeps() && !$b->getDeps()) {
80261            return -1; // $a must be installed after $b because $a has dependencies
80262        }
80263        if (!$a->getDeps() && $b->getDeps()) {
80264            return 1; // $b must be installed after $a because $b has dependencies
80265        }
80266        // both packages have dependencies
80267        if ($this->_dependencyDB->dependsOn($a, $b)) {
80268            return -1;
80269        }
80270        if ($this->_dependencyDB->dependsOn($b, $a)) {
80271            return 1;
80272        }
80273        return 0;
80274    }
80275
80276    // }}}
80277    // {{{ _sortDirs()
80278    function _sortDirs($a, $b)
80279    {
80280        if (strnatcmp($a, $b) == -1) return 1;
80281        if (strnatcmp($a, $b) == 1) return -1;
80282        return 0;
80283    }
80284
80285    // }}}
80286
80287    // {{{ _buildCallback()
80288
80289    function _buildCallback($what, $data)
80290    {
80291        if (($what == 'cmdoutput' && $this->debug > 1) ||
80292            ($what == 'output' && $this->debug > 0)) {
80293            $this->ui->outputData(rtrim($data), 'build');
80294        }
80295    }
80296
80297    // }}}
80298}<?php
80299/**
80300 * PEAR_Installer_Role
80301 *
80302 * PHP versions 4 and 5
80303 *
80304 * @category   pear
80305 * @package    PEAR
80306 * @author     Greg Beaver <cellog@php.net>
80307 * @copyright  1997-2009 The Authors
80308 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80309 * @version    CVS: $Id: Role.php 313023 2011-07-06 19:17:11Z dufuz $
80310 * @link       http://pear.php.net/package/PEAR
80311 * @since      File available since Release 1.4.0a1
80312 */
80313
80314/**
80315 * base class for installer roles
80316 */
80317require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Installer/Role/Common.php';
80318require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';
80319/**
80320 * @category   pear
80321 * @package    PEAR
80322 * @author     Greg Beaver <cellog@php.net>
80323 * @copyright  1997-2009 The Authors
80324 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80325 * @version    Release: 1.9.4
80326 * @link       http://pear.php.net/package/PEAR
80327 * @since      Class available since Release 1.4.0a1
80328 */
80329class PEAR_Installer_Role
80330{
80331    /**
80332     * Set up any additional configuration variables that file roles require
80333     *
80334     * Never call this directly, it is called by the PEAR_Config constructor
80335     * @param PEAR_Config
80336     * @access private
80337     * @static
80338     */
80339    function initializeConfig(&$config)
80340    {
80341        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
80342            PEAR_Installer_Role::registerRoles();
80343        }
80344
80345        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $class => $info) {
80346            if (!$info['config_vars']) {
80347                continue;
80348            }
80349
80350            $config->_addConfigVars($class, $info['config_vars']);
80351        }
80352    }
80353
80354    /**
80355     * @param PEAR_PackageFile_v2
80356     * @param string role name
80357     * @param PEAR_Config
80358     * @return PEAR_Installer_Role_Common
80359     * @static
80360     */
80361    function &factory($pkg, $role, &$config)
80362    {
80363        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
80364            PEAR_Installer_Role::registerRoles();
80365        }
80366
80367        if (!in_array($role, PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
80368            $a = false;
80369            return $a;
80370        }
80371
80372        $a = 'PEAR_Installer_Role_' . ucfirst($role);
80373        if (!class_exists($a)) {
80374            require_once 'phar://install-pear-nozlib.phar/' . str_replace('_', '/', $a) . '.php';
80375        }
80376
80377        $b = new $a($config);
80378        return $b;
80379    }
80380
80381    /**
80382     * Get a list of file roles that are valid for the particular release type.
80383     *
80384     * For instance, src files serve no purpose in regular php releases.
80385     * @param string
80386     * @param bool clear cache
80387     * @return array
80388     * @static
80389     */
80390    function getValidRoles($release, $clear = false)
80391    {
80392        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
80393            PEAR_Installer_Role::registerRoles();
80394        }
80395
80396        static $ret = array();
80397        if ($clear) {
80398            $ret = array();
80399        }
80400
80401        if (isset($ret[$release])) {
80402            return $ret[$release];
80403        }
80404
80405        $ret[$release] = array();
80406        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
80407            if (in_array($release, $okreleases['releasetypes'])) {
80408                $ret[$release][] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
80409            }
80410        }
80411
80412        return $ret[$release];
80413    }
80414
80415    /**
80416     * Get a list of roles that require their files to be installed
80417     *
80418     * Most roles must be installed, but src and package roles, for instance
80419     * are pseudo-roles.  src files are compiled into a new extension.  Package
80420     * roles are actually fully bundled releases of a package
80421     * @param bool clear cache
80422     * @return array
80423     * @static
80424     */
80425    function getInstallableRoles($clear = false)
80426    {
80427        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
80428            PEAR_Installer_Role::registerRoles();
80429        }
80430
80431        static $ret;
80432        if ($clear) {
80433            unset($ret);
80434        }
80435
80436        if (isset($ret)) {
80437            return $ret;
80438        }
80439
80440        $ret = array();
80441        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
80442            if ($okreleases['installable']) {
80443                $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
80444            }
80445        }
80446
80447        return $ret;
80448    }
80449
80450    /**
80451     * Return an array of roles that are affected by the baseinstalldir attribute
80452     *
80453     * Most roles ignore this attribute, and instead install directly into:
80454     * PackageName/filepath
80455     * so a tests file tests/file.phpt is installed into PackageName/tests/filepath.php
80456     * @param bool clear cache
80457     * @return array
80458     * @static
80459     */
80460    function getBaseinstallRoles($clear = false)
80461    {
80462        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
80463            PEAR_Installer_Role::registerRoles();
80464        }
80465
80466        static $ret;
80467        if ($clear) {
80468            unset($ret);
80469        }
80470
80471        if (isset($ret)) {
80472            return $ret;
80473        }
80474
80475        $ret = array();
80476        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
80477            if ($okreleases['honorsbaseinstall']) {
80478                $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
80479            }
80480        }
80481
80482        return $ret;
80483    }
80484
80485    /**
80486     * Return an array of file roles that should be analyzed for PHP content at package time,
80487     * like the "php" role.
80488     * @param bool clear cache
80489     * @return array
80490     * @static
80491     */
80492    function getPhpRoles($clear = false)
80493    {
80494        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
80495            PEAR_Installer_Role::registerRoles();
80496        }
80497
80498        static $ret;
80499        if ($clear) {
80500            unset($ret);
80501        }
80502
80503        if (isset($ret)) {
80504            return $ret;
80505        }
80506
80507        $ret = array();
80508        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
80509            if ($okreleases['phpfile']) {
80510                $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
80511            }
80512        }
80513
80514        return $ret;
80515    }
80516
80517    /**
80518     * Scan through the Command directory looking for classes
80519     * and see what commands they implement.
80520     * @param string which directory to look for classes, defaults to
80521     *               the Installer/Roles subdirectory of
80522     *               the directory from where this file (__FILE__) is
80523     *               included.
80524     *
80525     * @return bool TRUE on success, a PEAR error on failure
80526     * @access public
80527     * @static
80528     */
80529    function registerRoles($dir = null)
80530    {
80531        $GLOBALS['_PEAR_INSTALLER_ROLES'] = array();
80532        $parser = new PEAR_XMLParser;
80533        if ($dir === null) {
80534            $dir = dirname(__FILE__) . '/Role';
80535        }
80536
80537        if (!file_exists($dir) || !is_dir($dir)) {
80538            return PEAR::raiseError("registerRoles: opendir($dir) failed: does not exist/is not directory");
80539        }
80540
80541        $dp = @opendir($dir);
80542        if (empty($dp)) {
80543            return PEAR::raiseError("registerRoles: opendir($dir) failed: $php_errmsg");
80544        }
80545
80546        while ($entry = readdir($dp)) {
80547            if ($entry{0} == '.' || substr($entry, -4) != '.xml') {
80548                continue;
80549            }
80550
80551            $class = "PEAR_Installer_Role_".substr($entry, 0, -4);
80552            // List of roles
80553            if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'][$class])) {
80554                $file = "$dir/$entry";
80555                $parser->parse(file_get_contents($file));
80556                $data = $parser->getData();
80557                if (!is_array($data['releasetypes'])) {
80558                    $data['releasetypes'] = array($data['releasetypes']);
80559                }
80560
80561                $GLOBALS['_PEAR_INSTALLER_ROLES'][$class] = $data;
80562            }
80563        }
80564
80565        closedir($dp);
80566        ksort($GLOBALS['_PEAR_INSTALLER_ROLES']);
80567        PEAR_Installer_Role::getBaseinstallRoles(true);
80568        PEAR_Installer_Role::getInstallableRoles(true);
80569        PEAR_Installer_Role::getPhpRoles(true);
80570        PEAR_Installer_Role::getValidRoles('****', true);
80571        return true;
80572    }
80573}<?php
80574/**
80575 * PEAR_Installer_Role_Cfg
80576 *
80577 * PHP versions 4 and 5
80578 *
80579 * @category   pear
80580 * @package    PEAR
80581 * @author     Greg Beaver <cellog@php.net>
80582 * @copyright  2007-2009 The Authors
80583 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80584 * @version    CVS: $Id: Cfg.php 313023 2011-07-06 19:17:11Z dufuz $
80585 * @link       http://pear.php.net/package/PEAR
80586 * @since      File available since Release 1.7.0
80587 */
80588
80589/**
80590 * @category   pear
80591 * @package    PEAR
80592 * @author     Greg Beaver <cellog@php.net>
80593 * @copyright  2007-2009 The Authors
80594 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80595 * @version    Release: 1.9.4
80596 * @link       http://pear.php.net/package/PEAR
80597 * @since      Class available since Release 1.7.0
80598 */
80599class PEAR_Installer_Role_Cfg extends PEAR_Installer_Role_Common
80600{
80601    /**
80602     * @var PEAR_Installer
80603     */
80604    var $installer;
80605
80606    /**
80607     * the md5 of the original file
80608     *
80609     * @var unknown_type
80610     */
80611    var $md5 = null;
80612
80613    /**
80614     * Do any unusual setup here
80615     * @param PEAR_Installer
80616     * @param PEAR_PackageFile_v2
80617     * @param array file attributes
80618     * @param string file name
80619     */
80620    function setup(&$installer, $pkg, $atts, $file)
80621    {
80622        $this->installer = &$installer;
80623        $reg = &$this->installer->config->getRegistry();
80624        $package = $reg->getPackage($pkg->getPackage(), $pkg->getChannel());
80625        if ($package) {
80626            $filelist = $package->getFilelist();
80627            if (isset($filelist[$file]) && isset($filelist[$file]['md5sum'])) {
80628                $this->md5 = $filelist[$file]['md5sum'];
80629            }
80630        }
80631    }
80632
80633    function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
80634    {
80635        $test = parent::processInstallation($pkg, $atts, $file, $tmp_path, $layer);
80636        if (@file_exists($test[2]) && @file_exists($test[3])) {
80637            $md5 = md5_file($test[2]);
80638            // configuration has already been installed, check for mods
80639            if ($md5 !== $this->md5 && $md5 !== md5_file($test[3])) {
80640                // configuration has been modified, so save our version as
80641                // configfile-version
80642                $old = $test[2];
80643                $test[2] .= '.new-' . $pkg->getVersion();
80644                // backup original and re-install it
80645                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
80646                $tmpcfg = $this->config->get('temp_dir');
80647                $newloc = System::mkdir(array('-p', $tmpcfg));
80648                if (!$newloc) {
80649                    // try temp_dir
80650                    $newloc = System::mktemp(array('-d'));
80651                    if (!$newloc || PEAR::isError($newloc)) {
80652                        PEAR::popErrorHandling();
80653                        return PEAR::raiseError('Could not save existing configuration file '.
80654                            $old . ', unable to install.  Please set temp_dir ' .
80655                            'configuration variable to a writeable location and try again');
80656                    }
80657                } else {
80658                    $newloc = $tmpcfg;
80659                }
80660
80661                $temp_file = $newloc . DIRECTORY_SEPARATOR . uniqid('savefile');
80662                if (!@copy($old, $temp_file)) {
80663                    PEAR::popErrorHandling();
80664                    return PEAR::raiseError('Could not save existing configuration file '.
80665                        $old . ', unable to install.  Please set temp_dir ' .
80666                        'configuration variable to a writeable location and try again');
80667                }
80668
80669                PEAR::popErrorHandling();
80670                $this->installer->log(0, "WARNING: configuration file $old is being installed as $test[2], you should manually merge in changes to the existing configuration file");
80671                $this->installer->addFileOperation('rename', array($temp_file, $old, false));
80672                $this->installer->addFileOperation('delete', array($temp_file));
80673            }
80674        }
80675
80676        return $test;
80677    }
80678}<role version="1.0">
80679 <releasetypes>php</releasetypes>
80680 <releasetypes>extsrc</releasetypes>
80681 <releasetypes>extbin</releasetypes>
80682 <releasetypes>zendextsrc</releasetypes>
80683 <releasetypes>zendextbin</releasetypes>
80684 <installable>1</installable>
80685 <locationconfig>cfg_dir</locationconfig>
80686 <honorsbaseinstall />
80687 <unusualbaseinstall>1</unusualbaseinstall>
80688 <phpfile />
80689 <executable />
80690 <phpextension />
80691 <config_vars />
80692</role><?php
80693/**
80694 * Base class for all installation roles.
80695 *
80696 * PHP versions 4 and 5
80697 *
80698 * @category   pear
80699 * @package    PEAR
80700 * @author     Greg Beaver <cellog@php.net>
80701 * @copyright  1997-2006 The PHP Group
80702 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80703 * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
80704 * @link       http://pear.php.net/package/PEAR
80705 * @since      File available since Release 1.4.0a1
80706 */
80707/**
80708 * Base class for all installation roles.
80709 *
80710 * This class allows extensibility of file roles.  Packages with complex
80711 * customization can now provide custom file roles along with the possibility of
80712 * adding configuration values to match.
80713 * @category   pear
80714 * @package    PEAR
80715 * @author     Greg Beaver <cellog@php.net>
80716 * @copyright  1997-2006 The PHP Group
80717 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80718 * @version    Release: 1.9.4
80719 * @link       http://pear.php.net/package/PEAR
80720 * @since      Class available since Release 1.4.0a1
80721 */
80722class PEAR_Installer_Role_Common
80723{
80724    /**
80725     * @var PEAR_Config
80726     * @access protected
80727     */
80728    var $config;
80729
80730    /**
80731     * @param PEAR_Config
80732     */
80733    function PEAR_Installer_Role_Common(&$config)
80734    {
80735        $this->config = $config;
80736    }
80737
80738    /**
80739     * Retrieve configuration information about a file role from its XML info
80740     *
80741     * @param string $role Role Classname, as in "PEAR_Installer_Role_Data"
80742     * @return array
80743     */
80744    function getInfo($role)
80745    {
80746        if (empty($GLOBALS['_PEAR_INSTALLER_ROLES'][$role])) {
80747            return PEAR::raiseError('Unknown Role class: "' . $role . '"');
80748        }
80749        return $GLOBALS['_PEAR_INSTALLER_ROLES'][$role];
80750    }
80751
80752    /**
80753     * This is called for each file to set up the directories and files
80754     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
80755     * @param array attributes from the <file> tag
80756     * @param string file name
80757     * @return array an array consisting of:
80758     *
80759     *    1 the original, pre-baseinstalldir installation directory
80760     *    2 the final installation directory
80761     *    3 the full path to the final location of the file
80762     *    4 the location of the pre-installation file
80763     */
80764    function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
80765    {
80766        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
80767            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
80768        if (PEAR::isError($roleInfo)) {
80769            return $roleInfo;
80770        }
80771        if (!$roleInfo['locationconfig']) {
80772            return false;
80773        }
80774        if ($roleInfo['honorsbaseinstall']) {
80775            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'], $layer,
80776                $pkg->getChannel());
80777            if (!empty($atts['baseinstalldir'])) {
80778                $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
80779            }
80780        } elseif ($roleInfo['unusualbaseinstall']) {
80781            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
80782                    $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
80783            if (!empty($atts['baseinstalldir'])) {
80784                $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
80785            }
80786        } else {
80787            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
80788                    $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
80789        }
80790        if (dirname($file) != '.' && empty($atts['install-as'])) {
80791            $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
80792        }
80793        if (empty($atts['install-as'])) {
80794            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
80795        } else {
80796            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
80797        }
80798        $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
80799
80800        // Clean up the DIRECTORY_SEPARATOR mess
80801        $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
80802        
80803        list($dest_dir, $dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
80804                                                    array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR,
80805                                                          DIRECTORY_SEPARATOR),
80806                                                    array($dest_dir, $dest_file, $orig_file));
80807        return array($save_destdir, $dest_dir, $dest_file, $orig_file);
80808    }
80809
80810    /**
80811     * Get the name of the configuration variable that specifies the location of this file
80812     * @return string|false
80813     */
80814    function getLocationConfig()
80815    {
80816        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
80817            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
80818        if (PEAR::isError($roleInfo)) {
80819            return $roleInfo;
80820        }
80821        return $roleInfo['locationconfig'];
80822    }
80823
80824    /**
80825     * Do any unusual setup here
80826     * @param PEAR_Installer
80827     * @param PEAR_PackageFile_v2
80828     * @param array file attributes
80829     * @param string file name
80830     */
80831    function setup(&$installer, $pkg, $atts, $file)
80832    {
80833    }
80834
80835    function isExecutable()
80836    {
80837        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
80838            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
80839        if (PEAR::isError($roleInfo)) {
80840            return $roleInfo;
80841        }
80842        return $roleInfo['executable'];
80843    }
80844
80845    function isInstallable()
80846    {
80847        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
80848            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
80849        if (PEAR::isError($roleInfo)) {
80850            return $roleInfo;
80851        }
80852        return $roleInfo['installable'];
80853    }
80854
80855    function isExtension()
80856    {
80857        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
80858            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
80859        if (PEAR::isError($roleInfo)) {
80860            return $roleInfo;
80861        }
80862        return $roleInfo['phpextension'];
80863    }
80864}
80865?><?php
80866/**
80867 * PEAR_Installer_Role_Data
80868 *
80869 * PHP versions 4 and 5
80870 *
80871 * @category   pear
80872 * @package    PEAR
80873 * @author     Greg Beaver <cellog@php.net>
80874 * @copyright  1997-2009 The Authors
80875 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80876 * @version    CVS: $Id: Data.php 313023 2011-07-06 19:17:11Z dufuz $
80877 * @link       http://pear.php.net/package/PEAR
80878 * @since      File available since Release 1.4.0a1
80879 */
80880
80881/**
80882 * @category   pear
80883 * @package    PEAR
80884 * @author     Greg Beaver <cellog@php.net>
80885 * @copyright  1997-2009 The Authors
80886 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80887 * @version    Release: 1.9.4
80888 * @link       http://pear.php.net/package/PEAR
80889 * @since      Class available since Release 1.4.0a1
80890 */
80891class PEAR_Installer_Role_Data extends PEAR_Installer_Role_Common {}
80892?><role version="1.0">
80893 <releasetypes>php</releasetypes>
80894 <releasetypes>extsrc</releasetypes>
80895 <releasetypes>extbin</releasetypes>
80896 <releasetypes>zendextsrc</releasetypes>
80897 <releasetypes>zendextbin</releasetypes>
80898 <installable>1</installable>
80899 <locationconfig>data_dir</locationconfig>
80900 <honorsbaseinstall />
80901 <unusualbaseinstall />
80902 <phpfile />
80903 <executable />
80904 <phpextension />
80905 <config_vars />
80906</role><?php
80907/**
80908 * PEAR_Installer_Role_Doc
80909 *
80910 * PHP versions 4 and 5
80911 *
80912 * @category   pear
80913 * @package    PEAR
80914 * @author     Greg Beaver <cellog@php.net>
80915 * @copyright  1997-2009 The Authors
80916 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80917 * @version    CVS: $Id: Doc.php 313023 2011-07-06 19:17:11Z dufuz $
80918 * @link       http://pear.php.net/package/PEAR
80919 * @since      File available since Release 1.4.0a1
80920 */
80921
80922/**
80923 * @category   pear
80924 * @package    PEAR
80925 * @author     Greg Beaver <cellog@php.net>
80926 * @copyright  1997-2009 The Authors
80927 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80928 * @version    Release: 1.9.4
80929 * @link       http://pear.php.net/package/PEAR
80930 * @since      Class available since Release 1.4.0a1
80931 */
80932class PEAR_Installer_Role_Doc extends PEAR_Installer_Role_Common {}
80933?><role version="1.0">
80934 <releasetypes>php</releasetypes>
80935 <releasetypes>extsrc</releasetypes>
80936 <releasetypes>extbin</releasetypes>
80937 <releasetypes>zendextsrc</releasetypes>
80938 <releasetypes>zendextbin</releasetypes>
80939 <installable>1</installable>
80940 <locationconfig>doc_dir</locationconfig>
80941 <honorsbaseinstall />
80942 <unusualbaseinstall />
80943 <phpfile />
80944 <executable />
80945 <phpextension />
80946 <config_vars />
80947</role><?php
80948/**
80949 * PEAR_Installer_Role_Ext
80950 *
80951 * PHP versions 4 and 5
80952 *
80953 * @category   pear
80954 * @package    PEAR
80955 * @author     Greg Beaver <cellog@php.net>
80956 * @copyright  1997-2009 The Authors
80957 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80958 * @version    CVS: $Id: Ext.php 313023 2011-07-06 19:17:11Z dufuz $
80959 * @link       http://pear.php.net/package/PEAR
80960 * @since      File available since Release 1.4.0a1
80961 */
80962
80963/**
80964 * @category   pear
80965 * @package    PEAR
80966 * @author     Greg Beaver <cellog@php.net>
80967 * @copyright  1997-2009 The Authors
80968 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80969 * @version    Release: 1.9.4
80970 * @link       http://pear.php.net/package/PEAR
80971 * @since      Class available since Release 1.4.0a1
80972 */
80973class PEAR_Installer_Role_Ext extends PEAR_Installer_Role_Common {}
80974?><role version="1.0">
80975 <releasetypes>extbin</releasetypes>
80976 <releasetypes>zendextbin</releasetypes>
80977 <installable>1</installable>
80978 <locationconfig>ext_dir</locationconfig>
80979 <honorsbaseinstall>1</honorsbaseinstall>
80980 <unusualbaseinstall />
80981 <phpfile />
80982 <executable />
80983 <phpextension>1</phpextension>
80984 <config_vars />
80985</role><?php
80986/**
80987 * PEAR_Installer_Role_Php
80988 *
80989 * PHP versions 4 and 5
80990 *
80991 * @category   pear
80992 * @package    PEAR
80993 * @author     Greg Beaver <cellog@php.net>
80994 * @copyright  1997-2009 The Authors
80995 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
80996 * @version    CVS: $Id: Php.php 313023 2011-07-06 19:17:11Z dufuz $
80997 * @link       http://pear.php.net/package/PEAR
80998 * @since      File available since Release 1.4.0a1
80999 */
81000
81001/**
81002 * @category   pear
81003 * @package    PEAR
81004 * @author     Greg Beaver <cellog@php.net>
81005 * @copyright  1997-2009 The Authors
81006 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81007 * @version    Release: 1.9.4
81008 * @link       http://pear.php.net/package/PEAR
81009 * @since      Class available since Release 1.4.0a1
81010 */
81011class PEAR_Installer_Role_Php extends PEAR_Installer_Role_Common {}
81012?><role version="1.0">
81013 <releasetypes>php</releasetypes>
81014 <releasetypes>extsrc</releasetypes>
81015 <releasetypes>extbin</releasetypes>
81016 <releasetypes>zendextsrc</releasetypes>
81017 <releasetypes>zendextbin</releasetypes>
81018 <installable>1</installable>
81019 <locationconfig>php_dir</locationconfig>
81020 <honorsbaseinstall>1</honorsbaseinstall>
81021 <unusualbaseinstall />
81022 <phpfile>1</phpfile>
81023 <executable />
81024 <phpextension />
81025 <config_vars />
81026</role><?php
81027/**
81028 * PEAR_Installer_Role_Script
81029 *
81030 * PHP versions 4 and 5
81031 *
81032 * @category   pear
81033 * @package    PEAR
81034 * @author     Greg Beaver <cellog@php.net>
81035 * @copyright  1997-2009 The Authors
81036 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81037 * @version    CVS: $Id: Script.php 313023 2011-07-06 19:17:11Z dufuz $
81038 * @link       http://pear.php.net/package/PEAR
81039 * @since      File available since Release 1.4.0a1
81040 */
81041
81042/**
81043 * @category   pear
81044 * @package    PEAR
81045 * @author     Greg Beaver <cellog@php.net>
81046 * @copyright  1997-2009 The Authors
81047 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81048 * @version    Release: 1.9.4
81049 * @link       http://pear.php.net/package/PEAR
81050 * @since      Class available since Release 1.4.0a1
81051 */
81052class PEAR_Installer_Role_Script extends PEAR_Installer_Role_Common {}
81053?><role version="1.0">
81054 <releasetypes>php</releasetypes>
81055 <releasetypes>extsrc</releasetypes>
81056 <releasetypes>extbin</releasetypes>
81057 <releasetypes>zendextsrc</releasetypes>
81058 <releasetypes>zendextbin</releasetypes>
81059 <installable>1</installable>
81060 <locationconfig>bin_dir</locationconfig>
81061 <honorsbaseinstall>1</honorsbaseinstall>
81062 <unusualbaseinstall />
81063 <phpfile />
81064 <executable>1</executable>
81065 <phpextension />
81066 <config_vars />
81067</role><?php
81068/**
81069 * PEAR_Installer_Role_Src
81070 *
81071 * PHP versions 4 and 5
81072 *
81073 * @category   pear
81074 * @package    PEAR
81075 * @author     Greg Beaver <cellog@php.net>
81076 * @copyright  1997-2009 The Authors
81077 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81078 * @version    CVS: $Id: Src.php 313023 2011-07-06 19:17:11Z dufuz $
81079 * @link       http://pear.php.net/package/PEAR
81080 * @since      File available since Release 1.4.0a1
81081 */
81082
81083/**
81084 * @category   pear
81085 * @package    PEAR
81086 * @author     Greg Beaver <cellog@php.net>
81087 * @copyright  1997-2009 The Authors
81088 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81089 * @version    Release: 1.9.4
81090 * @link       http://pear.php.net/package/PEAR
81091 * @since      Class available since Release 1.4.0a1
81092 */
81093class PEAR_Installer_Role_Src extends PEAR_Installer_Role_Common
81094{
81095    function setup(&$installer, $pkg, $atts, $file)
81096    {
81097        $installer->source_files++;
81098    }
81099}
81100?><role version="1.0">
81101 <releasetypes>extsrc</releasetypes>
81102 <releasetypes>zendextsrc</releasetypes>
81103 <installable>1</installable>
81104 <locationconfig>temp_dir</locationconfig>
81105 <honorsbaseinstall />
81106 <unusualbaseinstall />
81107 <phpfile />
81108 <executable />
81109 <phpextension />
81110 <config_vars />
81111</role><?php
81112/**
81113 * PEAR_Installer_Role_Test
81114 *
81115 * PHP versions 4 and 5
81116 *
81117 * @category   pear
81118 * @package    PEAR
81119 * @author     Greg Beaver <cellog@php.net>
81120 * @copyright  1997-2009 The Authors
81121 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81122 * @version    CVS: $Id: Test.php 313023 2011-07-06 19:17:11Z dufuz $
81123 * @link       http://pear.php.net/package/PEAR
81124 * @since      File available since Release 1.4.0a1
81125 */
81126
81127/**
81128 * @category   pear
81129 * @package    PEAR
81130 * @author     Greg Beaver <cellog@php.net>
81131 * @copyright  1997-2009 The Authors
81132 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81133 * @version    Release: 1.9.4
81134 * @link       http://pear.php.net/package/PEAR
81135 * @since      Class available since Release 1.4.0a1
81136 */
81137class PEAR_Installer_Role_Test extends PEAR_Installer_Role_Common {}
81138?><role version="1.0">
81139 <releasetypes>php</releasetypes>
81140 <releasetypes>extsrc</releasetypes>
81141 <releasetypes>extbin</releasetypes>
81142 <releasetypes>zendextsrc</releasetypes>
81143 <releasetypes>zendextbin</releasetypes>
81144 <installable>1</installable>
81145 <locationconfig>test_dir</locationconfig>
81146 <honorsbaseinstall />
81147 <unusualbaseinstall />
81148 <phpfile />
81149 <executable />
81150 <phpextension />
81151 <config_vars />
81152</role><?php
81153/**
81154 * PEAR_Installer_Role_Www
81155 *
81156 * PHP versions 4 and 5
81157 *
81158 * @category   pear
81159 * @package    PEAR
81160 * @author     Greg Beaver <cellog@php.net>
81161 * @copyright  2007-2009 The Authors
81162 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81163 * @version    CVS: $Id: Www.php 313023 2011-07-06 19:17:11Z dufuz $
81164 * @link       http://pear.php.net/package/PEAR
81165 * @since      File available since Release 1.7.0
81166 */
81167
81168/**
81169 * @category   pear
81170 * @package    PEAR
81171 * @author     Greg Beaver <cellog@php.net>
81172 * @copyright  2007-2009 The Authors
81173 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81174 * @version    Release: 1.9.4
81175 * @link       http://pear.php.net/package/PEAR
81176 * @since      Class available since Release 1.7.0
81177 */
81178class PEAR_Installer_Role_Www extends PEAR_Installer_Role_Common {}
81179?><role version="1.0">
81180 <releasetypes>php</releasetypes>
81181 <releasetypes>extsrc</releasetypes>
81182 <releasetypes>extbin</releasetypes>
81183 <releasetypes>zendextsrc</releasetypes>
81184 <releasetypes>zendextbin</releasetypes>
81185 <installable>1</installable>
81186 <locationconfig>www_dir</locationconfig>
81187 <honorsbaseinstall>1</honorsbaseinstall>
81188 <unusualbaseinstall />
81189 <phpfile />
81190 <executable />
81191 <phpextension />
81192 <config_vars />
81193</role><?php
81194/**
81195 * PEAR_PackageFile, package.xml parsing utility class
81196 *
81197 * PHP versions 4 and 5
81198 *
81199 * @category   pear
81200 * @package    PEAR
81201 * @author     Greg Beaver <cellog@php.net>
81202 * @copyright  1997-2009 The Authors
81203 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81204 * @version    CVS: $Id: PackageFile.php 313024 2011-07-06 19:51:24Z dufuz $
81205 * @link       http://pear.php.net/package/PEAR
81206 * @since      File available since Release 1.4.0a1
81207 */
81208
81209/**
81210 * needed for PEAR_VALIDATE_* constants
81211 */
81212require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validate.php';
81213/**
81214 * Error code if the package.xml <package> tag does not contain a valid version
81215 */
81216define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1);
81217/**
81218 * Error code if the package.xml <package> tag version is not supported (version 1.0 and 1.1 are the only supported versions,
81219 * currently
81220 */
81221define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2);
81222/**
81223 * Abstraction for the package.xml package description file
81224 *
81225 * @category   pear
81226 * @package    PEAR
81227 * @author     Greg Beaver <cellog@php.net>
81228 * @copyright  1997-2009 The Authors
81229 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81230 * @version    Release: 1.9.4
81231 * @link       http://pear.php.net/package/PEAR
81232 * @since      Class available since Release 1.4.0a1
81233 */
81234class PEAR_PackageFile
81235{
81236    /**
81237     * @var PEAR_Config
81238     */
81239    var $_config;
81240    var $_debug;
81241
81242    var $_logger = false;
81243    /**
81244     * @var boolean
81245     */
81246    var $_rawReturn = false;
81247
81248    /**
81249     * helper for extracting Archive_Tar errors
81250     * @var array
81251     * @access private
81252     */
81253    var $_extractErrors = array();
81254
81255    /**
81256     *
81257     * @param   PEAR_Config $config
81258     * @param   ?   $debug
81259     * @param   string @tmpdir Optional temporary directory for uncompressing
81260     *          files
81261     */
81262    function PEAR_PackageFile(&$config, $debug = false)
81263    {
81264        $this->_config = $config;
81265        $this->_debug = $debug;
81266    }
81267
81268    /**
81269     * Turn off validation - return a parsed package.xml without checking it
81270     *
81271     * This is used by the package-validate command
81272     */
81273    function rawReturn()
81274    {
81275        $this->_rawReturn = true;
81276    }
81277
81278    function setLogger(&$l)
81279    {
81280        $this->_logger = &$l;
81281    }
81282
81283    /**
81284     * Create a PEAR_PackageFile_Parser_v* of a given version.
81285     * @param   int $version
81286     * @return  PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1
81287     */
81288    function &parserFactory($version)
81289    {
81290        if (!in_array($version{0}, array('1', '2'))) {
81291            $a = false;
81292            return $a;
81293        }
81294
81295        include_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/Parser/v' . $version{0} . '.php';
81296        $version = $version{0};
81297        $class = "PEAR_PackageFile_Parser_v$version";
81298        $a = new $class;
81299        return $a;
81300    }
81301
81302    /**
81303     * For simpler unit-testing
81304     * @return string
81305     */
81306    function getClassPrefix()
81307    {
81308        return 'PEAR_PackageFile_v';
81309    }
81310
81311    /**
81312     * Create a PEAR_PackageFile_v* of a given version.
81313     * @param   int $version
81314     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v1
81315     */
81316    function &factory($version)
81317    {
81318        if (!in_array($version{0}, array('1', '2'))) {
81319            $a = false;
81320            return $a;
81321        }
81322
81323        include_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v' . $version{0} . '.php';
81324        $version = $version{0};
81325        $class = $this->getClassPrefix() . $version;
81326        $a = new $class;
81327        return $a;
81328    }
81329
81330    /**
81331     * Create a PEAR_PackageFile_v* from its toArray() method
81332     *
81333     * WARNING: no validation is performed, the array is assumed to be valid,
81334     * always parse from xml if you want validation.
81335     * @param   array $arr
81336     * @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2
81337     * @uses    factory() to construct the returned object.
81338     */
81339    function &fromArray($arr)
81340    {
81341        if (isset($arr['xsdversion'])) {
81342            $obj = &$this->factory($arr['xsdversion']);
81343            if ($this->_logger) {
81344                $obj->setLogger($this->_logger);
81345            }
81346
81347            $obj->setConfig($this->_config);
81348            $obj->fromArray($arr);
81349            return $obj;
81350        }
81351
81352        if (isset($arr['package']['attribs']['version'])) {
81353            $obj = &$this->factory($arr['package']['attribs']['version']);
81354        } else {
81355            $obj = &$this->factory('1.0');
81356        }
81357
81358        if ($this->_logger) {
81359            $obj->setLogger($this->_logger);
81360        }
81361
81362        $obj->setConfig($this->_config);
81363        $obj->fromArray($arr);
81364        return $obj;
81365    }
81366
81367    /**
81368     * Create a PEAR_PackageFile_v* from an XML string.
81369     * @access  public
81370     * @param   string $data contents of package.xml file
81371     * @param   int $state package state (one of PEAR_VALIDATE_* constants)
81372     * @param   string $file full path to the package.xml file (and the files
81373     *          it references)
81374     * @param   string $archive optional name of the archive that the XML was
81375     *          extracted from, if any
81376     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
81377     * @uses    parserFactory() to construct a parser to load the package.
81378     */
81379    function &fromXmlString($data, $state, $file, $archive = false)
81380    {
81381        if (preg_match('/<package[^>]+version=[\'"]([0-9]+\.[0-9]+)[\'"]/', $data, $packageversion)) {
81382            if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) {
81383                return PEAR::raiseError('package.xml version "' . $packageversion[1] .
81384                    '" is not supported, only 1.0, 2.0, and 2.1 are supported.');
81385            }
81386
81387            $object = &$this->parserFactory($packageversion[1]);
81388            if ($this->_logger) {
81389                $object->setLogger($this->_logger);
81390            }
81391
81392            $object->setConfig($this->_config);
81393            $pf = $object->parse($data, $file, $archive);
81394            if (PEAR::isError($pf)) {
81395                return $pf;
81396            }
81397
81398            if ($this->_rawReturn) {
81399                return $pf;
81400            }
81401
81402            if (!$pf->validate($state)) {;
81403                if ($this->_config->get('verbose') > 0
81404                    && $this->_logger && $pf->getValidationWarnings(false)
81405                ) {
81406                    foreach ($pf->getValidationWarnings(false) as $warning) {
81407                        $this->_logger->log(0, 'ERROR: ' . $warning['message']);
81408                    }
81409                }
81410
81411                $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
81412                    2, null, null, $pf->getValidationWarnings());
81413                return $a;
81414            }
81415
81416            if ($this->_logger && $pf->getValidationWarnings(false)) {
81417                foreach ($pf->getValidationWarnings() as $warning) {
81418                    $this->_logger->log(0, 'WARNING: ' . $warning['message']);
81419                }
81420            }
81421
81422            if (method_exists($pf, 'flattenFilelist')) {
81423                $pf->flattenFilelist(); // for v2
81424            }
81425
81426            return $pf;
81427        } elseif (preg_match('/<package[^>]+version=[\'"]([^"\']+)[\'"]/', $data, $packageversion)) {
81428            $a = PEAR::raiseError('package.xml file "' . $file .
81429                '" has unsupported package.xml <package> version "' . $packageversion[1] . '"');
81430            return $a;
81431        } else {
81432            if (!class_exists('PEAR_ErrorStack')) {
81433                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ErrorStack.php';
81434            }
81435
81436            PEAR_ErrorStack::staticPush('PEAR_PackageFile',
81437                PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION,
81438                'warning', array('xml' => $data), 'package.xml "' . $file .
81439                    '" has no package.xml <package> version');
81440            $object = &$this->parserFactory('1.0');
81441            $object->setConfig($this->_config);
81442            $pf = $object->parse($data, $file, $archive);
81443            if (PEAR::isError($pf)) {
81444                return $pf;
81445            }
81446
81447            if ($this->_rawReturn) {
81448                return $pf;
81449            }
81450
81451            if (!$pf->validate($state)) {
81452                $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
81453                    2, null, null, $pf->getValidationWarnings());
81454                return $a;
81455            }
81456
81457            if ($this->_logger && $pf->getValidationWarnings(false)) {
81458                foreach ($pf->getValidationWarnings() as $warning) {
81459                    $this->_logger->log(0, 'WARNING: ' . $warning['message']);
81460                }
81461            }
81462
81463            if (method_exists($pf, 'flattenFilelist')) {
81464                $pf->flattenFilelist(); // for v2
81465            }
81466
81467            return $pf;
81468        }
81469    }
81470
81471    /**
81472     * Register a temporary file or directory.  When the destructor is
81473     * executed, all registered temporary files and directories are
81474     * removed.
81475     *
81476     * @param string  $file  name of file or directory
81477     * @return  void
81478     */
81479    function addTempFile($file)
81480    {
81481        $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
81482    }
81483
81484    /**
81485     * Create a PEAR_PackageFile_v* from a compresed Tar or Tgz file.
81486     * @access  public
81487     * @param string contents of package.xml file
81488     * @param int package state (one of PEAR_VALIDATE_* constants)
81489     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
81490     * @using   Archive_Tar to extract the files
81491     * @using   fromPackageFile() to load the package after the package.xml
81492     *          file is extracted.
81493     */
81494    function &fromTgzFile($file, $state)
81495    {
81496        if (!class_exists('Archive_Tar')) {
81497            require_once 'phar://install-pear-nozlib.phar/' . 'Archive/Tar.php';
81498        }
81499
81500        $tar = new Archive_Tar($file);
81501        if ($this->_debug <= 1) {
81502            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
81503        }
81504
81505        $content = $tar->listContent();
81506        if ($this->_debug <= 1) {
81507            $tar->popErrorHandling();
81508        }
81509
81510        if (!is_array($content)) {
81511            if (is_string($file) && strlen($file < 255) &&
81512                  (!file_exists($file) || !@is_file($file))) {
81513                $ret = PEAR::raiseError("could not open file \"$file\"");
81514                return $ret;
81515            }
81516
81517            $file = realpath($file);
81518            $ret = PEAR::raiseError("Could not get contents of package \"$file\"".
81519                                     '. Invalid tgz file.');
81520            return $ret;
81521        }
81522
81523        if (!count($content) && !@is_file($file)) {
81524            $ret = PEAR::raiseError("could not open file \"$file\"");
81525            return $ret;
81526        }
81527
81528        $xml      = null;
81529        $origfile = $file;
81530        foreach ($content as $file) {
81531            $name = $file['filename'];
81532            if ($name == 'package2.xml') { // allow a .tgz to distribute both versions
81533                $xml = $name;
81534                break;
81535            }
81536
81537            if ($name == 'package.xml') {
81538                $xml = $name;
81539                break;
81540            } elseif (preg_match('/package.xml$/', $name, $match)) {
81541                $xml = $name;
81542                break;
81543            }
81544        }
81545
81546        $tmpdir = System::mktemp('-t "' . $this->_config->get('temp_dir') . '" -d pear');
81547        if ($tmpdir === false) {
81548            $ret = PEAR::raiseError("there was a problem with getting the configured temp directory");
81549            return $ret;
81550        }
81551
81552        PEAR_PackageFile::addTempFile($tmpdir);
81553
81554        $this->_extractErrors();
81555        PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors'));
81556
81557        if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
81558            $extra = implode("\n", $this->_extractErrors());
81559            if ($extra) {
81560                $extra = ' ' . $extra;
81561            }
81562
81563            PEAR::staticPopErrorHandling();
81564            $ret = PEAR::raiseError('could not extract the package.xml file from "' .
81565                $origfile . '"' . $extra);
81566            return $ret;
81567        }
81568
81569        PEAR::staticPopErrorHandling();
81570        $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile);
81571        return $ret;
81572    }
81573
81574    /**
81575     * helper callback for extracting Archive_Tar errors
81576     *
81577     * @param PEAR_Error|null $err
81578     * @return array
81579     * @access private
81580     */
81581    function _extractErrors($err = null)
81582    {
81583        static $errors = array();
81584        if ($err === null) {
81585            $e = $errors;
81586            $errors = array();
81587            return $e;
81588        }
81589        $errors[] = $err->getMessage();
81590    }
81591
81592    /**
81593     * Create a PEAR_PackageFile_v* from a package.xml file.
81594     *
81595     * @access public
81596     * @param   string  $descfile  name of package xml file
81597     * @param   int     $state package state (one of PEAR_VALIDATE_* constants)
81598     * @param   string|false $archive name of the archive this package.xml came
81599     *          from, if any
81600     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
81601     * @uses    PEAR_PackageFile::fromXmlString to create the oject after the
81602     *          XML is loaded from the package.xml file.
81603     */
81604    function &fromPackageFile($descfile, $state, $archive = false)
81605    {
81606        $fp = false;
81607        if (is_string($descfile) && strlen($descfile) < 255 &&
81608             (
81609              !file_exists($descfile) || !is_file($descfile) || !is_readable($descfile)
81610              || (!$fp = @fopen($descfile, 'r'))
81611             )
81612        ) {
81613            $a = PEAR::raiseError("Unable to open $descfile");
81614            return $a;
81615        }
81616
81617        // read the whole thing so we only get one cdata callback
81618        // for each block of cdata
81619        fclose($fp);
81620        $data = file_get_contents($descfile);
81621        $ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive);
81622        return $ret;
81623    }
81624
81625    /**
81626     * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file.
81627     *
81628     * This method is able to extract information about a package from a .tgz
81629     * archive or from a XML package definition file.
81630     *
81631     * @access public
81632     * @param   string  $info file name
81633     * @param   int     $state package state (one of PEAR_VALIDATE_* constants)
81634     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
81635     * @uses    fromPackageFile() if the file appears to be XML
81636     * @uses    fromTgzFile() to load all non-XML files
81637     */
81638    function &fromAnyFile($info, $state)
81639    {
81640        if (is_dir($info)) {
81641            $dir_name = realpath($info);
81642            if (file_exists($dir_name . '/package.xml')) {
81643                $info = PEAR_PackageFile::fromPackageFile($dir_name .  '/package.xml', $state);
81644            } elseif (file_exists($dir_name .  '/package2.xml')) {
81645                $info = PEAR_PackageFile::fromPackageFile($dir_name .  '/package2.xml', $state);
81646            } else {
81647                $info = PEAR::raiseError("No package definition found in '$info' directory");
81648            }
81649
81650            return $info;
81651        }
81652
81653        $fp = false;
81654        if (is_string($info) && strlen($info) < 255 &&
81655             (file_exists($info) || ($fp = @fopen($info, 'r')))
81656        ) {
81657
81658            if ($fp) {
81659                fclose($fp);
81660            }
81661
81662            $tmp = substr($info, -4);
81663            if ($tmp == '.xml') {
81664                $info = &PEAR_PackageFile::fromPackageFile($info, $state);
81665            } elseif ($tmp == '.tar' || $tmp == '.tgz') {
81666                $info = &PEAR_PackageFile::fromTgzFile($info, $state);
81667            } else {
81668                $fp   = fopen($info, 'r');
81669                $test = fread($fp, 5);
81670                fclose($fp);
81671                if ($test == '<?xml') {
81672                    $info = &PEAR_PackageFile::fromPackageFile($info, $state);
81673                } else {
81674                    $info = &PEAR_PackageFile::fromTgzFile($info, $state);
81675                }
81676            }
81677
81678            return $info;
81679        }
81680
81681        $info = PEAR::raiseError("Cannot open '$info' for parsing");
81682        return $info;
81683    }
81684}<?php
81685/**
81686 * package.xml generation class, package.xml version 1.0
81687 *
81688 * PHP versions 4 and 5
81689 *
81690 * @category   pear
81691 * @package    PEAR
81692 * @author     Greg Beaver <cellog@php.net>
81693 * @copyright  1997-2009 The Authors
81694 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81695 * @version    CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $
81696 * @link       http://pear.php.net/package/PEAR
81697 * @since      File available since Release 1.4.0a1
81698 */
81699/**
81700 * needed for PEAR_VALIDATE_* constants
81701 */
81702require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validate.php';
81703require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
81704require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
81705/**
81706 * This class converts a PEAR_PackageFile_v1 object into any output format.
81707 *
81708 * Supported output formats include array, XML string, and a PEAR_PackageFile_v2
81709 * object, for converting package.xml 1.0 into package.xml 2.0 with no sweat.
81710 * @category   pear
81711 * @package    PEAR
81712 * @author     Greg Beaver <cellog@php.net>
81713 * @copyright  1997-2009 The Authors
81714 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
81715 * @version    Release: 1.9.4
81716 * @link       http://pear.php.net/package/PEAR
81717 * @since      Class available since Release 1.4.0a1
81718 */
81719class PEAR_PackageFile_Generator_v1
81720{
81721    /**
81722     * @var PEAR_PackageFile_v1
81723     */
81724    var $_packagefile;
81725    function PEAR_PackageFile_Generator_v1(&$packagefile)
81726    {
81727        $this->_packagefile = &$packagefile;
81728    }
81729
81730    function getPackagerVersion()
81731    {
81732        return '1.9.4';
81733    }
81734
81735    /**
81736     * @param PEAR_Packager
81737     * @param bool if true, a .tgz is written, otherwise a .tar is written
81738     * @param string|null directory in which to save the .tgz
81739     * @return string|PEAR_Error location of package or error object
81740     */
81741    function toTgz(&$packager, $compress = true, $where = null)
81742    {
81743        require_once 'phar://install-pear-nozlib.phar/' . 'Archive/Tar.php';
81744        if ($where === null) {
81745            if (!($where = System::mktemp(array('-d')))) {
81746                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: mktemp failed');
81747            }
81748        } elseif (!@System::mkDir(array('-p', $where))) {
81749            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: "' . $where . '" could' .
81750                ' not be created');
81751        }
81752        if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') &&
81753              !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) {
81754            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: unable to save package.xml as' .
81755                ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"');
81756        }
81757        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
81758            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: invalid package file');
81759        }
81760        $pkginfo = $this->_packagefile->getArray();
81761        $ext = $compress ? '.tgz' : '.tar';
81762        $pkgver = $pkginfo['package'] . '-' . $pkginfo['version'];
81763        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
81764        if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) &&
81765              !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) {
81766            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: cannot create tgz file "' .
81767                getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"');
81768        }
81769        if ($pkgfile = $this->_packagefile->getPackageFile()) {
81770            $pkgdir = dirname(realpath($pkgfile));
81771            $pkgfile = basename($pkgfile);
81772        } else {
81773            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: package file object must ' .
81774                'be created from a real file');
81775        }
81776        // {{{ Create the package file list
81777        $filelist = array();
81778        $i = 0;
81779
81780        foreach ($this->_packagefile->getFilelist() as $fname => $atts) {
81781            $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
81782            if (!file_exists($file)) {
81783                return PEAR::raiseError("File does not exist: $fname");
81784            } else {
81785                $filelist[$i++] = $file;
81786                if (!isset($atts['md5sum'])) {
81787                    $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($file));
81788                }
81789                $packager->log(2, "Adding file $fname");
81790            }
81791        }
81792        // }}}
81793        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
81794        if ($packagexml) {
81795            $tar =& new Archive_Tar($dest_package, $compress);
81796            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
81797            // ----- Creates with the package.xml file
81798            $ok = $tar->createModify(array($packagexml), '', $where);
81799            if (PEAR::isError($ok)) {
81800                return $ok;
81801            } elseif (!$ok) {
81802                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
81803            }
81804            // ----- Add the content of the package
81805            if (!$tar->addModify($filelist, $pkgver, $pkgdir)) {
81806                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
81807            }
81808            return $dest_package;
81809        }
81810    }
81811
81812    /**
81813     * @param string|null directory to place the package.xml in, or null for a temporary dir
81814     * @param int one of the PEAR_VALIDATE_* constants
81815     * @param string name of the generated file
81816     * @param bool if true, then no analysis will be performed on role="php" files
81817     * @return string|PEAR_Error path to the created file on success
81818     */
81819    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml',
81820                           $nofilechecking = false)
81821    {
81822        if (!$this->_packagefile->validate($state, $nofilechecking)) {
81823            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: invalid package.xml',
81824                null, null, null, $this->_packagefile->getValidationWarnings());
81825        }
81826        if ($where === null) {
81827            if (!($where = System::mktemp(array('-d')))) {
81828                return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: mktemp failed');
81829            }
81830        } elseif (!@System::mkDir(array('-p', $where))) {
81831            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: "' . $where . '" could' .
81832                ' not be created');
81833        }
81834        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
81835        $np = @fopen($newpkgfile, 'wb');
81836        if (!$np) {
81837            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: unable to save ' .
81838               "$name as $newpkgfile");
81839        }
81840        fwrite($np, $this->toXml($state, true));
81841        fclose($np);
81842        return $newpkgfile;
81843    }
81844
81845    /**
81846     * fix both XML encoding to be UTF8, and replace standard XML entities < > " & '
81847     *
81848     * @param string $string
81849     * @return string
81850     * @access private
81851     */
81852    function _fixXmlEncoding($string)
81853    {
81854        if (version_compare(phpversion(), '5.0.0', 'lt')) {
81855            $string = utf8_encode($string);
81856        }
81857        return strtr($string, array(
81858                                          '&'  => '&amp;',
81859                                          '>'  => '&gt;',
81860                                          '<'  => '&lt;',
81861                                          '"'  => '&quot;',
81862                                          '\'' => '&apos;' ));
81863    }
81864
81865    /**
81866     * Return an XML document based on the package info (as returned
81867     * by the PEAR_Common::infoFrom* methods).
81868     *
81869     * @return string XML data
81870     */
81871    function toXml($state = PEAR_VALIDATE_NORMAL, $nofilevalidation = false)
81872    {
81873        $this->_packagefile->setDate(date('Y-m-d'));
81874        if (!$this->_packagefile->validate($state, $nofilevalidation)) {
81875            return false;
81876        }
81877        $pkginfo = $this->_packagefile->getArray();
81878        static $maint_map = array(
81879            "handle" => "user",
81880            "name" => "name",
81881            "email" => "email",
81882            "role" => "role",
81883            );
81884        $ret = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
81885        $ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/dtd/package-1.0\">\n";
81886        $ret .= "<package version=\"1.0\" packagerversion=\"1.9.4\">\n" .
81887" <name>$pkginfo[package]</name>";
81888        if (isset($pkginfo['extends'])) {
81889            $ret .= "\n<extends>$pkginfo[extends]</extends>";
81890        }
81891        $ret .=
81892 "\n <summary>".$this->_fixXmlEncoding($pkginfo['summary'])."</summary>\n" .
81893" <description>".trim($this->_fixXmlEncoding($pkginfo['description']))."\n </description>\n" .
81894" <maintainers>\n";
81895        foreach ($pkginfo['maintainers'] as $maint) {
81896            $ret .= "  <maintainer>\n";
81897            foreach ($maint_map as $idx => $elm) {
81898                $ret .= "   <$elm>";
81899                $ret .= $this->_fixXmlEncoding($maint[$idx]);
81900                $ret .= "</$elm>\n";
81901            }
81902            $ret .= "  </maintainer>\n";
81903        }
81904        $ret .= "  </maintainers>\n";
81905        $ret .= $this->_makeReleaseXml($pkginfo, false, $state);
81906        if (isset($pkginfo['changelog']) && count($pkginfo['changelog']) > 0) {
81907            $ret .= " <changelog>\n";
81908            foreach ($pkginfo['changelog'] as $oldrelease) {
81909                $ret .= $this->_makeReleaseXml($oldrelease, true);
81910            }
81911            $ret .= " </changelog>\n";
81912        }
81913        $ret .= "</package>\n";
81914        return $ret;
81915    }
81916
81917    // }}}
81918    // {{{ _makeReleaseXml()
81919
81920    /**
81921     * Generate part of an XML description with release information.
81922     *
81923     * @param array  $pkginfo    array with release information
81924     * @param bool   $changelog  whether the result will be in a changelog element
81925     *
81926     * @return string XML data
81927     *
81928     * @access private
81929     */
81930    function _makeReleaseXml($pkginfo, $changelog = false, $state = PEAR_VALIDATE_NORMAL)
81931    {
81932        // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
81933        $indent = $changelog ? "  " : "";
81934        $ret = "$indent <release>\n";
81935        if (!empty($pkginfo['version'])) {
81936            $ret .= "$indent  <version>$pkginfo[version]</version>\n";
81937        }
81938        if (!empty($pkginfo['release_date'])) {
81939            $ret .= "$indent  <date>$pkginfo[release_date]</date>\n";
81940        }
81941        if (!empty($pkginfo['release_license'])) {
81942            $ret .= "$indent  <license>$pkginfo[release_license]</license>\n";
81943        }
81944        if (!empty($pkginfo['release_state'])) {
81945            $ret .= "$indent  <state>$pkginfo[release_state]</state>\n";
81946        }
81947        if (!empty($pkginfo['release_notes'])) {
81948            $ret .= "$indent  <notes>".trim($this->_fixXmlEncoding($pkginfo['release_notes']))
81949            ."\n$indent  </notes>\n";
81950        }
81951        if (!empty($pkginfo['release_warnings'])) {
81952            $ret .= "$indent  <warnings>".$this->_fixXmlEncoding($pkginfo['release_warnings'])."</warnings>\n";
81953        }
81954        if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
81955            $ret .= "$indent  <deps>\n";
81956            foreach ($pkginfo['release_deps'] as $dep) {
81957                $ret .= "$indent   <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
81958                if (isset($dep['version'])) {
81959                    $ret .= " version=\"$dep[version]\"";
81960                }
81961                if (isset($dep['optional'])) {
81962                    $ret .= " optional=\"$dep[optional]\"";
81963                }
81964                if (isset($dep['name'])) {
81965                    $ret .= ">$dep[name]</dep>\n";
81966                } else {
81967                    $ret .= "/>\n";
81968                }
81969            }
81970            $ret .= "$indent  </deps>\n";
81971        }
81972        if (isset($pkginfo['configure_options'])) {
81973            $ret .= "$indent  <configureoptions>\n";
81974            foreach ($pkginfo['configure_options'] as $c) {
81975                $ret .= "$indent   <configureoption name=\"".
81976                    $this->_fixXmlEncoding($c['name']) . "\"";
81977                if (isset($c['default'])) {
81978                    $ret .= " default=\"" . $this->_fixXmlEncoding($c['default']) . "\"";
81979                }
81980                $ret .= " prompt=\"" . $this->_fixXmlEncoding($c['prompt']) . "\"";
81981                $ret .= "/>\n";
81982            }
81983            $ret .= "$indent  </configureoptions>\n";
81984        }
81985        if (isset($pkginfo['provides'])) {
81986            foreach ($pkginfo['provides'] as $key => $what) {
81987                $ret .= "$indent  <provides type=\"$what[type]\" ";
81988                $ret .= "name=\"$what[name]\" ";
81989                if (isset($what['extends'])) {
81990                    $ret .= "extends=\"$what[extends]\" ";
81991                }
81992                $ret .= "/>\n";
81993            }
81994        }
81995        if (isset($pkginfo['filelist'])) {
81996            $ret .= "$indent  <filelist>\n";
81997            if ($state ^ PEAR_VALIDATE_PACKAGING) {
81998                $ret .= $this->recursiveXmlFilelist($pkginfo['filelist']);
81999            } else {
82000                foreach ($pkginfo['filelist'] as $file => $fa) {
82001                    if (!isset($fa['role'])) {
82002                        $fa['role'] = '';
82003                    }
82004                    $ret .= "$indent   <file role=\"$fa[role]\"";
82005                    if (isset($fa['baseinstalldir'])) {
82006                        $ret .= ' baseinstalldir="' .
82007                            $this->_fixXmlEncoding($fa['baseinstalldir']) . '"';
82008                    }
82009                    if (isset($fa['md5sum'])) {
82010                        $ret .= " md5sum=\"$fa[md5sum]\"";
82011                    }
82012                    if (isset($fa['platform'])) {
82013                        $ret .= " platform=\"$fa[platform]\"";
82014                    }
82015                    if (!empty($fa['install-as'])) {
82016                        $ret .= ' install-as="' .
82017                            $this->_fixXmlEncoding($fa['install-as']) . '"';
82018                    }
82019                    $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
82020                    if (empty($fa['replacements'])) {
82021                        $ret .= "/>\n";
82022                    } else {
82023                        $ret .= ">\n";
82024                        foreach ($fa['replacements'] as $r) {
82025                            $ret .= "$indent    <replace";
82026                            foreach ($r as $k => $v) {
82027                                $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
82028                            }
82029                            $ret .= "/>\n";
82030                        }
82031                        $ret .= "$indent   </file>\n";
82032                    }
82033                }
82034            }
82035            $ret .= "$indent  </filelist>\n";
82036        }
82037        $ret .= "$indent </release>\n";
82038        return $ret;
82039    }
82040
82041    /**
82042     * @param array
82043     * @access protected
82044     */
82045    function recursiveXmlFilelist($list)
82046    {
82047        $this->_dirs = array();
82048        foreach ($list as $file => $attributes) {
82049            $this->_addDir($this->_dirs, explode('/', dirname($file)), $file, $attributes);
82050        }
82051        return $this->_formatDir($this->_dirs);
82052    }
82053
82054    /**
82055     * @param array
82056     * @param array
82057     * @param string|null
82058     * @param array|null
82059     * @access private
82060     */
82061    function _addDir(&$dirs, $dir, $file = null, $attributes = null)
82062    {
82063        if ($dir == array() || $dir == array('.')) {
82064            $dirs['files'][basename($file)] = $attributes;
82065            return;
82066        }
82067        $curdir = array_shift($dir);
82068        if (!isset($dirs['dirs'][$curdir])) {
82069            $dirs['dirs'][$curdir] = array();
82070        }
82071        $this->_addDir($dirs['dirs'][$curdir], $dir, $file, $attributes);
82072    }
82073
82074    /**
82075     * @param array
82076     * @param string
82077     * @param string
82078     * @access private
82079     */
82080    function _formatDir($dirs, $indent = '', $curdir = '')
82081    {
82082        $ret = '';
82083        if (!count($dirs)) {
82084            return '';
82085        }
82086        if (isset($dirs['dirs'])) {
82087            uksort($dirs['dirs'], 'strnatcasecmp');
82088            foreach ($dirs['dirs'] as $dir => $contents) {
82089                $usedir = "$curdir/$dir";
82090                $ret .= "$indent   <dir name=\"$dir\">\n";
82091                $ret .= $this->_formatDir($contents, "$indent ", $usedir);
82092                $ret .= "$indent   </dir> <!-- $usedir -->\n";
82093            }
82094        }
82095        if (isset($dirs['files'])) {
82096            uksort($dirs['files'], 'strnatcasecmp');
82097            foreach ($dirs['files'] as $file => $attribs) {
82098                $ret .= $this->_formatFile($file, $attribs, $indent);
82099            }
82100        }
82101        return $ret;
82102    }
82103
82104    /**
82105     * @param string
82106     * @param array
82107     * @param string
82108     * @access private
82109     */
82110    function _formatFile($file, $attributes, $indent)
82111    {
82112        $ret = "$indent   <file role=\"$attributes[role]\"";
82113        if (isset($attributes['baseinstalldir'])) {
82114            $ret .= ' baseinstalldir="' .
82115                $this->_fixXmlEncoding($attributes['baseinstalldir']) . '"';
82116        }
82117        if (isset($attributes['md5sum'])) {
82118            $ret .= " md5sum=\"$attributes[md5sum]\"";
82119        }
82120        if (isset($attributes['platform'])) {
82121            $ret .= " platform=\"$attributes[platform]\"";
82122        }
82123        if (!empty($attributes['install-as'])) {
82124            $ret .= ' install-as="' .
82125                $this->_fixXmlEncoding($attributes['install-as']) . '"';
82126        }
82127        $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
82128        if (empty($attributes['replacements'])) {
82129            $ret .= "/>\n";
82130        } else {
82131            $ret .= ">\n";
82132            foreach ($attributes['replacements'] as $r) {
82133                $ret .= "$indent    <replace";
82134                foreach ($r as $k => $v) {
82135                    $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
82136                }
82137                $ret .= "/>\n";
82138            }
82139            $ret .= "$indent   </file>\n";
82140        }
82141        return $ret;
82142    }
82143
82144    // {{{ _unIndent()
82145
82146    /**
82147     * Unindent given string (?)
82148     *
82149     * @param string $str The string that has to be unindented.
82150     * @return string
82151     * @access private
82152     */
82153    function _unIndent($str)
82154    {
82155        // remove leading newlines
82156        $str = preg_replace('/^[\r\n]+/', '', $str);
82157        // find whitespace at the beginning of the first line
82158        $indent_len = strspn($str, " \t");
82159        $indent = substr($str, 0, $indent_len);
82160        $data = '';
82161        // remove the same amount of whitespace from following lines
82162        foreach (explode("\n", $str) as $line) {
82163            if (substr($line, 0, $indent_len) == $indent) {
82164                $data .= substr($line, $indent_len) . "\n";
82165            }
82166        }
82167        return $data;
82168    }
82169
82170    /**
82171     * @return array
82172     */
82173    function dependenciesToV2()
82174    {
82175        $arr = array();
82176        $this->_convertDependencies2_0($arr);
82177        return $arr['dependencies'];
82178    }
82179
82180    /**
82181     * Convert a package.xml version 1.0 into version 2.0
82182     *
82183     * Note that this does a basic conversion, to allow more advanced
82184     * features like bundles and multiple releases
82185     * @param string the classname to instantiate and return.  This must be
82186     *               PEAR_PackageFile_v2 or a descendant
82187     * @param boolean if true, only valid, deterministic package.xml 1.0 as defined by the
82188     *                strictest parameters will be converted
82189     * @return PEAR_PackageFile_v2|PEAR_Error
82190     */
82191    function &toV2($class = 'PEAR_PackageFile_v2', $strict = false)
82192    {
82193        if ($strict) {
82194            if (!$this->_packagefile->validate()) {
82195                $a = PEAR::raiseError('invalid package.xml version 1.0 cannot be converted' .
82196                    ' to version 2.0', null, null, null,
82197                    $this->_packagefile->getValidationWarnings(true));
82198                return $a;
82199            }
82200        }
82201
82202        $arr = array(
82203            'attribs' => array(
82204                             'version' => '2.0',
82205                             'xmlns' => 'http://pear.php.net/dtd/package-2.0',
82206                             'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
82207                             'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
82208                             'xsi:schemaLocation' => "http://pear.php.net/dtd/tasks-1.0\n" .
82209"http://pear.php.net/dtd/tasks-1.0.xsd\n" .
82210"http://pear.php.net/dtd/package-2.0\n" .
82211'http://pear.php.net/dtd/package-2.0.xsd',
82212                         ),
82213            'name' => $this->_packagefile->getPackage(),
82214            'channel' => 'pear.php.net',
82215        );
82216        $arr['summary'] = $this->_packagefile->getSummary();
82217        $arr['description'] = $this->_packagefile->getDescription();
82218        $maintainers = $this->_packagefile->getMaintainers();
82219        foreach ($maintainers as $maintainer) {
82220            if ($maintainer['role'] != 'lead') {
82221                continue;
82222            }
82223            $new = array(
82224                'name' => $maintainer['name'],
82225                'user' => $maintainer['handle'],
82226                'email' => $maintainer['email'],
82227                'active' => 'yes',
82228            );
82229            $arr['lead'][] = $new;
82230        }
82231
82232        if (!isset($arr['lead'])) { // some people... you know?
82233            $arr['lead'] = array(
82234                'name' => 'unknown',
82235                'user' => 'unknown',
82236                'email' => 'noleadmaintainer@example.com',
82237                'active' => 'no',
82238            );
82239        }
82240
82241        if (count($arr['lead']) == 1) {
82242            $arr['lead'] = $arr['lead'][0];
82243        }
82244
82245        foreach ($maintainers as $maintainer) {
82246            if ($maintainer['role'] == 'lead') {
82247                continue;
82248            }
82249            $new = array(
82250                'name' => $maintainer['name'],
82251                'user' => $maintainer['handle'],
82252                'email' => $maintainer['email'],
82253                'active' => 'yes',
82254            );
82255            $arr[$maintainer['role']][] = $new;
82256        }
82257
82258        if (isset($arr['developer']) && count($arr['developer']) == 1) {
82259            $arr['developer'] = $arr['developer'][0];
82260        }
82261
82262        if (isset($arr['contributor']) && count($arr['contributor']) == 1) {
82263            $arr['contributor'] = $arr['contributor'][0];
82264        }
82265
82266        if (isset($arr['helper']) && count($arr['helper']) == 1) {
82267            $arr['helper'] = $arr['helper'][0];
82268        }
82269
82270        $arr['date'] = $this->_packagefile->getDate();
82271        $arr['version'] =
82272            array(
82273                'release' => $this->_packagefile->getVersion(),
82274                'api' => $this->_packagefile->getVersion(),
82275            );
82276        $arr['stability'] =
82277            array(
82278                'release' => $this->_packagefile->getState(),
82279                'api' => $this->_packagefile->getState(),
82280            );
82281        $licensemap =
82282            array(
82283                'php' => 'http://www.php.net/license',
82284                'php license' => 'http://www.php.net/license',
82285                'lgpl' => 'http://www.gnu.org/copyleft/lesser.html',
82286                'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
82287                'bsd style' => 'http://www.opensource.org/licenses/bsd-license.php',
82288                'bsd-style' => 'http://www.opensource.org/licenses/bsd-license.php',
82289                'mit' => 'http://www.opensource.org/licenses/mit-license.php',
82290                'gpl' => 'http://www.gnu.org/copyleft/gpl.html',
82291                'apache' => 'http://www.opensource.org/licenses/apache2.0.php'
82292            );
82293
82294        if (isset($licensemap[strtolower($this->_packagefile->getLicense())])) {
82295            $arr['license'] = array(
82296                'attribs' => array('uri' =>
82297                    $licensemap[strtolower($this->_packagefile->getLicense())]),
82298                '_content' => $this->_packagefile->getLicense()
82299                );
82300        } else {
82301            // don't use bogus uri
82302            $arr['license'] = $this->_packagefile->getLicense();
82303        }
82304
82305        $arr['notes'] = $this->_packagefile->getNotes();
82306        $temp = array();
82307        $arr['contents'] = $this->_convertFilelist2_0($temp);
82308        $this->_convertDependencies2_0($arr);
82309        $release = ($this->_packagefile->getConfigureOptions() || $this->_isExtension) ?
82310            'extsrcrelease' : 'phprelease';
82311        if ($release == 'extsrcrelease') {
82312            $arr['channel'] = 'pecl.php.net';
82313            $arr['providesextension'] = $arr['name']; // assumption
82314        }
82315
82316        $arr[$release] = array();
82317        if ($this->_packagefile->getConfigureOptions()) {
82318            $arr[$release]['configureoption'] = $this->_packagefile->getConfigureOptions();
82319            foreach ($arr[$release]['configureoption'] as $i => $opt) {
82320                $arr[$release]['configureoption'][$i] = array('attribs' => $opt);
82321            }
82322            if (count($arr[$release]['configureoption']) == 1) {
82323                $arr[$release]['configureoption'] = $arr[$release]['configureoption'][0];
82324            }
82325        }
82326
82327        $this->_convertRelease2_0($arr[$release], $temp);
82328        if ($release == 'extsrcrelease' && count($arr[$release]) > 1) {
82329            // multiple extsrcrelease tags added in PEAR 1.4.1
82330            $arr['dependencies']['required']['pearinstaller']['min'] = '1.4.1';
82331        }
82332
82333        if ($cl = $this->_packagefile->getChangelog()) {
82334            foreach ($cl as $release) {
82335                $rel = array();
82336                $rel['version'] =
82337                    array(
82338                        'release' => $release['version'],
82339                        'api' => $release['version'],
82340                    );
82341                if (!isset($release['release_state'])) {
82342                    $release['release_state'] = 'stable';
82343                }
82344
82345                $rel['stability'] =
82346                    array(
82347                        'release' => $release['release_state'],
82348                        'api' => $release['release_state'],
82349                    );
82350                if (isset($release['release_date'])) {
82351                    $rel['date'] = $release['release_date'];
82352                } else {
82353                    $rel['date'] = date('Y-m-d');
82354                }
82355
82356                if (isset($release['release_license'])) {
82357                    if (isset($licensemap[strtolower($release['release_license'])])) {
82358                        $uri = $licensemap[strtolower($release['release_license'])];
82359                    } else {
82360                        $uri = 'http://www.example.com';
82361                    }
82362                    $rel['license'] = array(
82363                            'attribs' => array('uri' => $uri),
82364                            '_content' => $release['release_license']
82365                        );
82366                } else {
82367                    $rel['license'] = $arr['license'];
82368                }
82369
82370                if (!isset($release['release_notes'])) {
82371                    $release['release_notes'] = 'no release notes';
82372                }
82373
82374                $rel['notes'] = $release['release_notes'];
82375                $arr['changelog']['release'][] = $rel;
82376            }
82377        }
82378
82379        $ret = new $class;
82380        $ret->setConfig($this->_packagefile->_config);
82381        if (isset($this->_packagefile->_logger) && is_object($this->_packagefile->_logger)) {
82382            $ret->setLogger($this->_packagefile->_logger);
82383        }
82384
82385        $ret->fromArray($arr);
82386        return $ret;
82387    }
82388
82389    /**
82390     * @param array
82391     * @param bool
82392     * @access private
82393     */
82394    function _convertDependencies2_0(&$release, $internal = false)
82395    {
82396        $peardep = array('pearinstaller' =>
82397            array('min' => '1.4.0b1')); // this is a lot safer
82398        $required = $optional = array();
82399        $release['dependencies'] = array('required' => array());
82400        if ($this->_packagefile->hasDeps()) {
82401            foreach ($this->_packagefile->getDeps() as $dep) {
82402                if (!isset($dep['optional']) || $dep['optional'] == 'no') {
82403                    $required[] = $dep;
82404                } else {
82405                    $optional[] = $dep;
82406                }
82407            }
82408            foreach (array('required', 'optional') as $arr) {
82409                $deps = array();
82410                foreach ($$arr as $dep) {
82411                    // organize deps by dependency type and name
82412                    if (!isset($deps[$dep['type']])) {
82413                        $deps[$dep['type']] = array();
82414                    }
82415                    if (isset($dep['name'])) {
82416                        $deps[$dep['type']][$dep['name']][] = $dep;
82417                    } else {
82418                        $deps[$dep['type']][] = $dep;
82419                    }
82420                }
82421                do {
82422                    if (isset($deps['php'])) {
82423                        $php = array();
82424                        if (count($deps['php']) > 1) {
82425                            $php = $this->_processPhpDeps($deps['php']);
82426                        } else {
82427                            if (!isset($deps['php'][0])) {
82428                                list($key, $blah) = each ($deps['php']); // stupid buggy versions
82429                                $deps['php'] = array($blah[0]);
82430                            }
82431                            $php = $this->_processDep($deps['php'][0]);
82432                            if (!$php) {
82433                                break; // poor mans throw
82434                            }
82435                        }
82436                        $release['dependencies'][$arr]['php'] = $php;
82437                    }
82438                } while (false);
82439                do {
82440                    if (isset($deps['pkg'])) {
82441                        $pkg = array();
82442                        $pkg = $this->_processMultipleDepsName($deps['pkg']);
82443                        if (!$pkg) {
82444                            break; // poor mans throw
82445                        }
82446                        $release['dependencies'][$arr]['package'] = $pkg;
82447                    }
82448                } while (false);
82449                do {
82450                    if (isset($deps['ext'])) {
82451                        $pkg = array();
82452                        $pkg = $this->_processMultipleDepsName($deps['ext']);
82453                        $release['dependencies'][$arr]['extension'] = $pkg;
82454                    }
82455                } while (false);
82456                // skip sapi - it's not supported so nobody will have used it
82457                // skip os - it's not supported in 1.0
82458            }
82459        }
82460        if (isset($release['dependencies']['required'])) {
82461            $release['dependencies']['required'] =
82462                array_merge($peardep, $release['dependencies']['required']);
82463        } else {
82464            $release['dependencies']['required'] = $peardep;
82465        }
82466        if (!isset($release['dependencies']['required']['php'])) {
82467            $release['dependencies']['required']['php'] =
82468                array('min' => '4.0.0');
82469        }
82470        $order = array();
82471        $bewm = $release['dependencies']['required'];
82472        $order['php'] = $bewm['php'];
82473        $order['pearinstaller'] = $bewm['pearinstaller'];
82474        isset($bewm['package']) ? $order['package'] = $bewm['package'] :0;
82475        isset($bewm['extension']) ? $order['extension'] = $bewm['extension'] :0;
82476        $release['dependencies']['required'] = $order;
82477    }
82478
82479    /**
82480     * @param array
82481     * @access private
82482     */
82483    function _convertFilelist2_0(&$package)
82484    {
82485        $ret = array('dir' =>
82486                    array(
82487                        'attribs' => array('name' => '/'),
82488                        'file' => array()
82489                        )
82490                    );
82491        $package['platform'] =
82492        $package['install-as'] = array();
82493        $this->_isExtension = false;
82494        foreach ($this->_packagefile->getFilelist() as $name => $file) {
82495            $file['name'] = $name;
82496            if (isset($file['role']) && $file['role'] == 'src') {
82497                $this->_isExtension = true;
82498            }
82499            if (isset($file['replacements'])) {
82500                $repl = $file['replacements'];
82501                unset($file['replacements']);
82502            } else {
82503                unset($repl);
82504            }
82505            if (isset($file['install-as'])) {
82506                $package['install-as'][$name] = $file['install-as'];
82507                unset($file['install-as']);
82508            }
82509            if (isset($file['platform'])) {
82510                $package['platform'][$name] = $file['platform'];
82511                unset($file['platform']);
82512            }
82513            $file = array('attribs' => $file);
82514            if (isset($repl)) {
82515                foreach ($repl as $replace ) {
82516                    $file['tasks:replace'][] = array('attribs' => $replace);
82517                }
82518                if (count($repl) == 1) {
82519                    $file['tasks:replace'] = $file['tasks:replace'][0];
82520                }
82521            }
82522            $ret['dir']['file'][] = $file;
82523        }
82524        return $ret;
82525    }
82526
82527    /**
82528     * Post-process special files with install-as/platform attributes and
82529     * make the release tag.
82530     *
82531     * This complex method follows this work-flow to create the release tags:
82532     *
82533     * <pre>
82534     * - if any install-as/platform exist, create a generic release and fill it with
82535     *   o <install as=..> tags for <file name=... install-as=...>
82536     *   o <install as=..> tags for <file name=... platform=!... install-as=..>
82537     *   o <ignore> tags for <file name=... platform=...>
82538     *   o <ignore> tags for <file name=... platform=... install-as=..>
82539     * - create a release for each platform encountered and fill with
82540     *   o <install as..> tags for <file name=... install-as=...>
82541     *   o <install as..> tags for <file name=... platform=this platform install-as=..>
82542     *   o <install as..> tags for <file name=... platform=!other platform install-as=..>
82543     *   o <ignore> tags for <file name=... platform=!this platform>
82544     *   o <ignore> tags for <file name=... platform=other platform>
82545     *   o <ignore> tags for <file name=... platform=other platform install-as=..>
82546     *   o <ignore> tags for <file name=... platform=!this platform install-as=..>
82547     * </pre>
82548     *
82549     * It does this by accessing the $package parameter, which contains an array with
82550     * indices:
82551     *
82552     *  - platform: mapping of file => OS the file should be installed on
82553     *  - install-as: mapping of file => installed name
82554     *  - osmap: mapping of OS => list of files that should be installed
82555     *    on that OS
82556     *  - notosmap: mapping of OS => list of files that should not be
82557     *    installed on that OS
82558     *
82559     * @param array
82560     * @param array
82561     * @access private
82562     */
82563    function _convertRelease2_0(&$release, $package)
82564    {
82565        //- if any install-as/platform exist, create a generic release and fill it with
82566        if (count($package['platform']) || count($package['install-as'])) {
82567            $generic = array();
82568            $genericIgnore = array();
82569            foreach ($package['install-as'] as $file => $as) {
82570                //o <install as=..> tags for <file name=... install-as=...>
82571                if (!isset($package['platform'][$file])) {
82572                    $generic[] = $file;
82573                    continue;
82574                }
82575                //o <install as=..> tags for <file name=... platform=!... install-as=..>
82576                if (isset($package['platform'][$file]) &&
82577                      $package['platform'][$file]{0} == '!') {
82578                    $generic[] = $file;
82579                    continue;
82580                }
82581                //o <ignore> tags for <file name=... platform=... install-as=..>
82582                if (isset($package['platform'][$file]) &&
82583                      $package['platform'][$file]{0} != '!') {
82584                    $genericIgnore[] = $file;
82585                    continue;
82586                }
82587            }
82588            foreach ($package['platform'] as $file => $platform) {
82589                if (isset($package['install-as'][$file])) {
82590                    continue;
82591                }
82592                if ($platform{0} != '!') {
82593                    //o <ignore> tags for <file name=... platform=...>
82594                    $genericIgnore[] = $file;
82595                }
82596            }
82597            if (count($package['platform'])) {
82598                $oses = $notplatform = $platform = array();
82599                foreach ($package['platform'] as $file => $os) {
82600                    // get a list of oses
82601                    if ($os{0} == '!') {
82602                        if (isset($oses[substr($os, 1)])) {
82603                            continue;
82604                        }
82605                        $oses[substr($os, 1)] = count($oses);
82606                    } else {
82607                        if (isset($oses[$os])) {
82608                            continue;
82609                        }
82610                        $oses[$os] = count($oses);
82611                    }
82612                }
82613                //- create a release for each platform encountered and fill with
82614                foreach ($oses as $os => $releaseNum) {
82615                    $release[$releaseNum]['installconditions']['os']['name'] = $os;
82616                    $release[$releaseNum]['filelist'] = array('install' => array(),
82617                        'ignore' => array());
82618                    foreach ($package['install-as'] as $file => $as) {
82619                        //o <install as=..> tags for <file name=... install-as=...>
82620                        if (!isset($package['platform'][$file])) {
82621                            $release[$releaseNum]['filelist']['install'][] =
82622                                array(
82623                                    'attribs' => array(
82624                                        'name' => $file,
82625                                        'as' => $as,
82626                                    ),
82627                                );
82628                            continue;
82629                        }
82630                        //o <install as..> tags for
82631                        //  <file name=... platform=this platform install-as=..>
82632                        if (isset($package['platform'][$file]) &&
82633                              $package['platform'][$file] == $os) {
82634                            $release[$releaseNum]['filelist']['install'][] =
82635                                array(
82636                                    'attribs' => array(
82637                                        'name' => $file,
82638                                        'as' => $as,
82639                                    ),
82640                                );
82641                            continue;
82642                        }
82643                        //o <install as..> tags for
82644                        //  <file name=... platform=!other platform install-as=..>
82645                        if (isset($package['platform'][$file]) &&
82646                              $package['platform'][$file] != "!$os" &&
82647                              $package['platform'][$file]{0} == '!') {
82648                            $release[$releaseNum]['filelist']['install'][] =
82649                                array(
82650                                    'attribs' => array(
82651                                        'name' => $file,
82652                                        'as' => $as,
82653                                    ),
82654                                );
82655                            continue;
82656                        }
82657                        //o <ignore> tags for
82658                        //  <file name=... platform=!this platform install-as=..>
82659                        if (isset($package['platform'][$file]) &&
82660                              $package['platform'][$file] == "!$os") {
82661                            $release[$releaseNum]['filelist']['ignore'][] =
82662                                array(
82663                                    'attribs' => array(
82664                                        'name' => $file,
82665                                    ),
82666                                );
82667                            continue;
82668                        }
82669                        //o <ignore> tags for
82670                        //  <file name=... platform=other platform install-as=..>
82671                        if (isset($package['platform'][$file]) &&
82672                              $package['platform'][$file]{0} != '!' &&
82673                              $package['platform'][$file] != $os) {
82674                            $release[$releaseNum]['filelist']['ignore'][] =
82675                                array(
82676                                    'attribs' => array(
82677                                        'name' => $file,
82678                                    ),
82679                                );
82680                            continue;
82681                        }
82682                    }
82683                    foreach ($package['platform'] as $file => $platform) {
82684                        if (isset($package['install-as'][$file])) {
82685                            continue;
82686                        }
82687                        //o <ignore> tags for <file name=... platform=!this platform>
82688                        if ($platform == "!$os") {
82689                            $release[$releaseNum]['filelist']['ignore'][] =
82690                                array(
82691                                    'attribs' => array(
82692                                        'name' => $file,
82693                                    ),
82694                                );
82695                            continue;
82696                        }
82697                        //o <ignore> tags for <file name=... platform=other platform>
82698                        if ($platform{0} != '!' && $platform != $os) {
82699                            $release[$releaseNum]['filelist']['ignore'][] =
82700                                array(
82701                                    'attribs' => array(
82702                                        'name' => $file,
82703                                    ),
82704                                );
82705                        }
82706                    }
82707                    if (!count($release[$releaseNum]['filelist']['install'])) {
82708                        unset($release[$releaseNum]['filelist']['install']);
82709                    }
82710                    if (!count($release[$releaseNum]['filelist']['ignore'])) {
82711                        unset($release[$releaseNum]['filelist']['ignore']);
82712                    }
82713                }
82714                if (count($generic) || count($genericIgnore)) {
82715                    $release[count($oses)] = array();
82716                    if (count($generic)) {
82717                        foreach ($generic as $file) {
82718                            if (isset($package['install-as'][$file])) {
82719                                $installas = $package['install-as'][$file];
82720                            } else {
82721                                $installas = $file;
82722                            }
82723                            $release[count($oses)]['filelist']['install'][] =
82724                                array(
82725                                    'attribs' => array(
82726                                        'name' => $file,
82727                                        'as' => $installas,
82728                                    )
82729                                );
82730                        }
82731                    }
82732                    if (count($genericIgnore)) {
82733                        foreach ($genericIgnore as $file) {
82734                            $release[count($oses)]['filelist']['ignore'][] =
82735                                array(
82736                                    'attribs' => array(
82737                                        'name' => $file,
82738                                    )
82739                                );
82740                        }
82741                    }
82742                }
82743                // cleanup
82744                foreach ($release as $i => $rel) {
82745                    if (isset($rel['filelist']['install']) &&
82746                          count($rel['filelist']['install']) == 1) {
82747                        $release[$i]['filelist']['install'] =
82748                            $release[$i]['filelist']['install'][0];
82749                    }
82750                    if (isset($rel['filelist']['ignore']) &&
82751                          count($rel['filelist']['ignore']) == 1) {
82752                        $release[$i]['filelist']['ignore'] =
82753                            $release[$i]['filelist']['ignore'][0];
82754                    }
82755                }
82756                if (count($release) == 1) {
82757                    $release = $release[0];
82758                }
82759            } else {
82760                // no platform atts, but some install-as atts
82761                foreach ($package['install-as'] as $file => $value) {
82762                    $release['filelist']['install'][] =
82763                        array(
82764                            'attribs' => array(
82765                                'name' => $file,
82766                                'as' => $value
82767                            )
82768                        );
82769                }
82770                if (count($release['filelist']['install']) == 1) {
82771                    $release['filelist']['install'] = $release['filelist']['install'][0];
82772                }
82773            }
82774        }
82775    }
82776
82777    /**
82778     * @param array
82779     * @return array
82780     * @access private
82781     */
82782    function _processDep($dep)
82783    {
82784        if ($dep['type'] == 'php') {
82785            if ($dep['rel'] == 'has') {
82786                // come on - everyone has php!
82787                return false;
82788            }
82789        }
82790        $php = array();
82791        if ($dep['type'] != 'php') {
82792            $php['name'] = $dep['name'];
82793            if ($dep['type'] == 'pkg') {
82794                $php['channel'] = 'pear.php.net';
82795            }
82796        }
82797        switch ($dep['rel']) {
82798            case 'gt' :
82799                $php['min'] = $dep['version'];
82800                $php['exclude'] = $dep['version'];
82801            break;
82802            case 'ge' :
82803                if (!isset($dep['version'])) {
82804                    if ($dep['type'] == 'php') {
82805                        if (isset($dep['name'])) {
82806                            $dep['version'] = $dep['name'];
82807                        }
82808                    }
82809                }
82810                $php['min'] = $dep['version'];
82811            break;
82812            case 'lt' :
82813                $php['max'] = $dep['version'];
82814                $php['exclude'] = $dep['version'];
82815            break;
82816            case 'le' :
82817                $php['max'] = $dep['version'];
82818            break;
82819            case 'eq' :
82820                $php['min'] = $dep['version'];
82821                $php['max'] = $dep['version'];
82822            break;
82823            case 'ne' :
82824                $php['exclude'] = $dep['version'];
82825            break;
82826            case 'not' :
82827                $php['conflicts'] = 'yes';
82828            break;
82829        }
82830        return $php;
82831    }
82832
82833    /**
82834     * @param array
82835     * @return array
82836     */
82837    function _processPhpDeps($deps)
82838    {
82839        $test = array();
82840        foreach ($deps as $dep) {
82841            $test[] = $this->_processDep($dep);
82842        }
82843        $min = array();
82844        $max = array();
82845        foreach ($test as $dep) {
82846            if (!$dep) {
82847                continue;
82848            }
82849            if (isset($dep['min'])) {
82850                $min[$dep['min']] = count($min);
82851            }
82852            if (isset($dep['max'])) {
82853                $max[$dep['max']] = count($max);
82854            }
82855        }
82856        if (count($min) > 0) {
82857            uksort($min, 'version_compare');
82858        }
82859        if (count($max) > 0) {
82860            uksort($max, 'version_compare');
82861        }
82862        if (count($min)) {
82863            // get the highest minimum
82864            $min = array_pop($a = array_flip($min));
82865        } else {
82866            $min = false;
82867        }
82868        if (count($max)) {
82869            // get the lowest maximum
82870            $max = array_shift($a = array_flip($max));
82871        } else {
82872            $max = false;
82873        }
82874        if ($min) {
82875            $php['min'] = $min;
82876        }
82877        if ($max) {
82878            $php['max'] = $max;
82879        }
82880        $exclude = array();
82881        foreach ($test as $dep) {
82882            if (!isset($dep['exclude'])) {
82883                continue;
82884            }
82885            $exclude[] = $dep['exclude'];
82886        }
82887        if (count($exclude)) {
82888            $php['exclude'] = $exclude;
82889        }
82890        return $php;
82891    }
82892
82893    /**
82894     * process multiple dependencies that have a name, like package deps
82895     * @param array
82896     * @return array
82897     * @access private
82898     */
82899    function _processMultipleDepsName($deps)
82900    {
82901        $ret = $tests = array();
82902        foreach ($deps as $name => $dep) {
82903            foreach ($dep as $d) {
82904                $tests[$name][] = $this->_processDep($d);
82905            }
82906        }
82907
82908        foreach ($tests as $name => $test) {
82909            $max = $min = $php = array();
82910            $php['name'] = $name;
82911            foreach ($test as $dep) {
82912                if (!$dep) {
82913                    continue;
82914                }
82915                if (isset($dep['channel'])) {
82916                    $php['channel'] = 'pear.php.net';
82917                }
82918                if (isset($dep['conflicts']) && $dep['conflicts'] == 'yes') {
82919                    $php['conflicts'] = 'yes';
82920                }
82921                if (isset($dep['min'])) {
82922                    $min[$dep['min']] = count($min);
82923                }
82924                if (isset($dep['max'])) {
82925                    $max[$dep['max']] = count($max);
82926                }
82927            }
82928            if (count($min) > 0) {
82929                uksort($min, 'version_compare');
82930            }
82931            if (count($max) > 0) {
82932                uksort($max, 'version_compare');
82933            }
82934            if (count($min)) {
82935                // get the highest minimum
82936                $min = array_pop($a = array_flip($min));
82937            } else {
82938                $min = false;
82939            }
82940            if (count($max)) {
82941                // get the lowest maximum
82942                $max = array_shift($a = array_flip($max));
82943            } else {
82944                $max = false;
82945            }
82946            if ($min) {
82947                $php['min'] = $min;
82948            }
82949            if ($max) {
82950                $php['max'] = $max;
82951            }
82952            $exclude = array();
82953            foreach ($test as $dep) {
82954                if (!isset($dep['exclude'])) {
82955                    continue;
82956                }
82957                $exclude[] = $dep['exclude'];
82958            }
82959            if (count($exclude)) {
82960                $php['exclude'] = $exclude;
82961            }
82962            $ret[] = $php;
82963        }
82964        return $ret;
82965    }
82966}
82967?><?php
82968/**
82969 * package.xml generation class, package.xml version 2.0
82970 *
82971 * PHP versions 4 and 5
82972 *
82973 * @category   pear
82974 * @package    PEAR
82975 * @author     Greg Beaver <cellog@php.net>
82976 * @author     Stephan Schmidt (original XML_Serializer code)
82977 * @copyright  1997-2009 The Authors
82978 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
82979 * @version    CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
82980 * @link       http://pear.php.net/package/PEAR
82981 * @since      File available since Release 1.4.0a1
82982 */
82983/**
82984 * file/dir manipulation routines
82985 */
82986require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
82987require_once 'phar://install-pear-nozlib.phar/' . 'XML/Util.php';
82988
82989/**
82990 * This class converts a PEAR_PackageFile_v2 object into any output format.
82991 *
82992 * Supported output formats include array, XML string (using S. Schmidt's
82993 * XML_Serializer, slightly customized)
82994 * @category   pear
82995 * @package    PEAR
82996 * @author     Greg Beaver <cellog@php.net>
82997 * @author     Stephan Schmidt (original XML_Serializer code)
82998 * @copyright  1997-2009 The Authors
82999 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
83000 * @version    Release: 1.9.4
83001 * @link       http://pear.php.net/package/PEAR
83002 * @since      Class available since Release 1.4.0a1
83003 */
83004class PEAR_PackageFile_Generator_v2
83005{
83006   /**
83007    * default options for the serialization
83008    * @access private
83009    * @var array $_defaultOptions
83010    */
83011    var $_defaultOptions = array(
83012        'indent'             => ' ',                    // string used for indentation
83013        'linebreak'          => "\n",                  // string used for newlines
83014        'typeHints'          => false,                 // automatically add type hin attributes
83015        'addDecl'            => true,                 // add an XML declaration
83016        'defaultTagName'     => 'XML_Serializer_Tag',  // tag used for indexed arrays or invalid names
83017        'classAsTagName'     => false,                 // use classname for objects in indexed arrays
83018        'keyAttribute'       => '_originalKey',        // attribute where original key is stored
83019        'typeAttribute'      => '_type',               // attribute for type (only if typeHints => true)
83020        'classAttribute'     => '_class',              // attribute for class of objects (only if typeHints => true)
83021        'scalarAsAttributes' => false,                 // scalar values (strings, ints,..) will be serialized as attribute
83022        'prependAttributes'  => '',                    // prepend string for attributes
83023        'indentAttributes'   => false,                 // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
83024        'mode'               => 'simplexml',             // use 'simplexml' to use parent name as tagname if transforming an indexed array
83025        'addDoctype'         => false,                 // add a doctype declaration
83026        'doctype'            => null,                  // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
83027        'rootName'           => 'package',                  // name of the root tag
83028        'rootAttributes'     => array(
83029            'version' => '2.0',
83030            'xmlns' => 'http://pear.php.net/dtd/package-2.0',
83031            'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
83032            'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
83033            'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
83034http://pear.php.net/dtd/tasks-1.0.xsd
83035http://pear.php.net/dtd/package-2.0
83036http://pear.php.net/dtd/package-2.0.xsd',
83037        ),               // attributes of the root tag
83038        'attributesArray'    => 'attribs',                  // all values in this key will be treated as attributes
83039        'contentName'        => '_content',                   // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
83040        'beautifyFilelist'   => false,
83041        'encoding' => 'UTF-8',
83042    );
83043
83044   /**
83045    * options for the serialization
83046    * @access private
83047    * @var array $options
83048    */
83049    var $options = array();
83050
83051   /**
83052    * current tag depth
83053    * @var integer $_tagDepth
83054    */
83055    var $_tagDepth = 0;
83056
83057   /**
83058    * serilialized representation of the data
83059    * @var string $_serializedData
83060    */
83061    var $_serializedData = null;
83062    /**
83063     * @var PEAR_PackageFile_v2
83064     */
83065    var $_packagefile;
83066    /**
83067     * @param PEAR_PackageFile_v2
83068     */
83069    function PEAR_PackageFile_Generator_v2(&$packagefile)
83070    {
83071        $this->_packagefile = &$packagefile;
83072        if (isset($this->_packagefile->encoding)) {
83073            $this->_defaultOptions['encoding'] = $this->_packagefile->encoding;
83074        }
83075    }
83076
83077    /**
83078     * @return string
83079     */
83080    function getPackagerVersion()
83081    {
83082        return '1.9.4';
83083    }
83084
83085    /**
83086     * @param PEAR_Packager
83087     * @param bool generate a .tgz or a .tar
83088     * @param string|null temporary directory to package in
83089     */
83090    function toTgz(&$packager, $compress = true, $where = null)
83091    {
83092        $a = null;
83093        return $this->toTgz2($packager, $a, $compress, $where);
83094    }
83095
83096    /**
83097     * Package up both a package.xml and package2.xml for the same release
83098     * @param PEAR_Packager
83099     * @param PEAR_PackageFile_v1
83100     * @param bool generate a .tgz or a .tar
83101     * @param string|null temporary directory to package in
83102     */
83103    function toTgz2(&$packager, &$pf1, $compress = true, $where = null)
83104    {
83105        require_once 'phar://install-pear-nozlib.phar/' . 'Archive/Tar.php';
83106        if (!$this->_packagefile->isEquivalent($pf1)) {
83107            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' .
83108                basename($pf1->getPackageFile()) .
83109                '" is not equivalent to "' . basename($this->_packagefile->getPackageFile())
83110                . '"');
83111        }
83112
83113        if ($where === null) {
83114            if (!($where = System::mktemp(array('-d')))) {
83115                return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed');
83116            }
83117        } elseif (!@System::mkDir(array('-p', $where))) {
83118            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' .
83119                ' not be created');
83120        }
83121
83122        $file = $where . DIRECTORY_SEPARATOR . 'package.xml';
83123        if (file_exists($file) && !is_file($file)) {
83124            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' .
83125                ' "' . $file  .'"');
83126        }
83127
83128        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
83129            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml');
83130        }
83131
83132        $ext = $compress ? '.tgz' : '.tar';
83133        $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion();
83134        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
83135        if (file_exists($dest_package) && !is_file($dest_package)) {
83136            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' .
83137                $dest_package . '"');
83138        }
83139
83140        $pkgfile = $this->_packagefile->getPackageFile();
83141        if (!$pkgfile) {
83142            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' .
83143                'be created from a real file');
83144        }
83145
83146        $pkgdir  = dirname(realpath($pkgfile));
83147        $pkgfile = basename($pkgfile);
83148
83149        // {{{ Create the package file list
83150        $filelist = array();
83151        $i = 0;
83152        $this->_packagefile->flattenFilelist();
83153        $contents = $this->_packagefile->getContents();
83154        if (isset($contents['bundledpackage'])) { // bundles of packages
83155            $contents = $contents['bundledpackage'];
83156            if (!isset($contents[0])) {
83157                $contents = array($contents);
83158            }
83159
83160            $packageDir = $where;
83161            foreach ($contents as $i => $package) {
83162                $fname = $package;
83163                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
83164                if (!file_exists($file)) {
83165                    return $packager->raiseError("File does not exist: $fname");
83166                }
83167
83168                $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
83169                System::mkdir(array('-p', dirname($tfile)));
83170                copy($file, $tfile);
83171                $filelist[$i++] = $tfile;
83172                $packager->log(2, "Adding package $fname");
83173            }
83174        } else { // normal packages
83175            $contents = $contents['dir']['file'];
83176            if (!isset($contents[0])) {
83177                $contents = array($contents);
83178            }
83179
83180            $packageDir = $where;
83181            foreach ($contents as $i => $file) {
83182                $fname = $file['attribs']['name'];
83183                $atts = $file['attribs'];
83184                $orig = $file;
83185                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
83186                if (!file_exists($file)) {
83187                    return $packager->raiseError("File does not exist: $fname");
83188                }
83189
83190                $origperms = fileperms($file);
83191                $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
83192                unset($orig['attribs']);
83193                if (count($orig)) { // file with tasks
83194                    // run any package-time tasks
83195                    $contents = file_get_contents($file);
83196                    foreach ($orig as $tag => $raw) {
83197                        $tag = str_replace(
83198                            array($this->_packagefile->getTasksNs() . ':', '-'),
83199                            array('', '_'), $tag);
83200                        $task = "PEAR_Task_$tag";
83201                        $task = &new $task($this->_packagefile->_config,
83202                            $this->_packagefile->_logger,
83203                            PEAR_TASK_PACKAGE);
83204                        $task->init($raw, $atts, null);
83205                        $res = $task->startSession($this->_packagefile, $contents, $tfile);
83206                        if (!$res) {
83207                            continue; // skip this task
83208                        }
83209
83210                        if (PEAR::isError($res)) {
83211                            return $res;
83212                        }
83213
83214                        $contents = $res; // save changes
83215                        System::mkdir(array('-p', dirname($tfile)));
83216                        $wp = fopen($tfile, "wb");
83217                        fwrite($wp, $contents);
83218                        fclose($wp);
83219                    }
83220                }
83221
83222                if (!file_exists($tfile)) {
83223                    System::mkdir(array('-p', dirname($tfile)));
83224                    copy($file, $tfile);
83225                }
83226
83227                chmod($tfile, $origperms);
83228                $filelist[$i++] = $tfile;
83229                $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1);
83230                $packager->log(2, "Adding file $fname");
83231            }
83232        }
83233            // }}}
83234
83235        $name       = $pf1 !== null ? 'package2.xml' : 'package.xml';
83236        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name);
83237        if ($packagexml) {
83238            $tar =& new Archive_Tar($dest_package, $compress);
83239            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
83240            // ----- Creates with the package.xml file
83241            $ok = $tar->createModify(array($packagexml), '', $where);
83242            if (PEAR::isError($ok)) {
83243                return $packager->raiseError($ok);
83244            } elseif (!$ok) {
83245                return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name .
83246                    ' failed');
83247            }
83248
83249            // ----- Add the content of the package
83250            if (!$tar->addModify($filelist, $pkgver, $where)) {
83251                return $packager->raiseError(
83252                    'PEAR_Packagefile_v2::toTgz(): tarball creation failed');
83253            }
83254
83255            // add the package.xml version 1.0
83256            if ($pf1 !== null) {
83257                $pfgen = &$pf1->getDefaultGenerator();
83258                $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
83259                if (!$tar->addModify(array($packagexml1), '', $where)) {
83260                    return $packager->raiseError(
83261                        'PEAR_Packagefile_v2::toTgz(): adding package.xml failed');
83262                }
83263            }
83264
83265            return $dest_package;
83266        }
83267    }
83268
83269    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml')
83270    {
83271        if (!$this->_packagefile->validate($state)) {
83272            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml',
83273                null, null, null, $this->_packagefile->getValidationWarnings());
83274        }
83275
83276        if ($where === null) {
83277            if (!($where = System::mktemp(array('-d')))) {
83278                return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed');
83279            }
83280        } elseif (!@System::mkDir(array('-p', $where))) {
83281            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' .
83282                ' not be created');
83283        }
83284
83285        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
83286        $np = @fopen($newpkgfile, 'wb');
83287        if (!$np) {
83288            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' .
83289               "$name as $newpkgfile");
83290        }
83291        fwrite($np, $this->toXml($state));
83292        fclose($np);
83293        return $newpkgfile;
83294    }
83295
83296    function &toV2()
83297    {
83298        return $this->_packagefile;
83299    }
83300
83301    /**
83302     * Return an XML document based on the package info (as returned
83303     * by the PEAR_Common::infoFrom* methods).
83304     *
83305     * @return string XML data
83306     */
83307    function toXml($state = PEAR_VALIDATE_NORMAL, $options = array())
83308    {
83309        $this->_packagefile->setDate(date('Y-m-d'));
83310        $this->_packagefile->setTime(date('H:i:s'));
83311        if (!$this->_packagefile->validate($state)) {
83312            return false;
83313        }
83314
83315        if (is_array($options)) {
83316            $this->options = array_merge($this->_defaultOptions, $options);
83317        } else {
83318            $this->options = $this->_defaultOptions;
83319        }
83320
83321        $arr = $this->_packagefile->getArray();
83322        if (isset($arr['filelist'])) {
83323            unset($arr['filelist']);
83324        }
83325
83326        if (isset($arr['_lastversion'])) {
83327            unset($arr['_lastversion']);
83328        }
83329
83330        // Fix the notes a little bit
83331        if (isset($arr['notes'])) {
83332            // This trims out the indenting, needs fixing
83333            $arr['notes'] = "\n" . trim($arr['notes']) . "\n";
83334        }
83335
83336        if (isset($arr['changelog']) && !empty($arr['changelog'])) {
83337            // Fix for inconsistency how the array is filled depending on the changelog release amount
83338            if (!isset($arr['changelog']['release'][0])) {
83339                $release = $arr['changelog']['release'];
83340                unset($arr['changelog']['release']);
83341
83342                $arr['changelog']['release']    = array();
83343                $arr['changelog']['release'][0] = $release;
83344            }
83345
83346            foreach (array_keys($arr['changelog']['release']) as $key) {
83347                $c =& $arr['changelog']['release'][$key];
83348                if (isset($c['notes'])) {
83349                    // This trims out the indenting, needs fixing
83350                    $c['notes'] = "\n" . trim($c['notes']) . "\n";
83351                }
83352            }
83353        }
83354
83355        if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) {
83356            $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']);
83357            unset($arr['contents']['dir']['file']);
83358            if (isset($use['dir'])) {
83359                $arr['contents']['dir']['dir'] = $use['dir'];
83360            }
83361            if (isset($use['file'])) {
83362                $arr['contents']['dir']['file'] = $use['file'];
83363            }
83364            $this->options['beautifyFilelist'] = true;
83365        }
83366
83367        $arr['attribs']['packagerversion'] = '1.9.4';
83368        if ($this->serialize($arr, $options)) {
83369            return $this->_serializedData . "\n";
83370        }
83371
83372        return false;
83373    }
83374
83375
83376    function _recursiveXmlFilelist($list)
83377    {
83378        $dirs = array();
83379        if (isset($list['attribs'])) {
83380            $file = $list['attribs']['name'];
83381            unset($list['attribs']['name']);
83382            $attributes = $list['attribs'];
83383            $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes);
83384        } else {
83385            foreach ($list as $a) {
83386                $file = $a['attribs']['name'];
83387                $attributes = $a['attribs'];
83388                unset($a['attribs']);
83389                $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a);
83390            }
83391        }
83392        $this->_formatDir($dirs);
83393        $this->_deFormat($dirs);
83394        return $dirs;
83395    }
83396
83397    function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null)
83398    {
83399        if (!$tasks) {
83400            $tasks = array();
83401        }
83402        if ($dir == array() || $dir == array('.')) {
83403            $dirs['file'][basename($file)] = $tasks;
83404            $attributes['name'] = basename($file);
83405            $dirs['file'][basename($file)]['attribs'] = $attributes;
83406            return;
83407        }
83408        $curdir = array_shift($dir);
83409        if (!isset($dirs['dir'][$curdir])) {
83410            $dirs['dir'][$curdir] = array();
83411        }
83412        $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks);
83413    }
83414
83415    function _formatDir(&$dirs)
83416    {
83417        if (!count($dirs)) {
83418            return array();
83419        }
83420        $newdirs = array();
83421        if (isset($dirs['dir'])) {
83422            $newdirs['dir'] = $dirs['dir'];
83423        }
83424        if (isset($dirs['file'])) {
83425            $newdirs['file'] = $dirs['file'];
83426        }
83427        $dirs = $newdirs;
83428        if (isset($dirs['dir'])) {
83429            uksort($dirs['dir'], 'strnatcasecmp');
83430            foreach ($dirs['dir'] as $dir => $contents) {
83431                $this->_formatDir($dirs['dir'][$dir]);
83432            }
83433        }
83434        if (isset($dirs['file'])) {
83435            uksort($dirs['file'], 'strnatcasecmp');
83436        };
83437    }
83438
83439    function _deFormat(&$dirs)
83440    {
83441        if (!count($dirs)) {
83442            return array();
83443        }
83444        $newdirs = array();
83445        if (isset($dirs['dir'])) {
83446            foreach ($dirs['dir'] as $dir => $contents) {
83447                $newdir = array();
83448                $newdir['attribs']['name'] = $dir;
83449                $this->_deFormat($contents);
83450                foreach ($contents as $tag => $val) {
83451                    $newdir[$tag] = $val;
83452                }
83453                $newdirs['dir'][] = $newdir;
83454            }
83455            if (count($newdirs['dir']) == 1) {
83456                $newdirs['dir'] = $newdirs['dir'][0];
83457            }
83458        }
83459        if (isset($dirs['file'])) {
83460            foreach ($dirs['file'] as $name => $file) {
83461                $newdirs['file'][] = $file;
83462            }
83463            if (count($newdirs['file']) == 1) {
83464                $newdirs['file'] = $newdirs['file'][0];
83465            }
83466        }
83467        $dirs = $newdirs;
83468    }
83469
83470    /**
83471    * reset all options to default options
83472    *
83473    * @access   public
83474    * @see      setOption(), XML_Unserializer()
83475    */
83476    function resetOptions()
83477    {
83478        $this->options = $this->_defaultOptions;
83479    }
83480
83481   /**
83482    * set an option
83483    *
83484    * You can use this method if you do not want to set all options in the constructor
83485    *
83486    * @access   public
83487    * @see      resetOption(), XML_Serializer()
83488    */
83489    function setOption($name, $value)
83490    {
83491        $this->options[$name] = $value;
83492    }
83493
83494   /**
83495    * sets several options at once
83496    *
83497    * You can use this method if you do not want to set all options in the constructor
83498    *
83499    * @access   public
83500    * @see      resetOption(), XML_Unserializer(), setOption()
83501    */
83502    function setOptions($options)
83503    {
83504        $this->options = array_merge($this->options, $options);
83505    }
83506
83507   /**
83508    * serialize data
83509    *
83510    * @access   public
83511    * @param    mixed    $data data to serialize
83512    * @return   boolean  true on success, pear error on failure
83513    */
83514    function serialize($data, $options = null)
83515    {
83516        // if options have been specified, use them instead
83517        // of the previously defined ones
83518        if (is_array($options)) {
83519            $optionsBak = $this->options;
83520            if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
83521                $this->options = array_merge($this->_defaultOptions, $options);
83522            } else {
83523                $this->options = array_merge($this->options, $options);
83524            }
83525        } else {
83526            $optionsBak = null;
83527        }
83528
83529        //  start depth is zero
83530        $this->_tagDepth = 0;
83531        $this->_serializedData = '';
83532        // serialize an array
83533        if (is_array($data)) {
83534            $tagName = isset($this->options['rootName']) ? $this->options['rootName'] : 'array';
83535            $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']);
83536        }
83537
83538        // add doctype declaration
83539        if ($this->options['addDoctype'] === true) {
83540            $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype'])
83541                                   . $this->options['linebreak']
83542                                   . $this->_serializedData;
83543        }
83544
83545        //  build xml declaration
83546        if ($this->options['addDecl']) {
83547            $atts = array();
83548            $encoding = isset($this->options['encoding']) ? $this->options['encoding'] : null;
83549            $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $encoding)
83550                                   . $this->options['linebreak']
83551                                   . $this->_serializedData;
83552        }
83553
83554
83555        if ($optionsBak !== null) {
83556            $this->options = $optionsBak;
83557        }
83558
83559        return  true;
83560    }
83561
83562   /**
83563    * get the result of the serialization
83564    *
83565    * @access public
83566    * @return string serialized XML
83567    */
83568    function getSerializedData()
83569    {
83570        if ($this->_serializedData === null) {
83571            return  $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
83572        }
83573        return $this->_serializedData;
83574    }
83575
83576   /**
83577    * serialize any value
83578    *
83579    * This method checks for the type of the value and calls the appropriate method
83580    *
83581    * @access private
83582    * @param  mixed     $value
83583    * @param  string    $tagName
83584    * @param  array     $attributes
83585    * @return string
83586    */
83587    function _serializeValue($value, $tagName = null, $attributes = array())
83588    {
83589        if (is_array($value)) {
83590            $xml = $this->_serializeArray($value, $tagName, $attributes);
83591        } elseif (is_object($value)) {
83592            $xml = $this->_serializeObject($value, $tagName);
83593        } else {
83594            $tag = array(
83595                          'qname'      => $tagName,
83596                          'attributes' => $attributes,
83597                          'content'    => $value
83598                        );
83599            $xml = $this->_createXMLTag($tag);
83600        }
83601        return $xml;
83602    }
83603
83604   /**
83605    * serialize an array
83606    *
83607    * @access   private
83608    * @param    array   $array       array to serialize
83609    * @param    string  $tagName     name of the root tag
83610    * @param    array   $attributes  attributes for the root tag
83611    * @return   string  $string      serialized data
83612    * @uses     XML_Util::isValidName() to check, whether key has to be substituted
83613    */
83614    function _serializeArray(&$array, $tagName = null, $attributes = array())
83615    {
83616        $_content = null;
83617
83618        /**
83619         * check for special attributes
83620         */
83621        if ($this->options['attributesArray'] !== null) {
83622            if (isset($array[$this->options['attributesArray']])) {
83623                $attributes = $array[$this->options['attributesArray']];
83624                unset($array[$this->options['attributesArray']]);
83625            }
83626            /**
83627             * check for special content
83628             */
83629            if ($this->options['contentName'] !== null) {
83630                if (isset($array[$this->options['contentName']])) {
83631                    $_content = $array[$this->options['contentName']];
83632                    unset($array[$this->options['contentName']]);
83633                }
83634            }
83635        }
83636
83637        /*
83638        * if mode is set to simpleXML, check whether
83639        * the array is associative or indexed
83640        */
83641        if (is_array($array) && $this->options['mode'] == 'simplexml') {
83642            $indexed = true;
83643            if (!count($array)) {
83644                $indexed = false;
83645            }
83646            foreach ($array as $key => $val) {
83647                if (!is_int($key)) {
83648                    $indexed = false;
83649                    break;
83650                }
83651            }
83652
83653            if ($indexed && $this->options['mode'] == 'simplexml') {
83654                $string = '';
83655                foreach ($array as $key => $val) {
83656                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
83657                        if (!isset($this->_curdir)) {
83658                            $this->_curdir = '';
83659                        }
83660                        $savedir = $this->_curdir;
83661                        if (isset($val['attribs'])) {
83662                            if ($val['attribs']['name'] == '/') {
83663                                $this->_curdir = '/';
83664                            } else {
83665                                if ($this->_curdir == '/') {
83666                                    $this->_curdir = '';
83667                                }
83668                                $this->_curdir .= '/' . $val['attribs']['name'];
83669                            }
83670                        }
83671                    }
83672                    $string .= $this->_serializeValue( $val, $tagName, $attributes);
83673                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
83674                        $string .= ' <!-- ' . $this->_curdir . ' -->';
83675                        if (empty($savedir)) {
83676                            unset($this->_curdir);
83677                        } else {
83678                            $this->_curdir = $savedir;
83679                        }
83680                    }
83681
83682                    $string .= $this->options['linebreak'];
83683                    // do indentation
83684                    if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
83685                        $string .= str_repeat($this->options['indent'], $this->_tagDepth);
83686                    }
83687                }
83688                return rtrim($string);
83689            }
83690        }
83691
83692        if ($this->options['scalarAsAttributes'] === true) {
83693            foreach ($array as $key => $value) {
83694                if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
83695                    unset($array[$key]);
83696                    $attributes[$this->options['prependAttributes'].$key] = $value;
83697                }
83698            }
83699        }
83700
83701        // check for empty array => create empty tag
83702        if (empty($array)) {
83703            $tag = array(
83704                            'qname'      => $tagName,
83705                            'content'    => $_content,
83706                            'attributes' => $attributes
83707                        );
83708
83709        } else {
83710            $this->_tagDepth++;
83711            $tmp = $this->options['linebreak'];
83712            foreach ($array as $key => $value) {
83713                // do indentation
83714                if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
83715                    $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
83716                }
83717
83718                // copy key
83719                $origKey = $key;
83720                // key cannot be used as tagname => use default tag
83721                $valid = XML_Util::isValidName($key);
83722                if (PEAR::isError($valid)) {
83723                    if ($this->options['classAsTagName'] && is_object($value)) {
83724                        $key = get_class($value);
83725                    } else {
83726                        $key = $this->options['defaultTagName'];
83727                    }
83728                }
83729                $atts = array();
83730                if ($this->options['typeHints'] === true) {
83731                    $atts[$this->options['typeAttribute']] = gettype($value);
83732                    if ($key !== $origKey) {
83733                        $atts[$this->options['keyAttribute']] = (string)$origKey;
83734                    }
83735
83736                }
83737                if ($this->options['beautifyFilelist'] && $key == 'dir') {
83738                    if (!isset($this->_curdir)) {
83739                        $this->_curdir = '';
83740                    }
83741                    $savedir = $this->_curdir;
83742                    if (isset($value['attribs'])) {
83743                        if ($value['attribs']['name'] == '/') {
83744                            $this->_curdir = '/';
83745                        } else {
83746                            $this->_curdir .= '/' . $value['attribs']['name'];
83747                        }
83748                    }
83749                }
83750
83751                if (is_string($value) && $value && ($value{strlen($value) - 1} == "\n")) {
83752                    $value .= str_repeat($this->options['indent'], $this->_tagDepth);
83753                }
83754                $tmp .= $this->_createXMLTag(array(
83755                                                    'qname'      => $key,
83756                                                    'attributes' => $atts,
83757                                                    'content'    => $value )
83758                                            );
83759                if ($this->options['beautifyFilelist'] && $key == 'dir') {
83760                    if (isset($value['attribs'])) {
83761                        $tmp .= ' <!-- ' . $this->_curdir . ' -->';
83762                        if (empty($savedir)) {
83763                            unset($this->_curdir);
83764                        } else {
83765                            $this->_curdir = $savedir;
83766                        }
83767                    }
83768                }
83769                $tmp .= $this->options['linebreak'];
83770            }
83771
83772            $this->_tagDepth--;
83773            if ($this->options['indent']!==null && $this->_tagDepth>0) {
83774                $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
83775            }
83776
83777            if (trim($tmp) === '') {
83778                $tmp = null;
83779            }
83780
83781            $tag = array(
83782                'qname'      => $tagName,
83783                'content'    => $tmp,
83784                'attributes' => $attributes
83785            );
83786        }
83787        if ($this->options['typeHints'] === true) {
83788            if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
83789                $tag['attributes'][$this->options['typeAttribute']] = 'array';
83790            }
83791        }
83792
83793        $string = $this->_createXMLTag($tag, false);
83794        return $string;
83795    }
83796
83797   /**
83798    * create a tag from an array
83799    * this method awaits an array in the following format
83800    * array(
83801    *       'qname'        => $tagName,
83802    *       'attributes'   => array(),
83803    *       'content'      => $content,      // optional
83804    *       'namespace'    => $namespace     // optional
83805    *       'namespaceUri' => $namespaceUri  // optional
83806    *   )
83807    *
83808    * @access   private
83809    * @param    array   $tag tag definition
83810    * @param    boolean $replaceEntities whether to replace XML entities in content or not
83811    * @return   string  $string XML tag
83812    */
83813    function _createXMLTag($tag, $replaceEntities = true)
83814    {
83815        if ($this->options['indentAttributes'] !== false) {
83816            $multiline = true;
83817            $indent    = str_repeat($this->options['indent'], $this->_tagDepth);
83818
83819            if ($this->options['indentAttributes'] == '_auto') {
83820                $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
83821
83822            } else {
83823                $indent .= $this->options['indentAttributes'];
83824            }
83825        } else {
83826            $indent = $multiline = false;
83827        }
83828
83829        if (is_array($tag['content'])) {
83830            if (empty($tag['content'])) {
83831                $tag['content'] = '';
83832            }
83833        } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
83834            $tag['content'] = '';
83835        }
83836
83837        if (is_scalar($tag['content']) || is_null($tag['content'])) {
83838            if ($this->options['encoding'] == 'UTF-8' &&
83839                  version_compare(phpversion(), '5.0.0', 'lt')
83840            ) {
83841                $tag['content'] = utf8_encode($tag['content']);
83842            }
83843
83844            if ($replaceEntities === true) {
83845                $replaceEntities = XML_UTIL_ENTITIES_XML;
83846            }
83847
83848            $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak']);
83849        } elseif (is_array($tag['content'])) {
83850            $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
83851        } elseif (is_object($tag['content'])) {
83852            $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
83853        } elseif (is_resource($tag['content'])) {
83854            settype($tag['content'], 'string');
83855            $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
83856        }
83857        return  $tag;
83858    }
83859}<?php
83860/**
83861 * package.xml parsing class, package.xml version 1.0
83862 *
83863 * PHP versions 4 and 5
83864 *
83865 * @category   pear
83866 * @package    PEAR
83867 * @author     Greg Beaver <cellog@php.net>
83868 * @copyright  1997-2009 The Authors
83869 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
83870 * @version    CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $
83871 * @link       http://pear.php.net/package/PEAR
83872 * @since      File available since Release 1.4.0a1
83873 */
83874/**
83875 * package.xml abstraction class
83876 */
83877require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
83878/**
83879 * Parser for package.xml version 1.0
83880 * @category   pear
83881 * @package    PEAR
83882 * @author     Greg Beaver <cellog@php.net>
83883 * @copyright  1997-2009 The Authors
83884 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
83885 * @version    Release: 1.10.0beta1
83886 * @link       http://pear.php.net/package/PEAR
83887 * @since      Class available since Release 1.4.0a1
83888 */
83889class PEAR_PackageFile_Parser_v1
83890{
83891    var $_registry;
83892    var $_config;
83893    var $_logger;
83894    /**
83895     * BC hack to allow PEAR_Common::infoFromString() to sort of
83896     * work with the version 2.0 format - there's no filelist though
83897     * @param PEAR_PackageFile_v2
83898     */
83899    function fromV2($packagefile)
83900    {
83901        $info = $packagefile->getArray(true);
83902        $ret = new PEAR_PackageFile_v1;
83903        $ret->fromArray($info['old']);
83904    }
83905
83906    function setConfig(&$c)
83907    {
83908        $this->_config = &$c;
83909        $this->_registry = &$c->getRegistry();
83910    }
83911
83912    function setLogger(&$l)
83913    {
83914        $this->_logger = &$l;
83915    }
83916
83917    /**
83918     * @param string contents of package.xml file, version 1.0
83919     * @return bool success of parsing
83920     */
83921    function &parse($data, $file, $archive = false)
83922    {
83923        if (!extension_loaded('xml')) {
83924            return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension');
83925        }
83926        $xp = xml_parser_create();
83927        if (!$xp) {
83928            $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml');
83929            return $a;
83930        }
83931        xml_set_object($xp, $this);
83932        xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0');
83933        xml_set_character_data_handler($xp, '_pkginfo_cdata_1_0');
83934        xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
83935
83936        $this->element_stack = array();
83937        $this->_packageInfo = array('provides' => array());
83938        $this->current_element = false;
83939        unset($this->dir_install);
83940        $this->_packageInfo['filelist'] = array();
83941        $this->filelist =& $this->_packageInfo['filelist'];
83942        $this->dir_names = array();
83943        $this->in_changelog = false;
83944        $this->d_i = 0;
83945        $this->cdata = '';
83946        $this->_isValid = true;
83947
83948        if (!xml_parse($xp, $data, 1)) {
83949            $code = xml_get_error_code($xp);
83950            $line = xml_get_current_line_number($xp);
83951            xml_parser_free($xp);
83952            $a = &PEAR::raiseError(sprintf("XML error: %s at line %d",
83953                           $str = xml_error_string($code), $line), 2);
83954            return $a;
83955        }
83956
83957        xml_parser_free($xp);
83958
83959        $pf = new PEAR_PackageFile_v1;
83960        $pf->setConfig($this->_config);
83961        if (isset($this->_logger)) {
83962            $pf->setLogger($this->_logger);
83963        }
83964        $pf->setPackagefile($file, $archive);
83965        $pf->fromArray($this->_packageInfo);
83966        return $pf;
83967    }
83968    // {{{ _unIndent()
83969
83970    /**
83971     * Unindent given string
83972     *
83973     * @param string $str The string that has to be unindented.
83974     * @return string
83975     * @access private
83976     */
83977    function _unIndent($str)
83978    {
83979        // remove leading newlines
83980        $str = preg_replace('/^[\r\n]+/', '', $str);
83981        // find whitespace at the beginning of the first line
83982        $indent_len = strspn($str, " \t");
83983        $indent = substr($str, 0, $indent_len);
83984        $data = '';
83985        // remove the same amount of whitespace from following lines
83986        foreach (explode("\n", $str) as $line) {
83987            if (substr($line, 0, $indent_len) == $indent) {
83988                $data .= substr($line, $indent_len) . "\n";
83989            } elseif (trim(substr($line, 0, $indent_len))) {
83990                $data .= ltrim($line);
83991            }
83992        }
83993        return $data;
83994    }
83995
83996    // Support for package DTD v1.0:
83997    // {{{ _element_start_1_0()
83998
83999    /**
84000     * XML parser callback for ending elements.  Used for version 1.0
84001     * packages.
84002     *
84003     * @param resource  $xp    XML parser resource
84004     * @param string    $name  name of ending element
84005     *
84006     * @return void
84007     *
84008     * @access private
84009     */
84010    function _element_start_1_0($xp, $name, $attribs)
84011    {
84012        array_push($this->element_stack, $name);
84013        $this->current_element = $name;
84014        $spos = sizeof($this->element_stack) - 2;
84015        $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
84016        $this->current_attributes = $attribs;
84017        $this->cdata = '';
84018        switch ($name) {
84019            case 'dir':
84020                if ($this->in_changelog) {
84021                    break;
84022                }
84023                if (array_key_exists('name', $attribs) && $attribs['name'] != '/') {
84024                    $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
84025                        $attribs['name']);
84026                    if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) {
84027                        $attribs['name'] = substr($attribs['name'], 0,
84028                            strlen($attribs['name']) - 1);
84029                    }
84030                    if (strpos($attribs['name'], '/') === 0) {
84031                        $attribs['name'] = substr($attribs['name'], 1);
84032                    }
84033                    $this->dir_names[] = $attribs['name'];
84034                }
84035                if (isset($attribs['baseinstalldir'])) {
84036                    $this->dir_install = $attribs['baseinstalldir'];
84037                }
84038                if (isset($attribs['role'])) {
84039                    $this->dir_role = $attribs['role'];
84040                }
84041                break;
84042            case 'file':
84043                if ($this->in_changelog) {
84044                    break;
84045                }
84046                if (isset($attribs['name'])) {
84047                    $path = '';
84048                    if (count($this->dir_names)) {
84049                        foreach ($this->dir_names as $dir) {
84050                            $path .= $dir . '/';
84051                        }
84052                    }
84053                    $path .= preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
84054                        $attribs['name']);
84055                    unset($attribs['name']);
84056                    $this->current_path = $path;
84057                    $this->filelist[$path] = $attribs;
84058                    // Set the baseinstalldir only if the file don't have this attrib
84059                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
84060                        isset($this->dir_install))
84061                    {
84062                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
84063                    }
84064                    // Set the Role
84065                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
84066                        $this->filelist[$path]['role'] = $this->dir_role;
84067                    }
84068                }
84069                break;
84070            case 'replace':
84071                if (!$this->in_changelog) {
84072                    $this->filelist[$this->current_path]['replacements'][] = $attribs;
84073                }
84074                break;
84075            case 'maintainers':
84076                $this->_packageInfo['maintainers'] = array();
84077                $this->m_i = 0; // maintainers array index
84078                break;
84079            case 'maintainer':
84080                // compatibility check
84081                if (!isset($this->_packageInfo['maintainers'])) {
84082                    $this->_packageInfo['maintainers'] = array();
84083                    $this->m_i = 0;
84084                }
84085                $this->_packageInfo['maintainers'][$this->m_i] = array();
84086                $this->current_maintainer =& $this->_packageInfo['maintainers'][$this->m_i];
84087                break;
84088            case 'changelog':
84089                $this->_packageInfo['changelog'] = array();
84090                $this->c_i = 0; // changelog array index
84091                $this->in_changelog = true;
84092                break;
84093            case 'release':
84094                if ($this->in_changelog) {
84095                    $this->_packageInfo['changelog'][$this->c_i] = array();
84096                    $this->current_release = &$this->_packageInfo['changelog'][$this->c_i];
84097                } else {
84098                    $this->current_release = &$this->_packageInfo;
84099                }
84100                break;
84101            case 'deps':
84102                if (!$this->in_changelog) {
84103                    $this->_packageInfo['release_deps'] = array();
84104                }
84105                break;
84106            case 'dep':
84107                // dependencies array index
84108                if (!$this->in_changelog) {
84109                    $this->d_i++;
84110                    isset($attribs['type']) ? ($attribs['type'] = strtolower($attribs['type'])) : false;
84111                    $this->_packageInfo['release_deps'][$this->d_i] = $attribs;
84112                }
84113                break;
84114            case 'configureoptions':
84115                if (!$this->in_changelog) {
84116                    $this->_packageInfo['configure_options'] = array();
84117                }
84118                break;
84119            case 'configureoption':
84120                if (!$this->in_changelog) {
84121                    $this->_packageInfo['configure_options'][] = $attribs;
84122                }
84123                break;
84124            case 'provides':
84125                if (empty($attribs['type']) || empty($attribs['name'])) {
84126                    break;
84127                }
84128                $attribs['explicit'] = true;
84129                $this->_packageInfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
84130                break;
84131            case 'package' :
84132                if (isset($attribs['version'])) {
84133                    $this->_packageInfo['xsdversion'] = trim($attribs['version']);
84134                } else {
84135                    $this->_packageInfo['xsdversion'] = '1.0';
84136                }
84137                if (isset($attribs['packagerversion'])) {
84138                    $this->_packageInfo['packagerversion'] = $attribs['packagerversion'];
84139                }
84140                break;
84141        }
84142    }
84143
84144    // }}}
84145    // {{{ _element_end_1_0()
84146
84147    /**
84148     * XML parser callback for ending elements.  Used for version 1.0
84149     * packages.
84150     *
84151     * @param resource  $xp    XML parser resource
84152     * @param string    $name  name of ending element
84153     *
84154     * @return void
84155     *
84156     * @access private
84157     */
84158    function _element_end_1_0($xp, $name)
84159    {
84160        $data = trim($this->cdata);
84161        switch ($name) {
84162            case 'name':
84163                switch ($this->prev_element) {
84164                    case 'package':
84165                        $this->_packageInfo['package'] = $data;
84166                        break;
84167                    case 'maintainer':
84168                        $this->current_maintainer['name'] = $data;
84169                        break;
84170                }
84171                break;
84172            case 'extends' :
84173                $this->_packageInfo['extends'] = $data;
84174                break;
84175            case 'summary':
84176                $this->_packageInfo['summary'] = $data;
84177                break;
84178            case 'description':
84179                $data = $this->_unIndent($this->cdata);
84180                $this->_packageInfo['description'] = $data;
84181                break;
84182            case 'user':
84183                $this->current_maintainer['handle'] = $data;
84184                break;
84185            case 'email':
84186                $this->current_maintainer['email'] = $data;
84187                break;
84188            case 'role':
84189                $this->current_maintainer['role'] = $data;
84190                break;
84191            case 'version':
84192                if ($this->in_changelog) {
84193                    $this->current_release['version'] = $data;
84194                } else {
84195                    $this->_packageInfo['version'] = $data;
84196                }
84197                break;
84198            case 'date':
84199                if ($this->in_changelog) {
84200                    $this->current_release['release_date'] = $data;
84201                } else {
84202                    $this->_packageInfo['release_date'] = $data;
84203                }
84204                break;
84205            case 'notes':
84206                // try to "de-indent" release notes in case someone
84207                // has been over-indenting their xml ;-)
84208                // Trim only on the right side
84209                $data = rtrim($this->_unIndent($this->cdata));
84210                if ($this->in_changelog) {
84211                    $this->current_release['release_notes'] = $data;
84212                } else {
84213                    $this->_packageInfo['release_notes'] = $data;
84214                }
84215                break;
84216            case 'warnings':
84217                if ($this->in_changelog) {
84218                    $this->current_release['release_warnings'] = $data;
84219                } else {
84220                    $this->_packageInfo['release_warnings'] = $data;
84221                }
84222                break;
84223            case 'state':
84224                if ($this->in_changelog) {
84225                    $this->current_release['release_state'] = $data;
84226                } else {
84227                    $this->_packageInfo['release_state'] = $data;
84228                }
84229                break;
84230            case 'license':
84231                if ($this->in_changelog) {
84232                    $this->current_release['release_license'] = $data;
84233                } else {
84234                    $this->_packageInfo['release_license'] = $data;
84235                }
84236                break;
84237            case 'dep':
84238                if ($data && !$this->in_changelog) {
84239                    $this->_packageInfo['release_deps'][$this->d_i]['name'] = $data;
84240                }
84241                break;
84242            case 'dir':
84243                if ($this->in_changelog) {
84244                    break;
84245                }
84246                array_pop($this->dir_names);
84247                break;
84248            case 'file':
84249                if ($this->in_changelog) {
84250                    break;
84251                }
84252                if ($data) {
84253                    $path = '';
84254                    if (count($this->dir_names)) {
84255                        foreach ($this->dir_names as $dir) {
84256                            $path .= $dir . '/';
84257                        }
84258                    }
84259                    $path .= $data;
84260                    $this->filelist[$path] = $this->current_attributes;
84261                    // Set the baseinstalldir only if the file don't have this attrib
84262                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
84263                        isset($this->dir_install))
84264                    {
84265                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
84266                    }
84267                    // Set the Role
84268                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
84269                        $this->filelist[$path]['role'] = $this->dir_role;
84270                    }
84271                }
84272                break;
84273            case 'maintainer':
84274                if (empty($this->_packageInfo['maintainers'][$this->m_i]['role'])) {
84275                    $this->_packageInfo['maintainers'][$this->m_i]['role'] = 'lead';
84276                }
84277                $this->m_i++;
84278                break;
84279            case 'release':
84280                if ($this->in_changelog) {
84281                    $this->c_i++;
84282                }
84283                break;
84284            case 'changelog':
84285                $this->in_changelog = false;
84286                break;
84287        }
84288        array_pop($this->element_stack);
84289        $spos = sizeof($this->element_stack) - 1;
84290        $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
84291        $this->cdata = '';
84292    }
84293
84294    // }}}
84295    // {{{ _pkginfo_cdata_1_0()
84296
84297    /**
84298     * XML parser callback for character data.  Used for version 1.0
84299     * packages.
84300     *
84301     * @param resource  $xp    XML parser resource
84302     * @param string    $name  character data
84303     *
84304     * @return void
84305     *
84306     * @access private
84307     */
84308    function _pkginfo_cdata_1_0($xp, $data)
84309    {
84310        if (isset($this->cdata)) {
84311            $this->cdata .= $data;
84312        }
84313    }
84314
84315    // }}}
84316}
84317?><?php
84318/**
84319 * package.xml parsing class, package.xml version 2.0
84320 *
84321 * PHP versions 4 and 5
84322 *
84323 * @category   pear
84324 * @package    PEAR
84325 * @author     Greg Beaver <cellog@php.net>
84326 * @copyright  1997-2009 The Authors
84327 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
84328 * @version    CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
84329 * @link       http://pear.php.net/package/PEAR
84330 * @since      File available since Release 1.4.0a1
84331 */
84332/**
84333 * base xml parser class
84334 */
84335require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';
84336require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
84337/**
84338 * Parser for package.xml version 2.0
84339 * @category   pear
84340 * @package    PEAR
84341 * @author     Greg Beaver <cellog@php.net>
84342 * @copyright  1997-2009 The Authors
84343 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
84344 * @version    Release: 1.10.0beta1
84345 * @link       http://pear.php.net/package/PEAR
84346 * @since      Class available since Release 1.4.0a1
84347 */
84348class PEAR_PackageFile_Parser_v2 extends PEAR_XMLParser
84349{
84350    var $_config;
84351    var $_logger;
84352    var $_registry;
84353
84354    function setConfig(&$c)
84355    {
84356        $this->_config = &$c;
84357        $this->_registry = &$c->getRegistry();
84358    }
84359
84360    function setLogger(&$l)
84361    {
84362        $this->_logger = &$l;
84363    }
84364    /**
84365     * Unindent given string
84366     *
84367     * @param string $str The string that has to be unindented.
84368     * @return string
84369     * @access private
84370     */
84371    function _unIndent($str)
84372    {
84373        // remove leading newlines
84374        $str = preg_replace('/^[\r\n]+/', '', $str);
84375        // find whitespace at the beginning of the first line
84376        $indent_len = strspn($str, " \t");
84377        $indent = substr($str, 0, $indent_len);
84378        $data = '';
84379        // remove the same amount of whitespace from following lines
84380        foreach (explode("\n", $str) as $line) {
84381            if (substr($line, 0, $indent_len) == $indent) {
84382                $data .= substr($line, $indent_len) . "\n";
84383            } else {
84384                $data .= $line . "\n";
84385            }
84386        }
84387        return $data;
84388    }
84389
84390    /**
84391     * post-process data
84392     *
84393     * @param string $data
84394     * @param string $element element name
84395     */
84396    function postProcess($data, $element)
84397    {
84398        if ($element == 'notes') {
84399            return trim($this->_unIndent($data));
84400        }
84401        return trim($data);
84402    }
84403
84404    /**
84405     * @param string
84406     * @param string file name of the package.xml
84407     * @param string|false name of the archive this package.xml came from, if any
84408     * @param string class name to instantiate and return.  This must be PEAR_PackageFile_v2 or
84409     *               a subclass
84410     * @return PEAR_PackageFile_v2
84411     */
84412    function &parse($data, $file, $archive = false, $class = 'PEAR_PackageFile_v2')
84413    {
84414        if (PEAR::isError($err = parent::parse($data, $file))) {
84415            return $err;
84416        }
84417
84418        $ret = new $class;
84419        $ret->encoding = $this->encoding;
84420        $ret->setConfig($this->_config);
84421        if (isset($this->_logger)) {
84422            $ret->setLogger($this->_logger);
84423        }
84424
84425        $ret->fromArray($this->_unserializedData);
84426        $ret->setPackagefile($file, $archive);
84427        return $ret;
84428    }
84429}<?php
84430/**
84431 * PEAR_PackageFile_v1, package.xml version 1.0
84432 *
84433 * PHP versions 4 and 5
84434 *
84435 * @category   pear
84436 * @package    PEAR
84437 * @author     Greg Beaver <cellog@php.net>
84438 * @copyright  1997-2009 The Authors
84439 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
84440 * @version    CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $
84441 * @link       http://pear.php.net/package/PEAR
84442 * @since      File available since Release 1.4.0a1
84443 */
84444/**
84445 * For error handling
84446 */
84447require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ErrorStack.php';
84448
84449/**
84450 * Error code if parsing is attempted with no xml extension
84451 */
84452define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3);
84453
84454/**
84455 * Error code if creating the xml parser resource fails
84456 */
84457define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4);
84458
84459/**
84460 * Error code used for all sax xml parsing errors
84461 */
84462define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5);
84463
84464/**
84465 * Error code used when there is no name
84466 */
84467define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6);
84468
84469/**
84470 * Error code when a package name is not valid
84471 */
84472define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7);
84473
84474/**
84475 * Error code used when no summary is parsed
84476 */
84477define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8);
84478
84479/**
84480 * Error code for summaries that are more than 1 line
84481 */
84482define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9);
84483
84484/**
84485 * Error code used when no description is present
84486 */
84487define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10);
84488
84489/**
84490 * Error code used when no license is present
84491 */
84492define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11);
84493
84494/**
84495 * Error code used when a <version> version number is not present
84496 */
84497define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12);
84498
84499/**
84500 * Error code used when a <version> version number is invalid
84501 */
84502define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13);
84503
84504/**
84505 * Error code when release state is missing
84506 */
84507define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14);
84508
84509/**
84510 * Error code when release state is invalid
84511 */
84512define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15);
84513
84514/**
84515 * Error code when release state is missing
84516 */
84517define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16);
84518
84519/**
84520 * Error code when release state is invalid
84521 */
84522define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17);
84523
84524/**
84525 * Error code when no release notes are found
84526 */
84527define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18);
84528
84529/**
84530 * Error code when no maintainers are found
84531 */
84532define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19);
84533
84534/**
84535 * Error code when a maintainer has no handle
84536 */
84537define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20);
84538
84539/**
84540 * Error code when a maintainer has no handle
84541 */
84542define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21);
84543
84544/**
84545 * Error code when a maintainer has no name
84546 */
84547define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22);
84548
84549/**
84550 * Error code when a maintainer has no email
84551 */
84552define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23);
84553
84554/**
84555 * Error code when a maintainer has no handle
84556 */
84557define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24);
84558
84559/**
84560 * Error code when a dependency is not a PHP dependency, but has no name
84561 */
84562define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25);
84563
84564/**
84565 * Error code when a dependency has no type (pkg, php, etc.)
84566 */
84567define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26);
84568
84569/**
84570 * Error code when a dependency has no relation (lt, ge, has, etc.)
84571 */
84572define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27);
84573
84574/**
84575 * Error code when a dependency is not a 'has' relation, but has no version
84576 */
84577define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28);
84578
84579/**
84580 * Error code when a dependency has an invalid relation
84581 */
84582define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29);
84583
84584/**
84585 * Error code when a dependency has an invalid type
84586 */
84587define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30);
84588
84589/**
84590 * Error code when a dependency has an invalid optional option
84591 */
84592define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31);
84593
84594/**
84595 * Error code when a dependency is a pkg dependency, and has an invalid package name
84596 */
84597define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32);
84598
84599/**
84600 * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel
84601 */
84602define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33);
84603
84604/**
84605 * Error code when rel="has" and version attribute is present.
84606 */
84607define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34);
84608
84609/**
84610 * Error code when type="php" and dependency name is present
84611 */
84612define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35);
84613
84614/**
84615 * Error code when a configure option has no name
84616 */
84617define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36);
84618
84619/**
84620 * Error code when a configure option has no name
84621 */
84622define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37);
84623
84624/**
84625 * Error code when a file in the filelist has an invalid role
84626 */
84627define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38);
84628
84629/**
84630 * Error code when a file in the filelist has no role
84631 */
84632define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39);
84633
84634/**
84635 * Error code when analyzing a php source file that has parse errors
84636 */
84637define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40);
84638
84639/**
84640 * Error code when analyzing a php source file reveals a source element
84641 * without a package name prefix
84642 */
84643define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41);
84644
84645/**
84646 * Error code when an unknown channel is specified
84647 */
84648define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42);
84649
84650/**
84651 * Error code when no files are found in the filelist
84652 */
84653define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43);
84654
84655/**
84656 * Error code when a file is not valid php according to _analyzeSourceCode()
84657 */
84658define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44);
84659
84660/**
84661 * Error code when the channel validator returns an error or warning
84662 */
84663define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45);
84664
84665/**
84666 * Error code when a php5 package is packaged in php4 (analysis doesn't work)
84667 */
84668define('PEAR_PACKAGEFILE_ERROR_PHP5', 46);
84669
84670/**
84671 * Error code when a file is listed in package.xml but does not exist
84672 */
84673define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47);
84674
84675/**
84676 * Error code when a <dep type="php" rel="not"... is encountered (use rel="ne")
84677 */
84678define('PEAR_PACKAGEFILE_PHP_NO_NOT', 48);
84679
84680/**
84681 * Error code when a package.xml contains non-ISO-8859-1 characters
84682 */
84683define('PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS', 49);
84684
84685/**
84686 * Error code when a dependency is not a 'has' relation, but has no version
84687 */
84688define('PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION', 50);
84689
84690/**
84691 * Error code when a package has no lead developer
84692 */
84693define('PEAR_PACKAGEFILE_ERROR_NO_LEAD', 51);
84694
84695/**
84696 * Error code when a filename begins with "."
84697 */
84698define('PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME', 52);
84699/**
84700 * package.xml encapsulator
84701 * @category   pear
84702 * @package    PEAR
84703 * @author     Greg Beaver <cellog@php.net>
84704 * @copyright  1997-2009 The Authors
84705 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
84706 * @version    Release: 1.9.4
84707 * @link       http://pear.php.net/package/PEAR
84708 * @since      Class available since Release 1.4.0a1
84709 */
84710class PEAR_PackageFile_v1
84711{
84712    /**
84713     * @access private
84714     * @var PEAR_ErrorStack
84715     * @access private
84716     */
84717    var $_stack;
84718
84719    /**
84720     * A registry object, used to access the package name validation regex for non-standard channels
84721     * @var PEAR_Registry
84722     * @access private
84723     */
84724    var $_registry;
84725
84726    /**
84727     * An object that contains a log method that matches PEAR_Common::log's signature
84728     * @var object
84729     * @access private
84730     */
84731    var $_logger;
84732
84733    /**
84734     * Parsed package information
84735     * @var array
84736     * @access private
84737     */
84738    var $_packageInfo;
84739
84740    /**
84741     * path to package.xml
84742     * @var string
84743     * @access private
84744     */
84745    var $_packageFile;
84746
84747    /**
84748     * path to package .tgz or false if this is a local/extracted package.xml
84749     * @var string
84750     * @access private
84751     */
84752    var $_archiveFile;
84753
84754    /**
84755     * @var int
84756     * @access private
84757     */
84758    var $_isValid = 0;
84759
84760    /**
84761     * Determines whether this packagefile was initialized only with partial package info
84762     *
84763     * If this package file was constructed via parsing REST, it will only contain
84764     *
84765     * - package name
84766     * - channel name
84767     * - dependencies 
84768     * @var boolean
84769     * @access private
84770     */
84771    var $_incomplete = true;
84772
84773    /**
84774     * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack
84775     * @param string Name of Error Stack class to use.
84776     */
84777    function PEAR_PackageFile_v1()
84778    {
84779        $this->_stack = &new PEAR_ErrorStack('PEAR_PackageFile_v1');
84780        $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
84781        $this->_isValid = 0;
84782    }
84783
84784    function installBinary($installer)
84785    {
84786        return false;
84787    }
84788
84789    function isExtension($name)
84790    {
84791        return false;
84792    }
84793
84794    function setConfig(&$config)
84795    {
84796        $this->_config = &$config;
84797        $this->_registry = &$config->getRegistry();
84798    }
84799
84800    function setRequestedGroup()
84801    {
84802        // placeholder
84803    }
84804
84805    /**
84806     * For saving in the registry.
84807     *
84808     * Set the last version that was installed
84809     * @param string
84810     */
84811    function setLastInstalledVersion($version)
84812    {
84813        $this->_packageInfo['_lastversion'] = $version;
84814    }
84815
84816    /**
84817     * @return string|false
84818     */
84819    function getLastInstalledVersion()
84820    {
84821        if (isset($this->_packageInfo['_lastversion'])) {
84822            return $this->_packageInfo['_lastversion'];
84823        }
84824        return false;
84825    }
84826
84827    function getInstalledBinary()
84828    {
84829        return false;
84830    }
84831
84832    function listPostinstallScripts()
84833    {
84834        return false;
84835    }
84836
84837    function initPostinstallScripts()
84838    {
84839        return false;
84840    }
84841
84842    function setLogger(&$logger)
84843    {
84844        if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) {
84845            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
84846        }
84847        $this->_logger = &$logger;
84848    }
84849
84850    function setPackagefile($file, $archive = false)
84851    {
84852        $this->_packageFile = $file;
84853        $this->_archiveFile = $archive ? $archive : $file;
84854    }
84855
84856    function getPackageFile()
84857    {
84858        return isset($this->_packageFile) ? $this->_packageFile : false;
84859    }
84860
84861    function getPackageType()
84862    {
84863        return 'php';
84864    }
84865
84866    function getArchiveFile()
84867    {
84868        return $this->_archiveFile;
84869    }
84870
84871    function packageInfo($field)
84872    {
84873        if (!is_string($field) || empty($field) ||
84874            !isset($this->_packageInfo[$field])) {
84875            return false;
84876        }
84877        return $this->_packageInfo[$field];
84878    }
84879
84880    function setDirtree($path)
84881    {
84882        if (!isset($this->_packageInfo['dirtree'])) {
84883            $this->_packageInfo['dirtree'] = array();
84884        }
84885        $this->_packageInfo['dirtree'][$path] = true;
84886    }
84887
84888    function getDirtree()
84889    {
84890        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
84891            return $this->_packageInfo['dirtree'];
84892        }
84893        return false;
84894    }
84895
84896    function resetDirtree()
84897    {
84898        unset($this->_packageInfo['dirtree']);
84899    }
84900
84901    function fromArray($pinfo)
84902    {
84903        $this->_incomplete = false;
84904        $this->_packageInfo = $pinfo;
84905    }
84906
84907    function isIncomplete()
84908    {
84909        return $this->_incomplete;
84910    }
84911
84912    function getChannel()
84913    {
84914        return 'pear.php.net';
84915    }
84916
84917    function getUri()
84918    {
84919        return false;
84920    }
84921
84922    function getTime()
84923    {
84924        return false;
84925    }
84926
84927    function getExtends()
84928    {
84929        if (isset($this->_packageInfo['extends'])) {
84930            return $this->_packageInfo['extends'];
84931        }
84932        return false;
84933    }
84934
84935    /**
84936     * @return array
84937     */
84938    function toArray()
84939    {
84940        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
84941            return false;
84942        }
84943        return $this->getArray();
84944    }
84945
84946    function getArray()
84947    {
84948        return $this->_packageInfo;
84949    }
84950
84951    function getName()
84952    {
84953        return $this->getPackage();
84954    }
84955
84956    function getPackage()
84957    {
84958        if (isset($this->_packageInfo['package'])) {
84959            return $this->_packageInfo['package'];
84960        }
84961        return false;
84962    }
84963
84964    /**
84965     * WARNING - don't use this unless you know what you are doing
84966     */
84967    function setRawPackage($package)
84968    {
84969        $this->_packageInfo['package'] = $package;
84970    }
84971
84972    function setPackage($package)
84973    {
84974        $this->_packageInfo['package'] = $package;
84975        $this->_isValid = false;
84976    }
84977
84978    function getVersion()
84979    {
84980        if (isset($this->_packageInfo['version'])) {
84981            return $this->_packageInfo['version'];
84982        }
84983        return false;
84984    }
84985
84986    function setVersion($version)
84987    {
84988        $this->_packageInfo['version'] = $version;
84989        $this->_isValid = false;
84990    }
84991
84992    function clearMaintainers()
84993    {
84994        unset($this->_packageInfo['maintainers']);
84995    }
84996
84997    function getMaintainers()
84998    {
84999        if (isset($this->_packageInfo['maintainers'])) {
85000            return $this->_packageInfo['maintainers'];
85001        }
85002        return false;
85003    }
85004
85005    /**
85006     * Adds a new maintainer - no checking of duplicates is performed, use
85007     * updatemaintainer for that purpose.
85008     */
85009    function addMaintainer($role, $handle, $name, $email)
85010    {
85011        $this->_packageInfo['maintainers'][] =
85012            array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name);
85013        $this->_isValid = false;
85014    }
85015
85016    function updateMaintainer($role, $handle, $name, $email)
85017    {
85018        $found = false;
85019        if (!isset($this->_packageInfo['maintainers']) ||
85020              !is_array($this->_packageInfo['maintainers'])) {
85021            return $this->addMaintainer($role, $handle, $name, $email);
85022        }
85023        foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
85024            if ($maintainer['handle'] == $handle) {
85025                $found = $i;
85026                break;
85027            }
85028        }
85029        if ($found !== false) {
85030            unset($this->_packageInfo['maintainers'][$found]);
85031            $this->_packageInfo['maintainers'] =
85032                array_values($this->_packageInfo['maintainers']);
85033        }
85034        $this->addMaintainer($role, $handle, $name, $email);
85035    }
85036
85037    function deleteMaintainer($handle)
85038    {
85039        $found = false;
85040        foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
85041            if ($maintainer['handle'] == $handle) {
85042                $found = $i;
85043                break;
85044            }
85045        }
85046        if ($found !== false) {
85047            unset($this->_packageInfo['maintainers'][$found]);
85048            $this->_packageInfo['maintainers'] =
85049                array_values($this->_packageInfo['maintainers']);
85050            return true;
85051        }
85052        return false;
85053    }
85054
85055    function getState()
85056    {
85057        if (isset($this->_packageInfo['release_state'])) {
85058            return $this->_packageInfo['release_state'];
85059        }
85060        return false;
85061    }
85062
85063    function setRawState($state)
85064    {
85065        $this->_packageInfo['release_state'] = $state;
85066    }
85067
85068    function setState($state)
85069    {
85070        $this->_packageInfo['release_state'] = $state;
85071        $this->_isValid = false;
85072    }
85073
85074    function getDate()
85075    {
85076        if (isset($this->_packageInfo['release_date'])) {
85077            return $this->_packageInfo['release_date'];
85078        }
85079        return false;
85080    }
85081
85082    function setDate($date)
85083    {
85084        $this->_packageInfo['release_date'] = $date;
85085        $this->_isValid = false;
85086    }
85087
85088    function getLicense()
85089    {
85090        if (isset($this->_packageInfo['release_license'])) {
85091            return $this->_packageInfo['release_license'];
85092        }
85093        return false;
85094    }
85095
85096    function setLicense($date)
85097    {
85098        $this->_packageInfo['release_license'] = $date;
85099        $this->_isValid = false;
85100    }
85101
85102    function getSummary()
85103    {
85104        if (isset($this->_packageInfo['summary'])) {
85105            return $this->_packageInfo['summary'];
85106        }
85107        return false;
85108    }
85109
85110    function setSummary($summary)
85111    {
85112        $this->_packageInfo['summary'] = $summary;
85113        $this->_isValid = false;
85114    }
85115
85116    function getDescription()
85117    {
85118        if (isset($this->_packageInfo['description'])) {
85119            return $this->_packageInfo['description'];
85120        }
85121        return false;
85122    }
85123
85124    function setDescription($desc)
85125    {
85126        $this->_packageInfo['description'] = $desc;
85127        $this->_isValid = false;
85128    }
85129
85130    function getNotes()
85131    {
85132        if (isset($this->_packageInfo['release_notes'])) {
85133            return $this->_packageInfo['release_notes'];
85134        }
85135        return false;
85136    }
85137
85138    function setNotes($notes)
85139    {
85140        $this->_packageInfo['release_notes'] = $notes;
85141        $this->_isValid = false;
85142    }
85143
85144    function getDeps()
85145    {
85146        if (isset($this->_packageInfo['release_deps'])) {
85147            return $this->_packageInfo['release_deps'];
85148        }
85149        return false;
85150    }
85151
85152    /**
85153     * Reset dependencies prior to adding new ones
85154     */
85155    function clearDeps()
85156    {
85157        unset($this->_packageInfo['release_deps']);
85158    }
85159
85160    function addPhpDep($version, $rel)
85161    {
85162        $this->_isValid = false;
85163        $this->_packageInfo['release_deps'][] =
85164            array('type' => 'php',
85165                  'rel' => $rel,
85166                  'version' => $version);
85167    }
85168
85169    function addPackageDep($name, $version, $rel, $optional = 'no')
85170    {
85171        $this->_isValid = false;
85172        $dep =
85173            array('type' => 'pkg',
85174                  'name' => $name,
85175                  'rel' => $rel,
85176                  'optional' => $optional);
85177        if ($rel != 'has' && $rel != 'not') {
85178            $dep['version'] = $version;
85179        }
85180        $this->_packageInfo['release_deps'][] = $dep;
85181    }
85182
85183    function addExtensionDep($name, $version, $rel, $optional = 'no')
85184    {
85185        $this->_isValid = false;
85186        $this->_packageInfo['release_deps'][] =
85187            array('type' => 'ext',
85188                  'name' => $name,
85189                  'rel' => $rel,
85190                  'version' => $version,
85191                  'optional' => $optional);
85192    }
85193
85194    /**
85195     * WARNING - do not use this function directly unless you know what you're doing
85196     */
85197    function setDeps($deps)
85198    {
85199        $this->_packageInfo['release_deps'] = $deps;
85200    }
85201
85202    function hasDeps()
85203    {
85204        return isset($this->_packageInfo['release_deps']) &&
85205            count($this->_packageInfo['release_deps']);
85206    }
85207
85208    function getDependencyGroup($group)
85209    {
85210        return false;
85211    }
85212
85213    function isCompatible($pf)
85214    {
85215        return false;
85216    }
85217
85218    function isSubpackageOf($p)
85219    {
85220        return $p->isSubpackage($this);
85221    }
85222
85223    function isSubpackage($p)
85224    {
85225        return false;
85226    }
85227
85228    function dependsOn($package, $channel)
85229    {
85230        if (strtolower($channel) != 'pear.php.net') {
85231            return false;
85232        }
85233        if (!($deps = $this->getDeps())) {
85234            return false;
85235        }
85236        foreach ($deps as $dep) {
85237            if ($dep['type'] != 'pkg') {
85238                continue;
85239            }
85240            if (strtolower($dep['name']) == strtolower($package)) {
85241                return true;
85242            }
85243        }
85244        return false;
85245    }
85246
85247    function getConfigureOptions()
85248    {
85249        if (isset($this->_packageInfo['configure_options'])) {
85250            return $this->_packageInfo['configure_options'];
85251        }
85252        return false;
85253    }
85254
85255    function hasConfigureOptions()
85256    {
85257        return isset($this->_packageInfo['configure_options']) &&
85258            count($this->_packageInfo['configure_options']);
85259    }
85260
85261    function addConfigureOption($name, $prompt, $default = false)
85262    {
85263        $o = array('name' => $name, 'prompt' => $prompt);
85264        if ($default !== false) {
85265            $o['default'] = $default;
85266        }
85267        if (!isset($this->_packageInfo['configure_options'])) {
85268            $this->_packageInfo['configure_options'] = array();
85269        }
85270        $this->_packageInfo['configure_options'][] = $o;
85271    }
85272
85273    function clearConfigureOptions()
85274    {
85275        unset($this->_packageInfo['configure_options']);
85276    }
85277
85278    function getProvides()
85279    {
85280        if (isset($this->_packageInfo['provides'])) {
85281            return $this->_packageInfo['provides'];
85282        }
85283        return false;
85284    }
85285
85286    function getProvidesExtension()
85287    {
85288        return false;
85289    }
85290
85291    function addFile($dir, $file, $attrs)
85292    {
85293        $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
85294        if ($dir == '/' || $dir == '') {
85295            $dir = '';
85296        } else {
85297            $dir .= '/';
85298        }
85299        $file = $dir . $file;
85300        $file = preg_replace('![\\/]+!', '/', $file);
85301        $this->_packageInfo['filelist'][$file] = $attrs;
85302    }
85303
85304    function getInstallationFilelist()
85305    {
85306        return $this->getFilelist();
85307    }
85308
85309    function getFilelist()
85310    {
85311        if (isset($this->_packageInfo['filelist'])) {
85312            return $this->_packageInfo['filelist'];
85313        }
85314        return false;
85315    }
85316
85317    function setFileAttribute($file, $attr, $value)
85318    {
85319        $this->_packageInfo['filelist'][$file][$attr] = $value;
85320    }
85321
85322    function resetFilelist()
85323    {
85324        $this->_packageInfo['filelist'] = array();
85325    }
85326
85327    function setInstalledAs($file, $path)
85328    {
85329        if ($path) {
85330            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
85331        }
85332        unset($this->_packageInfo['filelist'][$file]['installed_as']);
85333    }
85334
85335    function installedFile($file, $atts)
85336    {
85337        if (isset($this->_packageInfo['filelist'][$file])) {
85338            $this->_packageInfo['filelist'][$file] =
85339                array_merge($this->_packageInfo['filelist'][$file], $atts);
85340        } else {
85341            $this->_packageInfo['filelist'][$file] = $atts;
85342        }
85343    }
85344
85345    function getChangelog()
85346    {
85347        if (isset($this->_packageInfo['changelog'])) {
85348            return $this->_packageInfo['changelog'];
85349        }
85350        return false;
85351    }
85352
85353    function getPackagexmlVersion()
85354    {
85355        return '1.0';
85356    }
85357
85358    /**
85359     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
85360     * @param boolean determines whether to purge the error stack after retrieving
85361     * @return array
85362     */
85363    function getValidationWarnings($purge = true)
85364    {
85365        return $this->_stack->getErrors($purge);
85366    }
85367
85368    // }}}
85369    /**
85370     * Validation error.  Also marks the object contents as invalid
85371     * @param error code
85372     * @param array error information
85373     * @access private
85374     */
85375    function _validateError($code, $params = array())
85376    {
85377        $this->_stack->push($code, 'error', $params, false, false, debug_backtrace());
85378        $this->_isValid = false;
85379    }
85380
85381    /**
85382     * Validation warning.  Does not mark the object contents invalid.
85383     * @param error code
85384     * @param array error information
85385     * @access private
85386     */
85387    function _validateWarning($code, $params = array())
85388    {
85389        $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace());
85390    }
85391
85392    /**
85393     * @param integer error code
85394     * @access protected
85395     */
85396    function _getErrorMessage()
85397    {
85398        return array(
85399                PEAR_PACKAGEFILE_ERROR_NO_NAME =>
85400                    'Missing Package Name',
85401                PEAR_PACKAGEFILE_ERROR_NO_SUMMARY =>
85402                    'No summary found',
85403                PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY =>
85404                    'Summary should be on one line',
85405                PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION =>
85406                    'Missing description',
85407                PEAR_PACKAGEFILE_ERROR_NO_LICENSE =>
85408                    'Missing license',
85409                PEAR_PACKAGEFILE_ERROR_NO_VERSION =>
85410                    'No release version found',
85411                PEAR_PACKAGEFILE_ERROR_NO_STATE =>
85412                    'No release state found',
85413                PEAR_PACKAGEFILE_ERROR_NO_DATE =>
85414                    'No release date found',
85415                PEAR_PACKAGEFILE_ERROR_NO_NOTES =>
85416                    'No release notes found',
85417                PEAR_PACKAGEFILE_ERROR_NO_LEAD =>
85418                    'Package must have at least one lead maintainer',
85419                PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS =>
85420                    'No maintainers found, at least one must be defined',
85421                PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE =>
85422                    'Maintainer %index% has no handle (user ID at channel server)',
85423                PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE =>
85424                    'Maintainer %index% has no role',
85425                PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME =>
85426                    'Maintainer %index% has no name',
85427                PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL =>
85428                    'Maintainer %index% has no email',
85429                PEAR_PACKAGEFILE_ERROR_NO_DEPNAME =>
85430                    'Dependency %index% is not a php dependency, and has no name',
85431                PEAR_PACKAGEFILE_ERROR_NO_DEPREL =>
85432                    'Dependency %index% has no relation (rel)',
85433                PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE =>
85434                    'Dependency %index% has no type',
85435                PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED =>
85436                    'PHP Dependency %index% has a name attribute of "%name%" which will be' .
85437                        ' ignored!',
85438                PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION =>
85439                    'Dependency %index% is not a rel="has" or rel="not" dependency, ' .
85440                        'and has no version',
85441                PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION =>
85442                    'Dependency %index% is a type="php" dependency, ' .
85443                        'and has no version',
85444                PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED =>
85445                    'Dependency %index% is a rel="%rel%" dependency, versioning is ignored',
85446                PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL =>
85447                    'Dependency %index% has invalid optional value "%opt%", should be yes or no',
85448                PEAR_PACKAGEFILE_PHP_NO_NOT =>
85449                    'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' .
85450                        ' to exclude specific versions',
85451                PEAR_PACKAGEFILE_ERROR_NO_CONFNAME =>
85452                    'Configure Option %index% has no name',
85453                PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT =>
85454                    'Configure Option %index% has no prompt',
85455                PEAR_PACKAGEFILE_ERROR_NO_FILES =>
85456                    'No files in <filelist> section of package.xml',
85457                PEAR_PACKAGEFILE_ERROR_NO_FILEROLE =>
85458                    'File "%file%" has no role, expecting one of "%roles%"',
85459                PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE =>
85460                    'File "%file%" has invalid role "%role%", expecting one of "%roles%"',
85461                PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME =>
85462                    'File "%file%" cannot start with ".", cannot package or install',
85463                PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE =>
85464                    'Parser error: invalid PHP found in file "%file%"',
85465                PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX =>
85466                    'in %file%: %type% "%name%" not prefixed with package name "%package%"',
85467                PEAR_PACKAGEFILE_ERROR_INVALID_FILE =>
85468                    'Parser error: invalid PHP file "%file%"',
85469                PEAR_PACKAGEFILE_ERROR_CHANNELVAL =>
85470                    'Channel validator error: field "%field%" - %reason%',
85471                PEAR_PACKAGEFILE_ERROR_PHP5 =>
85472                    'Error, PHP5 token encountered in %file%, analysis should be in PHP5',
85473                PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND =>
85474                    'File "%file%" in package.xml does not exist',
85475                PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS =>
85476                    'Package.xml contains non-ISO-8859-1 characters, and may not validate',
85477            );
85478    }
85479
85480    /**
85481     * Validate XML package definition file.
85482     *
85483     * @access public
85484     * @return boolean
85485     */
85486    function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false)
85487    {
85488        if (($this->_isValid & $state) == $state) {
85489            return true;
85490        }
85491        $this->_isValid = true;
85492        $info = $this->_packageInfo;
85493        if (empty($info['package'])) {
85494            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME);
85495            $this->_packageName = $pn = 'unknown';
85496        } else {
85497            $this->_packageName = $pn = $info['package'];
85498        }
85499
85500        if (empty($info['summary'])) {
85501            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY);
85502        } elseif (strpos(trim($info['summary']), "\n") !== false) {
85503            $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY,
85504                array('summary' => $info['summary']));
85505        }
85506        if (empty($info['description'])) {
85507            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION);
85508        }
85509        if (empty($info['release_license'])) {
85510            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE);
85511        }
85512        if (empty($info['version'])) {
85513            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION);
85514        }
85515        if (empty($info['release_state'])) {
85516            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE);
85517        }
85518        if (empty($info['release_date'])) {
85519            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE);
85520        }
85521        if (empty($info['release_notes'])) {
85522            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES);
85523        }
85524        if (empty($info['maintainers'])) {
85525            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS);
85526        } else {
85527            $haslead = false;
85528            $i = 1;
85529            foreach ($info['maintainers'] as $m) {
85530                if (empty($m['handle'])) {
85531                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE,
85532                        array('index' => $i));
85533                }
85534                if (empty($m['role'])) {
85535                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE,
85536                        array('index' => $i, 'roles' => PEAR_Common::getUserRoles()));
85537                } elseif ($m['role'] == 'lead') {
85538                    $haslead = true;
85539                }
85540                if (empty($m['name'])) {
85541                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME,
85542                        array('index' => $i));
85543                }
85544                if (empty($m['email'])) {
85545                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL,
85546                        array('index' => $i));
85547                }
85548                $i++;
85549            }
85550            if (!$haslead) {
85551                $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD);
85552            }
85553        }
85554        if (!empty($info['release_deps'])) {
85555            $i = 1;
85556            foreach ($info['release_deps'] as $d) {
85557                if (!isset($d['type']) || empty($d['type'])) {
85558                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE,
85559                        array('index' => $i, 'types' => PEAR_Common::getDependencyTypes()));
85560                    continue;
85561                }
85562                if (!isset($d['rel']) || empty($d['rel'])) {
85563                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL,
85564                        array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations()));
85565                    continue;
85566                }
85567                if (!empty($d['optional'])) {
85568                    if (!in_array($d['optional'], array('yes', 'no'))) {
85569                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL,
85570                            array('index' => $i, 'opt' => $d['optional']));
85571                    }
85572                }
85573                if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) {
85574                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION,
85575                        array('index' => $i));
85576                } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) {
85577                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED,
85578                        array('index' => $i, 'rel' => $d['rel']));
85579                }
85580                if ($d['type'] == 'php' && !empty($d['name'])) {
85581                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED,
85582                        array('index' => $i, 'name' => $d['name']));
85583                } elseif ($d['type'] != 'php' && empty($d['name'])) {
85584                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME,
85585                        array('index' => $i));
85586                }
85587                if ($d['type'] == 'php' && empty($d['version'])) {
85588                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION,
85589                        array('index' => $i));
85590                }
85591                if (($d['rel'] == 'not') && ($d['type'] == 'php')) {
85592                    $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT,
85593                        array('index' => $i));
85594                }
85595                $i++;
85596            }
85597        }
85598        if (!empty($info['configure_options'])) {
85599            $i = 1;
85600            foreach ($info['configure_options'] as $c) {
85601                if (empty($c['name'])) {
85602                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME,
85603                        array('index' => $i));
85604                }
85605                if (empty($c['prompt'])) {
85606                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT,
85607                        array('index' => $i));
85608                }
85609                $i++;
85610            }
85611        }
85612        if (empty($info['filelist'])) {
85613            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES);
85614            $errors[] = 'no files';
85615        } else {
85616            foreach ($info['filelist'] as $file => $fa) {
85617                if (empty($fa['role'])) {
85618                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE,
85619                        array('file' => $file, 'roles' => PEAR_Common::getFileRoles()));
85620                    continue;
85621                } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
85622                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE,
85623                        array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles()));
85624                }
85625                if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) {
85626                    // file contains .. parent directory or . cur directory references
85627                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
85628                        array('file' => $file));
85629                }
85630                if (isset($fa['install-as']) &&
85631                      preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
85632                                 str_replace('\\', '/', $fa['install-as']))) {
85633                    // install-as contains .. parent directory or . cur directory references
85634                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
85635                        array('file' => $file . ' [installed as ' . $fa['install-as'] . ']'));
85636                }
85637                if (isset($fa['baseinstalldir']) &&
85638                      preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
85639                                 str_replace('\\', '/', $fa['baseinstalldir']))) {
85640                    // install-as contains .. parent directory or . cur directory references
85641                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
85642                        array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']'));
85643                }
85644            }
85645        }
85646        if (isset($this->_registry) && $this->_isValid) {
85647            $chan = $this->_registry->getChannel('pear.php.net');
85648            if (PEAR::isError($chan)) {
85649                $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage());
85650                return $this->_isValid = 0;
85651            }
85652            $validator = $chan->getValidationObject();
85653            $validator->setPackageFile($this);
85654            $validator->validate($state);
85655            $failures = $validator->getFailures();
85656            foreach ($failures['errors'] as $error) {
85657                $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error);
85658            }
85659            foreach ($failures['warnings'] as $warning) {
85660                $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning);
85661            }
85662        }
85663        if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) {
85664            if ($this->_analyzePhpFiles()) {
85665                $this->_isValid = true;
85666            }
85667        }
85668        if ($this->_isValid) {
85669            return $this->_isValid = $state;
85670        }
85671        return $this->_isValid = 0;
85672    }
85673
85674    function _analyzePhpFiles()
85675    {
85676        if (!$this->_isValid) {
85677            return false;
85678        }
85679        if (!isset($this->_packageFile)) {
85680            return false;
85681        }
85682        $dir_prefix = dirname($this->_packageFile);
85683        $common = new PEAR_Common;
85684        $log = isset($this->_logger) ? array(&$this->_logger, 'log') :
85685            array($common, 'log');
85686        $info = $this->getFilelist();
85687        foreach ($info as $file => $fa) {
85688            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
85689                $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND,
85690                    array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file));
85691                continue;
85692            }
85693            if ($fa['role'] == 'php' && $dir_prefix) {
85694                call_user_func_array($log, array(1, "Analyzing $file"));
85695                $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
85696                if ($srcinfo) {
85697                    $this->_buildProvidesArray($srcinfo);
85698                }
85699            }
85700        }
85701        $this->_packageName = $pn = $this->getPackage();
85702        $pnl = strlen($pn);
85703        if (isset($this->_packageInfo['provides'])) {
85704            foreach ((array) $this->_packageInfo['provides'] as $key => $what) {
85705                if (isset($what['explicit'])) {
85706                    // skip conformance checks if the provides entry is
85707                    // specified in the package.xml file
85708                    continue;
85709                }
85710                extract($what);
85711                if ($type == 'class') {
85712                    if (!strncasecmp($name, $pn, $pnl)) {
85713                        continue;
85714                    }
85715                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
85716                        array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
85717                } elseif ($type == 'function') {
85718                    if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
85719                        continue;
85720                    }
85721                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
85722                        array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
85723                }
85724            }
85725        }
85726        return $this->_isValid;
85727    }
85728
85729    /**
85730     * Get the default xml generator object
85731     *
85732     * @return PEAR_PackageFile_Generator_v1
85733     */
85734    function &getDefaultGenerator()
85735    {
85736        if (!class_exists('PEAR_PackageFile_Generator_v1')) {
85737            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/Generator/v1.php';
85738        }
85739        $a = &new PEAR_PackageFile_Generator_v1($this);
85740        return $a;
85741    }
85742
85743    /**
85744     * Get the contents of a file listed within the package.xml
85745     * @param string
85746     * @return string
85747     */
85748    function getFileContents($file)
85749    {
85750        if ($this->_archiveFile == $this->_packageFile) { // unpacked
85751            $dir = dirname($this->_packageFile);
85752            $file = $dir . DIRECTORY_SEPARATOR . $file;
85753            $file = str_replace(array('/', '\\'),
85754                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
85755            if (file_exists($file) && is_readable($file)) {
85756                return implode('', file($file));
85757            }
85758        } else { // tgz
85759            if (!class_exists('Archive_Tar')) {
85760                require_once 'phar://install-pear-nozlib.phar/' . 'Archive/Tar.php';
85761            }
85762            $tar = &new Archive_Tar($this->_archiveFile);
85763            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
85764            if ($file != 'package.xml' && $file != 'package2.xml') {
85765                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
85766            }
85767            $file = $tar->extractInString($file);
85768            $tar->popErrorHandling();
85769            if (PEAR::isError($file)) {
85770                return PEAR::raiseError("Cannot locate file '$file' in archive");
85771            }
85772            return $file;
85773        }
85774    }
85775
85776    // {{{ analyzeSourceCode()
85777    /**
85778     * Analyze the source code of the given PHP file
85779     *
85780     * @param  string Filename of the PHP file
85781     * @return mixed
85782     * @access private
85783     */
85784    function _analyzeSourceCode($file)
85785    {
85786        if (!function_exists("token_get_all")) {
85787            return false;
85788        }
85789        if (!defined('T_DOC_COMMENT')) {
85790            define('T_DOC_COMMENT', T_COMMENT);
85791        }
85792        if (!defined('T_INTERFACE')) {
85793            define('T_INTERFACE', -1);
85794        }
85795        if (!defined('T_IMPLEMENTS')) {
85796            define('T_IMPLEMENTS', -1);
85797        }
85798        if (!$fp = @fopen($file, "r")) {
85799            return false;
85800        }
85801        fclose($fp);
85802        $contents = file_get_contents($file);
85803        $tokens = token_get_all($contents);
85804/*
85805        for ($i = 0; $i < sizeof($tokens); $i++) {
85806            @list($token, $data) = $tokens[$i];
85807            if (is_string($token)) {
85808                var_dump($token);
85809            } else {
85810                print token_name($token) . ' ';
85811                var_dump(rtrim($data));
85812            }
85813        }
85814*/
85815        $look_for = 0;
85816        $paren_level = 0;
85817        $bracket_level = 0;
85818        $brace_level = 0;
85819        $lastphpdoc = '';
85820        $current_class = '';
85821        $current_interface = '';
85822        $current_class_level = -1;
85823        $current_function = '';
85824        $current_function_level = -1;
85825        $declared_classes = array();
85826        $declared_interfaces = array();
85827        $declared_functions = array();
85828        $declared_methods = array();
85829        $used_classes = array();
85830        $used_functions = array();
85831        $extends = array();
85832        $implements = array();
85833        $nodeps = array();
85834        $inquote = false;
85835        $interface = false;
85836        for ($i = 0; $i < sizeof($tokens); $i++) {
85837            if (is_array($tokens[$i])) {
85838                list($token, $data) = $tokens[$i];
85839            } else {
85840                $token = $tokens[$i];
85841                $data = '';
85842            }
85843            if ($inquote) {
85844                if ($token != '"' && $token != T_END_HEREDOC) {
85845                    continue;
85846                } else {
85847                    $inquote = false;
85848                    continue;
85849                }
85850            }
85851            switch ($token) {
85852                case T_WHITESPACE :
85853                    continue;
85854                case ';':
85855                    if ($interface) {
85856                        $current_function = '';
85857                        $current_function_level = -1;
85858                    }
85859                    break;
85860                case '"':
85861                case T_START_HEREDOC:
85862                    $inquote = true;
85863                    break;
85864                case T_CURLY_OPEN:
85865                case T_DOLLAR_OPEN_CURLY_BRACES:
85866                case '{': $brace_level++; continue 2;
85867                case '}':
85868                    $brace_level--;
85869                    if ($current_class_level == $brace_level) {
85870                        $current_class = '';
85871                        $current_class_level = -1;
85872                    }
85873                    if ($current_function_level == $brace_level) {
85874                        $current_function = '';
85875                        $current_function_level = -1;
85876                    }
85877                    continue 2;
85878                case '[': $bracket_level++; continue 2;
85879                case ']': $bracket_level--; continue 2;
85880                case '(': $paren_level++;   continue 2;
85881                case ')': $paren_level--;   continue 2;
85882                case T_INTERFACE:
85883                    $interface = true;
85884                case T_CLASS:
85885                    if (($current_class_level != -1) || ($current_function_level != -1)) {
85886                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
85887                            array('file' => $file));
85888                        return false;
85889                    }
85890                case T_FUNCTION:
85891                case T_NEW:
85892                case T_EXTENDS:
85893                case T_IMPLEMENTS:
85894                    $look_for = $token;
85895                    continue 2;
85896                case T_STRING:
85897                    if (version_compare(zend_version(), '2.0', '<')) {
85898                        if (in_array(strtolower($data),
85899                            array('public', 'private', 'protected', 'abstract',
85900                                  'interface', 'implements', 'throw') 
85901                                 )) {
85902                            $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_PHP5,
85903                                array($file));
85904                        }
85905                    }
85906                    if ($look_for == T_CLASS) {
85907                        $current_class = $data;
85908                        $current_class_level = $brace_level;
85909                        $declared_classes[] = $current_class;
85910                    } elseif ($look_for == T_INTERFACE) {
85911                        $current_interface = $data;
85912                        $current_class_level = $brace_level;
85913                        $declared_interfaces[] = $current_interface;
85914                    } elseif ($look_for == T_IMPLEMENTS) {
85915                        $implements[$current_class] = $data;
85916                    } elseif ($look_for == T_EXTENDS) {
85917                        $extends[$current_class] = $data;
85918                    } elseif ($look_for == T_FUNCTION) {
85919                        if ($current_class) {
85920                            $current_function = "$current_class::$data";
85921                            $declared_methods[$current_class][] = $data;
85922                        } elseif ($current_interface) {
85923                            $current_function = "$current_interface::$data";
85924                            $declared_methods[$current_interface][] = $data;
85925                        } else {
85926                            $current_function = $data;
85927                            $declared_functions[] = $current_function;
85928                        }
85929                        $current_function_level = $brace_level;
85930                        $m = array();
85931                    } elseif ($look_for == T_NEW) {
85932                        $used_classes[$data] = true;
85933                    }
85934                    $look_for = 0;
85935                    continue 2;
85936                case T_VARIABLE:
85937                    $look_for = 0;
85938                    continue 2;
85939                case T_DOC_COMMENT:
85940                case T_COMMENT:
85941                    if (preg_match('!^/\*\*\s!', $data)) {
85942                        $lastphpdoc = $data;
85943                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
85944                            $nodeps = array_merge($nodeps, $m[1]);
85945                        }
85946                    }
85947                    continue 2;
85948                case T_DOUBLE_COLON:
85949                    if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
85950                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
85951                            array('file' => $file));
85952                        return false;
85953                    }
85954                    $class = $tokens[$i - 1][1];
85955                    if (strtolower($class) != 'parent') {
85956                        $used_classes[$class] = true;
85957                    }
85958                    continue 2;
85959            }
85960        }
85961        return array(
85962            "source_file" => $file,
85963            "declared_classes" => $declared_classes,
85964            "declared_interfaces" => $declared_interfaces,
85965            "declared_methods" => $declared_methods,
85966            "declared_functions" => $declared_functions,
85967            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
85968            "inheritance" => $extends,
85969            "implements" => $implements,
85970            );
85971    }
85972
85973    /**
85974     * Build a "provides" array from data returned by
85975     * analyzeSourceCode().  The format of the built array is like
85976     * this:
85977     *
85978     *  array(
85979     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
85980     *    ...
85981     *  )
85982     *
85983     *
85984     * @param array $srcinfo array with information about a source file
85985     * as returned by the analyzeSourceCode() method.
85986     *
85987     * @return void
85988     *
85989     * @access private
85990     *
85991     */
85992    function _buildProvidesArray($srcinfo)
85993    {
85994        if (!$this->_isValid) {
85995            return false;
85996        }
85997        $file = basename($srcinfo['source_file']);
85998        $pn = $this->getPackage();
85999        $pnl = strlen($pn);
86000        foreach ($srcinfo['declared_classes'] as $class) {
86001            $key = "class;$class";
86002            if (isset($this->_packageInfo['provides'][$key])) {
86003                continue;
86004            }
86005            $this->_packageInfo['provides'][$key] =
86006                array('file'=> $file, 'type' => 'class', 'name' => $class);
86007            if (isset($srcinfo['inheritance'][$class])) {
86008                $this->_packageInfo['provides'][$key]['extends'] =
86009                    $srcinfo['inheritance'][$class];
86010            }
86011        }
86012        foreach ($srcinfo['declared_methods'] as $class => $methods) {
86013            foreach ($methods as $method) {
86014                $function = "$class::$method";
86015                $key = "function;$function";
86016                if ($method{0} == '_' || !strcasecmp($method, $class) ||
86017                    isset($this->_packageInfo['provides'][$key])) {
86018                    continue;
86019                }
86020                $this->_packageInfo['provides'][$key] =
86021                    array('file'=> $file, 'type' => 'function', 'name' => $function);
86022            }
86023        }
86024
86025        foreach ($srcinfo['declared_functions'] as $function) {
86026            $key = "function;$function";
86027            if ($function{0} == '_' || isset($this->_packageInfo['provides'][$key])) {
86028                continue;
86029            }
86030            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
86031                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
86032            }
86033            $this->_packageInfo['provides'][$key] =
86034                array('file'=> $file, 'type' => 'function', 'name' => $function);
86035        }
86036    }
86037
86038    // }}}
86039}
86040?>
86041<?php
86042/**
86043 * PEAR_PackageFile_v2, package.xml version 2.0
86044 *
86045 * PHP versions 4 and 5
86046 *
86047 * @category   pear
86048 * @package    PEAR
86049 * @author     Greg Beaver <cellog@php.net>
86050 * @copyright  1997-2009 The Authors
86051 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
86052 * @version    CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
86053 * @link       http://pear.php.net/package/PEAR
86054 * @since      File available since Release 1.4.0a1
86055 */
86056/**
86057 * For error handling
86058 */
86059require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ErrorStack.php';
86060/**
86061 * @category   pear
86062 * @package    PEAR
86063 * @author     Greg Beaver <cellog@php.net>
86064 * @copyright  1997-2009 The Authors
86065 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
86066 * @version    Release: 1.9.4
86067 * @link       http://pear.php.net/package/PEAR
86068 * @since      Class available since Release 1.4.0a1
86069 */
86070class PEAR_PackageFile_v2
86071{
86072
86073    /**
86074     * Parsed package information
86075     * @var array
86076     * @access private
86077     */
86078    var $_packageInfo = array();
86079
86080    /**
86081     * path to package .tgz or false if this is a local/extracted package.xml
86082     * @var string|false
86083     * @access private
86084     */
86085    var $_archiveFile;
86086
86087    /**
86088     * path to package .xml or false if this is an abstract parsed-from-string xml
86089     * @var string|false
86090     * @access private
86091     */
86092    var $_packageFile;
86093
86094    /**
86095     * This is used by file analysis routines to log progress information
86096     * @var PEAR_Common
86097     * @access protected
86098     */
86099    var $_logger;
86100
86101    /**
86102     * This is set to the highest validation level that has been validated
86103     *
86104     * If the package.xml is invalid or unknown, this is set to 0.  If
86105     * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL.  If
86106     * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING
86107     * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING.  This allows validation
86108     * "caching" to occur, which is particularly important for package validation, so
86109     * that PHP files are not validated twice
86110     * @var int
86111     * @access private
86112     */
86113    var $_isValid = 0;
86114
86115    /**
86116     * True if the filelist has been validated
86117     * @param bool
86118     */
86119    var $_filesValid = false;
86120
86121    /**
86122     * @var PEAR_Registry
86123     * @access protected
86124     */
86125    var $_registry;
86126
86127    /**
86128     * @var PEAR_Config
86129     * @access protected
86130     */
86131    var $_config;
86132
86133    /**
86134     * Optional Dependency group requested for installation
86135     * @var string
86136     * @access private
86137     */
86138    var $_requestedGroup = false;
86139
86140    /**
86141     * @var PEAR_ErrorStack
86142     * @access protected
86143     */
86144    var $_stack;
86145
86146    /**
86147     * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible
86148     */
86149    var $_tasksNs;
86150
86151    /**
86152     * Determines whether this packagefile was initialized only with partial package info
86153     *
86154     * If this package file was constructed via parsing REST, it will only contain
86155     *
86156     * - package name
86157     * - channel name
86158     * - dependencies
86159     * @var boolean
86160     * @access private
86161     */
86162    var $_incomplete = true;
86163
86164    /**
86165     * @var PEAR_PackageFile_v2_Validator
86166     */
86167    var $_v2Validator;
86168
86169    /**
86170     * The constructor merely sets up the private error stack
86171     */
86172    function PEAR_PackageFile_v2()
86173    {
86174        $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null);
86175        $this->_isValid = false;
86176    }
86177
86178    /**
86179     * To make unit-testing easier
86180     * @param PEAR_Frontend_*
86181     * @param array options
86182     * @param PEAR_Config
86183     * @return PEAR_Downloader
86184     * @access protected
86185     */
86186    function &getPEARDownloader(&$i, $o, &$c)
86187    {
86188        $z = &new PEAR_Downloader($i, $o, $c);
86189        return $z;
86190    }
86191
86192    /**
86193     * To make unit-testing easier
86194     * @param PEAR_Config
86195     * @param array options
86196     * @param array package name as returned from {@link PEAR_Registry::parsePackageName()}
86197     * @param int PEAR_VALIDATE_* constant
86198     * @return PEAR_Dependency2
86199     * @access protected
86200     */
86201    function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING)
86202    {
86203        if (!class_exists('PEAR_Dependency2')) {
86204            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Dependency2.php';
86205        }
86206        $z = &new PEAR_Dependency2($c, $o, $p, $s);
86207        return $z;
86208    }
86209
86210    function getInstalledBinary()
86211    {
86212        return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] :
86213            false;
86214    }
86215
86216    /**
86217     * Installation of source package has failed, attempt to download and install the
86218     * binary version of this package.
86219     * @param PEAR_Installer
86220     * @return array|false
86221     */
86222    function installBinary(&$installer)
86223    {
86224        if (!OS_WINDOWS) {
86225            $a = false;
86226            return $a;
86227        }
86228        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
86229            $releasetype = $this->getPackageType() . 'release';
86230            if (!is_array($installer->getInstallPackages())) {
86231                $a = false;
86232                return $a;
86233            }
86234            foreach ($installer->getInstallPackages() as $p) {
86235                if ($p->isExtension($this->_packageInfo['providesextension'])) {
86236                    if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') {
86237                        $a = false;
86238                        return $a; // the user probably downloaded it separately
86239                    }
86240                }
86241            }
86242            if (isset($this->_packageInfo[$releasetype]['binarypackage'])) {
86243                $installer->log(0, 'Attempting to download binary version of extension "' .
86244                    $this->_packageInfo['providesextension'] . '"');
86245                $params = $this->_packageInfo[$releasetype]['binarypackage'];
86246                if (!is_array($params) || !isset($params[0])) {
86247                    $params = array($params);
86248                }
86249                if (isset($this->_packageInfo['channel'])) {
86250                    foreach ($params as $i => $param) {
86251                        $params[$i] = array('channel' => $this->_packageInfo['channel'],
86252                            'package' => $param, 'version' => $this->getVersion());
86253                    }
86254                }
86255                $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(),
86256                    $installer->config);
86257                $verbose = $dl->config->get('verbose');
86258                $dl->config->set('verbose', -1);
86259                foreach ($params as $param) {
86260                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
86261                    $ret = $dl->download(array($param));
86262                    PEAR::popErrorHandling();
86263                    if (is_array($ret) && count($ret)) {
86264                        break;
86265                    }
86266                }
86267                $dl->config->set('verbose', $verbose);
86268                if (is_array($ret)) {
86269                    if (count($ret) == 1) {
86270                        $pf = $ret[0]->getPackageFile();
86271                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
86272                        $err = $installer->install($ret[0]);
86273                        PEAR::popErrorHandling();
86274                        if (is_array($err)) {
86275                            $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage();
86276                            // "install" self, so all dependencies will work transparently
86277                            $this->_registry->addPackage2($this);
86278                            $installer->log(0, 'Download and install of binary extension "' .
86279                                $this->_registry->parsedPackageNameToString(
86280                                    array('channel' => $pf->getChannel(),
86281                                          'package' => $pf->getPackage()), true) . '" successful');
86282                            $a = array($ret[0], $err);
86283                            return $a;
86284                        }
86285                        $installer->log(0, 'Download and install of binary extension "' .
86286                            $this->_registry->parsedPackageNameToString(
86287                                    array('channel' => $pf->getChannel(),
86288                                          'package' => $pf->getPackage()), true) . '" failed');
86289                    }
86290                }
86291            }
86292        }
86293        $a = false;
86294        return $a;
86295    }
86296
86297    /**
86298     * @return string|false Extension name
86299     */
86300    function getProvidesExtension()
86301    {
86302        if (in_array($this->getPackageType(),
86303              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
86304            if (isset($this->_packageInfo['providesextension'])) {
86305                return $this->_packageInfo['providesextension'];
86306            }
86307        }
86308        return false;
86309    }
86310
86311    /**
86312     * @param string Extension name
86313     * @return bool
86314     */
86315    function isExtension($extension)
86316    {
86317        if (in_array($this->getPackageType(),
86318              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
86319            return $this->_packageInfo['providesextension'] == $extension;
86320        }
86321        return false;
86322    }
86323
86324    /**
86325     * Tests whether every part of the package.xml 1.0 is represented in
86326     * this package.xml 2.0
86327     * @param PEAR_PackageFile_v1
86328     * @return bool
86329     */
86330    function isEquivalent($pf1)
86331    {
86332        if (!$pf1) {
86333            return true;
86334        }
86335        if ($this->getPackageType() == 'bundle') {
86336            return false;
86337        }
86338        $this->_stack->getErrors(true);
86339        if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) {
86340            return false;
86341        }
86342        $pass = true;
86343        if ($pf1->getPackage() != $this->getPackage()) {
86344            $this->_differentPackage($pf1->getPackage());
86345            $pass = false;
86346        }
86347        if ($pf1->getVersion() != $this->getVersion()) {
86348            $this->_differentVersion($pf1->getVersion());
86349            $pass = false;
86350        }
86351        if (trim($pf1->getSummary()) != $this->getSummary()) {
86352            $this->_differentSummary($pf1->getSummary());
86353            $pass = false;
86354        }
86355        if (preg_replace('/\s+/', '', $pf1->getDescription()) !=
86356              preg_replace('/\s+/', '', $this->getDescription())) {
86357            $this->_differentDescription($pf1->getDescription());
86358            $pass = false;
86359        }
86360        if ($pf1->getState() != $this->getState()) {
86361            $this->_differentState($pf1->getState());
86362            $pass = false;
86363        }
86364        if (!strstr(preg_replace('/\s+/', '', $this->getNotes()),
86365              preg_replace('/\s+/', '', $pf1->getNotes()))) {
86366            $this->_differentNotes($pf1->getNotes());
86367            $pass = false;
86368        }
86369        $mymaintainers = $this->getMaintainers();
86370        $yourmaintainers = $pf1->getMaintainers();
86371        for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) {
86372            $reset = false;
86373            for ($i2 = 0; $i2 < count($mymaintainers); $i2++) {
86374                if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) {
86375                    if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) {
86376                        $this->_differentRole($mymaintainers[$i2]['handle'],
86377                            $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']);
86378                        $pass = false;
86379                    }
86380                    if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
86381                        $this->_differentEmail($mymaintainers[$i2]['handle'],
86382                            $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
86383                        $pass = false;
86384                    }
86385                    if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
86386                        $this->_differentName($mymaintainers[$i2]['handle'],
86387                            $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
86388                        $pass = false;
86389                    }
86390                    unset($mymaintainers[$i2]);
86391                    $mymaintainers = array_values($mymaintainers);
86392                    unset($yourmaintainers[$i1]);
86393                    $yourmaintainers = array_values($yourmaintainers);
86394                    $reset = true;
86395                    break;
86396                }
86397            }
86398            if ($reset) {
86399                $i1 = -1;
86400            }
86401        }
86402        $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers);
86403        $filelist = $this->getFilelist();
86404        foreach ($pf1->getFilelist() as $file => $atts) {
86405            if (!isset($filelist[$file])) {
86406                $this->_missingFile($file);
86407                $pass = false;
86408            }
86409        }
86410        return $pass;
86411    }
86412
86413    function _differentPackage($package)
86414    {
86415        $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
86416            'self' => $this->getPackage()),
86417            'package.xml 1.0 package "%package%" does not match "%self%"');
86418    }
86419
86420    function _differentVersion($version)
86421    {
86422        $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
86423            'self' => $this->getVersion()),
86424            'package.xml 1.0 version "%version%" does not match "%self%"');
86425    }
86426
86427    function _differentState($state)
86428    {
86429        $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
86430            'self' => $this->getState()),
86431            'package.xml 1.0 state "%state%" does not match "%self%"');
86432    }
86433
86434    function _differentRole($handle, $role, $selfrole)
86435    {
86436        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
86437            'role' => $role, 'self' => $selfrole),
86438            'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"');
86439    }
86440
86441    function _differentEmail($handle, $email, $selfemail)
86442    {
86443        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
86444            'email' => $email, 'self' => $selfemail),
86445            'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"');
86446    }
86447
86448    function _differentName($handle, $name, $selfname)
86449    {
86450        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
86451            'name' => $name, 'self' => $selfname),
86452            'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"');
86453    }
86454
86455    function _unmatchedMaintainers($my, $yours)
86456    {
86457        if ($my) {
86458            array_walk($my, create_function('&$i, $k', '$i = $i["handle"];'));
86459            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my),
86460                'package.xml 2.0 has unmatched extra maintainers "%handles%"');
86461        }
86462        if ($yours) {
86463            array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];'));
86464            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours),
86465                'package.xml 1.0 has unmatched extra maintainers "%handles%"');
86466        }
86467    }
86468
86469    function _differentNotes($notes)
86470    {
86471        $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...';
86472        $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() :
86473            substr($this->getNotes(), 0, 24) . '...';
86474        $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes,
86475            'self' => $truncmynotes),
86476            'package.xml 1.0 release notes "%notes%" do not match "%self%"');
86477    }
86478
86479    function _differentSummary($summary)
86480    {
86481        $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...';
86482        $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() :
86483            substr($this->getsummary(), 0, 24) . '...';
86484        $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary,
86485            'self' => $truncmysummary),
86486            'package.xml 1.0 summary "%summary%" does not match "%self%"');
86487    }
86488
86489    function _differentDescription($description)
86490    {
86491        $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...');
86492        $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() :
86493            substr($this->getdescription(), 0, 24) . '...');
86494        $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription,
86495            'self' => $truncmydescription),
86496            'package.xml 1.0 description "%description%" does not match "%self%"');
86497    }
86498
86499    function _missingFile($file)
86500    {
86501        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
86502            'package.xml 1.0 file "%file%" is not present in <contents>');
86503    }
86504
86505    /**
86506     * WARNING - do not use this function unless you know what you're doing
86507     */
86508    function setRawState($state)
86509    {
86510        if (!isset($this->_packageInfo['stability'])) {
86511            $this->_packageInfo['stability'] = array();
86512        }
86513        $this->_packageInfo['stability']['release'] = $state;
86514    }
86515
86516    /**
86517     * WARNING - do not use this function unless you know what you're doing
86518     */
86519    function setRawCompatible($compatible)
86520    {
86521        $this->_packageInfo['compatible'] = $compatible;
86522    }
86523
86524    /**
86525     * WARNING - do not use this function unless you know what you're doing
86526     */
86527    function setRawPackage($package)
86528    {
86529        $this->_packageInfo['name'] = $package;
86530    }
86531
86532    /**
86533     * WARNING - do not use this function unless you know what you're doing
86534     */
86535    function setRawChannel($channel)
86536    {
86537        $this->_packageInfo['channel'] = $channel;
86538    }
86539
86540    function setRequestedGroup($group)
86541    {
86542        $this->_requestedGroup = $group;
86543    }
86544
86545    function getRequestedGroup()
86546    {
86547        if (isset($this->_requestedGroup)) {
86548            return $this->_requestedGroup;
86549        }
86550        return false;
86551    }
86552
86553    /**
86554     * For saving in the registry.
86555     *
86556     * Set the last version that was installed
86557     * @param string
86558     */
86559    function setLastInstalledVersion($version)
86560    {
86561        $this->_packageInfo['_lastversion'] = $version;
86562    }
86563
86564    /**
86565     * @return string|false
86566     */
86567    function getLastInstalledVersion()
86568    {
86569        if (isset($this->_packageInfo['_lastversion'])) {
86570            return $this->_packageInfo['_lastversion'];
86571        }
86572        return false;
86573    }
86574
86575    /**
86576     * Determines whether this package.xml has post-install scripts or not
86577     * @return array|false
86578     */
86579    function listPostinstallScripts()
86580    {
86581        $filelist = $this->getFilelist();
86582        $contents = $this->getContents();
86583        $contents = $contents['dir']['file'];
86584        if (!is_array($contents) || !isset($contents[0])) {
86585            $contents = array($contents);
86586        }
86587        $taskfiles = array();
86588        foreach ($contents as $file) {
86589            $atts = $file['attribs'];
86590            unset($file['attribs']);
86591            if (count($file)) {
86592                $taskfiles[$atts['name']] = $file;
86593            }
86594        }
86595        $common = new PEAR_Common;
86596        $common->debug = $this->_config->get('verbose');
86597        $this->_scripts = array();
86598        $ret = array();
86599        foreach ($taskfiles as $name => $tasks) {
86600            if (!isset($filelist[$name])) {
86601                // ignored files will not be in the filelist
86602                continue;
86603            }
86604            $atts = $filelist[$name];
86605            foreach ($tasks as $tag => $raw) {
86606                $task = $this->getTask($tag);
86607                $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL);
86608                if ($task->isScript()) {
86609                    $ret[] = $filelist[$name]['installed_as'];
86610                }
86611            }
86612        }
86613        if (count($ret)) {
86614            return $ret;
86615        }
86616        return false;
86617    }
86618
86619    /**
86620     * Initialize post-install scripts for running
86621     *
86622     * This method can be used to detect post-install scripts, as the return value
86623     * indicates whether any exist
86624     * @return bool
86625     */
86626    function initPostinstallScripts()
86627    {
86628        $filelist = $this->getFilelist();
86629        $contents = $this->getContents();
86630        $contents = $contents['dir']['file'];
86631        if (!is_array($contents) || !isset($contents[0])) {
86632            $contents = array($contents);
86633        }
86634        $taskfiles = array();
86635        foreach ($contents as $file) {
86636            $atts = $file['attribs'];
86637            unset($file['attribs']);
86638            if (count($file)) {
86639                $taskfiles[$atts['name']] = $file;
86640            }
86641        }
86642        $common = new PEAR_Common;
86643        $common->debug = $this->_config->get('verbose');
86644        $this->_scripts = array();
86645        foreach ($taskfiles as $name => $tasks) {
86646            if (!isset($filelist[$name])) {
86647                // file was not installed due to installconditions
86648                continue;
86649            }
86650            $atts = $filelist[$name];
86651            foreach ($tasks as $tag => $raw) {
86652                $taskname = $this->getTask($tag);
86653                $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL);
86654                if (!$task->isScript()) {
86655                    continue; // scripts are only handled after installation
86656                }
86657                $lastversion = isset($this->_packageInfo['_lastversion']) ?
86658                    $this->_packageInfo['_lastversion'] : null;
86659                $task->init($raw, $atts, $lastversion);
86660                $res = $task->startSession($this, $atts['installed_as']);
86661                if (!$res) {
86662                    continue; // skip this file
86663                }
86664                if (PEAR::isError($res)) {
86665                    return $res;
86666                }
86667                $assign = &$task;
86668                $this->_scripts[] = &$assign;
86669            }
86670        }
86671        if (count($this->_scripts)) {
86672            return true;
86673        }
86674        return false;
86675    }
86676
86677    function runPostinstallScripts()
86678    {
86679        if ($this->initPostinstallScripts()) {
86680            $ui = &PEAR_Frontend::singleton();
86681            if ($ui) {
86682                $ui->runPostinstallScripts($this->_scripts, $this);
86683            }
86684        }
86685    }
86686
86687
86688    /**
86689     * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
86690     * <file> tags.
86691     */
86692    function flattenFilelist()
86693    {
86694        if (isset($this->_packageInfo['bundle'])) {
86695            return;
86696        }
86697        $filelist = array();
86698        if (isset($this->_packageInfo['contents']['dir']['dir'])) {
86699            $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']);
86700            if (!isset($filelist[1])) {
86701                $filelist = $filelist[0];
86702            }
86703            $this->_packageInfo['contents']['dir']['file'] = $filelist;
86704            unset($this->_packageInfo['contents']['dir']['dir']);
86705        } else {
86706            // else already flattened but check for baseinstalldir propagation
86707            if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) {
86708                if (isset($this->_packageInfo['contents']['dir']['file'][0])) {
86709                    foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) {
86710                        if (isset($file['attribs']['baseinstalldir'])) {
86711                            continue;
86712                        }
86713                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
86714                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
86715                    }
86716                } else {
86717                    if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) {
86718                       $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir']
86719                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
86720                    }
86721                }
86722            }
86723        }
86724    }
86725
86726    /**
86727     * @param array the final flattened file list
86728     * @param array the current directory being processed
86729     * @param string|false any recursively inherited baeinstalldir attribute
86730     * @param string private recursion variable
86731     * @return array
86732     * @access protected
86733     */
86734    function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
86735    {
86736        if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
86737            $baseinstall = $dir['attribs']['baseinstalldir'];
86738        }
86739        if (isset($dir['dir'])) {
86740            if (!isset($dir['dir'][0])) {
86741                $dir['dir'] = array($dir['dir']);
86742            }
86743            foreach ($dir['dir'] as $subdir) {
86744                if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
86745                    $name = '*unknown*';
86746                } else {
86747                    $name = $subdir['attribs']['name'];
86748                }
86749                $newpath = empty($path) ? $name :
86750                    $path . '/' . $name;
86751                $this->_getFlattenedFilelist($files, $subdir,
86752                    $baseinstall, $newpath);
86753            }
86754        }
86755        if (isset($dir['file'])) {
86756            if (!isset($dir['file'][0])) {
86757                $dir['file'] = array($dir['file']);
86758            }
86759            foreach ($dir['file'] as $file) {
86760                $attrs = $file['attribs'];
86761                $name = $attrs['name'];
86762                if ($baseinstall && !isset($attrs['baseinstalldir'])) {
86763                    $attrs['baseinstalldir'] = $baseinstall;
86764                }
86765                $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
86766                $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
86767                    $attrs['name']);
86768                $file['attribs'] = $attrs;
86769                $files[] = $file;
86770            }
86771        }
86772    }
86773
86774    function setConfig(&$config)
86775    {
86776        $this->_config = &$config;
86777        $this->_registry = &$config->getRegistry();
86778    }
86779
86780    function setLogger(&$logger)
86781    {
86782        if (!is_object($logger) || !method_exists($logger, 'log')) {
86783            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
86784        }
86785        $this->_logger = &$logger;
86786    }
86787
86788    /**
86789     * WARNING - do not use this function directly unless you know what you're doing
86790     */
86791    function setDeps($deps)
86792    {
86793        $this->_packageInfo['dependencies'] = $deps;
86794    }
86795
86796    /**
86797     * WARNING - do not use this function directly unless you know what you're doing
86798     */
86799    function setCompatible($compat)
86800    {
86801        $this->_packageInfo['compatible'] = $compat;
86802    }
86803
86804    function setPackagefile($file, $archive = false)
86805    {
86806        $this->_packageFile = $file;
86807        $this->_archiveFile = $archive ? $archive : $file;
86808    }
86809
86810    /**
86811     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
86812     * @param boolean determines whether to purge the error stack after retrieving
86813     * @return array
86814     */
86815    function getValidationWarnings($purge = true)
86816    {
86817        return $this->_stack->getErrors($purge);
86818    }
86819
86820    function getPackageFile()
86821    {
86822        return $this->_packageFile;
86823    }
86824
86825    function getArchiveFile()
86826    {
86827        return $this->_archiveFile;
86828    }
86829
86830
86831    /**
86832     * Directly set the array that defines this packagefile
86833     *
86834     * WARNING: no validation.  This should only be performed by internal methods
86835     * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2
86836     * @param array
86837     */
86838    function fromArray($pinfo)
86839    {
86840        unset($pinfo['old']);
86841        unset($pinfo['xsdversion']);
86842        // If the changelog isn't an array then it was passed in as an empty tag
86843        if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) {
86844          unset($pinfo['changelog']);
86845        }
86846        $this->_incomplete = false;
86847        $this->_packageInfo = $pinfo;
86848    }
86849
86850    function isIncomplete()
86851    {
86852        return $this->_incomplete;
86853    }
86854
86855    /**
86856     * @return array
86857     */
86858    function toArray($forreg = false)
86859    {
86860        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
86861            return false;
86862        }
86863        return $this->getArray($forreg);
86864    }
86865
86866    function getArray($forReg = false)
86867    {
86868        if ($forReg) {
86869            $arr = $this->_packageInfo;
86870            $arr['old'] = array();
86871            $arr['old']['version'] = $this->getVersion();
86872            $arr['old']['release_date'] = $this->getDate();
86873            $arr['old']['release_state'] = $this->getState();
86874            $arr['old']['release_license'] = $this->getLicense();
86875            $arr['old']['release_notes'] = $this->getNotes();
86876            $arr['old']['release_deps'] = $this->getDeps();
86877            $arr['old']['maintainers'] = $this->getMaintainers();
86878            $arr['xsdversion'] = '2.0';
86879            return $arr;
86880        } else {
86881            $info = $this->_packageInfo;
86882            unset($info['dirtree']);
86883            if (isset($info['_lastversion'])) {
86884                unset($info['_lastversion']);
86885            }
86886            if (isset($info['#binarypackage'])) {
86887                unset($info['#binarypackage']);
86888            }
86889            return $info;
86890        }
86891    }
86892
86893    function packageInfo($field)
86894    {
86895        $arr = $this->getArray(true);
86896        if ($field == 'state') {
86897            return $arr['stability']['release'];
86898        }
86899        if ($field == 'api-version') {
86900            return $arr['version']['api'];
86901        }
86902        if ($field == 'api-state') {
86903            return $arr['stability']['api'];
86904        }
86905        if (isset($arr['old'][$field])) {
86906            if (!is_string($arr['old'][$field])) {
86907                return null;
86908            }
86909            return $arr['old'][$field];
86910        }
86911        if (isset($arr[$field])) {
86912            if (!is_string($arr[$field])) {
86913                return null;
86914            }
86915            return $arr[$field];
86916        }
86917        return null;
86918    }
86919
86920    function getName()
86921    {
86922        return $this->getPackage();
86923    }
86924
86925    function getPackage()
86926    {
86927        if (isset($this->_packageInfo['name'])) {
86928            return $this->_packageInfo['name'];
86929        }
86930        return false;
86931    }
86932
86933    function getChannel()
86934    {
86935        if (isset($this->_packageInfo['uri'])) {
86936            return '__uri';
86937        }
86938        if (isset($this->_packageInfo['channel'])) {
86939            return strtolower($this->_packageInfo['channel']);
86940        }
86941        return false;
86942    }
86943
86944    function getUri()
86945    {
86946        if (isset($this->_packageInfo['uri'])) {
86947            return $this->_packageInfo['uri'];
86948        }
86949        return false;
86950    }
86951
86952    function getExtends()
86953    {
86954        if (isset($this->_packageInfo['extends'])) {
86955            return $this->_packageInfo['extends'];
86956        }
86957        return false;
86958    }
86959
86960    function getSummary()
86961    {
86962        if (isset($this->_packageInfo['summary'])) {
86963            return $this->_packageInfo['summary'];
86964        }
86965        return false;
86966    }
86967
86968    function getDescription()
86969    {
86970        if (isset($this->_packageInfo['description'])) {
86971            return $this->_packageInfo['description'];
86972        }
86973        return false;
86974    }
86975
86976    function getMaintainers($raw = false)
86977    {
86978        if (!isset($this->_packageInfo['lead'])) {
86979            return false;
86980        }
86981        if ($raw) {
86982            $ret = array('lead' => $this->_packageInfo['lead']);
86983            (isset($this->_packageInfo['developer'])) ?
86984                $ret['developer'] = $this->_packageInfo['developer'] :null;
86985            (isset($this->_packageInfo['contributor'])) ?
86986                $ret['contributor'] = $this->_packageInfo['contributor'] :null;
86987            (isset($this->_packageInfo['helper'])) ?
86988                $ret['helper'] = $this->_packageInfo['helper'] :null;
86989            return $ret;
86990        } else {
86991            $ret = array();
86992            $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
86993                array($this->_packageInfo['lead']);
86994            foreach ($leads as $lead) {
86995                $s = $lead;
86996                $s['handle'] = $s['user'];
86997                unset($s['user']);
86998                $s['role'] = 'lead';
86999                $ret[] = $s;
87000            }
87001            if (isset($this->_packageInfo['developer'])) {
87002                $leads = isset($this->_packageInfo['developer'][0]) ?
87003                    $this->_packageInfo['developer'] :
87004                    array($this->_packageInfo['developer']);
87005                foreach ($leads as $maintainer) {
87006                    $s = $maintainer;
87007                    $s['handle'] = $s['user'];
87008                    unset($s['user']);
87009                    $s['role'] = 'developer';
87010                    $ret[] = $s;
87011                }
87012            }
87013            if (isset($this->_packageInfo['contributor'])) {
87014                $leads = isset($this->_packageInfo['contributor'][0]) ?
87015                    $this->_packageInfo['contributor'] :
87016                    array($this->_packageInfo['contributor']);
87017                foreach ($leads as $maintainer) {
87018                    $s = $maintainer;
87019                    $s['handle'] = $s['user'];
87020                    unset($s['user']);
87021                    $s['role'] = 'contributor';
87022                    $ret[] = $s;
87023                }
87024            }
87025            if (isset($this->_packageInfo['helper'])) {
87026                $leads = isset($this->_packageInfo['helper'][0]) ?
87027                    $this->_packageInfo['helper'] :
87028                    array($this->_packageInfo['helper']);
87029                foreach ($leads as $maintainer) {
87030                    $s = $maintainer;
87031                    $s['handle'] = $s['user'];
87032                    unset($s['user']);
87033                    $s['role'] = 'helper';
87034                    $ret[] = $s;
87035                }
87036            }
87037            return $ret;
87038        }
87039        return false;
87040    }
87041
87042    function getLeads()
87043    {
87044        if (isset($this->_packageInfo['lead'])) {
87045            return $this->_packageInfo['lead'];
87046        }
87047        return false;
87048    }
87049
87050    function getDevelopers()
87051    {
87052        if (isset($this->_packageInfo['developer'])) {
87053            return $this->_packageInfo['developer'];
87054        }
87055        return false;
87056    }
87057
87058    function getContributors()
87059    {
87060        if (isset($this->_packageInfo['contributor'])) {
87061            return $this->_packageInfo['contributor'];
87062        }
87063        return false;
87064    }
87065
87066    function getHelpers()
87067    {
87068        if (isset($this->_packageInfo['helper'])) {
87069            return $this->_packageInfo['helper'];
87070        }
87071        return false;
87072    }
87073
87074    function setDate($date)
87075    {
87076        if (!isset($this->_packageInfo['date'])) {
87077            // ensure that the extends tag is set up in the right location
87078            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
87079                array('time', 'version',
87080                    'stability', 'license', 'notes', 'contents', 'compatible',
87081                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
87082                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
87083                    'zendextbinrelease', 'bundle', 'changelog'), array(), 'date');
87084        }
87085        $this->_packageInfo['date'] = $date;
87086        $this->_isValid = 0;
87087    }
87088
87089    function setTime($time)
87090    {
87091        $this->_isValid = 0;
87092        if (!isset($this->_packageInfo['time'])) {
87093            // ensure that the time tag is set up in the right location
87094            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
87095                    array('version',
87096                    'stability', 'license', 'notes', 'contents', 'compatible',
87097                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
87098                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
87099                    'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
87100        }
87101        $this->_packageInfo['time'] = $time;
87102    }
87103
87104    function getDate()
87105    {
87106        if (isset($this->_packageInfo['date'])) {
87107            return $this->_packageInfo['date'];
87108        }
87109        return false;
87110    }
87111
87112    function getTime()
87113    {
87114        if (isset($this->_packageInfo['time'])) {
87115            return $this->_packageInfo['time'];
87116        }
87117        return false;
87118    }
87119
87120    /**
87121     * @param package|api version category to return
87122     */
87123    function getVersion($key = 'release')
87124    {
87125        if (isset($this->_packageInfo['version'][$key])) {
87126            return $this->_packageInfo['version'][$key];
87127        }
87128        return false;
87129    }
87130
87131    function getStability()
87132    {
87133        if (isset($this->_packageInfo['stability'])) {
87134            return $this->_packageInfo['stability'];
87135        }
87136        return false;
87137    }
87138
87139    function getState($key = 'release')
87140    {
87141        if (isset($this->_packageInfo['stability'][$key])) {
87142            return $this->_packageInfo['stability'][$key];
87143        }
87144        return false;
87145    }
87146
87147    function getLicense($raw = false)
87148    {
87149        if (isset($this->_packageInfo['license'])) {
87150            if ($raw) {
87151                return $this->_packageInfo['license'];
87152            }
87153            if (is_array($this->_packageInfo['license'])) {
87154                return $this->_packageInfo['license']['_content'];
87155            } else {
87156                return $this->_packageInfo['license'];
87157            }
87158        }
87159        return false;
87160    }
87161
87162    function getLicenseLocation()
87163    {
87164        if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
87165            return false;
87166        }
87167        return $this->_packageInfo['license']['attribs'];
87168    }
87169
87170    function getNotes()
87171    {
87172        if (isset($this->_packageInfo['notes'])) {
87173            return $this->_packageInfo['notes'];
87174        }
87175        return false;
87176    }
87177
87178    /**
87179     * Return the <usesrole> tag contents, if any
87180     * @return array|false
87181     */
87182    function getUsesrole()
87183    {
87184        if (isset($this->_packageInfo['usesrole'])) {
87185            return $this->_packageInfo['usesrole'];
87186        }
87187        return false;
87188    }
87189
87190    /**
87191     * Return the <usestask> tag contents, if any
87192     * @return array|false
87193     */
87194    function getUsestask()
87195    {
87196        if (isset($this->_packageInfo['usestask'])) {
87197            return $this->_packageInfo['usestask'];
87198        }
87199        return false;
87200    }
87201
87202    /**
87203     * This should only be used to retrieve filenames and install attributes
87204     */
87205    function getFilelist($preserve = false)
87206    {
87207        if (isset($this->_packageInfo['filelist']) && !$preserve) {
87208            return $this->_packageInfo['filelist'];
87209        }
87210        $this->flattenFilelist();
87211        if ($contents = $this->getContents()) {
87212            $ret = array();
87213            if (!isset($contents['dir'])) {
87214                return false;
87215            }
87216            if (!isset($contents['dir']['file'][0])) {
87217                $contents['dir']['file'] = array($contents['dir']['file']);
87218            }
87219            foreach ($contents['dir']['file'] as $file) {
87220                $name = $file['attribs']['name'];
87221                if (!$preserve) {
87222                    $file = $file['attribs'];
87223                }
87224                $ret[$name] = $file;
87225            }
87226            if (!$preserve) {
87227                $this->_packageInfo['filelist'] = $ret;
87228            }
87229            return $ret;
87230        }
87231        return false;
87232    }
87233
87234    /**
87235     * Return configure options array, if any
87236     *
87237     * @return array|false
87238     */
87239    function getConfigureOptions()
87240    {
87241        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
87242            return false;
87243        }
87244
87245        $releases = $this->getReleases();
87246        if (isset($releases[0])) {
87247            $releases = $releases[0];
87248        }
87249
87250        if (isset($releases['configureoption'])) {
87251            if (!isset($releases['configureoption'][0])) {
87252                $releases['configureoption'] = array($releases['configureoption']);
87253            }
87254
87255            for ($i = 0; $i < count($releases['configureoption']); $i++) {
87256                $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
87257            }
87258
87259            return $releases['configureoption'];
87260        }
87261
87262        return false;
87263    }
87264
87265    /**
87266     * This is only used at install-time, after all serialization
87267     * is over.
87268     */
87269    function resetFilelist()
87270    {
87271        $this->_packageInfo['filelist'] = array();
87272    }
87273
87274    /**
87275     * Retrieve a list of files that should be installed on this computer
87276     * @return array
87277     */
87278    function getInstallationFilelist($forfilecheck = false)
87279    {
87280        $contents = $this->getFilelist(true);
87281        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
87282            $base = $contents['dir']['attribs']['baseinstalldir'];
87283        }
87284        if (isset($this->_packageInfo['bundle'])) {
87285            return PEAR::raiseError(
87286                'Exception: bundles should be handled in download code only');
87287        }
87288        $release = $this->getReleases();
87289        if ($release) {
87290            if (!isset($release[0])) {
87291                if (!isset($release['installconditions']) && !isset($release['filelist'])) {
87292                    if ($forfilecheck) {
87293                        return $this->getFilelist();
87294                    }
87295                    return $contents;
87296                }
87297                $release = array($release);
87298            }
87299            $depchecker = &$this->getPEARDependency2($this->_config, array(),
87300                array('channel' => $this->getChannel(), 'package' => $this->getPackage()),
87301                PEAR_VALIDATE_INSTALLING);
87302            foreach ($release as $instance) {
87303                if (isset($instance['installconditions'])) {
87304                    $installconditions = $instance['installconditions'];
87305                    if (is_array($installconditions)) {
87306                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
87307                        foreach ($installconditions as $type => $conditions) {
87308                            if (!isset($conditions[0])) {
87309                                $conditions = array($conditions);
87310                            }
87311                            foreach ($conditions as $condition) {
87312                                $ret = $depchecker->{"validate{$type}Dependency"}($condition);
87313                                if (PEAR::isError($ret)) {
87314                                    PEAR::popErrorHandling();
87315                                    continue 3; // skip this release
87316                                }
87317                            }
87318                        }
87319                        PEAR::popErrorHandling();
87320                    }
87321                }
87322                // this is the release to use
87323                if (isset($instance['filelist'])) {
87324                    // ignore files
87325                    if (isset($instance['filelist']['ignore'])) {
87326                        $ignore = isset($instance['filelist']['ignore'][0]) ?
87327                            $instance['filelist']['ignore'] :
87328                            array($instance['filelist']['ignore']);
87329                        foreach ($ignore as $ig) {
87330                            unset ($contents[$ig['attribs']['name']]);
87331                        }
87332                    }
87333                    // install files as this name
87334                    if (isset($instance['filelist']['install'])) {
87335                        $installas = isset($instance['filelist']['install'][0]) ?
87336                            $instance['filelist']['install'] :
87337                            array($instance['filelist']['install']);
87338                        foreach ($installas as $as) {
87339                            $contents[$as['attribs']['name']]['attribs']['install-as'] =
87340                                $as['attribs']['as'];
87341                        }
87342                    }
87343                }
87344                if ($forfilecheck) {
87345                    foreach ($contents as $file => $attrs) {
87346                        $contents[$file] = $attrs['attribs'];
87347                    }
87348                }
87349                return $contents;
87350            }
87351        } else { // simple release - no installconditions or install-as
87352            if ($forfilecheck) {
87353                return $this->getFilelist();
87354            }
87355            return $contents;
87356        }
87357        // no releases matched
87358        return PEAR::raiseError('No releases in package.xml matched the existing operating ' .
87359            'system, extensions installed, or architecture, cannot install');
87360    }
87361
87362    /**
87363     * This is only used at install-time, after all serialization
87364     * is over.
87365     * @param string file name
87366     * @param string installed path
87367     */
87368    function setInstalledAs($file, $path)
87369    {
87370        if ($path) {
87371            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
87372        }
87373        unset($this->_packageInfo['filelist'][$file]['installed_as']);
87374    }
87375
87376    function getInstalledLocation($file)
87377    {
87378        if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
87379            return $this->_packageInfo['filelist'][$file]['installed_as'];
87380        }
87381        return false;
87382    }
87383
87384    /**
87385     * This is only used at install-time, after all serialization
87386     * is over.
87387     */
87388    function installedFile($file, $atts)
87389    {
87390        if (isset($this->_packageInfo['filelist'][$file])) {
87391            $this->_packageInfo['filelist'][$file] =
87392                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
87393        } else {
87394            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
87395        }
87396    }
87397
87398    /**
87399     * Retrieve the contents tag
87400     */
87401    function getContents()
87402    {
87403        if (isset($this->_packageInfo['contents'])) {
87404            return $this->_packageInfo['contents'];
87405        }
87406        return false;
87407    }
87408
87409    /**
87410     * @param string full path to file
87411     * @param string attribute name
87412     * @param string attribute value
87413     * @param int risky but fast - use this to choose a file based on its position in the list
87414     *            of files.  Index is zero-based like PHP arrays.
87415     * @return bool success of operation
87416     */
87417    function setFileAttribute($filename, $attr, $value, $index = false)
87418    {
87419        $this->_isValid = 0;
87420        if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
87421            $this->_filesValid = false;
87422        }
87423        if ($index !== false &&
87424              isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
87425            $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
87426            return true;
87427        }
87428        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
87429            return false;
87430        }
87431        $files = $this->_packageInfo['contents']['dir']['file'];
87432        if (!isset($files[0])) {
87433            $files = array($files);
87434            $ind = false;
87435        } else {
87436            $ind = true;
87437        }
87438        foreach ($files as $i => $file) {
87439            if (isset($file['attribs'])) {
87440                if ($file['attribs']['name'] == $filename) {
87441                    if ($ind) {
87442                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
87443                    } else {
87444                        $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
87445                    }
87446                    return true;
87447                }
87448            }
87449        }
87450        return false;
87451    }
87452
87453    function setDirtree($path)
87454    {
87455        if (!isset($this->_packageInfo['dirtree'])) {
87456            $this->_packageInfo['dirtree'] = array();
87457        }
87458        $this->_packageInfo['dirtree'][$path] = true;
87459    }
87460
87461    function getDirtree()
87462    {
87463        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
87464            return $this->_packageInfo['dirtree'];
87465        }
87466        return false;
87467    }
87468
87469    function resetDirtree()
87470    {
87471        unset($this->_packageInfo['dirtree']);
87472    }
87473
87474    /**
87475     * Determines whether this package claims it is compatible with the version of
87476     * the package that has a recommended version dependency
87477     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package
87478     * @return boolean
87479     */
87480    function isCompatible($pf)
87481    {
87482        if (!isset($this->_packageInfo['compatible'])) {
87483            return false;
87484        }
87485        if (!isset($this->_packageInfo['channel'])) {
87486            return false;
87487        }
87488        $me = $pf->getVersion();
87489        $compatible = $this->_packageInfo['compatible'];
87490        if (!isset($compatible[0])) {
87491            $compatible = array($compatible);
87492        }
87493        $found = false;
87494        foreach ($compatible as $info) {
87495            if (strtolower($info['name']) == strtolower($pf->getPackage())) {
87496                if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
87497                    $found = true;
87498                    break;
87499                }
87500            }
87501        }
87502        if (!$found) {
87503            return false;
87504        }
87505        if (isset($info['exclude'])) {
87506            if (!isset($info['exclude'][0])) {
87507                $info['exclude'] = array($info['exclude']);
87508            }
87509            foreach ($info['exclude'] as $exclude) {
87510                if (version_compare($me, $exclude, '==')) {
87511                    return false;
87512                }
87513            }
87514        }
87515        if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
87516            return true;
87517        }
87518        return false;
87519    }
87520
87521    /**
87522     * @return array|false
87523     */
87524    function getCompatible()
87525    {
87526        if (isset($this->_packageInfo['compatible'])) {
87527            return $this->_packageInfo['compatible'];
87528        }
87529        return false;
87530    }
87531
87532    function getDependencies()
87533    {
87534        if (isset($this->_packageInfo['dependencies'])) {
87535            return $this->_packageInfo['dependencies'];
87536        }
87537        return false;
87538    }
87539
87540    function isSubpackageOf($p)
87541    {
87542        return $p->isSubpackage($this);
87543    }
87544
87545    /**
87546     * Determines whether the passed in package is a subpackage of this package.
87547     *
87548     * No version checking is done, only name verification.
87549     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
87550     * @return bool
87551     */
87552    function isSubpackage($p)
87553    {
87554        $sub = array();
87555        if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
87556            $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
87557            if (!isset($sub[0])) {
87558                $sub = array($sub);
87559            }
87560        }
87561        if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
87562            $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
87563            if (!isset($sub1[0])) {
87564                $sub1 = array($sub1);
87565            }
87566            $sub = array_merge($sub, $sub1);
87567        }
87568        if (isset($this->_packageInfo['dependencies']['group'])) {
87569            $group = $this->_packageInfo['dependencies']['group'];
87570            if (!isset($group[0])) {
87571                $group = array($group);
87572            }
87573            foreach ($group as $deps) {
87574                if (isset($deps['subpackage'])) {
87575                    $sub2 = $deps['subpackage'];
87576                    if (!isset($sub2[0])) {
87577                        $sub2 = array($sub2);
87578                    }
87579                    $sub = array_merge($sub, $sub2);
87580                }
87581            }
87582        }
87583        foreach ($sub as $dep) {
87584            if (strtolower($dep['name']) == strtolower($p->getPackage())) {
87585                if (isset($dep['channel'])) {
87586                    if (strtolower($dep['channel']) == strtolower($p->getChannel())) {
87587                        return true;
87588                    }
87589                } else {
87590                    if ($dep['uri'] == $p->getURI()) {
87591                        return true;
87592                    }
87593                }
87594            }
87595        }
87596        return false;
87597    }
87598
87599    function dependsOn($package, $channel)
87600    {
87601        if (!($deps = $this->getDependencies())) {
87602            return false;
87603        }
87604        foreach (array('package', 'subpackage') as $type) {
87605            foreach (array('required', 'optional') as $needed) {
87606                if (isset($deps[$needed][$type])) {
87607                    if (!isset($deps[$needed][$type][0])) {
87608                        $deps[$needed][$type] = array($deps[$needed][$type]);
87609                    }
87610                    foreach ($deps[$needed][$type] as $dep) {
87611                        $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
87612                        if (strtolower($dep['name']) == strtolower($package) &&
87613                              $depchannel == $channel) {
87614                            return true;
87615                        }
87616                    }
87617                }
87618            }
87619            if (isset($deps['group'])) {
87620                if (!isset($deps['group'][0])) {
87621                    $dep['group'] = array($deps['group']);
87622                }
87623                foreach ($deps['group'] as $group) {
87624                    if (isset($group[$type])) {
87625                        if (!is_array($group[$type])) {
87626                            $group[$type] = array($group[$type]);
87627                        }
87628                        foreach ($group[$type] as $dep) {
87629                            $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
87630                            if (strtolower($dep['name']) == strtolower($package) &&
87631                                  $depchannel == $channel) {
87632                                return true;
87633                            }
87634                        }
87635                    }
87636                }
87637            }
87638        }
87639        return false;
87640    }
87641
87642    /**
87643     * Get the contents of a dependency group
87644     * @param string
87645     * @return array|false
87646     */
87647    function getDependencyGroup($name)
87648    {
87649        $name = strtolower($name);
87650        if (!isset($this->_packageInfo['dependencies']['group'])) {
87651            return false;
87652        }
87653        $groups = $this->_packageInfo['dependencies']['group'];
87654        if (!isset($groups[0])) {
87655            $groups = array($groups);
87656        }
87657        foreach ($groups as $group) {
87658            if (strtolower($group['attribs']['name']) == $name) {
87659                return $group;
87660            }
87661        }
87662        return false;
87663    }
87664
87665    /**
87666     * Retrieve a partial package.xml 1.0 representation of dependencies
87667     *
87668     * a very limited representation of dependencies is returned by this method.
87669     * The <exclude> tag for excluding certain versions of a dependency is
87670     * completely ignored.  In addition, dependency groups are ignored, with the
87671     * assumption that all dependencies in dependency groups are also listed in
87672     * the optional group that work with all dependency groups
87673     * @param boolean return package.xml 2.0 <dependencies> tag
87674     * @return array|false
87675     */
87676    function getDeps($raw = false, $nopearinstaller = false)
87677    {
87678        if (isset($this->_packageInfo['dependencies'])) {
87679            if ($raw) {
87680                return $this->_packageInfo['dependencies'];
87681            }
87682            $ret = array();
87683            $map = array(
87684                'php' => 'php',
87685                'package' => 'pkg',
87686                'subpackage' => 'pkg',
87687                'extension' => 'ext',
87688                'os' => 'os',
87689                'pearinstaller' => 'pkg',
87690                );
87691            foreach (array('required', 'optional') as $type) {
87692                $optional = ($type == 'optional') ? 'yes' : 'no';
87693                if (!isset($this->_packageInfo['dependencies'][$type])
87694                    || empty($this->_packageInfo['dependencies'][$type])) {
87695                    continue;
87696                }
87697                foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
87698                    if ($dtype == 'pearinstaller' && $nopearinstaller) {
87699                        continue;
87700                    }
87701                    if (!isset($deps[0])) {
87702                        $deps = array($deps);
87703                    }
87704                    foreach ($deps as $dep) {
87705                        if (!isset($map[$dtype])) {
87706                            // no support for arch type
87707                            continue;
87708                        }
87709                        if ($dtype == 'pearinstaller') {
87710                            $dep['name'] = 'PEAR';
87711                            $dep['channel'] = 'pear.php.net';
87712                        }
87713                        $s = array('type' => $map[$dtype]);
87714                        if (isset($dep['channel'])) {
87715                            $s['channel'] = $dep['channel'];
87716                        }
87717                        if (isset($dep['uri'])) {
87718                            $s['uri'] = $dep['uri'];
87719                        }
87720                        if (isset($dep['name'])) {
87721                            $s['name'] = $dep['name'];
87722                        }
87723                        if (isset($dep['conflicts'])) {
87724                            $s['rel'] = 'not';
87725                        } else {
87726                            if (!isset($dep['min']) &&
87727                                  !isset($dep['max'])) {
87728                                $s['rel'] = 'has';
87729                                $s['optional'] = $optional;
87730                            } elseif (isset($dep['min']) &&
87731                                  isset($dep['max'])) {
87732                                $s['rel'] = 'ge';
87733                                $s1 = $s;
87734                                $s1['rel'] = 'le';
87735                                $s['version'] = $dep['min'];
87736                                $s1['version'] = $dep['max'];
87737                                if (isset($dep['channel'])) {
87738                                    $s1['channel'] = $dep['channel'];
87739                                }
87740                                if ($dtype != 'php') {
87741                                    $s['name'] = $dep['name'];
87742                                    $s1['name'] = $dep['name'];
87743                                }
87744                                $s['optional'] = $optional;
87745                                $s1['optional'] = $optional;
87746                                $ret[] = $s1;
87747                            } elseif (isset($dep['min'])) {
87748                                if (isset($dep['exclude']) &&
87749                                      $dep['exclude'] == $dep['min']) {
87750                                    $s['rel'] = 'gt';
87751                                } else {
87752                                    $s['rel'] = 'ge';
87753                                }
87754                                $s['version'] = $dep['min'];
87755                                $s['optional'] = $optional;
87756                                if ($dtype != 'php') {
87757                                    $s['name'] = $dep['name'];
87758                                }
87759                            } elseif (isset($dep['max'])) {
87760                                if (isset($dep['exclude']) &&
87761                                      $dep['exclude'] == $dep['max']) {
87762                                    $s['rel'] = 'lt';
87763                                } else {
87764                                    $s['rel'] = 'le';
87765                                }
87766                                $s['version'] = $dep['max'];
87767                                $s['optional'] = $optional;
87768                                if ($dtype != 'php') {
87769                                    $s['name'] = $dep['name'];
87770                                }
87771                            }
87772                        }
87773                        $ret[] = $s;
87774                    }
87775                }
87776            }
87777            if (count($ret)) {
87778                return $ret;
87779            }
87780        }
87781        return false;
87782    }
87783
87784    /**
87785     * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
87786     */
87787    function getPackageType()
87788    {
87789        if (isset($this->_packageInfo['phprelease'])) {
87790            return 'php';
87791        }
87792        if (isset($this->_packageInfo['extsrcrelease'])) {
87793            return 'extsrc';
87794        }
87795        if (isset($this->_packageInfo['extbinrelease'])) {
87796            return 'extbin';
87797        }
87798        if (isset($this->_packageInfo['zendextsrcrelease'])) {
87799            return 'zendextsrc';
87800        }
87801        if (isset($this->_packageInfo['zendextbinrelease'])) {
87802            return 'zendextbin';
87803        }
87804        if (isset($this->_packageInfo['bundle'])) {
87805            return 'bundle';
87806        }
87807        return false;
87808    }
87809
87810    /**
87811     * @return array|false
87812     */
87813    function getReleases()
87814    {
87815        $type = $this->getPackageType();
87816        if ($type != 'bundle') {
87817            $type .= 'release';
87818        }
87819        if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
87820            return $this->_packageInfo[$type];
87821        }
87822        return false;
87823    }
87824
87825    /**
87826     * @return array
87827     */
87828    function getChangelog()
87829    {
87830        if (isset($this->_packageInfo['changelog'])) {
87831            return $this->_packageInfo['changelog'];
87832        }
87833        return false;
87834    }
87835
87836    function hasDeps()
87837    {
87838        return isset($this->_packageInfo['dependencies']);
87839    }
87840
87841    function getPackagexmlVersion()
87842    {
87843        if (isset($this->_packageInfo['zendextsrcrelease'])) {
87844            return '2.1';
87845        }
87846        if (isset($this->_packageInfo['zendextbinrelease'])) {
87847            return '2.1';
87848        }
87849        return '2.0';
87850    }
87851
87852    /**
87853     * @return array|false
87854     */
87855    function getSourcePackage()
87856    {
87857        if (isset($this->_packageInfo['extbinrelease']) ||
87858              isset($this->_packageInfo['zendextbinrelease'])) {
87859            return array('channel' => $this->_packageInfo['srcchannel'],
87860                         'package' => $this->_packageInfo['srcpackage']);
87861        }
87862        return false;
87863    }
87864
87865    function getBundledPackages()
87866    {
87867        if (isset($this->_packageInfo['bundle'])) {
87868            return $this->_packageInfo['contents']['bundledpackage'];
87869        }
87870        return false;
87871    }
87872
87873    function getLastModified()
87874    {
87875        if (isset($this->_packageInfo['_lastmodified'])) {
87876            return $this->_packageInfo['_lastmodified'];
87877        }
87878        return false;
87879    }
87880
87881    /**
87882     * Get the contents of a file listed within the package.xml
87883     * @param string
87884     * @return string
87885     */
87886    function getFileContents($file)
87887    {
87888        if ($this->_archiveFile == $this->_packageFile) { // unpacked
87889            $dir = dirname($this->_packageFile);
87890            $file = $dir . DIRECTORY_SEPARATOR . $file;
87891            $file = str_replace(array('/', '\\'),
87892                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
87893            if (file_exists($file) && is_readable($file)) {
87894                return implode('', file($file));
87895            }
87896        } else { // tgz
87897            $tar = &new Archive_Tar($this->_archiveFile);
87898            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
87899            if ($file != 'package.xml' && $file != 'package2.xml') {
87900                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
87901            }
87902            $file = $tar->extractInString($file);
87903            $tar->popErrorHandling();
87904            if (PEAR::isError($file)) {
87905                return PEAR::raiseError("Cannot locate file '$file' in archive");
87906            }
87907            return $file;
87908        }
87909    }
87910
87911    function &getRW()
87912    {
87913        if (!class_exists('PEAR_PackageFile_v2_rw')) {
87914            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2/rw.php';
87915        }
87916        $a = new PEAR_PackageFile_v2_rw;
87917        foreach (get_object_vars($this) as $name => $unused) {
87918            if (!isset($this->$name)) {
87919                continue;
87920            }
87921            if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
87922                  $name == '_stack') {
87923                $a->$name = &$this->$name;
87924            } else {
87925                $a->$name = $this->$name;
87926            }
87927        }
87928        return $a;
87929    }
87930
87931    function &getDefaultGenerator()
87932    {
87933        if (!class_exists('PEAR_PackageFile_Generator_v2')) {
87934            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/Generator/v2.php';
87935        }
87936        $a = &new PEAR_PackageFile_Generator_v2($this);
87937        return $a;
87938    }
87939
87940    function analyzeSourceCode($file, $string = false)
87941    {
87942        if (!isset($this->_v2Validator) ||
87943              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
87944            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
87945                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2/Validator.php';
87946            }
87947            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
87948        }
87949        return $this->_v2Validator->analyzeSourceCode($file, $string);
87950    }
87951
87952    function validate($state = PEAR_VALIDATE_NORMAL)
87953    {
87954        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
87955            return false;
87956        }
87957        if (!isset($this->_v2Validator) ||
87958              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
87959            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
87960                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2/Validator.php';
87961            }
87962            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
87963        }
87964        if (isset($this->_packageInfo['xsdversion'])) {
87965            unset($this->_packageInfo['xsdversion']);
87966        }
87967        return $this->_v2Validator->validate($this, $state);
87968    }
87969
87970    function getTasksNs()
87971    {
87972        if (!isset($this->_tasksNs)) {
87973            if (isset($this->_packageInfo['attribs'])) {
87974                foreach ($this->_packageInfo['attribs'] as $name => $value) {
87975                    if ($value == 'http://pear.php.net/dtd/tasks-1.0') {
87976                        $this->_tasksNs = str_replace('xmlns:', '', $name);
87977                        break;
87978                    }
87979                }
87980            }
87981        }
87982        return $this->_tasksNs;
87983    }
87984
87985    /**
87986     * Determine whether a task name is a valid task.  Custom tasks may be defined
87987     * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task>
87988     *
87989     * Note that this method will auto-load the task class file and test for the existence
87990     * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class
87991     * PEAR_Task_mycustom_task
87992     * @param string
87993     * @return boolean
87994     */
87995    function getTask($task)
87996    {
87997        $this->getTasksNs();
87998        // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace
87999        $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task);
88000        $taskfile = str_replace(' ', '/', ucwords($task));
88001        $task = str_replace(array(' ', '/'), '_', ucwords($task));
88002        if (class_exists("PEAR_Task_$task")) {
88003            return "PEAR_Task_$task";
88004        }
88005        $fp = @fopen("phar://install-pear-nozlib.phar/PEAR/Task/$taskfile.php", 'r', true);
88006        if ($fp) {
88007            fclose($fp);
88008            require_once  'phar://install-pear-nozlib.phar/' . "PEAR/Task/$taskfile.php";
88009            return "PEAR_Task_$task";
88010        }
88011        return false;
88012    }
88013
88014    /**
88015     * Key-friendly array_splice
88016     * @param tagname to splice a value in before
88017     * @param mixed the value to splice in
88018     * @param string the new tag name
88019     */
88020    function _ksplice($array, $key, $value, $newkey)
88021    {
88022        $offset = array_search($key, array_keys($array));
88023        $after = array_slice($array, $offset);
88024        $before = array_slice($array, 0, $offset);
88025        $before[$newkey] = $value;
88026        return array_merge($before, $after);
88027    }
88028
88029    /**
88030     * @param array a list of possible keys, in the order they may occur
88031     * @param mixed contents of the new package.xml tag
88032     * @param string tag name
88033     * @access private
88034     */
88035    function _insertBefore($array, $keys, $contents, $newkey)
88036    {
88037        foreach ($keys as $key) {
88038            if (isset($array[$key])) {
88039                return $array = $this->_ksplice($array, $key, $contents, $newkey);
88040            }
88041        }
88042        $array[$newkey] = $contents;
88043        return $array;
88044    }
88045
88046    /**
88047     * @param subsection of {@link $_packageInfo}
88048     * @param array|string tag contents
88049     * @param array format:
88050     * <pre>
88051     * array(
88052     *   tagname => array(list of tag names that follow this one),
88053     *   childtagname => array(list of child tag names that follow this one),
88054     * )
88055     * </pre>
88056     *
88057     * This allows construction of nested tags
88058     * @access private
88059     */
88060    function _mergeTag($manip, $contents, $order)
88061    {
88062        if (count($order)) {
88063            foreach ($order as $tag => $curorder) {
88064                if (!isset($manip[$tag])) {
88065                    // ensure that the tag is set up
88066                    $manip = $this->_insertBefore($manip, $curorder, array(), $tag);
88067                }
88068                if (count($order) > 1) {
88069                    $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
88070                    return $manip;
88071                }
88072            }
88073        } else {
88074            return $manip;
88075        }
88076        if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
88077            $manip[$tag][] = $contents;
88078        } else {
88079            if (!count($manip[$tag])) {
88080                $manip[$tag] = $contents;
88081            } else {
88082                $manip[$tag] = array($manip[$tag]);
88083                $manip[$tag][] = $contents;
88084            }
88085        }
88086        return $manip;
88087    }
88088}
88089?>
88090<?php
88091/**
88092 * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
88093 *
88094 * PHP versions 4 and 5
88095 *
88096 * @category   pear
88097 * @package    PEAR
88098 * @author     Greg Beaver <cellog@php.net>
88099 * @copyright  1997-2009 The Authors
88100 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
88101 * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
88102 * @link       http://pear.php.net/package/PEAR
88103 * @since      File available since Release 1.4.0a8
88104 */
88105/**
88106 * For base class
88107 */
88108require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
88109/**
88110 * @category   pear
88111 * @package    PEAR
88112 * @author     Greg Beaver <cellog@php.net>
88113 * @copyright  1997-2009 The Authors
88114 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
88115 * @version    Release: 1.9.4
88116 * @link       http://pear.php.net/package/PEAR
88117 * @since      Class available since Release 1.4.0a8
88118 */
88119class PEAR_PackageFile_v2_rw extends PEAR_PackageFile_v2
88120{
88121    /**
88122     * @param string Extension name
88123     * @return bool success of operation
88124     */
88125    function setProvidesExtension($extension)
88126    {
88127        if (in_array($this->getPackageType(),
88128              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
88129            if (!isset($this->_packageInfo['providesextension'])) {
88130                // ensure that the channel tag is set up in the right location
88131                $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
88132                    array('usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
88133                    'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
88134                    'bundle', 'changelog'),
88135                    $extension, 'providesextension');
88136            }
88137            $this->_packageInfo['providesextension'] = $extension;
88138            return true;
88139        }
88140        return false;
88141    }
88142
88143    function setPackage($package)
88144    {
88145        $this->_isValid = 0;
88146        if (!isset($this->_packageInfo['attribs'])) {
88147            $this->_packageInfo = array_merge(array('attribs' => array(
88148                                 'version' => '2.0',
88149                                 'xmlns' => 'http://pear.php.net/dtd/package-2.0',
88150                                 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
88151                                 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
88152                                 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
88153    http://pear.php.net/dtd/tasks-1.0.xsd
88154    http://pear.php.net/dtd/package-2.0
88155    http://pear.php.net/dtd/package-2.0.xsd',
88156                             )), $this->_packageInfo);
88157        }
88158        if (!isset($this->_packageInfo['name'])) {
88159            return $this->_packageInfo = array_merge(array('name' => $package),
88160                $this->_packageInfo);
88161        }
88162        $this->_packageInfo['name'] = $package;
88163    }
88164
88165    /**
88166     * set this as a package.xml version 2.1
88167     * @access private
88168     */
88169    function _setPackageVersion2_1()
88170    {
88171        $info = array(
88172                                 'version' => '2.1',
88173                                 'xmlns' => 'http://pear.php.net/dtd/package-2.1',
88174                                 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
88175                                 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
88176                                 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
88177    http://pear.php.net/dtd/tasks-1.0.xsd
88178    http://pear.php.net/dtd/package-2.1
88179    http://pear.php.net/dtd/package-2.1.xsd',
88180                             );
88181        if (!isset($this->_packageInfo['attribs'])) {
88182            $this->_packageInfo = array_merge(array('attribs' => $info), $this->_packageInfo);
88183        } else {
88184            $this->_packageInfo['attribs'] = $info;
88185        }
88186    }
88187
88188    function setUri($uri)
88189    {
88190        unset($this->_packageInfo['channel']);
88191        $this->_isValid = 0;
88192        if (!isset($this->_packageInfo['uri'])) {
88193            // ensure that the uri tag is set up in the right location
88194            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
88195                array('extends', 'summary', 'description', 'lead',
88196                'developer', 'contributor', 'helper', 'date', 'time', 'version',
88197                'stability', 'license', 'notes', 'contents', 'compatible',
88198                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
88199                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
88200                'extbinrelease', 'bundle', 'changelog'), $uri, 'uri');
88201        }
88202        $this->_packageInfo['uri'] = $uri;
88203    }
88204
88205    function setChannel($channel)
88206    {
88207        unset($this->_packageInfo['uri']);
88208        $this->_isValid = 0;
88209        if (!isset($this->_packageInfo['channel'])) {
88210            // ensure that the channel tag is set up in the right location
88211            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
88212                array('extends', 'summary', 'description', 'lead',
88213                'developer', 'contributor', 'helper', 'date', 'time', 'version',
88214                'stability', 'license', 'notes', 'contents', 'compatible',
88215                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
88216                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
88217                'extbinrelease', 'bundle', 'changelog'), $channel, 'channel');
88218        }
88219        $this->_packageInfo['channel'] = $channel;
88220    }
88221
88222    function setExtends($extends)
88223    {
88224        $this->_isValid = 0;
88225        if (!isset($this->_packageInfo['extends'])) {
88226            // ensure that the extends tag is set up in the right location
88227            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
88228                array('summary', 'description', 'lead',
88229                'developer', 'contributor', 'helper', 'date', 'time', 'version',
88230                'stability', 'license', 'notes', 'contents', 'compatible',
88231                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
88232                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
88233                'extbinrelease', 'bundle', 'changelog'), $extends, 'extends');
88234        }
88235        $this->_packageInfo['extends'] = $extends;
88236    }
88237
88238    function setSummary($summary)
88239    {
88240        $this->_isValid = 0;
88241        if (!isset($this->_packageInfo['summary'])) {
88242            // ensure that the summary tag is set up in the right location
88243            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
88244                array('description', 'lead',
88245                'developer', 'contributor', 'helper', 'date', 'time', 'version',
88246                'stability', 'license', 'notes', 'contents', 'compatible',
88247                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
88248                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
88249                'extbinrelease', 'bundle', 'changelog'), $summary, 'summary');
88250        }
88251        $this->_packageInfo['summary'] = $summary;
88252    }
88253
88254    function setDescription($desc)
88255    {
88256        $this->_isValid = 0;
88257        if (!isset($this->_packageInfo['description'])) {
88258            // ensure that the description tag is set up in the right location
88259            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
88260                array('lead',
88261                'developer', 'contributor', 'helper', 'date', 'time', 'version',
88262                'stability', 'license', 'notes', 'contents', 'compatible',
88263                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
88264                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
88265                'extbinrelease', 'bundle', 'changelog'), $desc, 'description');
88266        }
88267        $this->_packageInfo['description'] = $desc;
88268    }
88269
88270    /**
88271     * Adds a new maintainer - no checking of duplicates is performed, use
88272     * updatemaintainer for that purpose.
88273     */
88274    function addMaintainer($role, $handle, $name, $email, $active = 'yes')
88275    {
88276        if (!in_array($role, array('lead', 'developer', 'contributor', 'helper'))) {
88277            return false;
88278        }
88279        if (isset($this->_packageInfo[$role])) {
88280            if (!isset($this->_packageInfo[$role][0])) {
88281                $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
88282            }
88283            $this->_packageInfo[$role][] =
88284                array(
88285                    'name' => $name,
88286                    'user' => $handle,
88287                    'email' => $email,
88288                    'active' => $active,
88289                );
88290        } else {
88291            $testarr = array('lead',
88292                    'developer', 'contributor', 'helper', 'date', 'time', 'version',
88293                    'stability', 'license', 'notes', 'contents', 'compatible',
88294                    'dependencies', 'providesextension', 'usesrole', 'usestask',
88295                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
88296                    'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog');
88297            foreach (array('lead', 'developer', 'contributor', 'helper') as $testrole) {
88298                array_shift($testarr);
88299                if ($role == $testrole) {
88300                    break;
88301                }
88302            }
88303            if (!isset($this->_packageInfo[$role])) {
88304                // ensure that the extends tag is set up in the right location
88305                $this->_packageInfo = $this->_insertBefore($this->_packageInfo, $testarr,
88306                    array(), $role);
88307            }
88308            $this->_packageInfo[$role] =
88309                array(
88310                    'name' => $name,
88311                    'user' => $handle,
88312                    'email' => $email,
88313                    'active' => $active,
88314                );
88315        }
88316        $this->_isValid = 0;
88317    }
88318
88319    function updateMaintainer($newrole, $handle, $name, $email, $active = 'yes')
88320    {
88321        $found = false;
88322        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
88323            if (!isset($this->_packageInfo[$role])) {
88324                continue;
88325            }
88326            $info = $this->_packageInfo[$role];
88327            if (!isset($info[0])) {
88328                if ($info['user'] == $handle) {
88329                    $found = true;
88330                    break;
88331                }
88332            }
88333            foreach ($info as $i => $maintainer) {
88334                if ($maintainer['user'] == $handle) {
88335                    $found = $i;
88336                    break 2;
88337                }
88338            }
88339        }
88340        if ($found === false) {
88341            return $this->addMaintainer($newrole, $handle, $name, $email, $active);
88342        }
88343        if ($found !== false) {
88344            if ($found === true) {
88345                unset($this->_packageInfo[$role]);
88346            } else {
88347                unset($this->_packageInfo[$role][$found]);
88348                $this->_packageInfo[$role] = array_values($this->_packageInfo[$role]);
88349            }
88350        }
88351        $this->addMaintainer($newrole, $handle, $name, $email, $active);
88352        $this->_isValid = 0;
88353    }
88354
88355    function deleteMaintainer($handle)
88356    {
88357        $found = false;
88358        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
88359            if (!isset($this->_packageInfo[$role])) {
88360                continue;
88361            }
88362            if (!isset($this->_packageInfo[$role][0])) {
88363                $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
88364            }
88365            foreach ($this->_packageInfo[$role] as $i => $maintainer) {
88366                if ($maintainer['user'] == $handle) {
88367                    $found = $i;
88368                    break;
88369                }
88370            }
88371            if ($found !== false) {
88372                unset($this->_packageInfo[$role][$found]);
88373                if (!count($this->_packageInfo[$role]) && $role == 'lead') {
88374                    $this->_isValid = 0;
88375                }
88376                if (!count($this->_packageInfo[$role])) {
88377                    unset($this->_packageInfo[$role]);
88378                    return true;
88379                }
88380                $this->_packageInfo[$role] =
88381                    array_values($this->_packageInfo[$role]);
88382                if (count($this->_packageInfo[$role]) == 1) {
88383                    $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
88384                }
88385                return true;
88386            }
88387            if (count($this->_packageInfo[$role]) == 1) {
88388                $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
88389            }
88390        }
88391        return false;
88392    }
88393
88394    function setReleaseVersion($version)
88395    {
88396        if (isset($this->_packageInfo['version']) &&
88397              isset($this->_packageInfo['version']['release'])) {
88398            unset($this->_packageInfo['version']['release']);
88399        }
88400        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
88401            'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
88402                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
88403                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
88404                'extbinrelease', 'bundle', 'changelog'),
88405            'release' => array('api')));
88406        $this->_isValid = 0;
88407    }
88408
88409    function setAPIVersion($version)
88410    {
88411        if (isset($this->_packageInfo['version']) &&
88412              isset($this->_packageInfo['version']['api'])) {
88413            unset($this->_packageInfo['version']['api']);
88414        }
88415        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
88416            'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
88417                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
88418                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
88419                'extbinrelease', 'bundle', 'changelog'),
88420            'api' => array()));
88421        $this->_isValid = 0;
88422    }
88423
88424    /**
88425     * snapshot|devel|alpha|beta|stable
88426     */
88427    function setReleaseStability($state)
88428    {
88429        if (isset($this->_packageInfo['stability']) &&
88430              isset($this->_packageInfo['stability']['release'])) {
88431            unset($this->_packageInfo['stability']['release']);
88432        }
88433        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
88434            'stability' => array('license', 'notes', 'contents', 'compatible',
88435                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
88436                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
88437                'extbinrelease', 'bundle', 'changelog'),
88438            'release' => array('api')));
88439        $this->_isValid = 0;
88440    }
88441
88442    /**
88443     * @param devel|alpha|beta|stable
88444     */
88445    function setAPIStability($state)
88446    {
88447        if (isset($this->_packageInfo['stability']) &&
88448              isset($this->_packageInfo['stability']['api'])) {
88449            unset($this->_packageInfo['stability']['api']);
88450        }
88451        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
88452            'stability' => array('license', 'notes', 'contents', 'compatible',
88453                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
88454                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
88455                'extbinrelease', 'bundle', 'changelog'),
88456            'api' => array()));
88457        $this->_isValid = 0;
88458    }
88459
88460    function setLicense($license, $uri = false, $filesource = false)
88461    {
88462        if (!isset($this->_packageInfo['license'])) {
88463            // ensure that the license tag is set up in the right location
88464            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
88465                array('notes', 'contents', 'compatible',
88466                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
88467                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
88468                'extbinrelease', 'bundle', 'changelog'), 0, 'license');
88469        }
88470        if ($uri || $filesource) {
88471            $attribs = array();
88472            if ($uri) {
88473                $attribs['uri'] = $uri;
88474            }
88475            $uri = true; // for test below
88476            if ($filesource) {
88477                $attribs['filesource'] = $filesource;
88478            }
88479        }
88480        $license = $uri ? array('attribs' => $attribs, '_content' => $license) : $license;
88481        $this->_packageInfo['license'] = $license;
88482        $this->_isValid = 0;
88483    }
88484
88485    function setNotes($notes)
88486    {
88487        $this->_isValid = 0;
88488        if (!isset($this->_packageInfo['notes'])) {
88489            // ensure that the notes tag is set up in the right location
88490            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
88491                array('contents', 'compatible',
88492                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
88493                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
88494                'extbinrelease', 'bundle', 'changelog'), $notes, 'notes');
88495        }
88496        $this->_packageInfo['notes'] = $notes;
88497    }
88498
88499    /**
88500     * This is only used at install-time, after all serialization
88501     * is over.
88502     * @param string file name
88503     * @param string installed path
88504     */
88505    function setInstalledAs($file, $path)
88506    {
88507        if ($path) {
88508            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
88509        }
88510        unset($this->_packageInfo['filelist'][$file]['installed_as']);
88511    }
88512
88513    /**
88514     * This is only used at install-time, after all serialization
88515     * is over.
88516     */
88517    function installedFile($file, $atts)
88518    {
88519        if (isset($this->_packageInfo['filelist'][$file])) {
88520            $this->_packageInfo['filelist'][$file] =
88521                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
88522        } else {
88523            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
88524        }
88525    }
88526
88527    /**
88528     * Reset the listing of package contents
88529     * @param string base installation dir for the whole package, if any
88530     */
88531    function clearContents($baseinstall = false)
88532    {
88533        $this->_filesValid = false;
88534        $this->_isValid = 0;
88535        if (!isset($this->_packageInfo['contents'])) {
88536            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
88537                array('compatible',
88538                    'dependencies', 'providesextension', 'usesrole', 'usestask',
88539                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
88540                    'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
88541                    'bundle', 'changelog'), array(), 'contents');
88542        }
88543        if ($this->getPackageType() != 'bundle') {
88544            $this->_packageInfo['contents'] =
88545                array('dir' => array('attribs' => array('name' => '/')));
88546            if ($baseinstall) {
88547                $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'] = $baseinstall;
88548            }
88549        } else {
88550            $this->_packageInfo['contents'] = array('bundledpackage' => array());
88551        }
88552    }
88553
88554    /**
88555     * @param string relative path of the bundled package.
88556     */
88557    function addBundledPackage($path)
88558    {
88559        if ($this->getPackageType() != 'bundle') {
88560            return false;
88561        }
88562        $this->_filesValid = false;
88563        $this->_isValid = 0;
88564        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $path, array(
88565                'contents' => array('compatible', 'dependencies', 'providesextension',
88566                'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
88567                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
88568                'bundle', 'changelog'),
88569                'bundledpackage' => array()));
88570    }
88571
88572    /**
88573     * @param string file name
88574     * @param PEAR_Task_Common a read/write task
88575     */
88576    function addTaskToFile($filename, $task)
88577    {
88578        if (!method_exists($task, 'getXml')) {
88579            return false;
88580        }
88581        if (!method_exists($task, 'getName')) {
88582            return false;
88583        }
88584        if (!method_exists($task, 'validate')) {
88585            return false;
88586        }
88587        if (!$task->validate()) {
88588            return false;
88589        }
88590        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
88591            return false;
88592        }
88593        $this->getTasksNs(); // discover the tasks namespace if not done already
88594        $files = $this->_packageInfo['contents']['dir']['file'];
88595        if (!isset($files[0])) {
88596            $files = array($files);
88597            $ind = false;
88598        } else {
88599            $ind = true;
88600        }
88601        foreach ($files as $i => $file) {
88602            if (isset($file['attribs'])) {
88603                if ($file['attribs']['name'] == $filename) {
88604                    if ($ind) {
88605                        $t = isset($this->_packageInfo['contents']['dir']['file'][$i]
88606                              ['attribs'][$this->_tasksNs .
88607                              ':' . $task->getName()]) ?
88608                              $this->_packageInfo['contents']['dir']['file'][$i]
88609                              ['attribs'][$this->_tasksNs .
88610                              ':' . $task->getName()] : false;
88611                        if ($t && !isset($t[0])) {
88612                            $this->_packageInfo['contents']['dir']['file'][$i]
88613                                [$this->_tasksNs . ':' . $task->getName()] = array($t);
88614                        }
88615                        $this->_packageInfo['contents']['dir']['file'][$i][$this->_tasksNs .
88616                            ':' . $task->getName()][] = $task->getXml();
88617                    } else {
88618                        $t = isset($this->_packageInfo['contents']['dir']['file']
88619                              ['attribs'][$this->_tasksNs .
88620                              ':' . $task->getName()]) ? $this->_packageInfo['contents']['dir']['file']
88621                              ['attribs'][$this->_tasksNs .
88622                              ':' . $task->getName()] : false;
88623                        if ($t && !isset($t[0])) {
88624                            $this->_packageInfo['contents']['dir']['file']
88625                                [$this->_tasksNs . ':' . $task->getName()] = array($t);
88626                        }
88627                        $this->_packageInfo['contents']['dir']['file'][$this->_tasksNs .
88628                            ':' . $task->getName()][] = $task->getXml();
88629                    }
88630                    return true;
88631                }
88632            }
88633        }
88634        return false;
88635    }
88636
88637    /**
88638     * @param string path to the file
88639     * @param string filename
88640     * @param array extra attributes
88641     */
88642    function addFile($dir, $file, $attrs)
88643    {
88644        if ($this->getPackageType() == 'bundle') {
88645            return false;
88646        }
88647        $this->_filesValid = false;
88648        $this->_isValid = 0;
88649        $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
88650        if ($dir == '/' || $dir == '') {
88651            $dir = '';
88652        } else {
88653            $dir .= '/';
88654        }
88655        $attrs['name'] = $dir . $file;
88656        if (!isset($this->_packageInfo['contents'])) {
88657            // ensure that the contents tag is set up
88658            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
88659                array('compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask',
88660                'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
88661                'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
88662                'bundle', 'changelog'), array(), 'contents');
88663        }
88664        if (isset($this->_packageInfo['contents']['dir']['file'])) {
88665            if (!isset($this->_packageInfo['contents']['dir']['file'][0])) {
88666                $this->_packageInfo['contents']['dir']['file'] =
88667                    array($this->_packageInfo['contents']['dir']['file']);
88668            }
88669            $this->_packageInfo['contents']['dir']['file'][]['attribs'] = $attrs;
88670        } else {
88671            $this->_packageInfo['contents']['dir']['file']['attribs'] = $attrs;
88672        }
88673    }
88674
88675    /**
88676     * @param string Dependent package name
88677     * @param string Dependent package's channel name
88678     * @param string minimum version of specified package that this release is guaranteed to be
88679     *               compatible with
88680     * @param string maximum version of specified package that this release is guaranteed to be
88681     *               compatible with
88682     * @param string versions of specified package that this release is not compatible with
88683     */
88684    function addCompatiblePackage($name, $channel, $min, $max, $exclude = false)
88685    {
88686        $this->_isValid = 0;
88687        $set = array(
88688            'name' => $name,
88689            'channel' => $channel,
88690            'min' => $min,
88691            'max' => $max,
88692        );
88693        if ($exclude) {
88694            $set['exclude'] = $exclude;
88695        }
88696        $this->_isValid = 0;
88697        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
88698                'compatible' => array('dependencies', 'providesextension', 'usesrole', 'usestask',
88699                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
88700                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
88701            ));
88702    }
88703
88704    /**
88705     * Removes the <usesrole> tag entirely
88706     */
88707    function resetUsesrole()
88708    {
88709        if (isset($this->_packageInfo['usesrole'])) {
88710            unset($this->_packageInfo['usesrole']);
88711        }
88712    }
88713
88714    /**
88715     * @param string
88716     * @param string package name or uri
88717     * @param string channel name if non-uri
88718     */
88719    function addUsesrole($role, $packageOrUri, $channel = false) {
88720        $set = array('role' => $role);
88721        if ($channel) {
88722            $set['package'] = $packageOrUri;
88723            $set['channel'] = $channel;
88724        } else {
88725            $set['uri'] = $packageOrUri;
88726        }
88727        $this->_isValid = 0;
88728        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
88729                'usesrole' => array('usestask', 'srcpackage', 'srcuri',
88730                    'phprelease', 'extsrcrelease', 'extbinrelease',
88731                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
88732            ));
88733    }
88734
88735    /**
88736     * Removes the <usestask> tag entirely
88737     */
88738    function resetUsestask()
88739    {
88740        if (isset($this->_packageInfo['usestask'])) {
88741            unset($this->_packageInfo['usestask']);
88742        }
88743    }
88744
88745
88746    /**
88747     * @param string
88748     * @param string package name or uri
88749     * @param string channel name if non-uri
88750     */
88751    function addUsestask($task, $packageOrUri, $channel = false) {
88752        $set = array('task' => $task);
88753        if ($channel) {
88754            $set['package'] = $packageOrUri;
88755            $set['channel'] = $channel;
88756        } else {
88757            $set['uri'] = $packageOrUri;
88758        }
88759        $this->_isValid = 0;
88760        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
88761                'usestask' => array('srcpackage', 'srcuri',
88762                    'phprelease', 'extsrcrelease', 'extbinrelease',
88763                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
88764            ));
88765    }
88766
88767    /**
88768     * Remove all compatible tags
88769     */
88770    function clearCompatible()
88771    {
88772        unset($this->_packageInfo['compatible']);
88773    }
88774
88775    /**
88776     * Reset dependencies prior to adding new ones
88777     */
88778    function clearDeps()
88779    {
88780        if (!isset($this->_packageInfo['dependencies'])) {
88781            $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
88782                array(
88783                    'dependencies' => array('providesextension', 'usesrole', 'usestask',
88784                        'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
88785                        'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')));
88786        }
88787        $this->_packageInfo['dependencies'] = array();
88788    }
88789
88790    /**
88791     * @param string minimum PHP version allowed
88792     * @param string maximum PHP version allowed
88793     * @param array $exclude incompatible PHP versions
88794     */
88795    function setPhpDep($min, $max = false, $exclude = false)
88796    {
88797        $this->_isValid = 0;
88798        $dep =
88799            array(
88800                'min' => $min,
88801            );
88802        if ($max) {
88803            $dep['max'] = $max;
88804        }
88805        if ($exclude) {
88806            if (count($exclude) == 1) {
88807                $exclude = $exclude[0];
88808            }
88809            $dep['exclude'] = $exclude;
88810        }
88811        if (isset($this->_packageInfo['dependencies']['required']['php'])) {
88812            $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
88813            $this->_packageInfo['dependencies']['required']['php']),
88814                'warning: PHP dependency already exists, overwriting');
88815            unset($this->_packageInfo['dependencies']['required']['php']);
88816        }
88817        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
88818            array(
88819                'dependencies' => array('providesextension', 'usesrole', 'usestask',
88820                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
88821                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
88822                'required' => array('optional', 'group'),
88823                'php' => array('pearinstaller', 'package', 'subpackage', 'extension', 'os', 'arch')
88824            ));
88825        return true;
88826    }
88827
88828    /**
88829     * @param string minimum allowed PEAR installer version
88830     * @param string maximum allowed PEAR installer version
88831     * @param string recommended PEAR installer version
88832     * @param array incompatible version of the PEAR installer
88833     */
88834    function setPearinstallerDep($min, $max = false, $recommended = false, $exclude = false)
88835    {
88836        $this->_isValid = 0;
88837        $dep =
88838            array(
88839                'min' => $min,
88840            );
88841        if ($max) {
88842            $dep['max'] = $max;
88843        }
88844        if ($recommended) {
88845            $dep['recommended'] = $recommended;
88846        }
88847        if ($exclude) {
88848            if (count($exclude) == 1) {
88849                $exclude = $exclude[0];
88850            }
88851            $dep['exclude'] = $exclude;
88852        }
88853        if (isset($this->_packageInfo['dependencies']['required']['pearinstaller'])) {
88854            $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
88855            $this->_packageInfo['dependencies']['required']['pearinstaller']),
88856                'warning: PEAR Installer dependency already exists, overwriting');
88857            unset($this->_packageInfo['dependencies']['required']['pearinstaller']);
88858        }
88859        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
88860            array(
88861                'dependencies' => array('providesextension', 'usesrole', 'usestask',
88862                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
88863                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
88864                'required' => array('optional', 'group'),
88865                'pearinstaller' => array('package', 'subpackage', 'extension', 'os', 'arch')
88866            ));
88867    }
88868
88869    /**
88870     * Mark a package as conflicting with this package
88871     * @param string package name
88872     * @param string package channel
88873     * @param string extension this package provides, if any
88874     * @param string|false minimum version required
88875     * @param string|false maximum version allowed
88876     * @param array|false versions to exclude from installation
88877     */
88878    function addConflictingPackageDepWithChannel($name, $channel,
88879                $providesextension = false, $min = false, $max = false, $exclude = false)
88880    {
88881        $this->_isValid = 0;
88882        $dep = $this->_constructDep($name, $channel, false, $min, $max, false,
88883            $exclude, $providesextension, false, true);
88884        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
88885            array(
88886                'dependencies' => array('providesextension', 'usesrole', 'usestask',
88887                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
88888                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
88889                'required' => array('optional', 'group'),
88890                'package' => array('subpackage', 'extension', 'os', 'arch')
88891            ));
88892    }
88893
88894    /**
88895     * Mark a package as conflicting with this package
88896     * @param string package name
88897     * @param string package channel
88898     * @param string extension this package provides, if any
88899     */
88900    function addConflictingPackageDepWithUri($name, $uri, $providesextension = false)
88901    {
88902        $this->_isValid = 0;
88903        $dep =
88904            array(
88905                'name' => $name,
88906                'uri' => $uri,
88907                'conflicts' => '',
88908            );
88909        if ($providesextension) {
88910            $dep['providesextension'] = $providesextension;
88911        }
88912        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
88913            array(
88914                'dependencies' => array('providesextension', 'usesrole', 'usestask',
88915                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
88916                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
88917                'required' => array('optional', 'group'),
88918                'package' => array('subpackage', 'extension', 'os', 'arch')
88919            ));
88920    }
88921
88922    function addDependencyGroup($name, $hint)
88923    {
88924        $this->_isValid = 0;
88925        $this->_packageInfo = $this->_mergeTag($this->_packageInfo,
88926            array('attribs' => array('name' => $name, 'hint' => $hint)),
88927            array(
88928                'dependencies' => array('providesextension', 'usesrole', 'usestask',
88929                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
88930                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
88931                'group' => array(),
88932            ));
88933    }
88934
88935    /**
88936     * @param string package name
88937     * @param string|false channel name, false if this is a uri
88938     * @param string|false uri name, false if this is a channel
88939     * @param string|false minimum version required
88940     * @param string|false maximum version allowed
88941     * @param string|false recommended installation version
88942     * @param array|false versions to exclude from installation
88943     * @param string extension this package provides, if any
88944     * @param bool if true, tells the installer to ignore the default optional dependency group
88945     *             when installing this package
88946     * @param bool if true, tells the installer to negate this dependency (conflicts)
88947     * @return array
88948     * @access private
88949     */
88950    function _constructDep($name, $channel, $uri, $min, $max, $recommended, $exclude,
88951                           $providesextension = false, $nodefault = false,
88952                           $conflicts = false)
88953    {
88954        $dep =
88955            array(
88956                'name' => $name,
88957            );
88958        if ($channel) {
88959            $dep['channel'] = $channel;
88960        } elseif ($uri) {
88961            $dep['uri'] = $uri;
88962        }
88963        if ($min) {
88964            $dep['min'] = $min;
88965        }
88966        if ($max) {
88967            $dep['max'] = $max;
88968        }
88969        if ($recommended) {
88970            $dep['recommended'] = $recommended;
88971        }
88972        if ($exclude) {
88973            if (is_array($exclude) && count($exclude) == 1) {
88974                $exclude = $exclude[0];
88975            }
88976            $dep['exclude'] = $exclude;
88977        }
88978        if ($conflicts) {
88979            $dep['conflicts'] = '';
88980        }
88981        if ($nodefault) {
88982            $dep['nodefault'] = '';
88983        }
88984        if ($providesextension) {
88985            $dep['providesextension'] = $providesextension;
88986        }
88987        return $dep;
88988    }
88989
88990    /**
88991     * @param package|subpackage
88992     * @param string group name
88993     * @param string package name
88994     * @param string package channel
88995     * @param string minimum version
88996     * @param string maximum version
88997     * @param string recommended version
88998     * @param array|false optional excluded versions
88999     * @param string extension this package provides, if any
89000     * @param bool if true, tells the installer to ignore the default optional dependency group
89001     *             when installing this package
89002     * @return bool false if the dependency group has not been initialized with
89003     *              {@link addDependencyGroup()}, or a subpackage is added with
89004     *              a providesextension
89005     */
89006    function addGroupPackageDepWithChannel($type, $groupname, $name, $channel, $min = false,
89007                                      $max = false, $recommended = false, $exclude = false,
89008                                      $providesextension = false, $nodefault = false)
89009    {
89010        if ($type == 'subpackage' && $providesextension) {
89011            return false; // subpackages must be php packages
89012        }
89013        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
89014            $providesextension, $nodefault);
89015        return $this->_addGroupDependency($type, $dep, $groupname);
89016    }
89017
89018    /**
89019     * @param package|subpackage
89020     * @param string group name
89021     * @param string package name
89022     * @param string package uri
89023     * @param string extension this package provides, if any
89024     * @param bool if true, tells the installer to ignore the default optional dependency group
89025     *             when installing this package
89026     * @return bool false if the dependency group has not been initialized with
89027     *              {@link addDependencyGroup()}
89028     */
89029    function addGroupPackageDepWithURI($type, $groupname, $name, $uri, $providesextension = false,
89030                                       $nodefault = false)
89031    {
89032        if ($type == 'subpackage' && $providesextension) {
89033            return false; // subpackages must be php packages
89034        }
89035        $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
89036            $providesextension, $nodefault);
89037        return $this->_addGroupDependency($type, $dep, $groupname);
89038    }
89039
89040    /**
89041     * @param string group name (must be pre-existing)
89042     * @param string extension name
89043     * @param string minimum version allowed
89044     * @param string maximum version allowed
89045     * @param string recommended version
89046     * @param array incompatible versions
89047     */
89048    function addGroupExtensionDep($groupname, $name, $min = false, $max = false,
89049                                         $recommended = false, $exclude = false)
89050    {
89051        $this->_isValid = 0;
89052        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
89053        return $this->_addGroupDependency('extension', $dep, $groupname);
89054    }
89055
89056    /**
89057     * @param package|subpackage|extension
89058     * @param array dependency contents
89059     * @param string name of the dependency group to add this to
89060     * @return boolean
89061     * @access private
89062     */
89063    function _addGroupDependency($type, $dep, $groupname)
89064    {
89065        $arr = array('subpackage', 'extension');
89066        if ($type != 'package') {
89067            array_shift($arr);
89068        }
89069        if ($type == 'extension') {
89070            array_shift($arr);
89071        }
89072        if (!isset($this->_packageInfo['dependencies']['group'])) {
89073            return false;
89074        } else {
89075            if (!isset($this->_packageInfo['dependencies']['group'][0])) {
89076                if ($this->_packageInfo['dependencies']['group']['attribs']['name'] == $groupname) {
89077                    $this->_packageInfo['dependencies']['group'] = $this->_mergeTag(
89078                        $this->_packageInfo['dependencies']['group'], $dep,
89079                        array(
89080                            $type => $arr
89081                        ));
89082                    $this->_isValid = 0;
89083                    return true;
89084                } else {
89085                    return false;
89086                }
89087            } else {
89088                foreach ($this->_packageInfo['dependencies']['group'] as $i => $group) {
89089                    if ($group['attribs']['name'] == $groupname) {
89090                    $this->_packageInfo['dependencies']['group'][$i] = $this->_mergeTag(
89091                        $this->_packageInfo['dependencies']['group'][$i], $dep,
89092                        array(
89093                            $type => $arr
89094                        ));
89095                        $this->_isValid = 0;
89096                        return true;
89097                    }
89098                }
89099                return false;
89100            }
89101        }
89102    }
89103
89104    /**
89105     * @param optional|required
89106     * @param string package name
89107     * @param string package channel
89108     * @param string minimum version
89109     * @param string maximum version
89110     * @param string recommended version
89111     * @param string extension this package provides, if any
89112     * @param bool if true, tells the installer to ignore the default optional dependency group
89113     *             when installing this package
89114     * @param array|false optional excluded versions
89115     */
89116    function addPackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
89117                                      $recommended = false, $exclude = false,
89118                                      $providesextension = false, $nodefault = false)
89119    {
89120        if (!in_array($type, array('optional', 'required'), true)) {
89121            $type = 'required';
89122        }
89123        $this->_isValid = 0;
89124        $arr = array('optional', 'group');
89125        if ($type != 'required') {
89126            array_shift($arr);
89127        }
89128        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
89129            $providesextension, $nodefault);
89130        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
89131            array(
89132                'dependencies' => array('providesextension', 'usesrole', 'usestask',
89133                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
89134                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
89135                $type => $arr,
89136                'package' => array('subpackage', 'extension', 'os', 'arch')
89137            ));
89138    }
89139
89140    /**
89141     * @param optional|required
89142     * @param string name of the package
89143     * @param string uri of the package
89144     * @param string extension this package provides, if any
89145     * @param bool if true, tells the installer to ignore the default optional dependency group
89146     *             when installing this package
89147     */
89148    function addPackageDepWithUri($type, $name, $uri, $providesextension = false,
89149                                  $nodefault = false)
89150    {
89151        $this->_isValid = 0;
89152        $arr = array('optional', 'group');
89153        if ($type != 'required') {
89154            array_shift($arr);
89155        }
89156        $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
89157            $providesextension, $nodefault);
89158        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
89159            array(
89160                'dependencies' => array('providesextension', 'usesrole', 'usestask',
89161                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
89162                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
89163                $type => $arr,
89164                'package' => array('subpackage', 'extension', 'os', 'arch')
89165            ));
89166    }
89167
89168    /**
89169     * @param optional|required optional, required
89170     * @param string package name
89171     * @param string package channel
89172     * @param string minimum version
89173     * @param string maximum version
89174     * @param string recommended version
89175     * @param array incompatible versions
89176     * @param bool if true, tells the installer to ignore the default optional dependency group
89177     *             when installing this package
89178     */
89179    function addSubpackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
89180                                         $recommended = false, $exclude = false,
89181                                         $nodefault = false)
89182    {
89183        $this->_isValid = 0;
89184        $arr = array('optional', 'group');
89185        if ($type != 'required') {
89186            array_shift($arr);
89187        }
89188        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
89189            $nodefault);
89190        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
89191            array(
89192                'dependencies' => array('providesextension', 'usesrole', 'usestask',
89193                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
89194                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
89195                $type => $arr,
89196                'subpackage' => array('extension', 'os', 'arch')
89197            ));
89198    }
89199
89200    /**
89201     * @param optional|required optional, required
89202     * @param string package name
89203     * @param string package uri for download
89204     * @param bool if true, tells the installer to ignore the default optional dependency group
89205     *             when installing this package
89206     */
89207    function addSubpackageDepWithUri($type, $name, $uri, $nodefault = false)
89208    {
89209        $this->_isValid = 0;
89210        $arr = array('optional', 'group');
89211        if ($type != 'required') {
89212            array_shift($arr);
89213        }
89214        $dep = $this->_constructDep($name, false, $uri, false, false, false, false, $nodefault);
89215        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
89216            array(
89217                'dependencies' => array('providesextension', 'usesrole', 'usestask',
89218                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
89219                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
89220                $type => $arr,
89221                'subpackage' => array('extension', 'os', 'arch')
89222            ));
89223    }
89224
89225    /**
89226     * @param optional|required optional, required
89227     * @param string extension name
89228     * @param string minimum version
89229     * @param string maximum version
89230     * @param string recommended version
89231     * @param array incompatible versions
89232     */
89233    function addExtensionDep($type, $name, $min = false, $max = false, $recommended = false,
89234                             $exclude = false)
89235    {
89236        $this->_isValid = 0;
89237        $arr = array('optional', 'group');
89238        if ($type != 'required') {
89239            array_shift($arr);
89240        }
89241        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
89242        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
89243            array(
89244                'dependencies' => array('providesextension', 'usesrole', 'usestask',
89245                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
89246                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
89247                $type => $arr,
89248                'extension' => array('os', 'arch')
89249            ));
89250    }
89251
89252    /**
89253     * @param string Operating system name
89254     * @param boolean true if this package cannot be installed on this OS
89255     */
89256    function addOsDep($name, $conflicts = false)
89257    {
89258        $this->_isValid = 0;
89259        $dep = array('name' => $name);
89260        if ($conflicts) {
89261            $dep['conflicts'] = '';
89262        }
89263        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
89264            array(
89265                'dependencies' => array('providesextension', 'usesrole', 'usestask',
89266                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
89267                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
89268                'required' => array('optional', 'group'),
89269                'os' => array('arch')
89270            ));
89271    }
89272
89273    /**
89274     * @param string Architecture matching pattern
89275     * @param boolean true if this package cannot be installed on this architecture
89276     */
89277    function addArchDep($pattern, $conflicts = false)
89278    {
89279        $this->_isValid = 0;
89280        $dep = array('pattern' => $pattern);
89281        if ($conflicts) {
89282            $dep['conflicts'] = '';
89283        }
89284        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
89285            array(
89286                'dependencies' => array('providesextension', 'usesrole', 'usestask',
89287                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
89288                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
89289                'required' => array('optional', 'group'),
89290                'arch' => array()
89291            ));
89292    }
89293
89294    /**
89295     * Set the kind of package, and erase all release tags
89296     *
89297     * - a php package is a PEAR-style package
89298     * - an extbin package is a PECL-style extension binary
89299     * - an extsrc package is a PECL-style source for a binary
89300     * - an zendextbin package is a PECL-style zend extension binary
89301     * - an zendextsrc package is a PECL-style source for a zend extension binary
89302     * - a bundle package is a collection of other pre-packaged packages
89303     * @param php|extbin|extsrc|zendextsrc|zendextbin|bundle
89304     * @return bool success
89305     */
89306    function setPackageType($type)
89307    {
89308        $this->_isValid = 0;
89309        if (!in_array($type, array('php', 'extbin', 'extsrc', 'zendextsrc',
89310                                   'zendextbin', 'bundle'))) {
89311            return false;
89312        }
89313
89314        if (in_array($type, array('zendextsrc', 'zendextbin'))) {
89315            $this->_setPackageVersion2_1();
89316        }
89317
89318        if ($type != 'bundle') {
89319            $type .= 'release';
89320        }
89321
89322        foreach (array('phprelease', 'extbinrelease', 'extsrcrelease',
89323                       'zendextsrcrelease', 'zendextbinrelease', 'bundle') as $test) {
89324            unset($this->_packageInfo[$test]);
89325        }
89326
89327        if (!isset($this->_packageInfo[$type])) {
89328            // ensure that the release tag is set up
89329            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('changelog'),
89330                array(), $type);
89331        }
89332
89333        $this->_packageInfo[$type] = array();
89334        return true;
89335    }
89336
89337    /**
89338     * @return bool true if package type is set up
89339     */
89340    function addRelease()
89341    {
89342        if ($type = $this->getPackageType()) {
89343            if ($type != 'bundle') {
89344                $type .= 'release';
89345            }
89346            $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
89347                array($type => array('changelog')));
89348            return true;
89349        }
89350        return false;
89351    }
89352
89353    /**
89354     * Get the current release tag in order to add to it
89355     * @param bool returns only releases that have installcondition if true
89356     * @return array|null
89357     */
89358    function &_getCurrentRelease($strict = true)
89359    {
89360        if ($p = $this->getPackageType()) {
89361            if ($strict) {
89362                if ($p == 'extsrc' || $p == 'zendextsrc') {
89363                    $a = null;
89364                    return $a;
89365                }
89366            }
89367            if ($p != 'bundle') {
89368                $p .= 'release';
89369            }
89370            if (isset($this->_packageInfo[$p][0])) {
89371                return $this->_packageInfo[$p][count($this->_packageInfo[$p]) - 1];
89372            } else {
89373                return $this->_packageInfo[$p];
89374            }
89375        } else {
89376            $a = null;
89377            return $a;
89378        }
89379    }
89380
89381    /**
89382     * Add a file to the current release that should be installed under a different name
89383     * @param string <contents> path to file
89384     * @param string name the file should be installed as
89385     */
89386    function addInstallAs($path, $as)
89387    {
89388        $r = &$this->_getCurrentRelease();
89389        if ($r === null) {
89390            return false;
89391        }
89392        $this->_isValid = 0;
89393        $r = $this->_mergeTag($r, array('attribs' => array('name' => $path, 'as' => $as)),
89394            array(
89395                'filelist' => array(),
89396                'install' => array('ignore')
89397            ));
89398    }
89399
89400    /**
89401     * Add a file to the current release that should be ignored
89402     * @param string <contents> path to file
89403     * @return bool success of operation
89404     */
89405    function addIgnore($path)
89406    {
89407        $r = &$this->_getCurrentRelease();
89408        if ($r === null) {
89409            return false;
89410        }
89411        $this->_isValid = 0;
89412        $r = $this->_mergeTag($r, array('attribs' => array('name' => $path)),
89413            array(
89414                'filelist' => array(),
89415                'ignore' => array()
89416            ));
89417    }
89418
89419    /**
89420     * Add an extension binary package for this extension source code release
89421     *
89422     * Note that the package must be from the same channel as the extension source package
89423     * @param string
89424     */
89425    function addBinarypackage($package)
89426    {
89427        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
89428            return false;
89429        }
89430        $r = &$this->_getCurrentRelease(false);
89431        if ($r === null) {
89432            return false;
89433        }
89434        $this->_isValid = 0;
89435        $r = $this->_mergeTag($r, $package,
89436            array(
89437                'binarypackage' => array('filelist'),
89438            ));
89439    }
89440
89441    /**
89442     * Add a configureoption to an extension source package
89443     * @param string
89444     * @param string
89445     * @param string
89446     */
89447    function addConfigureOption($name, $prompt, $default = null)
89448    {
89449        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
89450            return false;
89451        }
89452
89453        $r = &$this->_getCurrentRelease(false);
89454        if ($r === null) {
89455            return false;
89456        }
89457
89458        $opt = array('attribs' => array('name' => $name, 'prompt' => $prompt));
89459        if ($default !== null) {
89460            $opt['attribs']['default'] = $default;
89461        }
89462
89463        $this->_isValid = 0;
89464        $r = $this->_mergeTag($r, $opt,
89465            array(
89466                'configureoption' => array('binarypackage', 'filelist'),
89467            ));
89468    }
89469
89470    /**
89471     * Set an installation condition based on php version for the current release set
89472     * @param string minimum version
89473     * @param string maximum version
89474     * @param false|array incompatible versions of PHP
89475     */
89476    function setPhpInstallCondition($min, $max, $exclude = false)
89477    {
89478        $r = &$this->_getCurrentRelease();
89479        if ($r === null) {
89480            return false;
89481        }
89482        $this->_isValid = 0;
89483        if (isset($r['installconditions']['php'])) {
89484            unset($r['installconditions']['php']);
89485        }
89486        $dep = array('min' => $min, 'max' => $max);
89487        if ($exclude) {
89488            if (is_array($exclude) && count($exclude) == 1) {
89489                $exclude = $exclude[0];
89490            }
89491            $dep['exclude'] = $exclude;
89492        }
89493        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
89494            $r = $this->_mergeTag($r, $dep,
89495                array(
89496                    'installconditions' => array('configureoption', 'binarypackage',
89497                        'filelist'),
89498                    'php' => array('extension', 'os', 'arch')
89499                ));
89500        } else {
89501            $r = $this->_mergeTag($r, $dep,
89502                array(
89503                    'installconditions' => array('filelist'),
89504                    'php' => array('extension', 'os', 'arch')
89505                ));
89506        }
89507    }
89508
89509    /**
89510     * @param optional|required optional, required
89511     * @param string extension name
89512     * @param string minimum version
89513     * @param string maximum version
89514     * @param string recommended version
89515     * @param array incompatible versions
89516     */
89517    function addExtensionInstallCondition($name, $min = false, $max = false, $recommended = false,
89518                                          $exclude = false)
89519    {
89520        $r = &$this->_getCurrentRelease();
89521        if ($r === null) {
89522            return false;
89523        }
89524        $this->_isValid = 0;
89525        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
89526        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
89527            $r = $this->_mergeTag($r, $dep,
89528                array(
89529                    'installconditions' => array('configureoption', 'binarypackage',
89530                        'filelist'),
89531                    'extension' => array('os', 'arch')
89532                ));
89533        } else {
89534            $r = $this->_mergeTag($r, $dep,
89535                array(
89536                    'installconditions' => array('filelist'),
89537                    'extension' => array('os', 'arch')
89538                ));
89539        }
89540    }
89541
89542    /**
89543     * Set an installation condition based on operating system for the current release set
89544     * @param string OS name
89545     * @param bool whether this OS is incompatible with the current release
89546     */
89547    function setOsInstallCondition($name, $conflicts = false)
89548    {
89549        $r = &$this->_getCurrentRelease();
89550        if ($r === null) {
89551            return false;
89552        }
89553        $this->_isValid = 0;
89554        if (isset($r['installconditions']['os'])) {
89555            unset($r['installconditions']['os']);
89556        }
89557        $dep = array('name' => $name);
89558        if ($conflicts) {
89559            $dep['conflicts'] = '';
89560        }
89561        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
89562            $r = $this->_mergeTag($r, $dep,
89563                array(
89564                    'installconditions' => array('configureoption', 'binarypackage',
89565                        'filelist'),
89566                    'os' => array('arch')
89567                ));
89568        } else {
89569            $r = $this->_mergeTag($r, $dep,
89570                array(
89571                    'installconditions' => array('filelist'),
89572                    'os' => array('arch')
89573                ));
89574        }
89575    }
89576
89577    /**
89578     * Set an installation condition based on architecture for the current release set
89579     * @param string architecture pattern
89580     * @param bool whether this arch is incompatible with the current release
89581     */
89582    function setArchInstallCondition($pattern, $conflicts = false)
89583    {
89584        $r = &$this->_getCurrentRelease();
89585        if ($r === null) {
89586            return false;
89587        }
89588        $this->_isValid = 0;
89589        if (isset($r['installconditions']['arch'])) {
89590            unset($r['installconditions']['arch']);
89591        }
89592        $dep = array('pattern' => $pattern);
89593        if ($conflicts) {
89594            $dep['conflicts'] = '';
89595        }
89596        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
89597            $r = $this->_mergeTag($r, $dep,
89598                array(
89599                    'installconditions' => array('configureoption', 'binarypackage',
89600                        'filelist'),
89601                    'arch' => array()
89602                ));
89603        } else {
89604            $r = $this->_mergeTag($r, $dep,
89605                array(
89606                    'installconditions' => array('filelist'),
89607                    'arch' => array()
89608                ));
89609        }
89610    }
89611
89612    /**
89613     * For extension binary releases, this is used to specify either the
89614     * static URI to a source package, or the package name and channel of the extsrc/zendextsrc
89615     * package it is based on.
89616     * @param string Package name, or full URI to source package (extsrc/zendextsrc type)
89617     */
89618    function setSourcePackage($packageOrUri)
89619    {
89620        $this->_isValid = 0;
89621        if (isset($this->_packageInfo['channel'])) {
89622            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
89623                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
89624                'bundle', 'changelog'),
89625                $packageOrUri, 'srcpackage');
89626        } else {
89627            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
89628                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
89629                'bundle', 'changelog'), $packageOrUri, 'srcuri');
89630        }
89631    }
89632
89633    /**
89634     * Generate a valid change log entry from the current package.xml
89635     * @param string|false
89636     */
89637    function generateChangeLogEntry($notes = false)
89638    {
89639        return array(
89640            'version' =>
89641                array(
89642                    'release' => $this->getVersion('release'),
89643                    'api' => $this->getVersion('api'),
89644                    ),
89645            'stability' =>
89646                $this->getStability(),
89647            'date' => $this->getDate(),
89648            'license' => $this->getLicense(true),
89649            'notes' => $notes ? $notes : $this->getNotes()
89650            );
89651    }
89652
89653    /**
89654     * @param string release version to set change log notes for
89655     * @param array output of {@link generateChangeLogEntry()}
89656     */
89657    function setChangelogEntry($releaseversion, $contents)
89658    {
89659        if (!isset($this->_packageInfo['changelog'])) {
89660            $this->_packageInfo['changelog']['release'] = $contents;
89661            return;
89662        }
89663        if (!isset($this->_packageInfo['changelog']['release'][0])) {
89664            if ($this->_packageInfo['changelog']['release']['version']['release'] == $releaseversion) {
89665                $this->_packageInfo['changelog']['release'] = array(
89666                    $this->_packageInfo['changelog']['release']);
89667            } else {
89668                $this->_packageInfo['changelog']['release'] = array(
89669                    $this->_packageInfo['changelog']['release']);
89670                return $this->_packageInfo['changelog']['release'][] = $contents;
89671            }
89672        }
89673        foreach($this->_packageInfo['changelog']['release'] as $index => $changelog) {
89674            if (isset($changelog['version']) &&
89675                  strnatcasecmp($changelog['version']['release'], $releaseversion) == 0) {
89676                $curlog = $index;
89677            }
89678        }
89679        if (isset($curlog)) {
89680            $this->_packageInfo['changelog']['release'][$curlog] = $contents;
89681        } else {
89682            $this->_packageInfo['changelog']['release'][] = $contents;
89683        }
89684    }
89685
89686    /**
89687     * Remove the changelog entirely
89688     */
89689    function clearChangeLog()
89690    {
89691        unset($this->_packageInfo['changelog']);
89692    }
89693}<?php
89694/**
89695 * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
89696 *
89697 * PHP versions 4 and 5
89698 *
89699 * @category   pear
89700 * @package    PEAR
89701 * @author     Greg Beaver <cellog@php.net>
89702 * @copyright  1997-2009 The Authors
89703 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
89704 * @version    CVS: $Id: Validator.php 313023 2011-07-06 19:17:11Z dufuz $
89705 * @link       http://pear.php.net/package/PEAR
89706 * @since      File available since Release 1.4.0a8
89707 */
89708/**
89709 * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its
89710 * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller
89711 * @category   pear
89712 * @package    PEAR
89713 * @author     Greg Beaver <cellog@php.net>
89714 * @copyright  1997-2009 The Authors
89715 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
89716 * @version    Release: 1.9.4
89717 * @link       http://pear.php.net/package/PEAR
89718 * @since      Class available since Release 1.4.0a8
89719 * @access private
89720 */
89721class PEAR_PackageFile_v2_Validator
89722{
89723    /**
89724     * @var array
89725     */
89726    var $_packageInfo;
89727    /**
89728     * @var PEAR_PackageFile_v2
89729     */
89730    var $_pf;
89731    /**
89732     * @var PEAR_ErrorStack
89733     */
89734    var $_stack;
89735    /**
89736     * @var int
89737     */
89738    var $_isValid = 0;
89739    /**
89740     * @var int
89741     */
89742    var $_filesValid = 0;
89743    /**
89744     * @var int
89745     */
89746    var $_curState = 0;
89747    /**
89748     * @param PEAR_PackageFile_v2
89749     * @param int
89750     */
89751    function validate(&$pf, $state = PEAR_VALIDATE_NORMAL)
89752    {
89753        $this->_pf = &$pf;
89754        $this->_curState = $state;
89755        $this->_packageInfo = $this->_pf->getArray();
89756        $this->_isValid = $this->_pf->_isValid;
89757        $this->_filesValid = $this->_pf->_filesValid;
89758        $this->_stack = &$pf->_stack;
89759        $this->_stack->getErrors(true);
89760        if (($this->_isValid & $state) == $state) {
89761            return true;
89762        }
89763        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
89764            return false;
89765        }
89766        if (!isset($this->_packageInfo['attribs']['version']) ||
89767              ($this->_packageInfo['attribs']['version'] != '2.0' &&
89768               $this->_packageInfo['attribs']['version'] != '2.1')
89769        ) {
89770            $this->_noPackageVersion();
89771        }
89772        $structure =
89773        array(
89774            'name',
89775            'channel|uri',
89776            '*extends', // can't be multiple, but this works fine
89777            'summary',
89778            'description',
89779            '+lead', // these all need content checks
89780            '*developer',
89781            '*contributor',
89782            '*helper',
89783            'date',
89784            '*time',
89785            'version',
89786            'stability',
89787            'license->?uri->?filesource',
89788            'notes',
89789            'contents', //special validation needed
89790            '*compatible',
89791            'dependencies', //special validation needed
89792            '*usesrole',
89793            '*usestask', // reserve these for 1.4.0a1 to implement
89794                         // this will allow a package.xml to gracefully say it
89795                         // needs a certain package installed in order to implement a role or task
89796            '*providesextension',
89797            '*srcpackage|*srcuri',
89798            '+phprelease|+extsrcrelease|+extbinrelease|' .
89799                '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed
89800            '*changelog',
89801        );
89802        $test = $this->_packageInfo;
89803        if (isset($test['dependencies']) &&
89804              isset($test['dependencies']['required']) &&
89805              isset($test['dependencies']['required']['pearinstaller']) &&
89806              isset($test['dependencies']['required']['pearinstaller']['min']) &&
89807              version_compare('1.9.4',
89808                $test['dependencies']['required']['pearinstaller']['min'], '<')
89809        ) {
89810            $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']);
89811            return false;
89812        }
89813        // ignore post-installation array fields
89814        if (array_key_exists('filelist', $test)) {
89815            unset($test['filelist']);
89816        }
89817        if (array_key_exists('_lastmodified', $test)) {
89818            unset($test['_lastmodified']);
89819        }
89820        if (array_key_exists('#binarypackage', $test)) {
89821            unset($test['#binarypackage']);
89822        }
89823        if (array_key_exists('old', $test)) {
89824            unset($test['old']);
89825        }
89826        if (array_key_exists('_lastversion', $test)) {
89827            unset($test['_lastversion']);
89828        }
89829        if (!$this->_stupidSchemaValidate($structure, $test, '<package>')) {
89830            return false;
89831        }
89832        if (empty($this->_packageInfo['name'])) {
89833            $this->_tagCannotBeEmpty('name');
89834        }
89835        $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel';
89836        if (empty($this->_packageInfo[$test])) {
89837            $this->_tagCannotBeEmpty($test);
89838        }
89839        if (is_array($this->_packageInfo['license']) &&
89840              (!isset($this->_packageInfo['license']['_content']) ||
89841              empty($this->_packageInfo['license']['_content']))) {
89842            $this->_tagCannotBeEmpty('license');
89843        } elseif (empty($this->_packageInfo['license'])) {
89844            $this->_tagCannotBeEmpty('license');
89845        }
89846        if (empty($this->_packageInfo['summary'])) {
89847            $this->_tagCannotBeEmpty('summary');
89848        }
89849        if (empty($this->_packageInfo['description'])) {
89850            $this->_tagCannotBeEmpty('description');
89851        }
89852        if (empty($this->_packageInfo['date'])) {
89853            $this->_tagCannotBeEmpty('date');
89854        }
89855        if (empty($this->_packageInfo['notes'])) {
89856            $this->_tagCannotBeEmpty('notes');
89857        }
89858        if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) {
89859            $this->_tagCannotBeEmpty('time');
89860        }
89861        if (isset($this->_packageInfo['dependencies'])) {
89862            $this->_validateDependencies();
89863        }
89864        if (isset($this->_packageInfo['compatible'])) {
89865            $this->_validateCompatible();
89866        }
89867        if (!isset($this->_packageInfo['bundle'])) {
89868            if (empty($this->_packageInfo['contents'])) {
89869                $this->_tagCannotBeEmpty('contents');
89870            }
89871            if (!isset($this->_packageInfo['contents']['dir'])) {
89872                $this->_filelistMustContainDir('contents');
89873                return false;
89874            }
89875            if (isset($this->_packageInfo['contents']['file'])) {
89876                $this->_filelistCannotContainFile('contents');
89877                return false;
89878            }
89879        }
89880        $this->_validateMaintainers();
89881        $this->_validateStabilityVersion();
89882        $fail = false;
89883        if (array_key_exists('usesrole', $this->_packageInfo)) {
89884            $roles = $this->_packageInfo['usesrole'];
89885            if (!is_array($roles) || !isset($roles[0])) {
89886                $roles = array($roles);
89887            }
89888            foreach ($roles as $role) {
89889                if (!isset($role['role'])) {
89890                    $this->_usesroletaskMustHaveRoleTask('usesrole', 'role');
89891                    $fail = true;
89892                } else {
89893                    if (!isset($role['channel'])) {
89894                        if (!isset($role['uri'])) {
89895                            $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole');
89896                            $fail = true;
89897                        }
89898                    } elseif (!isset($role['package'])) {
89899                        $this->_usesroletaskMustHavePackage($role['role'], 'usesrole');
89900                        $fail = true;
89901                    }
89902                }
89903            }
89904        }
89905        if (array_key_exists('usestask', $this->_packageInfo)) {
89906            $roles = $this->_packageInfo['usestask'];
89907            if (!is_array($roles) || !isset($roles[0])) {
89908                $roles = array($roles);
89909            }
89910            foreach ($roles as $role) {
89911                if (!isset($role['task'])) {
89912                    $this->_usesroletaskMustHaveRoleTask('usestask', 'task');
89913                    $fail = true;
89914                } else {
89915                    if (!isset($role['channel'])) {
89916                        if (!isset($role['uri'])) {
89917                            $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask');
89918                            $fail = true;
89919                        }
89920                    } elseif (!isset($role['package'])) {
89921                        $this->_usesroletaskMustHavePackage($role['task'], 'usestask');
89922                        $fail = true;
89923                    }
89924                }
89925            }
89926        }
89927
89928        if ($fail) {
89929            return false;
89930        }
89931
89932        $list = $this->_packageInfo['contents'];
89933        if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) {
89934            $this->_multipleToplevelDirNotAllowed();
89935            return $this->_isValid = 0;
89936        }
89937
89938        $this->_validateFilelist();
89939        $this->_validateRelease();
89940        if (!$this->_stack->hasErrors()) {
89941            $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true);
89942            if (PEAR::isError($chan)) {
89943                $this->_unknownChannel($this->_pf->getChannel());
89944            } else {
89945                $valpack = $chan->getValidationPackage();
89946                // for channel validator packages, always use the default PEAR validator.
89947                // otherwise, they can't be installed or packaged
89948                $validator = $chan->getValidationObject($this->_pf->getPackage());
89949                if (!$validator) {
89950                    $this->_stack->push(__FUNCTION__, 'error',
89951                        array('channel' => $chan->getName(),
89952                              'package' => $this->_pf->getPackage(),
89953                              'name'    => $valpack['_content'],
89954                              'version' => $valpack['attribs']['version']),
89955                        'package "%channel%/%package%" cannot be properly validated without ' .
89956                        'validation package "%channel%/%name%-%version%"');
89957                    return $this->_isValid = 0;
89958                }
89959                $validator->setPackageFile($this->_pf);
89960                $validator->validate($state);
89961                $failures = $validator->getFailures();
89962                foreach ($failures['errors'] as $error) {
89963                    $this->_stack->push(__FUNCTION__, 'error', $error,
89964                        'Channel validator error: field "%field%" - %reason%');
89965                }
89966                foreach ($failures['warnings'] as $warning) {
89967                    $this->_stack->push(__FUNCTION__, 'warning', $warning,
89968                        'Channel validator warning: field "%field%" - %reason%');
89969                }
89970            }
89971        }
89972
89973        $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error');
89974        if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) {
89975            if ($this->_pf->getPackageType() == 'bundle') {
89976                if ($this->_analyzeBundledPackages()) {
89977                    $this->_filesValid = $this->_pf->_filesValid = true;
89978                } else {
89979                    $this->_pf->_isValid = $this->_isValid = 0;
89980                }
89981            } else {
89982                if (!$this->_analyzePhpFiles()) {
89983                    $this->_pf->_isValid = $this->_isValid = 0;
89984                } else {
89985                    $this->_filesValid = $this->_pf->_filesValid = true;
89986                }
89987            }
89988        }
89989
89990        if ($this->_isValid) {
89991            return $this->_pf->_isValid = $this->_isValid = $state;
89992        }
89993
89994        return $this->_pf->_isValid = $this->_isValid = 0;
89995    }
89996
89997    function _stupidSchemaValidate($structure, $xml, $root)
89998    {
89999        if (!is_array($xml)) {
90000            $xml = array();
90001        }
90002        $keys = array_keys($xml);
90003        reset($keys);
90004        $key = current($keys);
90005        while ($key == 'attribs' || $key == '_contents') {
90006            $key = next($keys);
90007        }
90008        $unfoundtags = $optionaltags = array();
90009        $ret = true;
90010        $mismatch = false;
90011        foreach ($structure as $struc) {
90012            if ($key) {
90013                $tag = $xml[$key];
90014            }
90015            $test = $this->_processStructure($struc);
90016            if (isset($test['choices'])) {
90017                $loose = true;
90018                foreach ($test['choices'] as $choice) {
90019                    if ($key == $choice['tag']) {
90020                        $key = next($keys);
90021                        while ($key == 'attribs' || $key == '_contents') {
90022                            $key = next($keys);
90023                        }
90024                        $unfoundtags = $optionaltags = array();
90025                        $mismatch = false;
90026                        if ($key && $key != $choice['tag'] && isset($choice['multiple'])) {
90027                            $unfoundtags[] = $choice['tag'];
90028                            $optionaltags[] = $choice['tag'];
90029                            if ($key) {
90030                                $mismatch = true;
90031                            }
90032                        }
90033                        $ret &= $this->_processAttribs($choice, $tag, $root);
90034                        continue 2;
90035                    } else {
90036                        $unfoundtags[] = $choice['tag'];
90037                        $mismatch = true;
90038                    }
90039                    if (!isset($choice['multiple']) || $choice['multiple'] != '*') {
90040                        $loose = false;
90041                    } else {
90042                        $optionaltags[] = $choice['tag'];
90043                    }
90044                }
90045                if (!$loose) {
90046                    $this->_invalidTagOrder($unfoundtags, $key, $root);
90047                    return false;
90048                }
90049            } else {
90050                if ($key != $test['tag']) {
90051                    if (isset($test['multiple']) && $test['multiple'] != '*') {
90052                        $unfoundtags[] = $test['tag'];
90053                        $this->_invalidTagOrder($unfoundtags, $key, $root);
90054                        return false;
90055                    } else {
90056                        if ($key) {
90057                            $mismatch = true;
90058                        }
90059                        $unfoundtags[] = $test['tag'];
90060                        $optionaltags[] = $test['tag'];
90061                    }
90062                    if (!isset($test['multiple'])) {
90063                        $this->_invalidTagOrder($unfoundtags, $key, $root);
90064                        return false;
90065                    }
90066                    continue;
90067                } else {
90068                    $unfoundtags = $optionaltags = array();
90069                    $mismatch = false;
90070                }
90071                $key = next($keys);
90072                while ($key == 'attribs' || $key == '_contents') {
90073                    $key = next($keys);
90074                }
90075                if ($key && $key != $test['tag'] && isset($test['multiple'])) {
90076                    $unfoundtags[] = $test['tag'];
90077                    $optionaltags[] = $test['tag'];
90078                    $mismatch = true;
90079                }
90080                $ret &= $this->_processAttribs($test, $tag, $root);
90081                continue;
90082            }
90083        }
90084        if (!$mismatch && count($optionaltags)) {
90085            // don't error out on any optional tags
90086            $unfoundtags = array_diff($unfoundtags, $optionaltags);
90087        }
90088        if (count($unfoundtags)) {
90089            $this->_invalidTagOrder($unfoundtags, $key, $root);
90090        } elseif ($key) {
90091            // unknown tags
90092            $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
90093            while ($key = next($keys)) {
90094                $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
90095            }
90096        }
90097        return $ret;
90098    }
90099
90100    function _processAttribs($choice, $tag, $context)
90101    {
90102        if (isset($choice['attribs'])) {
90103            if (!is_array($tag)) {
90104                $tag = array($tag);
90105            }
90106            $tags = $tag;
90107            if (!isset($tags[0])) {
90108                $tags = array($tags);
90109            }
90110            $ret = true;
90111            foreach ($tags as $i => $tag) {
90112                if (!is_array($tag) || !isset($tag['attribs'])) {
90113                    foreach ($choice['attribs'] as $attrib) {
90114                        if ($attrib{0} != '?') {
90115                            $ret &= $this->_tagHasNoAttribs($choice['tag'],
90116                                $context);
90117                            continue 2;
90118                        }
90119                    }
90120                }
90121                foreach ($choice['attribs'] as $attrib) {
90122                    if ($attrib{0} != '?') {
90123                        if (!isset($tag['attribs'][$attrib])) {
90124                            $ret &= $this->_tagMissingAttribute($choice['tag'],
90125                                $attrib, $context);
90126                        }
90127                    }
90128                }
90129            }
90130            return $ret;
90131        }
90132        return true;
90133    }
90134
90135    function _processStructure($key)
90136    {
90137        $ret = array();
90138        if (count($pieces = explode('|', $key)) > 1) {
90139            $ret['choices'] = array();
90140            foreach ($pieces as $piece) {
90141                $ret['choices'][] = $this->_processStructure($piece);
90142            }
90143            return $ret;
90144        }
90145        $multi = $key{0};
90146        if ($multi == '+' || $multi == '*') {
90147            $ret['multiple'] = $key{0};
90148            $key = substr($key, 1);
90149        }
90150        if (count($attrs = explode('->', $key)) > 1) {
90151            $ret['tag'] = array_shift($attrs);
90152            $ret['attribs'] = $attrs;
90153        } else {
90154            $ret['tag'] = $key;
90155        }
90156        return $ret;
90157    }
90158
90159    function _validateStabilityVersion()
90160    {
90161        $structure = array('release', 'api');
90162        $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], '<version>');
90163        $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], '<stability>');
90164        if ($a) {
90165            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90166                  $this->_packageInfo['version']['release'])) {
90167                $this->_invalidVersion('release', $this->_packageInfo['version']['release']);
90168            }
90169            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90170                  $this->_packageInfo['version']['api'])) {
90171                $this->_invalidVersion('api', $this->_packageInfo['version']['api']);
90172            }
90173            if (!in_array($this->_packageInfo['stability']['release'],
90174                  array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) {
90175                $this->_invalidState('release', $this->_packageInfo['stability']['release']);
90176            }
90177            if (!in_array($this->_packageInfo['stability']['api'],
90178                  array('devel', 'alpha', 'beta', 'stable'))) {
90179                $this->_invalidState('api', $this->_packageInfo['stability']['api']);
90180            }
90181        }
90182    }
90183
90184    function _validateMaintainers()
90185    {
90186        $structure =
90187            array(
90188                'name',
90189                'user',
90190                'email',
90191                'active',
90192            );
90193        foreach (array('lead', 'developer', 'contributor', 'helper') as $type) {
90194            if (!isset($this->_packageInfo[$type])) {
90195                continue;
90196            }
90197            if (isset($this->_packageInfo[$type][0])) {
90198                foreach ($this->_packageInfo[$type] as $lead) {
90199                    $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>');
90200                }
90201            } else {
90202                $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type],
90203                    '<' . $type . '>');
90204            }
90205        }
90206    }
90207
90208    function _validatePhpDep($dep, $installcondition = false)
90209    {
90210        $structure = array(
90211            'min',
90212            '*max',
90213            '*exclude',
90214        );
90215        $type = $installcondition ? '<installcondition><php>' : '<dependencies><required><php>';
90216        $this->_stupidSchemaValidate($structure, $dep, $type);
90217        if (isset($dep['min'])) {
90218            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
90219                  $dep['min'])) {
90220                $this->_invalidVersion($type . '<min>', $dep['min']);
90221            }
90222        }
90223        if (isset($dep['max'])) {
90224            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
90225                  $dep['max'])) {
90226                $this->_invalidVersion($type . '<max>', $dep['max']);
90227            }
90228        }
90229        if (isset($dep['exclude'])) {
90230            if (!is_array($dep['exclude'])) {
90231                $dep['exclude'] = array($dep['exclude']);
90232            }
90233            foreach ($dep['exclude'] as $exclude) {
90234                if (!preg_match(
90235                     '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
90236                     $exclude)) {
90237                    $this->_invalidVersion($type . '<exclude>', $exclude);
90238                }
90239            }
90240        }
90241    }
90242
90243    function _validatePearinstallerDep($dep)
90244    {
90245        $structure = array(
90246            'min',
90247            '*max',
90248            '*recommended',
90249            '*exclude',
90250        );
90251        $this->_stupidSchemaValidate($structure, $dep, '<dependencies><required><pearinstaller>');
90252        if (isset($dep['min'])) {
90253            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90254                  $dep['min'])) {
90255                $this->_invalidVersion('<dependencies><required><pearinstaller><min>',
90256                    $dep['min']);
90257            }
90258        }
90259        if (isset($dep['max'])) {
90260            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90261                  $dep['max'])) {
90262                $this->_invalidVersion('<dependencies><required><pearinstaller><max>',
90263                    $dep['max']);
90264            }
90265        }
90266        if (isset($dep['recommended'])) {
90267            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90268                  $dep['recommended'])) {
90269                $this->_invalidVersion('<dependencies><required><pearinstaller><recommended>',
90270                    $dep['recommended']);
90271            }
90272        }
90273        if (isset($dep['exclude'])) {
90274            if (!is_array($dep['exclude'])) {
90275                $dep['exclude'] = array($dep['exclude']);
90276            }
90277            foreach ($dep['exclude'] as $exclude) {
90278                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90279                      $exclude)) {
90280                    $this->_invalidVersion('<dependencies><required><pearinstaller><exclude>',
90281                        $exclude);
90282                }
90283            }
90284        }
90285    }
90286
90287    function _validatePackageDep($dep, $group, $type = '<package>')
90288    {
90289        if (isset($dep['uri'])) {
90290            if (isset($dep['conflicts'])) {
90291                $structure = array(
90292                    'name',
90293                    'uri',
90294                    'conflicts',
90295                    '*providesextension',
90296                );
90297            } else {
90298                $structure = array(
90299                    'name',
90300                    'uri',
90301                    '*providesextension',
90302                );
90303            }
90304        } else {
90305            if (isset($dep['conflicts'])) {
90306                $structure = array(
90307                    'name',
90308                    'channel',
90309                    '*min',
90310                    '*max',
90311                    '*exclude',
90312                    'conflicts',
90313                    '*providesextension',
90314                );
90315            } else {
90316                $structure = array(
90317                    'name',
90318                    'channel',
90319                    '*min',
90320                    '*max',
90321                    '*recommended',
90322                    '*exclude',
90323                    '*nodefault',
90324                    '*providesextension',
90325                );
90326            }
90327        }
90328        if (isset($dep['name'])) {
90329            $type .= '<name>' . $dep['name'] . '</name>';
90330        }
90331        $this->_stupidSchemaValidate($structure, $dep, '<dependencies>' . $group . $type);
90332        if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) ||
90333              isset($dep['recommended']) || isset($dep['exclude']))) {
90334            $this->_uriDepsCannotHaveVersioning('<dependencies>' . $group . $type);
90335        }
90336        if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') {
90337            $this->_DepchannelCannotBeUri('<dependencies>' . $group . $type);
90338        }
90339        if (isset($dep['min'])) {
90340            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90341                  $dep['min'])) {
90342                $this->_invalidVersion('<dependencies>' . $group . $type . '<min>', $dep['min']);
90343            }
90344        }
90345        if (isset($dep['max'])) {
90346            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90347                  $dep['max'])) {
90348                $this->_invalidVersion('<dependencies>' . $group . $type . '<max>', $dep['max']);
90349            }
90350        }
90351        if (isset($dep['recommended'])) {
90352            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90353                  $dep['recommended'])) {
90354                $this->_invalidVersion('<dependencies>' . $group . $type . '<recommended>',
90355                    $dep['recommended']);
90356            }
90357        }
90358        if (isset($dep['exclude'])) {
90359            if (!is_array($dep['exclude'])) {
90360                $dep['exclude'] = array($dep['exclude']);
90361            }
90362            foreach ($dep['exclude'] as $exclude) {
90363                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90364                      $exclude)) {
90365                    $this->_invalidVersion('<dependencies>' . $group . $type . '<exclude>',
90366                        $exclude);
90367                }
90368            }
90369        }
90370    }
90371
90372    function _validateSubpackageDep($dep, $group)
90373    {
90374        $this->_validatePackageDep($dep, $group, '<subpackage>');
90375        if (isset($dep['providesextension'])) {
90376            $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : '');
90377        }
90378        if (isset($dep['conflicts'])) {
90379            $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : '');
90380        }
90381    }
90382
90383    function _validateExtensionDep($dep, $group = false, $installcondition = false)
90384    {
90385        if (isset($dep['conflicts'])) {
90386            $structure = array(
90387                'name',
90388                '*min',
90389                '*max',
90390                '*exclude',
90391                'conflicts',
90392            );
90393        } else {
90394            $structure = array(
90395                'name',
90396                '*min',
90397                '*max',
90398                '*recommended',
90399                '*exclude',
90400            );
90401        }
90402        if ($installcondition) {
90403            $type = '<installcondition><extension>';
90404        } else {
90405            $type = '<dependencies>' . $group . '<extension>';
90406        }
90407        if (isset($dep['name'])) {
90408            $type .= '<name>' . $dep['name'] . '</name>';
90409        }
90410        $this->_stupidSchemaValidate($structure, $dep, $type);
90411        if (isset($dep['min'])) {
90412            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90413                  $dep['min'])) {
90414                $this->_invalidVersion(substr($type, 1) . '<min', $dep['min']);
90415            }
90416        }
90417        if (isset($dep['max'])) {
90418            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90419                  $dep['max'])) {
90420                $this->_invalidVersion(substr($type, 1) . '<max', $dep['max']);
90421            }
90422        }
90423        if (isset($dep['recommended'])) {
90424            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90425                  $dep['recommended'])) {
90426                $this->_invalidVersion(substr($type, 1) . '<recommended', $dep['recommended']);
90427            }
90428        }
90429        if (isset($dep['exclude'])) {
90430            if (!is_array($dep['exclude'])) {
90431                $dep['exclude'] = array($dep['exclude']);
90432            }
90433            foreach ($dep['exclude'] as $exclude) {
90434                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90435                      $exclude)) {
90436                    $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
90437                }
90438            }
90439        }
90440    }
90441
90442    function _validateOsDep($dep, $installcondition = false)
90443    {
90444        $structure = array(
90445            'name',
90446            '*conflicts',
90447        );
90448        $type = $installcondition ? '<installcondition><os>' : '<dependencies><required><os>';
90449        if ($this->_stupidSchemaValidate($structure, $dep, $type)) {
90450            if ($dep['name'] == '*') {
90451                if (array_key_exists('conflicts', $dep)) {
90452                    $this->_cannotConflictWithAllOs($type);
90453                }
90454            }
90455        }
90456    }
90457
90458    function _validateArchDep($dep, $installcondition = false)
90459    {
90460        $structure = array(
90461            'pattern',
90462            '*conflicts',
90463        );
90464        $type = $installcondition ? '<installcondition><arch>' : '<dependencies><required><arch>';
90465        $this->_stupidSchemaValidate($structure, $dep, $type);
90466    }
90467
90468    function _validateInstallConditions($cond, $release)
90469    {
90470        $structure = array(
90471            '*php',
90472            '*extension',
90473            '*os',
90474            '*arch',
90475        );
90476        if (!$this->_stupidSchemaValidate($structure,
90477              $cond, $release)) {
90478            return false;
90479        }
90480        foreach (array('php', 'extension', 'os', 'arch') as $type) {
90481            if (isset($cond[$type])) {
90482                $iter = $cond[$type];
90483                if (!is_array($iter) || !isset($iter[0])) {
90484                    $iter = array($iter);
90485                }
90486                foreach ($iter as $package) {
90487                    if ($type == 'extension') {
90488                        $this->{"_validate{$type}Dep"}($package, false, true);
90489                    } else {
90490                        $this->{"_validate{$type}Dep"}($package, true);
90491                    }
90492                }
90493            }
90494        }
90495    }
90496
90497    function _validateDependencies()
90498    {
90499        $structure = array(
90500            'required',
90501            '*optional',
90502            '*group->name->hint'
90503        );
90504        if (!$this->_stupidSchemaValidate($structure,
90505              $this->_packageInfo['dependencies'], '<dependencies>')) {
90506            return false;
90507        }
90508        foreach (array('required', 'optional') as $simpledep) {
90509            if (isset($this->_packageInfo['dependencies'][$simpledep])) {
90510                if ($simpledep == 'optional') {
90511                    $structure = array(
90512                        '*package',
90513                        '*subpackage',
90514                        '*extension',
90515                    );
90516                } else {
90517                    $structure = array(
90518                        'php',
90519                        'pearinstaller',
90520                        '*package',
90521                        '*subpackage',
90522                        '*extension',
90523                        '*os',
90524                        '*arch',
90525                    );
90526                }
90527                if ($this->_stupidSchemaValidate($structure,
90528                      $this->_packageInfo['dependencies'][$simpledep],
90529                      "<dependencies><$simpledep>")) {
90530                    foreach (array('package', 'subpackage', 'extension') as $type) {
90531                        if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
90532                            $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
90533                            if (!isset($iter[0])) {
90534                                $iter = array($iter);
90535                            }
90536                            foreach ($iter as $package) {
90537                                if ($type != 'extension') {
90538                                    if (isset($package['uri'])) {
90539                                        if (isset($package['channel'])) {
90540                                            $this->_UrlOrChannel($type,
90541                                                $package['name']);
90542                                        }
90543                                    } else {
90544                                        if (!isset($package['channel'])) {
90545                                            $this->_NoChannel($type, $package['name']);
90546                                        }
90547                                    }
90548                                }
90549                                $this->{"_validate{$type}Dep"}($package, "<$simpledep>");
90550                            }
90551                        }
90552                    }
90553                    if ($simpledep == 'optional') {
90554                        continue;
90555                    }
90556                    foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) {
90557                        if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
90558                            $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
90559                            if (!isset($iter[0])) {
90560                                $iter = array($iter);
90561                            }
90562                            foreach ($iter as $package) {
90563                                $this->{"_validate{$type}Dep"}($package);
90564                            }
90565                        }
90566                    }
90567                }
90568            }
90569        }
90570        if (isset($this->_packageInfo['dependencies']['group'])) {
90571            $groups = $this->_packageInfo['dependencies']['group'];
90572            if (!isset($groups[0])) {
90573                $groups = array($groups);
90574            }
90575            $structure = array(
90576                '*package',
90577                '*subpackage',
90578                '*extension',
90579            );
90580            foreach ($groups as $group) {
90581                if ($this->_stupidSchemaValidate($structure, $group, '<group>')) {
90582                    if (!PEAR_Validate::validGroupName($group['attribs']['name'])) {
90583                        $this->_invalidDepGroupName($group['attribs']['name']);
90584                    }
90585                    foreach (array('package', 'subpackage', 'extension') as $type) {
90586                        if (isset($group[$type])) {
90587                            $iter = $group[$type];
90588                            if (!isset($iter[0])) {
90589                                $iter = array($iter);
90590                            }
90591                            foreach ($iter as $package) {
90592                                if ($type != 'extension') {
90593                                    if (isset($package['uri'])) {
90594                                        if (isset($package['channel'])) {
90595                                            $this->_UrlOrChannelGroup($type,
90596                                                $package['name'],
90597                                                $group['name']);
90598                                        }
90599                                    } else {
90600                                        if (!isset($package['channel'])) {
90601                                            $this->_NoChannelGroup($type,
90602                                                $package['name'],
90603                                                $group['name']);
90604                                        }
90605                                    }
90606                                }
90607                                $this->{"_validate{$type}Dep"}($package, '<group name="' .
90608                                    $group['attribs']['name'] . '">');
90609                            }
90610                        }
90611                    }
90612                }
90613            }
90614        }
90615    }
90616
90617    function _validateCompatible()
90618    {
90619        $compat = $this->_packageInfo['compatible'];
90620        if (!isset($compat[0])) {
90621            $compat = array($compat);
90622        }
90623        $required = array('name', 'channel', 'min', 'max', '*exclude');
90624        foreach ($compat as $package) {
90625            $type = '<compatible>';
90626            if (is_array($package) && array_key_exists('name', $package)) {
90627                $type .= '<name>' . $package['name'] . '</name>';
90628            }
90629            $this->_stupidSchemaValidate($required, $package, $type);
90630            if (is_array($package) && array_key_exists('min', $package)) {
90631                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90632                      $package['min'])) {
90633                    $this->_invalidVersion(substr($type, 1) . '<min', $package['min']);
90634                }
90635            }
90636            if (is_array($package) && array_key_exists('max', $package)) {
90637                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90638                      $package['max'])) {
90639                    $this->_invalidVersion(substr($type, 1) . '<max', $package['max']);
90640                }
90641            }
90642            if (is_array($package) && array_key_exists('exclude', $package)) {
90643                if (!is_array($package['exclude'])) {
90644                    $package['exclude'] = array($package['exclude']);
90645                }
90646                foreach ($package['exclude'] as $exclude) {
90647                    if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
90648                          $exclude)) {
90649                        $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
90650                    }
90651                }
90652            }
90653        }
90654    }
90655
90656    function _validateBundle($list)
90657    {
90658        if (!is_array($list) || !isset($list['bundledpackage'])) {
90659            return $this->_NoBundledPackages();
90660        }
90661        if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) {
90662            return $this->_AtLeast2BundledPackages();
90663        }
90664        foreach ($list['bundledpackage'] as $package) {
90665            if (!is_string($package)) {
90666                $this->_bundledPackagesMustBeFilename();
90667            }
90668        }
90669    }
90670
90671    function _validateFilelist($list = false, $allowignore = false, $dirs = '')
90672    {
90673        $iscontents = false;
90674        if (!$list) {
90675            $iscontents = true;
90676            $list = $this->_packageInfo['contents'];
90677            if (isset($this->_packageInfo['bundle'])) {
90678                return $this->_validateBundle($list);
90679            }
90680        }
90681        if ($allowignore) {
90682            $struc = array(
90683                '*install->name->as',
90684                '*ignore->name'
90685            );
90686        } else {
90687            $struc = array(
90688                '*dir->name->?baseinstalldir',
90689                '*file->name->role->?baseinstalldir->?md5sum'
90690            );
90691            if (isset($list['dir']) && isset($list['file'])) {
90692                // stave off validation errors without requiring a set order.
90693                $_old = $list;
90694                if (isset($list['attribs'])) {
90695                    $list = array('attribs' => $_old['attribs']);
90696                }
90697                $list['dir'] = $_old['dir'];
90698                $list['file'] = $_old['file'];
90699            }
90700        }
90701        if (!isset($list['attribs']) || !isset($list['attribs']['name'])) {
90702            $unknown = $allowignore ? '<filelist>' : '<dir name="*unknown*">';
90703            $dirname = $iscontents ? '<contents>' : $unknown;
90704        } else {
90705            $dirname = '<dir name="' . $list['attribs']['name'] . '">';
90706            if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
90707                          str_replace('\\', '/', $list['attribs']['name']))) {
90708                // file contains .. parent directory or . cur directory
90709                $this->_invalidDirName($list['attribs']['name']);
90710            }
90711        }
90712        $res = $this->_stupidSchemaValidate($struc, $list, $dirname);
90713        if ($allowignore && $res) {
90714            $ignored_or_installed = array();
90715            $this->_pf->getFilelist();
90716            $fcontents = $this->_pf->getContents();
90717            $filelist = array();
90718            if (!isset($fcontents['dir']['file'][0])) {
90719                $fcontents['dir']['file'] = array($fcontents['dir']['file']);
90720            }
90721            foreach ($fcontents['dir']['file'] as $file) {
90722                $filelist[$file['attribs']['name']] = true;
90723            }
90724            if (isset($list['install'])) {
90725                if (!isset($list['install'][0])) {
90726                    $list['install'] = array($list['install']);
90727                }
90728                foreach ($list['install'] as $file) {
90729                    if (!isset($filelist[$file['attribs']['name']])) {
90730                        $this->_notInContents($file['attribs']['name'], 'install');
90731                        continue;
90732                    }
90733                    if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
90734                        $this->_multipleInstallAs($file['attribs']['name']);
90735                    }
90736                    if (!isset($ignored_or_installed[$file['attribs']['name']])) {
90737                        $ignored_or_installed[$file['attribs']['name']] = array();
90738                    }
90739                    $ignored_or_installed[$file['attribs']['name']][] = 1;
90740                    if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
90741                                  str_replace('\\', '/', $file['attribs']['as']))) {
90742                        // file contains .. parent directory or . cur directory references
90743                        $this->_invalidFileInstallAs($file['attribs']['name'],
90744                            $file['attribs']['as']);
90745                    }
90746                }
90747            }
90748            if (isset($list['ignore'])) {
90749                if (!isset($list['ignore'][0])) {
90750                    $list['ignore'] = array($list['ignore']);
90751                }
90752                foreach ($list['ignore'] as $file) {
90753                    if (!isset($filelist[$file['attribs']['name']])) {
90754                        $this->_notInContents($file['attribs']['name'], 'ignore');
90755                        continue;
90756                    }
90757                    if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
90758                        $this->_ignoreAndInstallAs($file['attribs']['name']);
90759                    }
90760                }
90761            }
90762        }
90763        if (!$allowignore && isset($list['file'])) {
90764            if (is_string($list['file'])) {
90765                $this->_oldStyleFileNotAllowed();
90766                return false;
90767            }
90768            if (!isset($list['file'][0])) {
90769                // single file
90770                $list['file'] = array($list['file']);
90771            }
90772            foreach ($list['file'] as $i => $file)
90773            {
90774                if (isset($file['attribs']) && isset($file['attribs']['name'])) {
90775                    if ($file['attribs']['name']{0} == '.' &&
90776                          $file['attribs']['name']{1} == '/') {
90777                        // name is something like "./doc/whatever.txt"
90778                        $this->_invalidFileName($file['attribs']['name'], $dirname);
90779                    }
90780                    if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
90781                                  str_replace('\\', '/', $file['attribs']['name']))) {
90782                        // file contains .. parent directory or . cur directory
90783                        $this->_invalidFileName($file['attribs']['name'], $dirname);
90784                    }
90785                }
90786                if (isset($file['attribs']) && isset($file['attribs']['role'])) {
90787                    if (!$this->_validateRole($file['attribs']['role'])) {
90788                        if (isset($this->_packageInfo['usesrole'])) {
90789                            $roles = $this->_packageInfo['usesrole'];
90790                            if (!isset($roles[0])) {
90791                                $roles = array($roles);
90792                            }
90793                            foreach ($roles as $role) {
90794                                if ($role['role'] = $file['attribs']['role']) {
90795                                    $msg = 'This package contains role "%role%" and requires ' .
90796                                        'package "%package%" to be used';
90797                                    if (isset($role['uri'])) {
90798                                        $params = array('role' => $role['role'],
90799                                            'package' => $role['uri']);
90800                                    } else {
90801                                        $params = array('role' => $role['role'],
90802                                            'package' => $this->_pf->_registry->
90803                                            parsedPackageNameToString(array('package' =>
90804                                                $role['package'], 'channel' => $role['channel']),
90805                                                true));
90806                                    }
90807                                    $this->_stack->push('_mustInstallRole', 'error', $params, $msg);
90808                                }
90809                            }
90810                        }
90811                        $this->_invalidFileRole($file['attribs']['name'],
90812                            $dirname, $file['attribs']['role']);
90813                    }
90814                }
90815                if (!isset($file['attribs'])) {
90816                    continue;
90817                }
90818                $save = $file['attribs'];
90819                if ($dirs) {
90820                    $save['name'] = $dirs . '/' . $save['name'];
90821                }
90822                unset($file['attribs']);
90823                if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks
90824                    foreach ($file as $task => $value) {
90825                        if ($tagClass = $this->_pf->getTask($task)) {
90826                            if (!is_array($value) || !isset($value[0])) {
90827                                $value = array($value);
90828                            }
90829                            foreach ($value as $v) {
90830                                $ret = call_user_func(array($tagClass, 'validateXml'),
90831                                    $this->_pf, $v, $this->_pf->_config, $save);
90832                                if (is_array($ret)) {
90833                                    $this->_invalidTask($task, $ret, isset($save['name']) ?
90834                                        $save['name'] : '');
90835                                }
90836                            }
90837                        } else {
90838                            if (isset($this->_packageInfo['usestask'])) {
90839                                $roles = $this->_packageInfo['usestask'];
90840                                if (!isset($roles[0])) {
90841                                    $roles = array($roles);
90842                                }
90843                                foreach ($roles as $role) {
90844                                    if ($role['task'] = $task) {
90845                                        $msg = 'This package contains task "%task%" and requires ' .
90846                                            'package "%package%" to be used';
90847                                        if (isset($role['uri'])) {
90848                                            $params = array('task' => $role['task'],
90849                                                'package' => $role['uri']);
90850                                        } else {
90851                                            $params = array('task' => $role['task'],
90852                                                'package' => $this->_pf->_registry->
90853                                                parsedPackageNameToString(array('package' =>
90854                                                    $role['package'], 'channel' => $role['channel']),
90855                                                    true));
90856                                        }
90857                                        $this->_stack->push('_mustInstallTask', 'error',
90858                                            $params, $msg);
90859                                    }
90860                                }
90861                            }
90862                            $this->_unknownTask($task, $save['name']);
90863                        }
90864                    }
90865                }
90866            }
90867        }
90868        if (isset($list['ignore'])) {
90869            if (!$allowignore) {
90870                $this->_ignoreNotAllowed('ignore');
90871            }
90872        }
90873        if (isset($list['install'])) {
90874            if (!$allowignore) {
90875                $this->_ignoreNotAllowed('install');
90876            }
90877        }
90878        if (isset($list['file'])) {
90879            if ($allowignore) {
90880                $this->_fileNotAllowed('file');
90881            }
90882        }
90883        if (isset($list['dir'])) {
90884            if ($allowignore) {
90885                $this->_fileNotAllowed('dir');
90886            } else {
90887                if (!isset($list['dir'][0])) {
90888                    $list['dir'] = array($list['dir']);
90889                }
90890                foreach ($list['dir'] as $dir) {
90891                    if (isset($dir['attribs']) && isset($dir['attribs']['name'])) {
90892                        if ($dir['attribs']['name'] == '/' ||
90893                              !isset($this->_packageInfo['contents']['dir']['dir'])) {
90894                            // always use nothing if the filelist has already been flattened
90895                            $newdirs = '';
90896                        } elseif ($dirs == '') {
90897                            $newdirs = $dir['attribs']['name'];
90898                        } else {
90899                            $newdirs = $dirs . '/' . $dir['attribs']['name'];
90900                        }
90901                    } else {
90902                        $newdirs = $dirs;
90903                    }
90904                    $this->_validateFilelist($dir, $allowignore, $newdirs);
90905                }
90906            }
90907        }
90908    }
90909
90910    function _validateRelease()
90911    {
90912        if (isset($this->_packageInfo['phprelease'])) {
90913            $release = 'phprelease';
90914            if (isset($this->_packageInfo['providesextension'])) {
90915                $this->_cannotProvideExtension($release);
90916            }
90917            if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
90918                $this->_cannotHaveSrcpackage($release);
90919            }
90920            $releases = $this->_packageInfo['phprelease'];
90921            if (!is_array($releases)) {
90922                return true;
90923            }
90924            if (!isset($releases[0])) {
90925                $releases = array($releases);
90926            }
90927            foreach ($releases as $rel) {
90928                $this->_stupidSchemaValidate(array(
90929                    '*installconditions',
90930                    '*filelist',
90931                ), $rel, '<phprelease>');
90932            }
90933        }
90934        foreach (array('', 'zend') as $prefix) {
90935            $releasetype = $prefix . 'extsrcrelease';
90936            if (isset($this->_packageInfo[$releasetype])) {
90937                $release = $releasetype;
90938                if (!isset($this->_packageInfo['providesextension'])) {
90939                    $this->_mustProvideExtension($release);
90940                }
90941                if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
90942                    $this->_cannotHaveSrcpackage($release);
90943                }
90944                $releases = $this->_packageInfo[$releasetype];
90945                if (!is_array($releases)) {
90946                    return true;
90947                }
90948                if (!isset($releases[0])) {
90949                    $releases = array($releases);
90950                }
90951                foreach ($releases as $rel) {
90952                    $this->_stupidSchemaValidate(array(
90953                        '*installconditions',
90954                        '*configureoption->name->prompt->?default',
90955                        '*binarypackage',
90956                        '*filelist',
90957                    ), $rel, '<' . $releasetype . '>');
90958                    if (isset($rel['binarypackage'])) {
90959                        if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) {
90960                            $rel['binarypackage'] = array($rel['binarypackage']);
90961                        }
90962                        foreach ($rel['binarypackage'] as $bin) {
90963                            if (!is_string($bin)) {
90964                                $this->_binaryPackageMustBePackagename();
90965                            }
90966                        }
90967                    }
90968                }
90969            }
90970            $releasetype = 'extbinrelease';
90971            if (isset($this->_packageInfo[$releasetype])) {
90972                $release = $releasetype;
90973                if (!isset($this->_packageInfo['providesextension'])) {
90974                    $this->_mustProvideExtension($release);
90975                }
90976                if (isset($this->_packageInfo['channel']) &&
90977                      !isset($this->_packageInfo['srcpackage'])) {
90978                    $this->_mustSrcPackage($release);
90979                }
90980                if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) {
90981                    $this->_mustSrcuri($release);
90982                }
90983                $releases = $this->_packageInfo[$releasetype];
90984                if (!is_array($releases)) {
90985                    return true;
90986                }
90987                if (!isset($releases[0])) {
90988                    $releases = array($releases);
90989                }
90990                foreach ($releases as $rel) {
90991                    $this->_stupidSchemaValidate(array(
90992                        '*installconditions',
90993                        '*filelist',
90994                    ), $rel, '<' . $releasetype . '>');
90995                }
90996            }
90997        }
90998        if (isset($this->_packageInfo['bundle'])) {
90999            $release = 'bundle';
91000            if (isset($this->_packageInfo['providesextension'])) {
91001                $this->_cannotProvideExtension($release);
91002            }
91003            if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
91004                $this->_cannotHaveSrcpackage($release);
91005            }
91006            $releases = $this->_packageInfo['bundle'];
91007            if (!is_array($releases) || !isset($releases[0])) {
91008                $releases = array($releases);
91009            }
91010            foreach ($releases as $rel) {
91011                $this->_stupidSchemaValidate(array(
91012                    '*installconditions',
91013                    '*filelist',
91014                ), $rel, '<bundle>');
91015            }
91016        }
91017        foreach ($releases as $rel) {
91018            if (is_array($rel) && array_key_exists('installconditions', $rel)) {
91019                $this->_validateInstallConditions($rel['installconditions'],
91020                    "<$release><installconditions>");
91021            }
91022            if (is_array($rel) && array_key_exists('filelist', $rel)) {
91023                if ($rel['filelist']) {
91024
91025                    $this->_validateFilelist($rel['filelist'], true);
91026                }
91027            }
91028        }
91029    }
91030
91031    /**
91032     * This is here to allow role extension through plugins
91033     * @param string
91034     */
91035    function _validateRole($role)
91036    {
91037        return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType()));
91038    }
91039
91040    function _pearVersionTooLow($version)
91041    {
91042        $this->_stack->push(__FUNCTION__, 'error',
91043            array('version' => $version),
91044            'This package.xml requires PEAR version %version% to parse properly, we are ' .
91045            'version 1.9.4');
91046    }
91047
91048    function _invalidTagOrder($oktags, $actual, $root)
91049    {
91050        $this->_stack->push(__FUNCTION__, 'error',
91051            array('oktags' => $oktags, 'actual' => $actual, 'root' => $root),
91052            'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"');
91053    }
91054
91055    function _ignoreNotAllowed($type)
91056    {
91057        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
91058            '<%type%> is not allowed inside global <contents>, only inside ' .
91059            '<phprelease>/<extbinrelease>/<zendextbinrelease>, use <dir> and <file> only');
91060    }
91061
91062    function _fileNotAllowed($type)
91063    {
91064        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
91065            '<%type%> is not allowed inside release <filelist>, only inside ' .
91066            '<contents>, use <ignore> and <install> only');
91067    }
91068
91069    function _oldStyleFileNotAllowed()
91070    {
91071        $this->_stack->push(__FUNCTION__, 'error', array(),
91072            'Old-style <file>name</file> is not allowed.  Use' .
91073            '<file name="name" role="role"/>');
91074    }
91075
91076    function _tagMissingAttribute($tag, $attr, $context)
91077    {
91078        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
91079            'attribute' => $attr, 'context' => $context),
91080            'tag <%tag%> in context "%context%" has no attribute "%attribute%"');
91081    }
91082
91083    function _tagHasNoAttribs($tag, $context)
91084    {
91085        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
91086            'context' => $context),
91087            'tag <%tag%> has no attributes in context "%context%"');
91088    }
91089
91090    function _invalidInternalStructure()
91091    {
91092        $this->_stack->push(__FUNCTION__, 'exception', array(),
91093            'internal array was not generated by compatible parser, or extreme parser error, cannot continue');
91094    }
91095
91096    function _invalidFileRole($file, $dir, $role)
91097    {
91098        $this->_stack->push(__FUNCTION__, 'error', array(
91099            'file' => $file, 'dir' => $dir, 'role' => $role,
91100            'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())),
91101            'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%');
91102    }
91103
91104    function _invalidFileName($file, $dir)
91105    {
91106        $this->_stack->push(__FUNCTION__, 'error', array(
91107            'file' => $file),
91108            'File "%file%" in directory "%dir%" cannot begin with "./" or contain ".."');
91109    }
91110
91111    function _invalidFileInstallAs($file, $as)
91112    {
91113        $this->_stack->push(__FUNCTION__, 'error', array(
91114            'file' => $file, 'as' => $as),
91115            'File "%file%" <install as="%as%"/> cannot contain "./" or contain ".."');
91116    }
91117
91118    function _invalidDirName($dir)
91119    {
91120        $this->_stack->push(__FUNCTION__, 'error', array(
91121            'dir' => $file),
91122            'Directory "%dir%" cannot begin with "./" or contain ".."');
91123    }
91124
91125    function _filelistCannotContainFile($filelist)
91126    {
91127        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
91128            '<%tag%> can only contain <dir>, contains <file>.  Use ' .
91129            '<dir name="/"> as the first dir element');
91130    }
91131
91132    function _filelistMustContainDir($filelist)
91133    {
91134        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
91135            '<%tag%> must contain <dir>.  Use <dir name="/"> as the ' .
91136            'first dir element');
91137    }
91138
91139    function _tagCannotBeEmpty($tag)
91140    {
91141        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
91142            '<%tag%> cannot be empty (<%tag%/>)');
91143    }
91144
91145    function _UrlOrChannel($type, $name)
91146    {
91147        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
91148            'name' => $name),
91149            'Required dependency <%type%> "%name%" can have either url OR ' .
91150            'channel attributes, and not both');
91151    }
91152
91153    function _NoChannel($type, $name)
91154    {
91155        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
91156            'name' => $name),
91157            'Required dependency <%type%> "%name%" must have either url OR ' .
91158            'channel attributes');
91159    }
91160
91161    function _UrlOrChannelGroup($type, $name, $group)
91162    {
91163        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
91164            'name' => $name, 'group' => $group),
91165            'Group "%group%" dependency <%type%> "%name%" can have either url OR ' .
91166            'channel attributes, and not both');
91167    }
91168
91169    function _NoChannelGroup($type, $name, $group)
91170    {
91171        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
91172            'name' => $name, 'group' => $group),
91173            'Group "%group%" dependency <%type%> "%name%" must have either url OR ' .
91174            'channel attributes');
91175    }
91176
91177    function _unknownChannel($channel)
91178    {
91179        $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel),
91180            'Unknown channel "%channel%"');
91181    }
91182
91183    function _noPackageVersion()
91184    {
91185        $this->_stack->push(__FUNCTION__, 'error', array(),
91186            'package.xml <package> tag has no version attribute, or version is not 2.0');
91187    }
91188
91189    function _NoBundledPackages()
91190    {
91191        $this->_stack->push(__FUNCTION__, 'error', array(),
91192            'No <bundledpackage> tag was found in <contents>, required for bundle packages');
91193    }
91194
91195    function _AtLeast2BundledPackages()
91196    {
91197        $this->_stack->push(__FUNCTION__, 'error', array(),
91198            'At least 2 packages must be bundled in a bundle package');
91199    }
91200
91201    function _ChannelOrUri($name)
91202    {
91203        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
91204            'Bundled package "%name%" can have either a uri or a channel, not both');
91205    }
91206
91207    function _noChildTag($child, $tag)
91208    {
91209        $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag),
91210            'Tag <%tag%> is missing child tag <%child%>');
91211    }
91212
91213    function _invalidVersion($type, $value)
91214    {
91215        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value),
91216            'Version type <%type%> is not a valid version (%value%)');
91217    }
91218
91219    function _invalidState($type, $value)
91220    {
91221        $states = array('stable', 'beta', 'alpha', 'devel');
91222        if ($type != 'api') {
91223            $states[] = 'snapshot';
91224        }
91225        if (strtolower($value) == 'rc') {
91226            $this->_stack->push(__FUNCTION__, 'error',
91227                array('version' => $this->_packageInfo['version']['release']),
91228                'RC is not a state, it is a version postfix, try %version%RC1, stability beta');
91229        }
91230        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value,
91231            'types' => $states),
91232            'Stability type <%type%> is not a valid stability (%value%), must be one of ' .
91233            '%types%');
91234    }
91235
91236    function _invalidTask($task, $ret, $file)
91237    {
91238        switch ($ret[0]) {
91239            case PEAR_TASK_ERROR_MISSING_ATTRIB :
91240                $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file);
91241                $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%';
91242            break;
91243            case PEAR_TASK_ERROR_NOATTRIBS :
91244                $info = array('task' => $task, 'file' => $file);
91245                $msg = 'task <%task%> has no attributes in file %file%';
91246            break;
91247            case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE :
91248                $info = array('attrib' => $ret[1], 'values' => $ret[3],
91249                    'was' => $ret[2], 'task' => $task, 'file' => $file);
91250                $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '.
91251                    'in file %file%, expecting one of "%values%"';
91252            break;
91253            case PEAR_TASK_ERROR_INVALID :
91254                $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file);
91255                $msg = 'task <%task%> in file %file% is invalid because of "%reason%"';
91256            break;
91257        }
91258        $this->_stack->push(__FUNCTION__, 'error', $info, $msg);
91259    }
91260
91261    function _unknownTask($task, $file)
91262    {
91263        $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file),
91264            'Unknown task "%task%" passed in file <file name="%file%">');
91265    }
91266
91267    function _subpackageCannotProvideExtension($name)
91268    {
91269        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
91270            'Subpackage dependency "%name%" cannot use <providesextension>, ' .
91271            'only package dependencies can use this tag');
91272    }
91273
91274    function _subpackagesCannotConflict($name)
91275    {
91276        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
91277            'Subpackage dependency "%name%" cannot use <conflicts/>, ' .
91278            'only package dependencies can use this tag');
91279    }
91280
91281    function _cannotProvideExtension($release)
91282    {
91283        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
91284            '<%release%> packages cannot use <providesextension>, only extbinrelease, extsrcrelease, zendextsrcrelease, and zendextbinrelease can provide a PHP extension');
91285    }
91286
91287    function _mustProvideExtension($release)
91288    {
91289        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
91290            '<%release%> packages must use <providesextension> to indicate which PHP extension is provided');
91291    }
91292
91293    function _cannotHaveSrcpackage($release)
91294    {
91295        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
91296            '<%release%> packages cannot specify a source code package, only extension binaries may use the <srcpackage> tag');
91297    }
91298
91299    function _mustSrcPackage($release)
91300    {
91301        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
91302            '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcpackage>');
91303    }
91304
91305    function _mustSrcuri($release)
91306    {
91307        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
91308            '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcuri>');
91309    }
91310
91311    function _uriDepsCannotHaveVersioning($type)
91312    {
91313        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
91314            '%type%: dependencies with a <uri> tag cannot have any versioning information');
91315    }
91316
91317    function _conflictingDepsCannotHaveVersioning($type)
91318    {
91319        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
91320            '%type%: conflicting dependencies cannot have versioning info, use <exclude> to ' .
91321            'exclude specific versions of a dependency');
91322    }
91323
91324    function _DepchannelCannotBeUri($type)
91325    {
91326        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
91327            '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' .
91328            'dependencies only');
91329    }
91330
91331    function _bundledPackagesMustBeFilename()
91332    {
91333        $this->_stack->push(__FUNCTION__, 'error', array(),
91334            '<bundledpackage> tags must contain only the filename of a package release ' .
91335            'in the bundle');
91336    }
91337
91338    function _binaryPackageMustBePackagename()
91339    {
91340        $this->_stack->push(__FUNCTION__, 'error', array(),
91341            '<binarypackage> tags must contain the name of a package that is ' .
91342            'a compiled version of this extsrc/zendextsrc package');
91343    }
91344
91345    function _fileNotFound($file)
91346    {
91347        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
91348            'File "%file%" in package.xml does not exist');
91349    }
91350
91351    function _notInContents($file, $tag)
91352    {
91353        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag),
91354            '<%tag% name="%file%"> is invalid, file is not in <contents>');
91355    }
91356
91357    function _cannotValidateNoPathSet()
91358    {
91359        $this->_stack->push(__FUNCTION__, 'error', array(),
91360            'Cannot validate files, no path to package file is set (use setPackageFile())');
91361    }
91362
91363    function _usesroletaskMustHaveChannelOrUri($role, $tag)
91364    {
91365        $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
91366            '<%tag%> for role "%role%" must contain either <uri>, or <channel> and <package>');
91367    }
91368
91369    function _usesroletaskMustHavePackage($role, $tag)
91370    {
91371        $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
91372            '<%tag%> for role "%role%" must contain <package>');
91373    }
91374
91375    function _usesroletaskMustHaveRoleTask($tag, $type)
91376    {
91377        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type),
91378            '<%tag%> must contain <%type%> defining the %type% to be used');
91379    }
91380
91381    function _cannotConflictWithAllOs($type)
91382    {
91383        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
91384            '%tag% cannot conflict with all OSes');
91385    }
91386
91387    function _invalidDepGroupName($name)
91388    {
91389        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
91390            'Invalid dependency group name "%name%"');
91391    }
91392
91393    function _multipleToplevelDirNotAllowed()
91394    {
91395        $this->_stack->push(__FUNCTION__, 'error', array(),
91396            'Multiple top-level <dir> tags are not allowed.  Enclose them ' .
91397                'in a <dir name="/">');
91398    }
91399
91400    function _multipleInstallAs($file)
91401    {
91402        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
91403            'Only one <install> tag is allowed for file "%file%"');
91404    }
91405
91406    function _ignoreAndInstallAs($file)
91407    {
91408        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
91409            'Cannot have both <ignore> and <install> tags for file "%file%"');
91410    }
91411
91412    function _analyzeBundledPackages()
91413    {
91414        if (!$this->_isValid) {
91415            return false;
91416        }
91417        if (!$this->_pf->getPackageType() == 'bundle') {
91418            return false;
91419        }
91420        if (!isset($this->_pf->_packageFile)) {
91421            return false;
91422        }
91423        $dir_prefix = dirname($this->_pf->_packageFile);
91424        $common = new PEAR_Common;
91425        $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
91426            array($common, 'log');
91427        $info = $this->_pf->getContents();
91428        $info = $info['bundledpackage'];
91429        if (!is_array($info)) {
91430            $info = array($info);
91431        }
91432        $pkg = &new PEAR_PackageFile($this->_pf->_config);
91433        foreach ($info as $package) {
91434            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) {
91435                $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package);
91436                $this->_isValid = 0;
91437                continue;
91438            }
91439            call_user_func_array($log, array(1, "Analyzing bundled package $package"));
91440            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
91441            $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package,
91442                PEAR_VALIDATE_NORMAL);
91443            PEAR::popErrorHandling();
91444            if (PEAR::isError($ret)) {
91445                call_user_func_array($log, array(0, "ERROR: package $package is not a valid " .
91446                    'package'));
91447                $inf = $ret->getUserInfo();
91448                if (is_array($inf)) {
91449                    foreach ($inf as $err) {
91450                        call_user_func_array($log, array(1, $err['message']));
91451                    }
91452                }
91453                return false;
91454            }
91455        }
91456        return true;
91457    }
91458
91459    function _analyzePhpFiles()
91460    {
91461        if (!$this->_isValid) {
91462            return false;
91463        }
91464        if (!isset($this->_pf->_packageFile)) {
91465            $this->_cannotValidateNoPathSet();
91466            return false;
91467        }
91468        $dir_prefix = dirname($this->_pf->_packageFile);
91469        $common = new PEAR_Common;
91470        $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
91471            array(&$common, 'log');
91472        $info = $this->_pf->getContents();
91473        if (!$info || !isset($info['dir']['file'])) {
91474            $this->_tagCannotBeEmpty('contents><dir');
91475            return false;
91476        }
91477        $info = $info['dir']['file'];
91478        if (isset($info['attribs'])) {
91479            $info = array($info);
91480        }
91481        $provides = array();
91482        foreach ($info as $fa) {
91483            $fa = $fa['attribs'];
91484            $file = $fa['name'];
91485            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
91486                $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file);
91487                $this->_isValid = 0;
91488                continue;
91489            }
91490            if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) {
91491                call_user_func_array($log, array(1, "Analyzing $file"));
91492                $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
91493                if ($srcinfo) {
91494                    $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo));
91495                }
91496            }
91497        }
91498        $this->_packageName = $pn = $this->_pf->getPackage();
91499        $pnl = strlen($pn);
91500        foreach ($provides as $key => $what) {
91501            if (isset($what['explicit']) || !$what) {
91502                // skip conformance checks if the provides entry is
91503                // specified in the package.xml file
91504                continue;
91505            }
91506            extract($what);
91507            if ($type == 'class') {
91508                if (!strncasecmp($name, $pn, $pnl)) {
91509                    continue;
91510                }
91511                $this->_stack->push(__FUNCTION__, 'warning',
91512                    array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
91513                    'in %file%: %type% "%name%" not prefixed with package name "%package%"');
91514            } elseif ($type == 'function') {
91515                if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
91516                    continue;
91517                }
91518                $this->_stack->push(__FUNCTION__, 'warning',
91519                    array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
91520                    'in %file%: %type% "%name%" not prefixed with package name "%package%"');
91521            }
91522        }
91523        return $this->_isValid;
91524    }
91525
91526    /**
91527     * Analyze the source code of the given PHP file
91528     *
91529     * @param  string Filename of the PHP file
91530     * @param  boolean whether to analyze $file as the file contents
91531     * @return mixed
91532     */
91533    function analyzeSourceCode($file, $string = false)
91534    {
91535        if (!function_exists("token_get_all")) {
91536            $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
91537                'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer');
91538            return false;
91539        }
91540
91541        if (!defined('T_DOC_COMMENT')) {
91542            define('T_DOC_COMMENT', T_COMMENT);
91543        }
91544
91545        if (!defined('T_INTERFACE')) {
91546            define('T_INTERFACE', -1);
91547        }
91548
91549        if (!defined('T_IMPLEMENTS')) {
91550            define('T_IMPLEMENTS', -1);
91551        }
91552
91553        if ($string) {
91554            $contents = $file;
91555        } else {
91556            if (!$fp = @fopen($file, "r")) {
91557                return false;
91558            }
91559            fclose($fp);
91560            $contents = file_get_contents($file);
91561        }
91562
91563        // Silence this function so we can catch PHP Warnings and show our own custom message
91564        $tokens = @token_get_all($contents);
91565        if (isset($php_errormsg)) {
91566            if (isset($this->_stack)) {
91567                $pn = $this->_pf->getPackage();
91568                $this->_stack->push(__FUNCTION__, 'warning',
91569                        array('file' => $file, 'package' => $pn),
91570                        'in %file%: Could not process file for unkown reasons,' .
91571                        ' possibly a PHP parse error in %file% from %package%');
91572            }
91573        }
91574/*
91575        for ($i = 0; $i < sizeof($tokens); $i++) {
91576            @list($token, $data) = $tokens[$i];
91577            if (is_string($token)) {
91578                var_dump($token);
91579            } else {
91580                print token_name($token) . ' ';
91581                var_dump(rtrim($data));
91582            }
91583        }
91584*/
91585        $look_for = 0;
91586        $paren_level = 0;
91587        $bracket_level = 0;
91588        $brace_level = 0;
91589        $lastphpdoc = '';
91590        $current_class = '';
91591        $current_interface = '';
91592        $current_class_level = -1;
91593        $current_function = '';
91594        $current_function_level = -1;
91595        $declared_classes = array();
91596        $declared_interfaces = array();
91597        $declared_functions = array();
91598        $declared_methods = array();
91599        $used_classes = array();
91600        $used_functions = array();
91601        $extends = array();
91602        $implements = array();
91603        $nodeps = array();
91604        $inquote = false;
91605        $interface = false;
91606        for ($i = 0; $i < sizeof($tokens); $i++) {
91607            if (is_array($tokens[$i])) {
91608                list($token, $data) = $tokens[$i];
91609            } else {
91610                $token = $tokens[$i];
91611                $data = '';
91612            }
91613
91614            if ($inquote) {
91615                if ($token != '"' && $token != T_END_HEREDOC) {
91616                    continue;
91617                } else {
91618                    $inquote = false;
91619                    continue;
91620                }
91621            }
91622
91623            switch ($token) {
91624                case T_WHITESPACE :
91625                    continue;
91626                case ';':
91627                    if ($interface) {
91628                        $current_function = '';
91629                        $current_function_level = -1;
91630                    }
91631                    break;
91632                case '"':
91633                case T_START_HEREDOC:
91634                    $inquote = true;
91635                    break;
91636                case T_CURLY_OPEN:
91637                case T_DOLLAR_OPEN_CURLY_BRACES:
91638                case '{': $brace_level++; continue 2;
91639                case '}':
91640                    $brace_level--;
91641                    if ($current_class_level == $brace_level) {
91642                        $current_class = '';
91643                        $current_class_level = -1;
91644                    }
91645                    if ($current_function_level == $brace_level) {
91646                        $current_function = '';
91647                        $current_function_level = -1;
91648                    }
91649                    continue 2;
91650                case '[': $bracket_level++; continue 2;
91651                case ']': $bracket_level--; continue 2;
91652                case '(': $paren_level++;   continue 2;
91653                case ')': $paren_level--;   continue 2;
91654                case T_INTERFACE:
91655                    $interface = true;
91656                case T_CLASS:
91657                    if (($current_class_level != -1) || ($current_function_level != -1)) {
91658                        if (isset($this->_stack)) {
91659                            $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
91660                            'Parser error: invalid PHP found in file "%file%"');
91661                        } else {
91662                            PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
91663                                PEAR_COMMON_ERROR_INVALIDPHP);
91664                        }
91665
91666                        return false;
91667                    }
91668                case T_FUNCTION:
91669                case T_NEW:
91670                case T_EXTENDS:
91671                case T_IMPLEMENTS:
91672                    $look_for = $token;
91673                    continue 2;
91674                case T_STRING:
91675                    if (version_compare(zend_version(), '2.0', '<')) {
91676                        if (in_array(strtolower($data),
91677                            array('public', 'private', 'protected', 'abstract',
91678                                  'interface', 'implements', 'throw')
91679                                 )
91680                        ) {
91681                            if (isset($this->_stack)) {
91682                                $this->_stack->push(__FUNCTION__, 'warning', array(
91683                                    'file' => $file),
91684                                    'Error, PHP5 token encountered in %file%,' .
91685                                    ' analysis should be in PHP5');
91686                            } else {
91687                                PEAR::raiseError('Error: PHP5 token encountered in ' . $file .
91688                                    'packaging should be done in PHP 5');
91689                                return false;
91690                            }
91691                        }
91692                    }
91693
91694                    if ($look_for == T_CLASS) {
91695                        $current_class = $data;
91696                        $current_class_level = $brace_level;
91697                        $declared_classes[] = $current_class;
91698                    } elseif ($look_for == T_INTERFACE) {
91699                        $current_interface = $data;
91700                        $current_class_level = $brace_level;
91701                        $declared_interfaces[] = $current_interface;
91702                    } elseif ($look_for == T_IMPLEMENTS) {
91703                        $implements[$current_class] = $data;
91704                    } elseif ($look_for == T_EXTENDS) {
91705                        $extends[$current_class] = $data;
91706                    } elseif ($look_for == T_FUNCTION) {
91707                        if ($current_class) {
91708                            $current_function = "$current_class::$data";
91709                            $declared_methods[$current_class][] = $data;
91710                        } elseif ($current_interface) {
91711                            $current_function = "$current_interface::$data";
91712                            $declared_methods[$current_interface][] = $data;
91713                        } else {
91714                            $current_function = $data;
91715                            $declared_functions[] = $current_function;
91716                        }
91717
91718                        $current_function_level = $brace_level;
91719                        $m = array();
91720                    } elseif ($look_for == T_NEW) {
91721                        $used_classes[$data] = true;
91722                    }
91723
91724                    $look_for = 0;
91725                    continue 2;
91726                case T_VARIABLE:
91727                    $look_for = 0;
91728                    continue 2;
91729                case T_DOC_COMMENT:
91730                case T_COMMENT:
91731                    if (preg_match('!^/\*\*\s!', $data)) {
91732                        $lastphpdoc = $data;
91733                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
91734                            $nodeps = array_merge($nodeps, $m[1]);
91735                        }
91736                    }
91737                    continue 2;
91738                case T_DOUBLE_COLON:
91739                    $token = $tokens[$i - 1][0];
91740                    if (!($token == T_WHITESPACE || $token == T_STRING || $token == T_STATIC)) {
91741                        if (isset($this->_stack)) {
91742                            $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file),
91743                                'Parser error: invalid PHP found in file "%file%"');
91744                        } else {
91745                            PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
91746                                PEAR_COMMON_ERROR_INVALIDPHP);
91747                        }
91748
91749                        return false;
91750                    }
91751
91752                    $class = $tokens[$i - 1][1];
91753                    if (strtolower($class) != 'parent') {
91754                        $used_classes[$class] = true;
91755                    }
91756
91757                    continue 2;
91758            }
91759        }
91760
91761        return array(
91762            "source_file" => $file,
91763            "declared_classes" => $declared_classes,
91764            "declared_interfaces" => $declared_interfaces,
91765            "declared_methods" => $declared_methods,
91766            "declared_functions" => $declared_functions,
91767            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
91768            "inheritance" => $extends,
91769            "implements" => $implements,
91770        );
91771    }
91772
91773    /**
91774     * Build a "provides" array from data returned by
91775     * analyzeSourceCode().  The format of the built array is like
91776     * this:
91777     *
91778     *  array(
91779     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
91780     *    ...
91781     *  )
91782     *
91783     *
91784     * @param array $srcinfo array with information about a source file
91785     * as returned by the analyzeSourceCode() method.
91786     *
91787     * @return void
91788     *
91789     * @access private
91790     *
91791     */
91792    function _buildProvidesArray($srcinfo)
91793    {
91794        if (!$this->_isValid) {
91795            return array();
91796        }
91797
91798        $providesret = array();
91799        $file        = basename($srcinfo['source_file']);
91800        $pn          = isset($this->_pf) ? $this->_pf->getPackage() : '';
91801        $pnl         = strlen($pn);
91802        foreach ($srcinfo['declared_classes'] as $class) {
91803            $key = "class;$class";
91804            if (isset($providesret[$key])) {
91805                continue;
91806            }
91807
91808            $providesret[$key] =
91809                array('file'=> $file, 'type' => 'class', 'name' => $class);
91810            if (isset($srcinfo['inheritance'][$class])) {
91811                $providesret[$key]['extends'] =
91812                    $srcinfo['inheritance'][$class];
91813            }
91814        }
91815
91816        foreach ($srcinfo['declared_methods'] as $class => $methods) {
91817            foreach ($methods as $method) {
91818                $function = "$class::$method";
91819                $key = "function;$function";
91820                if ($method{0} == '_' || !strcasecmp($method, $class) ||
91821                    isset($providesret[$key])) {
91822                    continue;
91823                }
91824
91825                $providesret[$key] =
91826                    array('file'=> $file, 'type' => 'function', 'name' => $function);
91827            }
91828        }
91829
91830        foreach ($srcinfo['declared_functions'] as $function) {
91831            $key = "function;$function";
91832            if ($function{0} == '_' || isset($providesret[$key])) {
91833                continue;
91834            }
91835
91836            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
91837                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
91838            }
91839
91840            $providesret[$key] =
91841                array('file'=> $file, 'type' => 'function', 'name' => $function);
91842        }
91843
91844        return $providesret;
91845    }
91846}<?php
91847/**
91848 * PEAR_Packager for generating releases
91849 *
91850 * PHP versions 4 and 5
91851 *
91852 * @category   pear
91853 * @package    PEAR
91854 * @author     Stig Bakken <ssb@php.net>
91855 * @author     Tomas V. V. Cox <cox@idecnet.com>
91856 * @author     Greg Beaver <cellog@php.net>
91857 * @copyright  1997-2009 The Authors
91858 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
91859 * @version    CVS: $Id: Packager.php 313023 2011-07-06 19:17:11Z dufuz $
91860 * @link       http://pear.php.net/package/PEAR
91861 * @since      File available since Release 0.1
91862 */
91863
91864/**
91865 * base class
91866 */
91867require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
91868require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
91869require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
91870
91871/**
91872 * Administration class used to make a PEAR release tarball.
91873 *
91874 * @category   pear
91875 * @package    PEAR
91876 * @author     Greg Beaver <cellog@php.net>
91877 * @copyright  1997-2009 The Authors
91878 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
91879 * @version    Release: 1.9.4
91880 * @link       http://pear.php.net/package/PEAR
91881 * @since      Class available since Release 0.1
91882 */
91883class PEAR_Packager extends PEAR_Common
91884{
91885    /**
91886     * @var PEAR_Registry
91887     */
91888    var $_registry;
91889
91890    function package($pkgfile = null, $compress = true, $pkg2 = null)
91891    {
91892        // {{{ validate supplied package.xml file
91893        if (empty($pkgfile)) {
91894            $pkgfile = 'package.xml';
91895        }
91896
91897        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
91898        $pkg  = &new PEAR_PackageFile($this->config, $this->debug);
91899        $pf   = &$pkg->fromPackageFile($pkgfile, PEAR_VALIDATE_NORMAL);
91900        $main = &$pf;
91901        PEAR::staticPopErrorHandling();
91902        if (PEAR::isError($pf)) {
91903            if (is_array($pf->getUserInfo())) {
91904                foreach ($pf->getUserInfo() as $error) {
91905                    $this->log(0, 'Error: ' . $error['message']);
91906                }
91907            }
91908
91909            $this->log(0, $pf->getMessage());
91910            return $this->raiseError("Cannot package, errors in package file");
91911        }
91912
91913        foreach ($pf->getValidationWarnings() as $warning) {
91914            $this->log(1, 'Warning: ' . $warning['message']);
91915        }
91916
91917        // }}}
91918        if ($pkg2) {
91919            $this->log(0, 'Attempting to process the second package file');
91920            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
91921            $pf2 = &$pkg->fromPackageFile($pkg2, PEAR_VALIDATE_NORMAL);
91922            PEAR::staticPopErrorHandling();
91923            if (PEAR::isError($pf2)) {
91924                if (is_array($pf2->getUserInfo())) {
91925                    foreach ($pf2->getUserInfo() as $error) {
91926                        $this->log(0, 'Error: ' . $error['message']);
91927                    }
91928                }
91929                $this->log(0, $pf2->getMessage());
91930                return $this->raiseError("Cannot package, errors in second package file");
91931            }
91932
91933            foreach ($pf2->getValidationWarnings() as $warning) {
91934                $this->log(1, 'Warning: ' . $warning['message']);
91935            }
91936
91937            if ($pf2->getPackagexmlVersion() == '2.0' ||
91938                  $pf2->getPackagexmlVersion() == '2.1'
91939            ) {
91940                $main  = &$pf2;
91941                $other = &$pf;
91942            } else {
91943                $main  = &$pf;
91944                $other = &$pf2;
91945            }
91946
91947            if ($main->getPackagexmlVersion() != '2.0' &&
91948                  $main->getPackagexmlVersion() != '2.1') {
91949                return PEAR::raiseError('Error: cannot package two package.xml version 1.0, can ' .
91950                    'only package together a package.xml 1.0 and package.xml 2.0');
91951            }
91952
91953            if ($other->getPackagexmlVersion() != '1.0') {
91954                return PEAR::raiseError('Error: cannot package two package.xml version 2.0, can ' .
91955                    'only package together a package.xml 1.0 and package.xml 2.0');
91956            }
91957        }
91958
91959        $main->setLogger($this);
91960        if (!$main->validate(PEAR_VALIDATE_PACKAGING)) {
91961            foreach ($main->getValidationWarnings() as $warning) {
91962                $this->log(0, 'Error: ' . $warning['message']);
91963            }
91964            return $this->raiseError("Cannot package, errors in package");
91965        }
91966
91967        foreach ($main->getValidationWarnings() as $warning) {
91968            $this->log(1, 'Warning: ' . $warning['message']);
91969        }
91970
91971        if ($pkg2) {
91972            $other->setLogger($this);
91973            $a = false;
91974            if (!$other->validate(PEAR_VALIDATE_NORMAL) || $a = !$main->isEquivalent($other)) {
91975                foreach ($other->getValidationWarnings() as $warning) {
91976                    $this->log(0, 'Error: ' . $warning['message']);
91977                }
91978
91979                foreach ($main->getValidationWarnings() as $warning) {
91980                    $this->log(0, 'Error: ' . $warning['message']);
91981                }
91982
91983                if ($a) {
91984                    return $this->raiseError('The two package.xml files are not equivalent!');
91985                }
91986
91987                return $this->raiseError("Cannot package, errors in package");
91988            }
91989
91990            foreach ($other->getValidationWarnings() as $warning) {
91991                $this->log(1, 'Warning: ' . $warning['message']);
91992            }
91993
91994            $gen = &$main->getDefaultGenerator();
91995            $tgzfile = $gen->toTgz2($this, $other, $compress);
91996            if (PEAR::isError($tgzfile)) {
91997                return $tgzfile;
91998            }
91999
92000            $dest_package = basename($tgzfile);
92001            $pkgdir       = dirname($pkgfile);
92002
92003            // TAR the Package -------------------------------------------
92004            $this->log(1, "Package $dest_package done");
92005            if (file_exists("$pkgdir/CVS/Root")) {
92006                $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion());
92007                $cvstag = "RELEASE_$cvsversion";
92008                $this->log(1, 'Tag the released code with "pear cvstag ' .
92009                    $main->getPackageFile() . '"');
92010                $this->log(1, "(or set the CVS tag $cvstag by hand)");
92011            } elseif (file_exists("$pkgdir/.svn")) {
92012                $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion());
92013                $svntag = $pf->getName() . "-$svnversion";
92014                $this->log(1, 'Tag the released code with "pear svntag ' .
92015                    $main->getPackageFile() . '"');
92016                $this->log(1, "(or set the SVN tag $svntag by hand)");
92017            }
92018        } else { // this branch is executed for single packagefile packaging
92019            $gen = &$pf->getDefaultGenerator();
92020            $tgzfile = $gen->toTgz($this, $compress);
92021            if (PEAR::isError($tgzfile)) {
92022                $this->log(0, $tgzfile->getMessage());
92023                return $this->raiseError("Cannot package, errors in package");
92024            }
92025
92026            $dest_package = basename($tgzfile);
92027            $pkgdir       = dirname($pkgfile);
92028
92029            // TAR the Package -------------------------------------------
92030            $this->log(1, "Package $dest_package done");
92031            if (file_exists("$pkgdir/CVS/Root")) {
92032                $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion());
92033                $cvstag = "RELEASE_$cvsversion";
92034                $this->log(1, "Tag the released code with `pear cvstag $pkgfile'");
92035                $this->log(1, "(or set the CVS tag $cvstag by hand)");
92036            } elseif (file_exists("$pkgdir/.svn")) {
92037                $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion());
92038                $svntag = $pf->getName() . "-$svnversion";
92039                $this->log(1, "Tag the released code with `pear svntag $pkgfile'");
92040                $this->log(1, "(or set the SVN tag $svntag by hand)");
92041            }
92042        }
92043
92044        return $dest_package;
92045    }
92046}<?php
92047/**
92048 * PEAR_Registry
92049 *
92050 * PHP versions 4 and 5
92051 *
92052 * @category   pear
92053 * @package    PEAR
92054 * @author     Stig Bakken <ssb@php.net>
92055 * @author     Tomas V. V. Cox <cox@idecnet.com>
92056 * @author     Greg Beaver <cellog@php.net>
92057 * @copyright  1997-2009 The Authors
92058 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
92059 * @version    CVS: $Id: Registry.php 313023 2011-07-06 19:17:11Z dufuz $
92060 * @link       http://pear.php.net/package/PEAR
92061 * @since      File available since Release 0.1
92062 */
92063
92064/**
92065 * for PEAR_Error
92066 */
92067require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
92068require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/DependencyDB.php';
92069
92070define('PEAR_REGISTRY_ERROR_LOCK',         -2);
92071define('PEAR_REGISTRY_ERROR_FORMAT',       -3);
92072define('PEAR_REGISTRY_ERROR_FILE',         -4);
92073define('PEAR_REGISTRY_ERROR_CONFLICT',     -5);
92074define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6);
92075
92076/**
92077 * Administration class used to maintain the installed package database.
92078 * @category   pear
92079 * @package    PEAR
92080 * @author     Stig Bakken <ssb@php.net>
92081 * @author     Tomas V. V. Cox <cox@idecnet.com>
92082 * @author     Greg Beaver <cellog@php.net>
92083 * @copyright  1997-2009 The Authors
92084 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
92085 * @version    Release: 1.9.4
92086 * @link       http://pear.php.net/package/PEAR
92087 * @since      Class available since Release 1.4.0a1
92088 */
92089class PEAR_Registry extends PEAR
92090{
92091    /**
92092     * File containing all channel information.
92093     * @var string
92094     */
92095    var $channels = '';
92096
92097    /** Directory where registry files are stored.
92098     * @var string
92099     */
92100    var $statedir = '';
92101
92102    /** File where the file map is stored
92103     * @var string
92104     */
92105    var $filemap = '';
92106
92107    /** Directory where registry files for channels are stored.
92108     * @var string
92109     */
92110    var $channelsdir = '';
92111
92112    /** Name of file used for locking the registry
92113     * @var string
92114     */
92115    var $lockfile = '';
92116
92117    /** File descriptor used during locking
92118     * @var resource
92119     */
92120    var $lock_fp = null;
92121
92122    /** Mode used during locking
92123     * @var int
92124     */
92125    var $lock_mode = 0; // XXX UNUSED
92126
92127    /** Cache of package information.  Structure:
92128     * array(
92129     *   'package' => array('id' => ... ),
92130     *   ... )
92131     * @var array
92132     */
92133    var $pkginfo_cache = array();
92134
92135    /** Cache of file map.  Structure:
92136     * array( '/path/to/file' => 'package', ... )
92137     * @var array
92138     */
92139    var $filemap_cache = array();
92140
92141    /**
92142     * @var false|PEAR_ChannelFile
92143     */
92144    var $_pearChannel;
92145
92146    /**
92147     * @var false|PEAR_ChannelFile
92148     */
92149    var $_peclChannel;
92150
92151    /**
92152     * @var false|PEAR_ChannelFile
92153     */
92154    var $_docChannel;
92155
92156    /**
92157     * @var PEAR_DependencyDB
92158     */
92159    var $_dependencyDB;
92160
92161    /**
92162     * @var PEAR_Config
92163     */
92164    var $_config;
92165
92166    /**
92167     * PEAR_Registry constructor.
92168     *
92169     * @param string (optional) PEAR install directory (for .php files)
92170     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if
92171     *        default values are not desired.  Only used the very first time a PEAR
92172     *        repository is initialized
92173     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if
92174     *        default values are not desired.  Only used the very first time a PEAR
92175     *        repository is initialized
92176     *
92177     * @access public
92178     */
92179    function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false,
92180                           $pecl_channel = false)
92181    {
92182        parent::PEAR();
92183        $this->setInstallDir($pear_install_dir);
92184        $this->_pearChannel = $pear_channel;
92185        $this->_peclChannel = $pecl_channel;
92186        $this->_config      = false;
92187    }
92188
92189    function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR)
92190    {
92191        $ds = DIRECTORY_SEPARATOR;
92192        $this->install_dir = $pear_install_dir;
92193        $this->channelsdir = $pear_install_dir.$ds.'.channels';
92194        $this->statedir    = $pear_install_dir.$ds.'.registry';
92195        $this->filemap     = $pear_install_dir.$ds.'.filemap';
92196        $this->lockfile    = $pear_install_dir.$ds.'.lock';
92197    }
92198
92199    function hasWriteAccess()
92200    {
92201        if (!file_exists($this->install_dir)) {
92202            $dir = $this->install_dir;
92203            while ($dir && $dir != '.') {
92204                $olddir = $dir;
92205                $dir    = dirname($dir);
92206                if ($dir != '.' && file_exists($dir)) {
92207                    if (is_writeable($dir)) {
92208                        return true;
92209                    }
92210
92211                    return false;
92212                }
92213
92214                if ($dir == $olddir) { // this can happen in safe mode
92215                    return @is_writable($dir);
92216                }
92217            }
92218
92219            return false;
92220        }
92221
92222        return is_writeable($this->install_dir);
92223    }
92224
92225    function setConfig(&$config, $resetInstallDir = true)
92226    {
92227        $this->_config = &$config;
92228        if ($resetInstallDir) {
92229            $this->setInstallDir($config->get('php_dir'));
92230        }
92231    }
92232
92233    function _initializeChannelDirs()
92234    {
92235        static $running = false;
92236        if (!$running) {
92237            $running = true;
92238            $ds = DIRECTORY_SEPARATOR;
92239            if (!is_dir($this->channelsdir) ||
92240                  !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
92241                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
92242                  !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
92243                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
92244                if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
92245                    $pear_channel = $this->_pearChannel;
92246                    if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) {
92247                        if (!class_exists('PEAR_ChannelFile')) {
92248                            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
92249                        }
92250
92251                        $pear_channel = new PEAR_ChannelFile;
92252                        $pear_channel->setAlias('pear');
92253                        $pear_channel->setServer('pear.php.net');
92254                        $pear_channel->setSummary('PHP Extension and Application Repository');
92255                        $pear_channel->setDefaultPEARProtocols();
92256                        $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
92257                        $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
92258                        $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
92259                        //$pear_channel->setBaseURL('REST1.4', 'http://pear.php.net/rest/');
92260                    } else {
92261                        $pear_channel->setServer('pear.php.net');
92262                        $pear_channel->setAlias('pear');
92263                    }
92264
92265                    $pear_channel->validate();
92266                    $this->_addChannel($pear_channel);
92267                }
92268
92269                if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) {
92270                    $pecl_channel = $this->_peclChannel;
92271                    if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) {
92272                        if (!class_exists('PEAR_ChannelFile')) {
92273                            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
92274                        }
92275
92276                        $pecl_channel = new PEAR_ChannelFile;
92277                        $pecl_channel->setAlias('pecl');
92278                        $pecl_channel->setServer('pecl.php.net');
92279                        $pecl_channel->setSummary('PHP Extension Community Library');
92280                        $pecl_channel->setDefaultPEARProtocols();
92281                        $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
92282                        $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
92283                        $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
92284                    } else {
92285                        $pecl_channel->setServer('pecl.php.net');
92286                        $pecl_channel->setAlias('pecl');
92287                    }
92288
92289                    $pecl_channel->validate();
92290                    $this->_addChannel($pecl_channel);
92291                }
92292
92293                if (!file_exists($this->channelsdir . $ds . 'doc.php.net.reg')) {
92294                    $doc_channel = $this->_docChannel;
92295                    if (!is_a($doc_channel, 'PEAR_ChannelFile') || !$doc_channel->validate()) {
92296                        if (!class_exists('PEAR_ChannelFile')) {
92297                            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
92298                        }
92299
92300                        $doc_channel = new PEAR_ChannelFile;
92301                        $doc_channel->setAlias('phpdocs');
92302                        $doc_channel->setServer('doc.php.net');
92303                        $doc_channel->setSummary('PHP Documentation Team');
92304                        $doc_channel->setDefaultPEARProtocols();
92305                        $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
92306                        $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
92307                        $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
92308                    } else {
92309                        $doc_channel->setServer('doc.php.net');
92310                        $doc_channel->setAlias('doc');
92311                    }
92312
92313                    $doc_channel->validate();
92314                    $this->_addChannel($doc_channel);
92315                }
92316
92317                if (!file_exists($this->channelsdir . $ds . '__uri.reg')) {
92318                    if (!class_exists('PEAR_ChannelFile')) {
92319                        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
92320                    }
92321
92322                    $private = new PEAR_ChannelFile;
92323                    $private->setName('__uri');
92324                    $private->setDefaultPEARProtocols();
92325                    $private->setBaseURL('REST1.0', '****');
92326                    $private->setSummary('Pseudo-channel for static packages');
92327                    $this->_addChannel($private);
92328                }
92329                $this->_rebuildFileMap();
92330            }
92331
92332            $running = false;
92333        }
92334    }
92335
92336    function _initializeDirs()
92337    {
92338        $ds = DIRECTORY_SEPARATOR;
92339        // XXX Compatibility code should be removed in the future
92340        // rename all registry files if any to lowercase
92341        if (!OS_WINDOWS && file_exists($this->statedir) && is_dir($this->statedir) &&
92342              $handle = opendir($this->statedir)) {
92343            $dest = $this->statedir . $ds;
92344            while (false !== ($file = readdir($handle))) {
92345                if (preg_match('/^.*[A-Z].*\.reg\\z/', $file)) {
92346                    rename($dest . $file, $dest . strtolower($file));
92347                }
92348            }
92349            closedir($handle);
92350        }
92351
92352        $this->_initializeChannelDirs();
92353        if (!file_exists($this->filemap)) {
92354            $this->_rebuildFileMap();
92355        }
92356        $this->_initializeDepDB();
92357    }
92358
92359    function _initializeDepDB()
92360    {
92361        if (!isset($this->_dependencyDB)) {
92362            static $initializing = false;
92363            if (!$initializing) {
92364                $initializing = true;
92365                if (!$this->_config) { // never used?
92366                    $file = OS_WINDOWS ? 'pear.ini' : '.pearrc';
92367                    $this->_config = &new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR .
92368                        $file);
92369                    $this->_config->setRegistry($this);
92370                    $this->_config->set('php_dir', $this->install_dir);
92371                }
92372
92373                $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
92374                if (PEAR::isError($this->_dependencyDB)) {
92375                    // attempt to recover by removing the dep db
92376                    if (file_exists($this->_config->get('php_dir', null, 'pear.php.net') .
92377                        DIRECTORY_SEPARATOR . '.depdb')) {
92378                        @unlink($this->_config->get('php_dir', null, 'pear.php.net') .
92379                            DIRECTORY_SEPARATOR . '.depdb');
92380                    }
92381
92382                    $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
92383                    if (PEAR::isError($this->_dependencyDB)) {
92384                        echo $this->_dependencyDB->getMessage();
92385                        echo 'Unrecoverable error';
92386                        exit(1);
92387                    }
92388                }
92389
92390                $initializing = false;
92391            }
92392        }
92393    }
92394
92395    /**
92396     * PEAR_Registry destructor.  Makes sure no locks are forgotten.
92397     *
92398     * @access private
92399     */
92400    function _PEAR_Registry()
92401    {
92402        parent::_PEAR();
92403        if (is_resource($this->lock_fp)) {
92404            $this->_unlock();
92405        }
92406    }
92407
92408    /**
92409     * Make sure the directory where we keep registry files exists.
92410     *
92411     * @return bool TRUE if directory exists, FALSE if it could not be
92412     * created
92413     *
92414     * @access private
92415     */
92416    function _assertStateDir($channel = false)
92417    {
92418        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
92419            return $this->_assertChannelStateDir($channel);
92420        }
92421
92422        static $init = false;
92423        if (!file_exists($this->statedir)) {
92424            if (!$this->hasWriteAccess()) {
92425                return false;
92426            }
92427
92428            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
92429            if (!System::mkdir(array('-p', $this->statedir))) {
92430                return $this->raiseError("could not create directory '{$this->statedir}'");
92431            }
92432            $init = true;
92433        } elseif (!is_dir($this->statedir)) {
92434            return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' .
92435                'it already exists and is not a directory');
92436        }
92437
92438        $ds = DIRECTORY_SEPARATOR;
92439        if (!file_exists($this->channelsdir)) {
92440            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
92441                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
92442                  !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
92443                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
92444                $init = true;
92445            }
92446        } elseif (!is_dir($this->channelsdir)) {
92447            return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' .
92448                'it already exists and is not a directory');
92449        }
92450
92451        if ($init) {
92452            static $running = false;
92453            if (!$running) {
92454                $running = true;
92455                $this->_initializeDirs();
92456                $running = false;
92457                $init = false;
92458            }
92459        } else {
92460            $this->_initializeDepDB();
92461        }
92462
92463        return true;
92464    }
92465
92466    /**
92467     * Make sure the directory where we keep registry files exists for a non-standard channel.
92468     *
92469     * @param string channel name
92470     * @return bool TRUE if directory exists, FALSE if it could not be
92471     * created
92472     *
92473     * @access private
92474     */
92475    function _assertChannelStateDir($channel)
92476    {
92477        $ds = DIRECTORY_SEPARATOR;
92478        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
92479            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
92480                $this->_initializeChannelDirs();
92481            }
92482            return $this->_assertStateDir($channel);
92483        }
92484
92485        $channelDir = $this->_channelDirectoryName($channel);
92486        if (!is_dir($this->channelsdir) ||
92487              !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
92488            $this->_initializeChannelDirs();
92489        }
92490
92491        if (!file_exists($channelDir)) {
92492            if (!$this->hasWriteAccess()) {
92493                return false;
92494            }
92495
92496            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
92497            if (!System::mkdir(array('-p', $channelDir))) {
92498                return $this->raiseError("could not create directory '" . $channelDir .
92499                    "'");
92500            }
92501        } elseif (!is_dir($channelDir)) {
92502            return $this->raiseError("could not create directory '" . $channelDir .
92503                "', already exists and is not a directory");
92504        }
92505
92506        return true;
92507    }
92508
92509    /**
92510     * Make sure the directory where we keep registry files for channels exists
92511     *
92512     * @return bool TRUE if directory exists, FALSE if it could not be
92513     * created
92514     *
92515     * @access private
92516     */
92517    function _assertChannelDir()
92518    {
92519        if (!file_exists($this->channelsdir)) {
92520            if (!$this->hasWriteAccess()) {
92521                return false;
92522            }
92523
92524            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
92525            if (!System::mkdir(array('-p', $this->channelsdir))) {
92526                return $this->raiseError("could not create directory '{$this->channelsdir}'");
92527            }
92528        } elseif (!is_dir($this->channelsdir)) {
92529            return $this->raiseError("could not create directory '{$this->channelsdir}" .
92530                "', it already exists and is not a directory");
92531        }
92532
92533        if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
92534            if (!$this->hasWriteAccess()) {
92535                return false;
92536            }
92537
92538            require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
92539            if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) {
92540                return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'");
92541            }
92542        } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
92543            return $this->raiseError("could not create directory '{$this->channelsdir}" .
92544                "/.alias', it already exists and is not a directory");
92545        }
92546
92547        return true;
92548    }
92549
92550    /**
92551     * Get the name of the file where data for a given package is stored.
92552     *
92553     * @param string channel name, or false if this is a PEAR package
92554     * @param string package name
92555     *
92556     * @return string registry file name
92557     *
92558     * @access public
92559     */
92560    function _packageFileName($package, $channel = false)
92561    {
92562        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
92563            return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR .
92564                strtolower($package) . '.reg';
92565        }
92566
92567        return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
92568    }
92569
92570    /**
92571     * Get the name of the file where data for a given channel is stored.
92572     * @param string channel name
92573     * @return string registry file name
92574     */
92575    function _channelFileName($channel, $noaliases = false)
92576    {
92577        if (!$noaliases) {
92578            if (file_exists($this->_getChannelAliasFileName($channel))) {
92579                $channel = implode('', file($this->_getChannelAliasFileName($channel)));
92580            }
92581        }
92582        return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_',
92583            strtolower($channel)) . '.reg';
92584    }
92585
92586    /**
92587     * @param string
92588     * @return string
92589     */
92590    function _getChannelAliasFileName($alias)
92591    {
92592        return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' .
92593              DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt';
92594    }
92595
92596    /**
92597     * Get the name of a channel from its alias
92598     */
92599    function _getChannelFromAlias($channel)
92600    {
92601        if (!$this->_channelExists($channel)) {
92602            if ($channel == 'pear.php.net') {
92603                return 'pear.php.net';
92604            }
92605
92606            if ($channel == 'pecl.php.net') {
92607                return 'pecl.php.net';
92608            }
92609
92610            if ($channel == 'doc.php.net') {
92611                return 'doc.php.net';
92612            }
92613
92614            if ($channel == '__uri') {
92615                return '__uri';
92616            }
92617
92618            return false;
92619        }
92620
92621        $channel = strtolower($channel);
92622        if (file_exists($this->_getChannelAliasFileName($channel))) {
92623            // translate an alias to an actual channel
92624            return implode('', file($this->_getChannelAliasFileName($channel)));
92625        }
92626
92627        return $channel;
92628    }
92629
92630    /**
92631     * Get the alias of a channel from its alias or its name
92632     */
92633    function _getAlias($channel)
92634    {
92635        if (!$this->_channelExists($channel)) {
92636            if ($channel == 'pear.php.net') {
92637                return 'pear';
92638            }
92639
92640            if ($channel == 'pecl.php.net') {
92641                return 'pecl';
92642            }
92643
92644            if ($channel == 'doc.php.net') {
92645                return 'phpdocs';
92646            }
92647
92648            return false;
92649        }
92650
92651        $channel = $this->_getChannel($channel);
92652        if (PEAR::isError($channel)) {
92653            return $channel;
92654        }
92655
92656        return $channel->getAlias();
92657    }
92658
92659    /**
92660     * Get the name of the file where data for a given package is stored.
92661     *
92662     * @param string channel name, or false if this is a PEAR package
92663     * @param string package name
92664     *
92665     * @return string registry file name
92666     *
92667     * @access public
92668     */
92669    function _channelDirectoryName($channel)
92670    {
92671        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
92672            return $this->statedir;
92673        }
92674
92675        $ch = $this->_getChannelFromAlias($channel);
92676        if (!$ch) {
92677            $ch = $channel;
92678        }
92679
92680        return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' .
92681            str_replace('/', '_', $ch));
92682    }
92683
92684    function _openPackageFile($package, $mode, $channel = false)
92685    {
92686        if (!$this->_assertStateDir($channel)) {
92687            return null;
92688        }
92689
92690        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
92691            return null;
92692        }
92693
92694        $file = $this->_packageFileName($package, $channel);
92695        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
92696            return null;
92697        }
92698
92699        $fp = @fopen($file, $mode);
92700        if (!$fp) {
92701            return null;
92702        }
92703
92704        return $fp;
92705    }
92706
92707    function _closePackageFile($fp)
92708    {
92709        fclose($fp);
92710    }
92711
92712    function _openChannelFile($channel, $mode)
92713    {
92714        if (!$this->_assertChannelDir()) {
92715            return null;
92716        }
92717
92718        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
92719            return null;
92720        }
92721
92722        $file = $this->_channelFileName($channel);
92723        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
92724            return null;
92725        }
92726
92727        $fp = @fopen($file, $mode);
92728        if (!$fp) {
92729            return null;
92730        }
92731
92732        return $fp;
92733    }
92734
92735    function _closeChannelFile($fp)
92736    {
92737        fclose($fp);
92738    }
92739
92740    function _rebuildFileMap()
92741    {
92742        if (!class_exists('PEAR_Installer_Role')) {
92743            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Installer/Role.php';
92744        }
92745
92746        $channels = $this->_listAllPackages();
92747        $files = array();
92748        foreach ($channels as $channel => $packages) {
92749            foreach ($packages as $package) {
92750                $version = $this->_packageInfo($package, 'version', $channel);
92751                $filelist = $this->_packageInfo($package, 'filelist', $channel);
92752                if (!is_array($filelist)) {
92753                    continue;
92754                }
92755
92756                foreach ($filelist as $name => $attrs) {
92757                    if (isset($attrs['attribs'])) {
92758                        $attrs = $attrs['attribs'];
92759                    }
92760
92761                    // it is possible for conflicting packages in different channels to
92762                    // conflict with data files/doc files
92763                    if ($name == 'dirtree') {
92764                        continue;
92765                    }
92766
92767                    if (isset($attrs['role']) && !in_array($attrs['role'],
92768                          PEAR_Installer_Role::getInstallableRoles())) {
92769                        // these are not installed
92770                        continue;
92771                    }
92772
92773                    if (isset($attrs['role']) && !in_array($attrs['role'],
92774                          PEAR_Installer_Role::getBaseinstallRoles())) {
92775                        $attrs['baseinstalldir'] = $package;
92776                    }
92777
92778                    if (isset($attrs['baseinstalldir'])) {
92779                        $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
92780                    } else {
92781                        $file = $name;
92782                    }
92783
92784                    $file = preg_replace(',^/+,', '', $file);
92785                    if ($channel != 'pear.php.net') {
92786                        if (!isset($files[$attrs['role']])) {
92787                            $files[$attrs['role']] = array();
92788                        }
92789                        $files[$attrs['role']][$file] = array(strtolower($channel),
92790                            strtolower($package));
92791                    } else {
92792                        if (!isset($files[$attrs['role']])) {
92793                            $files[$attrs['role']] = array();
92794                        }
92795                        $files[$attrs['role']][$file] = strtolower($package);
92796                    }
92797                }
92798            }
92799        }
92800
92801
92802        $this->_assertStateDir();
92803        if (!$this->hasWriteAccess()) {
92804            return false;
92805        }
92806
92807        $fp = @fopen($this->filemap, 'wb');
92808        if (!$fp) {
92809            return false;
92810        }
92811
92812        $this->filemap_cache = $files;
92813        fwrite($fp, serialize($files));
92814        fclose($fp);
92815        return true;
92816    }
92817
92818    function _readFileMap()
92819    {
92820        if (!file_exists($this->filemap)) {
92821            return array();
92822        }
92823
92824        $fp = @fopen($this->filemap, 'r');
92825        if (!$fp) {
92826            return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg);
92827        }
92828
92829        clearstatcache();
92830        $rt = get_magic_quotes_runtime();
92831        set_magic_quotes_runtime(0);
92832        $fsize = filesize($this->filemap);
92833        fclose($fp);
92834        $data = file_get_contents($this->filemap);
92835        set_magic_quotes_runtime($rt);
92836        $tmp = unserialize($data);
92837        if (!$tmp && $fsize > 7) {
92838            return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
92839        }
92840
92841        $this->filemap_cache = $tmp;
92842        return true;
92843    }
92844
92845    /**
92846     * Lock the registry.
92847     *
92848     * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
92849     *                See flock manual for more information.
92850     *
92851     * @return bool TRUE on success, FALSE if locking failed, or a
92852     *              PEAR error if some other error occurs (such as the
92853     *              lock file not being writable).
92854     *
92855     * @access private
92856     */
92857    function _lock($mode = LOCK_EX)
92858    {
92859        if (stristr(php_uname(), 'Windows 9')) {
92860            return true;
92861        }
92862
92863        if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
92864            // XXX does not check type of lock (LOCK_SH/LOCK_EX)
92865            return true;
92866        }
92867
92868        if (!$this->_assertStateDir()) {
92869            if ($mode == LOCK_EX) {
92870                return $this->raiseError('Registry directory is not writeable by the current user');
92871            }
92872
92873            return true;
92874        }
92875
92876        $open_mode = 'w';
92877        // XXX People reported problems with LOCK_SH and 'w'
92878        if ($mode === LOCK_SH || $mode === LOCK_UN) {
92879            if (!file_exists($this->lockfile)) {
92880                touch($this->lockfile);
92881            }
92882            $open_mode = 'r';
92883        }
92884
92885        if (!is_resource($this->lock_fp)) {
92886            $this->lock_fp = @fopen($this->lockfile, $open_mode);
92887        }
92888
92889        if (!is_resource($this->lock_fp)) {
92890            $this->lock_fp = null;
92891            return $this->raiseError("could not create lock file" .
92892                                     (isset($php_errormsg) ? ": " . $php_errormsg : ""));
92893        }
92894
92895        if (!(int)flock($this->lock_fp, $mode)) {
92896            switch ($mode) {
92897                case LOCK_SH: $str = 'shared';    break;
92898                case LOCK_EX: $str = 'exclusive'; break;
92899                case LOCK_UN: $str = 'unlock';    break;
92900                default:      $str = 'unknown';   break;
92901            }
92902
92903            //is resource at this point, close it on error.
92904            fclose($this->lock_fp);
92905            $this->lock_fp = null;
92906            return $this->raiseError("could not acquire $str lock ($this->lockfile)",
92907                                     PEAR_REGISTRY_ERROR_LOCK);
92908        }
92909
92910        return true;
92911    }
92912
92913    function _unlock()
92914    {
92915        $ret = $this->_lock(LOCK_UN);
92916        if (is_resource($this->lock_fp)) {
92917            fclose($this->lock_fp);
92918        }
92919
92920        $this->lock_fp = null;
92921        return $ret;
92922    }
92923
92924    function _packageExists($package, $channel = false)
92925    {
92926        return file_exists($this->_packageFileName($package, $channel));
92927    }
92928
92929    /**
92930     * Determine whether a channel exists in the registry
92931     *
92932     * @param string Channel name
92933     * @param bool if true, then aliases will be ignored
92934     * @return boolean
92935     */
92936    function _channelExists($channel, $noaliases = false)
92937    {
92938        $a = file_exists($this->_channelFileName($channel, $noaliases));
92939        if (!$a && $channel == 'pear.php.net') {
92940            return true;
92941        }
92942
92943        if (!$a && $channel == 'pecl.php.net') {
92944            return true;
92945        }
92946
92947        if (!$a && $channel == 'doc.php.net') {
92948            return true;
92949        }
92950
92951        return $a;
92952    }
92953
92954    /**
92955     * Determine whether a mirror exists within the deafult channel in the registry
92956     *
92957     * @param string Channel name
92958     * @param string Mirror name
92959     *
92960     * @return boolean
92961     */
92962    function _mirrorExists($channel, $mirror)
92963    {
92964        $data = $this->_channelInfo($channel);
92965        if (!isset($data['servers']['mirror'])) {
92966            return false;
92967        }
92968
92969        foreach ($data['servers']['mirror'] as $m) {
92970            if ($m['attribs']['host'] == $mirror) {
92971                return true;
92972            }
92973        }
92974
92975        return false;
92976    }
92977
92978    /**
92979     * @param PEAR_ChannelFile Channel object
92980     * @param donotuse
92981     * @param string Last-Modified HTTP tag from remote request
92982     * @return boolean|PEAR_Error True on creation, false if it already exists
92983     */
92984    function _addChannel($channel, $update = false, $lastmodified = false)
92985    {
92986        if (!is_a($channel, 'PEAR_ChannelFile')) {
92987            return false;
92988        }
92989
92990        if (!$channel->validate()) {
92991            return false;
92992        }
92993
92994        if (file_exists($this->_channelFileName($channel->getName()))) {
92995            if (!$update) {
92996                return false;
92997            }
92998
92999            $checker = $this->_getChannel($channel->getName());
93000            if (PEAR::isError($checker)) {
93001                return $checker;
93002            }
93003
93004            if ($channel->getAlias() != $checker->getAlias()) {
93005                if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) {
93006                    @unlink($this->_getChannelAliasFileName($checker->getAlias()));
93007                }
93008            }
93009        } else {
93010            if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net', 'doc.php.net'))) {
93011                return false;
93012            }
93013        }
93014
93015        $ret = $this->_assertChannelDir();
93016        if (PEAR::isError($ret)) {
93017            return $ret;
93018        }
93019
93020        $ret = $this->_assertChannelStateDir($channel->getName());
93021        if (PEAR::isError($ret)) {
93022            return $ret;
93023        }
93024
93025        if ($channel->getAlias() != $channel->getName()) {
93026            if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) &&
93027                  $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) {
93028                $channel->setAlias($channel->getName());
93029            }
93030
93031            if (!$this->hasWriteAccess()) {
93032                return false;
93033            }
93034
93035            $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w');
93036            if (!$fp) {
93037                return false;
93038            }
93039
93040            fwrite($fp, $channel->getName());
93041            fclose($fp);
93042        }
93043
93044        if (!$this->hasWriteAccess()) {
93045            return false;
93046        }
93047
93048        $fp = @fopen($this->_channelFileName($channel->getName()), 'wb');
93049        if (!$fp) {
93050            return false;
93051        }
93052
93053        $info = $channel->toArray();
93054        if ($lastmodified) {
93055            $info['_lastmodified'] = $lastmodified;
93056        } else {
93057            $info['_lastmodified'] = date('r');
93058        }
93059
93060        fwrite($fp, serialize($info));
93061        fclose($fp);
93062        return true;
93063    }
93064
93065    /**
93066     * Deletion fails if there are any packages installed from the channel
93067     * @param string|PEAR_ChannelFile channel name
93068     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
93069     */
93070    function _deleteChannel($channel)
93071    {
93072        if (!is_string($channel)) {
93073            if (!is_a($channel, 'PEAR_ChannelFile')) {
93074                return false;
93075            }
93076
93077            if (!$channel->validate()) {
93078                return false;
93079            }
93080            $channel = $channel->getName();
93081        }
93082
93083        if ($this->_getChannelFromAlias($channel) == '__uri') {
93084            return false;
93085        }
93086
93087        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
93088            return false;
93089        }
93090
93091        if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
93092            return false;
93093        }
93094
93095        if (!$this->_channelExists($channel)) {
93096            return false;
93097        }
93098
93099        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
93100            return false;
93101        }
93102
93103        $channel = $this->_getChannelFromAlias($channel);
93104        if ($channel == 'pear.php.net') {
93105            return false;
93106        }
93107
93108        $test = $this->_listChannelPackages($channel);
93109        if (count($test)) {
93110            return false;
93111        }
93112
93113        $test = @rmdir($this->_channelDirectoryName($channel));
93114        if (!$test) {
93115            return false;
93116        }
93117
93118        $file = $this->_getChannelAliasFileName($this->_getAlias($channel));
93119        if (file_exists($file)) {
93120            $test = @unlink($file);
93121            if (!$test) {
93122                return false;
93123            }
93124        }
93125
93126        $file = $this->_channelFileName($channel);
93127        $ret = true;
93128        if (file_exists($file)) {
93129            $ret = @unlink($file);
93130        }
93131
93132        return $ret;
93133    }
93134
93135    /**
93136     * Determine whether a channel exists in the registry
93137     * @param string Channel Alias
93138     * @return boolean
93139     */
93140    function _isChannelAlias($alias)
93141    {
93142        return file_exists($this->_getChannelAliasFileName($alias));
93143    }
93144
93145    /**
93146     * @param string|null
93147     * @param string|null
93148     * @param string|null
93149     * @return array|null
93150     * @access private
93151     */
93152    function _packageInfo($package = null, $key = null, $channel = 'pear.php.net')
93153    {
93154        if ($package === null) {
93155            if ($channel === null) {
93156                $channels = $this->_listChannels();
93157                $ret = array();
93158                foreach ($channels as $channel) {
93159                    $channel = strtolower($channel);
93160                    $ret[$channel] = array();
93161                    $packages = $this->_listPackages($channel);
93162                    foreach ($packages as $package) {
93163                        $ret[$channel][] = $this->_packageInfo($package, null, $channel);
93164                    }
93165                }
93166
93167                return $ret;
93168            }
93169
93170            $ps = $this->_listPackages($channel);
93171            if (!count($ps)) {
93172                return array();
93173            }
93174            return array_map(array(&$this, '_packageInfo'),
93175                             $ps, array_fill(0, count($ps), null),
93176                             array_fill(0, count($ps), $channel));
93177        }
93178
93179        $fp = $this->_openPackageFile($package, 'r', $channel);
93180        if ($fp === null) {
93181            return null;
93182        }
93183
93184        $rt = get_magic_quotes_runtime();
93185        set_magic_quotes_runtime(0);
93186        clearstatcache();
93187        $this->_closePackageFile($fp);
93188        $data = file_get_contents($this->_packageFileName($package, $channel));
93189        set_magic_quotes_runtime($rt);
93190        $data = unserialize($data);
93191        if ($key === null) {
93192            return $data;
93193        }
93194
93195        // compatibility for package.xml version 2.0
93196        if (isset($data['old'][$key])) {
93197            return $data['old'][$key];
93198        }
93199
93200        if (isset($data[$key])) {
93201            return $data[$key];
93202        }
93203
93204        return null;
93205    }
93206
93207    /**
93208     * @param string Channel name
93209     * @param bool whether to strictly retrieve info of channels, not just aliases
93210     * @return array|null
93211     */
93212    function _channelInfo($channel, $noaliases = false)
93213    {
93214        if (!$this->_channelExists($channel, $noaliases)) {
93215            return null;
93216        }
93217
93218        $fp = $this->_openChannelFile($channel, 'r');
93219        if ($fp === null) {
93220            return null;
93221        }
93222
93223        $rt = get_magic_quotes_runtime();
93224        set_magic_quotes_runtime(0);
93225        clearstatcache();
93226        $this->_closeChannelFile($fp);
93227        $data = file_get_contents($this->_channelFileName($channel));
93228        set_magic_quotes_runtime($rt);
93229        $data = unserialize($data);
93230        return $data;
93231    }
93232
93233    function _listChannels()
93234    {
93235        $channellist = array();
93236        if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) {
93237            return array('pear.php.net', 'pecl.php.net', 'doc.php.net', '__uri');
93238        }
93239
93240        $dp = opendir($this->channelsdir);
93241        while ($ent = readdir($dp)) {
93242            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
93243                continue;
93244            }
93245
93246            if ($ent == '__uri.reg') {
93247                $channellist[] = '__uri';
93248                continue;
93249            }
93250
93251            $channellist[] = str_replace('_', '/', substr($ent, 0, -4));
93252        }
93253
93254        closedir($dp);
93255        if (!in_array('pear.php.net', $channellist)) {
93256            $channellist[] = 'pear.php.net';
93257        }
93258
93259        if (!in_array('pecl.php.net', $channellist)) {
93260            $channellist[] = 'pecl.php.net';
93261        }
93262
93263        if (!in_array('doc.php.net', $channellist)) {
93264            $channellist[] = 'doc.php.net';
93265        }
93266
93267
93268        if (!in_array('__uri', $channellist)) {
93269            $channellist[] = '__uri';
93270        }
93271
93272        natsort($channellist);
93273        return $channellist;
93274    }
93275
93276    function _listPackages($channel = false)
93277    {
93278        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
93279            return $this->_listChannelPackages($channel);
93280        }
93281
93282        if (!file_exists($this->statedir) || !is_dir($this->statedir)) {
93283            return array();
93284        }
93285
93286        $pkglist = array();
93287        $dp = opendir($this->statedir);
93288        if (!$dp) {
93289            return $pkglist;
93290        }
93291
93292        while ($ent = readdir($dp)) {
93293            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
93294                continue;
93295            }
93296
93297            $pkglist[] = substr($ent, 0, -4);
93298        }
93299        closedir($dp);
93300        return $pkglist;
93301    }
93302
93303    function _listChannelPackages($channel)
93304    {
93305        $pkglist = array();
93306        if (!file_exists($this->_channelDirectoryName($channel)) ||
93307              !is_dir($this->_channelDirectoryName($channel))) {
93308            return array();
93309        }
93310
93311        $dp = opendir($this->_channelDirectoryName($channel));
93312        if (!$dp) {
93313            return $pkglist;
93314        }
93315
93316        while ($ent = readdir($dp)) {
93317            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
93318                continue;
93319            }
93320            $pkglist[] = substr($ent, 0, -4);
93321        }
93322
93323        closedir($dp);
93324        return $pkglist;
93325    }
93326
93327    function _listAllPackages()
93328    {
93329        $ret = array();
93330        foreach ($this->_listChannels() as $channel) {
93331            $ret[$channel] = $this->_listPackages($channel);
93332        }
93333
93334        return $ret;
93335    }
93336
93337    /**
93338     * Add an installed package to the registry
93339     * @param string package name
93340     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
93341     * @return bool success of saving
93342     * @access private
93343     */
93344    function _addPackage($package, $info)
93345    {
93346        if ($this->_packageExists($package)) {
93347            return false;
93348        }
93349
93350        $fp = $this->_openPackageFile($package, 'wb');
93351        if ($fp === null) {
93352            return false;
93353        }
93354
93355        $info['_lastmodified'] = time();
93356        fwrite($fp, serialize($info));
93357        $this->_closePackageFile($fp);
93358        if (isset($info['filelist'])) {
93359            $this->_rebuildFileMap();
93360        }
93361
93362        return true;
93363    }
93364
93365    /**
93366     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
93367     * @return bool
93368     * @access private
93369     */
93370    function _addPackage2($info)
93371    {
93372        if (!is_a($info, 'PEAR_PackageFile_v1') && !is_a($info, 'PEAR_PackageFile_v2')) {
93373            return false;
93374        }
93375
93376        if (!$info->validate()) {
93377            if (class_exists('PEAR_Common')) {
93378                $ui = PEAR_Frontend::singleton();
93379                if ($ui) {
93380                    foreach ($info->getValidationWarnings() as $err) {
93381                        $ui->log($err['message'], true);
93382                    }
93383                }
93384            }
93385            return false;
93386        }
93387
93388        $channel = $info->getChannel();
93389        $package = $info->getPackage();
93390        $save = $info;
93391        if ($this->_packageExists($package, $channel)) {
93392            return false;
93393        }
93394
93395        if (!$this->_channelExists($channel, true)) {
93396            return false;
93397        }
93398
93399        $info = $info->toArray(true);
93400        if (!$info) {
93401            return false;
93402        }
93403
93404        $fp = $this->_openPackageFile($package, 'wb', $channel);
93405        if ($fp === null) {
93406            return false;
93407        }
93408
93409        $info['_lastmodified'] = time();
93410        fwrite($fp, serialize($info));
93411        $this->_closePackageFile($fp);
93412        $this->_rebuildFileMap();
93413        return true;
93414    }
93415
93416    /**
93417     * @param string Package name
93418     * @param array parsed package.xml 1.0
93419     * @param bool this parameter is only here for BC.  Don't use it.
93420     * @access private
93421     */
93422    function _updatePackage($package, $info, $merge = true)
93423    {
93424        $oldinfo = $this->_packageInfo($package);
93425        if (empty($oldinfo)) {
93426            return false;
93427        }
93428
93429        $fp = $this->_openPackageFile($package, 'w');
93430        if ($fp === null) {
93431            return false;
93432        }
93433
93434        if (is_object($info)) {
93435            $info = $info->toArray();
93436        }
93437        $info['_lastmodified'] = time();
93438
93439        $newinfo = $info;
93440        if ($merge) {
93441            $info = array_merge($oldinfo, $info);
93442        } else {
93443            $diff = $info;
93444        }
93445
93446        fwrite($fp, serialize($info));
93447        $this->_closePackageFile($fp);
93448        if (isset($newinfo['filelist'])) {
93449            $this->_rebuildFileMap();
93450        }
93451
93452        return true;
93453    }
93454
93455    /**
93456     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
93457     * @return bool
93458     * @access private
93459     */
93460    function _updatePackage2($info)
93461    {
93462        if (!$this->_packageExists($info->getPackage(), $info->getChannel())) {
93463            return false;
93464        }
93465
93466        $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel());
93467        if ($fp === null) {
93468            return false;
93469        }
93470
93471        $save = $info;
93472        $info = $save->getArray(true);
93473        $info['_lastmodified'] = time();
93474        fwrite($fp, serialize($info));
93475        $this->_closePackageFile($fp);
93476        $this->_rebuildFileMap();
93477        return true;
93478    }
93479
93480    /**
93481     * @param string Package name
93482     * @param string Channel name
93483     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
93484     * @access private
93485     */
93486    function &_getPackage($package, $channel = 'pear.php.net')
93487    {
93488        $info = $this->_packageInfo($package, null, $channel);
93489        if ($info === null) {
93490            return $info;
93491        }
93492
93493        $a = $this->_config;
93494        if (!$a) {
93495            $this->_config = &new PEAR_Config;
93496            $this->_config->set('php_dir', $this->statedir);
93497        }
93498
93499        if (!class_exists('PEAR_PackageFile')) {
93500            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile.php';
93501        }
93502
93503        $pkg = &new PEAR_PackageFile($this->_config);
93504        $pf = &$pkg->fromArray($info);
93505        return $pf;
93506    }
93507
93508    /**
93509     * @param string channel name
93510     * @param bool whether to strictly retrieve channel names
93511     * @return PEAR_ChannelFile|PEAR_Error
93512     * @access private
93513     */
93514    function &_getChannel($channel, $noaliases = false)
93515    {
93516        $ch = false;
93517        if ($this->_channelExists($channel, $noaliases)) {
93518            $chinfo = $this->_channelInfo($channel, $noaliases);
93519            if ($chinfo) {
93520                if (!class_exists('PEAR_ChannelFile')) {
93521                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
93522                }
93523
93524                $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo);
93525            }
93526        }
93527
93528        if ($ch) {
93529            if ($ch->validate()) {
93530                return $ch;
93531            }
93532
93533            foreach ($ch->getErrors(true) as $err) {
93534                $message = $err['message'] . "\n";
93535            }
93536
93537            $ch = PEAR::raiseError($message);
93538            return $ch;
93539        }
93540
93541        if ($this->_getChannelFromAlias($channel) == 'pear.php.net') {
93542            // the registry is not properly set up, so use defaults
93543            if (!class_exists('PEAR_ChannelFile')) {
93544                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
93545            }
93546
93547            $pear_channel = new PEAR_ChannelFile;
93548            $pear_channel->setServer('pear.php.net');
93549            $pear_channel->setAlias('pear');
93550            $pear_channel->setSummary('PHP Extension and Application Repository');
93551            $pear_channel->setDefaultPEARProtocols();
93552            $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
93553            $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
93554            $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
93555            return $pear_channel;
93556        }
93557
93558        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
93559            // the registry is not properly set up, so use defaults
93560            if (!class_exists('PEAR_ChannelFile')) {
93561                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
93562            }
93563            $pear_channel = new PEAR_ChannelFile;
93564            $pear_channel->setServer('pecl.php.net');
93565            $pear_channel->setAlias('pecl');
93566            $pear_channel->setSummary('PHP Extension Community Library');
93567            $pear_channel->setDefaultPEARProtocols();
93568            $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
93569            $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
93570            $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
93571            return $pear_channel;
93572        }
93573
93574        if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
93575            // the registry is not properly set up, so use defaults
93576            if (!class_exists('PEAR_ChannelFile')) {
93577                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
93578            }
93579
93580            $doc_channel = new PEAR_ChannelFile;
93581            $doc_channel->setServer('doc.php.net');
93582            $doc_channel->setAlias('phpdocs');
93583            $doc_channel->setSummary('PHP Documentation Team');
93584            $doc_channel->setDefaultPEARProtocols();
93585            $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
93586            $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
93587            $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
93588            return $doc_channel;
93589        }
93590
93591
93592        if ($this->_getChannelFromAlias($channel) == '__uri') {
93593            // the registry is not properly set up, so use defaults
93594            if (!class_exists('PEAR_ChannelFile')) {
93595                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/ChannelFile.php';
93596            }
93597
93598            $private = new PEAR_ChannelFile;
93599            $private->setName('__uri');
93600            $private->setDefaultPEARProtocols();
93601            $private->setBaseURL('REST1.0', '****');
93602            $private->setSummary('Pseudo-channel for static packages');
93603            return $private;
93604        }
93605
93606        return $ch;
93607    }
93608
93609    /**
93610     * @param string Package name
93611     * @param string Channel name
93612     * @return bool
93613     */
93614    function packageExists($package, $channel = 'pear.php.net')
93615    {
93616        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93617            return $e;
93618        }
93619        $ret = $this->_packageExists($package, $channel);
93620        $this->_unlock();
93621        return $ret;
93622    }
93623
93624    // }}}
93625
93626    // {{{ channelExists()
93627
93628    /**
93629     * @param string channel name
93630     * @param bool if true, then aliases will be ignored
93631     * @return bool
93632     */
93633    function channelExists($channel, $noaliases = false)
93634    {
93635        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93636            return $e;
93637        }
93638        $ret = $this->_channelExists($channel, $noaliases);
93639        $this->_unlock();
93640        return $ret;
93641    }
93642
93643    // }}}
93644
93645    /**
93646     * @param string channel name mirror is in
93647     * @param string mirror name
93648     *
93649     * @return bool
93650     */
93651    function mirrorExists($channel, $mirror)
93652    {
93653        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93654            return $e;
93655        }
93656
93657        $ret = $this->_mirrorExists($channel, $mirror);
93658        $this->_unlock();
93659        return $ret;
93660    }
93661
93662    // {{{ isAlias()
93663
93664    /**
93665     * Determines whether the parameter is an alias of a channel
93666     * @param string
93667     * @return bool
93668     */
93669    function isAlias($alias)
93670    {
93671        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93672            return $e;
93673        }
93674        $ret = $this->_isChannelAlias($alias);
93675        $this->_unlock();
93676        return $ret;
93677    }
93678
93679    // }}}
93680    // {{{ packageInfo()
93681
93682    /**
93683     * @param string|null
93684     * @param string|null
93685     * @param string
93686     * @return array|null
93687     */
93688    function packageInfo($package = null, $key = null, $channel = 'pear.php.net')
93689    {
93690        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93691            return $e;
93692        }
93693        $ret = $this->_packageInfo($package, $key, $channel);
93694        $this->_unlock();
93695        return $ret;
93696    }
93697
93698    // }}}
93699    // {{{ channelInfo()
93700
93701    /**
93702     * Retrieve a raw array of channel data.
93703     *
93704     * Do not use this, instead use {@link getChannel()} for normal
93705     * operations.  Array structure is undefined in this method
93706     * @param string channel name
93707     * @param bool whether to strictly retrieve information only on non-aliases
93708     * @return array|null|PEAR_Error
93709     */
93710    function channelInfo($channel = null, $noaliases = false)
93711    {
93712        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93713            return $e;
93714        }
93715        $ret = $this->_channelInfo($channel, $noaliases);
93716        $this->_unlock();
93717        return $ret;
93718    }
93719
93720    // }}}
93721
93722    /**
93723     * @param string
93724     */
93725    function channelName($channel)
93726    {
93727        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93728            return $e;
93729        }
93730        $ret = $this->_getChannelFromAlias($channel);
93731        $this->_unlock();
93732        return $ret;
93733    }
93734
93735    /**
93736     * @param string
93737     */
93738    function channelAlias($channel)
93739    {
93740        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93741            return $e;
93742        }
93743        $ret = $this->_getAlias($channel);
93744        $this->_unlock();
93745        return $ret;
93746    }
93747    // {{{ listPackages()
93748
93749    function listPackages($channel = false)
93750    {
93751        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93752            return $e;
93753        }
93754        $ret = $this->_listPackages($channel);
93755        $this->_unlock();
93756        return $ret;
93757    }
93758
93759    // }}}
93760    // {{{ listAllPackages()
93761
93762    function listAllPackages()
93763    {
93764        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93765            return $e;
93766        }
93767        $ret = $this->_listAllPackages();
93768        $this->_unlock();
93769        return $ret;
93770    }
93771
93772    // }}}
93773    // {{{ listChannel()
93774
93775    function listChannels()
93776    {
93777        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93778            return $e;
93779        }
93780        $ret = $this->_listChannels();
93781        $this->_unlock();
93782        return $ret;
93783    }
93784
93785    // }}}
93786    // {{{ addPackage()
93787
93788    /**
93789     * Add an installed package to the registry
93790     * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object
93791     *               that will be passed to {@link addPackage2()}
93792     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
93793     * @return bool success of saving
93794     */
93795    function addPackage($package, $info)
93796    {
93797        if (is_object($info)) {
93798            return $this->addPackage2($info);
93799        }
93800        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
93801            return $e;
93802        }
93803        $ret = $this->_addPackage($package, $info);
93804        $this->_unlock();
93805        if ($ret) {
93806            if (!class_exists('PEAR_PackageFile_v1')) {
93807                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
93808            }
93809            $pf = new PEAR_PackageFile_v1;
93810            $pf->setConfig($this->_config);
93811            $pf->fromArray($info);
93812            $this->_dependencyDB->uninstallPackage($pf);
93813            $this->_dependencyDB->installPackage($pf);
93814        }
93815        return $ret;
93816    }
93817
93818    // }}}
93819    // {{{ addPackage2()
93820
93821    function addPackage2($info)
93822    {
93823        if (!is_object($info)) {
93824            return $this->addPackage($info['package'], $info);
93825        }
93826        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
93827            return $e;
93828        }
93829        $ret = $this->_addPackage2($info);
93830        $this->_unlock();
93831        if ($ret) {
93832            $this->_dependencyDB->uninstallPackage($info);
93833            $this->_dependencyDB->installPackage($info);
93834        }
93835        return $ret;
93836    }
93837
93838    // }}}
93839    // {{{ updateChannel()
93840
93841    /**
93842     * For future expandibility purposes, separate this
93843     * @param PEAR_ChannelFile
93844     */
93845    function updateChannel($channel, $lastmodified = null)
93846    {
93847        if ($channel->getName() == '__uri') {
93848            return false;
93849        }
93850        return $this->addChannel($channel, $lastmodified, true);
93851    }
93852
93853    // }}}
93854    // {{{ deleteChannel()
93855
93856    /**
93857     * Deletion fails if there are any packages installed from the channel
93858     * @param string|PEAR_ChannelFile channel name
93859     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
93860     */
93861    function deleteChannel($channel)
93862    {
93863        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
93864            return $e;
93865        }
93866
93867        $ret = $this->_deleteChannel($channel);
93868        $this->_unlock();
93869        if ($ret && is_a($this->_config, 'PEAR_Config')) {
93870            $this->_config->setChannels($this->listChannels());
93871        }
93872
93873        return $ret;
93874    }
93875
93876    // }}}
93877    // {{{ addChannel()
93878
93879    /**
93880     * @param PEAR_ChannelFile Channel object
93881     * @param string Last-Modified header from HTTP for caching
93882     * @return boolean|PEAR_Error True on creation, false if it already exists
93883     */
93884    function addChannel($channel, $lastmodified = false, $update = false)
93885    {
93886        if (!is_a($channel, 'PEAR_ChannelFile') || !$channel->validate()) {
93887            return false;
93888        }
93889
93890        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
93891            return $e;
93892        }
93893
93894        $ret = $this->_addChannel($channel, $update, $lastmodified);
93895        $this->_unlock();
93896        if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) {
93897            $this->_config->setChannels($this->listChannels());
93898        }
93899
93900        return $ret;
93901    }
93902
93903    // }}}
93904    // {{{ deletePackage()
93905
93906    function deletePackage($package, $channel = 'pear.php.net')
93907    {
93908        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
93909            return $e;
93910        }
93911
93912        $file = $this->_packageFileName($package, $channel);
93913        $ret  = file_exists($file) ? @unlink($file) : false;
93914        $this->_rebuildFileMap();
93915        $this->_unlock();
93916        $p = array('channel' => $channel, 'package' => $package);
93917        $this->_dependencyDB->uninstallPackage($p);
93918        return $ret;
93919    }
93920
93921    // }}}
93922    // {{{ updatePackage()
93923
93924    function updatePackage($package, $info, $merge = true)
93925    {
93926        if (is_object($info)) {
93927            return $this->updatePackage2($info, $merge);
93928        }
93929        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
93930            return $e;
93931        }
93932        $ret = $this->_updatePackage($package, $info, $merge);
93933        $this->_unlock();
93934        if ($ret) {
93935            if (!class_exists('PEAR_PackageFile_v1')) {
93936                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v1.php';
93937            }
93938            $pf = new PEAR_PackageFile_v1;
93939            $pf->setConfig($this->_config);
93940            $pf->fromArray($this->packageInfo($package));
93941            $this->_dependencyDB->uninstallPackage($pf);
93942            $this->_dependencyDB->installPackage($pf);
93943        }
93944        return $ret;
93945    }
93946
93947    // }}}
93948    // {{{ updatePackage2()
93949
93950    function updatePackage2($info)
93951    {
93952
93953        if (!is_object($info)) {
93954            return $this->updatePackage($info['package'], $info, $merge);
93955        }
93956
93957        if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) {
93958            return false;
93959        }
93960
93961        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
93962            return $e;
93963        }
93964
93965        $ret = $this->_updatePackage2($info);
93966        $this->_unlock();
93967        if ($ret) {
93968            $this->_dependencyDB->uninstallPackage($info);
93969            $this->_dependencyDB->installPackage($info);
93970        }
93971
93972        return $ret;
93973    }
93974
93975    // }}}
93976    // {{{ getChannel()
93977    /**
93978     * @param string channel name
93979     * @param bool whether to strictly return raw channels (no aliases)
93980     * @return PEAR_ChannelFile|PEAR_Error
93981     */
93982    function &getChannel($channel, $noaliases = false)
93983    {
93984        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
93985            return $e;
93986        }
93987        $ret = &$this->_getChannel($channel, $noaliases);
93988        $this->_unlock();
93989        if (!$ret) {
93990            return PEAR::raiseError('Unknown channel: ' . $channel);
93991        }
93992        return $ret;
93993    }
93994
93995    // }}}
93996    // {{{ getPackage()
93997    /**
93998     * @param string package name
93999     * @param string channel name
94000     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
94001     */
94002    function &getPackage($package, $channel = 'pear.php.net')
94003    {
94004        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
94005            return $e;
94006        }
94007        $pf = &$this->_getPackage($package, $channel);
94008        $this->_unlock();
94009        return $pf;
94010    }
94011
94012    // }}}
94013
94014    /**
94015     * Get PEAR_PackageFile_v[1/2] objects representing the contents of
94016     * a dependency group that are installed.
94017     *
94018     * This is used at uninstall-time
94019     * @param array
94020     * @return array|false
94021     */
94022    function getInstalledGroup($group)
94023    {
94024        $ret = array();
94025        if (isset($group['package'])) {
94026            if (!isset($group['package'][0])) {
94027                $group['package'] = array($group['package']);
94028            }
94029            foreach ($group['package'] as $package) {
94030                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
94031                $p = &$this->getPackage($package['name'], $depchannel);
94032                if ($p) {
94033                    $save = &$p;
94034                    $ret[] = &$save;
94035                }
94036            }
94037        }
94038        if (isset($group['subpackage'])) {
94039            if (!isset($group['subpackage'][0])) {
94040                $group['subpackage'] = array($group['subpackage']);
94041            }
94042            foreach ($group['subpackage'] as $package) {
94043                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
94044                $p = &$this->getPackage($package['name'], $depchannel);
94045                if ($p) {
94046                    $save = &$p;
94047                    $ret[] = &$save;
94048                }
94049            }
94050        }
94051        if (!count($ret)) {
94052            return false;
94053        }
94054        return $ret;
94055    }
94056
94057    // {{{ getChannelValidator()
94058    /**
94059     * @param string channel name
94060     * @return PEAR_Validate|false
94061     */
94062    function &getChannelValidator($channel)
94063    {
94064        $chan = $this->getChannel($channel);
94065        if (PEAR::isError($chan)) {
94066            return $chan;
94067        }
94068        $val = $chan->getValidationObject();
94069        return $val;
94070    }
94071    // }}}
94072    // {{{ getChannels()
94073    /**
94074     * @param string channel name
94075     * @return array an array of PEAR_ChannelFile objects representing every installed channel
94076     */
94077    function &getChannels()
94078    {
94079        $ret = array();
94080        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
94081            return $e;
94082        }
94083        foreach ($this->_listChannels() as $channel) {
94084            $e = &$this->_getChannel($channel);
94085            if (!$e || PEAR::isError($e)) {
94086                continue;
94087            }
94088            $ret[] = $e;
94089        }
94090        $this->_unlock();
94091        return $ret;
94092    }
94093
94094    // }}}
94095    // {{{ checkFileMap()
94096
94097    /**
94098     * Test whether a file or set of files belongs to a package.
94099     *
94100     * If an array is passed in
94101     * @param string|array file path, absolute or relative to the pear
94102     *                     install dir
94103     * @param string|array name of PEAR package or array('package' => name, 'channel' =>
94104     *                     channel) of a package that will be ignored
94105     * @param string API version - 1.1 will exclude any files belonging to a package
94106     * @param array private recursion variable
94107     * @return array|false which package and channel the file belongs to, or an empty
94108     *                     string if the file does not belong to an installed package,
94109     *                     or belongs to the second parameter's package
94110     */
94111    function checkFileMap($path, $package = false, $api = '1.0', $attrs = false)
94112    {
94113        if (is_array($path)) {
94114            static $notempty;
94115            if (empty($notempty)) {
94116                if (!class_exists('PEAR_Installer_Role')) {
94117                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Installer/Role.php';
94118                }
94119                $notempty = create_function('$a','return !empty($a);');
94120            }
94121            $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1]))
94122                : strtolower($package);
94123            $pkgs = array();
94124            foreach ($path as $name => $attrs) {
94125                if (is_array($attrs)) {
94126                    if (isset($attrs['install-as'])) {
94127                        $name = $attrs['install-as'];
94128                    }
94129                    if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) {
94130                        // these are not installed
94131                        continue;
94132                    }
94133                    if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) {
94134                        $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package;
94135                    }
94136                    if (isset($attrs['baseinstalldir'])) {
94137                        $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name;
94138                    }
94139                }
94140                $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs);
94141                if (PEAR::isError($pkgs[$name])) {
94142                    return $pkgs[$name];
94143                }
94144            }
94145            return array_filter($pkgs, $notempty);
94146        }
94147        if (empty($this->filemap_cache)) {
94148            if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
94149                return $e;
94150            }
94151            $err = $this->_readFileMap();
94152            $this->_unlock();
94153            if (PEAR::isError($err)) {
94154                return $err;
94155            }
94156        }
94157        if (!$attrs) {
94158            $attrs = array('role' => 'php'); // any old call would be for PHP role only
94159        }
94160        if (isset($this->filemap_cache[$attrs['role']][$path])) {
94161            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
94162                return false;
94163            }
94164            return $this->filemap_cache[$attrs['role']][$path];
94165        }
94166        $l = strlen($this->install_dir);
94167        if (substr($path, 0, $l) == $this->install_dir) {
94168            $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
94169        }
94170        if (isset($this->filemap_cache[$attrs['role']][$path])) {
94171            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
94172                return false;
94173            }
94174            return $this->filemap_cache[$attrs['role']][$path];
94175        }
94176        return false;
94177    }
94178
94179    // }}}
94180    // {{{ flush()
94181    /**
94182     * Force a reload of the filemap
94183     * @since 1.5.0RC3
94184     */
94185    function flushFileMap()
94186    {
94187        $this->filemap_cache = null;
94188        clearstatcache(); // ensure that the next read gets the full, current filemap
94189    }
94190
94191    // }}}
94192    // {{{ apiVersion()
94193    /**
94194     * Get the expected API version.  Channels API is version 1.1, as it is backwards
94195     * compatible with 1.0
94196     * @return string
94197     */
94198    function apiVersion()
94199    {
94200        return '1.1';
94201    }
94202    // }}}
94203
94204
94205    /**
94206     * Parse a package name, or validate a parsed package name array
94207     * @param string|array pass in an array of format
94208     *                     array(
94209     *                      'package' => 'pname',
94210     *                     ['channel' => 'channame',]
94211     *                     ['version' => 'version',]
94212     *                     ['state' => 'state',]
94213     *                     ['group' => 'groupname'])
94214     *                     or a string of format
94215     *                     [channel://][channame/]pname[-version|-state][/group=groupname]
94216     * @return array|PEAR_Error
94217     */
94218    function parsePackageName($param, $defaultchannel = 'pear.php.net')
94219    {
94220        $saveparam = $param;
94221        if (is_array($param)) {
94222            // convert to string for error messages
94223            $saveparam = $this->parsedPackageNameToString($param);
94224            // process the array
94225            if (!isset($param['package'])) {
94226                return PEAR::raiseError('parsePackageName(): array $param ' .
94227                    'must contain a valid package name in index "param"',
94228                    'package', null, null, $param);
94229            }
94230            if (!isset($param['uri'])) {
94231                if (!isset($param['channel'])) {
94232                    $param['channel'] = $defaultchannel;
94233                }
94234            } else {
94235                $param['channel'] = '__uri';
94236            }
94237        } else {
94238            $components = @parse_url((string) $param);
94239            if (isset($components['scheme'])) {
94240                if ($components['scheme'] == 'http') {
94241                    // uri package
94242                    $param = array('uri' => $param, 'channel' => '__uri');
94243                } elseif($components['scheme'] != 'channel') {
94244                    return PEAR::raiseError('parsePackageName(): only channel:// uris may ' .
94245                        'be downloaded, not "' . $param . '"', 'invalid', null, null, $param);
94246                }
94247            }
94248            if (!isset($components['path'])) {
94249                return PEAR::raiseError('parsePackageName(): array $param ' .
94250                    'must contain a valid package name in "' . $param . '"',
94251                    'package', null, null, $param);
94252            }
94253            if (isset($components['host'])) {
94254                // remove the leading "/"
94255                $components['path'] = substr($components['path'], 1);
94256            }
94257            if (!isset($components['scheme'])) {
94258                if (strpos($components['path'], '/') !== false) {
94259                    if ($components['path']{0} == '/') {
94260                        return PEAR::raiseError('parsePackageName(): this is not ' .
94261                            'a package name, it begins with "/" in "' . $param . '"',
94262                            'invalid', null, null, $param);
94263                    }
94264                    $parts = explode('/', $components['path']);
94265                    $components['host'] = array_shift($parts);
94266                    if (count($parts) > 1) {
94267                        $components['path'] = array_pop($parts);
94268                        $components['host'] .= '/' . implode('/', $parts);
94269                    } else {
94270                        $components['path'] = implode('/', $parts);
94271                    }
94272                } else {
94273                    $components['host'] = $defaultchannel;
94274                }
94275            } else {
94276                if (strpos($components['path'], '/')) {
94277                    $parts = explode('/', $components['path']);
94278                    $components['path'] = array_pop($parts);
94279                    $components['host'] .= '/' . implode('/', $parts);
94280                }
94281            }
94282
94283            if (is_array($param)) {
94284                $param['package'] = $components['path'];
94285            } else {
94286                $param = array(
94287                    'package' => $components['path']
94288                    );
94289                if (isset($components['host'])) {
94290                    $param['channel'] = $components['host'];
94291                }
94292            }
94293            if (isset($components['fragment'])) {
94294                $param['group'] = $components['fragment'];
94295            }
94296            if (isset($components['user'])) {
94297                $param['user'] = $components['user'];
94298            }
94299            if (isset($components['pass'])) {
94300                $param['pass'] = $components['pass'];
94301            }
94302            if (isset($components['query'])) {
94303                parse_str($components['query'], $param['opts']);
94304            }
94305            // check for extension
94306            $pathinfo = pathinfo($param['package']);
94307            if (isset($pathinfo['extension']) &&
94308                  in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) {
94309                $param['extension'] = $pathinfo['extension'];
94310                $param['package'] = substr($pathinfo['basename'], 0,
94311                    strlen($pathinfo['basename']) - 4);
94312            }
94313            // check for version
94314            if (strpos($param['package'], '-')) {
94315                $test = explode('-', $param['package']);
94316                if (count($test) != 2) {
94317                    return PEAR::raiseError('parsePackageName(): only one version/state ' .
94318                        'delimiter "-" is allowed in "' . $saveparam . '"',
94319                        'version', null, null, $param);
94320                }
94321                list($param['package'], $param['version']) = $test;
94322            }
94323        }
94324        // validation
94325        $info = $this->channelExists($param['channel']);
94326        if (PEAR::isError($info)) {
94327            return $info;
94328        }
94329        if (!$info) {
94330            return PEAR::raiseError('unknown channel "' . $param['channel'] .
94331                '" in "' . $saveparam . '"', 'channel', null, null, $param);
94332        }
94333        $chan = $this->getChannel($param['channel']);
94334        if (PEAR::isError($chan)) {
94335            return $chan;
94336        }
94337        if (!$chan) {
94338            return PEAR::raiseError("Exception: corrupt registry, could not " .
94339                "retrieve channel " . $param['channel'] . " information",
94340                'registry', null, null, $param);
94341        }
94342        $param['channel'] = $chan->getName();
94343        $validate = $chan->getValidationObject();
94344        $vpackage = $chan->getValidationPackage();
94345        // validate package name
94346        if (!$validate->validPackageName($param['package'], $vpackage['_content'])) {
94347            return PEAR::raiseError('parsePackageName(): invalid package name "' .
94348                $param['package'] . '" in "' . $saveparam . '"',
94349                'package', null, null, $param);
94350        }
94351        if (isset($param['group'])) {
94352            if (!PEAR_Validate::validGroupName($param['group'])) {
94353                return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] .
94354                    '" is not a valid group name in "' . $saveparam . '"', 'group', null, null,
94355                    $param);
94356            }
94357        }
94358        if (isset($param['state'])) {
94359            if (!in_array(strtolower($param['state']), $validate->getValidStates())) {
94360                return PEAR::raiseError('parsePackageName(): state "' . $param['state']
94361                    . '" is not a valid state in "' . $saveparam . '"',
94362                    'state', null, null, $param);
94363            }
94364        }
94365        if (isset($param['version'])) {
94366            if (isset($param['state'])) {
94367                return PEAR::raiseError('parsePackageName(): cannot contain both ' .
94368                    'a version and a stability (state) in "' . $saveparam . '"',
94369                    'version/state', null, null, $param);
94370            }
94371            // check whether version is actually a state
94372            if (in_array(strtolower($param['version']), $validate->getValidStates())) {
94373                $param['state'] = strtolower($param['version']);
94374                unset($param['version']);
94375            } else {
94376                if (!$validate->validVersion($param['version'])) {
94377                    return PEAR::raiseError('parsePackageName(): "' . $param['version'] .
94378                        '" is neither a valid version nor a valid state in "' .
94379                        $saveparam . '"', 'version/state', null, null, $param);
94380                }
94381            }
94382        }
94383        return $param;
94384    }
94385
94386    /**
94387     * @param array
94388     * @return string
94389     */
94390    function parsedPackageNameToString($parsed, $brief = false)
94391    {
94392        if (is_string($parsed)) {
94393            return $parsed;
94394        }
94395        if (is_object($parsed)) {
94396            $p = $parsed;
94397            $parsed = array(
94398                'package' => $p->getPackage(),
94399                'channel' => $p->getChannel(),
94400                'version' => $p->getVersion(),
94401            );
94402        }
94403        if (isset($parsed['uri'])) {
94404            return $parsed['uri'];
94405        }
94406        if ($brief) {
94407            if ($channel = $this->channelAlias($parsed['channel'])) {
94408                return $channel . '/' . $parsed['package'];
94409            }
94410        }
94411        $upass = '';
94412        if (isset($parsed['user'])) {
94413            $upass = $parsed['user'];
94414            if (isset($parsed['pass'])) {
94415                $upass .= ':' . $parsed['pass'];
94416            }
94417            $upass = "$upass@";
94418        }
94419        $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package'];
94420        if (isset($parsed['version']) || isset($parsed['state'])) {
94421            $ver = isset($parsed['version']) ? $parsed['version'] : '';
94422            $ver .= isset($parsed['state']) ? $parsed['state'] : '';
94423            $ret .= '-' . $ver;
94424        }
94425        if (isset($parsed['extension'])) {
94426            $ret .= '.' . $parsed['extension'];
94427        }
94428        if (isset($parsed['opts'])) {
94429            $ret .= '?';
94430            foreach ($parsed['opts'] as $name => $value) {
94431                $parsed['opts'][$name] = "$name=$value";
94432            }
94433            $ret .= implode('&', $parsed['opts']);
94434        }
94435        if (isset($parsed['group'])) {
94436            $ret .= '#' . $parsed['group'];
94437        }
94438        return $ret;
94439    }
94440}<?php
94441/**
94442 * PEAR_REST
94443 *
94444 * PHP versions 4 and 5
94445 *
94446 * @category   pear
94447 * @package    PEAR
94448 * @author     Greg Beaver <cellog@php.net>
94449 * @copyright  1997-2009 The Authors
94450 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
94451 * @version    CVS: $Id: REST.php 313023 2011-07-06 19:17:11Z dufuz $
94452 * @link       http://pear.php.net/package/PEAR
94453 * @since      File available since Release 1.4.0a1
94454 */
94455
94456/**
94457 * For downloading xml files
94458 */
94459require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
94460require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/XMLParser.php';
94461
94462/**
94463 * Intelligently retrieve data, following hyperlinks if necessary, and re-directing
94464 * as well
94465 * @category   pear
94466 * @package    PEAR
94467 * @author     Greg Beaver <cellog@php.net>
94468 * @copyright  1997-2009 The Authors
94469 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
94470 * @version    Release: 1.9.4
94471 * @link       http://pear.php.net/package/PEAR
94472 * @since      Class available since Release 1.4.0a1
94473 */
94474class PEAR_REST
94475{
94476    var $config;
94477    var $_options;
94478
94479    function PEAR_REST(&$config, $options = array())
94480    {
94481        $this->config   = &$config;
94482        $this->_options = $options;
94483    }
94484
94485    /**
94486     * Retrieve REST data, but always retrieve the local cache if it is available.
94487     *
94488     * This is useful for elements that should never change, such as information on a particular
94489     * release
94490     * @param string full URL to this resource
94491     * @param array|false contents of the accept-encoding header
94492     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
94493     *                    parsed using PEAR_XMLParser
94494     * @return string|array
94495     */
94496    function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false)
94497    {
94498        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
94499            md5($url) . 'rest.cachefile';
94500
94501        if (file_exists($cachefile)) {
94502            return unserialize(implode('', file($cachefile)));
94503        }
94504
94505        return $this->retrieveData($url, $accept, $forcestring, $channel);
94506    }
94507
94508    /**
94509     * Retrieve a remote REST resource
94510     * @param string full URL to this resource
94511     * @param array|false contents of the accept-encoding header
94512     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
94513     *                    parsed using PEAR_XMLParser
94514     * @return string|array
94515     */
94516    function retrieveData($url, $accept = false, $forcestring = false, $channel = false)
94517    {
94518        $cacheId = $this->getCacheId($url);
94519        if ($ret = $this->useLocalCache($url, $cacheId)) {
94520            return $ret;
94521        }
94522
94523        $file = $trieddownload = false;
94524        if (!isset($this->_options['offline'])) {
94525            $trieddownload = true;
94526            $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel);
94527        }
94528
94529        if (PEAR::isError($file)) {
94530            if ($file->getCode() !== -9276) {
94531                return $file;
94532            }
94533
94534            $trieddownload = false;
94535            $file = false; // use local copy if available on socket connect error
94536        }
94537
94538        if (!$file) {
94539            $ret = $this->getCache($url);
94540            if (!PEAR::isError($ret) && $trieddownload) {
94541                // reset the age of the cache if the server says it was unmodified
94542                $result = $this->saveCache($url, $ret, null, true, $cacheId);
94543                if (PEAR::isError($result)) {
94544                    return PEAR::raiseError($result->getMessage());
94545                }
94546            }
94547
94548            return $ret;
94549        }
94550
94551        if (is_array($file)) {
94552            $headers      = $file[2];
94553            $lastmodified = $file[1];
94554            $content      = $file[0];
94555        } else {
94556            $headers      = array();
94557            $lastmodified = false;
94558            $content      = $file;
94559        }
94560
94561        if ($forcestring) {
94562            $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId);
94563            if (PEAR::isError($result)) {
94564                return PEAR::raiseError($result->getMessage());
94565            }
94566
94567            return $content;
94568        }
94569
94570        if (isset($headers['content-type'])) {
94571            switch ($headers['content-type']) {
94572                case 'text/xml' :
94573                case 'application/xml' :
94574                case 'text/plain' :
94575                    if ($headers['content-type'] === 'text/plain') {
94576                        $check = substr($content, 0, 5);
94577                        if ($check !== '<?xml') {
94578                            break;
94579                        }
94580                    }
94581
94582                    $parser = new PEAR_XMLParser;
94583                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
94584                    $err = $parser->parse($content);
94585                    PEAR::popErrorHandling();
94586                    if (PEAR::isError($err)) {
94587                        return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .
94588                            $err->getMessage());
94589                    }
94590                    $content = $parser->getData();
94591                case 'text/html' :
94592                default :
94593                    // use it as a string
94594            }
94595        } else {
94596            // assume XML
94597            $parser = new PEAR_XMLParser;
94598            $parser->parse($content);
94599            $content = $parser->getData();
94600        }
94601
94602        $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId);
94603        if (PEAR::isError($result)) {
94604            return PEAR::raiseError($result->getMessage());
94605        }
94606
94607        return $content;
94608    }
94609
94610    function useLocalCache($url, $cacheid = null)
94611    {
94612        if ($cacheid === null) {
94613            $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
94614                md5($url) . 'rest.cacheid';
94615            if (!file_exists($cacheidfile)) {
94616                return false;
94617            }
94618
94619            $cacheid = unserialize(implode('', file($cacheidfile)));
94620        }
94621
94622        $cachettl = $this->config->get('cache_ttl');
94623        // If cache is newer than $cachettl seconds, we use the cache!
94624        if (time() - $cacheid['age'] < $cachettl) {
94625            return $this->getCache($url);
94626        }
94627
94628        return false;
94629    }
94630
94631    function getCacheId($url)
94632    {
94633        $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
94634            md5($url) . 'rest.cacheid';
94635
94636        if (!file_exists($cacheidfile)) {
94637            return false;
94638        }
94639
94640        $ret = unserialize(implode('', file($cacheidfile)));
94641        return $ret;
94642    }
94643
94644    function getCache($url)
94645    {
94646        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
94647            md5($url) . 'rest.cachefile';
94648
94649        if (!file_exists($cachefile)) {
94650            return PEAR::raiseError('No cached content available for "' . $url . '"');
94651        }
94652
94653        return unserialize(implode('', file($cachefile)));
94654    }
94655
94656    /**
94657     * @param string full URL to REST resource
94658     * @param string original contents of the REST resource
94659     * @param array  HTTP Last-Modified and ETag headers
94660     * @param bool   if true, then the cache id file should be regenerated to
94661     *               trigger a new time-to-live value
94662     */
94663    function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null)
94664    {
94665        $cache_dir   = $this->config->get('cache_dir');
94666        $d           = $cache_dir . DIRECTORY_SEPARATOR . md5($url);
94667        $cacheidfile = $d . 'rest.cacheid';
94668        $cachefile   = $d . 'rest.cachefile';
94669
94670        if (!is_dir($cache_dir)) {
94671            if (System::mkdir(array('-p', $cache_dir)) === false) {
94672              return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed.");
94673            }
94674        }
94675
94676        if ($cacheid === null && $nochange) {
94677            $cacheid = unserialize(implode('', file($cacheidfile)));
94678        }
94679
94680        $idData = serialize(array(
94681            'age'        => time(),
94682            'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified),
94683        ));
94684
94685        $result = $this->saveCacheFile($cacheidfile, $idData);
94686        if (PEAR::isError($result)) {
94687            return $result;
94688        } elseif ($nochange) {
94689            return true;
94690        }
94691
94692        $result = $this->saveCacheFile($cachefile, serialize($contents));
94693        if (PEAR::isError($result)) {
94694            if (file_exists($cacheidfile)) {
94695              @unlink($cacheidfile);
94696            }
94697
94698            return $result;
94699        }
94700
94701        return true;
94702    }
94703
94704    function saveCacheFile($file, $contents)
94705    {
94706        $len = strlen($contents);
94707
94708        $cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode
94709        if ($cachefile_fp !== false) { // create file
94710            if (fwrite($cachefile_fp, $contents, $len) < $len) {
94711                fclose($cachefile_fp);
94712                return PEAR::raiseError("Could not write $file.");
94713            }
94714        } else { // update file
94715            $cachefile_lstat = lstat($file);
94716            $cachefile_fp = @fopen($file, 'wb');
94717            if (!$cachefile_fp) {
94718                return PEAR::raiseError("Could not open $file for writing.");
94719            }
94720
94721            $cachefile_fstat = fstat($cachefile_fp);
94722            if (
94723              $cachefile_lstat['mode'] == $cachefile_fstat['mode'] &&
94724              $cachefile_lstat['ino']  == $cachefile_fstat['ino'] &&
94725              $cachefile_lstat['dev']  == $cachefile_fstat['dev'] &&
94726              $cachefile_fstat['nlink'] === 1
94727            ) {
94728                if (fwrite($cachefile_fp, $contents, $len) < $len) {
94729                    fclose($cachefile_fp);
94730                    return PEAR::raiseError("Could not write $file.");
94731                }
94732            } else {
94733                fclose($cachefile_fp);
94734                $link = function_exists('readlink') ? readlink($file) : $file;
94735                return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack');
94736            }
94737        }
94738
94739        fclose($cachefile_fp);
94740        return true;
94741    }
94742
94743    /**
94744     * Efficiently Download a file through HTTP.  Returns downloaded file as a string in-memory
94745     * This is best used for small files
94746     *
94747     * If an HTTP proxy has been configured (http_proxy PEAR_Config
94748     * setting), the proxy will be used.
94749     *
94750     * @param string  $url       the URL to download
94751     * @param string  $save_dir  directory to save file in
94752     * @param false|string|array $lastmodified header values to check against for caching
94753     *                           use false to return the header values from this download
94754     * @param false|array $accept Accept headers to send
94755     * @return string|array  Returns the contents of the downloaded file or a PEAR
94756     *                       error on failure.  If the error is caused by
94757     *                       socket-related errors, the error object will
94758     *                       have the fsockopen error code available through
94759     *                       getCode().  If caching is requested, then return the header
94760     *                       values.
94761     *
94762     * @access public
94763     */
94764    function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false)
94765    {
94766        static $redirect = 0;
94767        // always reset , so we are clean case of error
94768        $wasredirect = $redirect;
94769        $redirect = 0;
94770
94771        $info = parse_url($url);
94772        if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
94773            return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
94774        }
94775
94776        if (!isset($info['host'])) {
94777            return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
94778        }
94779
94780        $host   = isset($info['host']) ? $info['host'] : null;
94781        $port   = isset($info['port']) ? $info['port'] : null;
94782        $path   = isset($info['path']) ? $info['path'] : null;
94783        $schema = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http';
94784
94785        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
94786        if ($this->config->get('http_proxy')&&
94787              $proxy = parse_url($this->config->get('http_proxy'))
94788        ) {
94789            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
94790            if ($schema === 'https') {
94791                $proxy_host = 'ssl://' . $proxy_host;
94792            }
94793
94794            $proxy_port   = isset($proxy['port']) ? $proxy['port'] : 8080;
94795            $proxy_user   = isset($proxy['user']) ? urldecode($proxy['user']) : null;
94796            $proxy_pass   = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
94797            $proxy_schema = (isset($proxy['scheme']) && $proxy['scheme'] == 'https') ? 'https' : 'http';
94798        }
94799
94800        if (empty($port)) {
94801            $port = (isset($info['scheme']) && $info['scheme'] == 'https')  ? 443 : 80;
94802        }
94803
94804        if (isset($proxy['host'])) {
94805            $request = "GET $url HTTP/1.1\r\n";
94806        } else {
94807            $request = "GET $path HTTP/1.1\r\n";
94808        }
94809
94810        $request .= "Host: $host\r\n";
94811        $ifmodifiedsince = '';
94812        if (is_array($lastmodified)) {
94813            if (isset($lastmodified['Last-Modified'])) {
94814                $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
94815            }
94816
94817            if (isset($lastmodified['ETag'])) {
94818                $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
94819            }
94820        } else {
94821            $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
94822        }
94823
94824        $request .= $ifmodifiedsince .
94825            "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n";
94826
94827        $username = $this->config->get('username', null, $channel);
94828        $password = $this->config->get('password', null, $channel);
94829
94830        if ($username && $password) {
94831            $tmp = base64_encode("$username:$password");
94832            $request .= "Authorization: Basic $tmp\r\n";
94833        }
94834
94835        if ($proxy_host != '' && $proxy_user != '') {
94836            $request .= 'Proxy-Authorization: Basic ' .
94837                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
94838        }
94839
94840        if ($accept) {
94841            $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
94842        }
94843
94844        $request .= "Accept-Encoding:\r\n";
94845        $request .= "Connection: close\r\n";
94846        $request .= "\r\n";
94847
94848        if ($proxy_host != '') {
94849            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15);
94850            if (!$fp) {
94851                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", -9276);
94852            }
94853        } else {
94854            if ($schema === 'https') {
94855                $host = 'ssl://' . $host;
94856            }
94857
94858            $fp = @fsockopen($host, $port, $errno, $errstr);
94859            if (!$fp) {
94860                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
94861            }
94862        }
94863
94864        fwrite($fp, $request);
94865
94866        $headers = array();
94867        $reply   = 0;
94868        while ($line = trim(fgets($fp, 1024))) {
94869            if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
94870                $headers[strtolower($matches[1])] = trim($matches[2]);
94871            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
94872                $reply = (int)$matches[1];
94873                if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
94874                    return false;
94875                }
94876
94877                if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) {
94878                    return PEAR::raiseError("File $schema://$host:$port$path not valid (received: $line)");
94879                }
94880            }
94881        }
94882
94883        if ($reply != 200) {
94884            if (!isset($headers['location'])) {
94885                return PEAR::raiseError("File $schema://$host:$port$path not valid (redirected but no location)");
94886            }
94887
94888            if ($wasredirect > 4) {
94889                return PEAR::raiseError("File $schema://$host:$port$path not valid (redirection looped more than 5 times)");
94890            }
94891
94892            $redirect = $wasredirect + 1;
94893            return $this->downloadHttp($headers['location'], $lastmodified, $accept, $channel);
94894        }
94895
94896        $length = isset($headers['content-length']) ? $headers['content-length'] : -1;
94897
94898        $data = '';
94899        while ($chunk = @fread($fp, 8192)) {
94900            $data .= $chunk;
94901        }
94902        fclose($fp);
94903
94904        if ($lastmodified === false || $lastmodified) {
94905            if (isset($headers['etag'])) {
94906                $lastmodified = array('ETag' => $headers['etag']);
94907            }
94908
94909            if (isset($headers['last-modified'])) {
94910                if (is_array($lastmodified)) {
94911                    $lastmodified['Last-Modified'] = $headers['last-modified'];
94912                } else {
94913                    $lastmodified = $headers['last-modified'];
94914                }
94915            }
94916
94917            return array($data, $lastmodified, $headers);
94918        }
94919
94920        return $data;
94921    }
94922}<?php
94923/**
94924 * PEAR_REST_10
94925 *
94926 * PHP versions 4 and 5
94927 *
94928 * @category   pear
94929 * @package    PEAR
94930 * @author     Greg Beaver <cellog@php.net>
94931 * @copyright  1997-2009 The Authors
94932 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
94933 * @version    CVS: $Id: 10.php 313023 2011-07-06 19:17:11Z dufuz $
94934 * @link       http://pear.php.net/package/PEAR
94935 * @since      File available since Release 1.4.0a12
94936 */
94937
94938/**
94939 * For downloading REST xml/txt files
94940 */
94941require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/REST.php';
94942
94943/**
94944 * Implement REST 1.0
94945 *
94946 * @category   pear
94947 * @package    PEAR
94948 * @author     Greg Beaver <cellog@php.net>
94949 * @copyright  1997-2009 The Authors
94950 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
94951 * @version    Release: 1.9.4
94952 * @link       http://pear.php.net/package/PEAR
94953 * @since      Class available since Release 1.4.0a12
94954 */
94955class PEAR_REST_10
94956{
94957    /**
94958     * @var PEAR_REST
94959     */
94960    var $_rest;
94961    function PEAR_REST_10($config, $options = array())
94962    {
94963        $this->_rest = &new PEAR_REST($config, $options);
94964    }
94965
94966    /**
94967     * Retrieve information about a remote package to be downloaded from a REST server
94968     *
94969     * @param string $base The uri to prepend to all REST calls
94970     * @param array $packageinfo an array of format:
94971     * <pre>
94972     *  array(
94973     *   'package' => 'packagename',
94974     *   'channel' => 'channelname',
94975     *  ['state' => 'alpha' (or valid state),]
94976     *  -or-
94977     *  ['version' => '1.whatever']
94978     * </pre>
94979     * @param string $prefstate Current preferred_state config variable value
94980     * @param bool $installed the installed version of this package to compare against
94981     * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
94982     */
94983    function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
94984    {
94985        $states = $this->betterStates($prefstate, true);
94986        if (!$states) {
94987            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
94988        }
94989
94990        $channel  = $packageinfo['channel'];
94991        $package  = $packageinfo['package'];
94992        $state    = isset($packageinfo['state'])   ? $packageinfo['state']   : null;
94993        $version  = isset($packageinfo['version']) ? $packageinfo['version'] : null;
94994        $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';
94995
94996        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
94997        if (PEAR::isError($info)) {
94998            return PEAR::raiseError('No releases available for package "' .
94999                $channel . '/' . $package . '"');
95000        }
95001
95002        if (!isset($info['r'])) {
95003            return false;
95004        }
95005
95006        $release = $found = false;
95007        if (!is_array($info['r']) || !isset($info['r'][0])) {
95008            $info['r'] = array($info['r']);
95009        }
95010
95011        foreach ($info['r'] as $release) {
95012            if (!isset($this->_rest->_options['force']) && ($installed &&
95013                  version_compare($release['v'], $installed, '<'))) {
95014                continue;
95015            }
95016
95017            if (isset($state)) {
95018                // try our preferred state first
95019                if ($release['s'] == $state) {
95020                    $found = true;
95021                    break;
95022                }
95023                // see if there is something newer and more stable
95024                // bug #7221
95025                if (in_array($release['s'], $this->betterStates($state), true)) {
95026                    $found = true;
95027                    break;
95028                }
95029            } elseif (isset($version)) {
95030                if ($release['v'] == $version) {
95031                    $found = true;
95032                    break;
95033                }
95034            } else {
95035                if (in_array($release['s'], $states)) {
95036                    $found = true;
95037                    break;
95038                }
95039            }
95040        }
95041
95042        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
95043    }
95044
95045    function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
95046                               $prefstate = 'stable', $installed = false, $channel = false)
95047    {
95048        $states = $this->betterStates($prefstate, true);
95049        if (!$states) {
95050            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
95051        }
95052
95053        $channel  = $dependency['channel'];
95054        $package  = $dependency['name'];
95055        $state    = isset($dependency['state'])   ? $dependency['state']   : null;
95056        $version  = isset($dependency['version']) ? $dependency['version'] : null;
95057        $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';
95058
95059        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
95060        if (PEAR::isError($info)) {
95061            return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
95062                . '" dependency "' . $channel . '/' . $package . '" has no releases');
95063        }
95064
95065        if (!is_array($info) || !isset($info['r'])) {
95066            return false;
95067        }
95068
95069        $exclude = array();
95070        $min = $max = $recommended = false;
95071        if ($xsdversion == '1.0') {
95072            switch ($dependency['rel']) {
95073                case 'ge' :
95074                    $min = $dependency['version'];
95075                break;
95076                case 'gt' :
95077                    $min = $dependency['version'];
95078                    $exclude = array($dependency['version']);
95079                break;
95080                case 'eq' :
95081                    $recommended = $dependency['version'];
95082                break;
95083                case 'lt' :
95084                    $max = $dependency['version'];
95085                    $exclude = array($dependency['version']);
95086                break;
95087                case 'le' :
95088                    $max = $dependency['version'];
95089                break;
95090                case 'ne' :
95091                    $exclude = array($dependency['version']);
95092                break;
95093            }
95094        } else {
95095            $min = isset($dependency['min']) ? $dependency['min'] : false;
95096            $max = isset($dependency['max']) ? $dependency['max'] : false;
95097            $recommended = isset($dependency['recommended']) ?
95098                $dependency['recommended'] : false;
95099            if (isset($dependency['exclude'])) {
95100                if (!isset($dependency['exclude'][0])) {
95101                    $exclude = array($dependency['exclude']);
95102                }
95103            }
95104        }
95105        $release = $found = false;
95106        if (!is_array($info['r']) || !isset($info['r'][0])) {
95107            $info['r'] = array($info['r']);
95108        }
95109        foreach ($info['r'] as $release) {
95110            if (!isset($this->_rest->_options['force']) && ($installed &&
95111                  version_compare($release['v'], $installed, '<'))) {
95112                continue;
95113            }
95114            if (in_array($release['v'], $exclude)) { // skip excluded versions
95115                continue;
95116            }
95117            // allow newer releases to say "I'm OK with the dependent package"
95118            if ($xsdversion == '2.0' && isset($release['co'])) {
95119                if (!is_array($release['co']) || !isset($release['co'][0])) {
95120                    $release['co'] = array($release['co']);
95121                }
95122                foreach ($release['co'] as $entry) {
95123                    if (isset($entry['x']) && !is_array($entry['x'])) {
95124                        $entry['x'] = array($entry['x']);
95125                    } elseif (!isset($entry['x'])) {
95126                        $entry['x'] = array();
95127                    }
95128                    if ($entry['c'] == $deppackage['channel'] &&
95129                          strtolower($entry['p']) == strtolower($deppackage['package']) &&
95130                          version_compare($deppackage['version'], $entry['min'], '>=') &&
95131                          version_compare($deppackage['version'], $entry['max'], '<=') &&
95132                          !in_array($release['v'], $entry['x'])) {
95133                        $recommended = $release['v'];
95134                        break;
95135                    }
95136                }
95137            }
95138            if ($recommended) {
95139                if ($release['v'] != $recommended) { // if we want a specific
95140                    // version, then skip all others
95141                    continue;
95142                } else {
95143                    if (!in_array($release['s'], $states)) {
95144                        // the stability is too low, but we must return the
95145                        // recommended version if possible
95146                        return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
95147                    }
95148                }
95149            }
95150            if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
95151                continue;
95152            }
95153            if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
95154                continue;
95155            }
95156            if ($installed && version_compare($release['v'], $installed, '<')) {
95157                continue;
95158            }
95159            if (in_array($release['s'], $states)) { // if in the preferred state...
95160                $found = true; // ... then use it
95161                break;
95162            }
95163        }
95164        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
95165    }
95166
95167    /**
95168     * Take raw data and return the array needed for processing a download URL
95169     *
95170     * @param string $base REST base uri
95171     * @param string $package Package name
95172     * @param array $release an array of format array('v' => version, 's' => state)
95173     *                       describing the release to download
95174     * @param array $info list of all releases as defined by allreleases.xml
95175     * @param bool|null $found determines whether the release was found or this is the next
95176     *                    best alternative.  If null, then versions were skipped because
95177     *                    of PHP dependency
95178     * @return array|PEAR_Error
95179     * @access private
95180     */
95181    function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false)
95182    {
95183        if (!$found) {
95184            $release = $info['r'][0];
95185        }
95186
95187        $packageLower = strtolower($package);
95188        $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . $packageLower . '/' .
95189            'info.xml', false, false, $channel);
95190        if (PEAR::isError($pinfo)) {
95191            return PEAR::raiseError('Package "' . $package .
95192                '" does not have REST info xml available');
95193        }
95194
95195        $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
95196            $release['v'] . '.xml', false, false, $channel);
95197        if (PEAR::isError($releaseinfo)) {
95198            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
95199                '" does not have REST xml available');
95200        }
95201
95202        $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
95203            'deps.' . $release['v'] . '.txt', false, true, $channel);
95204        if (PEAR::isError($packagexml)) {
95205            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
95206                '" does not have REST dependency information available');
95207        }
95208
95209        $packagexml = unserialize($packagexml);
95210        if (!$packagexml) {
95211            $packagexml = array();
95212        }
95213
95214        $allinfo = $this->_rest->retrieveData($base . 'r/' . $packageLower .
95215            '/allreleases.xml', false, false, $channel);
95216        if (PEAR::isError($allinfo)) {
95217            return $allinfo;
95218        }
95219
95220        if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) {
95221            $allinfo['r'] = array($allinfo['r']);
95222        }
95223
95224        $compatible = false;
95225        foreach ($allinfo['r'] as $release) {
95226            if ($release['v'] != $releaseinfo['v']) {
95227                continue;
95228            }
95229
95230            if (!isset($release['co'])) {
95231                break;
95232            }
95233
95234            $compatible = array();
95235            if (!is_array($release['co']) || !isset($release['co'][0])) {
95236                $release['co'] = array($release['co']);
95237            }
95238
95239            foreach ($release['co'] as $entry) {
95240                $comp = array();
95241                $comp['name']    = $entry['p'];
95242                $comp['channel'] = $entry['c'];
95243                $comp['min']     = $entry['min'];
95244                $comp['max']     = $entry['max'];
95245                if (isset($entry['x']) && !is_array($entry['x'])) {
95246                    $comp['exclude'] = $entry['x'];
95247                }
95248
95249                $compatible[] = $comp;
95250            }
95251
95252            if (count($compatible) == 1) {
95253                $compatible = $compatible[0];
95254            }
95255
95256            break;
95257        }
95258
95259        $deprecated = false;
95260        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
95261            if (is_array($pinfo['dp'])) {
95262                $deprecated = array('channel' => (string) $pinfo['dc'],
95263                                    'package' => trim($pinfo['dp']['_content']));
95264            } else {
95265                $deprecated = array('channel' => (string) $pinfo['dc'],
95266                                    'package' => trim($pinfo['dp']));
95267            }
95268        }
95269
95270        $return = array(
95271            'version'    => $releaseinfo['v'],
95272            'info'       => $packagexml,
95273            'package'    => $releaseinfo['p']['_content'],
95274            'stability'  => $releaseinfo['st'],
95275            'compatible' => $compatible,
95276            'deprecated' => $deprecated,
95277        );
95278
95279        if ($found) {
95280            $return['url'] = $releaseinfo['g'];
95281            return $return;
95282        }
95283
95284        $return['php'] = $phpversion;
95285        return $return;
95286    }
95287
95288    function listPackages($base, $channel = false)
95289    {
95290        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
95291        if (PEAR::isError($packagelist)) {
95292            return $packagelist;
95293        }
95294
95295        if (!is_array($packagelist) || !isset($packagelist['p'])) {
95296            return array();
95297        }
95298
95299        if (!is_array($packagelist['p'])) {
95300            $packagelist['p'] = array($packagelist['p']);
95301        }
95302
95303        return $packagelist['p'];
95304    }
95305
95306    /**
95307     * List all categories of a REST server
95308     *
95309     * @param string $base base URL of the server
95310     * @return array of categorynames
95311     */
95312    function listCategories($base, $channel = false)
95313    {
95314        $categories = array();
95315
95316        // c/categories.xml does not exist;
95317        // check for every package its category manually
95318        // This is SLOOOWWWW : ///
95319        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
95320        if (PEAR::isError($packagelist)) {
95321            return $packagelist;
95322        }
95323
95324        if (!is_array($packagelist) || !isset($packagelist['p'])) {
95325            $ret = array();
95326            return $ret;
95327        }
95328
95329        if (!is_array($packagelist['p'])) {
95330            $packagelist['p'] = array($packagelist['p']);
95331        }
95332
95333        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
95334        foreach ($packagelist['p'] as $package) {
95335                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
95336                if (PEAR::isError($inf)) {
95337                    PEAR::popErrorHandling();
95338                    return $inf;
95339                }
95340                $cat = $inf['ca']['_content'];
95341                if (!isset($categories[$cat])) {
95342                    $categories[$cat] = $inf['ca'];
95343                }
95344        }
95345
95346        return array_values($categories);
95347    }
95348
95349    /**
95350     * List a category of a REST server
95351     *
95352     * @param string $base base URL of the server
95353     * @param string $category name of the category
95354     * @param boolean $info also download full package info
95355     * @return array of packagenames
95356     */
95357    function listCategory($base, $category, $info = false, $channel = false)
95358    {
95359        // gives '404 Not Found' error when category doesn't exist
95360        $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel);
95361        if (PEAR::isError($packagelist)) {
95362            return $packagelist;
95363        }
95364
95365        if (!is_array($packagelist) || !isset($packagelist['p'])) {
95366            return array();
95367        }
95368
95369        if (!is_array($packagelist['p']) ||
95370            !isset($packagelist['p'][0])) { // only 1 pkg
95371            $packagelist = array($packagelist['p']);
95372        } else {
95373            $packagelist = $packagelist['p'];
95374        }
95375
95376        if ($info == true) {
95377            // get individual package info
95378            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
95379            foreach ($packagelist as $i => $packageitem) {
95380                $url = sprintf('%s'.'r/%s/latest.txt',
95381                        $base,
95382                        strtolower($packageitem['_content']));
95383                $version = $this->_rest->retrieveData($url, false, false, $channel);
95384                if (PEAR::isError($version)) {
95385                    break; // skipit
95386                }
95387                $url = sprintf('%s'.'r/%s/%s.xml',
95388                        $base,
95389                        strtolower($packageitem['_content']),
95390                        $version);
95391                $info = $this->_rest->retrieveData($url, false, false, $channel);
95392                if (PEAR::isError($info)) {
95393                    break; // skipit
95394                }
95395                $packagelist[$i]['info'] = $info;
95396            }
95397            PEAR::popErrorHandling();
95398        }
95399
95400        return $packagelist;
95401    }
95402
95403
95404    function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
95405    {
95406        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
95407        if (PEAR::isError($packagelist)) {
95408            return $packagelist;
95409        }
95410        if ($this->_rest->config->get('verbose') > 0) {
95411            $ui = &PEAR_Frontend::singleton();
95412            $ui->log('Retrieving data...0%', true);
95413        }
95414        $ret = array();
95415        if (!is_array($packagelist) || !isset($packagelist['p'])) {
95416            return $ret;
95417        }
95418        if (!is_array($packagelist['p'])) {
95419            $packagelist['p'] = array($packagelist['p']);
95420        }
95421
95422        // only search-packagename = quicksearch !
95423        if ($searchpackage && (!$searchsummary || empty($searchpackage))) {
95424            $newpackagelist = array();
95425            foreach ($packagelist['p'] as $package) {
95426                if (!empty($searchpackage) && stristr($package, $searchpackage) !== false) {
95427                    $newpackagelist[] = $package;
95428                }
95429            }
95430            $packagelist['p'] = $newpackagelist;
95431        }
95432        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
95433        $next = .1;
95434        foreach ($packagelist['p'] as $progress => $package) {
95435            if ($this->_rest->config->get('verbose') > 0) {
95436                if ($progress / count($packagelist['p']) >= $next) {
95437                    if ($next == .5) {
95438                        $ui->log('50%', false);
95439                    } else {
95440                        $ui->log('.', false);
95441                    }
95442                    $next += .1;
95443                }
95444            }
95445
95446            if ($basic) { // remote-list command
95447                if ($dostable) {
95448                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
95449                        '/stable.txt', false, false, $channel);
95450                } else {
95451                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
95452                        '/latest.txt', false, false, $channel);
95453                }
95454                if (PEAR::isError($latest)) {
95455                    $latest = false;
95456                }
95457                $info = array('stable' => $latest);
95458            } else { // list-all command
95459                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
95460                if (PEAR::isError($inf)) {
95461                    PEAR::popErrorHandling();
95462                    return $inf;
95463                }
95464                if ($searchpackage) {
95465                    $found = (!empty($searchpackage) && stristr($package, $searchpackage) !== false);
95466                    if (!$found && !(isset($searchsummary) && !empty($searchsummary)
95467                        && (stristr($inf['s'], $searchsummary) !== false
95468                            || stristr($inf['d'], $searchsummary) !== false)))
95469                    {
95470                        continue;
95471                    };
95472                }
95473                $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
95474                    '/allreleases.xml', false, false, $channel);
95475                if (PEAR::isError($releases)) {
95476                    continue;
95477                }
95478                if (!isset($releases['r'][0])) {
95479                    $releases['r'] = array($releases['r']);
95480                }
95481                unset($latest);
95482                unset($unstable);
95483                unset($stable);
95484                unset($state);
95485                foreach ($releases['r'] as $release) {
95486                    if (!isset($latest)) {
95487                        if ($dostable && $release['s'] == 'stable') {
95488                            $latest = $release['v'];
95489                            $state = 'stable';
95490                        }
95491                        if (!$dostable) {
95492                            $latest = $release['v'];
95493                            $state = $release['s'];
95494                        }
95495                    }
95496                    if (!isset($stable) && $release['s'] == 'stable') {
95497                        $stable = $release['v'];
95498                        if (!isset($unstable)) {
95499                            $unstable = $stable;
95500                        }
95501                    }
95502                    if (!isset($unstable) && $release['s'] != 'stable') {
95503                        $latest = $unstable = $release['v'];
95504                        $state = $release['s'];
95505                    }
95506                    if (isset($latest) && !isset($state)) {
95507                        $state = $release['s'];
95508                    }
95509                    if (isset($latest) && isset($stable) && isset($unstable)) {
95510                        break;
95511                    }
95512                }
95513                $deps = array();
95514                if (!isset($unstable)) {
95515                    $unstable = false;
95516                    $state = 'stable';
95517                    if (isset($stable)) {
95518                        $latest = $unstable = $stable;
95519                    }
95520                } else {
95521                    $latest = $unstable;
95522                }
95523                if (!isset($latest)) {
95524                    $latest = false;
95525                }
95526                if ($latest) {
95527                    $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
95528                        $latest . '.txt', false, false, $channel);
95529                    if (!PEAR::isError($d)) {
95530                        $d = unserialize($d);
95531                        if ($d) {
95532                            if (isset($d['required'])) {
95533                                if (!class_exists('PEAR_PackageFile_v2')) {
95534                                    require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
95535                                }
95536                                if (!isset($pf)) {
95537                                    $pf = new PEAR_PackageFile_v2;
95538                                }
95539                                $pf->setDeps($d);
95540                                $tdeps = $pf->getDeps();
95541                            } else {
95542                                $tdeps = $d;
95543                            }
95544                            foreach ($tdeps as $dep) {
95545                                if ($dep['type'] !== 'pkg') {
95546                                    continue;
95547                                }
95548                                $deps[] = $dep;
95549                            }
95550                        }
95551                    }
95552                }
95553                if (!isset($stable)) {
95554                    $stable = '-n/a-';
95555                }
95556                if (!$searchpackage) {
95557                    $info = array('stable' => $latest, 'summary' => $inf['s'], 'description' =>
95558                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
95559                        'unstable' => $unstable, 'state' => $state);
95560                } else {
95561                    $info = array('stable' => $stable, 'summary' => $inf['s'], 'description' =>
95562                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
95563                        'unstable' => $unstable, 'state' => $state);
95564                }
95565            }
95566            $ret[$package] = $info;
95567        }
95568        PEAR::popErrorHandling();
95569        return $ret;
95570    }
95571
95572    function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg)
95573    {
95574        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
95575        if (PEAR::isError($packagelist)) {
95576            return $packagelist;
95577        }
95578
95579        $ret = array();
95580        if (!is_array($packagelist) || !isset($packagelist['p'])) {
95581            return $ret;
95582        }
95583
95584        if (!is_array($packagelist['p'])) {
95585            $packagelist['p'] = array($packagelist['p']);
95586        }
95587
95588        foreach ($packagelist['p'] as $package) {
95589            if (!isset($installed[strtolower($package)])) {
95590                continue;
95591            }
95592
95593            $inst_version = $reg->packageInfo($package, 'version', $channel);
95594            $inst_state   = $reg->packageInfo($package, 'release_state', $channel);
95595            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
95596            $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
95597                '/allreleases.xml', false, false, $channel);
95598            PEAR::popErrorHandling();
95599            if (PEAR::isError($info)) {
95600                continue; // no remote releases
95601            }
95602
95603            if (!isset($info['r'])) {
95604                continue;
95605            }
95606
95607            $release = $found = false;
95608            if (!is_array($info['r']) || !isset($info['r'][0])) {
95609                $info['r'] = array($info['r']);
95610            }
95611
95612            // $info['r'] is sorted by version number
95613            usort($info['r'], array($this, '_sortReleasesByVersionNumber'));
95614            foreach ($info['r'] as $release) {
95615                if ($inst_version && version_compare($release['v'], $inst_version, '<=')) {
95616                    // not newer than the one installed
95617                    break;
95618                }
95619
95620                // new version > installed version
95621                if (!$pref_state) {
95622                    // every state is a good state
95623                    $found = true;
95624                    break;
95625                } else {
95626                    $new_state = $release['s'];
95627                    // if new state >= installed state: go
95628                    if (in_array($new_state, $this->betterStates($inst_state, true))) {
95629                        $found = true;
95630                        break;
95631                    } else {
95632                        // only allow to lower the state of package,
95633                        // if new state >= preferred state: go
95634                        if (in_array($new_state, $this->betterStates($pref_state, true))) {
95635                            $found = true;
95636                            break;
95637                        }
95638                    }
95639                }
95640            }
95641
95642            if (!$found) {
95643                continue;
95644            }
95645
95646            $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
95647                $release['v'] . '.xml', false, false, $channel);
95648            if (PEAR::isError($relinfo)) {
95649                return $relinfo;
95650            }
95651
95652            $ret[$package] = array(
95653                'version'  => $release['v'],
95654                'state'    => $release['s'],
95655                'filesize' => $relinfo['f'],
95656            );
95657        }
95658
95659        return $ret;
95660    }
95661
95662    function packageInfo($base, $package, $channel = false)
95663    {
95664        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
95665        $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
95666        if (PEAR::isError($pinfo)) {
95667            PEAR::popErrorHandling();
95668            return PEAR::raiseError('Unknown package: "' . $package . '" in channel "' . $channel . '"' . "\n". 'Debug: ' .
95669                $pinfo->getMessage());
95670        }
95671
95672        $releases = array();
95673        $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
95674            '/allreleases.xml', false, false, $channel);
95675        if (!PEAR::isError($allreleases)) {
95676            if (!class_exists('PEAR_PackageFile_v2')) {
95677                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
95678            }
95679
95680            if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) {
95681                $allreleases['r'] = array($allreleases['r']);
95682            }
95683
95684            $pf = new PEAR_PackageFile_v2;
95685            foreach ($allreleases['r'] as $release) {
95686                $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
95687                    $release['v'] . '.txt', false, false, $channel);
95688                if (PEAR::isError($ds)) {
95689                    continue;
95690                }
95691
95692                if (!isset($latest)) {
95693                    $latest = $release['v'];
95694                }
95695
95696                $pf->setDeps(unserialize($ds));
95697                $ds = $pf->getDeps();
95698                $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package)
95699                    . '/' . $release['v'] . '.xml', false, false, $channel);
95700
95701                if (PEAR::isError($info)) {
95702                    continue;
95703                }
95704
95705                $releases[$release['v']] = array(
95706                    'doneby' => $info['m'],
95707                    'license' => $info['l'],
95708                    'summary' => $info['s'],
95709                    'description' => $info['d'],
95710                    'releasedate' => $info['da'],
95711                    'releasenotes' => $info['n'],
95712                    'state' => $release['s'],
95713                    'deps' => $ds ? $ds : array(),
95714                );
95715            }
95716        } else {
95717            $latest = '';
95718        }
95719
95720        PEAR::popErrorHandling();
95721        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
95722            if (is_array($pinfo['dp'])) {
95723                $deprecated = array('channel' => (string) $pinfo['dc'],
95724                                    'package' => trim($pinfo['dp']['_content']));
95725            } else {
95726                $deprecated = array('channel' => (string) $pinfo['dc'],
95727                                    'package' => trim($pinfo['dp']));
95728            }
95729        } else {
95730            $deprecated = false;
95731        }
95732
95733        if (!isset($latest)) {
95734            $latest = '';
95735        }
95736
95737        return array(
95738            'name' => $pinfo['n'],
95739            'channel' => $pinfo['c'],
95740            'category' => $pinfo['ca']['_content'],
95741            'stable' => $latest,
95742            'license' => $pinfo['l'],
95743            'summary' => $pinfo['s'],
95744            'description' => $pinfo['d'],
95745            'releases' => $releases,
95746            'deprecated' => $deprecated,
95747            );
95748    }
95749
95750    /**
95751     * Return an array containing all of the states that are more stable than
95752     * or equal to the passed in state
95753     *
95754     * @param string Release state
95755     * @param boolean Determines whether to include $state in the list
95756     * @return false|array False if $state is not a valid release state
95757     */
95758    function betterStates($state, $include = false)
95759    {
95760        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
95761        $i = array_search($state, $states);
95762        if ($i === false) {
95763            return false;
95764        }
95765
95766        if ($include) {
95767            $i--;
95768        }
95769
95770        return array_slice($states, $i + 1);
95771    }
95772
95773    /**
95774     * Sort releases by version number
95775     *
95776     * @access private
95777     */
95778    function _sortReleasesByVersionNumber($a, $b)
95779    {
95780        if (version_compare($a['v'], $b['v'], '=')) {
95781            return 0;
95782        }
95783
95784        if (version_compare($a['v'], $b['v'], '>')) {
95785            return -1;
95786        }
95787
95788        if (version_compare($a['v'], $b['v'], '<')) {
95789            return 1;
95790        }
95791    }
95792}<?php
95793/**
95794 * PEAR_REST_11 - implement faster list-all/remote-list command
95795 *
95796 * PHP versions 4 and 5
95797 *
95798 * @category   pear
95799 * @package    PEAR
95800 * @author     Greg Beaver <cellog@php.net>
95801 * @copyright  1997-2009 The Authors
95802 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
95803 * @version    CVS: $Id: 11.php 313023 2011-07-06 19:17:11Z dufuz $
95804 * @link       http://pear.php.net/package/PEAR
95805 * @since      File available since Release 1.4.3
95806 */
95807
95808/**
95809 * For downloading REST xml/txt files
95810 */
95811require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/REST.php';
95812
95813/**
95814 * Implement REST 1.1
95815 *
95816 * @category   pear
95817 * @package    PEAR
95818 * @author     Greg Beaver <cellog@php.net>
95819 * @copyright  1997-2009 The Authors
95820 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
95821 * @version    Release: 1.9.4
95822 * @link       http://pear.php.net/package/PEAR
95823 * @since      Class available since Release 1.4.3
95824 */
95825class PEAR_REST_11
95826{
95827    /**
95828     * @var PEAR_REST
95829     */
95830    var $_rest;
95831
95832    function PEAR_REST_11($config, $options = array())
95833    {
95834        $this->_rest = &new PEAR_REST($config, $options);
95835    }
95836
95837    function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
95838    {
95839        $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel);
95840        if (PEAR::isError($categorylist)) {
95841            return $categorylist;
95842        }
95843
95844        $ret = array();
95845        if (!is_array($categorylist['c']) || !isset($categorylist['c'][0])) {
95846            $categorylist['c'] = array($categorylist['c']);
95847        }
95848
95849        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
95850
95851        foreach ($categorylist['c'] as $progress => $category) {
95852            $category = $category['_content'];
95853            $packagesinfo = $this->_rest->retrieveData($base .
95854                'c/' . urlencode($category) . '/packagesinfo.xml', false, false, $channel);
95855
95856            if (PEAR::isError($packagesinfo)) {
95857                continue;
95858            }
95859
95860            if (!is_array($packagesinfo) || !isset($packagesinfo['pi'])) {
95861                continue;
95862            }
95863
95864            if (!is_array($packagesinfo['pi']) || !isset($packagesinfo['pi'][0])) {
95865                $packagesinfo['pi'] = array($packagesinfo['pi']);
95866            }
95867
95868            foreach ($packagesinfo['pi'] as $packageinfo) {
95869                if (empty($packageinfo)) {
95870                    continue;
95871                }
95872
95873                $info     = $packageinfo['p'];
95874                $package  = $info['n'];
95875                $releases = isset($packageinfo['a']) ? $packageinfo['a'] : false;
95876                unset($latest);
95877                unset($unstable);
95878                unset($stable);
95879                unset($state);
95880
95881                if ($releases) {
95882                    if (!isset($releases['r'][0])) {
95883                        $releases['r'] = array($releases['r']);
95884                    }
95885
95886                    foreach ($releases['r'] as $release) {
95887                        if (!isset($latest)) {
95888                            if ($dostable && $release['s'] == 'stable') {
95889                                $latest = $release['v'];
95890                                $state = 'stable';
95891                            }
95892                            if (!$dostable) {
95893                                $latest = $release['v'];
95894                                $state = $release['s'];
95895                            }
95896                        }
95897
95898                        if (!isset($stable) && $release['s'] == 'stable') {
95899                            $stable = $release['v'];
95900                            if (!isset($unstable)) {
95901                                $unstable = $stable;
95902                            }
95903                        }
95904
95905                        if (!isset($unstable) && $release['s'] != 'stable') {
95906                            $unstable = $release['v'];
95907                            $state = $release['s'];
95908                        }
95909
95910                        if (isset($latest) && !isset($state)) {
95911                            $state = $release['s'];
95912                        }
95913
95914                        if (isset($latest) && isset($stable) && isset($unstable)) {
95915                            break;
95916                        }
95917                    }
95918                }
95919
95920                if ($basic) { // remote-list command
95921                    if (!isset($latest)) {
95922                        $latest = false;
95923                    }
95924
95925                    if ($dostable) {
95926                        // $state is not set if there are no releases
95927                        if (isset($state) && $state == 'stable') {
95928                            $ret[$package] = array('stable' => $latest);
95929                        } else {
95930                            $ret[$package] = array('stable' => '-n/a-');
95931                        }
95932                    } else {
95933                        $ret[$package] = array('stable' => $latest);
95934                    }
95935
95936                    continue;
95937                }
95938
95939                // list-all command
95940                if (!isset($unstable)) {
95941                    $unstable = false;
95942                    $state = 'stable';
95943                    if (isset($stable)) {
95944                        $latest = $unstable = $stable;
95945                    }
95946                } else {
95947                    $latest = $unstable;
95948                }
95949
95950                if (!isset($latest)) {
95951                    $latest = false;
95952                }
95953
95954                $deps = array();
95955                if ($latest && isset($packageinfo['deps'])) {
95956                    if (!is_array($packageinfo['deps']) ||
95957                          !isset($packageinfo['deps'][0])
95958                    ) {
95959                        $packageinfo['deps'] = array($packageinfo['deps']);
95960                    }
95961
95962                    $d = false;
95963                    foreach ($packageinfo['deps'] as $dep) {
95964                        if ($dep['v'] == $latest) {
95965                            $d = unserialize($dep['d']);
95966                        }
95967                    }
95968
95969                    if ($d) {
95970                        if (isset($d['required'])) {
95971                            if (!class_exists('PEAR_PackageFile_v2')) {
95972                                require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/PackageFile/v2.php';
95973                            }
95974
95975                            if (!isset($pf)) {
95976                                $pf = new PEAR_PackageFile_v2;
95977                            }
95978
95979                            $pf->setDeps($d);
95980                            $tdeps = $pf->getDeps();
95981                        } else {
95982                            $tdeps = $d;
95983                        }
95984
95985                        foreach ($tdeps as $dep) {
95986                            if ($dep['type'] !== 'pkg') {
95987                                continue;
95988                            }
95989
95990                            $deps[] = $dep;
95991                        }
95992                    }
95993                }
95994
95995                $info = array(
95996                    'stable'      => $latest,
95997                    'summary'     => $info['s'],
95998                    'description' => $info['d'],
95999                    'deps'        => $deps,
96000                    'category'    => $info['ca']['_content'],
96001                    'unstable'    => $unstable,
96002                    'state'       => $state
96003                );
96004                $ret[$package] = $info;
96005            }
96006        }
96007
96008        PEAR::popErrorHandling();
96009        return $ret;
96010    }
96011
96012    /**
96013     * List all categories of a REST server
96014     *
96015     * @param string $base base URL of the server
96016     * @return array of categorynames
96017     */
96018    function listCategories($base, $channel = false)
96019    {
96020        $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel);
96021        if (PEAR::isError($categorylist)) {
96022            return $categorylist;
96023        }
96024
96025        if (!is_array($categorylist) || !isset($categorylist['c'])) {
96026            return array();
96027        }
96028
96029        if (isset($categorylist['c']['_content'])) {
96030            // only 1 category
96031            $categorylist['c'] = array($categorylist['c']);
96032        }
96033
96034        return $categorylist['c'];
96035    }
96036
96037    /**
96038     * List packages in a category of a REST server
96039     *
96040     * @param string $base base URL of the server
96041     * @param string $category name of the category
96042     * @param boolean $info also download full package info
96043     * @return array of packagenames
96044     */
96045    function listCategory($base, $category, $info = false, $channel = false)
96046    {
96047        if ($info == false) {
96048            $url = '%s'.'c/%s/packages.xml';
96049        } else {
96050            $url = '%s'.'c/%s/packagesinfo.xml';
96051        }
96052        $url = sprintf($url,
96053                    $base,
96054                    urlencode($category));
96055
96056        // gives '404 Not Found' error when category doesn't exist
96057        $packagelist = $this->_rest->retrieveData($url, false, false, $channel);
96058        if (PEAR::isError($packagelist)) {
96059            return $packagelist;
96060        }
96061        if (!is_array($packagelist)) {
96062            return array();
96063        }
96064
96065        if ($info == false) {
96066            if (!isset($packagelist['p'])) {
96067                return array();
96068            }
96069            if (!is_array($packagelist['p']) ||
96070                !isset($packagelist['p'][0])) { // only 1 pkg
96071                $packagelist = array($packagelist['p']);
96072            } else {
96073                $packagelist = $packagelist['p'];
96074            }
96075            return $packagelist;
96076        }
96077
96078        // info == true
96079        if (!isset($packagelist['pi'])) {
96080            return array();
96081        }
96082
96083        if (!is_array($packagelist['pi']) ||
96084            !isset($packagelist['pi'][0])) { // only 1 pkg
96085            $packagelist_pre = array($packagelist['pi']);
96086        } else {
96087            $packagelist_pre = $packagelist['pi'];
96088        }
96089
96090        $packagelist = array();
96091        foreach ($packagelist_pre as $i => $item) {
96092            // compatibility with r/<latest.txt>.xml
96093            if (isset($item['a']['r'][0])) {
96094                // multiple releases
96095                $item['p']['v'] = $item['a']['r'][0]['v'];
96096                $item['p']['st'] = $item['a']['r'][0]['s'];
96097            } elseif (isset($item['a'])) {
96098                // first and only release
96099                $item['p']['v'] = $item['a']['r']['v'];
96100                $item['p']['st'] = $item['a']['r']['s'];
96101            }
96102
96103            $packagelist[$i] = array('attribs' => $item['p']['r'],
96104                                     '_content' => $item['p']['n'],
96105                                     'info' => $item['p']);
96106        }
96107
96108        return $packagelist;
96109    }
96110
96111    /**
96112     * Return an array containing all of the states that are more stable than
96113     * or equal to the passed in state
96114     *
96115     * @param string Release state
96116     * @param boolean Determines whether to include $state in the list
96117     * @return false|array False if $state is not a valid release state
96118     */
96119    function betterStates($state, $include = false)
96120    {
96121        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
96122        $i = array_search($state, $states);
96123        if ($i === false) {
96124            return false;
96125        }
96126        if ($include) {
96127            $i--;
96128        }
96129        return array_slice($states, $i + 1);
96130    }
96131}
96132?><?php
96133/**
96134 * PEAR_REST_13
96135 *
96136 * PHP versions 4 and 5
96137 *
96138 * @category   pear
96139 * @package    PEAR
96140 * @author     Greg Beaver <cellog@php.net>
96141 * @copyright  1997-2009 The Authors
96142 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
96143 * @version    CVS: $Id: 13.php 313023 2011-07-06 19:17:11Z dufuz $
96144 * @link       http://pear.php.net/package/PEAR
96145 * @since      File available since Release 1.4.0a12
96146 */
96147
96148/**
96149 * For downloading REST xml/txt files
96150 */
96151require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/REST.php';
96152require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/REST/10.php';
96153
96154/**
96155 * Implement REST 1.3
96156 *
96157 * @category   pear
96158 * @package    PEAR
96159 * @author     Greg Beaver <cellog@php.net>
96160 * @copyright  1997-2009 The Authors
96161 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
96162 * @version    Release: 1.9.4
96163 * @link       http://pear.php.net/package/PEAR
96164 * @since      Class available since Release 1.4.0a12
96165 */
96166class PEAR_REST_13 extends PEAR_REST_10
96167{
96168    /**
96169     * Retrieve information about a remote package to be downloaded from a REST server
96170     *
96171     * This is smart enough to resolve the minimum PHP version dependency prior to download
96172     * @param string $base The uri to prepend to all REST calls
96173     * @param array $packageinfo an array of format:
96174     * <pre>
96175     *  array(
96176     *   'package' => 'packagename',
96177     *   'channel' => 'channelname',
96178     *  ['state' => 'alpha' (or valid state),]
96179     *  -or-
96180     *  ['version' => '1.whatever']
96181     * </pre>
96182     * @param string $prefstate Current preferred_state config variable value
96183     * @param bool $installed the installed version of this package to compare against
96184     * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
96185     */
96186    function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
96187    {
96188        $states = $this->betterStates($prefstate, true);
96189        if (!$states) {
96190            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
96191        }
96192
96193        $channel  = $packageinfo['channel'];
96194        $package  = $packageinfo['package'];
96195        $state    = isset($packageinfo['state'])   ? $packageinfo['state']   : null;
96196        $version  = isset($packageinfo['version']) ? $packageinfo['version'] : null;
96197        $restFile = $base . 'r/' . strtolower($package) . '/allreleases2.xml';
96198
96199        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
96200        if (PEAR::isError($info)) {
96201            return PEAR::raiseError('No releases available for package "' .
96202                $channel . '/' . $package . '"');
96203        }
96204
96205        if (!isset($info['r'])) {
96206            return false;
96207        }
96208
96209        $release = $found = false;
96210        if (!is_array($info['r']) || !isset($info['r'][0])) {
96211            $info['r'] = array($info['r']);
96212        }
96213
96214        $skippedphp = false;
96215        foreach ($info['r'] as $release) {
96216            if (!isset($this->_rest->_options['force']) && ($installed &&
96217                  version_compare($release['v'], $installed, '<'))) {
96218                continue;
96219            }
96220
96221            if (isset($state)) {
96222                // try our preferred state first
96223                if ($release['s'] == $state) {
96224                    if (!isset($version) && version_compare($release['m'], phpversion(), '>')) {
96225                        // skip releases that require a PHP version newer than our PHP version
96226                        $skippedphp = $release;
96227                        continue;
96228                    }
96229                    $found = true;
96230                    break;
96231                }
96232
96233                // see if there is something newer and more stable
96234                // bug #7221
96235                if (in_array($release['s'], $this->betterStates($state), true)) {
96236                    if (!isset($version) && version_compare($release['m'], phpversion(), '>')) {
96237                        // skip releases that require a PHP version newer than our PHP version
96238                        $skippedphp = $release;
96239                        continue;
96240                    }
96241                    $found = true;
96242                    break;
96243                }
96244            } elseif (isset($version)) {
96245                if ($release['v'] == $version) {
96246                    if (!isset($this->_rest->_options['force']) &&
96247                          !isset($version) &&
96248                          version_compare($release['m'], phpversion(), '>')) {
96249                        // skip releases that require a PHP version newer than our PHP version
96250                        $skippedphp = $release;
96251                        continue;
96252                    }
96253                    $found = true;
96254                    break;
96255                }
96256            } else {
96257                if (in_array($release['s'], $states)) {
96258                    if (version_compare($release['m'], phpversion(), '>')) {
96259                        // skip releases that require a PHP version newer than our PHP version
96260                        $skippedphp = $release;
96261                        continue;
96262                    }
96263                    $found = true;
96264                    break;
96265                }
96266            }
96267        }
96268
96269        if (!$found && $skippedphp) {
96270            $found = null;
96271        }
96272
96273        return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel);
96274    }
96275
96276    function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
96277                               $prefstate = 'stable', $installed = false, $channel = false)
96278    {
96279        $states = $this->betterStates($prefstate, true);
96280        if (!$states) {
96281            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
96282        }
96283
96284        $channel  = $dependency['channel'];
96285        $package  = $dependency['name'];
96286        $state    = isset($dependency['state'])   ? $dependency['state']   : null;
96287        $version  = isset($dependency['version']) ? $dependency['version'] : null;
96288        $restFile = $base . 'r/' . strtolower($package) .'/allreleases2.xml';
96289
96290        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
96291        if (PEAR::isError($info)) {
96292            return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
96293                . '" dependency "' . $channel . '/' . $package . '" has no releases');
96294        }
96295
96296        if (!is_array($info) || !isset($info['r'])) {
96297            return false;
96298        }
96299
96300        $exclude = array();
96301        $min = $max = $recommended = false;
96302        if ($xsdversion == '1.0') {
96303            $pinfo['package'] = $dependency['name'];
96304            $pinfo['channel'] = 'pear.php.net'; // this is always true - don't change this
96305            switch ($dependency['rel']) {
96306                case 'ge' :
96307                    $min = $dependency['version'];
96308                break;
96309                case 'gt' :
96310                    $min = $dependency['version'];
96311                    $exclude = array($dependency['version']);
96312                break;
96313                case 'eq' :
96314                    $recommended = $dependency['version'];
96315                break;
96316                case 'lt' :
96317                    $max = $dependency['version'];
96318                    $exclude = array($dependency['version']);
96319                break;
96320                case 'le' :
96321                    $max = $dependency['version'];
96322                break;
96323                case 'ne' :
96324                    $exclude = array($dependency['version']);
96325                break;
96326            }
96327        } else {
96328            $pinfo['package'] = $dependency['name'];
96329            $min = isset($dependency['min']) ? $dependency['min'] : false;
96330            $max = isset($dependency['max']) ? $dependency['max'] : false;
96331            $recommended = isset($dependency['recommended']) ?
96332                $dependency['recommended'] : false;
96333            if (isset($dependency['exclude'])) {
96334                if (!isset($dependency['exclude'][0])) {
96335                    $exclude = array($dependency['exclude']);
96336                }
96337            }
96338        }
96339
96340        $skippedphp = $found = $release = false;
96341        if (!is_array($info['r']) || !isset($info['r'][0])) {
96342            $info['r'] = array($info['r']);
96343        }
96344
96345        foreach ($info['r'] as $release) {
96346            if (!isset($this->_rest->_options['force']) && ($installed &&
96347                  version_compare($release['v'], $installed, '<'))) {
96348                continue;
96349            }
96350
96351            if (in_array($release['v'], $exclude)) { // skip excluded versions
96352                continue;
96353            }
96354
96355            // allow newer releases to say "I'm OK with the dependent package"
96356            if ($xsdversion == '2.0' && isset($release['co'])) {
96357                if (!is_array($release['co']) || !isset($release['co'][0])) {
96358                    $release['co'] = array($release['co']);
96359                }
96360
96361                foreach ($release['co'] as $entry) {
96362                    if (isset($entry['x']) && !is_array($entry['x'])) {
96363                        $entry['x'] = array($entry['x']);
96364                    } elseif (!isset($entry['x'])) {
96365                        $entry['x'] = array();
96366                    }
96367
96368                    if ($entry['c'] == $deppackage['channel'] &&
96369                          strtolower($entry['p']) == strtolower($deppackage['package']) &&
96370                          version_compare($deppackage['version'], $entry['min'], '>=') &&
96371                          version_compare($deppackage['version'], $entry['max'], '<=') &&
96372                          !in_array($release['v'], $entry['x'])) {
96373                        if (version_compare($release['m'], phpversion(), '>')) {
96374                            // skip dependency releases that require a PHP version
96375                            // newer than our PHP version
96376                            $skippedphp = $release;
96377                            continue;
96378                        }
96379
96380                        $recommended = $release['v'];
96381                        break;
96382                    }
96383                }
96384            }
96385
96386            if ($recommended) {
96387                if ($release['v'] != $recommended) { // if we want a specific
96388                    // version, then skip all others
96389                    continue;
96390                }
96391
96392                if (!in_array($release['s'], $states)) {
96393                    // the stability is too low, but we must return the
96394                    // recommended version if possible
96395                    return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
96396                }
96397            }
96398
96399            if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
96400                continue;
96401            }
96402
96403            if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
96404                continue;
96405            }
96406
96407            if ($installed && version_compare($release['v'], $installed, '<')) {
96408                continue;
96409            }
96410
96411            if (in_array($release['s'], $states)) { // if in the preferred state...
96412                if (version_compare($release['m'], phpversion(), '>')) {
96413                    // skip dependency releases that require a PHP version
96414                    // newer than our PHP version
96415                    $skippedphp = $release;
96416                    continue;
96417                }
96418
96419                $found = true; // ... then use it
96420                break;
96421            }
96422        }
96423
96424        if (!$found && $skippedphp) {
96425            $found = null;
96426        }
96427
96428        return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel);
96429    }
96430}<?php
96431/**
96432 * PEAR_RunTest
96433 *
96434 * PHP versions 4 and 5
96435 *
96436 * @category   pear
96437 * @package    PEAR
96438 * @author     Tomas V.V.Cox <cox@idecnet.com>
96439 * @author     Greg Beaver <cellog@php.net>
96440 * @copyright  1997-2009 The Authors
96441 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
96442 * @version    CVS: $Id: RunTest.php 313024 2011-07-06 19:51:24Z dufuz $
96443 * @link       http://pear.php.net/package/PEAR
96444 * @since      File available since Release 1.3.3
96445 */
96446
96447/**
96448 * for error handling
96449 */
96450require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
96451require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Config.php';
96452
96453define('DETAILED', 1);
96454putenv("PHP_PEAR_RUNTESTS=1");
96455
96456/**
96457 * Simplified version of PHP's test suite
96458 *
96459 * Try it with:
96460 *
96461 * $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);'
96462 *
96463 *
96464 * @category   pear
96465 * @package    PEAR
96466 * @author     Tomas V.V.Cox <cox@idecnet.com>
96467 * @author     Greg Beaver <cellog@php.net>
96468 * @copyright  1997-2009 The Authors
96469 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
96470 * @version    Release: 1.9.4
96471 * @link       http://pear.php.net/package/PEAR
96472 * @since      Class available since Release 1.3.3
96473 */
96474class PEAR_RunTest
96475{
96476    var $_headers = array();
96477    var $_logger;
96478    var $_options;
96479    var $_php;
96480    var $tests_count;
96481    var $xdebug_loaded;
96482    /**
96483     * Saved value of php executable, used to reset $_php when we
96484     * have a test that uses cgi
96485     *
96486     * @var unknown_type
96487     */
96488    var $_savephp;
96489    var $ini_overwrites = array(
96490        'output_handler=',
96491        'open_basedir=',
96492        'safe_mode=0',
96493        'disable_functions=',
96494        'output_buffering=Off',
96495        'display_errors=1',
96496        'log_errors=0',
96497        'html_errors=0',
96498        'track_errors=1',
96499        'report_memleaks=0',
96500        'report_zend_debug=0',
96501        'docref_root=',
96502        'docref_ext=.html',
96503        'error_prepend_string=',
96504        'error_append_string=',
96505        'auto_prepend_file=',
96506        'auto_append_file=',
96507        'magic_quotes_runtime=0',
96508        'xdebug.default_enable=0',
96509        'allow_url_fopen=1',
96510    );
96511
96512    /**
96513     * An object that supports the PEAR_Common->log() signature, or null
96514     * @param PEAR_Common|null
96515     */
96516    function PEAR_RunTest($logger = null, $options = array())
96517    {
96518        if (!defined('E_DEPRECATED')) {
96519            define('E_DEPRECATED', 0);
96520        }
96521        if (!defined('E_STRICT')) {
96522            define('E_STRICT', 0);
96523        }
96524        $this->ini_overwrites[] = 'error_reporting=' . (E_ALL & ~(E_DEPRECATED | E_STRICT));
96525        if (is_null($logger)) {
96526            require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
96527            $logger = new PEAR_Common;
96528        }
96529        $this->_logger  = $logger;
96530        $this->_options = $options;
96531
96532        $conf = &PEAR_Config::singleton();
96533        $this->_php = $conf->get('php_bin');
96534    }
96535
96536    /**
96537     * Taken from php-src/run-tests.php
96538     *
96539     * @param string $commandline command name
96540     * @param array $env
96541     * @param string $stdin standard input to pass to the command
96542     * @return unknown
96543     */
96544    function system_with_timeout($commandline, $env = null, $stdin = null)
96545    {
96546        $data = '';
96547        if (version_compare(phpversion(), '5.0.0', '<')) {
96548            $proc = proc_open($commandline, array(
96549                0 => array('pipe', 'r'),
96550                1 => array('pipe', 'w'),
96551                2 => array('pipe', 'w')
96552                ), $pipes);
96553        } else {
96554            $proc = proc_open($commandline, array(
96555                0 => array('pipe', 'r'),
96556                1 => array('pipe', 'w'),
96557                2 => array('pipe', 'w')
96558                ), $pipes, null, $env, array('suppress_errors' => true));
96559        }
96560
96561        if (!$proc) {
96562            return false;
96563        }
96564
96565        if (is_string($stdin)) {
96566            fwrite($pipes[0], $stdin);
96567        }
96568        fclose($pipes[0]);
96569
96570        while (true) {
96571            /* hide errors from interrupted syscalls */
96572            $r = $pipes;
96573            $e = $w = null;
96574            $n = @stream_select($r, $w, $e, 60);
96575
96576            if ($n === 0) {
96577                /* timed out */
96578                $data .= "\n ** ERROR: process timed out **\n";
96579                proc_terminate($proc);
96580                return array(1234567890, $data);
96581            } else if ($n > 0) {
96582                $line = fread($pipes[1], 8192);
96583                if (strlen($line) == 0) {
96584                    /* EOF */
96585                    break;
96586                }
96587                $data .= $line;
96588            }
96589        }
96590        if (function_exists('proc_get_status')) {
96591            $stat = proc_get_status($proc);
96592            if ($stat['signaled']) {
96593                $data .= "\nTermsig=".$stat['stopsig'];
96594            }
96595        }
96596        $code = proc_close($proc);
96597        if (function_exists('proc_get_status')) {
96598            $code = $stat['exitcode'];
96599        }
96600        return array($code, $data);
96601    }
96602
96603    /**
96604     * Turns a PHP INI string into an array
96605     *
96606     * Turns -d "include_path=/foo/bar" into this:
96607     * array(
96608     *   'include_path' => array(
96609     *          'operator' => '-d',
96610     *          'value'    => '/foo/bar',
96611     *   )
96612     * )
96613     * Works both with quotes and without
96614     *
96615     * @param string an PHP INI string, -d "include_path=/foo/bar"
96616     * @return array
96617     */
96618    function iniString2array($ini_string)
96619    {
96620        if (!$ini_string) {
96621            return array();
96622        }
96623        $split = preg_split('/[\s]|=/', $ini_string, -1, PREG_SPLIT_NO_EMPTY);
96624        $key   = $split[1][0] == '"'                     ? substr($split[1], 1)     : $split[1];
96625        $value = $split[2][strlen($split[2]) - 1] == '"' ? substr($split[2], 0, -1) : $split[2];
96626        // FIXME review if this is really the struct to go with
96627        $array = array($key => array('operator' => $split[0], 'value' => $value));
96628        return $array;
96629    }
96630
96631    function settings2array($settings, $ini_settings)
96632    {
96633        foreach ($settings as $setting) {
96634            if (strpos($setting, '=') !== false) {
96635                $setting = explode('=', $setting, 2);
96636                $name  = trim(strtolower($setting[0]));
96637                $value = trim($setting[1]);
96638                $ini_settings[$name] = $value;
96639            }
96640        }
96641        return $ini_settings;
96642    }
96643
96644    function settings2params($ini_settings)
96645    {
96646        $settings = '';
96647        foreach ($ini_settings as $name => $value) {
96648            if (is_array($value)) {
96649                $operator = $value['operator'];
96650                $value    = $value['value'];
96651            } else {
96652                $operator = '-d';
96653            }
96654            $value = addslashes($value);
96655            $settings .= " $operator \"$name=$value\"";
96656        }
96657        return $settings;
96658    }
96659
96660    function _preparePhpBin($php, $file, $ini_settings)
96661    {
96662        $file = escapeshellarg($file);
96663        // This was fixed in php 5.3 and is not needed after that
96664        if (OS_WINDOWS && version_compare(PHP_VERSION, '5.3', '<')) {
96665            $cmd = '"'.escapeshellarg($php).' '.$ini_settings.' -f ' . $file .'"';
96666        } else {
96667            $cmd = $php . $ini_settings . ' -f ' . $file;
96668        }
96669
96670        return $cmd;
96671    }
96672
96673    function runPHPUnit($file, $ini_settings = '')
96674    {
96675        if (!file_exists($file) && file_exists(getcwd() . DIRECTORY_SEPARATOR . $file)) {
96676            $file = realpath(getcwd() . DIRECTORY_SEPARATOR . $file);
96677        } elseif (file_exists($file)) {
96678            $file = realpath($file);
96679        }
96680
96681        $cmd = $this->_preparePhpBin($this->_php, $file, $ini_settings);
96682        if (isset($this->_logger)) {
96683            $this->_logger->log(2, 'Running command "' . $cmd . '"');
96684        }
96685
96686        $savedir = getcwd(); // in case the test moves us around
96687        chdir(dirname($file));
96688        echo `$cmd`;
96689        chdir($savedir);
96690        return 'PASSED'; // we have no way of knowing this information so assume passing
96691    }
96692
96693    /**
96694     * Runs an individual test case.
96695     *
96696     * @param string       The filename of the test
96697     * @param array|string INI settings to be applied to the test run
96698     * @param integer      Number what the current running test is of the
96699     *                     whole test suite being runned.
96700     *
96701     * @return string|object Returns PASSED, WARNED, FAILED depending on how the
96702     *                       test came out.
96703     *                       PEAR Error when the tester it self fails
96704     */
96705    function run($file, $ini_settings = array(), $test_number = 1)
96706    {
96707        if (isset($this->_savephp)) {
96708            $this->_php = $this->_savephp;
96709            unset($this->_savephp);
96710        }
96711        if (empty($this->_options['cgi'])) {
96712            // try to see if php-cgi is in the path
96713            $res = $this->system_with_timeout('php-cgi -v');
96714            if (false !== $res && !(is_array($res) && in_array($res[0], array(-1, 127)))) {
96715                $this->_options['cgi'] = 'php-cgi';
96716            }
96717        }
96718        if (1 < $len = strlen($this->tests_count)) {
96719            $test_number = str_pad($test_number, $len, ' ', STR_PAD_LEFT);
96720            $test_nr = "[$test_number/$this->tests_count] ";
96721        } else {
96722            $test_nr = '';
96723        }
96724
96725        $file = realpath($file);
96726        $section_text = $this->_readFile($file);
96727        if (PEAR::isError($section_text)) {
96728            return $section_text;
96729        }
96730
96731        if (isset($section_text['POST_RAW']) && isset($section_text['UPLOAD'])) {
96732            return PEAR::raiseError("Cannot contain both POST_RAW and UPLOAD in test file: $file");
96733        }
96734
96735        $cwd = getcwd();
96736
96737        $pass_options = '';
96738        if (!empty($this->_options['ini'])) {
96739            $pass_options = $this->_options['ini'];
96740        }
96741
96742        if (is_string($ini_settings)) {
96743            $ini_settings = $this->iniString2array($ini_settings);
96744        }
96745
96746        $ini_settings = $this->settings2array($this->ini_overwrites, $ini_settings);
96747        if ($section_text['INI']) {
96748            if (strpos($section_text['INI'], '{PWD}') !== false) {
96749                $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']);
96750            }
96751            $ini = preg_split( "/[\n\r]+/", $section_text['INI']);
96752            $ini_settings = $this->settings2array($ini, $ini_settings);
96753        }
96754        $ini_settings = $this->settings2params($ini_settings);
96755        $shortname = str_replace($cwd . DIRECTORY_SEPARATOR, '', $file);
96756
96757        $tested = trim($section_text['TEST']);
96758        $tested.= !isset($this->_options['simple']) ? "[$shortname]" : ' ';
96759
96760        if (!empty($section_text['POST']) || !empty($section_text['POST_RAW']) ||
96761              !empty($section_text['UPLOAD']) || !empty($section_text['GET']) ||
96762              !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) {
96763            if (empty($this->_options['cgi'])) {
96764                if (!isset($this->_options['quiet'])) {
96765                    $this->_logger->log(0, "SKIP $test_nr$tested (reason: --cgi option needed for this test, type 'pear help run-tests')");
96766                }
96767                if (isset($this->_options['tapoutput'])) {
96768                    return array('ok', ' # skip --cgi option needed for this test, "pear help run-tests" for info');
96769                }
96770                return 'SKIPPED';
96771            }
96772            $this->_savephp = $this->_php;
96773            $this->_php = $this->_options['cgi'];
96774        }
96775
96776        $temp_dir = realpath(dirname($file));
96777        $main_file_name = basename($file, 'phpt');
96778        $diff_filename     = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'diff';
96779        $log_filename      = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'log';
96780        $exp_filename      = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'exp';
96781        $output_filename   = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'out';
96782        $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'mem';
96783        $temp_file         = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'php';
96784        $temp_skipif       = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'skip.php';
96785        $temp_clean        = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'clean.php';
96786        $tmp_post          = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');
96787
96788        // unlink old test results
96789        $this->_cleanupOldFiles($file);
96790
96791        // Check if test should be skipped.
96792        $res  = $this->_runSkipIf($section_text, $temp_skipif, $tested, $ini_settings);
96793        if (count($res) != 2) {
96794            return $res;
96795        }
96796        $info = $res['info'];
96797        $warn = $res['warn'];
96798
96799        // We've satisfied the preconditions - run the test!
96800        if (isset($this->_options['coverage']) && $this->xdebug_loaded) {
96801            $xdebug_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'xdebug';
96802            $text = "\n" . 'function coverage_shutdown() {' .
96803                    "\n" . '    $xdebug = var_export(xdebug_get_code_coverage(), true);';
96804            if (!function_exists('file_put_contents')) {
96805                $text .= "\n" . '    $fh = fopen(\'' . $xdebug_file . '\', "wb");' .
96806                        "\n" . '    if ($fh !== false) {' .
96807                        "\n" . '        fwrite($fh, $xdebug);' .
96808                        "\n" . '        fclose($fh);' .
96809                        "\n" . '    }';
96810            } else {
96811                $text .= "\n" . '    file_put_contents(\'' . $xdebug_file . '\', $xdebug);';
96812            }
96813
96814            // Workaround for http://pear.php.net/bugs/bug.php?id=17292
96815            $lines             = explode("\n", $section_text['FILE']);
96816            $numLines          = count($lines);
96817            $namespace         = '';
96818            $coverage_shutdown = 'coverage_shutdown';
96819
96820            if (
96821                substr($lines[0], 0, 2) == '<?' ||
96822                substr($lines[0], 0, 5) == '<?php'
96823            ) {
96824                unset($lines[0]);
96825            }
96826
96827
96828            for ($i = 0; $i < $numLines; $i++) {
96829                if (isset($lines[$i]) && substr($lines[$i], 0, 9) == 'namespace') {
96830                    $namespace         = substr($lines[$i], 10, -1);
96831                    $coverage_shutdown = $namespace . '\\coverage_shutdown';
96832                    $namespace         = "namespace " . $namespace . ";\n";
96833
96834                    unset($lines[$i]);
96835                    break;
96836                }
96837            }
96838
96839            $text .= "\n    xdebug_stop_code_coverage();" .
96840                "\n" . '} // end coverage_shutdown()' .
96841                "\n\n" . 'register_shutdown_function("' . $coverage_shutdown . '");';
96842            $text .= "\n" . 'xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);' . "\n";
96843
96844            $this->save_text($temp_file, "<?php\n" . $namespace . $text  . "\n" . implode("\n", $lines));
96845        } else {
96846            $this->save_text($temp_file, $section_text['FILE']);
96847        }
96848
96849        $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : '';
96850        $cmd = $this->_preparePhpBin($this->_php, $temp_file, $ini_settings);
96851        $cmd.= "$args 2>&1";
96852        if (isset($this->_logger)) {
96853            $this->_logger->log(2, 'Running command "' . $cmd . '"');
96854        }
96855
96856        // Reset environment from any previous test.
96857        $env = $this->_resetEnv($section_text, $temp_file);
96858
96859        $section_text = $this->_processUpload($section_text, $file);
96860        if (PEAR::isError($section_text)) {
96861            return $section_text;
96862        }
96863
96864        if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) {
96865            $post = trim($section_text['POST_RAW']);
96866            $raw_lines = explode("\n", $post);
96867
96868            $request = '';
96869            $started = false;
96870            foreach ($raw_lines as $i => $line) {
96871                if (empty($env['CONTENT_TYPE']) &&
96872                    preg_match('/^Content-Type:(.*)/i', $line, $res)) {
96873                    $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1]));
96874                    continue;
96875                }
96876                if ($started) {
96877                    $request .= "\n";
96878                }
96879                $started = true;
96880                $request .= $line;
96881            }
96882
96883            $env['CONTENT_LENGTH'] = strlen($request);
96884            $env['REQUEST_METHOD'] = 'POST';
96885
96886            $this->save_text($tmp_post, $request);
96887            $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
96888        } elseif (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) {
96889            $post = trim($section_text['POST']);
96890            $this->save_text($tmp_post, $post);
96891            $content_length = strlen($post);
96892
96893            $env['REQUEST_METHOD'] = 'POST';
96894            $env['CONTENT_TYPE']   = 'application/x-www-form-urlencoded';
96895            $env['CONTENT_LENGTH'] = $content_length;
96896
96897            $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
96898        } else {
96899            $env['REQUEST_METHOD'] = 'GET';
96900            $env['CONTENT_TYPE']   = '';
96901            $env['CONTENT_LENGTH'] = '';
96902        }
96903
96904        if (OS_WINDOWS && isset($section_text['RETURNS'])) {
96905            ob_start();
96906            system($cmd, $return_value);
96907            $out = ob_get_contents();
96908            ob_end_clean();
96909            $section_text['RETURNS'] = (int) trim($section_text['RETURNS']);
96910            $returnfail = ($return_value != $section_text['RETURNS']);
96911        } else {
96912            $returnfail = false;
96913            $stdin = isset($section_text['STDIN']) ? $section_text['STDIN'] : null;
96914            $out = $this->system_with_timeout($cmd, $env, $stdin);
96915            $return_value = $out[0];
96916            $out = $out[1];
96917        }
96918
96919        $output = preg_replace('/\r\n/', "\n", trim($out));
96920
96921        if (isset($tmp_post) && realpath($tmp_post) && file_exists($tmp_post)) {
96922            @unlink(realpath($tmp_post));
96923        }
96924        chdir($cwd); // in case the test moves us around
96925
96926        $this->_testCleanup($section_text, $temp_clean);
96927
96928        /* when using CGI, strip the headers from the output */
96929        $output = $this->_stripHeadersCGI($output);
96930
96931        if (isset($section_text['EXPECTHEADERS'])) {
96932            $testheaders = $this->_processHeaders($section_text['EXPECTHEADERS']);
96933            $missing = array_diff_assoc($testheaders, $this->_headers);
96934            $changed = '';
96935            foreach ($missing as $header => $value) {
96936                if (isset($this->_headers[$header])) {
96937                    $changed .= "-$header: $value\n+$header: ";
96938                    $changed .= $this->_headers[$header];
96939                } else {
96940                    $changed .= "-$header: $value\n";
96941                }
96942            }
96943            if ($missing) {
96944                // tack on failed headers to output:
96945                $output .= "\n====EXPECTHEADERS FAILURE====:\n$changed";
96946            }
96947        }
96948        // Does the output match what is expected?
96949        do {
96950            if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) {
96951                if (isset($section_text['EXPECTF'])) {
96952                    $wanted = trim($section_text['EXPECTF']);
96953                } else {
96954                    $wanted = trim($section_text['EXPECTREGEX']);
96955                }
96956                $wanted_re = preg_replace('/\r\n/', "\n", $wanted);
96957                if (isset($section_text['EXPECTF'])) {
96958                    $wanted_re = preg_quote($wanted_re, '/');
96959                    // Stick to basics
96960                    $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy
96961                    $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re);
96962                    $wanted_re = str_replace("%d", "[0-9]+", $wanted_re);
96963                    $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re);
96964                    $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re);
96965                    $wanted_re = str_replace("%c", ".", $wanted_re);
96966                    // %f allows two points "-.0.0" but that is the best *simple* expression
96967                }
96968
96969    /* DEBUG YOUR REGEX HERE
96970            var_dump($wanted_re);
96971            print(str_repeat('=', 80) . "\n");
96972            var_dump($output);
96973    */
96974                if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) {
96975                    if (file_exists($temp_file)) {
96976                        unlink($temp_file);
96977                    }
96978                    if (array_key_exists('FAIL', $section_text)) {
96979                        break;
96980                    }
96981                    if (!isset($this->_options['quiet'])) {
96982                        $this->_logger->log(0, "PASS $test_nr$tested$info");
96983                    }
96984                    if (isset($this->_options['tapoutput'])) {
96985                        return array('ok', ' - ' . $tested);
96986                    }
96987                    return 'PASSED';
96988                }
96989            } else {
96990                if (isset($section_text['EXPECTFILE'])) {
96991                    $f = $temp_dir . '/' . trim($section_text['EXPECTFILE']);
96992                    if (!($fp = @fopen($f, 'rb'))) {
96993                        return PEAR::raiseError('--EXPECTFILE-- section file ' .
96994                            $f . ' not found');
96995                    }
96996                    fclose($fp);
96997                    $section_text['EXPECT'] = file_get_contents($f);
96998                }
96999
97000                if (isset($section_text['EXPECT'])) {
97001                    $wanted = preg_replace('/\r\n/', "\n", trim($section_text['EXPECT']));
97002                } else {
97003                    $wanted = '';
97004                }
97005
97006                // compare and leave on success
97007                if (!$returnfail && 0 == strcmp($output, $wanted)) {
97008                    if (file_exists($temp_file)) {
97009                        unlink($temp_file);
97010                    }
97011                    if (array_key_exists('FAIL', $section_text)) {
97012                        break;
97013                    }
97014                    if (!isset($this->_options['quiet'])) {
97015                        $this->_logger->log(0, "PASS $test_nr$tested$info");
97016                    }
97017                    if (isset($this->_options['tapoutput'])) {
97018                        return array('ok', ' - ' . $tested);
97019                    }
97020                    return 'PASSED';
97021                }
97022            }
97023        } while (false);
97024
97025        if (array_key_exists('FAIL', $section_text)) {
97026            // we expect a particular failure
97027            // this is only used for testing PEAR_RunTest
97028            $expectf  = isset($section_text['EXPECTF']) ? $wanted_re : null;
97029            $faildiff = $this->generate_diff($wanted, $output, null, $expectf);
97030            $faildiff = preg_replace('/\r/', '', $faildiff);
97031            $wanted   = preg_replace('/\r/', '', trim($section_text['FAIL']));
97032            if ($faildiff == $wanted) {
97033                if (!isset($this->_options['quiet'])) {
97034                    $this->_logger->log(0, "PASS $test_nr$tested$info");
97035                }
97036                if (isset($this->_options['tapoutput'])) {
97037                    return array('ok', ' - ' . $tested);
97038                }
97039                return 'PASSED';
97040            }
97041            unset($section_text['EXPECTF']);
97042            $output = $faildiff;
97043            if (isset($section_text['RETURNS'])) {
97044                return PEAR::raiseError('Cannot have both RETURNS and FAIL in the same test: ' .
97045                    $file);
97046            }
97047        }
97048
97049        // Test failed so we need to report details.
97050        $txt = $warn ? 'WARN ' : 'FAIL ';
97051        $this->_logger->log(0, $txt . $test_nr . $tested . $info);
97052
97053        // write .exp
97054        $res = $this->_writeLog($exp_filename, $wanted);
97055        if (PEAR::isError($res)) {
97056            return $res;
97057        }
97058
97059        // write .out
97060        $res = $this->_writeLog($output_filename, $output);
97061        if (PEAR::isError($res)) {
97062            return $res;
97063        }
97064
97065        // write .diff
97066        $returns = isset($section_text['RETURNS']) ?
97067                        array(trim($section_text['RETURNS']), $return_value) : null;
97068        $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null;
97069        $data = $this->generate_diff($wanted, $output, $returns, $expectf);
97070        $res  = $this->_writeLog($diff_filename, $data);
97071        if (PEAR::isError($res)) {
97072            return $res;
97073        }
97074
97075        // write .log
97076        $data = "
97077---- EXPECTED OUTPUT
97078$wanted
97079---- ACTUAL OUTPUT
97080$output
97081---- FAILED
97082";
97083
97084        if ($returnfail) {
97085            $data .= "
97086---- EXPECTED RETURN
97087$section_text[RETURNS]
97088---- ACTUAL RETURN
97089$return_value
97090";
97091        }
97092
97093        $res = $this->_writeLog($log_filename, $data);
97094        if (PEAR::isError($res)) {
97095            return $res;
97096        }
97097
97098        if (isset($this->_options['tapoutput'])) {
97099            $wanted = explode("\n", $wanted);
97100            $wanted = "# Expected output:\n#\n#" . implode("\n#", $wanted);
97101            $output = explode("\n", $output);
97102            $output = "#\n#\n# Actual output:\n#\n#" . implode("\n#", $output);
97103            return array($wanted . $output . 'not ok', ' - ' . $tested);
97104        }
97105        return $warn ? 'WARNED' : 'FAILED';
97106    }
97107
97108    function generate_diff($wanted, $output, $rvalue, $wanted_re)
97109    {
97110        $w  = explode("\n", $wanted);
97111        $o  = explode("\n", $output);
97112        $wr = explode("\n", $wanted_re);
97113        $w1 = array_diff_assoc($w, $o);
97114        $o1 = array_diff_assoc($o, $w);
97115        $o2 = $w2 = array();
97116        foreach ($w1 as $idx => $val) {
97117            if (!$wanted_re || !isset($wr[$idx]) || !isset($o1[$idx]) ||
97118                  !preg_match('/^' . $wr[$idx] . '\\z/', $o1[$idx])) {
97119                $w2[sprintf("%03d<", $idx)] = sprintf("%03d- ", $idx + 1) . $val;
97120            }
97121        }
97122        foreach ($o1 as $idx => $val) {
97123            if (!$wanted_re || !isset($wr[$idx]) ||
97124                  !preg_match('/^' . $wr[$idx] . '\\z/', $val)) {
97125                $o2[sprintf("%03d>", $idx)] = sprintf("%03d+ ", $idx + 1) . $val;
97126            }
97127        }
97128        $diff = array_merge($w2, $o2);
97129        ksort($diff);
97130        $extra = $rvalue ? "##EXPECTED: $rvalue[0]\r\n##RETURNED: $rvalue[1]" : '';
97131        return implode("\r\n", $diff) . $extra;
97132    }
97133
97134    //  Write the given text to a temporary file, and return the filename.
97135    function save_text($filename, $text)
97136    {
97137        if (!$fp = fopen($filename, 'w')) {
97138            return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)");
97139        }
97140        fwrite($fp, $text);
97141        fclose($fp);
97142    if (1 < DETAILED) echo "
97143FILE $filename {{{
97144$text
97145}}}
97146";
97147    }
97148
97149    function _cleanupOldFiles($file)
97150    {
97151        $temp_dir = realpath(dirname($file));
97152        $mainFileName = basename($file, 'phpt');
97153        $diff_filename     = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'diff';
97154        $log_filename      = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'log';
97155        $exp_filename      = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'exp';
97156        $output_filename   = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'out';
97157        $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'mem';
97158        $temp_file         = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'php';
97159        $temp_skipif       = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'skip.php';
97160        $temp_clean        = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'clean.php';
97161        $tmp_post          = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');
97162
97163        // unlink old test results
97164        @unlink($diff_filename);
97165        @unlink($log_filename);
97166        @unlink($exp_filename);
97167        @unlink($output_filename);
97168        @unlink($memcheck_filename);
97169        @unlink($temp_file);
97170        @unlink($temp_skipif);
97171        @unlink($tmp_post);
97172        @unlink($temp_clean);
97173    }
97174
97175    function _runSkipIf($section_text, $temp_skipif, $tested, $ini_settings)
97176    {
97177        $info = '';
97178        $warn = false;
97179        if (array_key_exists('SKIPIF', $section_text) && trim($section_text['SKIPIF'])) {
97180            $this->save_text($temp_skipif, $section_text['SKIPIF']);
97181            $output = $this->system_with_timeout("$this->_php$ini_settings -f \"$temp_skipif\"");
97182            $output = $output[1];
97183            $loutput = ltrim($output);
97184            unlink($temp_skipif);
97185            if (!strncasecmp('skip', $loutput, 4)) {
97186                $skipreason = "SKIP $tested";
97187                if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) {
97188                    $skipreason .= '(reason: ' . $m[1] . ')';
97189                }
97190                if (!isset($this->_options['quiet'])) {
97191                    $this->_logger->log(0, $skipreason);
97192                }
97193                if (isset($this->_options['tapoutput'])) {
97194                    return array('ok', ' # skip ' . $reason);
97195                }
97196                return 'SKIPPED';
97197            }
97198
97199            if (!strncasecmp('info', $loutput, 4)
97200                && preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) {
97201                $info = " (info: $m[1])";
97202            }
97203
97204            if (!strncasecmp('warn', $loutput, 4)
97205                && preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) {
97206                $warn = true; /* only if there is a reason */
97207                $info = " (warn: $m[1])";
97208            }
97209        }
97210
97211        return array('warn' => $warn, 'info' => $info);
97212    }
97213
97214    function _stripHeadersCGI($output)
97215    {
97216        $this->headers = array();
97217        if (!empty($this->_options['cgi']) &&
97218              $this->_php == $this->_options['cgi'] &&
97219              preg_match("/^(.*?)(?:\n\n(.*)|\\z)/s", $output, $match)) {
97220            $output = isset($match[2]) ? trim($match[2]) : '';
97221            $this->_headers = $this->_processHeaders($match[1]);
97222        }
97223
97224        return $output;
97225    }
97226
97227    /**
97228     * Return an array that can be used with array_diff() to compare headers
97229     *
97230     * @param string $text
97231     */
97232    function _processHeaders($text)
97233    {
97234        $headers = array();
97235        $rh = preg_split("/[\n\r]+/", $text);
97236        foreach ($rh as $line) {
97237            if (strpos($line, ':')!== false) {
97238                $line = explode(':', $line, 2);
97239                $headers[trim($line[0])] = trim($line[1]);
97240            }
97241        }
97242        return $headers;
97243    }
97244
97245    function _readFile($file)
97246    {
97247        // Load the sections of the test file.
97248        $section_text = array(
97249            'TEST'   => '(unnamed test)',
97250            'SKIPIF' => '',
97251            'GET'    => '',
97252            'COOKIE' => '',
97253            'POST'   => '',
97254            'ARGS'   => '',
97255            'INI'    => '',
97256            'CLEAN'  => '',
97257        );
97258
97259        if (!is_file($file) || !$fp = fopen($file, "r")) {
97260            return PEAR::raiseError("Cannot open test file: $file");
97261        }
97262
97263        $section = '';
97264        while (!feof($fp)) {
97265            $line = fgets($fp);
97266
97267            // Match the beginning of a section.
97268            if (preg_match('/^--([_A-Z]+)--/', $line, $r)) {
97269                $section = $r[1];
97270                $section_text[$section] = '';
97271                continue;
97272            } elseif (empty($section)) {
97273                fclose($fp);
97274                return PEAR::raiseError("Invalid sections formats in test file: $file");
97275            }
97276
97277            // Add to the section text.
97278            $section_text[$section] .= $line;
97279        }
97280        fclose($fp);
97281
97282        return $section_text;
97283    }
97284
97285    function _writeLog($logname, $data)
97286    {
97287        if (!$log = fopen($logname, 'w')) {
97288            return PEAR::raiseError("Cannot create test log - $logname");
97289        }
97290        fwrite($log, $data);
97291        fclose($log);
97292    }
97293
97294    function _resetEnv($section_text, $temp_file)
97295    {
97296        $env = $_ENV;
97297        $env['REDIRECT_STATUS'] = '';
97298        $env['QUERY_STRING']    = '';
97299        $env['PATH_TRANSLATED'] = '';
97300        $env['SCRIPT_FILENAME'] = '';
97301        $env['REQUEST_METHOD']  = '';
97302        $env['CONTENT_TYPE']    = '';
97303        $env['CONTENT_LENGTH']  = '';
97304        if (!empty($section_text['ENV'])) {
97305            if (strpos($section_text['ENV'], '{PWD}') !== false) {
97306                $section_text['ENV'] = str_replace('{PWD}', dirname($temp_file), $section_text['ENV']);
97307            }
97308            foreach (explode("\n", trim($section_text['ENV'])) as $e) {
97309                $e = explode('=', trim($e), 2);
97310                if (!empty($e[0]) && isset($e[1])) {
97311                    $env[$e[0]] = $e[1];
97312                }
97313            }
97314        }
97315        if (array_key_exists('GET', $section_text)) {
97316            $env['QUERY_STRING'] = trim($section_text['GET']);
97317        } else {
97318            $env['QUERY_STRING'] = '';
97319        }
97320        if (array_key_exists('COOKIE', $section_text)) {
97321            $env['HTTP_COOKIE'] = trim($section_text['COOKIE']);
97322        } else {
97323            $env['HTTP_COOKIE'] = '';
97324        }
97325        $env['REDIRECT_STATUS'] = '1';
97326        $env['PATH_TRANSLATED'] = $temp_file;
97327        $env['SCRIPT_FILENAME'] = $temp_file;
97328
97329        return $env;
97330    }
97331
97332    function _processUpload($section_text, $file)
97333    {
97334        if (array_key_exists('UPLOAD', $section_text) && !empty($section_text['UPLOAD'])) {
97335            $upload_files = trim($section_text['UPLOAD']);
97336            $upload_files = explode("\n", $upload_files);
97337
97338            $request = "Content-Type: multipart/form-data; boundary=---------------------------20896060251896012921717172737\n" .
97339                       "-----------------------------20896060251896012921717172737\n";
97340            foreach ($upload_files as $fileinfo) {
97341                $fileinfo = explode('=', $fileinfo);
97342                if (count($fileinfo) != 2) {
97343                    return PEAR::raiseError("Invalid UPLOAD section in test file: $file");
97344                }
97345                if (!realpath(dirname($file) . '/' . $fileinfo[1])) {
97346                    return PEAR::raiseError("File for upload does not exist: $fileinfo[1] " .
97347                        "in test file: $file");
97348                }
97349                $file_contents = file_get_contents(dirname($file) . '/' . $fileinfo[1]);
97350                $fileinfo[1] = basename($fileinfo[1]);
97351                $request .= "Content-Disposition: form-data; name=\"$fileinfo[0]\"; filename=\"$fileinfo[1]\"\n";
97352                $request .= "Content-Type: text/plain\n\n";
97353                $request .= $file_contents . "\n" .
97354                    "-----------------------------20896060251896012921717172737\n";
97355            }
97356
97357            if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) {
97358                // encode POST raw
97359                $post = trim($section_text['POST']);
97360                $post = explode('&', $post);
97361                foreach ($post as $i => $post_info) {
97362                    $post_info = explode('=', $post_info);
97363                    if (count($post_info) != 2) {
97364                        return PEAR::raiseError("Invalid POST data in test file: $file");
97365                    }
97366                    $post_info[0] = rawurldecode($post_info[0]);
97367                    $post_info[1] = rawurldecode($post_info[1]);
97368                    $post[$i] = $post_info;
97369                }
97370                foreach ($post as $post_info) {
97371                    $request .= "Content-Disposition: form-data; name=\"$post_info[0]\"\n\n";
97372                    $request .= $post_info[1] . "\n" .
97373                        "-----------------------------20896060251896012921717172737\n";
97374                }
97375                unset($section_text['POST']);
97376            }
97377            $section_text['POST_RAW'] = $request;
97378        }
97379
97380        return $section_text;
97381    }
97382
97383    function _testCleanup($section_text, $temp_clean)
97384    {
97385        if ($section_text['CLEAN']) {
97386            // perform test cleanup
97387            $this->save_text($temp_clean, $section_text['CLEAN']);
97388            $output = $this->system_with_timeout("$this->_php $temp_clean  2>&1");
97389            if (strlen($output[1])) {
97390                echo "BORKED --CLEAN-- section! output:\n", $output[1];
97391            }
97392            if (file_exists($temp_clean)) {
97393                unlink($temp_clean);
97394            }
97395        }
97396    }
97397}
97398<?php
97399require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
97400require_once 'phar://install-pear-nozlib.phar/' . 'System.php';
97401require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Config.php';
97402require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Command.php';
97403require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
97404class PEAR_Start extends PEAR
97405{
97406    var $bin_dir;
97407    var $data_dir;
97408    var $cfg_dir;
97409    var $www_dir;
97410    var $install_pfc;
97411    var $corePackages =
97412        array(
97413            'Archive_Tar',
97414            'Console_Getopt',
97415            'PEAR',
97416            'Structures_Graph',
97417            'XML_Util',
97418        );
97419    var $local_dir = array();
97420    var $origpwd;
97421    var $pfc_packages = array(
97422            'DB',
97423            'Net_Socket',
97424            'Net_SMTP',
97425            'Mail',
97426            'XML_Parser',
97427            'XML_RPC',
97428            'PHPUnit'
97429        );
97430    var $php_dir;
97431    var $php_bin;
97432    var $pear_conf;
97433    var $validPHPBin = false;
97434    var $test_dir;
97435    var $download_dir;
97436    var $temp_dir;
97437    var $config =
97438        array(
97439            'prefix',
97440            'bin_dir',
97441            'php_dir',
97442            'doc_dir',
97443            'data_dir',
97444            'cfg_dir',
97445            'www_dir',
97446            'test_dir',
97447            'temp_dir',
97448            'download_dir',
97449            'pear_conf',
97450        );
97451    var $prefix;
97452    var $progress = 0;
97453    var $configPrompt =
97454        array(
97455            'prefix' => 'Installation base ($prefix)',
97456            'temp_dir' => 'Temporary directory for processing',
97457            'download_dir' => 'Temporary directory for downloads',
97458            'bin_dir' => 'Binaries directory',
97459            'php_dir' => 'PHP code directory ($php_dir)',
97460            'doc_dir' => 'Documentation directory',
97461            'data_dir' => 'Data directory',
97462            'cfg_dir' => 'User-modifiable configuration files directory',
97463            'www_dir' => 'Public Web Files directory',
97464            'test_dir' => 'Tests directory',
97465            'pear_conf' => 'Name of configuration file',
97466        );
97467
97468    var $localInstall;
97469    var $PEARConfig;
97470    var $tarball = array();
97471
97472    function PEAR_Start()
97473    {
97474        parent::PEAR();
97475        if (OS_WINDOWS) {
97476            $this->configPrompt['php_bin'] = 'Path to CLI php.exe';
97477            $this->config[] = 'php_bin';
97478            $this->prefix = getcwd();
97479
97480            if (!@is_dir($this->prefix)) {
97481                if (@is_dir('c:\php5')) {
97482                    $this->prefix = 'c:\php5';
97483                } elseif (@is_dir('c:\php4')) {
97484                    $this->prefix = 'c:\php4';
97485                } elseif (@is_dir('c:\php')) {
97486                    $this->prefix = 'c:\php';
97487                }
97488            }
97489
97490            $slash = "\\";
97491            if (strrpos($this->prefix, '\\') === (strlen($this->prefix) - 1)) {
97492                $slash = '';
97493            }
97494
97495            $this->localInstall = false;
97496            $this->bin_dir   = '$prefix';
97497            $this->temp_dir   = '$prefix' . $slash . 'tmp';
97498            $this->download_dir   = '$prefix' . $slash . 'tmp';
97499            $this->php_dir   = '$prefix' . $slash . 'pear';
97500            $this->doc_dir   = '$prefix' . $slash . 'docs';
97501            $this->data_dir  = '$prefix' . $slash . 'data';
97502            $this->test_dir  = '$prefix' . $slash . 'tests';
97503            $this->www_dir  = '$prefix' . $slash . 'www';
97504            $this->cfg_dir  = '$prefix' . $slash . 'cfg';
97505            $this->pear_conf = PEAR_CONFIG_SYSCONFDIR . '\\pear.ini';
97506            /*
97507             * Detects php.exe
97508             */
97509            $this->validPHPBin = true;
97510            if ($t = $this->safeGetenv('PHP_PEAR_PHP_BIN')) {
97511                $this->php_bin   = dirname($t);
97512            } elseif ($t = $this->safeGetenv('PHP_BIN')) {
97513                $this->php_bin   = dirname($t);
97514            } elseif ($t = System::which('php')) {
97515                $this->php_bin = dirname($t);
97516            } elseif (is_file($this->prefix . '\cli\php.exe')) {
97517                $this->php_bin = $this->prefix . '\cli';
97518            } elseif (is_file($this->prefix . '\php.exe')) {
97519                $this->php_bin = $this->prefix;
97520            }
97521            $phpexe = OS_WINDOWS ? '\\php.exe' : '/php';
97522            if ($this->php_bin && !is_file($this->php_bin . $phpexe)) {
97523                $this->php_bin = '';
97524            } else {
97525                if (strpos($this->php_bin, ':') === 0) {
97526                    $this->php_bin = getcwd() . DIRECTORY_SEPARATOR . $this->php_bin;
97527                }
97528            }
97529            if (!is_file($this->php_bin . $phpexe)) {
97530                if (is_file('c:/php/cli/php.exe')) {
97531                    $this->php_bin = 'c"\\php\\cli';
97532                } elseif (is_file('c:/php5/php.exe')) {
97533                    $this->php_bin = 'c:\\php5';
97534                } elseif (is_file('c:/php4/cli/php.exe')) {
97535                    $this->php_bin = 'c:\\php4\\cli';
97536                } else {
97537                    $this->validPHPBin = false;
97538                }
97539            }
97540        } else {
97541            $this->prefix = dirname(PHP_BINDIR);
97542            $this->pear_conf = PEAR_CONFIG_SYSCONFDIR . '/pear.conf';
97543            if (get_current_user() != 'root') {
97544                $this->prefix = $this->safeGetenv('HOME') . '/pear';
97545                $this->pear_conf = $this->safeGetenv('HOME') . '.pearrc';
97546            }
97547            $this->bin_dir   = '$prefix/bin';
97548            $this->php_dir   = '$prefix/share/pear';
97549            $this->temp_dir  = '/tmp/pear/install';
97550            $this->download_dir  = '/tmp/pear/install';
97551            $this->doc_dir   = '$prefix/docs';
97552            $this->www_dir   = '$prefix/www';
97553            $this->cfg_dir   = '$prefix/cfg';
97554            $this->data_dir  = '$prefix/data';
97555            $this->test_dir  = '$prefix/tests';
97556            // check if the user has installed PHP with PHP or GNU layout
97557            if (@is_dir("$this->prefix/lib/php/.registry")) {
97558                $this->php_dir = '$prefix/lib/php';
97559            } elseif (@is_dir("$this->prefix/share/pear/lib/.registry")) {
97560                $this->php_dir = '$prefix/share/pear/lib';
97561                $this->doc_dir   = '$prefix/share/pear/docs';
97562                $this->data_dir  = '$prefix/share/pear/data';
97563                $this->test_dir  = '$prefix/share/pear/tests';
97564            } elseif (@is_dir("$this->prefix/share/php/.registry")) {
97565                $this->php_dir = '$prefix/share/php';
97566            }
97567        }
97568    }
97569
97570    function safeGetenv($var)
97571    {
97572        if (is_array($_ENV) && isset($_ENV[$var])) {
97573            return $_ENV[$var];
97574        }
97575
97576        return getenv($var);
97577    }
97578
97579    function show($stuff)
97580    {
97581        print $stuff;
97582    }
97583
97584    function locatePackagesToInstall()
97585    {
97586        $dp = @opendir(dirname(__FILE__) . '/go-pear-tarballs');
97587        if (empty($dp)) {
97588            return PEAR::raiseError("while locating packages to install: opendir('" .
97589                dirname(__FILE__) . "/go-pear-tarballs') failed");
97590        }
97591
97592        $potentials = array();
97593        while (false !== ($entry = readdir($dp))) {
97594            if ($entry{0} == '.' || !in_array(substr($entry, -4), array('.tar', '.tgz'))) {
97595                continue;
97596            }
97597            $potentials[] = $entry;
97598        }
97599
97600        closedir($dp);
97601        $notfound = array();
97602        foreach ($this->corePackages as $package) {
97603            foreach ($potentials as $i => $candidate) {
97604                if (preg_match('/^' . $package . '-' . _PEAR_COMMON_PACKAGE_VERSION_PREG
97605                      . '\.(tar|tgz)\\z/', $candidate)) {
97606                    $this->tarball[$package] = dirname(__FILE__) . '/go-pear-tarballs/' . $candidate;
97607                    unset($potentials[$i]);
97608                    continue 2;
97609                }
97610            }
97611
97612            $notfound[] = $package;
97613        }
97614
97615        if (count($notfound)) {
97616            return PEAR::raiseError("No tarballs found for core packages: " .
97617                    implode(', ', $notfound));
97618        }
97619
97620        $this->tarball = array_merge($this->tarball, $potentials);
97621    }
97622
97623    function setupTempStuff()
97624    {
97625        if (!($this->ptmp = System::mktemp(array('-d')))) {
97626            $this->show("System's Tempdir failed, trying to use \$prefix/tmp ...");
97627            $res = System::mkDir(array($this->prefix . '/tmp'));
97628            if (!$res) {
97629                return PEAR::raiseError('mkdir ' . $this->prefix . '/tmp ... failed');
97630            }
97631
97632            $_temp = tempnam($this->prefix . '/tmp', 'gope');
97633            System::rm(array('-rf', $_temp));
97634            System::mkdir(array('-p','-m', '0700', $_temp));
97635            $this->ptmp = $this->prefix . '/tmp';
97636            $ok = @chdir($this->ptmp);
97637
97638            if (!$ok) { // This should not happen, really ;)
97639                $this->bail('chdir ' . $this->ptmp . ' ... failed');
97640            }
97641
97642            print "ok\n";
97643
97644            // Adjust TEMPDIR envvars
97645            if (!isset($_ENV)) {
97646                $_ENV = array();
97647            };
97648            $_ENV['TMPDIR'] = $_ENV['TEMP'] = $this->prefix . '/tmp';
97649        }
97650
97651        return @chdir($this->ptmp);
97652    }
97653
97654    /**
97655     * Try to detect the kind of SAPI used by the
97656     * the given php.exe.
97657     * @author Pierrre-Alain Joye
97658     */
97659    function win32DetectPHPSAPI()
97660    {
97661        if ($this->php_bin != '') {
97662            if (OS_WINDOWS) {
97663                exec('"' . $this->php_bin . '\\php.exe" -v', $res);
97664            } else {
97665                exec('"' . $this->php_bin . '/php" -v', $res);
97666            }
97667
97668            if (is_array($res)) {
97669                if (isset($res[0]) && strpos($res[0],"(cli)")) {
97670                    return 'cli';
97671                }
97672
97673                if (isset($res[0]) && strpos($res[0],"cgi")) {
97674                    return 'cgi';
97675                }
97676
97677                if (isset($res[0]) && strpos($res[0],"cgi-fcgi")) {
97678                    return 'cgi';
97679                }
97680
97681                return 'unknown';
97682            }
97683        }
97684
97685        return 'unknown';
97686    }
97687
97688    function doInstall()
97689    {
97690        print "Beginning install...\n";
97691        // finish php_bin config
97692        if (OS_WINDOWS) {
97693            $this->php_bin .= '\\php.exe';
97694        } else {
97695            $this->php_bin .= '/php';
97696        }
97697        $this->PEARConfig = &PEAR_Config::singleton($this->pear_conf, $this->pear_conf);
97698        $this->PEARConfig->set('preferred_state', 'stable');
97699        foreach ($this->config as $var) {
97700            if ($var == 'pear_conf' || $var == 'prefix') {
97701                continue;
97702            }
97703            $this->PEARConfig->set($var, $this->$var);
97704        }
97705
97706        $this->PEARConfig->store();
97707//       $this->PEARConfig->set('verbose', 6);
97708        print "Configuration written to $this->pear_conf...\n";
97709        $this->registry = &$this->PEARConfig->getRegistry();
97710        print "Initialized registry...\n";
97711        $install = &PEAR_Command::factory('install', $this->PEARConfig);
97712        print "Preparing to install...\n";
97713        $options = array(
97714            'nodeps' => true,
97715            'force' => true,
97716            'upgrade' => true,
97717            );
97718        foreach ($this->tarball as $pkg => $src) {
97719            print "installing $src...\n";
97720        }
97721        $install->run('install', $options, array_values($this->tarball));
97722    }
97723
97724    function postProcessConfigVars()
97725    {
97726        foreach ($this->config as $n => $var) {
97727            for ($m = 1; $m <= count($this->config); $m++) {
97728                $var2 = $this->config[$m];
97729                $this->$var = str_replace('$'.$var2, $this->$var2, $this->$var);
97730            }
97731        }
97732
97733        foreach ($this->config as $var) {
97734            $dir = $this->$var;
97735
97736            if (!preg_match('/_dir\\z/', $var)) {
97737                continue;
97738            }
97739
97740            if (!@is_dir($dir)) {
97741                if (!System::mkDir(array('-p', $dir))) {
97742                    $root = OS_WINDOWS ? 'administrator' : 'root';
97743                    return PEAR::raiseError("Unable to create {$this->configPrompt[$var]} $dir.
97744Run this script as $root or pick another location.\n");
97745                }
97746            }
97747        }
97748    }
97749
97750    /**
97751     * Get the php.ini file used with the current
97752     * process or with the given php.exe
97753     *
97754     * Horrible hack, but well ;)
97755     *
97756     * Not used yet, will add the support later
97757     * @author Pierre-Alain Joye <paj@pearfr.org>
97758     */
97759    function getPhpiniPath()
97760    {
97761        $pathIni = get_cfg_var('cfg_file_path');
97762        if ($pathIni && is_file($pathIni)) {
97763            return $pathIni;
97764        }
97765
97766        // Oh well, we can keep this too :)
97767        // I dunno if get_cfg_var() is safe on every OS
97768        if (OS_WINDOWS) {
97769            // on Windows, we can be pretty sure that there is a php.ini
97770            // file somewhere
97771            do {
97772                $php_ini = PHP_CONFIG_FILE_PATH . DIRECTORY_SEPARATOR . 'php.ini';
97773                if (@file_exists($php_ini)) {
97774                    break;
97775                }
97776                $php_ini = 'c:\winnt\php.ini';
97777                if (@file_exists($php_ini)) {
97778                    break;
97779                }
97780                $php_ini = 'c:\windows\php.ini';
97781            } while (false);
97782        } else {
97783            $php_ini = PHP_CONFIG_FILE_PATH . DIRECTORY_SEPARATOR . 'php.ini';
97784        }
97785
97786        if (@is_file($php_ini)) {
97787            return $php_ini;
97788        }
97789
97790        // We re running in hackz&troubles :)
97791        ob_implicit_flush(false);
97792        ob_start();
97793        phpinfo(INFO_GENERAL);
97794        $strInfo = ob_get_contents();
97795        ob_end_clean();
97796        ob_implicit_flush(true);
97797
97798        if (php_sapi_name() != 'cli') {
97799            $strInfo = strip_tags($strInfo,'<td>');
97800            $arrayInfo = explode("</td>", $strInfo );
97801            $cli = false;
97802        } else {
97803            $arrayInfo = explode("\n", $strInfo);
97804            $cli = true;
97805        }
97806
97807        foreach ($arrayInfo as $val) {
97808            if (strpos($val,"php.ini")) {
97809                if ($cli) {
97810                    list(,$pathIni) = explode('=>', $val);
97811                } else {
97812                    $pathIni = strip_tags(trim($val));
97813                }
97814                $pathIni = trim($pathIni);
97815                if (is_file($pathIni)) {
97816                    return $pathIni;
97817                }
97818            }
97819        }
97820
97821        return false;
97822    }
97823}
97824?>
97825<?php
97826require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Start.php';
97827class PEAR_Start_CLI extends PEAR_Start
97828{
97829
97830    var $descLength;
97831    var $descFormat;
97832    var $first;
97833    var $last;
97834    var $origpwd;
97835    var $tty;
97836
97837    function PEAR_Start_CLI()
97838    {
97839        parent::PEAR_Start();
97840        ini_set('html_errors', 0);
97841        define('WIN32GUI', OS_WINDOWS && php_sapi_name() == 'cli' && System::which('cscript'));
97842        $this->tty = OS_WINDOWS ? @fopen('\con', 'r') : @fopen('/dev/tty', 'r');
97843
97844        if (!$this->tty) {
97845            $this->tty = fopen('php://stdin', 'r');
97846        }
97847        $this->origpwd = getcwd();
97848        $this->config = array_keys($this->configPrompt);
97849
97850        // make indices run from 1...
97851        array_unshift($this->config, "");
97852        unset($this->config[0]);
97853        reset($this->config);
97854        $this->descLength = max(array_map('strlen', $this->configPrompt));
97855        $this->descFormat = "%-{$this->descLength}s";
97856        $this->first = key($this->config);
97857        end($this->config);
97858        $this->last = key($this->config);
97859        PEAR_Command::setFrontendType('CLI');
97860    }
97861
97862    function _PEAR_Start_CLI()
97863    {
97864        if ($this->tty) {
97865            @fclose($this->tty);
97866        }
97867    }
97868
97869    function run()
97870    {
97871        if (PEAR::isError($err = $this->locatePackagesToInstall())) {
97872            return $err;
97873        }
97874        $this->startupQuestion();
97875        $this->setupTempStuff();
97876        $this->getInstallLocations();
97877        $this->displayPreamble();
97878        if (PEAR::isError($err = $this->postProcessConfigVars())) {
97879            return $err;
97880        }
97881        $this->doInstall();
97882        $this->finishInstall();
97883    }
97884
97885    function startupQuestion()
97886    {
97887        if (OS_WINDOWS) {
97888            print "
97889Are you installing a system-wide PEAR or a local copy?
97890(system|local) [system] : ";
97891            $tmp = trim(fgets($this->tty, 1024));
97892            if (!empty($tmp) && strtolower($tmp) !== 'system') {
97893                print "Please confirm local copy by typing 'yes' : ";
97894                $tmp = trim(fgets($this->tty, 1024));
97895                if (strtolower($tmp) == 'yes') {
97896                    $slash = "\\";
97897                    if (strrpos($this->prefix, '\\') === (strlen($this->prefix) - 1)) {
97898                        $slash = '';
97899                    }
97900
97901                    $this->localInstall = true;
97902                    $this->pear_conf = '$prefix' . $slash . 'pear.ini';
97903                }
97904            }
97905        } else {
97906            if (get_current_user() == 'root') {
97907                return;
97908            }
97909            $this->pear_conf = $this->safeGetenv('HOME') . '/.pearrc';
97910        }
97911    }
97912
97913    function getInstallLocations()
97914    {
97915        while (true) {
97916            print "
97917Below is a suggested file layout for your new PEAR installation.  To
97918change individual locations, type the number in front of the
97919directory.  Type 'all' to change all of them or simply press Enter to
97920accept these locations.
97921
97922";
97923
97924            foreach ($this->config as $n => $var) {
97925                $fullvar = $this->$var;
97926                foreach ($this->config as $blah => $unused) {
97927                    foreach ($this->config as $m => $var2) {
97928                        $fullvar = str_replace('$'.$var2, $this->$var2, $fullvar);
97929                    }
97930                }
97931                printf("%2d. $this->descFormat : %s\n", $n, $this->configPrompt[$var], $fullvar);
97932            }
97933
97934            print "\n$this->first-$this->last, 'all' or Enter to continue: ";
97935            $tmp = trim(fgets($this->tty, 1024));
97936            if (empty($tmp)) {
97937                if (OS_WINDOWS && !$this->validPHPBin) {
97938                    echo "**ERROR**
97939Please, enter the php.exe path.
97940
97941";
97942                } else {
97943                    break;
97944                }
97945            }
97946
97947            if (isset($this->config[(int)$tmp])) {
97948                $var = $this->config[(int)$tmp];
97949                $desc = $this->configPrompt[$var];
97950                $current = $this->$var;
97951                if (WIN32GUI && $var != 'pear_conf'){
97952                    $tmp = $this->win32BrowseForFolder("Choose a Folder for $desc [$current] :");
97953                    $tmp.= '\\';
97954                } else {
97955                    print "(Use \$prefix as a shortcut for '$this->prefix', etc.)
97956$desc [$current] : ";
97957                    $tmp = trim(fgets($this->tty, 1024));
97958                }
97959                $old = $this->$var;
97960                $this->$var = $$var = $tmp;
97961                if (OS_WINDOWS && $var=='php_bin') {
97962                    if ($this->validatePhpExecutable($tmp)) {
97963                        $this->php_bin = $tmp;
97964                    } else {
97965                        $this->php_bin = $old;
97966                    }
97967                }
97968            } elseif ($tmp == 'all') {
97969                foreach ($this->config as $n => $var) {
97970                    $desc = $this->configPrompt[$var];
97971                    $current = $this->$var;
97972                    print "$desc [$current] : ";
97973                    $tmp = trim(fgets($this->tty, 1024));
97974                    if (!empty($tmp)) {
97975                        $this->$var = $tmp;
97976                    }
97977                }
97978            }
97979        }
97980    }
97981
97982    function validatePhpExecutable($tmp)
97983    {
97984        if (OS_WINDOWS) {
97985            if (strpos($tmp, 'php.exe')) {
97986                $tmp = str_replace('php.exe', '', $tmp);
97987            }
97988            if (file_exists($tmp . DIRECTORY_SEPARATOR . 'php.exe')) {
97989                $tmp = $tmp . DIRECTORY_SEPARATOR . 'php.exe';
97990                $this->php_bin_sapi = $this->win32DetectPHPSAPI();
97991                if ($this->php_bin_sapi=='cgi'){
97992                    print "
97993******************************************************************************
97994NOTICE! We found php.exe under $this->php_bin, it uses a $this->php_bin_sapi SAPI.
97995PEAR commandline tool works well with it.
97996If you have a CLI php.exe available, we recommend using it.
97997
97998Press Enter to continue...";
97999                    $tmp = trim(fgets($this->tty, 1024));
98000                } elseif ($this->php_bin_sapi=='unknown') {
98001                    print "
98002******************************************************************************
98003WARNING! We found php.exe under $this->php_bin, it uses an $this->php_bin_sapi SAPI.
98004PEAR commandline tool has NOT been tested with it.
98005If you have a CLI (or CGI) php.exe available, we strongly recommend using it.
98006
98007Press Enter to continue...";
98008                    $tmp = trim(fgets($this->tty, 1024));
98009                }
98010                echo "php.exe (sapi: $this->php_bin_sapi) found.\n\n";
98011                return $this->validPHPBin = true;
98012            } else {
98013                echo "**ERROR**: not a folder, or no php.exe found in this folder.
98014Press Enter to continue...";
98015                $tmp = trim(fgets($this->tty, 1024));
98016                return $this->validPHPBin = false;
98017            }
98018        }
98019    }
98020
98021    /**
98022     * Create a vbs script to browse the getfolder dialog, called
98023     * by cscript, if it's available.
98024     * $label is the label text in the header of the dialog box
98025     *
98026     * TODO:
98027     * - Do not show Control panel
98028     * - Replace WSH with calls to w32 as soon as callbacks work
98029     * @author Pierrre-Alain Joye
98030     */
98031    function win32BrowseForFolder($label)
98032    {
98033        static $wshSaved=false;
98034        static $cscript='';
98035    $wsh_browserfolder = 'Option Explicit
98036Dim ArgObj, var1, var2, sa, sFld
98037Set ArgObj = WScript.Arguments
98038Const BIF_EDITBOX = &H10
98039Const BIF_NEWDIALOGSTYLE = &H40
98040Const BIF_RETURNONLYFSDIRS   = &H0001
98041Const BIF_DONTGOBELOWDOMAIN  = &H0002
98042Const BIF_STATUSTEXT         = &H0004
98043Const BIF_RETURNFSANCESTORS  = &H0008
98044Const BIF_VALIDATE           = &H0020
98045Const BIF_BROWSEFORCOMPUTER  = &H1000
98046Const BIF_BROWSEFORPRINTER   = &H2000
98047Const BIF_BROWSEINCLUDEFILES = &H4000
98048Const OFN_LONGNAMES = &H200000
98049Const OFN_NOLONGNAMES = &H40000
98050Const ssfDRIVES = &H11
98051Const ssfNETWORK = &H12
98052Set sa = CreateObject("Shell.Application")
98053var1=ArgObj(0)
98054Set sFld = sa.BrowseForFolder(0, var1, BIF_EDITBOX + BIF_VALIDATE + BIF_BROWSEINCLUDEFILES + BIF_RETURNFSANCESTORS+BIF_NEWDIALOGSTYLE , ssfDRIVES )
98055if not sFld is nothing Then
98056    if not left(sFld.items.item.path,1)=":" Then
98057        WScript.Echo sFld.items.item.path
98058    Else
98059        WScript.Echo "invalid"
98060    End If
98061Else
98062    WScript.Echo "cancel"
98063End If
98064';
98065        if( !$wshSaved){
98066            $cscript = $this->ptmp . DIRECTORY_SEPARATOR . "bf.vbs";
98067            $fh = fopen($cscript, "wb+");
98068            fwrite($fh, $wsh_browserfolder, strlen($wsh_browserfolder));
98069            fclose($fh);
98070            $wshSaved  = true;
98071        }
98072
98073        exec('cscript ' . escapeshellarg($cscript) . ' "' . escapeshellarg($label) . '" //noLogo', $arPath);
98074        if (!count($arPath) || $arPath[0]=='' || $arPath[0]=='cancel') {
98075            return '';
98076        } elseif ($arPath[0]=='invalid') {
98077            echo "Invalid Path.\n";
98078            return '';
98079        }
98080
98081        @unlink($cscript);
98082        return $arPath[0];
98083    }
98084
98085    function displayPreamble()
98086    {
98087        if (OS_WINDOWS) {
98088            /*
98089             * Checks PHP SAPI version under windows/CLI
98090             */
98091            if ($this->php_bin == '') {
98092                print "
98093We do not find any php.exe, please select the php.exe folder (CLI is
98094recommended, usually in c:\php\cli\php.exe)
98095";
98096                $this->validPHPBin = false;
98097            } elseif (strlen($this->php_bin)) {
98098                $this->php_bin_sapi = $this->win32DetectPHPSAPI();
98099                $this->validPHPBin = true;
98100                switch ($this->php_bin_sapi) {
98101                    case 'cli':
98102                    break;
98103                    case 'cgi':
98104                    case 'cgi-fcgi':
98105                        print "
98106*NOTICE*
98107We found php.exe under $this->php_bin, it uses a $this->php_bin_sapi SAPI. PEAR commandline
98108tool works well with it, if you have a CLI php.exe available, we
98109recommend using it.
98110";
98111                    break;
98112                    default:
98113                        print "
98114*WARNING*
98115We found php.exe under $this->php_bin, it uses an unknown SAPI. PEAR commandline
98116tool has not been tested with it, if you have a CLI (or CGI) php.exe available,
98117we strongly recommend using it.
98118
98119";
98120                    break;
98121                }
98122            }
98123        }
98124    }
98125
98126    function finishInstall()
98127    {
98128        $sep = OS_WINDOWS ? ';' : ':';
98129        $include_path = explode($sep, ini_get('include_path'));
98130        if (OS_WINDOWS) {
98131            $found = false;
98132            $t = strtolower($this->php_dir);
98133            foreach ($include_path as $path) {
98134                if ($t == strtolower($path)) {
98135                    $found = true;
98136                    break;
98137                }
98138            }
98139        } else {
98140            $found = in_array($this->php_dir, $include_path);
98141        }
98142        if (!$found) {
98143            print "
98144******************************************************************************
98145WARNING!  The include_path defined in the currently used php.ini does not
98146contain the PEAR PHP directory you just specified:
98147<$this->php_dir>
98148If the specified directory is also not in the include_path used by
98149your scripts, you will have problems getting any PEAR packages working.
98150";
98151
98152            if ($php_ini = $this->getPhpiniPath()) {
98153                print "\n\nWould you like to alter php.ini <$php_ini>? [Y/n] : ";
98154                $alter_phpini = !stristr(fgets($this->tty, 1024), "n");
98155                if ($alter_phpini) {
98156                    $this->alterPhpIni($php_ini);
98157                } else {
98158                    if (OS_WINDOWS) {
98159                        print "
98160Please look over your php.ini file to make sure
98161$this->php_dir is in your include_path.";
98162                    } else {
98163                        print "
98164I will add a workaround for this in the 'pear' command to make sure
98165the installer works, but please look over your php.ini or Apache
98166configuration to make sure $this->php_dir is in your include_path.
98167";
98168                    }
98169                }
98170            }
98171
98172        print "
98173Current include path           : ".ini_get('include_path')."
98174Configured directory           : $this->php_dir
98175Currently used php.ini (guess) : $php_ini
98176";
98177
98178            print "Press Enter to continue: ";
98179            fgets($this->tty, 1024);
98180        }
98181
98182        $pear_cmd = $this->bin_dir . DIRECTORY_SEPARATOR . 'pear';
98183        $pear_cmd = OS_WINDOWS ? strtolower($pear_cmd).'.bat' : $pear_cmd;
98184
98185        // check that the installed pear and the one in the path are the same (if any)
98186        $pear_old = System::which(OS_WINDOWS ? 'pear.bat' : 'pear', $this->bin_dir);
98187        if ($pear_old && ($pear_old != $pear_cmd)) {
98188            // check if it is a link or symlink
98189            $islink = OS_WINDOWS ? false : is_link($pear_old) ;
98190            if ($islink && readlink($pear_old) != $pear_cmd) {
98191                print "\n** WARNING! The link $pear_old does not point to the " .
98192                      "installed $pear_cmd\n";
98193            } elseif (!$this->localInstall && is_writable($pear_old) && !is_dir($pear_old)) {
98194                rename($pear_old, "{$pear_old}_old");
98195                print "\n** WARNING! Backed up old pear to {$pear_old}_old\n";
98196            } else {
98197                print "\n** WARNING! Old version found at $pear_old, please remove it or ".
98198                      "be sure to use the new $pear_cmd command\n";
98199            }
98200        }
98201
98202        print "\nThe 'pear' command is now at your service at $pear_cmd\n";
98203
98204        // Alert the user if the pear cmd is not in PATH
98205        $old_dir = $pear_old ? dirname($pear_old) : false;
98206        if (!$this->which('pear', $old_dir)) {
98207            print "
98208** The 'pear' command is not currently in your PATH, so you need to
98209** use '$pear_cmd' until you have added
98210** '$this->bin_dir' to your PATH environment variable.
98211
98212";
98213
98214        print "Run it without parameters to see the available actions, try 'pear list'
98215to see what packages are installed, or 'pear help' for help.
98216
98217For more information about PEAR, see:
98218
98219  http://pear.php.net/faq.php
98220  http://pear.php.net/manual/
98221
98222Thanks for using go-pear!
98223
98224";
98225        }
98226
98227        if (OS_WINDOWS && !$this->localInstall) {
98228            $this->win32CreateRegEnv();
98229        }
98230    }
98231
98232    /**
98233     * System::which() does not allow path exclusion
98234     */
98235    function which($program, $dont_search_in = false)
98236    {
98237        if (OS_WINDOWS) {
98238            if ($_path = $this->safeGetEnv('Path')) {
98239                $dirs = explode(';', $_path);
98240            } else {
98241                $dirs = explode(';', $this->safeGetEnv('PATH'));
98242            }
98243            foreach ($dirs as $i => $dir) {
98244                $dirs[$i] = strtolower(realpath($dir));
98245            }
98246            if ($dont_search_in) {
98247                $dont_search_in = strtolower(realpath($dont_search_in));
98248            }
98249            if ($dont_search_in &&
98250                ($key = array_search($dont_search_in, $dirs)) !== false)
98251            {
98252                unset($dirs[$key]);
98253            }
98254
98255            foreach ($dirs as $dir) {
98256                $dir = str_replace('\\\\', '\\', $dir);
98257                if (!strlen($dir)) {
98258                    continue;
98259                }
98260                if ($dir{strlen($dir) - 1} != '\\') {
98261                    $dir .= '\\';
98262                }
98263                $tmp = $dir . $program;
98264                $info = pathinfo($tmp);
98265                if (isset($info['extension']) && in_array(strtolower($info['extension']),
98266                      array('exe', 'com', 'bat', 'cmd'))) {
98267                    if (file_exists($tmp)) {
98268                        return strtolower($tmp);
98269                    }
98270                } elseif (file_exists($ret = $tmp . '.exe') ||
98271                    file_exists($ret = $tmp . '.com') ||
98272                    file_exists($ret = $tmp . '.bat') ||
98273                    file_exists($ret = $tmp . '.cmd')) {
98274                    return strtolower($ret);
98275                }
98276            }
98277        } else {
98278            $dirs = explode(':', $this->safeGetEnv('PATH'));
98279            if ($dont_search_in &&
98280                ($key = array_search($dont_search_in, $dirs)) !== false)
98281            {
98282                unset($dirs[$key]);
98283            }
98284            foreach ($dirs as $dir) {
98285                if (is_executable("$dir/$program")) {
98286                    return "$dir/$program";
98287                }
98288            }
98289        }
98290        return false;
98291    }
98292
98293    /**
98294     * Not optimized, but seems to work, if some nice
98295     * peardev will test it? :)
98296     *
98297     * @author Pierre-Alain Joye <paj@pearfr.org>
98298     */
98299    function alterPhpIni($pathIni='')
98300    {
98301        $foundAt = array();
98302        $iniSep = OS_WINDOWS ? ';' : ':';
98303
98304        if ($pathIni=='') {
98305            $pathIni =  $this->getPhpiniPath();
98306        }
98307
98308        $arrayIni = file($pathIni);
98309        $i=0;
98310        $found=0;
98311
98312        // Looks for each active include_path directives
98313        foreach ($arrayIni as $iniLine) {
98314            $iniLine = trim($iniLine);
98315            $iniLine = str_replace(array("\n", "\r"), array('', ''), $iniLine);
98316            if (preg_match("/^\s*include_path/", $iniLine)) {
98317                $foundAt[] = $i;
98318                $found++;
98319            }
98320            $i++;
98321        }
98322
98323        if ($found) {
98324            $includeLine = $arrayIni[$foundAt[0]];
98325            list(, $currentPath) = explode('=', $includeLine);
98326
98327            $currentPath = trim($currentPath);
98328            if (substr($currentPath,0,1) == '"') {
98329                $currentPath = substr($currentPath, 1, strlen($currentPath) - 2);
98330            }
98331
98332            $arrayPath = explode($iniSep, $currentPath);
98333            $newPath = array();
98334            if ($arrayPath[0]=='.') {
98335                $newPath[0] = '.';
98336                $newPath[1] = $this->php_dir;
98337                array_shift($arrayPath);
98338            } else {
98339                $newPath[0] = $this->php_dir;
98340            }
98341
98342            foreach ($arrayPath as $path) {
98343                $newPath[]= $path;
98344            }
98345        } else {
98346            $newPath = array();
98347            $newPath[0] = '.';
98348            $newPath[1] = $this->php_dir;
98349            $foundAt[] = count($arrayIni); // add a new line if none is present
98350        }
98351        $nl = OS_WINDOWS ? "\r\n" : "\n";
98352        $includepath = 'include_path="' . implode($iniSep,$newPath) . '"';
98353        $newInclude = "$nl$nl;***** Added by go-pear$nl" .
98354                       $includepath .
98355                       $nl . ";*****" .
98356                       $nl . $nl;
98357
98358        $arrayIni[$foundAt[0]] = $newInclude;
98359
98360        for ($i=1; $i<$found; $i++) {
98361            $arrayIni[$foundAt[$i]]=';' . trim($arrayIni[$foundAt[$i]]);
98362        }
98363
98364        $newIni = implode("", $arrayIni);
98365        if (!($fh = @fopen($pathIni, "wb+"))) {
98366            $prefixIni = $this->prefix . DIRECTORY_SEPARATOR . "php.ini-gopear";
98367            $fh = fopen($prefixIni, "wb+");
98368            if (!$fh) {
98369                echo "
98370******************************************************************************
98371WARNING: Cannot write to $pathIni nor in $this->prefix/php.ini-gopear. Please
98372modify manually your php.ini by adding:
98373
98374$includepath
98375
98376";
98377                return false;
98378            } else {
98379                fwrite($fh, $newIni, strlen($newIni));
98380                fclose($fh);
98381                echo "
98382******************************************************************************
98383WARNING: Cannot write to $pathIni, but php.ini was successfully created
98384at <$this->prefix/php.ini-gopear>. Please replace the file <$pathIni> with
98385<$prefixIni> or modify your php.ini by adding:
98386
98387$includepath
98388
98389";
98390
98391            }
98392        } else {
98393            fwrite($fh, $newIni, strlen($newIni));
98394            fclose($fh);
98395            echo "
98396php.ini <$pathIni> include_path updated.
98397";
98398        }
98399        return true;
98400    }
98401
98402    /**
98403     * Generates a registry addOn for Win32 platform
98404     * This addon set PEAR environment variables
98405     * @author Pierrre-Alain Joye
98406     */
98407    function win32CreateRegEnv()
98408    {
98409        $nl = "\r\n";
98410        $reg ='REGEDIT4'.$nl.
98411                '[HKEY_CURRENT_USER\Environment]'. $nl .
98412                '"PHP_PEAR_SYSCONF_DIR"="' . addslashes($this->prefix) . '"' . $nl .
98413                '"PHP_PEAR_INSTALL_DIR"="' . addslashes($this->php_dir) . '"' . $nl .
98414                '"PHP_PEAR_DOC_DIR"="' . addslashes($this->doc_dir) . '"' . $nl .
98415                '"PHP_PEAR_BIN_DIR"="' . addslashes($this->bin_dir) . '"' . $nl .
98416                '"PHP_PEAR_DATA_DIR"="' . addslashes($this->data_dir) . '"' . $nl .
98417                '"PHP_PEAR_PHP_BIN"="' . addslashes($this->php_bin) . '"' . $nl .
98418                '"PHP_PEAR_TEST_DIR"="' . addslashes($this->test_dir) . '"' . $nl;
98419
98420        $fh = fopen($this->prefix . DIRECTORY_SEPARATOR . 'PEAR_ENV.reg', 'wb');
98421        if($fh){
98422            fwrite($fh, $reg, strlen($reg));
98423            fclose($fh);
98424            echo "
98425
98426* WINDOWS ENVIRONMENT VARIABLES *
98427For convenience, a REG file is available under {$this->prefix}PEAR_ENV.reg .
98428This file creates ENV variables for the current user.
98429
98430Double-click this file to add it to the current user registry.
98431
98432";
98433        }
98434    }
98435
98436    function displayHTMLProgress()
98437    {
98438    }
98439}
98440?><?php
98441/**
98442 * PEAR_Task_Common, base class for installer tasks
98443 *
98444 * PHP versions 4 and 5
98445 *
98446 * @category   pear
98447 * @package    PEAR
98448 * @author     Greg Beaver <cellog@php.net>
98449 * @copyright  1997-2009 The Authors
98450 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
98451 * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
98452 * @link       http://pear.php.net/package/PEAR
98453 * @since      File available since Release 1.4.0a1
98454 */
98455/**#@+
98456 * Error codes for task validation routines
98457 */
98458define('PEAR_TASK_ERROR_NOATTRIBS', 1);
98459define('PEAR_TASK_ERROR_MISSING_ATTRIB', 2);
98460define('PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE', 3);
98461define('PEAR_TASK_ERROR_INVALID', 4);
98462/**#@-*/
98463define('PEAR_TASK_PACKAGE', 1);
98464define('PEAR_TASK_INSTALL', 2);
98465define('PEAR_TASK_PACKAGEANDINSTALL', 3);
98466/**
98467 * A task is an operation that manipulates the contents of a file.
98468 *
98469 * Simple tasks operate on 1 file.  Multiple tasks are executed after all files have been
98470 * processed and installed, and are designed to operate on all files containing the task.
98471 * The Post-install script task simply takes advantage of the fact that it will be run
98472 * after installation, replace is a simple task.
98473 *
98474 * Combining tasks is possible, but ordering is significant.
98475 *
98476 * <file name="test.php" role="php">
98477 *  <tasks:replace from="@data-dir@" to="data_dir" type="pear-config"/>
98478 *  <tasks:postinstallscript/>
98479 * </file>
98480 *
98481 * This will first replace any instance of @data-dir@ in the test.php file
98482 * with the path to the current data directory.  Then, it will include the
98483 * test.php file and run the script it contains to configure the package post-installation.
98484 * @category   pear
98485 * @package    PEAR
98486 * @author     Greg Beaver <cellog@php.net>
98487 * @copyright  1997-2009 The Authors
98488 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
98489 * @version    Release: 1.9.4
98490 * @link       http://pear.php.net/package/PEAR
98491 * @since      Class available since Release 1.4.0a1
98492 * @abstract
98493 */
98494class PEAR_Task_Common
98495{
98496    /**
98497     * Valid types for this version are 'simple' and 'multiple'
98498     *
98499     * - simple tasks operate on the contents of a file and write out changes to disk
98500     * - multiple tasks operate on the contents of many files and write out the
98501     *   changes directly to disk
98502     *
98503     * Child task classes must override this property.
98504     * @access protected
98505     */
98506    var $type = 'simple';
98507    /**
98508     * Determines which install phase this task is executed under
98509     */
98510    var $phase = PEAR_TASK_INSTALL;
98511    /**
98512     * @access protected
98513     */
98514    var $config;
98515    /**
98516     * @access protected
98517     */
98518    var $registry;
98519    /**
98520     * @access protected
98521     */
98522    var $logger;
98523    /**
98524     * @access protected
98525     */
98526    var $installphase;
98527    /**
98528     * @param PEAR_Config
98529     * @param PEAR_Common
98530     */
98531    function PEAR_Task_Common(&$config, &$logger, $phase)
98532    {
98533        $this->config = &$config;
98534        $this->registry = &$config->getRegistry();
98535        $this->logger = &$logger;
98536        $this->installphase = $phase;
98537        if ($this->type == 'multiple') {
98538            $GLOBALS['_PEAR_TASK_POSTINSTANCES'][get_class($this)][] = &$this;
98539        }
98540    }
98541
98542    /**
98543     * Validate the basic contents of a task tag.
98544     * @param PEAR_PackageFile_v2
98545     * @param array
98546     * @param PEAR_Config
98547     * @param array the entire parsed <file> tag
98548     * @return true|array On error, return an array in format:
98549     *    array(PEAR_TASK_ERROR_???[, param1][, param2][, ...])
98550     *
98551     *    For PEAR_TASK_ERROR_MISSING_ATTRIB, pass the attribute name in
98552     *    For PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, pass the attribute name and an array
98553     *    of legal values in
98554     * @static
98555     * @abstract
98556     */
98557    function validateXml($pkg, $xml, $config, $fileXml)
98558    {
98559    }
98560
98561    /**
98562     * Initialize a task instance with the parameters
98563     * @param array raw, parsed xml
98564     * @param array attributes from the <file> tag containing this task
98565     * @param string|null last installed version of this package
98566     * @abstract
98567     */
98568    function init($xml, $fileAttributes, $lastVersion)
98569    {
98570    }
98571
98572    /**
98573     * Begin a task processing session.  All multiple tasks will be processed after each file
98574     * has been successfully installed, all simple tasks should perform their task here and
98575     * return any errors using the custom throwError() method to allow forward compatibility
98576     *
98577     * This method MUST NOT write out any changes to disk
98578     * @param PEAR_PackageFile_v2
98579     * @param string file contents
98580     * @param string the eventual final file location (informational only)
98581     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
98582     *         (use $this->throwError), otherwise return the new contents
98583     * @abstract
98584     */
98585    function startSession($pkg, $contents, $dest)
98586    {
98587    }
98588
98589    /**
98590     * This method is used to process each of the tasks for a particular multiple class
98591     * type.  Simple tasks need not implement this method.
98592     * @param array an array of tasks
98593     * @access protected
98594     * @static
98595     * @abstract
98596     */
98597    function run($tasks)
98598    {
98599    }
98600
98601    /**
98602     * @static
98603     * @final
98604     */
98605    function hasPostinstallTasks()
98606    {
98607        return isset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
98608    }
98609
98610    /**
98611     * @static
98612     * @final
98613     */
98614     function runPostinstallTasks()
98615     {
98616         foreach ($GLOBALS['_PEAR_TASK_POSTINSTANCES'] as $class => $tasks) {
98617             $err = call_user_func(array($class, 'run'),
98618                  $GLOBALS['_PEAR_TASK_POSTINSTANCES'][$class]);
98619             if ($err) {
98620                 return PEAR_Task_Common::throwError($err);
98621             }
98622         }
98623         unset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
98624    }
98625
98626    /**
98627     * Determines whether a role is a script
98628     * @return bool
98629     */
98630    function isScript()
98631    {
98632        return $this->type == 'script';
98633    }
98634
98635    function throwError($msg, $code = -1)
98636    {
98637        include_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
98638        return PEAR::raiseError($msg, $code);
98639    }
98640}
98641?><?php
98642/**
98643 * <tasks:postinstallscript>
98644 *
98645 * PHP versions 4 and 5
98646 *
98647 * @category   pear
98648 * @package    PEAR
98649 * @author     Greg Beaver <cellog@php.net>
98650 * @copyright  1997-2009 The Authors
98651 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
98652 * @version    CVS: $Id: Postinstallscript.php 313023 2011-07-06 19:17:11Z dufuz $
98653 * @link       http://pear.php.net/package/PEAR
98654 * @since      File available since Release 1.4.0a1
98655 */
98656/**
98657 * Base class
98658 */
98659require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Common.php';
98660/**
98661 * Implements the postinstallscript file task.
98662 *
98663 * Note that post-install scripts are handled separately from installation, by the
98664 * "pear run-scripts" command
98665 * @category   pear
98666 * @package    PEAR
98667 * @author     Greg Beaver <cellog@php.net>
98668 * @copyright  1997-2009 The Authors
98669 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
98670 * @version    Release: 1.9.4
98671 * @link       http://pear.php.net/package/PEAR
98672 * @since      Class available since Release 1.4.0a1
98673 */
98674class PEAR_Task_Postinstallscript extends PEAR_Task_Common
98675{
98676    var $type = 'script';
98677    var $_class;
98678    var $_params;
98679    var $_obj;
98680    /**
98681     *
98682     * @var PEAR_PackageFile_v2
98683     */
98684    var $_pkg;
98685    var $_contents;
98686    var $phase = PEAR_TASK_INSTALL;
98687
98688    /**
98689     * Validate the raw xml at parsing-time.
98690     *
98691     * This also attempts to validate the script to make sure it meets the criteria
98692     * for a post-install script
98693     * @param PEAR_PackageFile_v2
98694     * @param array The XML contents of the <postinstallscript> tag
98695     * @param PEAR_Config
98696     * @param array the entire parsed <file> tag
98697     * @static
98698     */
98699    function validateXml($pkg, $xml, $config, $fileXml)
98700    {
98701        if ($fileXml['role'] != 'php') {
98702            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98703            $fileXml['name'] . '" must be role="php"');
98704        }
98705        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
98706        $file = $pkg->getFileContents($fileXml['name']);
98707        if (PEAR::isError($file)) {
98708            PEAR::popErrorHandling();
98709            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98710                $fileXml['name'] . '" is not valid: ' .
98711                $file->getMessage());
98712        } elseif ($file === null) {
98713            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98714                $fileXml['name'] . '" could not be retrieved for processing!');
98715        } else {
98716            $analysis = $pkg->analyzeSourceCode($file, true);
98717            if (!$analysis) {
98718                PEAR::popErrorHandling();
98719                $warnings = '';
98720                foreach ($pkg->getValidationWarnings() as $warn) {
98721                    $warnings .= $warn['message'] . "\n";
98722                }
98723                return array(PEAR_TASK_ERROR_INVALID, 'Analysis of post-install script "' .
98724                    $fileXml['name'] . '" failed: ' . $warnings);
98725            }
98726            if (count($analysis['declared_classes']) != 1) {
98727                PEAR::popErrorHandling();
98728                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98729                    $fileXml['name'] . '" must declare exactly 1 class');
98730            }
98731            $class = $analysis['declared_classes'][0];
98732            if ($class != str_replace(array('/', '.php'), array('_', ''),
98733                  $fileXml['name']) . '_postinstall') {
98734                PEAR::popErrorHandling();
98735                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98736                    $fileXml['name'] . '" class "' . $class . '" must be named "' .
98737                    str_replace(array('/', '.php'), array('_', ''),
98738                    $fileXml['name']) . '_postinstall"');
98739            }
98740            if (!isset($analysis['declared_methods'][$class])) {
98741                PEAR::popErrorHandling();
98742                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98743                    $fileXml['name'] . '" must declare methods init() and run()');
98744            }
98745            $methods = array('init' => 0, 'run' => 1);
98746            foreach ($analysis['declared_methods'][$class] as $method) {
98747                if (isset($methods[$method])) {
98748                    unset($methods[$method]);
98749                }
98750            }
98751            if (count($methods)) {
98752                PEAR::popErrorHandling();
98753                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98754                    $fileXml['name'] . '" must declare methods init() and run()');
98755            }
98756        }
98757        PEAR::popErrorHandling();
98758        $definedparams = array();
98759        $tasksNamespace = $pkg->getTasksNs() . ':';
98760        if (!isset($xml[$tasksNamespace . 'paramgroup']) && isset($xml['paramgroup'])) {
98761            // in order to support the older betas, which did not expect internal tags
98762            // to also use the namespace
98763            $tasksNamespace = '';
98764        }
98765        if (isset($xml[$tasksNamespace . 'paramgroup'])) {
98766            $params = $xml[$tasksNamespace . 'paramgroup'];
98767            if (!is_array($params) || !isset($params[0])) {
98768                $params = array($params);
98769            }
98770            foreach ($params as $param) {
98771                if (!isset($param[$tasksNamespace . 'id'])) {
98772                    return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98773                        $fileXml['name'] . '" <paramgroup> must have ' .
98774                        'an ' . $tasksNamespace . 'id> tag');
98775                }
98776                if (isset($param[$tasksNamespace . 'name'])) {
98777                    if (!in_array($param[$tasksNamespace . 'name'], $definedparams)) {
98778                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98779                            $fileXml['name'] . '" ' . $tasksNamespace .
98780                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
98781                            '" parameter "' . $param[$tasksNamespace . 'name'] .
98782                            '" has not been previously defined');
98783                    }
98784                    if (!isset($param[$tasksNamespace . 'conditiontype'])) {
98785                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98786                            $fileXml['name'] . '" ' . $tasksNamespace .
98787                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
98788                            '" must have a ' . $tasksNamespace .
98789                            'conditiontype> tag containing either "=", ' .
98790                            '"!=", or "preg_match"');
98791                    }
98792                    if (!in_array($param[$tasksNamespace . 'conditiontype'],
98793                          array('=', '!=', 'preg_match'))) {
98794                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98795                            $fileXml['name'] . '" ' . $tasksNamespace .
98796                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
98797                            '" must have a ' . $tasksNamespace .
98798                            'conditiontype> tag containing either "=", ' .
98799                            '"!=", or "preg_match"');
98800                    }
98801                    if (!isset($param[$tasksNamespace . 'value'])) {
98802                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98803                            $fileXml['name'] . '" ' . $tasksNamespace .
98804                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
98805                            '" must have a ' . $tasksNamespace .
98806                            'value> tag containing expected parameter value');
98807                    }
98808                }
98809                if (isset($param[$tasksNamespace . 'instructions'])) {
98810                    if (!is_string($param[$tasksNamespace . 'instructions'])) {
98811                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98812                            $fileXml['name'] . '" ' . $tasksNamespace .
98813                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
98814                            '" ' . $tasksNamespace . 'instructions> must be simple text');
98815                    }
98816                }
98817                if (!isset($param[$tasksNamespace . 'param'])) {
98818                    continue; // <param> is no longer required
98819                }
98820                $subparams = $param[$tasksNamespace . 'param'];
98821                if (!is_array($subparams) || !isset($subparams[0])) {
98822                    $subparams = array($subparams);
98823                }
98824                foreach ($subparams as $subparam) {
98825                    if (!isset($subparam[$tasksNamespace . 'name'])) {
98826                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98827                            $fileXml['name'] . '" parameter for ' .
98828                            $tasksNamespace . 'paramgroup> id "' .
98829                            $param[$tasksNamespace . 'id'] . '" must have ' .
98830                            'a ' . $tasksNamespace . 'name> tag');
98831                    }
98832                    if (!preg_match('/[a-zA-Z0-9]+/',
98833                          $subparam[$tasksNamespace . 'name'])) {
98834                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98835                            $fileXml['name'] . '" parameter "' .
98836                            $subparam[$tasksNamespace . 'name'] .
98837                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
98838                            $param[$tasksNamespace . 'id'] .
98839                            '" is not a valid name.  Must contain only alphanumeric characters');
98840                    }
98841                    if (!isset($subparam[$tasksNamespace . 'prompt'])) {
98842                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98843                            $fileXml['name'] . '" parameter "' .
98844                            $subparam[$tasksNamespace . 'name'] .
98845                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
98846                            $param[$tasksNamespace . 'id'] .
98847                            '" must have a ' . $tasksNamespace . 'prompt> tag');
98848                    }
98849                    if (!isset($subparam[$tasksNamespace . 'type'])) {
98850                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
98851                            $fileXml['name'] . '" parameter "' .
98852                            $subparam[$tasksNamespace . 'name'] .
98853                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
98854                            $param[$tasksNamespace . 'id'] .
98855                            '" must have a ' . $tasksNamespace . 'type> tag');
98856                    }
98857                    $definedparams[] = $param[$tasksNamespace . 'id'] . '::' .
98858                    $subparam[$tasksNamespace . 'name'];
98859                }
98860            }
98861        }
98862        return true;
98863    }
98864
98865    /**
98866     * Initialize a task instance with the parameters
98867     * @param array raw, parsed xml
98868     * @param array attributes from the <file> tag containing this task
98869     * @param string|null last installed version of this package, if any (useful for upgrades)
98870     */
98871    function init($xml, $fileattribs, $lastversion)
98872    {
98873        $this->_class = str_replace('/', '_', $fileattribs['name']);
98874        $this->_filename = $fileattribs['name'];
98875        $this->_class = str_replace ('.php', '', $this->_class) . '_postinstall';
98876        $this->_params = $xml;
98877        $this->_lastversion = $lastversion;
98878    }
98879
98880    /**
98881     * Strip the tasks: namespace from internal params
98882     *
98883     * @access private
98884     */
98885    function _stripNamespace($params = null)
98886    {
98887        if ($params === null) {
98888            $params = array();
98889            if (!is_array($this->_params)) {
98890                return;
98891            }
98892            foreach ($this->_params as $i => $param) {
98893                if (is_array($param)) {
98894                    $param = $this->_stripNamespace($param);
98895                }
98896                $params[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param;
98897            }
98898            $this->_params = $params;
98899        } else {
98900            $newparams = array();
98901            foreach ($params as $i => $param) {
98902                if (is_array($param)) {
98903                    $param = $this->_stripNamespace($param);
98904                }
98905                $newparams[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param;
98906            }
98907            return $newparams;
98908        }
98909    }
98910
98911    /**
98912     * Unlike other tasks, the installed file name is passed in instead of the file contents,
98913     * because this task is handled post-installation
98914     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
98915     * @param string file name
98916     * @return bool|PEAR_Error false to skip this file, PEAR_Error to fail
98917     *         (use $this->throwError)
98918     */
98919    function startSession($pkg, $contents)
98920    {
98921        if ($this->installphase != PEAR_TASK_INSTALL) {
98922            return false;
98923        }
98924        // remove the tasks: namespace if present
98925        $this->_pkg = $pkg;
98926        $this->_stripNamespace();
98927        $this->logger->log(0, 'Including external post-installation script "' .
98928            $contents . '" - any errors are in this script');
98929        include_once 'phar://install-pear-nozlib.phar/' . $contents;
98930        if (class_exists($this->_class)) {
98931            $this->logger->log(0, 'Inclusion succeeded');
98932        } else {
98933            return $this->throwError('init of post-install script class "' . $this->_class
98934                . '" failed');
98935        }
98936        $this->_obj = new $this->_class;
98937        $this->logger->log(1, 'running post-install script "' . $this->_class . '->init()"');
98938        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
98939        $res = $this->_obj->init($this->config, $pkg, $this->_lastversion);
98940        PEAR::popErrorHandling();
98941        if ($res) {
98942            $this->logger->log(0, 'init succeeded');
98943        } else {
98944            return $this->throwError('init of post-install script "' . $this->_class .
98945                '->init()" failed');
98946        }
98947        $this->_contents = $contents;
98948        return true;
98949    }
98950
98951    /**
98952     * No longer used
98953     * @see PEAR_PackageFile_v2::runPostinstallScripts()
98954     * @param array an array of tasks
98955     * @param string install or upgrade
98956     * @access protected
98957     * @static
98958     */
98959    function run()
98960    {
98961    }
98962}
98963?><?php
98964/**
98965 * <tasks:postinstallscript> - read/write version
98966 *
98967 * PHP versions 4 and 5
98968 *
98969 * @category   pear
98970 * @package    PEAR
98971 * @author     Greg Beaver <cellog@php.net>
98972 * @copyright  1997-2009 The Authors
98973 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
98974 * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
98975 * @link       http://pear.php.net/package/PEAR
98976 * @since      File available since Release 1.4.0a10
98977 */
98978/**
98979 * Base class
98980 */
98981require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Postinstallscript.php';
98982/**
98983 * Abstracts the postinstallscript file task xml.
98984 * @category   pear
98985 * @package    PEAR
98986 * @author     Greg Beaver <cellog@php.net>
98987 * @copyright  1997-2009 The Authors
98988 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
98989 * @version    Release: 1.9.4
98990 * @link       http://pear.php.net/package/PEAR
98991 * @since      Class available since Release 1.4.0a10
98992 */
98993class PEAR_Task_Postinstallscript_rw extends PEAR_Task_Postinstallscript
98994{
98995    /**
98996     * parent package file object
98997     *
98998     * @var PEAR_PackageFile_v2_rw
98999     */
99000    var $_pkg;
99001    /**
99002     * Enter description here...
99003     *
99004     * @param PEAR_PackageFile_v2_rw $pkg
99005     * @param PEAR_Config $config
99006     * @param PEAR_Frontend $logger
99007     * @param array $fileXml
99008     * @return PEAR_Task_Postinstallscript_rw
99009     */
99010    function PEAR_Task_Postinstallscript_rw(&$pkg, &$config, &$logger, $fileXml)
99011    {
99012        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
99013        $this->_contents = $fileXml;
99014        $this->_pkg = &$pkg;
99015        $this->_params = array();
99016    }
99017
99018    function validate()
99019    {
99020        return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
99021    }
99022
99023    function getName()
99024    {
99025        return 'postinstallscript';
99026    }
99027
99028    /**
99029     * add a simple <paramgroup> to the post-install script
99030     *
99031     * Order is significant, so call this method in the same
99032     * sequence the users should see the paramgroups.  The $params
99033     * parameter should either be the result of a call to {@link getParam()}
99034     * or an array of calls to getParam().
99035     *
99036     * Use {@link addConditionTypeGroup()} to add a <paramgroup> containing
99037     * a <conditiontype> tag
99038     * @param string $id <paramgroup> id as seen by the script
99039     * @param array|false $params array of getParam() calls, or false for no params
99040     * @param string|false $instructions
99041     */
99042    function addParamGroup($id, $params = false, $instructions = false)
99043    {
99044        if ($params && isset($params[0]) && !isset($params[1])) {
99045            $params = $params[0];
99046        }
99047        $stuff =
99048            array(
99049                $this->_pkg->getTasksNs() . ':id' => $id,
99050            );
99051        if ($instructions) {
99052            $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions;
99053        }
99054        if ($params) {
99055            $stuff[$this->_pkg->getTasksNs() . ':param'] = $params;
99056        }
99057        $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff;
99058    }
99059
99060    /**
99061     * add a complex <paramgroup> to the post-install script with conditions
99062     *
99063     * This inserts a <paramgroup> with
99064     *
99065     * Order is significant, so call this method in the same
99066     * sequence the users should see the paramgroups.  The $params
99067     * parameter should either be the result of a call to {@link getParam()}
99068     * or an array of calls to getParam().
99069     *
99070     * Use {@link addParamGroup()} to add a simple <paramgroup>
99071     *
99072     * @param string $id <paramgroup> id as seen by the script
99073     * @param string $oldgroup <paramgroup> id of the section referenced by
99074     *                         <conditiontype>
99075     * @param string $param name of the <param> from the older section referenced
99076     *                      by <contitiontype>
99077     * @param string $value value to match of the parameter
99078     * @param string $conditiontype one of '=', '!=', 'preg_match'
99079     * @param array|false $params array of getParam() calls, or false for no params
99080     * @param string|false $instructions
99081     */
99082    function addConditionTypeGroup($id, $oldgroup, $param, $value, $conditiontype = '=',
99083                                   $params = false, $instructions = false)
99084    {
99085        if ($params && isset($params[0]) && !isset($params[1])) {
99086            $params = $params[0];
99087        }
99088        $stuff = array(
99089            $this->_pkg->getTasksNs() . ':id' => $id,
99090        );
99091        if ($instructions) {
99092            $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions;
99093        }
99094        $stuff[$this->_pkg->getTasksNs() . ':name'] = $oldgroup . '::' . $param;
99095        $stuff[$this->_pkg->getTasksNs() . ':conditiontype'] = $conditiontype;
99096        $stuff[$this->_pkg->getTasksNs() . ':value'] = $value;
99097        if ($params) {
99098            $stuff[$this->_pkg->getTasksNs() . ':param'] = $params;
99099        }
99100        $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff;
99101    }
99102
99103    function getXml()
99104    {
99105        return $this->_params;
99106    }
99107
99108    /**
99109     * Use to set up a param tag for use in creating a paramgroup
99110     * @static
99111     */
99112    function getParam($name, $prompt, $type = 'string', $default = null)
99113    {
99114        if ($default !== null) {
99115            return
99116            array(
99117                $this->_pkg->getTasksNs() . ':name' => $name,
99118                $this->_pkg->getTasksNs() . ':prompt' => $prompt,
99119                $this->_pkg->getTasksNs() . ':type' => $type,
99120                $this->_pkg->getTasksNs() . ':default' => $default
99121            );
99122        }
99123        return
99124            array(
99125                $this->_pkg->getTasksNs() . ':name' => $name,
99126                $this->_pkg->getTasksNs() . ':prompt' => $prompt,
99127                $this->_pkg->getTasksNs() . ':type' => $type,
99128            );
99129    }
99130}
99131?><?php
99132/**
99133 * <tasks:replace>
99134 *
99135 * PHP versions 4 and 5
99136 *
99137 * @category   pear
99138 * @package    PEAR
99139 * @author     Greg Beaver <cellog@php.net>
99140 * @copyright  1997-2009 The Authors
99141 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99142 * @version    CVS: $Id: Replace.php 313023 2011-07-06 19:17:11Z dufuz $
99143 * @link       http://pear.php.net/package/PEAR
99144 * @since      File available since Release 1.4.0a1
99145 */
99146/**
99147 * Base class
99148 */
99149require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Common.php';
99150/**
99151 * Implements the replace file task.
99152 * @category   pear
99153 * @package    PEAR
99154 * @author     Greg Beaver <cellog@php.net>
99155 * @copyright  1997-2009 The Authors
99156 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99157 * @version    Release: 1.9.4
99158 * @link       http://pear.php.net/package/PEAR
99159 * @since      Class available since Release 1.4.0a1
99160 */
99161class PEAR_Task_Replace extends PEAR_Task_Common
99162{
99163    var $type = 'simple';
99164    var $phase = PEAR_TASK_PACKAGEANDINSTALL;
99165    var $_replacements;
99166
99167    /**
99168     * Validate the raw xml at parsing-time.
99169     * @param PEAR_PackageFile_v2
99170     * @param array raw, parsed xml
99171     * @param PEAR_Config
99172     * @static
99173     */
99174    function validateXml($pkg, $xml, $config, $fileXml)
99175    {
99176        if (!isset($xml['attribs'])) {
99177            return array(PEAR_TASK_ERROR_NOATTRIBS);
99178        }
99179        if (!isset($xml['attribs']['type'])) {
99180            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'type');
99181        }
99182        if (!isset($xml['attribs']['to'])) {
99183            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'to');
99184        }
99185        if (!isset($xml['attribs']['from'])) {
99186            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'from');
99187        }
99188        if ($xml['attribs']['type'] == 'pear-config') {
99189            if (!in_array($xml['attribs']['to'], $config->getKeys())) {
99190                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
99191                    $config->getKeys());
99192            }
99193        } elseif ($xml['attribs']['type'] == 'php-const') {
99194            if (defined($xml['attribs']['to'])) {
99195                return true;
99196            } else {
99197                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
99198                    array('valid PHP constant'));
99199            }
99200        } elseif ($xml['attribs']['type'] == 'package-info') {
99201            if (in_array($xml['attribs']['to'],
99202                array('name', 'summary', 'channel', 'notes', 'extends', 'description',
99203                    'release_notes', 'license', 'release-license', 'license-uri',
99204                    'version', 'api-version', 'state', 'api-state', 'release_date',
99205                    'date', 'time'))) {
99206                return true;
99207            } else {
99208                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
99209                    array('name', 'summary', 'channel', 'notes', 'extends', 'description',
99210                    'release_notes', 'license', 'release-license', 'license-uri',
99211                    'version', 'api-version', 'state', 'api-state', 'release_date',
99212                    'date', 'time'));
99213            }
99214        } else {
99215            return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'type', $xml['attribs']['type'],
99216                array('pear-config', 'package-info', 'php-const'));
99217        }
99218        return true;
99219    }
99220
99221    /**
99222     * Initialize a task instance with the parameters
99223     * @param array raw, parsed xml
99224     * @param unused
99225     */
99226    function init($xml, $attribs)
99227    {
99228        $this->_replacements = isset($xml['attribs']) ? array($xml) : $xml;
99229    }
99230
99231    /**
99232     * Do a package.xml 1.0 replacement, with additional package-info fields available
99233     *
99234     * See validateXml() source for the complete list of allowed fields
99235     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
99236     * @param string file contents
99237     * @param string the eventual final file location (informational only)
99238     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
99239     *         (use $this->throwError), otherwise return the new contents
99240     */
99241    function startSession($pkg, $contents, $dest)
99242    {
99243        $subst_from = $subst_to = array();
99244        foreach ($this->_replacements as $a) {
99245            $a = $a['attribs'];
99246            $to = '';
99247            if ($a['type'] == 'pear-config') {
99248                if ($this->installphase == PEAR_TASK_PACKAGE) {
99249                    return false;
99250                }
99251                if ($a['to'] == 'master_server') {
99252                    $chan = $this->registry->getChannel($pkg->getChannel());
99253                    if (!PEAR::isError($chan)) {
99254                        $to = $chan->getServer();
99255                    } else {
99256                        $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
99257                        return false;
99258                    }
99259                } else {
99260                    if ($this->config->isDefinedLayer('ftp')) {
99261                        // try the remote config file first
99262                        $to = $this->config->get($a['to'], 'ftp', $pkg->getChannel());
99263                        if (is_null($to)) {
99264                            // then default to local
99265                            $to = $this->config->get($a['to'], null, $pkg->getChannel());
99266                        }
99267                    } else {
99268                        $to = $this->config->get($a['to'], null, $pkg->getChannel());
99269                    }
99270                }
99271                if (is_null($to)) {
99272                    $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
99273                    return false;
99274                }
99275            } elseif ($a['type'] == 'php-const') {
99276                if ($this->installphase == PEAR_TASK_PACKAGE) {
99277                    return false;
99278                }
99279                if (defined($a['to'])) {
99280                    $to = constant($a['to']);
99281                } else {
99282                    $this->logger->log(0, "$dest: invalid php-const replacement: $a[to]");
99283                    return false;
99284                }
99285            } else {
99286                if ($t = $pkg->packageInfo($a['to'])) {
99287                    $to = $t;
99288                } else {
99289                    $this->logger->log(0, "$dest: invalid package-info replacement: $a[to]");
99290                    return false;
99291                }
99292            }
99293            if (!is_null($to)) {
99294                $subst_from[] = $a['from'];
99295                $subst_to[] = $to;
99296            }
99297        }
99298        $this->logger->log(3, "doing " . sizeof($subst_from) .
99299            " substitution(s) for $dest");
99300        if (sizeof($subst_from)) {
99301            $contents = str_replace($subst_from, $subst_to, $contents);
99302        }
99303        return $contents;
99304    }
99305}
99306?><?php
99307/**
99308 * <tasks:replace> - read/write version
99309 *
99310 * PHP versions 4 and 5
99311 *
99312 * @category   pear
99313 * @package    PEAR
99314 * @author     Greg Beaver <cellog@php.net>
99315 * @copyright  1997-2009 The Authors
99316 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99317 * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
99318 * @link       http://pear.php.net/package/PEAR
99319 * @since      File available since Release 1.4.0a10
99320 */
99321/**
99322 * Base class
99323 */
99324require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Replace.php';
99325/**
99326 * Abstracts the replace task xml.
99327 * @category   pear
99328 * @package    PEAR
99329 * @author     Greg Beaver <cellog@php.net>
99330 * @copyright  1997-2009 The Authors
99331 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99332 * @version    Release: 1.9.4
99333 * @link       http://pear.php.net/package/PEAR
99334 * @since      Class available since Release 1.4.0a10
99335 */
99336class PEAR_Task_Replace_rw extends PEAR_Task_Replace
99337{
99338    function PEAR_Task_Replace_rw(&$pkg, &$config, &$logger, $fileXml)
99339    {
99340        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
99341        $this->_contents = $fileXml;
99342        $this->_pkg = &$pkg;
99343        $this->_params = array();
99344    }
99345
99346    function validate()
99347    {
99348        return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
99349    }
99350
99351    function setInfo($from, $to, $type)
99352    {
99353        $this->_params = array('attribs' => array('from' => $from, 'to' => $to, 'type' => $type));
99354    }
99355
99356    function getName()
99357    {
99358        return 'replace';
99359    }
99360
99361    function getXml()
99362    {
99363        return $this->_params;
99364    }
99365}
99366?><?php
99367/**
99368 * <tasks:unixeol>
99369 *
99370 * PHP versions 4 and 5
99371 *
99372 * @category   pear
99373 * @package    PEAR
99374 * @author     Greg Beaver <cellog@php.net>
99375 * @copyright  1997-2009 The Authors
99376 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99377 * @version    CVS: $Id: Unixeol.php 313023 2011-07-06 19:17:11Z dufuz $
99378 * @link       http://pear.php.net/package/PEAR
99379 * @since      File available since Release 1.4.0a1
99380 */
99381/**
99382 * Base class
99383 */
99384require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Common.php';
99385/**
99386 * Implements the unix line endings file task.
99387 * @category   pear
99388 * @package    PEAR
99389 * @author     Greg Beaver <cellog@php.net>
99390 * @copyright  1997-2009 The Authors
99391 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99392 * @version    Release: 1.9.4
99393 * @link       http://pear.php.net/package/PEAR
99394 * @since      Class available since Release 1.4.0a1
99395 */
99396class PEAR_Task_Unixeol extends PEAR_Task_Common
99397{
99398    var $type = 'simple';
99399    var $phase = PEAR_TASK_PACKAGE;
99400    var $_replacements;
99401
99402    /**
99403     * Validate the raw xml at parsing-time.
99404     * @param PEAR_PackageFile_v2
99405     * @param array raw, parsed xml
99406     * @param PEAR_Config
99407     * @static
99408     */
99409    function validateXml($pkg, $xml, $config, $fileXml)
99410    {
99411        if ($xml != '') {
99412            return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
99413        }
99414        return true;
99415    }
99416
99417    /**
99418     * Initialize a task instance with the parameters
99419     * @param array raw, parsed xml
99420     * @param unused
99421     */
99422    function init($xml, $attribs)
99423    {
99424    }
99425
99426    /**
99427     * Replace all line endings with line endings customized for the current OS
99428     *
99429     * See validateXml() source for the complete list of allowed fields
99430     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
99431     * @param string file contents
99432     * @param string the eventual final file location (informational only)
99433     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
99434     *         (use $this->throwError), otherwise return the new contents
99435     */
99436    function startSession($pkg, $contents, $dest)
99437    {
99438        $this->logger->log(3, "replacing all line endings with \\n in $dest");
99439        return preg_replace("/\r\n|\n\r|\r|\n/", "\n", $contents);
99440    }
99441}
99442?><?php
99443/**
99444 * <tasks:unixeol> - read/write version
99445 *
99446 * PHP versions 4 and 5
99447 *
99448 * @category   pear
99449 * @package    PEAR
99450 * @author     Greg Beaver <cellog@php.net>
99451 * @copyright  1997-2009 The Authors
99452 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99453 * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
99454 * @link       http://pear.php.net/package/PEAR
99455 * @since      File available since Release 1.4.0a10
99456 */
99457/**
99458 * Base class
99459 */
99460require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Unixeol.php';
99461/**
99462 * Abstracts the unixeol task xml.
99463 * @category   pear
99464 * @package    PEAR
99465 * @author     Greg Beaver <cellog@php.net>
99466 * @copyright  1997-2009 The Authors
99467 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99468 * @version    Release: 1.9.4
99469 * @link       http://pear.php.net/package/PEAR
99470 * @since      Class available since Release 1.4.0a10
99471 */
99472class PEAR_Task_Unixeol_rw extends PEAR_Task_Unixeol
99473{
99474    function PEAR_Task_Unixeol_rw(&$pkg, &$config, &$logger, $fileXml)
99475    {
99476        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
99477        $this->_contents = $fileXml;
99478        $this->_pkg = &$pkg;
99479        $this->_params = array();
99480    }
99481
99482    function validate()
99483    {
99484        return true;
99485    }
99486
99487    function getName()
99488    {
99489        return 'unixeol';
99490    }
99491
99492    function getXml()
99493    {
99494        return '';
99495    }
99496}
99497?><?php
99498/**
99499 * <tasks:windowseol>
99500 *
99501 * PHP versions 4 and 5
99502 *
99503 * @category   pear
99504 * @package    PEAR
99505 * @author     Greg Beaver <cellog@php.net>
99506 * @copyright  1997-2009 The Authors
99507 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99508 * @version    CVS: $Id: Windowseol.php 313023 2011-07-06 19:17:11Z dufuz $
99509 * @link       http://pear.php.net/package/PEAR
99510 * @since      File available since Release 1.4.0a1
99511 */
99512/**
99513 * Base class
99514 */
99515require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Common.php';
99516/**
99517 * Implements the windows line endsings file task.
99518 * @category   pear
99519 * @package    PEAR
99520 * @author     Greg Beaver <cellog@php.net>
99521 * @copyright  1997-2009 The Authors
99522 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99523 * @version    Release: 1.9.4
99524 * @link       http://pear.php.net/package/PEAR
99525 * @since      Class available since Release 1.4.0a1
99526 */
99527class PEAR_Task_Windowseol extends PEAR_Task_Common
99528{
99529    var $type = 'simple';
99530    var $phase = PEAR_TASK_PACKAGE;
99531    var $_replacements;
99532
99533    /**
99534     * Validate the raw xml at parsing-time.
99535     * @param PEAR_PackageFile_v2
99536     * @param array raw, parsed xml
99537     * @param PEAR_Config
99538     * @static
99539     */
99540    function validateXml($pkg, $xml, $config, $fileXml)
99541    {
99542        if ($xml != '') {
99543            return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
99544        }
99545        return true;
99546    }
99547
99548    /**
99549     * Initialize a task instance with the parameters
99550     * @param array raw, parsed xml
99551     * @param unused
99552     */
99553    function init($xml, $attribs)
99554    {
99555    }
99556
99557    /**
99558     * Replace all line endings with windows line endings
99559     *
99560     * See validateXml() source for the complete list of allowed fields
99561     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
99562     * @param string file contents
99563     * @param string the eventual final file location (informational only)
99564     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
99565     *         (use $this->throwError), otherwise return the new contents
99566     */
99567    function startSession($pkg, $contents, $dest)
99568    {
99569        $this->logger->log(3, "replacing all line endings with \\r\\n in $dest");
99570        return preg_replace("/\r\n|\n\r|\r|\n/", "\r\n", $contents);
99571    }
99572}
99573?><?php
99574/**
99575 * <tasks:windowseol> - read/write version
99576 *
99577 * PHP versions 4 and 5
99578 *
99579 * @category   pear
99580 * @package    PEAR
99581 * @author     Greg Beaver <cellog@php.net>
99582 * @copyright  1997-2009 The Authors
99583 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99584 * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
99585 * @link       http://pear.php.net/package/PEAR
99586 * @since      File available since Release 1.4.0a10
99587 */
99588/**
99589 * Base class
99590 */
99591require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Task/Windowseol.php';
99592/**
99593 * Abstracts the windowseol task xml.
99594 * @category   pear
99595 * @package    PEAR
99596 * @author     Greg Beaver <cellog@php.net>
99597 * @copyright  1997-2009 The Authors
99598 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99599 * @version    Release: 1.9.4
99600 * @link       http://pear.php.net/package/PEAR
99601 * @since      Class available since Release 1.4.0a10
99602 */
99603class PEAR_Task_Windowseol_rw extends PEAR_Task_Windowseol
99604{
99605    function PEAR_Task_Windowseol_rw(&$pkg, &$config, &$logger, $fileXml)
99606    {
99607        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
99608        $this->_contents = $fileXml;
99609        $this->_pkg = &$pkg;
99610        $this->_params = array();
99611    }
99612
99613    function validate()
99614    {
99615        return true;
99616    }
99617
99618    function getName()
99619    {
99620        return 'windowseol';
99621    }
99622
99623    function getXml()
99624    {
99625        return '';
99626    }
99627}
99628?><?php
99629/**
99630 * PEAR_Validate
99631 *
99632 * PHP versions 4 and 5
99633 *
99634 * @category   pear
99635 * @package    PEAR
99636 * @author     Greg Beaver <cellog@php.net>
99637 * @copyright  1997-2009 The Authors
99638 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99639 * @version    CVS: $Id: Validate.php 313023 2011-07-06 19:17:11Z dufuz $
99640 * @link       http://pear.php.net/package/PEAR
99641 * @since      File available since Release 1.4.0a1
99642 */
99643/**#@+
99644 * Constants for install stage
99645 */
99646define('PEAR_VALIDATE_INSTALLING', 1);
99647define('PEAR_VALIDATE_UNINSTALLING', 2); // this is not bit-mapped like the others
99648define('PEAR_VALIDATE_NORMAL', 3);
99649define('PEAR_VALIDATE_DOWNLOADING', 4); // this is not bit-mapped like the others
99650define('PEAR_VALIDATE_PACKAGING', 7);
99651/**#@-*/
99652require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Common.php';
99653require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validator/PECL.php';
99654
99655/**
99656 * Validation class for package.xml - channel-level advanced validation
99657 * @category   pear
99658 * @package    PEAR
99659 * @author     Greg Beaver <cellog@php.net>
99660 * @copyright  1997-2009 The Authors
99661 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
99662 * @version    Release: 1.9.4
99663 * @link       http://pear.php.net/package/PEAR
99664 * @since      Class available since Release 1.4.0a1
99665 */
99666class PEAR_Validate
99667{
99668    var $packageregex = _PEAR_COMMON_PACKAGE_NAME_PREG;
99669    /**
99670     * @var PEAR_PackageFile_v1|PEAR_PackageFile_v2
99671     */
99672    var $_packagexml;
99673    /**
99674     * @var int one of the PEAR_VALIDATE_* constants
99675     */
99676    var $_state = PEAR_VALIDATE_NORMAL;
99677    /**
99678     * Format: ('error' => array('field' => name, 'reason' => reason), 'warning' => same)
99679     * @var array
99680     * @access private
99681     */
99682    var $_failures = array('error' => array(), 'warning' => array());
99683
99684    /**
99685     * Override this method to handle validation of normal package names
99686     * @param string
99687     * @return bool
99688     * @access protected
99689     */
99690    function _validPackageName($name)
99691    {
99692        return (bool) preg_match('/^' . $this->packageregex . '\\z/', $name);
99693    }
99694
99695    /**
99696     * @param string package name to validate
99697     * @param string name of channel-specific validation package
99698     * @final
99699     */
99700    function validPackageName($name, $validatepackagename = false)
99701    {
99702        if ($validatepackagename) {
99703            if (strtolower($name) == strtolower($validatepackagename)) {
99704                return (bool) preg_match('/^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*\\z/', $name);
99705            }
99706        }
99707        return $this->_validPackageName($name);
99708    }
99709
99710    /**
99711     * This validates a bundle name, and bundle names must conform
99712     * to the PEAR naming convention, so the method is final and static.
99713     * @param string
99714     * @final
99715     * @static
99716     */
99717    function validGroupName($name)
99718    {
99719        return (bool) preg_match('/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/', $name);
99720    }
99721
99722    /**
99723     * Determine whether $state represents a valid stability level
99724     * @param string
99725     * @return bool
99726     * @static
99727     * @final
99728     */
99729    function validState($state)
99730    {
99731        return in_array($state, array('snapshot', 'devel', 'alpha', 'beta', 'stable'));
99732    }
99733
99734    /**
99735     * Get a list of valid stability levels
99736     * @return array
99737     * @static
99738     * @final
99739     */
99740    function getValidStates()
99741    {
99742        return array('snapshot', 'devel', 'alpha', 'beta', 'stable');
99743    }
99744
99745    /**
99746     * Determine whether a version is a properly formatted version number that can be used
99747     * by version_compare
99748     * @param string
99749     * @return bool
99750     * @static
99751     * @final
99752     */
99753    function validVersion($ver)
99754    {
99755        return (bool) preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
99756    }
99757
99758    /**
99759     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
99760     */
99761    function setPackageFile(&$pf)
99762    {
99763        $this->_packagexml = &$pf;
99764    }
99765
99766    /**
99767     * @access private
99768     */
99769    function _addFailure($field, $reason)
99770    {
99771        $this->_failures['errors'][] = array('field' => $field, 'reason' => $reason);
99772    }
99773
99774    /**
99775     * @access private
99776     */
99777    function _addWarning($field, $reason)
99778    {
99779        $this->_failures['warnings'][] = array('field' => $field, 'reason' => $reason);
99780    }
99781
99782    function getFailures()
99783    {
99784        $failures = $this->_failures;
99785        $this->_failures = array('warnings' => array(), 'errors' => array());
99786        return $failures;
99787    }
99788
99789    /**
99790     * @param int one of the PEAR_VALIDATE_* constants
99791     */
99792    function validate($state = null)
99793    {
99794        if (!isset($this->_packagexml)) {
99795            return false;
99796        }
99797        if ($state !== null) {
99798            $this->_state = $state;
99799        }
99800        $this->_failures = array('warnings' => array(), 'errors' => array());
99801        $this->validatePackageName();
99802        $this->validateVersion();
99803        $this->validateMaintainers();
99804        $this->validateDate();
99805        $this->validateSummary();
99806        $this->validateDescription();
99807        $this->validateLicense();
99808        $this->validateNotes();
99809        if ($this->_packagexml->getPackagexmlVersion() == '1.0') {
99810            $this->validateState();
99811            $this->validateFilelist();
99812        } elseif ($this->_packagexml->getPackagexmlVersion() == '2.0' ||
99813                  $this->_packagexml->getPackagexmlVersion() == '2.1') {
99814            $this->validateTime();
99815            $this->validateStability();
99816            $this->validateDeps();
99817            $this->validateMainFilelist();
99818            $this->validateReleaseFilelist();
99819            //$this->validateGlobalTasks();
99820            $this->validateChangelog();
99821        }
99822        return !((bool) count($this->_failures['errors']));
99823    }
99824
99825    /**
99826     * @access protected
99827     */
99828    function validatePackageName()
99829    {
99830        if ($this->_state == PEAR_VALIDATE_PACKAGING ||
99831              $this->_state == PEAR_VALIDATE_NORMAL) {
99832            if (($this->_packagexml->getPackagexmlVersion() == '2.0' ||
99833                 $this->_packagexml->getPackagexmlVersion() == '2.1') &&
99834                  $this->_packagexml->getExtends()) {
99835                $version = $this->_packagexml->getVersion() . '';
99836                $name = $this->_packagexml->getPackage();
99837                $test = array_shift($a = explode('.', $version));
99838                if ($test == '0') {
99839                    return true;
99840                }
99841                $vlen = strlen($test);
99842                $majver = substr($name, strlen($name) - $vlen);
99843                while ($majver && !is_numeric($majver{0})) {
99844                    $majver = substr($majver, 1);
99845                }
99846                if ($majver != $test) {
99847                    $this->_addWarning('package', "package $name extends package " .
99848                        $this->_packagexml->getExtends() . ' and so the name should ' .
99849                        'have a postfix equal to the major version like "' .
99850                        $this->_packagexml->getExtends() . $test . '"');
99851                    return true;
99852                } elseif (substr($name, 0, strlen($name) - $vlen) !=
99853                            $this->_packagexml->getExtends()) {
99854                    $this->_addWarning('package', "package $name extends package " .
99855                        $this->_packagexml->getExtends() . ' and so the name must ' .
99856                        'be an extension like "' . $this->_packagexml->getExtends() .
99857                        $test . '"');
99858                    return true;
99859                }
99860            }
99861        }
99862        if (!$this->validPackageName($this->_packagexml->getPackage())) {
99863            $this->_addFailure('name', 'package name "' .
99864                $this->_packagexml->getPackage() . '" is invalid');
99865            return false;
99866        }
99867    }
99868
99869    /**
99870     * @access protected
99871     */
99872    function validateVersion()
99873    {
99874        if ($this->_state != PEAR_VALIDATE_PACKAGING) {
99875            if (!$this->validVersion($this->_packagexml->getVersion())) {
99876                $this->_addFailure('version',
99877                    'Invalid version number "' . $this->_packagexml->getVersion() . '"');
99878            }
99879            return false;
99880        }
99881        $version = $this->_packagexml->getVersion();
99882        $versioncomponents = explode('.', $version);
99883        if (count($versioncomponents) != 3) {
99884            $this->_addWarning('version',
99885                'A version number should have 3 decimals (x.y.z)');
99886            return true;
99887        }
99888        $name = $this->_packagexml->getPackage();
99889        // version must be based upon state
99890        switch ($this->_packagexml->getState()) {
99891            case 'snapshot' :
99892                return true;
99893            case 'devel' :
99894                if ($versioncomponents[0] . 'a' == '0a') {
99895                    return true;
99896                }
99897                if ($versioncomponents[0] == 0) {
99898                    $versioncomponents[0] = '0';
99899                    $this->_addWarning('version',
99900                        'version "' . $version . '" should be "' .
99901                        implode('.' ,$versioncomponents) . '"');
99902                } else {
99903                    $this->_addWarning('version',
99904                        'packages with devel stability must be < version 1.0.0');
99905                }
99906                return true;
99907            break;
99908            case 'alpha' :
99909            case 'beta' :
99910                // check for a package that extends a package,
99911                // like Foo and Foo2
99912                if ($this->_state == PEAR_VALIDATE_PACKAGING) {
99913                    if (substr($versioncomponents[2], 1, 2) == 'rc') {
99914                        $this->_addFailure('version', 'Release Candidate versions ' .
99915                            'must have capital RC, not lower-case rc');
99916                        return false;
99917                    }
99918                }
99919                if (!$this->_packagexml->getExtends()) {
99920                    if ($versioncomponents[0] == '1') {
99921                        if ($versioncomponents[2]{0} == '0') {
99922                            if ($versioncomponents[2] == '0') {
99923                                // version 1.*.0000
99924                                $this->_addWarning('version',
99925                                    'version 1.' . $versioncomponents[1] .
99926                                        '.0 probably should not be alpha or beta');
99927                                return true;
99928                            } elseif (strlen($versioncomponents[2]) > 1) {
99929                                // version 1.*.0RC1 or 1.*.0beta24 etc.
99930                                return true;
99931                            } else {
99932                                // version 1.*.0
99933                                $this->_addWarning('version',
99934                                    'version 1.' . $versioncomponents[1] .
99935                                        '.0 probably should not be alpha or beta');
99936                                return true;
99937                            }
99938                        } else {
99939                            $this->_addWarning('version',
99940                                'bugfix versions (1.3.x where x > 0) probably should ' .
99941                                'not be alpha or beta');
99942                            return true;
99943                        }
99944                    } elseif ($versioncomponents[0] != '0') {
99945                        $this->_addWarning('version',
99946                            'major versions greater than 1 are not allowed for packages ' .
99947                            'without an <extends> tag or an identical postfix (foo2 v2.0.0)');
99948                        return true;
99949                    }
99950                    if ($versioncomponents[0] . 'a' == '0a') {
99951                        return true;
99952                    }
99953                    if ($versioncomponents[0] == 0) {
99954                        $versioncomponents[0] = '0';
99955                        $this->_addWarning('version',
99956                            'version "' . $version . '" should be "' .
99957                            implode('.' ,$versioncomponents) . '"');
99958                    }
99959                } else {
99960                    $vlen = strlen($versioncomponents[0] . '');
99961                    $majver = substr($name, strlen($name) - $vlen);
99962                    while ($majver && !is_numeric($majver{0})) {
99963                        $majver = substr($majver, 1);
99964                    }
99965                    if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
99966                        $this->_addWarning('version', 'first version number "' .
99967                            $versioncomponents[0] . '" must match the postfix of ' .
99968                            'package name "' . $name . '" (' .
99969                            $majver . ')');
99970                        return true;
99971                    }
99972                    if ($versioncomponents[0] == $majver) {
99973                        if ($versioncomponents[2]{0} == '0') {
99974                            if ($versioncomponents[2] == '0') {
99975                                // version 2.*.0000
99976                                $this->_addWarning('version',
99977                                    "version $majver." . $versioncomponents[1] .
99978                                        '.0 probably should not be alpha or beta');
99979                                return false;
99980                            } elseif (strlen($versioncomponents[2]) > 1) {
99981                                // version 2.*.0RC1 or 2.*.0beta24 etc.
99982                                return true;
99983                            } else {
99984                                // version 2.*.0
99985                                $this->_addWarning('version',
99986                                    "version $majver." . $versioncomponents[1] .
99987                                        '.0 cannot be alpha or beta');
99988                                return true;
99989                            }
99990                        } else {
99991                            $this->_addWarning('version',
99992                                "bugfix versions ($majver.x.y where y > 0) should " .
99993                                'not be alpha or beta');
99994                            return true;
99995                        }
99996                    } elseif ($versioncomponents[0] != '0') {
99997                        $this->_addWarning('version',
99998                            "only versions 0.x.y and $majver.x.y are allowed for alpha/beta releases");
99999                        return true;
100000                    }
100001                    if ($versioncomponents[0] . 'a' == '0a') {
100002                        return true;
100003                    }
100004                    if ($versioncomponents[0] == 0) {
100005                        $versioncomponents[0] = '0';
100006                        $this->_addWarning('version',
100007                            'version "' . $version . '" should be "' .
100008                            implode('.' ,$versioncomponents) . '"');
100009                    }
100010                }
100011                return true;
100012            break;
100013            case 'stable' :
100014                if ($versioncomponents[0] == '0') {
100015                    $this->_addWarning('version', 'versions less than 1.0.0 cannot ' .
100016                    'be stable');
100017                    return true;
100018                }
100019                if (!is_numeric($versioncomponents[2])) {
100020                    if (preg_match('/\d+(rc|a|alpha|b|beta)\d*/i',
100021                          $versioncomponents[2])) {
100022                        $this->_addWarning('version', 'version "' . $version . '" or any ' .
100023                            'RC/beta/alpha version cannot be stable');
100024                        return true;
100025                    }
100026                }
100027                // check for a package that extends a package,
100028                // like Foo and Foo2
100029                if ($this->_packagexml->getExtends()) {
100030                    $vlen = strlen($versioncomponents[0] . '');
100031                    $majver = substr($name, strlen($name) - $vlen);
100032                    while ($majver && !is_numeric($majver{0})) {
100033                        $majver = substr($majver, 1);
100034                    }
100035                    if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
100036                        $this->_addWarning('version', 'first version number "' .
100037                            $versioncomponents[0] . '" must match the postfix of ' .
100038                            'package name "' . $name . '" (' .
100039                            $majver . ')');
100040                        return true;
100041                    }
100042                } elseif ($versioncomponents[0] > 1) {
100043                    $this->_addWarning('version', 'major version x in x.y.z may not be greater than ' .
100044                        '1 for any package that does not have an <extends> tag');
100045                }
100046                return true;
100047            break;
100048            default :
100049                return false;
100050            break;
100051        }
100052    }
100053
100054    /**
100055     * @access protected
100056     */
100057    function validateMaintainers()
100058    {
100059        // maintainers can only be truly validated server-side for most channels
100060        // but allow this customization for those who wish it
100061        return true;
100062    }
100063
100064    /**
100065     * @access protected
100066     */
100067    function validateDate()
100068    {
100069        if ($this->_state == PEAR_VALIDATE_NORMAL ||
100070              $this->_state == PEAR_VALIDATE_PACKAGING) {
100071
100072            if (!preg_match('/(\d\d\d\d)\-(\d\d)\-(\d\d)/',
100073                  $this->_packagexml->getDate(), $res) ||
100074                  count($res) < 4
100075                  || !checkdate($res[2], $res[3], $res[1])
100076                ) {
100077                $this->_addFailure('date', 'invalid release date "' .
100078                    $this->_packagexml->getDate() . '"');
100079                return false;
100080            }
100081
100082            if ($this->_state == PEAR_VALIDATE_PACKAGING &&
100083                  $this->_packagexml->getDate() != date('Y-m-d')) {
100084                $this->_addWarning('date', 'Release Date "' .
100085                    $this->_packagexml->getDate() . '" is not today');
100086            }
100087        }
100088        return true;
100089    }
100090
100091    /**
100092     * @access protected
100093     */
100094    function validateTime()
100095    {
100096        if (!$this->_packagexml->getTime()) {
100097            // default of no time value set
100098            return true;
100099        }
100100
100101        // packager automatically sets time, so only validate if pear validate is called
100102        if ($this->_state = PEAR_VALIDATE_NORMAL) {
100103            if (!preg_match('/\d\d:\d\d:\d\d/',
100104                  $this->_packagexml->getTime())) {
100105                $this->_addFailure('time', 'invalid release time "' .
100106                    $this->_packagexml->getTime() . '"');
100107                return false;
100108            }
100109
100110            $result = preg_match('|\d{2}\:\d{2}\:\d{2}|', $this->_packagexml->getTime(), $matches);
100111            if ($result === false || empty($matches)) {
100112                $this->_addFailure('time', 'invalid release time "' .
100113                    $this->_packagexml->getTime() . '"');
100114                return false;
100115            }
100116        }
100117
100118        return true;
100119    }
100120
100121    /**
100122     * @access protected
100123     */
100124    function validateState()
100125    {
100126        // this is the closest to "final" php4 can get
100127        if (!PEAR_Validate::validState($this->_packagexml->getState())) {
100128            if (strtolower($this->_packagexml->getState() == 'rc')) {
100129                $this->_addFailure('state', 'RC is not a state, it is a version ' .
100130                    'postfix, use ' . $this->_packagexml->getVersion() . 'RC1, state beta');
100131            }
100132            $this->_addFailure('state', 'invalid release state "' .
100133                $this->_packagexml->getState() . '", must be one of: ' .
100134                implode(', ', PEAR_Validate::getValidStates()));
100135            return false;
100136        }
100137        return true;
100138    }
100139
100140    /**
100141     * @access protected
100142     */
100143    function validateStability()
100144    {
100145        $ret = true;
100146        $packagestability = $this->_packagexml->getState();
100147        $apistability = $this->_packagexml->getState('api');
100148        if (!PEAR_Validate::validState($packagestability)) {
100149            $this->_addFailure('state', 'invalid release stability "' .
100150                $this->_packagexml->getState() . '", must be one of: ' .
100151                implode(', ', PEAR_Validate::getValidStates()));
100152            $ret = false;
100153        }
100154        $apistates = PEAR_Validate::getValidStates();
100155        array_shift($apistates); // snapshot is not allowed
100156        if (!in_array($apistability, $apistates)) {
100157            $this->_addFailure('state', 'invalid API stability "' .
100158                $this->_packagexml->getState('api') . '", must be one of: ' .
100159                implode(', ', $apistates));
100160            $ret = false;
100161        }
100162        return $ret;
100163    }
100164
100165    /**
100166     * @access protected
100167     */
100168    function validateSummary()
100169    {
100170        return true;
100171    }
100172
100173    /**
100174     * @access protected
100175     */
100176    function validateDescription()
100177    {
100178        return true;
100179    }
100180
100181    /**
100182     * @access protected
100183     */
100184    function validateLicense()
100185    {
100186        return true;
100187    }
100188
100189    /**
100190     * @access protected
100191     */
100192    function validateNotes()
100193    {
100194        return true;
100195    }
100196
100197    /**
100198     * for package.xml 2.0 only - channels can't use package.xml 1.0
100199     * @access protected
100200     */
100201    function validateDependencies()
100202    {
100203        return true;
100204    }
100205
100206    /**
100207     * for package.xml 1.0 only
100208     * @access private
100209     */
100210    function _validateFilelist()
100211    {
100212        return true; // placeholder for now
100213    }
100214
100215    /**
100216     * for package.xml 2.0 only
100217     * @access protected
100218     */
100219    function validateMainFilelist()
100220    {
100221        return true; // placeholder for now
100222    }
100223
100224    /**
100225     * for package.xml 2.0 only
100226     * @access protected
100227     */
100228    function validateReleaseFilelist()
100229    {
100230        return true; // placeholder for now
100231    }
100232
100233    /**
100234     * @access protected
100235     */
100236    function validateChangelog()
100237    {
100238        return true;
100239    }
100240
100241    /**
100242     * @access protected
100243     */
100244    function validateFilelist()
100245    {
100246        return true;
100247    }
100248
100249    /**
100250     * @access protected
100251     */
100252    function validateDeps()
100253    {
100254        return true;
100255    }
100256}<?php
100257/**
100258 * Channel Validator for the pecl.php.net channel
100259 *
100260 * PHP 4 and PHP 5
100261 *
100262 * @category   pear
100263 * @package    PEAR
100264 * @author     Greg Beaver <cellog@php.net>
100265 * @copyright  1997-2006 The PHP Group
100266 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
100267 * @version    CVS: $Id: PECL.php 313023 2011-07-06 19:17:11Z dufuz $
100268 * @link       http://pear.php.net/package/PEAR
100269 * @since      File available since Release 1.4.0a5
100270 */
100271/**
100272 * This is the parent class for all validators
100273 */
100274require_once 'phar://install-pear-nozlib.phar/' . 'PEAR/Validate.php';
100275/**
100276 * Channel Validator for the pecl.php.net channel
100277 * @category   pear
100278 * @package    PEAR
100279 * @author     Greg Beaver <cellog@php.net>
100280 * @copyright  1997-2009 The Authors
100281 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
100282 * @version    Release: 1.9.4
100283 * @link       http://pear.php.net/package/PEAR
100284 * @since      Class available since Release 1.4.0a5
100285 */
100286class PEAR_Validator_PECL extends PEAR_Validate
100287{
100288    function validateVersion()
100289    {
100290        if ($this->_state == PEAR_VALIDATE_PACKAGING) {
100291            $version = $this->_packagexml->getVersion();
100292            $versioncomponents = explode('.', $version);
100293            $last = array_pop($versioncomponents);
100294            if (substr($last, 1, 2) == 'rc') {
100295                $this->_addFailure('version', 'Release Candidate versions must have ' .
100296                'upper-case RC, not lower-case rc');
100297                return false;
100298            }
100299        }
100300        return true;
100301    }
100302
100303    function validatePackageName()
100304    {
100305        $ret = parent::validatePackageName();
100306        if ($this->_packagexml->getPackageType() == 'extsrc' ||
100307              $this->_packagexml->getPackageType() == 'zendextsrc') {
100308            if (strtolower($this->_packagexml->getPackage()) !=
100309                  strtolower($this->_packagexml->getProvidesExtension())) {
100310                $this->_addWarning('providesextension', 'package name "' .
100311                    $this->_packagexml->getPackage() . '" is different from extension name "' .
100312                    $this->_packagexml->getProvidesExtension() . '"');
100313            }
100314        }
100315        return $ret;
100316    }
100317}
100318?><?php
100319/**
100320 * PEAR_XMLParser
100321 *
100322 * PHP versions 4 and 5
100323 *
100324 * @category   pear
100325 * @package    PEAR
100326 * @author     Greg Beaver <cellog@php.net>
100327 * @author     Stephan Schmidt (original XML_Unserializer code)
100328 * @copyright  1997-2009 The Authors
100329 * @license   http://opensource.org/licenses/bsd-license New BSD License
100330 * @version    CVS: $Id: XMLParser.php 313023 2011-07-06 19:17:11Z dufuz $
100331 * @link       http://pear.php.net/package/PEAR
100332 * @since      File available since Release 1.4.0a1
100333 */
100334
100335/**
100336 * Parser for any xml file
100337 * @category  pear
100338 * @package   PEAR
100339 * @author    Greg Beaver <cellog@php.net>
100340 * @author    Stephan Schmidt (original XML_Unserializer code)
100341 * @copyright 1997-2009 The Authors
100342 * @license   http://opensource.org/licenses/bsd-license New BSD License
100343 * @version   Release: 1.9.4
100344 * @link      http://pear.php.net/package/PEAR
100345 * @since     Class available since Release 1.4.0a1
100346 */
100347class PEAR_XMLParser
100348{
100349    /**
100350     * unserilialized data
100351     * @var string $_serializedData
100352     */
100353    var $_unserializedData = null;
100354
100355    /**
100356     * name of the root tag
100357     * @var string $_root
100358     */
100359    var $_root = null;
100360
100361    /**
100362     * stack for all data that is found
100363     * @var array    $_dataStack
100364     */
100365    var $_dataStack = array();
100366
100367    /**
100368     * stack for all values that are generated
100369     * @var array    $_valStack
100370     */
100371    var $_valStack = array();
100372
100373    /**
100374     * current tag depth
100375     * @var int    $_depth
100376     */
100377    var $_depth = 0;
100378
100379    /**
100380     * The XML encoding to use
100381     * @var string $encoding
100382     */
100383    var $encoding = 'ISO-8859-1';
100384
100385    /**
100386     * @return array
100387     */
100388    function getData()
100389    {
100390        return $this->_unserializedData;
100391    }
100392
100393    /**
100394     * @param string xml content
100395     * @return true|PEAR_Error
100396     */
100397    function parse($data)
100398    {
100399        if (!extension_loaded('xml')) {
100400            include_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
100401            return PEAR::raiseError("XML Extension not found", 1);
100402        }
100403        $this->_dataStack =  $this->_valStack = array();
100404        $this->_depth = 0;
100405
100406        if (
100407            strpos($data, 'encoding="UTF-8"')
100408            || strpos($data, 'encoding="utf-8"')
100409            || strpos($data, "encoding='UTF-8'")
100410            || strpos($data, "encoding='utf-8'")
100411        ) {
100412            $this->encoding = 'UTF-8';
100413        }
100414
100415        if (version_compare(phpversion(), '5.0.0', 'lt') && $this->encoding == 'UTF-8') {
100416            $data = utf8_decode($data);
100417            $this->encoding = 'ISO-8859-1';
100418        }
100419
100420        $xp = xml_parser_create($this->encoding);
100421        xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, 0);
100422        xml_set_object($xp, $this);
100423        xml_set_element_handler($xp, 'startHandler', 'endHandler');
100424        xml_set_character_data_handler($xp, 'cdataHandler');
100425        if (!xml_parse($xp, $data)) {
100426            $msg = xml_error_string(xml_get_error_code($xp));
100427            $line = xml_get_current_line_number($xp);
100428            xml_parser_free($xp);
100429            include_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
100430            return PEAR::raiseError("XML Error: '$msg' on line '$line'", 2);
100431        }
100432        xml_parser_free($xp);
100433        return true;
100434    }
100435
100436    /**
100437     * Start element handler for XML parser
100438     *
100439     * @access private
100440     * @param  object $parser  XML parser object
100441     * @param  string $element XML element
100442     * @param  array  $attribs attributes of XML tag
100443     * @return void
100444     */
100445    function startHandler($parser, $element, $attribs)
100446    {
100447        $this->_depth++;
100448        $this->_dataStack[$this->_depth] = null;
100449
100450        $val = array(
100451            'name'         => $element,
100452            'value'        => null,
100453            'type'         => 'string',
100454            'childrenKeys' => array(),
100455            'aggregKeys'   => array()
100456       );
100457
100458        if (count($attribs) > 0) {
100459            $val['children'] = array();
100460            $val['type'] = 'array';
100461            $val['children']['attribs'] = $attribs;
100462        }
100463
100464        array_push($this->_valStack, $val);
100465    }
100466
100467    /**
100468     * post-process data
100469     *
100470     * @param string $data
100471     * @param string $element element name
100472     */
100473    function postProcess($data, $element)
100474    {
100475        return trim($data);
100476    }
100477
100478    /**
100479     * End element handler for XML parser
100480     *
100481     * @access private
100482     * @param  object XML parser object
100483     * @param  string
100484     * @return void
100485     */
100486    function endHandler($parser, $element)
100487    {
100488        $value = array_pop($this->_valStack);
100489        $data  = $this->postProcess($this->_dataStack[$this->_depth], $element);
100490
100491        // adjust type of the value
100492        switch (strtolower($value['type'])) {
100493            // unserialize an array
100494            case 'array':
100495                if ($data !== '') {
100496                    $value['children']['_content'] = $data;
100497                }
100498
100499                $value['value'] = isset($value['children']) ? $value['children'] : array();
100500                break;
100501
100502            /*
100503             * unserialize a null value
100504             */
100505            case 'null':
100506                $data = null;
100507                break;
100508
100509            /*
100510             * unserialize any scalar value
100511             */
100512            default:
100513                settype($data, $value['type']);
100514                $value['value'] = $data;
100515                break;
100516        }
100517
100518        $parent = array_pop($this->_valStack);
100519        if ($parent === null) {
100520            $this->_unserializedData = &$value['value'];
100521            $this->_root = &$value['name'];
100522            return true;
100523        }
100524
100525        // parent has to be an array
100526        if (!isset($parent['children']) || !is_array($parent['children'])) {
100527            $parent['children'] = array();
100528            if ($parent['type'] != 'array') {
100529                $parent['type'] = 'array';
100530            }
100531        }
100532
100533        if (!empty($value['name'])) {
100534            // there already has been a tag with this name
100535            if (in_array($value['name'], $parent['childrenKeys'])) {
100536                // no aggregate has been created for this tag
100537                if (!in_array($value['name'], $parent['aggregKeys'])) {
100538                    if (isset($parent['children'][$value['name']])) {
100539                        $parent['children'][$value['name']] = array($parent['children'][$value['name']]);
100540                    } else {
100541                        $parent['children'][$value['name']] = array();
100542                    }
100543                    array_push($parent['aggregKeys'], $value['name']);
100544                }
100545                array_push($parent['children'][$value['name']], $value['value']);
100546            } else {
100547                $parent['children'][$value['name']] = &$value['value'];
100548                array_push($parent['childrenKeys'], $value['name']);
100549            }
100550        } else {
100551            array_push($parent['children'],$value['value']);
100552        }
100553        array_push($this->_valStack, $parent);
100554
100555        $this->_depth--;
100556    }
100557
100558    /**
100559     * Handler for character data
100560     *
100561     * @access private
100562     * @param  object XML parser object
100563     * @param  string CDATA
100564     * @return void
100565     */
100566    function cdataHandler($parser, $cdata)
100567    {
100568        $this->_dataStack[$this->_depth] .= $cdata;
100569    }
100570}<?php
100571/**
100572 * This is only meant for PHP 5 to get rid of certain strict warning
100573 * that doesn't get hidden since it's in the shutdown function
100574 */
100575class PEAR5
100576{
100577    /**
100578    * If you have a class that's mostly/entirely static, and you need static
100579    * properties, you can use this method to simulate them. Eg. in your method(s)
100580    * do this: $myVar = &PEAR5::getStaticProperty('myclass', 'myVar');
100581    * You MUST use a reference, or they will not persist!
100582    *
100583    * @access public
100584    * @param  string $class  The calling classname, to prevent clashes
100585    * @param  string $var    The variable to retrieve.
100586    * @return mixed   A reference to the variable. If not set it will be
100587    *                 auto initialised to NULL.
100588    */
100589    static function &getStaticProperty($class, $var)
100590    {
100591        static $properties;
100592        if (!isset($properties[$class])) {
100593            $properties[$class] = array();
100594        }
100595
100596        if (!array_key_exists($var, $properties[$class])) {
100597            $properties[$class][$var] = null;
100598        }
100599
100600        return $properties[$class][$var];
100601    }
100602}<?php
100603/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
100604// +-----------------------------------------------------------------------------+
100605// | Copyright (c) 2003 S�rgio Gon�alves Carvalho                                |
100606// +-----------------------------------------------------------------------------+
100607// | This file is part of Structures_Graph.                                      |
100608// |                                                                             |
100609// | Structures_Graph is free software; you can redistribute it and/or modify    |
100610// | it under the terms of the GNU Lesser General Public License as published by |
100611// | the Free Software Foundation; either version 2.1 of the License, or         |
100612// | (at your option) any later version.                                         |
100613// |                                                                             |
100614// | Structures_Graph is distributed in the hope that it will be useful,         |
100615// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
100616// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
100617// | GNU Lesser General Public License for more details.                         |
100618// |                                                                             |
100619// | You should have received a copy of the GNU Lesser General Public License    |
100620// | along with Structures_Graph; if not, write to the Free Software             |
100621// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
100622// | 02111-1307 USA                                                              |
100623// +-----------------------------------------------------------------------------+
100624// | Author: S�rgio Carvalho <sergio.carvalho@portugalmail.com>                  |
100625// +-----------------------------------------------------------------------------+
100626//
100627/**
100628 * The Graph.php file contains the definition of the Structures_Graph class 
100629 *
100630 * @see Structures_Graph
100631 * @package Structures_Graph
100632 */
100633
100634/* dependencies {{{ */
100635/** PEAR base classes */
100636require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
100637/** Graph Node */
100638require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Node.php';
100639/* }}} */
100640
100641define('STRUCTURES_GRAPH_ERROR_GENERIC', 100);
100642
100643/* class Structures_Graph {{{ */
100644/**
100645 * The Structures_Graph class represents a graph data structure. 
100646 *
100647 * A Graph is a data structure composed by a set of nodes, connected by arcs.
100648 * Graphs may either be directed or undirected. In a directed graph, arcs are 
100649 * directional, and can be traveled only one way. In an undirected graph, arcs
100650 * are bidirectional, and can be traveled both ways.
100651 *
100652 * @author		S�rgio Carvalho <sergio.carvalho@portugalmail.com> 
100653 * @copyright	(c) 2004 by S�rgio Carvalho
100654 * @package Structures_Graph
100655 */
100656/* }}} */
100657class Structures_Graph {
100658    /* fields {{{ */
100659    /**
100660     * @access private
100661     */
100662    var $_nodes = array();
100663    /**
100664     * @access private
100665     */
100666    var $_directed = false;
100667    /* }}} */
100668
100669    /* Constructor {{{ */
100670    /**
100671    *
100672    * Constructor
100673    *
100674    * @param    boolean    Set to true if the graph is directed. Set to false if it is not directed. (Optional, defaults to true)
100675    * @access	public
100676    */
100677    function Structures_Graph($directed = true) {
100678        $this->_directed = $directed;
100679    }
100680    /* }}} */
100681
100682    /* isDirected {{{ */
100683    /**
100684    *
100685    * Return true if a graph is directed
100686    *
100687    * @return	boolean	 true if the graph is directed
100688    * @access	public
100689    */
100690    function isDirected() {
100691        return (boolean) $this->_directed;
100692    }
100693    /* }}} */
100694
100695    /* addNode {{{ */
100696    /**
100697    *
100698    * Add a Node to the Graph
100699    *
100700    * @param    Structures_Graph_Node   The node to be added.
100701    * @access	public
100702    */
100703    function addNode(&$newNode) {
100704        // We only add nodes
100705        if (!is_a($newNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph::addNode received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC);
100706        // Graphs are node *sets*, so duplicates are forbidden. We allow nodes that are exactly equal, but disallow equal references.
100707        foreach($this->_nodes as $key => $node) {
100708            /*
100709             ZE1 equality operators choke on the recursive cycle introduced by the _graph field in the Node object.
100710             So, we'll check references the hard way (change $this->_nodes[$key] and check if the change reflects in 
100711             $node)
100712            */
100713            $savedData = $this->_nodes[$key];
100714            $referenceIsEqualFlag = false;
100715            $this->_nodes[$key] = true;
100716            if ($node === true) {
100717                $this->_nodes[$key] = false;
100718                if ($node === false) $referenceIsEqualFlag = true;
100719            }
100720            $this->_nodes[$key] = $savedData;
100721            if ($referenceIsEqualFlag) return Pear::raiseError('Structures_Graph::addNode received an object that is a duplicate for this dataset', STRUCTURES_GRAPH_ERROR_GENERIC);
100722        }
100723        $this->_nodes[] =& $newNode;
100724        $newNode->setGraph($this);
100725    }
100726    /* }}} */
100727
100728    /* removeNode (unimplemented) {{{ */
100729    /**
100730    *
100731    * Remove a Node from the Graph
100732    *
100733    * @todo     This is unimplemented
100734    * @param    Structures_Graph_Node   The node to be removed from the graph
100735    * @access	public
100736    */
100737    function removeNode(&$node) {
100738    }
100739    /* }}} */
100740
100741    /* getNodes {{{ */
100742    /**
100743    *
100744    * Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted.
100745    *
100746    * @access   public
100747    * @see      Structures_Graph_Manipulator_TopologicalSorter
100748    * @return   array The set of nodes in this graph
100749    */
100750    function &getNodes() {
100751        return $this->_nodes;
100752    }
100753    /* }}} */
100754}
100755?>
100756<?php
100757/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
100758// +-----------------------------------------------------------------------------+
100759// | Copyright (c) 2003 S�rgio Gon�alves Carvalho                                |
100760// +-----------------------------------------------------------------------------+
100761// | This file is part of Structures_Graph.                                      |
100762// |                                                                             |
100763// | Structures_Graph is free software; you can redistribute it and/or modify    |
100764// | it under the terms of the GNU Lesser General Public License as published by |
100765// | the Free Software Foundation; either version 2.1 of the License, or         |
100766// | (at your option) any later version.                                         |
100767// |                                                                             |
100768// | Structures_Graph is distributed in the hope that it will be useful,         |
100769// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
100770// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
100771// | GNU Lesser General Public License for more details.                         |
100772// |                                                                             |
100773// | You should have received a copy of the GNU Lesser General Public License    |
100774// | along with Structures_Graph; if not, write to the Free Software             |
100775// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
100776// | 02111-1307 USA                                                              |
100777// +-----------------------------------------------------------------------------+
100778// | Author: S�rgio Carvalho <sergio.carvalho@portugalmail.com>                  |
100779// +-----------------------------------------------------------------------------+
100780//
100781/**
100782 * This file contains the definition of the Structures_Graph_Manipulator_AcyclicTest graph manipulator.
100783 * 
100784 * @see Structures_Graph_Manipulator_AcyclicTest
100785 * @package Structures_Graph
100786 */
100787
100788/* dependencies {{{ */
100789/** */
100790require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
100791/** */
100792require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph.php';
100793/** */
100794require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Node.php';
100795/* }}} */
100796
100797/* class Structures_Graph_Manipulator_AcyclicTest {{{ */
100798/**
100799 * The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator
100800 * which tests whether a graph contains a cycle. 
100801 * 
100802 * The definition of an acyclic graph used in this manipulator is that of a 
100803 * DAG. The graph must be directed, or else it is considered cyclic, even when 
100804 * there are no arcs.
100805 *
100806 * @author		S�rgio Carvalho <sergio.carvalho@portugalmail.com> 
100807 * @copyright	(c) 2004 by S�rgio Carvalho
100808 * @package Structures_Graph
100809 */
100810class Structures_Graph_Manipulator_AcyclicTest {
100811    /* _nonVisitedInDegree {{{ */
100812    /**
100813    *
100814    * This is a variant of Structures_Graph::inDegree which does 
100815    * not count nodes marked as visited.
100816    *
100817    * @access   private
100818    * @return	integer	 Number of non-visited nodes that link to this one
100819    */
100820    function _nonVisitedInDegree(&$node) {
100821        $result = 0;
100822        $graphNodes =& $node->_graph->getNodes();
100823        foreach (array_keys($graphNodes) as $key) {
100824            if ((!$graphNodes[$key]->getMetadata('acyclic-test-visited')) && $graphNodes[$key]->connectsTo($node)) $result++;
100825        }
100826        return $result;
100827        
100828    }
100829    /* }}} */
100830
100831    /* _isAcyclic {{{ */
100832    /**
100833    * @access   private
100834    */
100835    function _isAcyclic(&$graph) {
100836        // Mark every node as not visited
100837        $nodes =& $graph->getNodes();
100838        $nodeKeys = array_keys($nodes);
100839        $refGenerator = array();
100840        foreach($nodeKeys as $key) {
100841            $refGenerator[] = false;
100842            $nodes[$key]->setMetadata('acyclic-test-visited', $refGenerator[sizeof($refGenerator) - 1]);
100843        }
100844
100845        // Iteratively peel off leaf nodes
100846        do {
100847            // Find out which nodes are leafs (excluding visited nodes)
100848            $leafNodes = array();
100849            foreach($nodeKeys as $key) {
100850                if ((!$nodes[$key]->getMetadata('acyclic-test-visited')) && Structures_Graph_Manipulator_AcyclicTest::_nonVisitedInDegree($nodes[$key]) == 0) {
100851                    $leafNodes[] =& $nodes[$key];
100852                }
100853            }
100854            // Mark leafs as visited
100855            for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
100856                $visited =& $leafNodes[$i]->getMetadata('acyclic-test-visited');
100857                $visited = true;
100858                $leafNodes[$i]->setMetadata('acyclic-test-visited', $visited);
100859            }
100860        } while (sizeof($leafNodes) > 0);
100861
100862        // If graph is a DAG, there should be no non-visited nodes. Let's try to prove otherwise
100863        $result = true;
100864        foreach($nodeKeys as $key) if (!$nodes[$key]->getMetadata('acyclic-test-visited')) $result = false;
100865        
100866        // Cleanup visited marks
100867        foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('acyclic-test-visited');
100868
100869        return $result;
100870    }
100871    /* }}} */
100872
100873    /* isAcyclic {{{ */
100874    /**
100875    *
100876    * isAcyclic returns true if a graph contains no cycles, false otherwise.
100877    *
100878    * @return	boolean	 true iff graph is acyclic
100879    * @access	public
100880    */
100881    function isAcyclic(&$graph) {
100882        // We only test graphs
100883        if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_AcyclicTest::isAcyclic received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC);
100884        if (!$graph->isDirected()) return false; // Only directed graphs may be acyclic
100885
100886        return Structures_Graph_Manipulator_AcyclicTest::_isAcyclic($graph);
100887    }
100888    /* }}} */
100889}
100890/* }}} */
100891?>
100892<?php
100893/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
100894// +-----------------------------------------------------------------------------+
100895// | Copyright (c) 2003 S�rgio Gon�alves Carvalho                                |
100896// +-----------------------------------------------------------------------------+
100897// | This file is part of Structures_Graph.                                      |
100898// |                                                                             |
100899// | Structures_Graph is free software; you can redistribute it and/or modify    |
100900// | it under the terms of the GNU Lesser General Public License as published by |
100901// | the Free Software Foundation; either version 2.1 of the License, or         |
100902// | (at your option) any later version.                                         |
100903// |                                                                             |
100904// | Structures_Graph is distributed in the hope that it will be useful,         |
100905// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
100906// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
100907// | GNU Lesser General Public License for more details.                         |
100908// |                                                                             |
100909// | You should have received a copy of the GNU Lesser General Public License    |
100910// | along with Structures_Graph; if not, write to the Free Software             |
100911// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
100912// | 02111-1307 USA                                                              |
100913// +-----------------------------------------------------------------------------+
100914// | Author: S�rgio Carvalho <sergio.carvalho@portugalmail.com>                  |
100915// +-----------------------------------------------------------------------------+
100916//
100917/**
100918 * This file contains the definition of the Structures_Graph_Manipulator_TopologicalSorter class.
100919 * 
100920 * @see Structures_Graph_Manipulator_TopologicalSorter
100921 * @package Structures_Graph
100922 */
100923
100924/* dependencies {{{ */
100925/** */
100926require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
100927/** */
100928require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph.php';
100929/** */
100930require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Node.php';
100931/** */
100932require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph/Manipulator/AcyclicTest.php';
100933/* }}} */
100934
100935/* class Structures_Graph_Manipulator_TopologicalSorter {{{ */
100936/**
100937 * The Structures_Graph_Manipulator_TopologicalSorter is a manipulator 
100938 * which is able to return the set of nodes in a graph, sorted by topological 
100939 * order.
100940 *
100941 * A graph may only be sorted topologically iff it's a DAG. You can test it
100942 * with the Structures_Graph_Manipulator_AcyclicTest.
100943 * 
100944 * @author		S�rgio Carvalho <sergio.carvalho@portugalmail.com> 
100945 * @copyright	(c) 2004 by S�rgio Carvalho
100946 * @see     Structures_Graph_Manipulator_AcyclicTest
100947 * @package Structures_Graph
100948 */
100949class Structures_Graph_Manipulator_TopologicalSorter {
100950    /* _nonVisitedInDegree {{{ */
100951    /**
100952    *
100953    * This is a variant of Structures_Graph::inDegree which does 
100954    * not count nodes marked as visited.
100955    *
100956    * @access   private
100957    * @return	integer	 Number of non-visited nodes that link to this one
100958    */
100959    function _nonVisitedInDegree(&$node) {
100960        $result = 0;
100961        $graphNodes =& $node->_graph->getNodes();
100962        foreach (array_keys($graphNodes) as $key) {
100963            if ((!$graphNodes[$key]->getMetadata('topological-sort-visited')) && $graphNodes[$key]->connectsTo($node)) $result++;
100964        }
100965        return $result;
100966        
100967    }
100968    /* }}} */
100969
100970    /* _sort {{{ */
100971    /**
100972    * @access   private
100973    */
100974    function _sort(&$graph) {
100975        // Mark every node as not visited
100976        $nodes =& $graph->getNodes();
100977        $nodeKeys = array_keys($nodes);
100978        $refGenerator = array();
100979        foreach($nodeKeys as $key) {
100980            $refGenerator[] = false;
100981            $nodes[$key]->setMetadata('topological-sort-visited', $refGenerator[sizeof($refGenerator) - 1]);
100982        }
100983
100984        // Iteratively peel off leaf nodes
100985        $topologicalLevel = 0;
100986        do {
100987            // Find out which nodes are leafs (excluding visited nodes)
100988            $leafNodes = array();
100989            foreach($nodeKeys as $key) {
100990                if ((!$nodes[$key]->getMetadata('topological-sort-visited')) && Structures_Graph_Manipulator_TopologicalSorter::_nonVisitedInDegree($nodes[$key]) == 0) {
100991                    $leafNodes[] =& $nodes[$key];
100992                }
100993            }
100994            // Mark leafs as visited
100995            $refGenerator[] = $topologicalLevel;
100996            for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
100997                $visited =& $leafNodes[$i]->getMetadata('topological-sort-visited');
100998                $visited = true;
100999                $leafNodes[$i]->setMetadata('topological-sort-visited', $visited);
101000                $leafNodes[$i]->setMetadata('topological-sort-level', $refGenerator[sizeof($refGenerator) - 1]);
101001            }
101002            $topologicalLevel++;
101003        } while (sizeof($leafNodes) > 0);
101004
101005        // Cleanup visited marks
101006        foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('topological-sort-visited');
101007    }
101008    /* }}} */
101009
101010    /* sort {{{ */
101011    /**
101012    *
101013    * sort returns the graph's nodes, sorted by topological order. 
101014    * 
101015    * The result is an array with 
101016    * as many entries as topological levels. Each entry in this array is an array of nodes within
101017    * the given topological level.
101018    *
101019    * @return	array	 The graph's nodes, sorted by topological order.
101020    * @access	public
101021    */
101022    function sort(&$graph) {
101023        // We only sort graphs
101024        if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC);
101025        if (!Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph)) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an graph that has cycles', STRUCTURES_GRAPH_ERROR_GENERIC);
101026
101027        Structures_Graph_Manipulator_TopologicalSorter::_sort($graph);
101028        $result = array();
101029
101030        // Fill out result array
101031        $nodes =& $graph->getNodes();
101032        $nodeKeys = array_keys($nodes);
101033        foreach($nodeKeys as $key) {
101034            if (!array_key_exists($nodes[$key]->getMetadata('topological-sort-level'), $result)) $result[$nodes[$key]->getMetadata('topological-sort-level')] = array();
101035            $result[$nodes[$key]->getMetadata('topological-sort-level')][] =& $nodes[$key];
101036            $nodes[$key]->unsetMetadata('topological-sort-level');
101037        }
101038
101039        return $result;
101040    }
101041    /* }}} */
101042}
101043/* }}} */
101044?>
101045<?php
101046/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
101047// +-----------------------------------------------------------------------------+
101048// | Copyright (c) 2003 S�rgio Gon�alves Carvalho                                |
101049// +-----------------------------------------------------------------------------+
101050// | This file is part of Structures_Graph.                                      |
101051// |                                                                             |
101052// | Structures_Graph is free software; you can redistribute it and/or modify    |
101053// | it under the terms of the GNU Lesser General Public License as published by |
101054// | the Free Software Foundation; either version 2.1 of the License, or         |
101055// | (at your option) any later version.                                         |
101056// |                                                                             |
101057// | Structures_Graph is distributed in the hope that it will be useful,         |
101058// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
101059// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
101060// | GNU Lesser General Public License for more details.                         |
101061// |                                                                             |
101062// | You should have received a copy of the GNU Lesser General Public License    |
101063// | along with Structures_Graph; if not, write to the Free Software             |
101064// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
101065// | 02111-1307 USA                                                              |
101066// +-----------------------------------------------------------------------------+
101067// | Author: S�rgio Carvalho <sergio.carvalho@portugalmail.com>                  |
101068// +-----------------------------------------------------------------------------+
101069//
101070/**
101071 * This file contains the definition of the Structures_Graph_Node class
101072 * 
101073 * @see Structures_Graph_Node
101074 * @package Structures_Graph
101075 */
101076
101077/* dependencies {{{ */
101078/** */
101079require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
101080/** */
101081require_once 'phar://install-pear-nozlib.phar/' . 'Structures/Graph.php';
101082/* }}} */
101083
101084/* class Structures_Graph_Node {{{ */
101085/**
101086 * The Structures_Graph_Node class represents a Node that can be member of a 
101087 * graph node set.
101088 *
101089 * A graph node can contain data. Under this API, the node contains default data, 
101090 * and key index data. It behaves, thus, both as a regular data node, and as a 
101091 * dictionary (or associative array) node.
101092 * 
101093 * Regular data is accessed via getData and setData. Key indexed data is accessed
101094 * via getMetadata and setMetadata.
101095 *
101096 * @author		S�rgio Carvalho <sergio.carvalho@portugalmail.com> 
101097 * @copyright	(c) 2004 by S�rgio Carvalho
101098 * @package Structures_Graph
101099 */
101100/* }}} */
101101class Structures_Graph_Node {
101102    /* fields {{{ */
101103    /** 
101104     * @access private 
101105     */
101106    var $_data = null;
101107    /** @access private */
101108    var $_metadata = array();
101109    /** @access private */
101110    var $_arcs = array();
101111    /** @access private */
101112    var $_graph = null;
101113    /* }}} */
101114
101115    /* Constructor {{{ */
101116    /**
101117    *
101118    * Constructor
101119    *
101120    * @access	public
101121    */
101122    function Structures_Graph_Node() {
101123    }
101124    /* }}} */
101125
101126    /* getGraph {{{ */
101127    /**
101128    *
101129    * Node graph getter
101130    *
101131    * @return	Structures_Graph	Graph where node is stored
101132    * @access	public
101133    */
101134    function &getGraph() {
101135        return $this->_graph;
101136    }
101137    /* }}} */
101138
101139    /* setGraph {{{ */
101140    /**
101141    *
101142    * Node graph setter. This method should not be called directly. Use Graph::addNode instead.
101143    *
101144    * @param    Structures_Graph   Set the graph for this node. 
101145    * @see      Structures_Graph::addNode()
101146    * @access	public
101147    */
101148    function setGraph(&$graph) {
101149        $this->_graph =& $graph;
101150    }
101151    /* }}} */
101152
101153    /* getData {{{ */
101154    /**
101155    *
101156    * Node data getter.
101157    * 
101158    * Each graph node can contain a reference to one variable. This is the getter for that reference.
101159    *
101160    * @return	mixed	Data stored in node
101161    * @access	public
101162    */
101163    function &getData() {
101164        return $this->_data;
101165    }
101166    /* }}} */
101167
101168    /* setData {{{ */
101169    /**
101170    *
101171    * Node data setter
101172    *
101173    * Each graph node can contain a reference to one variable. This is the setter for that reference.
101174    *
101175    * @return	mixed	Data to store in node
101176    * @access	public
101177    */
101178    function setData($data) {
101179        $this->_data =& $data;
101180    }
101181    /* }}} */
101182
101183    /* metadataKeyExists {{{ */
101184    /**
101185    *
101186    * Test for existence of metadata under a given key.
101187    *
101188    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
101189    * associative array or in a dictionary. This method tests whether a given metadata key exists for this node.
101190    *
101191    * @param    string    Key to test
101192    * @return	boolean	 
101193    * @access	public
101194    */
101195    function metadataKeyExists($key) {
101196        return array_key_exists($key, $this->_metadata);
101197    }
101198    /* }}} */
101199
101200    /* getMetadata {{{ */
101201    /**
101202    *
101203    * Node metadata getter
101204    *
101205    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
101206    * associative array or in a dictionary. This method gets the data under the given key. If the key does
101207    * not exist, an error will be thrown, so testing using metadataKeyExists might be needed.
101208    *
101209    * @param    string  Key
101210    * @param    boolean nullIfNonexistent (defaults to false).
101211    * @return	mixed	Metadata Data stored in node under given key
101212    * @see      metadataKeyExists
101213    * @access	public
101214    */
101215    function &getMetadata($key, $nullIfNonexistent = false) {
101216        if (array_key_exists($key, $this->_metadata)) {
101217            return $this->_metadata[$key];
101218        } else {
101219            if ($nullIfNonexistent) {
101220                $a = null;
101221                return $a;
101222            } else {
101223                $a = Pear::raiseError('Structures_Graph_Node::getMetadata: Requested key does not exist', STRUCTURES_GRAPH_ERROR_GENERIC);
101224                return $a;
101225            }
101226        }
101227    }
101228    /* }}} */
101229
101230    /* unsetMetadata {{{ */
101231    /**
101232    *
101233    * Delete metadata by key
101234    *
101235    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
101236    * associative array or in a dictionary. This method removes any data that might be stored under the provided key.
101237    * If the key does not exist, no error is thrown, so it is safe using this method without testing for key existence.
101238    *
101239    * @param    string  Key
101240    * @access	public
101241    */
101242    function unsetMetadata($key) {
101243        if (array_key_exists($key, $this->_metadata)) unset($this->_metadata[$key]);
101244    }
101245    /* }}} */
101246
101247    /* setMetadata {{{ */
101248    /**
101249    *
101250    * Node metadata setter
101251    *
101252    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
101253    * associative array or in a dictionary. This method stores data under the given key. If the key already exists,
101254    * previously stored data is discarded.
101255    *
101256    * @param    string  Key
101257    * @param    mixed   Data 
101258    * @access	public
101259    */
101260    function setMetadata($key, $data) {
101261        $this->_metadata[$key] =& $data;
101262    }
101263    /* }}} */
101264
101265    /* _connectTo {{{ */
101266    /** @access private */
101267    function _connectTo(&$destinationNode) {
101268        $this->_arcs[] =& $destinationNode;
101269    }
101270    /* }}} */
101271
101272    /* connectTo {{{ */
101273    /**
101274    *
101275    * Connect this node to another one.
101276    * 
101277    * If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created.
101278    *
101279    * @param    Structures_Graph_Node Node to connect to
101280    * @access	public
101281    */
101282    function connectTo(&$destinationNode) {
101283        // We only connect to nodes
101284        if (!is_a($destinationNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph_Node::connectTo received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC);
101285        // Nodes must already be in graphs to be connected
101286        if ($this->_graph == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
101287        if ($destinationNode->getGraph() == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect to a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
101288        // Connect here
101289        $this->_connectTo($destinationNode);
101290        // If graph is undirected, connect back
101291        if (!$this->_graph->isDirected()) {
101292            $destinationNode->_connectTo($this);
101293        }
101294    }
101295    /* }}} */
101296
101297    /* getNeighbours {{{ */
101298    /**
101299    *
101300    * Return nodes connected to this one.
101301    * 
101302    * @return   array   Array of nodes
101303    * @access	public
101304    */
101305    function getNeighbours() {
101306        return $this->_arcs;
101307    }
101308    /* }}} */
101309
101310    /* connectsTo {{{ */
101311    /**
101312    *
101313    * Test wether this node has an arc to the target node
101314    *
101315    * @return	boolean   True if the two nodes are connected
101316    * @access	public
101317    */
101318    function connectsTo(&$target) {
101319        if (version_compare(PHP_VERSION, '5.0.0') >= 0) {
101320            return in_array($target, $this->getNeighbours(), true);
101321        }
101322
101323        $copy = $target;
101324        $arcKeys = array_keys($this->_arcs);
101325        foreach($arcKeys as $key) {
101326            /* ZE1 chokes on this expression:
101327                if ($target === $arc) return true;
101328              so, we'll use more convoluted stuff
101329            */
101330            $arc =& $this->_arcs[$key];
101331            $target = true;
101332            if ($arc === true) {
101333                $target = false;
101334                if ($arc === false) {
101335                    $target = $copy;
101336                    return true;
101337                }
101338            }
101339        }
101340        $target = $copy;
101341        return false;
101342    }
101343    /* }}} */
101344
101345    /* inDegree {{{ */
101346    /**
101347    *
101348    * Calculate the in degree of the node.
101349    * 
101350    * The indegree for a node is the number of arcs entering the node. For non directed graphs, 
101351    * the indegree is equal to the outdegree.
101352    *
101353    * @return	integer	 In degree of the node
101354    * @access	public
101355    */
101356    function inDegree() {
101357        if ($this->_graph == null) return 0;
101358        if (!$this->_graph->isDirected()) return $this->outDegree();
101359        $result = 0;
101360        $graphNodes =& $this->_graph->getNodes();
101361        foreach (array_keys($graphNodes) as $key) {
101362            if ($graphNodes[$key]->connectsTo($this)) $result++;
101363        }
101364        return $result;
101365        
101366    }
101367    /* }}} */
101368
101369    /* outDegree {{{ */
101370    /**
101371    *
101372    * Calculate the out degree of the node.
101373    *
101374    * The outdegree for a node is the number of arcs exiting the node. For non directed graphs,
101375    * the outdegree is always equal to the indegree.
101376    * 
101377    * @return	integer	 Out degree of the node
101378    * @access	public
101379    */
101380    function outDegree() {
101381        if ($this->_graph == null) return 0;
101382        return sizeof($this->_arcs);
101383    }
101384    /* }}} */
101385}
101386?>
101387package.xml0000644000076600000240000001543011461440276012461 0ustar  bbieberstaff<?xml version="1.0" encoding="UTF-8"?>
101388<package packagerversion="1.9.1" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0     http://pear.php.net/dtd/tasks-1.0.xsd     http://pear.php.net/dtd/package-2.0     http://pear.php.net/dtd/package-2.0.xsd">
101389 <name>Structures_Graph</name>
101390 <channel>pear.php.net</channel>
101391 <summary>Graph datastructure manipulation library</summary>
101392 <description>Structures_Graph is a package for creating and manipulating graph datastructures. It allows building of directed
101393and undirected graphs, with data and metadata stored in nodes. The library provides functions for graph traversing
101394as well as for characteristic extraction from the graph topology.</description>
101395 <lead>
101396  <name>Sérgio Carvalho</name>
101397  <user>sergiosgc</user>
101398  <email>sergio.carvalho@portugalmail.com</email>
101399  <active>yes</active>
101400 </lead>
101401 <helper>
101402  <name>Brett Bieber</name>
101403  <user>saltybeagle</user>
101404  <email>brett.bieber@gmail.com</email>
101405  <active>yes</active>
101406 </helper>
101407 <date>2010-10-25</date>
101408 <time>21:45:17</time>
101409 <version>
101410  <release>1.0.4</release>
101411  <api>1.0.3</api>
101412 </version>
101413 <stability>
101414  <release>stable</release>
101415  <api>stable</api>
101416 </stability>
101417 <license>LGPL License</license>
101418 <notes>
101419Bugfix Release:
101420* Bug #17108 BasicGraph::test_directed_degree fails on PHP 5 [clockwerx]
101421 </notes>
101422 <contents>
101423  <dir baseinstalldir="/" name="/">
101424   <file baseinstalldir="/" md5sum="e43ca110d02f287cdaac6357ba539fff" name="docs/html/media/banner.css" role="doc" />
101425   <file baseinstalldir="/" md5sum="296dd865297508569a6e72fcfd20fa81" name="docs/html/media/stylesheet.css" role="doc" />
101426   <file baseinstalldir="/" md5sum="678ccf89e31bc7337803afd6b7c58827" name="docs/html/Structures_Graph/Structures_Graph.html" role="doc" />
101427   <file baseinstalldir="/" md5sum="686b7bd7108cf5ce9b1ae5f17cea79f4" name="docs/html/Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html" role="doc" />
101428   <file baseinstalldir="/" md5sum="08b05a395eca4b0ca49a956fadf83da6" name="docs/html/Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html" role="doc" />
101429   <file baseinstalldir="/" md5sum="3fa8a9fae581fc31fd1dfbb14f475f92" name="docs/html/Structures_Graph/Structures_Graph_Node.html" role="doc" />
101430   <file baseinstalldir="/" md5sum="fd9b59eb75a39d3a25a175660dfb12be" name="docs/html/Structures_Graph/tutorial_Structures_Graph.pkg.html" role="doc" />
101431   <file baseinstalldir="/" md5sum="9cfeca2ff35a44b4bb921a9a818d8fa6" name="docs/html/Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html" role="doc" />
101432   <file baseinstalldir="/" md5sum="4faffdcc81cbc92520104e90a651a971" name="docs/html/Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_php.html" role="doc" />
101433   <file baseinstalldir="/" md5sum="0337573b69355c8b7ad36cd8f40ce859" name="docs/html/Structures_Graph/_Structures_Graph_Node_php.html" role="doc" />
101434   <file baseinstalldir="/" md5sum="7c1f852d7aa1a2fcada473723c8a46c2" name="docs/html/Structures_Graph/_Structures_Graph_php.html" role="doc" />
101435   <file baseinstalldir="/" md5sum="238f8a9d335e49ef87a0a276bcfc7231" name="docs/html/classtrees_Structures_Graph.html" role="doc" />
101436   <file baseinstalldir="/" md5sum="e302b63d3b18fa1c6e13f76816564d39" name="docs/html/elementindex.html" role="doc" />
101437   <file baseinstalldir="/" md5sum="c97ebad7c5635abbbbf35a23e868649e" name="docs/html/elementindex_Structures_Graph.html" role="doc" />
101438   <file baseinstalldir="/" md5sum="6f58a7616dd96fb8cc0cbf928f66dd33" name="docs/html/errors.html" role="doc" />
101439   <file baseinstalldir="/" md5sum="cf5397f529a0d9a701fac13ac6aaaa69" name="docs/html/index.html" role="doc" />
101440   <file baseinstalldir="/" md5sum="b206403136db1de58901f825ad9f9cb0" name="docs/html/li_Structures_Graph.html" role="doc" />
101441   <file baseinstalldir="/" md5sum="a88b0fcc2f97dd2069f671ef1dc92b40" name="docs/html/packages.html" role="doc" />
101442   <file baseinstalldir="/" md5sum="6fdd16675f2181b53a4d2dc2c419752b" name="docs/html/todolist.html" role="doc" />
101443   <file baseinstalldir="/" md5sum="628eb6532a8047bf5962fe24c1c245df" name="docs/tutorials/Structures_Graph/Structures_Graph.pkg" role="doc" />
101444   <file baseinstalldir="/" md5sum="ce2da39dbb75e21074eb5e96231a3379" name="docs/generate.sh" role="doc" />
101445   <file baseinstalldir="/" md5sum="f0aff5a1efd188d63b4b8b9e9e840b97" name="Structures/Graph/Manipulator/AcyclicTest.php" role="php" />
101446   <file baseinstalldir="/" md5sum="0492e677436d29228df93dca23629e06" name="Structures/Graph/Manipulator/TopologicalSorter.php" role="php" />
101447   <file baseinstalldir="/" md5sum="254ebaba7537ad0f36e63eb8b975cc51" name="Structures/Graph/Node.php" role="php" />
101448   <file baseinstalldir="/" md5sum="4f25a6275af156f6f8e7b4309cb9f40d" name="Structures/Graph.php" role="php" />
101449   <file baseinstalldir="/" md5sum="5791baa61d5d36442be58ea5cd9d4bd0" name="tests/testCase/BasicGraph.php" role="test" />
101450   <file baseinstalldir="/" md5sum="4fed49ef60db01eed800105aae4f2c8b" name="tests/AllTests.php" role="test" />
101451   <file baseinstalldir="/" md5sum="7fbc338309ac38fefcd64b04bb903e34" name="LICENSE" role="data" />
101452  </dir>
101453 </contents>
101454 <compatible>
101455  <name>PEAR</name>
101456  <channel>pear.php.net</channel>
101457  <min>1.5.0RC3</min>
101458  <max>1.9.1</max>
101459 </compatible>
101460 <dependencies>
101461  <required>
101462   <php>
101463    <min>4.2.0</min>
101464   </php>
101465   <pearinstaller>
101466    <min>1.4.3</min>
101467   </pearinstaller>
101468  </required>
101469 </dependencies>
101470 <phprelease />
101471 <changelog>
101472  <release>
101473   <version>
101474    <release>1.0.2</release>
101475    <api>1.0.0</api>
101476   </version>
101477   <stability>
101478    <release>stable</release>
101479    <api>stable</api>
101480   </stability>
101481   <date>2007-01-07</date>
101482   <license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
101483   <notes>
101484- Bug #9682 only variables can be returned by reference
101485- fix Bug #9661 notice in Structures_Graph_Manipulator_Topological::sort()
101486   </notes>
101487  </release>
101488  <release>
101489   <version>
101490    <release>1.0.3</release>
101491    <api>1.0.3</api>
101492   </version>
101493   <stability>
101494    <release>stable</release>
101495    <api>stable</api>
101496   </stability>
101497   <date>2009-10-11</date>
101498   <license>LGPL License</license>
101499   <notes>
101500Bugfix Release:
101501Version 1.0.3 is functionally equivalent to 1.0.2 but with an updated package.xml file.
101502* Correct invalid md5 sum preventing installation with pyrus [saltybeagle]
101503* Add compatible tag for PEAR 1.5.0RC3-1.9.0 [saltybeagle]
101504* Update package.xml
101505   </notes>
101506  </release>
101507  <release>
101508   <version>
101509    <release>1.0.4</release>
101510    <api>1.0.3</api>
101511   </version>
101512   <stability>
101513    <release>stable</release>
101514    <api>stable</api>
101515   </stability>
101516   <date>2010-10-25</date>
101517   <license>LGPL License</license>
101518   <notes>
101519Bugfix Release:
101520* Bug #17108 BasicGraph::test_directed_degree fails on PHP 5 [clockwerx]
101521   </notes>
101522  </release>
101523 </changelog>
101524</package>
101525Structures_Graph-1.0.4/docs/html/media/banner.css0000644000076600000240000000061111461440275021232 0ustar  bbieberstaffbody 
101526{ 
101527	background-color: #CCCCFF; 
101528	margin: 0px; 
101529	padding: 0px;
101530}
101531
101532/* Banner (top bar) classes */
101533
101534.banner {  }
101535
101536.banner-menu 
101537{ 
101538	clear: both;
101539	padding: .5em;
101540	border-top: 2px solid #6666AA;	
101541}
101542
101543.banner-title 
101544{ 
101545	text-align: right; 
101546	font-size: 20pt; 
101547	font-weight: bold; 
101548	margin: .2em;
101549}
101550
101551.package-selector 
101552{ 
101553	background-color: #AAAADD; 
101554	border: 1px solid black; 
101555	color: yellow;
101556}
101557Structures_Graph-1.0.4/docs/html/media/stylesheet.css0000644000076600000240000001160411461440275022162 0ustar  bbieberstaffa { color: #336699; text-decoration: none; }
101558a:hover { color: #6699CC; text-decoration: underline; }
101559a:active { color: #6699CC; text-decoration: underline; }
101560
101561body { background : #FFFFFF; }
101562body, table { font-family: Georgia, Times New Roman, Times, serif; font-size: 10pt }
101563p, li { line-height: 140% }
101564a img { border: 0px; }
101565dd { margin-left: 0px; padding-left: 1em; }
101566
101567/* Page layout/boxes */
101568
101569.info-box {}
101570.info-box-title { margin: 1em 0em 0em 0em; padding: .25em; font-weight: normal; font-size: 14pt; border: 2px solid #999999; background-color: #CCCCFF }
101571.info-box-body { border: 1px solid #999999; padding: .5em; }
101572.nav-bar { font-size: 8pt; white-space: nowrap; text-align: right; padding: .2em; margin: 0em 0em 1em 0em; }
101573
101574.oddrow { background-color: #F8F8F8; border: 1px solid #AAAAAA; padding: .5em; margin-bottom: 1em}
101575.evenrow { border: 1px solid #AAAAAA; padding: .5em; margin-bottom: 1em}
101576
101577.page-body { max-width: 800px; margin: auto; }
101578.tree dl { margin: 0px }
101579
101580/* Index formatting classes */
101581
101582.index-item-body { margin-top: .5em; margin-bottom: .5em}
101583.index-item-description { margin-top: .25em }
101584.index-item-details { font-weight: normal; font-style: italic; font-size: 8pt }
101585.index-letter-section { background-color: #EEEEEE; border: 1px dotted #999999; padding: .5em; margin-bottom: 1em}
101586.index-letter-title { font-size: 12pt; font-weight: bold }
101587.index-letter-menu { text-align: center; margin: 1em }
101588.index-letter { font-size: 12pt }
101589
101590/* Docbook classes */
101591
101592.description {}
101593.short-description { font-weight: bold; color: #666666; }
101594.tags {	padding-left: 0em; margin-left: 3em; color: #666666; list-style-type: square; }
101595.parameters {	padding-left: 0em; margin-left: 3em; font-style: italic; list-style-type: square; }
101596.redefinitions { font-size: 8pt; padding-left: 0em; margin-left: 2em; }
101597.package {  }
101598.package-title { font-weight: bold; font-size: 14pt; border-bottom: 1px solid black }
101599.package-details { font-size: 85%; }
101600.sub-package { font-weight: bold; font-size: 120% }
101601.tutorial { border-width: thin; border-color: #0066ff }
101602.tutorial-nav-box { width: 100%; border: 1px solid #999999; background-color: #F8F8F8; }
101603.nav-button-disabled { color: #999999; }
101604.nav-button:active, 
101605.nav-button:focus, 
101606.nav-button:hover { background-color: #DDDDDD; outline: 1px solid #999999; text-decoration: none }
101607.folder-title { font-style: italic }
101608
101609/* Generic formatting */
101610
101611.field { font-weight: bold; }
101612.detail { font-size: 8pt; }
101613.notes { font-style: italic; font-size: 8pt; }
101614.separator { background-color: #999999; height: 2px; }
101615.warning {  color: #FF6600; }
101616.disabled { font-style: italic; color: #999999; }
101617
101618/* Code elements */
101619
101620.line-number {  }
101621
101622.class-table { width: 100%; }
101623.class-table-header { border-bottom: 1px dotted #666666; text-align: left}
101624.class-name { color: #000000; font-weight: bold; }
101625
101626.method-summary { padding-left: 1em; font-size: 8pt }
101627.method-header { }
101628.method-definition { margin-bottom: .3em }
101629.method-title { font-weight: bold; }
101630.method-name { font-weight: bold; }
101631.method-signature { font-size: 85%; color: #666666; margin: .5em 0em }
101632.method-result { font-style: italic; }
101633
101634.var-summary { padding-left: 1em; font-size: 8pt; }
101635.var-header { }
101636.var-title { margin-bottom: .3em }
101637.var-type { font-style: italic; }
101638.var-name { font-weight: bold; }
101639.var-default {}
101640.var-description { font-weight: normal; color: #000000; }
101641
101642.include-title {  }
101643.include-type { font-style: italic; }
101644.include-name { font-weight: bold; }
101645
101646.const-title {  }
101647.const-name { font-weight: bold; }
101648
101649/* Syntax highlighting */
101650
101651.src-code {  border: 1px solid #336699; padding: 1em; background-color: #EEEEEE; }
101652
101653.src-comm { color: green; }
101654.src-id {  }
101655.src-inc { color: #0000FF; }
101656.src-key { color: #0000FF; }
101657.src-num { color: #CC0000; }
101658.src-str { color: #66cccc; }
101659.src-sym { font-weight: bold; }
101660.src-var { }
101661
101662.src-php { font-weight: bold; }
101663
101664.src-doc { color: #009999 }
101665.src-doc-close-template { color: #0000FF }
101666.src-doc-coretag { color: #0099FF; font-weight: bold }
101667.src-doc-inlinetag { color: #0099FF }
101668.src-doc-internal { color: #6699cc }
101669.src-doc-tag { color: #0080CC }
101670.src-doc-template { color: #0000FF }
101671.src-doc-type { font-style: italic }
101672.src-doc-var { font-style: italic }
101673
101674/* tutorial */
101675
101676.authors {  }
101677.author { font-style: italic; font-weight: bold }
101678.author-blurb { margin: .5em 0em .5em 2em; font-size: 85%; font-weight: normal; font-style: normal }
101679.example { border: 1px dashed #999999; background-color: #EEEEEE; padding: .5em }
101680.listing { border: 1px dashed #999999; background-color: #EEEEEE; padding: .5em; white-space: nowrap }
101681.release-info { font-size: 85%; font-style: italic; margin: 1em 0em }
101682.ref-title-box {  }
101683.ref-title {  }
101684.ref-purpose { font-style: italic; color: #666666 }
101685.ref-synopsis {  }
101686.title { font-weight: bold; margin: 1em 0em 0em 0em; padding: .25em; border: 2px solid #999999; background-color: #CCCCFF  }
101687.cmd-synopsis { margin: 1em 0em }
101688.cmd-title { font-weight: bold }
101689.toc { margin-left: 2em; padding-left: 0em }
101690
101691Structures_Graph-1.0.4/docs/html/Structures_Graph/Structures_Graph.html0000644000076600000240000002127311461440275025701 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
101692<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
101693  <html xmlns="http://www.w3.org/1999/xhtml">
101694		<head>
101695			<!-- template designed by Marco Von Ballmoos -->
101696			<title>Docs For Class Structures_Graph</title>
101697			<link rel="stylesheet" href="../media/stylesheet.css" />
101698			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
101699		</head>
101700		<body>
101701			<div class="page-body">			
101702<h2 class="class-name">Class Structures_Graph</h2>
101703
101704<a name="sec-description"></a>
101705<div class="info-box">
101706	<div class="info-box-title">Description</div>
101707	<div class="nav-bar">
101708					<span class="disabled">Description</span> |
101709															<a href="#sec-method-summary">Methods</a> (<a href="#sec-methods">details</a>)
101710						
101711			</div>
101712	<div class="info-box-body">
101713		<!-- ========== Info from phpDoc block ========= -->
101714<p class="short-description">The Structures_Graph class represents a graph data structure.</p>
101715<p class="description"><p>A Graph is a data structure composed by a set of nodes, connected by arcs.  Graphs may either be directed or undirected. In a directed graph, arcs are  directional, and can be traveled only one way. In an undirected graph, arcs  are bidirectional, and can be traveled both ways.</p></p>
101716	<ul class="tags">
101717				<li><span class="field">copyright:</span> (c) 2004 by S�rgio Carvalho</li>
101718				<li><span class="field">author:</span> S�rgio Carvalho &lt;<a href="mailto:sergio.carvalho@portugalmail.com">mailto:sergio.carvalho@portugalmail.com</a>&gt;</li>
101719			</ul>
101720		<p class="notes">
101721			Located in <a class="field" href="_Structures_Graph_php.html">/Structures/Graph.php</a> (line <span class="field">56</span>)
101722		</p>
101723		
101724				
101725		<pre></pre>
101726	
101727			</div>
101728</div>
101729
101730
101731
101732	<a name="sec-method-summary"></a>
101733	<div class="info-box">
101734		<div class="info-box-title">Method Summary</span></div>
101735		<div class="nav-bar">
101736			<a href="#sec-description">Description</a> |
101737									<span class="disabled">Methods</span> (<a href="#sec-methods">details</a>)
101738		</div>
101739		<div class="info-box-body">			
101740			<div class="method-summary">
101741								
101742				<div class="method-definition">
101743											<span class="method-result">Structures_Graph</span>
101744										<a href="#Structures_Graph" title="details" class="method-name">Structures_Graph</a>
101745											([<span class="var-type">boolean</span>&nbsp;<span class="var-name">$directed</span> = <span class="var-default">true</span>])
101746									</div>
101747								
101748				<div class="method-definition">
101749											<span class="method-result">void</span>
101750										<a href="#addNode" title="details" class="method-name">addNode</a>
101751											(<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>&nbsp;<span class="var-name">&$newNode</span>)
101752									</div>
101753								
101754				<div class="method-definition">
101755											<span class="method-result">array</span>
101756										<a href="#getNodes" title="details" class="method-name">&amp;getNodes</a>
101757										()
101758									</div>
101759								
101760				<div class="method-definition">
101761											<span class="method-result">boolean</span>
101762										<a href="#isDirected" title="details" class="method-name">isDirected</a>
101763										()
101764									</div>
101765								
101766				<div class="method-definition">
101767											<span class="method-result">void</span>
101768										<a href="#removeNode" title="details" class="method-name">removeNode</a>
101769											(<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>&nbsp;<span class="var-name">&$node</span>)
101770									</div>
101771							</div>
101772		</div>
101773	</div>		
101774
101775	
101776	<a name="sec-methods"></a>
101777	<div class="info-box">
101778		<div class="info-box-title">Methods</div>
101779		<div class="nav-bar">
101780			<a href="#sec-description">Description</a> |
101781													<a href="#sec-method-summary">Methods</a> (<span class="disabled">details</span>)
101782						
101783		</div>
101784		<div class="info-box-body">
101785			<A NAME='method_detail'></A>
101786<a name="methodStructures_Graph" id="Structures_Graph"><!-- --></a>
101787<div class="evenrow">
101788	
101789	<div class="method-header">
101790		<span class="method-title">Constructor Structures_Graph</span> (line <span class="line-number">76</span>)
101791	</div> 
101792	
101793	<!-- ========== Info from phpDoc block ========= -->
101794<p class="short-description">Constructor</p>
101795	<ul class="tags">
101796				<li><span class="field">access:</span> public</li>
101797			</ul>
101798	
101799	<div class="method-signature">
101800		<span class="method-result">Structures_Graph</span>
101801		<span class="method-name">
101802			Structures_Graph
101803		</span>
101804					([<span class="var-type">boolean</span>&nbsp;<span class="var-name">$directed</span> = <span class="var-default">true</span>])
101805			</div>
101806	
101807			<ul class="parameters">
101808					<li>
101809				<span class="var-type">boolean</span>
101810				<span class="var-name">$directed</span><span class="var-description">: Set to true if the graph is directed. Set to false if it is not directed. (Optional, defaults to true)</span>			</li>
101811				</ul>
101812		
101813		
101814	</div>
101815<a name="methodaddNode" id="addNode"><!-- --></a>
101816<div class="oddrow">
101817	
101818	<div class="method-header">
101819		<span class="method-title">addNode</span> (line <span class="line-number">102</span>)
101820	</div> 
101821	
101822	<!-- ========== Info from phpDoc block ========= -->
101823<p class="short-description">Add a Node to the Graph</p>
101824	<ul class="tags">
101825				<li><span class="field">access:</span> public</li>
101826			</ul>
101827	
101828	<div class="method-signature">
101829		<span class="method-result">void</span>
101830		<span class="method-name">
101831			addNode
101832		</span>
101833					(<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>&nbsp;<span class="var-name">&$newNode</span>)
101834			</div>
101835	
101836			<ul class="parameters">
101837					<li>
101838				<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>
101839				<span class="var-name">&$newNode</span><span class="var-description">: The node to be added.</span>			</li>
101840				</ul>
101841		
101842		
101843	</div>
101844<a name="methodgetNodes" id="getNodes"><!-- --></a>
101845<div class="evenrow">
101846	
101847	<div class="method-header">
101848		<span class="method-title">getNodes</span> (line <span class="line-number">151</span>)
101849	</div> 
101850	
101851	<!-- ========== Info from phpDoc block ========= -->
101852<p class="short-description">Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted.</p>
101853	<ul class="tags">
101854				<li><span class="field">return:</span> The set of nodes in this graph</li>
101855				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a></li>
101856				<li><span class="field">access:</span> public</li>
101857			</ul>
101858	
101859	<div class="method-signature">
101860		<span class="method-result">array</span>
101861		<span class="method-name">
101862			&amp;getNodes
101863		</span>
101864				()
101865			</div>
101866	
101867		
101868		
101869	</div>
101870<a name="methodisDirected" id="isDirected"><!-- --></a>
101871<div class="oddrow">
101872	
101873	<div class="method-header">
101874		<span class="method-title">isDirected</span> (line <span class="line-number">89</span>)
101875	</div> 
101876	
101877	<!-- ========== Info from phpDoc block ========= -->
101878<p class="short-description">Return true if a graph is directed</p>
101879	<ul class="tags">
101880				<li><span class="field">return:</span> true if the graph is directed</li>
101881				<li><span class="field">access:</span> public</li>
101882			</ul>
101883	
101884	<div class="method-signature">
101885		<span class="method-result">boolean</span>
101886		<span class="method-name">
101887			isDirected
101888		</span>
101889				()
101890			</div>
101891	
101892		
101893		
101894	</div>
101895<a name="methodremoveNode" id="removeNode"><!-- --></a>
101896<div class="evenrow">
101897	
101898	<div class="method-header">
101899		<span class="method-title">removeNode</span> (line <span class="line-number">138</span>)
101900	</div> 
101901	
101902	<!-- ========== Info from phpDoc block ========= -->
101903<p class="short-description">Remove a Node from the Graph</p>
101904	<ul class="tags">
101905				<li><span class="field">access:</span> public</li>
101906				<li><span class="field">todo:</span> This is unimplemented</li>
101907			</ul>
101908	
101909	<div class="method-signature">
101910		<span class="method-result">void</span>
101911		<span class="method-name">
101912			removeNode
101913		</span>
101914					(<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>&nbsp;<span class="var-name">&$node</span>)
101915			</div>
101916	
101917			<ul class="parameters">
101918					<li>
101919				<span class="var-type"><a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></span>
101920				<span class="var-name">&$node</span><span class="var-description">: The node to be removed from the graph</span>			</li>
101921				</ul>
101922		
101923		
101924	</div>
101925						
101926		</div>
101927	</div>
101928	
101929	<p class="notes" id="credit">
101930		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
101931	</p>
101932	</div></body>
101933</html>Structures_Graph-1.0.4/docs/html/Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html0000644000076600000240000000741011461440275032540 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
101934<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
101935  <html xmlns="http://www.w3.org/1999/xhtml">
101936		<head>
101937			<!-- template designed by Marco Von Ballmoos -->
101938			<title>Docs For Class Structures_Graph_Manipulator_AcyclicTest</title>
101939			<link rel="stylesheet" href="../media/stylesheet.css" />
101940			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
101941		</head>
101942		<body>
101943			<div class="page-body">			
101944<h2 class="class-name">Class Structures_Graph_Manipulator_AcyclicTest</h2>
101945
101946<a name="sec-description"></a>
101947<div class="info-box">
101948	<div class="info-box-title">Description</div>
101949	<div class="nav-bar">
101950					<span class="disabled">Description</span> |
101951															<a href="#sec-method-summary">Methods</a> (<a href="#sec-methods">details</a>)
101952						
101953			</div>
101954	<div class="info-box-body">
101955		<!-- ========== Info from phpDoc block ========= -->
101956<p class="short-description">The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator  which tests whether a graph contains a cycle.</p>
101957<p class="description"><p>The definition of an acyclic graph used in this manipulator is that of a  DAG. The graph must be directed, or else it is considered cyclic, even when  there are no arcs.</p></p>
101958	<ul class="tags">
101959				<li><span class="field">copyright:</span> (c) 2004 by S�rgio Carvalho</li>
101960				<li><span class="field">author:</span> S�rgio Carvalho &lt;<a href="mailto:sergio.carvalho@portugalmail.com">mailto:sergio.carvalho@portugalmail.com</a>&gt;</li>
101961			</ul>
101962		<p class="notes">
101963			Located in <a class="field" href="_Structures_Graph_Manipulator_AcyclicTest_php.html">/Structures/Graph/Manipulator/AcyclicTest.php</a> (line <span class="field">55</span>)
101964		</p>
101965		
101966				
101967		<pre></pre>
101968	
101969			</div>
101970</div>
101971
101972
101973
101974	<a name="sec-method-summary"></a>
101975	<div class="info-box">
101976		<div class="info-box-title">Method Summary</span></div>
101977		<div class="nav-bar">
101978			<a href="#sec-description">Description</a> |
101979									<span class="disabled">Methods</span> (<a href="#sec-methods">details</a>)
101980		</div>
101981		<div class="info-box-body">			
101982			<div class="method-summary">
101983								
101984				<div class="method-definition">
101985											<span class="method-result">boolean</span>
101986										<a href="#isAcyclic" title="details" class="method-name">isAcyclic</a>
101987											(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$graph</span>)
101988									</div>
101989							</div>
101990		</div>
101991	</div>		
101992
101993	
101994	<a name="sec-methods"></a>
101995	<div class="info-box">
101996		<div class="info-box-title">Methods</div>
101997		<div class="nav-bar">
101998			<a href="#sec-description">Description</a> |
101999													<a href="#sec-method-summary">Methods</a> (<span class="disabled">details</span>)
102000						
102001		</div>
102002		<div class="info-box-body">
102003			<A NAME='method_detail'></A>
102004<a name="methodisAcyclic" id="isAcyclic"><!-- --></a>
102005<div class="evenrow">
102006	
102007	<div class="method-header">
102008		<span class="method-title">isAcyclic</span> (line <span class="line-number">126</span>)
102009	</div> 
102010	
102011	<!-- ========== Info from phpDoc block ========= -->
102012<p class="short-description">isAcyclic returns true if a graph contains no cycles, false otherwise.</p>
102013	<ul class="tags">
102014				<li><span class="field">return:</span> true iff graph is acyclic</li>
102015				<li><span class="field">access:</span> public</li>
102016			</ul>
102017	
102018	<div class="method-signature">
102019		<span class="method-result">boolean</span>
102020		<span class="method-name">
102021			isAcyclic
102022		</span>
102023					(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$graph</span>)
102024			</div>
102025	
102026		
102027		
102028	</div>
102029						
102030		</div>
102031	</div>
102032	
102033	<p class="notes" id="credit">
102034		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
102035	</p>
102036	</div></body>
102037</html>././@LongLink000        145 0003736 LStructures_Graph-1.0.4/docs/html/Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.htmlStructures_Graph-1.0.4/docs/html/Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.htm0000644000076600000240000001014111461440275033623 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
102038<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
102039  <html xmlns="http://www.w3.org/1999/xhtml">
102040		<head>
102041			<!-- template designed by Marco Von Ballmoos -->
102042			<title>Docs For Class Structures_Graph_Manipulator_TopologicalSorter</title>
102043			<link rel="stylesheet" href="../media/stylesheet.css" />
102044			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
102045		</head>
102046		<body>
102047			<div class="page-body">			
102048<h2 class="class-name">Class Structures_Graph_Manipulator_TopologicalSorter</h2>
102049
102050<a name="sec-description"></a>
102051<div class="info-box">
102052	<div class="info-box-title">Description</div>
102053	<div class="nav-bar">
102054					<span class="disabled">Description</span> |
102055															<a href="#sec-method-summary">Methods</a> (<a href="#sec-methods">details</a>)
102056						
102057			</div>
102058	<div class="info-box-body">
102059		<!-- ========== Info from phpDoc block ========= -->
102060<p class="short-description">The Structures_Graph_Manipulator_TopologicalSorter is a manipulator  which is able to return the set of nodes in a graph, sorted by topological  order.</p>
102061<p class="description"><p>A graph may only be sorted topologically iff it's a DAG. You can test it  with the Structures_Graph_Manipulator_AcyclicTest.</p></p>
102062	<ul class="tags">
102063				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a></li>
102064				<li><span class="field">copyright:</span> (c) 2004 by S�rgio Carvalho</li>
102065				<li><span class="field">author:</span> S�rgio Carvalho &lt;<a href="mailto:sergio.carvalho@portugalmail.com">mailto:sergio.carvalho@portugalmail.com</a>&gt;</li>
102066			</ul>
102067		<p class="notes">
102068			Located in <a class="field" href="_Structures_Graph_Manipulator_TopologicalSorter_php.html">/Structures/Graph/Manipulator/TopologicalSorter.php</a> (line <span class="field">58</span>)
102069		</p>
102070		
102071				
102072		<pre></pre>
102073	
102074			</div>
102075</div>
102076
102077
102078
102079	<a name="sec-method-summary"></a>
102080	<div class="info-box">
102081		<div class="info-box-title">Method Summary</span></div>
102082		<div class="nav-bar">
102083			<a href="#sec-description">Description</a> |
102084									<span class="disabled">Methods</span> (<a href="#sec-methods">details</a>)
102085		</div>
102086		<div class="info-box-body">			
102087			<div class="method-summary">
102088								
102089				<div class="method-definition">
102090											<span class="method-result">array</span>
102091										<a href="#sort" title="details" class="method-name">sort</a>
102092											(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$graph</span>)
102093									</div>
102094							</div>
102095		</div>
102096	</div>		
102097
102098	
102099	<a name="sec-methods"></a>
102100	<div class="info-box">
102101		<div class="info-box-title">Methods</div>
102102		<div class="nav-bar">
102103			<a href="#sec-description">Description</a> |
102104													<a href="#sec-method-summary">Methods</a> (<span class="disabled">details</span>)
102105						
102106		</div>
102107		<div class="info-box-body">
102108			<A NAME='method_detail'></A>
102109<a name="methodsort" id="sort"><!-- --></a>
102110<div class="evenrow">
102111	
102112	<div class="method-header">
102113		<span class="method-title">sort</span> (line <span class="line-number">133</span>)
102114	</div> 
102115	
102116	<!-- ========== Info from phpDoc block ========= -->
102117<p class="short-description">sort returns the graph's nodes, sorted by topological order.</p>
102118<p class="description"><p>The result is an array with  as many entries as topological levels. Each entry in this array is an array of nodes within  the given topological level.</p></p>
102119	<ul class="tags">
102120				<li><span class="field">return:</span> The graph's nodes, sorted by topological order.</li>
102121				<li><span class="field">access:</span> public</li>
102122			</ul>
102123	
102124	<div class="method-signature">
102125		<span class="method-result">array</span>
102126		<span class="method-name">
102127			sort
102128		</span>
102129					(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$graph</span>)
102130			</div>
102131	
102132		
102133		
102134	</div>
102135						
102136		</div>
102137	</div>
102138	
102139	<p class="notes" id="credit">
102140		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
102141	</p>
102142	</div></body>
102143</html>Structures_Graph-1.0.4/docs/html/Structures_Graph/Structures_Graph_Node.html0000644000076600000240000005007111461440275026644 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
102144<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
102145  <html xmlns="http://www.w3.org/1999/xhtml">
102146		<head>
102147			<!-- template designed by Marco Von Ballmoos -->
102148			<title>Docs For Class Structures_Graph_Node</title>
102149			<link rel="stylesheet" href="../media/stylesheet.css" />
102150			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
102151		</head>
102152		<body>
102153			<div class="page-body">			
102154<h2 class="class-name">Class Structures_Graph_Node</h2>
102155
102156<a name="sec-description"></a>
102157<div class="info-box">
102158	<div class="info-box-title">Description</div>
102159	<div class="nav-bar">
102160					<span class="disabled">Description</span> |
102161															<a href="#sec-method-summary">Methods</a> (<a href="#sec-methods">details</a>)
102162						
102163			</div>
102164	<div class="info-box-body">
102165		<!-- ========== Info from phpDoc block ========= -->
102166<p class="short-description">The Structures_Graph_Node class represents a Node that can be member of a  graph node set.</p>
102167<p class="description"><p>A graph node can contain data. Under this API, the node contains default data,  and key index data. It behaves, thus, both as a regular data node, and as a  dictionary (or associative array) node.</p><p>Regular data is accessed via getData and setData. Key indexed data is accessed  via getMetadata and setMetadata.</p></p>
102168	<ul class="tags">
102169				<li><span class="field">copyright:</span> (c) 2004 by S�rgio Carvalho</li>
102170				<li><span class="field">author:</span> S�rgio Carvalho &lt;<a href="mailto:sergio.carvalho@portugalmail.com">mailto:sergio.carvalho@portugalmail.com</a>&gt;</li>
102171			</ul>
102172		<p class="notes">
102173			Located in <a class="field" href="_Structures_Graph_Node_php.html">/Structures/Graph/Node.php</a> (line <span class="field">57</span>)
102174		</p>
102175		
102176				
102177		<pre></pre>
102178	
102179			</div>
102180</div>
102181
102182
102183
102184	<a name="sec-method-summary"></a>
102185	<div class="info-box">
102186		<div class="info-box-title">Method Summary</span></div>
102187		<div class="nav-bar">
102188			<a href="#sec-description">Description</a> |
102189									<span class="disabled">Methods</span> (<a href="#sec-methods">details</a>)
102190		</div>
102191		<div class="info-box-body">			
102192			<div class="method-summary">
102193								
102194				<div class="method-definition">
102195											<span class="method-result">Structures_Graph_Node</span>
102196										<a href="#Structures_Graph_Node" title="details" class="method-name">Structures_Graph_Node</a>
102197										()
102198									</div>
102199								
102200				<div class="method-definition">
102201											<span class="method-result">boolean</span>
102202										<a href="#connectsTo" title="details" class="method-name">connectsTo</a>
102203											(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$target</span>)
102204									</div>
102205								
102206				<div class="method-definition">
102207											<span class="method-result">void</span>
102208										<a href="#connectTo" title="details" class="method-name">connectTo</a>
102209											(<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>&nbsp;<span class="var-name">&$destinationNode</span>)
102210									</div>
102211								
102212				<div class="method-definition">
102213											<span class="method-result">mixed</span>
102214										<a href="#getData" title="details" class="method-name">&amp;getData</a>
102215										()
102216									</div>
102217								
102218				<div class="method-definition">
102219											<span class="method-result"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>
102220										<a href="#getGraph" title="details" class="method-name">&amp;getGraph</a>
102221										()
102222									</div>
102223								
102224				<div class="method-definition">
102225											<span class="method-result">mixed</span>
102226										<a href="#getMetadata" title="details" class="method-name">&amp;getMetadata</a>
102227											(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>, [<span class="var-type">boolean</span>&nbsp;<span class="var-name">$nullIfNonexistent</span> = <span class="var-default">false</span>])
102228									</div>
102229								
102230				<div class="method-definition">
102231											<span class="method-result">array</span>
102232										<a href="#getNeighbours" title="details" class="method-name">getNeighbours</a>
102233										()
102234									</div>
102235								
102236				<div class="method-definition">
102237											<span class="method-result">integer</span>
102238										<a href="#inDegree" title="details" class="method-name">inDegree</a>
102239										()
102240									</div>
102241								
102242				<div class="method-definition">
102243											<span class="method-result">boolean</span>
102244										<a href="#metadataKeyExists" title="details" class="method-name">metadataKeyExists</a>
102245											(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>)
102246									</div>
102247								
102248				<div class="method-definition">
102249											<span class="method-result">integer</span>
102250										<a href="#outDegree" title="details" class="method-name">outDegree</a>
102251										()
102252									</div>
102253								
102254				<div class="method-definition">
102255											<span class="method-result">mixed</span>
102256										<a href="#setData" title="details" class="method-name">setData</a>
102257											(<span class="var-type">mixed</span>&nbsp;<span class="var-name">$data</span>)
102258									</div>
102259								
102260				<div class="method-definition">
102261											<span class="method-result">void</span>
102262										<a href="#setGraph" title="details" class="method-name">setGraph</a>
102263											(<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>&nbsp;<span class="var-name">&$graph</span>)
102264									</div>
102265								
102266				<div class="method-definition">
102267											<span class="method-result">void</span>
102268										<a href="#setMetadata" title="details" class="method-name">setMetadata</a>
102269											(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>, <span class="var-type">mixed</span>&nbsp;<span class="var-name">$data</span>)
102270									</div>
102271								
102272				<div class="method-definition">
102273											<span class="method-result">void</span>
102274										<a href="#unsetMetadata" title="details" class="method-name">unsetMetadata</a>
102275											(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>)
102276									</div>
102277							</div>
102278		</div>
102279	</div>		
102280
102281	
102282	<a name="sec-methods"></a>
102283	<div class="info-box">
102284		<div class="info-box-title">Methods</div>
102285		<div class="nav-bar">
102286			<a href="#sec-description">Description</a> |
102287													<a href="#sec-method-summary">Methods</a> (<span class="disabled">details</span>)
102288						
102289		</div>
102290		<div class="info-box-body">
102291			<A NAME='method_detail'></A>
102292<a name="methodStructures_Graph_Node" id="Structures_Graph_Node"><!-- --></a>
102293<div class="evenrow">
102294	
102295	<div class="method-header">
102296		<span class="method-title">Constructor Structures_Graph_Node</span> (line <span class="line-number">78</span>)
102297	</div> 
102298	
102299	<!-- ========== Info from phpDoc block ========= -->
102300<p class="short-description">Constructor</p>
102301	<ul class="tags">
102302				<li><span class="field">access:</span> public</li>
102303			</ul>
102304	
102305	<div class="method-signature">
102306		<span class="method-result">Structures_Graph_Node</span>
102307		<span class="method-name">
102308			Structures_Graph_Node
102309		</span>
102310				()
102311			</div>
102312	
102313		
102314		
102315	</div>
102316<a name="methodconnectsTo" id="connectsTo"><!-- --></a>
102317<div class="oddrow">
102318	
102319	<div class="method-header">
102320		<span class="method-title">connectsTo</span> (line <span class="line-number">275</span>)
102321	</div> 
102322	
102323	<!-- ========== Info from phpDoc block ========= -->
102324<p class="short-description">Test wether this node has an arc to the target node</p>
102325	<ul class="tags">
102326				<li><span class="field">return:</span> True if the two nodes are connected</li>
102327				<li><span class="field">access:</span> public</li>
102328			</ul>
102329	
102330	<div class="method-signature">
102331		<span class="method-result">boolean</span>
102332		<span class="method-name">
102333			connectsTo
102334		</span>
102335					(<span class="var-type">mixed</span>&nbsp;<span class="var-name">&$target</span>)
102336			</div>
102337	
102338		
102339		
102340	</div>
102341<a name="methodconnectTo" id="connectTo"><!-- --></a>
102342<div class="evenrow">
102343	
102344	<div class="method-header">
102345		<span class="method-title">connectTo</span> (line <span class="line-number">236</span>)
102346	</div> 
102347	
102348	<!-- ========== Info from phpDoc block ========= -->
102349<p class="short-description">Connect this node to another one.</p>
102350<p class="description"><p>If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created.</p></p>
102351	<ul class="tags">
102352				<li><span class="field">access:</span> public</li>
102353			</ul>
102354	
102355	<div class="method-signature">
102356		<span class="method-result">void</span>
102357		<span class="method-name">
102358			connectTo
102359		</span>
102360					(<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>&nbsp;<span class="var-name">&$destinationNode</span>)
102361			</div>
102362	
102363			<ul class="parameters">
102364					<li>
102365				<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>
102366				<span class="var-name">&$destinationNode</span><span class="var-description">: Node to connect to</span>			</li>
102367				</ul>
102368		
102369		
102370	</div>
102371<a name="methodgetData" id="getData"><!-- --></a>
102372<div class="oddrow">
102373	
102374	<div class="method-header">
102375		<span class="method-title">getData</span> (line <span class="line-number">119</span>)
102376	</div> 
102377	
102378	<!-- ========== Info from phpDoc block ========= -->
102379<p class="short-description">Node data getter.</p>
102380<p class="description"><p>Each graph node can contain a reference to one variable. This is the getter for that reference.</p></p>
102381	<ul class="tags">
102382				<li><span class="field">return:</span> Data stored in node</li>
102383				<li><span class="field">access:</span> public</li>
102384			</ul>
102385	
102386	<div class="method-signature">
102387		<span class="method-result">mixed</span>
102388		<span class="method-name">
102389			&amp;getData
102390		</span>
102391				()
102392			</div>
102393	
102394		
102395		
102396	</div>
102397<a name="methodgetGraph" id="getGraph"><!-- --></a>
102398<div class="evenrow">
102399	
102400	<div class="method-header">
102401		<span class="method-title">getGraph</span> (line <span class="line-number">90</span>)
102402	</div> 
102403	
102404	<!-- ========== Info from phpDoc block ========= -->
102405<p class="short-description">Node graph getter</p>
102406	<ul class="tags">
102407				<li><span class="field">return:</span> Graph where node is stored</li>
102408				<li><span class="field">access:</span> public</li>
102409			</ul>
102410	
102411	<div class="method-signature">
102412		<span class="method-result"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>
102413		<span class="method-name">
102414			&amp;getGraph
102415		</span>
102416				()
102417			</div>
102418	
102419		
102420		
102421	</div>
102422<a name="methodgetMetadata" id="getMetadata"><!-- --></a>
102423<div class="oddrow">
102424	
102425	<div class="method-header">
102426		<span class="method-title">getMetadata</span> (line <span class="line-number">171</span>)
102427	</div> 
102428	
102429	<!-- ========== Info from phpDoc block ========= -->
102430<p class="short-description">Node metadata getter</p>
102431<p class="description"><p>Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an  associative array or in a dictionary. This method gets the data under the given key. If the key does  not exist, an error will be thrown, so testing using metadataKeyExists might be needed.</p></p>
102432	<ul class="tags">
102433				<li><span class="field">return:</span> Metadata Data stored in node under given key</li>
102434				<li><span class="field">access:</span> public</li>
102435				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Node.html#methodmetadataKeyExists">Structures_Graph_Node::metadataKeyExists()</a></li>
102436			</ul>
102437	
102438	<div class="method-signature">
102439		<span class="method-result">mixed</span>
102440		<span class="method-name">
102441			&amp;getMetadata
102442		</span>
102443					(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>, [<span class="var-type">boolean</span>&nbsp;<span class="var-name">$nullIfNonexistent</span> = <span class="var-default">false</span>])
102444			</div>
102445	
102446			<ul class="parameters">
102447					<li>
102448				<span class="var-type">string</span>
102449				<span class="var-name">$key</span><span class="var-description">: Key</span>			</li>
102450					<li>
102451				<span class="var-type">boolean</span>
102452				<span class="var-name">$nullIfNonexistent</span><span class="var-description">: nullIfNonexistent (defaults to false).</span>			</li>
102453				</ul>
102454		
102455		
102456	</div>
102457<a name="methodgetNeighbours" id="getNeighbours"><!-- --></a>
102458<div class="evenrow">
102459	
102460	<div class="method-header">
102461		<span class="method-title">getNeighbours</span> (line <span class="line-number">262</span>)
102462	</div> 
102463	
102464	<!-- ========== Info from phpDoc block ========= -->
102465<p class="short-description">Return nodes connected to this one.</p>
102466	<ul class="tags">
102467				<li><span class="field">return:</span> Array of nodes</li>
102468				<li><span class="field">access:</span> public</li>
102469			</ul>
102470	
102471	<div class="method-signature">
102472		<span class="method-result">array</span>
102473		<span class="method-name">
102474			getNeighbours
102475		</span>
102476				()
102477			</div>
102478	
102479		
102480		
102481	</div>
102482<a name="methodinDegree" id="inDegree"><!-- --></a>
102483<div class="oddrow">
102484	
102485	<div class="method-header">
102486		<span class="method-title">inDegree</span> (line <span class="line-number">309</span>)
102487	</div> 
102488	
102489	<!-- ========== Info from phpDoc block ========= -->
102490<p class="short-description">Calculate the in degree of the node.</p>
102491<p class="description"><p>The indegree for a node is the number of arcs entering the node. For non directed graphs,  the indegree is equal to the outdegree.</p></p>
102492	<ul class="tags">
102493				<li><span class="field">return:</span> In degree of the node</li>
102494				<li><span class="field">access:</span> public</li>
102495			</ul>
102496	
102497	<div class="method-signature">
102498		<span class="method-result">integer</span>
102499		<span class="method-name">
102500			inDegree
102501		</span>
102502				()
102503			</div>
102504	
102505		
102506		
102507	</div>
102508<a name="methodmetadataKeyExists" id="metadataKeyExists"><!-- --></a>
102509<div class="evenrow">
102510	
102511	<div class="method-header">
102512		<span class="method-title">metadataKeyExists</span> (line <span class="line-number">151</span>)
102513	</div> 
102514	
102515	<!-- ========== Info from phpDoc block ========= -->
102516<p class="short-description">Test for existence of metadata under a given key.</p>
102517<p class="description"><p>Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an  associative array or in a dictionary. This method tests whether a given metadata key exists for this node.</p></p>
102518	<ul class="tags">
102519				<li><span class="field">access:</span> public</li>
102520			</ul>
102521	
102522	<div class="method-signature">
102523		<span class="method-result">boolean</span>
102524		<span class="method-name">
102525			metadataKeyExists
102526		</span>
102527					(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>)
102528			</div>
102529	
102530			<ul class="parameters">
102531					<li>
102532				<span class="var-type">string</span>
102533				<span class="var-name">$key</span><span class="var-description">: Key to test</span>			</li>
102534				</ul>
102535		
102536		
102537	</div>
102538<a name="methodoutDegree" id="outDegree"><!-- --></a>
102539<div class="oddrow">
102540	
102541	<div class="method-header">
102542		<span class="method-title">outDegree</span> (line <span class="line-number">333</span>)
102543	</div> 
102544	
102545	<!-- ========== Info from phpDoc block ========= -->
102546<p class="short-description">Calculate the out degree of the node.</p>
102547<p class="description"><p>The outdegree for a node is the number of arcs exiting the node. For non directed graphs,  the outdegree is always equal to the indegree.</p></p>
102548	<ul class="tags">
102549				<li><span class="field">return:</span> Out degree of the node</li>
102550				<li><span class="field">access:</span> public</li>
102551			</ul>
102552	
102553	<div class="method-signature">
102554		<span class="method-result">integer</span>
102555		<span class="method-name">
102556			outDegree
102557		</span>
102558				()
102559			</div>
102560	
102561		
102562		
102563	</div>
102564<a name="methodsetData" id="setData"><!-- --></a>
102565<div class="evenrow">
102566	
102567	<div class="method-header">
102568		<span class="method-title">setData</span> (line <span class="line-number">134</span>)
102569	</div> 
102570	
102571	<!-- ========== Info from phpDoc block ========= -->
102572<p class="short-description">Node data setter</p>
102573<p class="description"><p>Each graph node can contain a reference to one variable. This is the setter for that reference.</p></p>
102574	<ul class="tags">
102575				<li><span class="field">return:</span> Data to store in node</li>
102576				<li><span class="field">access:</span> public</li>
102577			</ul>
102578	
102579	<div class="method-signature">
102580		<span class="method-result">mixed</span>
102581		<span class="method-name">
102582			setData
102583		</span>
102584					(<span class="var-type">mixed</span>&nbsp;<span class="var-name">$data</span>)
102585			</div>
102586	
102587		
102588		
102589	</div>
102590<a name="methodsetGraph" id="setGraph"><!-- --></a>
102591<div class="oddrow">
102592	
102593	<div class="method-header">
102594		<span class="method-title">setGraph</span> (line <span class="line-number">104</span>)
102595	</div> 
102596	
102597	<!-- ========== Info from phpDoc block ========= -->
102598<p class="short-description">Node graph setter. This method should not be called directly. Use Graph::addNode instead.</p>
102599	<ul class="tags">
102600				<li><span class="field">access:</span> public</li>
102601				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph.html#methodaddNode">Structures_Graph::addNode()</a></li>
102602			</ul>
102603	
102604	<div class="method-signature">
102605		<span class="method-result">void</span>
102606		<span class="method-name">
102607			setGraph
102608		</span>
102609					(<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>&nbsp;<span class="var-name">&$graph</span>)
102610			</div>
102611	
102612			<ul class="parameters">
102613					<li>
102614				<span class="var-type"><a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></span>
102615				<span class="var-name">&$graph</span><span class="var-description">: Set the graph for this node.</span>			</li>
102616				</ul>
102617		
102618		
102619	</div>
102620<a name="methodsetMetadata" id="setMetadata"><!-- --></a>
102621<div class="evenrow">
102622	
102623	<div class="method-header">
102624		<span class="method-title">setMetadata</span> (line <span class="line-number">214</span>)
102625	</div> 
102626	
102627	<!-- ========== Info from phpDoc block ========= -->
102628<p class="short-description">Node metadata setter</p>
102629<p class="description"><p>Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an  associative array or in a dictionary. This method stores data under the given key. If the key already exists,  previously stored data is discarded.</p></p>
102630	<ul class="tags">
102631				<li><span class="field">access:</span> public</li>
102632			</ul>
102633	
102634	<div class="method-signature">
102635		<span class="method-result">void</span>
102636		<span class="method-name">
102637			setMetadata
102638		</span>
102639					(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>, <span class="var-type">mixed</span>&nbsp;<span class="var-name">$data</span>)
102640			</div>
102641	
102642			<ul class="parameters">
102643					<li>
102644				<span class="var-type">string</span>
102645				<span class="var-name">$key</span><span class="var-description">: Key</span>			</li>
102646					<li>
102647				<span class="var-type">mixed</span>
102648				<span class="var-name">$data</span><span class="var-description">: Data</span>			</li>
102649				</ul>
102650		
102651		
102652	</div>
102653<a name="methodunsetMetadata" id="unsetMetadata"><!-- --></a>
102654<div class="oddrow">
102655	
102656	<div class="method-header">
102657		<span class="method-title">unsetMetadata</span> (line <span class="line-number">196</span>)
102658	</div> 
102659	
102660	<!-- ========== Info from phpDoc block ========= -->
102661<p class="short-description">Delete metadata by key</p>
102662<p class="description"><p>Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an  associative array or in a dictionary. This method removes any data that might be stored under the provided key.  If the key does not exist, no error is thrown, so it is safe using this method without testing for key existence.</p></p>
102663	<ul class="tags">
102664				<li><span class="field">access:</span> public</li>
102665			</ul>
102666	
102667	<div class="method-signature">
102668		<span class="method-result">void</span>
102669		<span class="method-name">
102670			unsetMetadata
102671		</span>
102672					(<span class="var-type">string</span>&nbsp;<span class="var-name">$key</span>)
102673			</div>
102674	
102675			<ul class="parameters">
102676					<li>
102677				<span class="var-type">string</span>
102678				<span class="var-name">$key</span><span class="var-description">: Key</span>			</li>
102679				</ul>
102680		
102681		
102682	</div>
102683						
102684		</div>
102685	</div>
102686	
102687	<p class="notes" id="credit">
102688		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
102689	</p>
102690	</div></body>
102691</html>Structures_Graph-1.0.4/docs/html/Structures_Graph/tutorial_Structures_Graph.pkg.html0000644000076600000240000001110611461440275030376 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
102692<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
102693  <html xmlns="http://www.w3.org/1999/xhtml">
102694		<head>
102695			<!-- template designed by Marco Von Ballmoos -->
102696			<title>Structures_Graph Tutorial</title>
102697			<link rel="stylesheet" href="../media/stylesheet.css" />
102698			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
102699		</head>
102700		<body>
102701			<div class="page-body">			
102702
102703<div><a name="package.database.structures_graph.tutorial"></a><div class="ref-title-box"><h1 class="ref-title">Structures_Graph Tutorial</h1>
102704  <h2 class="ref-purpose">A first tour of graph datastructure manipulation</h2></div>
102705 <span><a name="package.database.structures_graph.tutorial.intro"></a><h2 class="title">Introduction</h2><p>Structures_Graph is a package for creating and manipulating graph datastructures. A graph is a set of objects, called nodes, connected by arcs. When used as a datastructure, usually nodes contain data, and arcs represent relationships between nodes. When arcs have a direction, and can be travelled only one way, graphs are said to be directed. When arcs have no direction, and can always be travelled both ways, graphs are said to be non directed.</p>
102706  <p>Structures_Graph provides an object oriented API to create and directly query a graph, as well as a set of Manipulator classes to extract information from the graph.</p></span>
102707 <span><a name="package.database.structures_graph.tutorial.creation"></a><h2 class="title">Creating a Graph</h2><p>Creating a graph is done using the simple constructor:
102708   <pre class="listing"><pre>
102709require_once 'Structures/Graph.php';
102710
102711$directedGraph =&amp; new Structures_Graph(true);
102712$nonDirectedGraph =&amp; new Structures_Graph(false);
102713    </pre></pre>
102714   and passing the constructor a flag telling it whether the graph should be directed. A directed graph will always be directed during its lifetime. It's a permanent characteristic.</p>
102715  <p>To fill out the graph, we'll need to create some nodes, and then call Graph::addNode.
102716   <pre class="listing"><pre>
102717require_once 'Structures/Graph/Node.php';
102718
102719$nodeOne =&amp; new Structures_Graph_Node();
102720$nodeTwo =&amp; new Structures_Graph_Node();
102721$nodeThree =&amp; new Structures_Graph_Node();
102722
102723$directedGraph-&gt;addNode(&amp;$nodeOne);
102724$directedGraph-&gt;addNode(&amp;$nodeTwo);
102725$directedGraph-&gt;addNode(&amp;$nodeThree);
102726    </pre></pre>
102727   and then setup the arcs:
102728   <pre class="listing"><pre>
102729$nodeOne-&gt;connectTo($nodeTwo);
102730$nodeOne-&gt;connectTo($nodeThree);
102731    </pre></pre>
102732   Note that arcs can only be created after the nodes have been inserted into the graph.</p></span>
102733 <span><a name="package.database.structures_graph.tutorial.nodesanddata"></a><h2 class="title">Associating Data</h2><p>Graphs are only useful as datastructures if they can hold data. Structure_Graph stores data in nodes. Each node contains a setter and a getter for its data.
102734   <pre class="listing"><pre>
102735$nodeOne-&gt;setData(&quot;Node One's Data is a String&quot;);
102736$nodeTwo-&gt;setData(1976);
102737$nodeThree-&gt;setData('Some other string');
102738
102739print(&quot;NodeTwo's Data is an integer: &quot; . $nodeTwo-&gt;getData());
102740    </pre></pre></p>
102741  <p>Structure_Graph nodes can also store metadata, alongside with the main data. Metadata differs from regular data just because it is stored under a key, making it possible to store more than one data reference per node. The metadata getter and setter need the key to perform the operation:
102742   <pre class="listing"><pre>
102743$nodeOne-&gt;setMetadata('example key', &quot;Node One's Sample Metadata&quot;);
102744print(&quot;Metadata stored under key 'example key' in node one: &quot; . $nodeOne-&gt;getMetadata('example key'));
102745$nodeOne-&gt;unsetMetadata('example key');
102746    </pre></pre></p></span>
102747 <span><a name="package.database.structures_graph.tutorial.querying"></a><h2 class="title">Querying a Graph</h2><p>Structures_Graph provides for basic querying of the graph:
102748   <pre class="listing"><pre>
102749// Nodes are able to calculate their indegree and outdegree
102750print(&quot;NodeOne's inDegree: &quot; . $nodeOne-&gt;inDegree());
102751print(&quot;NodeOne's outDegree: &quot; . $nodeOne-&gt;outDegree());
102752
102753// and naturally, nodes can report on their arcs
102754$arcs = $nodeOne-&gt;getNeighbours();
102755for ($i=0;$i&lt;sizeof($arcs);$i++) {
102756    print(&quot;NodeOne has an arc to &quot; . $arcs[$i]-&gt;getData());
102757}
102758    </pre></pre></p></span></div>
102759
102760
102761	<p class="notes" id="credit">
102762		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
102763	</p>
102764	</div></body>
102765</html>././@LongLink000        144 0003735 LStructures_Graph-1.0.4/docs/html/Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.htmlStructures_Graph-1.0.4/docs/html/Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html0000644000076600000240000000746211461440275033555 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
102766<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
102767  <html xmlns="http://www.w3.org/1999/xhtml">
102768		<head>
102769			<!-- template designed by Marco Von Ballmoos -->
102770			<title>Docs for page AcyclicTest.php</title>
102771			<link rel="stylesheet" href="../media/stylesheet.css" />
102772			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
102773		</head>
102774		<body>
102775			<div class="page-body">			
102776<h2 class="file-name">/Structures/Graph/Manipulator/AcyclicTest.php</h2>
102777
102778<a name="sec-description"></a>
102779<div class="info-box">
102780	<div class="info-box-title">Description</div>
102781	<div class="nav-bar">
102782					<span class="disabled">Description</span> |
102783							<a href="#sec-classes">Classes</a>
102784			|							<a href="#sec-includes">Includes</a>
102785												</div>
102786	<div class="info-box-body">	
102787		<!-- ========== Info from phpDoc block ========= -->
102788<p class="short-description">This file contains the definition of the Structures_Graph_Manipulator_AcyclicTest graph manipulator.</p>
102789	<ul class="tags">
102790				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a></li>
102791			</ul>
102792		
102793			</div>
102794</div>
102795		
102796	<a name="sec-classes"></a>	
102797	<div class="info-box">
102798		<div class="info-box-title">Classes</div>
102799		<div class="nav-bar">
102800			<a href="#sec-description">Description</a> |
102801			<span class="disabled">Classes</span>
102802			|							<a href="#sec-includes">Includes</a>
102803																		</div>
102804		<div class="info-box-body">	
102805			<table cellpadding="2" cellspacing="0" class="class-table">
102806				<tr>
102807					<th class="class-table-header">Class</th>
102808					<th class="class-table-header">Description</th>
102809				</tr>
102810								<tr>
102811					<td style="padding-right: 2em; vertical-align: top">
102812						<a href="../Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a>
102813					</td>
102814					<td>
102815											The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator  which tests whether a graph contains a cycle.
102816										</td>
102817				</tr>
102818							</table>
102819		</div>
102820	</div>
102821
102822	<a name="sec-includes"></a>	
102823	<div class="info-box">
102824		<div class="info-box-title">Includes</div>
102825		<div class="nav-bar">
102826			<a href="#sec-description">Description</a> |
102827							<a href="#sec-classes">Classes</a>
102828				|						<span class="disabled">Includes</span>
102829														</div>
102830		<div class="info-box-body">	
102831			<a name="_PEAR_php"><!-- --></a>
102832<div class="oddrow">
102833	
102834	<div>
102835		<span class="include-title">
102836			<span class="include-type">require_once</span>
102837			(<span class="include-name">'PEAR.php'</span>)
102838			(line <span class="line-number">35</span>)
102839		</span>
102840	</div>
102841
102842	<!-- ========== Info from phpDoc block ========= -->
102843	
102844</div>
102845<a name="_Structures/Graph_php"><!-- --></a>
102846<div class="evenrow">
102847	
102848	<div>
102849		<span class="include-title">
102850			<span class="include-type">require_once</span>
102851			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_php.html">'Structures/Graph.php'</a></span>)
102852			(line <span class="line-number">37</span>)
102853		</span>
102854	</div>
102855
102856	<!-- ========== Info from phpDoc block ========= -->
102857	
102858</div>
102859<a name="_Structures/Graph/Node_php"><!-- --></a>
102860<div class="oddrow">
102861	
102862	<div>
102863		<span class="include-title">
102864			<span class="include-type">require_once</span>
102865			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_Node_php.html">'Structures/Graph/Node.php'</a></span>)
102866			(line <span class="line-number">39</span>)
102867		</span>
102868	</div>
102869
102870	<!-- ========== Info from phpDoc block ========= -->
102871	
102872</div>
102873		</div>
102874	</div>
102875	
102876	
102877	
102878	
102879	<p class="notes" id="credit">
102880		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
102881	</p>
102882	</div></body>
102883</html>././@LongLink000        152 0003734 LStructures_Graph-1.0.4/docs/html/Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_php.htmlStructures_Graph-1.0.4/docs/html/Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_ph0000644000076600000240000001052511461440275033670 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
102884<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
102885  <html xmlns="http://www.w3.org/1999/xhtml">
102886		<head>
102887			<!-- template designed by Marco Von Ballmoos -->
102888			<title>Docs for page TopologicalSorter.php</title>
102889			<link rel="stylesheet" href="../media/stylesheet.css" />
102890			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
102891		</head>
102892		<body>
102893			<div class="page-body">			
102894<h2 class="file-name">/Structures/Graph/Manipulator/TopologicalSorter.php</h2>
102895
102896<a name="sec-description"></a>
102897<div class="info-box">
102898	<div class="info-box-title">Description</div>
102899	<div class="nav-bar">
102900					<span class="disabled">Description</span> |
102901							<a href="#sec-classes">Classes</a>
102902			|							<a href="#sec-includes">Includes</a>
102903												</div>
102904	<div class="info-box-body">	
102905		<!-- ========== Info from phpDoc block ========= -->
102906<p class="short-description">This file contains the definition of the Structures_Graph_Manipulator_TopologicalSorter class.</p>
102907	<ul class="tags">
102908				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a></li>
102909			</ul>
102910		
102911			</div>
102912</div>
102913		
102914	<a name="sec-classes"></a>	
102915	<div class="info-box">
102916		<div class="info-box-title">Classes</div>
102917		<div class="nav-bar">
102918			<a href="#sec-description">Description</a> |
102919			<span class="disabled">Classes</span>
102920			|							<a href="#sec-includes">Includes</a>
102921																		</div>
102922		<div class="info-box-body">	
102923			<table cellpadding="2" cellspacing="0" class="class-table">
102924				<tr>
102925					<th class="class-table-header">Class</th>
102926					<th class="class-table-header">Description</th>
102927				</tr>
102928								<tr>
102929					<td style="padding-right: 2em; vertical-align: top">
102930						<a href="../Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a>
102931					</td>
102932					<td>
102933											The Structures_Graph_Manipulator_TopologicalSorter is a manipulator  which is able to return the set of nodes in a graph, sorted by topological  order.
102934										</td>
102935				</tr>
102936							</table>
102937		</div>
102938	</div>
102939
102940	<a name="sec-includes"></a>	
102941	<div class="info-box">
102942		<div class="info-box-title">Includes</div>
102943		<div class="nav-bar">
102944			<a href="#sec-description">Description</a> |
102945							<a href="#sec-classes">Classes</a>
102946				|						<span class="disabled">Includes</span>
102947														</div>
102948		<div class="info-box-body">	
102949			<a name="_PEAR_php"><!-- --></a>
102950<div class="oddrow">
102951	
102952	<div>
102953		<span class="include-title">
102954			<span class="include-type">require_once</span>
102955			(<span class="include-name">'PEAR.php'</span>)
102956			(line <span class="line-number">35</span>)
102957		</span>
102958	</div>
102959
102960	<!-- ========== Info from phpDoc block ========= -->
102961	
102962</div>
102963<a name="_Structures/Graph_php"><!-- --></a>
102964<div class="evenrow">
102965	
102966	<div>
102967		<span class="include-title">
102968			<span class="include-type">require_once</span>
102969			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_php.html">'Structures/Graph.php'</a></span>)
102970			(line <span class="line-number">37</span>)
102971		</span>
102972	</div>
102973
102974	<!-- ========== Info from phpDoc block ========= -->
102975	
102976</div>
102977<a name="_Structures/Graph/Node_php"><!-- --></a>
102978<div class="oddrow">
102979	
102980	<div>
102981		<span class="include-title">
102982			<span class="include-type">require_once</span>
102983			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_Node_php.html">'Structures/Graph/Node.php'</a></span>)
102984			(line <span class="line-number">39</span>)
102985		</span>
102986	</div>
102987
102988	<!-- ========== Info from phpDoc block ========= -->
102989	
102990</div>
102991<a name="_Structures/Graph/Manipulator/AcyclicTest_php"><!-- --></a>
102992<div class="evenrow">
102993	
102994	<div>
102995		<span class="include-title">
102996			<span class="include-type">require_once</span>
102997			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html">'Structures/Graph/Manipulator/AcyclicTest.php'</a></span>)
102998			(line <span class="line-number">41</span>)
102999		</span>
103000	</div>
103001
103002	<!-- ========== Info from phpDoc block ========= -->
103003	
103004</div>
103005		</div>
103006	</div>
103007	
103008	
103009	
103010	
103011	<p class="notes" id="credit">
103012		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
103013	</p>
103014	</div></body>
103015</html>Structures_Graph-1.0.4/docs/html/Structures_Graph/_Structures_Graph_Node_php.html0000644000076600000240000000635611461440275027661 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
103016<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
103017  <html xmlns="http://www.w3.org/1999/xhtml">
103018		<head>
103019			<!-- template designed by Marco Von Ballmoos -->
103020			<title>Docs for page Node.php</title>
103021			<link rel="stylesheet" href="../media/stylesheet.css" />
103022			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
103023		</head>
103024		<body>
103025			<div class="page-body">			
103026<h2 class="file-name">/Structures/Graph/Node.php</h2>
103027
103028<a name="sec-description"></a>
103029<div class="info-box">
103030	<div class="info-box-title">Description</div>
103031	<div class="nav-bar">
103032					<span class="disabled">Description</span> |
103033							<a href="#sec-classes">Classes</a>
103034			|							<a href="#sec-includes">Includes</a>
103035												</div>
103036	<div class="info-box-body">	
103037		<!-- ========== Info from phpDoc block ========= -->
103038<p class="short-description">This file contains the definition of the Structures_Graph_Node class</p>
103039	<ul class="tags">
103040				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></li>
103041			</ul>
103042		
103043			</div>
103044</div>
103045		
103046	<a name="sec-classes"></a>	
103047	<div class="info-box">
103048		<div class="info-box-title">Classes</div>
103049		<div class="nav-bar">
103050			<a href="#sec-description">Description</a> |
103051			<span class="disabled">Classes</span>
103052			|							<a href="#sec-includes">Includes</a>
103053																		</div>
103054		<div class="info-box-body">	
103055			<table cellpadding="2" cellspacing="0" class="class-table">
103056				<tr>
103057					<th class="class-table-header">Class</th>
103058					<th class="class-table-header">Description</th>
103059				</tr>
103060								<tr>
103061					<td style="padding-right: 2em; vertical-align: top">
103062						<a href="../Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a>
103063					</td>
103064					<td>
103065											The Structures_Graph_Node class represents a Node that can be member of a  graph node set.
103066										</td>
103067				</tr>
103068							</table>
103069		</div>
103070	</div>
103071
103072	<a name="sec-includes"></a>	
103073	<div class="info-box">
103074		<div class="info-box-title">Includes</div>
103075		<div class="nav-bar">
103076			<a href="#sec-description">Description</a> |
103077							<a href="#sec-classes">Classes</a>
103078				|						<span class="disabled">Includes</span>
103079														</div>
103080		<div class="info-box-body">	
103081			<a name="_PEAR_php"><!-- --></a>
103082<div class="evenrow">
103083	
103084	<div>
103085		<span class="include-title">
103086			<span class="include-type">require_once</span>
103087			(<span class="include-name">'PEAR.php'</span>)
103088			(line <span class="line-number">35</span>)
103089		</span>
103090	</div>
103091
103092	<!-- ========== Info from phpDoc block ========= -->
103093	
103094</div>
103095<a name="_Structures/Graph_php"><!-- --></a>
103096<div class="oddrow">
103097	
103098	<div>
103099		<span class="include-title">
103100			<span class="include-type">require_once</span>
103101			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_php.html">'Structures/Graph.php'</a></span>)
103102			(line <span class="line-number">37</span>)
103103		</span>
103104	</div>
103105
103106	<!-- ========== Info from phpDoc block ========= -->
103107	
103108</div>
103109		</div>
103110	</div>
103111	
103112	
103113	
103114	
103115	<p class="notes" id="credit">
103116		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
103117	</p>
103118	</div></body>
103119</html>Structures_Graph-1.0.4/docs/html/Structures_Graph/_Structures_Graph_php.html0000644000076600000240000001020711461440275026702 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
103120<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
103121  <html xmlns="http://www.w3.org/1999/xhtml">
103122		<head>
103123			<!-- template designed by Marco Von Ballmoos -->
103124			<title>Docs for page Graph.php</title>
103125			<link rel="stylesheet" href="../media/stylesheet.css" />
103126			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
103127		</head>
103128		<body>
103129			<div class="page-body">			
103130<h2 class="file-name">/Structures/Graph.php</h2>
103131
103132<a name="sec-description"></a>
103133<div class="info-box">
103134	<div class="info-box-title">Description</div>
103135	<div class="nav-bar">
103136					<span class="disabled">Description</span> |
103137							<a href="#sec-classes">Classes</a>
103138			|							<a href="#sec-includes">Includes</a>
103139			|							<a href="#sec-constants">Constants</a>
103140										</div>
103141	<div class="info-box-body">	
103142		<!-- ========== Info from phpDoc block ========= -->
103143<p class="short-description">The Graph.php file contains the definition of the Structures_Graph class</p>
103144	<ul class="tags">
103145				<li><span class="field">see:</span> <a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a></li>
103146			</ul>
103147		
103148			</div>
103149</div>
103150		
103151	<a name="sec-classes"></a>	
103152	<div class="info-box">
103153		<div class="info-box-title">Classes</div>
103154		<div class="nav-bar">
103155			<a href="#sec-description">Description</a> |
103156			<span class="disabled">Classes</span>
103157			|							<a href="#sec-includes">Includes</a>
103158				|										<a href="#sec-constants">Constants</a>
103159															</div>
103160		<div class="info-box-body">	
103161			<table cellpadding="2" cellspacing="0" class="class-table">
103162				<tr>
103163					<th class="class-table-header">Class</th>
103164					<th class="class-table-header">Description</th>
103165				</tr>
103166								<tr>
103167					<td style="padding-right: 2em; vertical-align: top">
103168						<a href="../Structures_Graph/Structures_Graph.html">Structures_Graph</a>
103169					</td>
103170					<td>
103171											The Structures_Graph class represents a graph data structure.
103172										</td>
103173				</tr>
103174							</table>
103175		</div>
103176	</div>
103177
103178	<a name="sec-includes"></a>	
103179	<div class="info-box">
103180		<div class="info-box-title">Includes</div>
103181		<div class="nav-bar">
103182			<a href="#sec-description">Description</a> |
103183							<a href="#sec-classes">Classes</a>
103184				|						<span class="disabled">Includes</span>
103185			|							<a href="#sec-constants">Constants</a>
103186															</div>
103187		<div class="info-box-body">	
103188			<a name="_Structures/Graph/Node_php"><!-- --></a>
103189<div class="oddrow">
103190	
103191	<div>
103192		<span class="include-title">
103193			<span class="include-type">require_once</span>
103194			(<span class="include-name"><a href="../Structures_Graph/_Structures_Graph_Node_php.html">'Structures/Graph/Node.php'</a></span>)
103195			(line <span class="line-number">37</span>)
103196		</span>
103197	</div>
103198
103199	<!-- ========== Info from phpDoc block ========= -->
103200<p class="short-description">Graph Node</p>
103201	
103202</div>
103203<a name="_PEAR_php"><!-- --></a>
103204<div class="evenrow">
103205	
103206	<div>
103207		<span class="include-title">
103208			<span class="include-type">require_once</span>
103209			(<span class="include-name">'PEAR.php'</span>)
103210			(line <span class="line-number">35</span>)
103211		</span>
103212	</div>
103213
103214	<!-- ========== Info from phpDoc block ========= -->
103215<p class="short-description">PEAR base classes</p>
103216	
103217</div>
103218		</div>
103219	</div>
103220	
103221	<a name="sec-constants"></a>	
103222	<div class="info-box">
103223		<div class="info-box-title">Constants</div>
103224		<div class="nav-bar">
103225			<a href="#sec-description">Description</a> |
103226							<a href="#sec-classes">Classes</a>
103227				|										<a href="#sec-includes">Includes</a>
103228				|						<span class="disabled">Constants</span>
103229											</div>
103230		<div class="info-box-body">	
103231			<a name="defineSTRUCTURES_GRAPH_ERROR_GENERIC"><!-- --></a>
103232<div class="oddrow">
103233	
103234	<div>
103235		<span class="const-title">
103236			<span class="const-name">STRUCTURES_GRAPH_ERROR_GENERIC</span> = 100
103237			(line <span class="line-number">40</span>)
103238		</span>
103239	</div>
103240	
103241	<!-- ========== Info from phpDoc block ========= -->
103242	
103243		
103244</div>
103245		</div>
103246	</div>
103247	
103248	
103249	
103250	<p class="notes" id="credit">
103251		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
103252	</p>
103253	</div></body>
103254</html>Structures_Graph-1.0.4/docs/html/classtrees_Structures_Graph.html0000644000076600000240000000253011461440275024620 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
103255<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
103256  <html xmlns="http://www.w3.org/1999/xhtml">
103257		<head>
103258			<!-- template designed by Marco Von Ballmoos -->
103259			<title></title>
103260			<link rel="stylesheet" href="media/stylesheet.css" />
103261			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
103262		</head>
103263		<body>
103264						
103265<!-- Start of Class Data -->
103266<H2>
103267	
103268</H2>
103269<h2>Root class Structures_Graph</h2>
103270<ul>
103271<li><a href="Structures_Graph/Structures_Graph.html">Structures_Graph</a></li></ul>
103272
103273<h2>Root class Structures_Graph_Manipulator_AcyclicTest</h2>
103274<ul>
103275<li><a href="Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a></li></ul>
103276
103277<h2>Root class Structures_Graph_Manipulator_TopologicalSorter</h2>
103278<ul>
103279<li><a href="Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a></li></ul>
103280
103281<h2>Root class Structures_Graph_Node</h2>
103282<ul>
103283<li><a href="Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a></li></ul>
103284
103285	<p class="notes" id="credit">
103286		Documentation generated on Fri, 30 Jan 2004 16:37:28 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
103287	</p>
103288	</body>
103289</html>Structures_Graph-1.0.4/docs/html/elementindex.html0000644000076600000240000003640711461440275021557 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
103290<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
103291  <html xmlns="http://www.w3.org/1999/xhtml">
103292		<head>
103293			<!-- template designed by Marco Von Ballmoos -->
103294			<title></title>
103295			<link rel="stylesheet" href="media/stylesheet.css" />
103296			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
103297		</head>
103298		<body>
103299						<a name="top"></a>
103300<h2>Full index</h2>
103301<h3>Package indexes</h3>
103302<ul>
103303	<li><a href="elementindex_Structures_Graph.html">Structures_Graph</a></li>
103304</ul>
103305<br />
103306<div class="index-letter-menu">
103307	<a class="index-letter" href="elementindex.html#a">a</a>
103308	<a class="index-letter" href="elementindex.html#c">c</a>
103309	<a class="index-letter" href="elementindex.html#g">g</a>
103310	<a class="index-letter" href="elementindex.html#i">i</a>
103311	<a class="index-letter" href="elementindex.html#m">m</a>
103312	<a class="index-letter" href="elementindex.html#n">n</a>
103313	<a class="index-letter" href="elementindex.html#o">o</a>
103314	<a class="index-letter" href="elementindex.html#r">r</a>
103315	<a class="index-letter" href="elementindex.html#s">s</a>
103316	<a class="index-letter" href="elementindex.html#t">t</a>
103317	<a class="index-letter" href="elementindex.html#u">u</a>
103318</div>
103319
103320	<a name="a"></a>
103321	<div class="index-letter-section">
103322		<div style="float: left" class="index-letter-title">a</div>
103323		<div style="float: right"><a href="#top">top</a></div>
103324		<div style="clear: both"></div>
103325	</div>
103326	<dl>
103327			<dt class="field">
103328						<span class="method-title">addNode</span>
103329					</dt>
103330		<dd class="index-item-body">
103331			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodaddNode">Structures_Graph::addNode()</a> in Graph.php</div>
103332							<div class="index-item-description">Add a Node to the Graph</div>
103333					</dd>
103334			<dt class="field">
103335						<span class="include-title">AcyclicTest.php</span>
103336					</dt>
103337		<dd class="index-item-body">
103338			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html">AcyclicTest.php</a> in AcyclicTest.php</div>
103339					</dd>
103340		</dl>
103341	<a name="c"></a>
103342	<div class="index-letter-section">
103343		<div style="float: left" class="index-letter-title">c</div>
103344		<div style="float: right"><a href="#top">top</a></div>
103345		<div style="clear: both"></div>
103346	</div>
103347	<dl>
103348			<dt class="field">
103349						<span class="method-title">connectsTo</span>
103350					</dt>
103351		<dd class="index-item-body">
103352			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodconnectsTo">Structures_Graph_Node::connectsTo()</a> in Node.php</div>
103353							<div class="index-item-description">Test wether this node has an arc to the target node</div>
103354					</dd>
103355			<dt class="field">
103356						<span class="method-title">connectTo</span>
103357					</dt>
103358		<dd class="index-item-body">
103359			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodconnectTo">Structures_Graph_Node::connectTo()</a> in Node.php</div>
103360							<div class="index-item-description">Connect this node to another one.</div>
103361					</dd>
103362		</dl>
103363	<a name="g"></a>
103364	<div class="index-letter-section">
103365		<div style="float: left" class="index-letter-title">g</div>
103366		<div style="float: right"><a href="#top">top</a></div>
103367		<div style="clear: both"></div>
103368	</div>
103369	<dl>
103370			<dt class="field">
103371						<span class="method-title">getData</span>
103372					</dt>
103373		<dd class="index-item-body">
103374			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetData">Structures_Graph_Node::getData()</a> in Node.php</div>
103375							<div class="index-item-description">Node data getter.</div>
103376					</dd>
103377			<dt class="field">
103378						<span class="method-title">getGraph</span>
103379					</dt>
103380		<dd class="index-item-body">
103381			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetGraph">Structures_Graph_Node::getGraph()</a> in Node.php</div>
103382							<div class="index-item-description">Node graph getter</div>
103383					</dd>
103384			<dt class="field">
103385						<span class="method-title">getMetadata</span>
103386					</dt>
103387		<dd class="index-item-body">
103388			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetMetadata">Structures_Graph_Node::getMetadata()</a> in Node.php</div>
103389							<div class="index-item-description">Node metadata getter</div>
103390					</dd>
103391			<dt class="field">
103392						<span class="method-title">getNeighbours</span>
103393					</dt>
103394		<dd class="index-item-body">
103395			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetNeighbours">Structures_Graph_Node::getNeighbours()</a> in Node.php</div>
103396							<div class="index-item-description">Return nodes connected to this one.</div>
103397					</dd>
103398			<dt class="field">
103399						<span class="method-title">getNodes</span>
103400					</dt>
103401		<dd class="index-item-body">
103402			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodgetNodes">Structures_Graph::getNodes()</a> in Graph.php</div>
103403							<div class="index-item-description">Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted.</div>
103404					</dd>
103405			<dt class="field">
103406						<span class="include-title">Graph.php</span>
103407					</dt>
103408		<dd class="index-item-body">
103409			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_php.html">Graph.php</a> in Graph.php</div>
103410					</dd>
103411		</dl>
103412	<a name="i"></a>
103413	<div class="index-letter-section">
103414		<div style="float: left" class="index-letter-title">i</div>
103415		<div style="float: right"><a href="#top">top</a></div>
103416		<div style="clear: both"></div>
103417	</div>
103418	<dl>
103419			<dt class="field">
103420						<span class="method-title">inDegree</span>
103421					</dt>
103422		<dd class="index-item-body">
103423			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodinDegree">Structures_Graph_Node::inDegree()</a> in Node.php</div>
103424							<div class="index-item-description">Calculate the in degree of the node.</div>
103425					</dd>
103426			<dt class="field">
103427						<span class="method-title">isAcyclic</span>
103428					</dt>
103429		<dd class="index-item-body">
103430			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html#methodisAcyclic">Structures_Graph_Manipulator_AcyclicTest::isAcyclic()</a> in AcyclicTest.php</div>
103431							<div class="index-item-description">isAcyclic returns true if a graph contains no cycles, false otherwise.</div>
103432					</dd>
103433			<dt class="field">
103434						<span class="method-title">isDirected</span>
103435					</dt>
103436		<dd class="index-item-body">
103437			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodisDirected">Structures_Graph::isDirected()</a> in Graph.php</div>
103438							<div class="index-item-description">Return true if a graph is directed</div>
103439					</dd>
103440		</dl>
103441	<a name="m"></a>
103442	<div class="index-letter-section">
103443		<div style="float: left" class="index-letter-title">m</div>
103444		<div style="float: right"><a href="#top">top</a></div>
103445		<div style="clear: both"></div>
103446	</div>
103447	<dl>
103448			<dt class="field">
103449						<span class="method-title">metadataKeyExists</span>
103450					</dt>
103451		<dd class="index-item-body">
103452			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodmetadataKeyExists">Structures_Graph_Node::metadataKeyExists()</a> in Node.php</div>
103453							<div class="index-item-description">Test for existence of metadata under a given key.</div>
103454					</dd>
103455		</dl>
103456	<a name="n"></a>
103457	<div class="index-letter-section">
103458		<div style="float: left" class="index-letter-title">n</div>
103459		<div style="float: right"><a href="#top">top</a></div>
103460		<div style="clear: both"></div>
103461	</div>
103462	<dl>
103463			<dt class="field">
103464						<span class="include-title">Node.php</span>
103465					</dt>
103466		<dd class="index-item-body">
103467			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Node_php.html">Node.php</a> in Node.php</div>
103468					</dd>
103469		</dl>
103470	<a name="o"></a>
103471	<div class="index-letter-section">
103472		<div style="float: left" class="index-letter-title">o</div>
103473		<div style="float: right"><a href="#top">top</a></div>
103474		<div style="clear: both"></div>
103475	</div>
103476	<dl>
103477			<dt class="field">
103478						<span class="method-title">outDegree</span>
103479					</dt>
103480		<dd class="index-item-body">
103481			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodoutDegree">Structures_Graph_Node::outDegree()</a> in Node.php</div>
103482							<div class="index-item-description">Calculate the out degree of the node.</div>
103483					</dd>
103484		</dl>
103485	<a name="r"></a>
103486	<div class="index-letter-section">
103487		<div style="float: left" class="index-letter-title">r</div>
103488		<div style="float: right"><a href="#top">top</a></div>
103489		<div style="clear: both"></div>
103490	</div>
103491	<dl>
103492			<dt class="field">
103493						<span class="method-title">removeNode</span>
103494					</dt>
103495		<dd class="index-item-body">
103496			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodremoveNode">Structures_Graph::removeNode()</a> in Graph.php</div>
103497							<div class="index-item-description">Remove a Node from the Graph</div>
103498					</dd>
103499		</dl>
103500	<a name="s"></a>
103501	<div class="index-letter-section">
103502		<div style="float: left" class="index-letter-title">s</div>
103503		<div style="float: right"><a href="#top">top</a></div>
103504		<div style="clear: both"></div>
103505	</div>
103506	<dl>
103507			<dt class="field">
103508						<span class="method-title">setData</span>
103509					</dt>
103510		<dd class="index-item-body">
103511			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetData">Structures_Graph_Node::setData()</a> in Node.php</div>
103512							<div class="index-item-description">Node data setter</div>
103513					</dd>
103514			<dt class="field">
103515						<span class="method-title">setGraph</span>
103516					</dt>
103517		<dd class="index-item-body">
103518			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetGraph">Structures_Graph_Node::setGraph()</a> in Node.php</div>
103519							<div class="index-item-description">Node graph setter. This method should not be called directly. Use Graph::addNode instead.</div>
103520					</dd>
103521			<dt class="field">
103522						<span class="method-title">setMetadata</span>
103523					</dt>
103524		<dd class="index-item-body">
103525			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetMetadata">Structures_Graph_Node::setMetadata()</a> in Node.php</div>
103526							<div class="index-item-description">Node metadata setter</div>
103527					</dd>
103528			<dt class="field">
103529						<span class="method-title">sort</span>
103530					</dt>
103531		<dd class="index-item-body">
103532			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html#methodsort">Structures_Graph_Manipulator_TopologicalSorter::sort()</a> in TopologicalSorter.php</div>
103533							<div class="index-item-description">sort returns the graph's nodes, sorted by topological order.</div>
103534					</dd>
103535			<dt class="field">
103536						Structures_Graph
103537					</dt>
103538		<dd class="index-item-body">
103539			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html">Structures_Graph</a> in Graph.php</div>
103540							<div class="index-item-description">The Structures_Graph class represents a graph data structure.</div>
103541					</dd>
103542			<dt class="field">
103543						<span class="method-title">Structures_Graph</span>
103544					</dt>
103545		<dd class="index-item-body">
103546			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodStructures_Graph">Structures_Graph::Structures_Graph()</a> in Graph.php</div>
103547							<div class="index-item-description">Constructor</div>
103548					</dd>
103549			<dt class="field">
103550						<span class="const-title">STRUCTURES_GRAPH_ERROR_GENERIC</span>
103551					</dt>
103552		<dd class="index-item-body">
103553			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_php.html#defineSTRUCTURES_GRAPH_ERROR_GENERIC">STRUCTURES_GRAPH_ERROR_GENERIC</a> in Graph.php</div>
103554					</dd>
103555			<dt class="field">
103556						Structures_Graph_Manipulator_AcyclicTest
103557					</dt>
103558		<dd class="index-item-body">
103559			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a> in AcyclicTest.php</div>
103560							<div class="index-item-description">The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator  which tests whether a graph contains a cycle.</div>
103561					</dd>
103562			<dt class="field">
103563						Structures_Graph_Manipulator_TopologicalSorter
103564					</dt>
103565		<dd class="index-item-body">
103566			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a> in TopologicalSorter.php</div>
103567							<div class="index-item-description">The Structures_Graph_Manipulator_TopologicalSorter is a manipulator  which is able to return the set of nodes in a graph, sorted by topological  order.</div>
103568					</dd>
103569			<dt class="field">
103570						<span class="method-title">Structures_Graph_Node</span>
103571					</dt>
103572		<dd class="index-item-body">
103573			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodStructures_Graph_Node">Structures_Graph_Node::Structures_Graph_Node()</a> in Node.php</div>
103574							<div class="index-item-description">Constructor</div>
103575					</dd>
103576			<dt class="field">
103577						Structures_Graph_Node
103578					</dt>
103579		<dd class="index-item-body">
103580			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a> in Node.php</div>
103581							<div class="index-item-description">The Structures_Graph_Node class represents a Node that can be member of a  graph node set.</div>
103582					</dd>
103583		</dl>
103584	<a name="t"></a>
103585	<div class="index-letter-section">
103586		<div style="float: left" class="index-letter-title">t</div>
103587		<div style="float: right"><a href="#top">top</a></div>
103588		<div style="clear: both"></div>
103589	</div>
103590	<dl>
103591			<dt class="field">
103592						<span class="include-title">TopologicalSorter.php</span>
103593					</dt>
103594		<dd class="index-item-body">
103595			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_php.html">TopologicalSorter.php</a> in TopologicalSorter.php</div>
103596					</dd>
103597		</dl>
103598	<a name="u"></a>
103599	<div class="index-letter-section">
103600		<div style="float: left" class="index-letter-title">u</div>
103601		<div style="float: right"><a href="#top">top</a></div>
103602		<div style="clear: both"></div>
103603	</div>
103604	<dl>
103605			<dt class="field">
103606						<span class="method-title">unsetMetadata</span>
103607					</dt>
103608		<dd class="index-item-body">
103609			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodunsetMetadata">Structures_Graph_Node::unsetMetadata()</a> in Node.php</div>
103610							<div class="index-item-description">Delete metadata by key</div>
103611					</dd>
103612		</dl>
103613
103614<div class="index-letter-menu">
103615	<a class="index-letter" href="elementindex.html#a">a</a>
103616	<a class="index-letter" href="elementindex.html#c">c</a>
103617	<a class="index-letter" href="elementindex.html#g">g</a>
103618	<a class="index-letter" href="elementindex.html#i">i</a>
103619	<a class="index-letter" href="elementindex.html#m">m</a>
103620	<a class="index-letter" href="elementindex.html#n">n</a>
103621	<a class="index-letter" href="elementindex.html#o">o</a>
103622	<a class="index-letter" href="elementindex.html#r">r</a>
103623	<a class="index-letter" href="elementindex.html#s">s</a>
103624	<a class="index-letter" href="elementindex.html#t">t</a>
103625	<a class="index-letter" href="elementindex.html#u">u</a>
103626</div>	</body>
103627</html>Structures_Graph-1.0.4/docs/html/elementindex_Structures_Graph.html0000644000076600000240000003712011461440275025134 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
103628<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
103629  <html xmlns="http://www.w3.org/1999/xhtml">
103630		<head>
103631			<!-- template designed by Marco Von Ballmoos -->
103632			<title></title>
103633			<link rel="stylesheet" href="media/stylesheet.css" />
103634			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
103635		</head>
103636		<body>
103637						<a name="top"></a>
103638<h2>[Structures_Graph] element index</h2>
103639<a href="elementindex.html">All elements</a>
103640<br />
103641<div class="index-letter-menu">
103642	<a class="index-letter" href="elementindex_Structures_Graph.html#a">a</a>
103643	<a class="index-letter" href="elementindex_Structures_Graph.html#c">c</a>
103644	<a class="index-letter" href="elementindex_Structures_Graph.html#g">g</a>
103645	<a class="index-letter" href="elementindex_Structures_Graph.html#i">i</a>
103646	<a class="index-letter" href="elementindex_Structures_Graph.html#m">m</a>
103647	<a class="index-letter" href="elementindex_Structures_Graph.html#n">n</a>
103648	<a class="index-letter" href="elementindex_Structures_Graph.html#o">o</a>
103649	<a class="index-letter" href="elementindex_Structures_Graph.html#r">r</a>
103650	<a class="index-letter" href="elementindex_Structures_Graph.html#s">s</a>
103651	<a class="index-letter" href="elementindex_Structures_Graph.html#t">t</a>
103652	<a class="index-letter" href="elementindex_Structures_Graph.html#u">u</a>
103653</div>
103654
103655	<a name="a"></a>
103656	<div class="index-letter-section">
103657		<div style="float: left" class="index-letter-title">a</div>
103658		<div style="float: right"><a href="#top">top</a></div>
103659		<div style="clear: both"></div>
103660	</div>
103661	<dl>
103662			<dt class="field">
103663						<span class="method-title">addNode</span>
103664					</dt>
103665		<dd class="index-item-body">
103666			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodaddNode">Structures_Graph::addNode()</a> in Graph.php</div>
103667							<div class="index-item-description">Add a Node to the Graph</div>
103668					</dd>
103669			<dt class="field">
103670						<span class="include-title">AcyclicTest.php</span>
103671					</dt>
103672		<dd class="index-item-body">
103673			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html">AcyclicTest.php</a> in AcyclicTest.php</div>
103674					</dd>
103675		</dl>
103676	<a name="c"></a>
103677	<div class="index-letter-section">
103678		<div style="float: left" class="index-letter-title">c</div>
103679		<div style="float: right"><a href="#top">top</a></div>
103680		<div style="clear: both"></div>
103681	</div>
103682	<dl>
103683			<dt class="field">
103684						<span class="method-title">connectsTo</span>
103685					</dt>
103686		<dd class="index-item-body">
103687			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodconnectsTo">Structures_Graph_Node::connectsTo()</a> in Node.php</div>
103688							<div class="index-item-description">Test wether this node has an arc to the target node</div>
103689					</dd>
103690			<dt class="field">
103691						<span class="method-title">connectTo</span>
103692					</dt>
103693		<dd class="index-item-body">
103694			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodconnectTo">Structures_Graph_Node::connectTo()</a> in Node.php</div>
103695							<div class="index-item-description">Connect this node to another one.</div>
103696					</dd>
103697		</dl>
103698	<a name="g"></a>
103699	<div class="index-letter-section">
103700		<div style="float: left" class="index-letter-title">g</div>
103701		<div style="float: right"><a href="#top">top</a></div>
103702		<div style="clear: both"></div>
103703	</div>
103704	<dl>
103705			<dt class="field">
103706						<span class="method-title">getData</span>
103707					</dt>
103708		<dd class="index-item-body">
103709			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetData">Structures_Graph_Node::getData()</a> in Node.php</div>
103710							<div class="index-item-description">Node data getter.</div>
103711					</dd>
103712			<dt class="field">
103713						<span class="method-title">getGraph</span>
103714					</dt>
103715		<dd class="index-item-body">
103716			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetGraph">Structures_Graph_Node::getGraph()</a> in Node.php</div>
103717							<div class="index-item-description">Node graph getter</div>
103718					</dd>
103719			<dt class="field">
103720						<span class="method-title">getMetadata</span>
103721					</dt>
103722		<dd class="index-item-body">
103723			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetMetadata">Structures_Graph_Node::getMetadata()</a> in Node.php</div>
103724							<div class="index-item-description">Node metadata getter</div>
103725					</dd>
103726			<dt class="field">
103727						<span class="method-title">getNeighbours</span>
103728					</dt>
103729		<dd class="index-item-body">
103730			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodgetNeighbours">Structures_Graph_Node::getNeighbours()</a> in Node.php</div>
103731							<div class="index-item-description">Return nodes connected to this one.</div>
103732					</dd>
103733			<dt class="field">
103734						<span class="method-title">getNodes</span>
103735					</dt>
103736		<dd class="index-item-body">
103737			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodgetNodes">Structures_Graph::getNodes()</a> in Graph.php</div>
103738							<div class="index-item-description">Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted.</div>
103739					</dd>
103740			<dt class="field">
103741						<span class="include-title">Graph.php</span>
103742					</dt>
103743		<dd class="index-item-body">
103744			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_php.html">Graph.php</a> in Graph.php</div>
103745					</dd>
103746		</dl>
103747	<a name="i"></a>
103748	<div class="index-letter-section">
103749		<div style="float: left" class="index-letter-title">i</div>
103750		<div style="float: right"><a href="#top">top</a></div>
103751		<div style="clear: both"></div>
103752	</div>
103753	<dl>
103754			<dt class="field">
103755						<span class="method-title">inDegree</span>
103756					</dt>
103757		<dd class="index-item-body">
103758			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodinDegree">Structures_Graph_Node::inDegree()</a> in Node.php</div>
103759							<div class="index-item-description">Calculate the in degree of the node.</div>
103760					</dd>
103761			<dt class="field">
103762						<span class="method-title">isAcyclic</span>
103763					</dt>
103764		<dd class="index-item-body">
103765			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html#methodisAcyclic">Structures_Graph_Manipulator_AcyclicTest::isAcyclic()</a> in AcyclicTest.php</div>
103766							<div class="index-item-description">isAcyclic returns true if a graph contains no cycles, false otherwise.</div>
103767					</dd>
103768			<dt class="field">
103769						<span class="method-title">isDirected</span>
103770					</dt>
103771		<dd class="index-item-body">
103772			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodisDirected">Structures_Graph::isDirected()</a> in Graph.php</div>
103773							<div class="index-item-description">Return true if a graph is directed</div>
103774					</dd>
103775		</dl>
103776	<a name="m"></a>
103777	<div class="index-letter-section">
103778		<div style="float: left" class="index-letter-title">m</div>
103779		<div style="float: right"><a href="#top">top</a></div>
103780		<div style="clear: both"></div>
103781	</div>
103782	<dl>
103783			<dt class="field">
103784						<span class="method-title">metadataKeyExists</span>
103785					</dt>
103786		<dd class="index-item-body">
103787			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodmetadataKeyExists">Structures_Graph_Node::metadataKeyExists()</a> in Node.php</div>
103788							<div class="index-item-description">Test for existence of metadata under a given key.</div>
103789					</dd>
103790		</dl>
103791	<a name="n"></a>
103792	<div class="index-letter-section">
103793		<div style="float: left" class="index-letter-title">n</div>
103794		<div style="float: right"><a href="#top">top</a></div>
103795		<div style="clear: both"></div>
103796	</div>
103797	<dl>
103798			<dt class="field">
103799						<span class="include-title">Node.php</span>
103800					</dt>
103801		<dd class="index-item-body">
103802			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Node_php.html">Node.php</a> in Node.php</div>
103803					</dd>
103804		</dl>
103805	<a name="o"></a>
103806	<div class="index-letter-section">
103807		<div style="float: left" class="index-letter-title">o</div>
103808		<div style="float: right"><a href="#top">top</a></div>
103809		<div style="clear: both"></div>
103810	</div>
103811	<dl>
103812			<dt class="field">
103813						<span class="method-title">outDegree</span>
103814					</dt>
103815		<dd class="index-item-body">
103816			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodoutDegree">Structures_Graph_Node::outDegree()</a> in Node.php</div>
103817							<div class="index-item-description">Calculate the out degree of the node.</div>
103818					</dd>
103819		</dl>
103820	<a name="r"></a>
103821	<div class="index-letter-section">
103822		<div style="float: left" class="index-letter-title">r</div>
103823		<div style="float: right"><a href="#top">top</a></div>
103824		<div style="clear: both"></div>
103825	</div>
103826	<dl>
103827			<dt class="field">
103828						<span class="method-title">removeNode</span>
103829					</dt>
103830		<dd class="index-item-body">
103831			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodremoveNode">Structures_Graph::removeNode()</a> in Graph.php</div>
103832							<div class="index-item-description">Remove a Node from the Graph</div>
103833					</dd>
103834		</dl>
103835	<a name="s"></a>
103836	<div class="index-letter-section">
103837		<div style="float: left" class="index-letter-title">s</div>
103838		<div style="float: right"><a href="#top">top</a></div>
103839		<div style="clear: both"></div>
103840	</div>
103841	<dl>
103842			<dt class="field">
103843						<span class="method-title">setData</span>
103844					</dt>
103845		<dd class="index-item-body">
103846			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetData">Structures_Graph_Node::setData()</a> in Node.php</div>
103847							<div class="index-item-description">Node data setter</div>
103848					</dd>
103849			<dt class="field">
103850						<span class="method-title">setGraph</span>
103851					</dt>
103852		<dd class="index-item-body">
103853			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetGraph">Structures_Graph_Node::setGraph()</a> in Node.php</div>
103854							<div class="index-item-description">Node graph setter. This method should not be called directly. Use Graph::addNode instead.</div>
103855					</dd>
103856			<dt class="field">
103857						<span class="method-title">setMetadata</span>
103858					</dt>
103859		<dd class="index-item-body">
103860			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodsetMetadata">Structures_Graph_Node::setMetadata()</a> in Node.php</div>
103861							<div class="index-item-description">Node metadata setter</div>
103862					</dd>
103863			<dt class="field">
103864						<span class="method-title">sort</span>
103865					</dt>
103866		<dd class="index-item-body">
103867			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html#methodsort">Structures_Graph_Manipulator_TopologicalSorter::sort()</a> in TopologicalSorter.php</div>
103868							<div class="index-item-description">sort returns the graph's nodes, sorted by topological order.</div>
103869					</dd>
103870			<dt class="field">
103871						Structures_Graph
103872					</dt>
103873		<dd class="index-item-body">
103874			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html">Structures_Graph</a> in Graph.php</div>
103875							<div class="index-item-description">The Structures_Graph class represents a graph data structure.</div>
103876					</dd>
103877			<dt class="field">
103878						<span class="method-title">Structures_Graph</span>
103879					</dt>
103880		<dd class="index-item-body">
103881			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph.html#methodStructures_Graph">Structures_Graph::Structures_Graph()</a> in Graph.php</div>
103882							<div class="index-item-description">Constructor</div>
103883					</dd>
103884			<dt class="field">
103885						<span class="const-title">STRUCTURES_GRAPH_ERROR_GENERIC</span>
103886					</dt>
103887		<dd class="index-item-body">
103888			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_php.html#defineSTRUCTURES_GRAPH_ERROR_GENERIC">STRUCTURES_GRAPH_ERROR_GENERIC</a> in Graph.php</div>
103889					</dd>
103890			<dt class="field">
103891						Structures_Graph_Manipulator_AcyclicTest
103892					</dt>
103893		<dd class="index-item-body">
103894			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html">Structures_Graph_Manipulator_AcyclicTest</a> in AcyclicTest.php</div>
103895							<div class="index-item-description">The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator  which tests whether a graph contains a cycle.</div>
103896					</dd>
103897			<dt class="field">
103898						Structures_Graph_Manipulator_TopologicalSorter
103899					</dt>
103900		<dd class="index-item-body">
103901			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html">Structures_Graph_Manipulator_TopologicalSorter</a> in TopologicalSorter.php</div>
103902							<div class="index-item-description">The Structures_Graph_Manipulator_TopologicalSorter is a manipulator  which is able to return the set of nodes in a graph, sorted by topological  order.</div>
103903					</dd>
103904			<dt class="field">
103905						<span class="method-title">Structures_Graph_Node</span>
103906					</dt>
103907		<dd class="index-item-body">
103908			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodStructures_Graph_Node">Structures_Graph_Node::Structures_Graph_Node()</a> in Node.php</div>
103909							<div class="index-item-description">Constructor</div>
103910					</dd>
103911			<dt class="field">
103912						Structures_Graph_Node
103913					</dt>
103914		<dd class="index-item-body">
103915			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html">Structures_Graph_Node</a> in Node.php</div>
103916							<div class="index-item-description">The Structures_Graph_Node class represents a Node that can be member of a  graph node set.</div>
103917					</dd>
103918		</dl>
103919	<a name="t"></a>
103920	<div class="index-letter-section">
103921		<div style="float: left" class="index-letter-title">t</div>
103922		<div style="float: right"><a href="#top">top</a></div>
103923		<div style="clear: both"></div>
103924	</div>
103925	<dl>
103926			<dt class="field">
103927						<span class="include-title">TopologicalSorter.php</span>
103928					</dt>
103929		<dd class="index-item-body">
103930			<div class="index-item-details"><a href="Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_php.html">TopologicalSorter.php</a> in TopologicalSorter.php</div>
103931					</dd>
103932		</dl>
103933	<a name="u"></a>
103934	<div class="index-letter-section">
103935		<div style="float: left" class="index-letter-title">u</div>
103936		<div style="float: right"><a href="#top">top</a></div>
103937		<div style="clear: both"></div>
103938	</div>
103939	<dl>
103940			<dt class="field">
103941						<span class="method-title">unsetMetadata</span>
103942					</dt>
103943		<dd class="index-item-body">
103944			<div class="index-item-details"><a href="Structures_Graph/Structures_Graph_Node.html#methodunsetMetadata">Structures_Graph_Node::unsetMetadata()</a> in Node.php</div>
103945							<div class="index-item-description">Delete metadata by key</div>
103946					</dd>
103947		</dl>
103948
103949<div class="index-letter-menu">
103950	<a class="index-letter" href="elementindex_Structures_Graph.html#a">a</a>
103951	<a class="index-letter" href="elementindex_Structures_Graph.html#c">c</a>
103952	<a class="index-letter" href="elementindex_Structures_Graph.html#g">g</a>
103953	<a class="index-letter" href="elementindex_Structures_Graph.html#i">i</a>
103954	<a class="index-letter" href="elementindex_Structures_Graph.html#m">m</a>
103955	<a class="index-letter" href="elementindex_Structures_Graph.html#n">n</a>
103956	<a class="index-letter" href="elementindex_Structures_Graph.html#o">o</a>
103957	<a class="index-letter" href="elementindex_Structures_Graph.html#r">r</a>
103958	<a class="index-letter" href="elementindex_Structures_Graph.html#s">s</a>
103959	<a class="index-letter" href="elementindex_Structures_Graph.html#t">t</a>
103960	<a class="index-letter" href="elementindex_Structures_Graph.html#u">u</a>
103961</div>	</body>
103962</html>Structures_Graph-1.0.4/docs/html/errors.html0000644000076600000240000000132511461440275020401 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
103963<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
103964  <html xmlns="http://www.w3.org/1999/xhtml">
103965		<head>
103966			<!-- template designed by Marco Von Ballmoos -->
103967			<title>phpDocumentor Parser Errors and Warnings</title>
103968			<link rel="stylesheet" href="media/stylesheet.css" />
103969			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
103970		</head>
103971		<body>
103972						<a href="#Post-parsing">Post-parsing</a><br>
103973	<p class="notes" id="credit">
103974		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
103975	</p>
103976	</body>
103977</html>Structures_Graph-1.0.4/docs/html/index.html0000644000076600000240000000176111461440275020200 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
103978<!DOCTYPE html 
103979     PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//FR"
103980     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
103981   <html xmlns="http://www.w3.org/1999/xhtml">
103982<head>
103983	<!-- Generated by phpDocumentor on Fri, 30 Jan 2004 16:37:28 +0000  -->
103984  <title>Structures_Graph Documentation</title>
103985  <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
103986</head>
103987
103988<FRAMESET rows='100,*'>
103989	<FRAME src='packages.html' name='left_top' frameborder="1" bordercolor="#999999">
103990	<FRAMESET cols='25%,*'>
103991		<FRAME src='li_Structures_Graph.html' name='left_bottom' frameborder="1" bordercolor="#999999">
103992		<FRAME src='Structures_Graph/tutorial_Structures_Graph.pkg.html' name='right' frameborder="1" bordercolor="#999999">
103993	</FRAMESET>
103994	<NOFRAMES>
103995		<H2>Frame Alert</H2>
103996		<P>This document is designed to be viewed using the frames feature.
103997		If you see this message, you are using a non-frame-capable web client.</P>
103998	</NOFRAMES>
103999</FRAMESET>
104000</HTML>Structures_Graph-1.0.4/docs/html/li_Structures_Graph.html0000644000076600000240000000476011461440275023063 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
104001<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
104002  <html xmlns="http://www.w3.org/1999/xhtml">
104003		<head>
104004			<!-- template designed by Marco Von Ballmoos -->
104005			<title></title>
104006			<link rel="stylesheet" href="media/stylesheet.css" />
104007			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
104008		</head>
104009		<body>
104010						<div class="package-title">Structures_Graph</div>
104011<div class="package-details">
104012
104013	<dl class="tree">
104014		
104015		<dt class="folder-title">Description</dt>
104016		<dd>
104017			<a href='classtrees_Structures_Graph.html' target='right'>Class trees</a><br />
104018			<a href='elementindex_Structures_Graph.html' target='right'>Index of elements</a><br />
104019							<a href="todolist.html" target="right">Todo List</a><br />
104020					</dd>
104021	
104022							
104023							
104024									<dt class="folder-title">Tutorials/Manuals</dt>
104025					<dd>
104026											<dl class="tree">
104027						<dt class="folder-title">Package-level</dt>
104028						<dd>
104029													<div><a href="Structures_Graph/tutorial_Structures_Graph.pkg.html" target="right">Structures_Graph Tutorial</a></div>
104030
104031												</dd>
104032						</dl>
104033										
104034										
104035										</dd>
104036													<dt class="folder-title">Classes</dt>
104037											<dd><a href='Structures_Graph/Structures_Graph.html' target='right'>Structures_Graph</a></dd>
104038											<dd><a href='Structures_Graph/Structures_Graph_Manipulator_AcyclicTest.html' target='right'>Structures_Graph_Manipulator_AcyclicTest</a></dd>
104039											<dd><a href='Structures_Graph/Structures_Graph_Manipulator_TopologicalSorter.html' target='right'>Structures_Graph_Manipulator_TopologicalSorter</a></dd>
104040											<dd><a href='Structures_Graph/Structures_Graph_Node.html' target='right'>Structures_Graph_Node</a></dd>
104041																						<dt class="folder-title">Files</dt>
104042											<dd><a href='Structures_Graph/_Structures_Graph_Manipulator_AcyclicTest_php.html' target='right'>AcyclicTest.php</a></dd>
104043											<dd><a href='Structures_Graph/_Structures_Graph_php.html' target='right'>Graph.php</a></dd>
104044											<dd><a href='Structures_Graph/_Structures_Graph_Node_php.html' target='right'>Node.php</a></dd>
104045											<dd><a href='Structures_Graph/_Structures_Graph_Manipulator_TopologicalSorter_php.html' target='right'>TopologicalSorter.php</a></dd>
104046																	
104047						
104048			</dl>
104049</div>
104050<p class="notes"><a href="http://www.phpdoc.org" target="_blank">phpDocumentor v <span class="field">1.2.3</span></a></p>
104051</BODY>
104052</HTML>Structures_Graph-1.0.4/docs/html/packages.html0000644000076600000240000000164111461440275020644 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
104053<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
104054  <html xmlns="http://www.w3.org/1999/xhtml">
104055		<head>
104056			<!-- template designed by Marco Von Ballmoos -->
104057			<title></title>
104058			<link rel="stylesheet" href="media/stylesheet.css" />
104059			<link rel="stylesheet" href="media/banner.css" />
104060			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
104061		</head>
104062		<body>
104063			<div class="banner">
104064				<div class="banner-title">Structures_Graph</div>
104065				<div class="banner-menu">
104066	        <table cellpadding="0" cellspacing="0" style="width: 100%">
104067	          <tr>
104068              <td>
104069								              </td>
104070              <td style="width: 2em">&nbsp;</td>
104071              <td style="text-align: right">
104072								              </td>
104073						</tr>
104074          </table>
104075				</div>
104076			</div>
104077		</body>
104078	</html>Structures_Graph-1.0.4/docs/html/todolist.html0000644000076600000240000000155411461440275020732 0ustar  bbieberstaff<?xml version="1.0" encoding="iso-8859-1"?>
104079<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
104080  <html xmlns="http://www.w3.org/1999/xhtml">
104081		<head>
104082			<!-- template designed by Marco Von Ballmoos -->
104083			<title>Todo List</title>
104084			<link rel="stylesheet" href="media/stylesheet.css" />
104085			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>
104086		</head>
104087		<body>
104088						<div align="center"><h1>Todo List</h1></div>
104089<h2>Structures_Graph</h2>
104090<h3><a href="Structures_Graph/Structures_Graph.html#methodremoveNode">Structures_Graph::removeNode()</a></h3>
104091<ul>
104092    <li>This is unimplemented</li>
104093</ul>
104094	<p class="notes" id="credit">
104095		Documentation generated on Fri, 30 Jan 2004 16:37:29 +0000 by <a href="http://www.phpdoc.org" target="_blank">phpDocumentor 1.2.3</a>
104096	</p>
104097	</body>
104098</html>Structures_Graph-1.0.4/docs/tutorials/Structures_Graph/Structures_Graph.pkg0000644000076600000240000000771411461440275026604 0ustar  bbieberstaff<refentry id="{@id package.database.structures_graph.tutorial}">
104099 <refnamediv>
104100  <refname><classname>Structures_Graph</classname> Tutorial</refname>
104101  <refpurpose>A first tour of graph datastructure manipulation</refpurpose>
104102 </refnamediv>
104103 <refsect1 id="{@id package.database.structures_graph.tutorial.intro}">
104104  <title>Introduction</title>
104105  <para>
104106  Structures_Graph is a package for creating and manipulating graph datastructures. A graph is a set of objects, called nodes, connected by arcs. When used as a datastructure, usually nodes contain data, and arcs represent relationships between nodes. When arcs have a direction, and can be travelled only one way, graphs are said to be directed. When arcs have no direction, and can always be travelled both ways, graphs are said to be non directed.
104107  </para>
104108  <para>
104109  Structures_Graph provides an object oriented API to create and directly query a graph, as well as a set of Manipulator classes to extract information from the graph.
104110  </para>
104111 </refsect1>
104112 <refsect1 id="{@id package.database.structures_graph.tutorial.creation}">
104113  <title>Creating a Graph</title>
104114  <para>
104115   Creating a graph is done using the simple constructor:
104116   <programlisting>
104117    <![CDATA[
104118require_once 'Structures/Graph.php';
104119
104120$directedGraph =& new Structures_Graph(true);
104121$nonDirectedGraph =& new Structures_Graph(false);
104122    ]]>
104123   </programlisting>
104124   and passing the constructor a flag telling it whether the graph should be directed. A directed graph will always be directed during its lifetime. It's a permanent characteristic.
104125  </para>
104126  <para>
104127  To fill out the graph, we'll need to create some nodes, and then call Graph::addNode.
104128   <programlisting>
104129    <![CDATA[
104130require_once 'Structures/Graph/Node.php';
104131
104132$nodeOne =& new Structures_Graph_Node();
104133$nodeTwo =& new Structures_Graph_Node();
104134$nodeThree =& new Structures_Graph_Node();
104135
104136$directedGraph->addNode(&$nodeOne);
104137$directedGraph->addNode(&$nodeTwo);
104138$directedGraph->addNode(&$nodeThree);
104139    ]]>
104140   </programlisting>
104141   and then setup the arcs:
104142   <programlisting>
104143    <![CDATA[
104144$nodeOne->connectTo($nodeTwo);
104145$nodeOne->connectTo($nodeThree);
104146    ]]>
104147   </programlisting>
104148   Note that arcs can only be created after the nodes have been inserted into the graph. 
104149  </para>
104150 </refsect1>
104151 <refsect1 id="{@id package.database.structures_graph.tutorial.nodesanddata}">
104152  <title>Associating Data</title>
104153  <para>
104154  Graphs are only useful as datastructures if they can hold data. Structure_Graph stores data in nodes. Each node contains a setter and a getter for its data.
104155   <programlisting>
104156    <![CDATA[
104157$nodeOne->setData("Node One's Data is a String");
104158$nodeTwo->setData(1976);
104159$nodeThree->setData('Some other string');
104160
104161print("NodeTwo's Data is an integer: " . $nodeTwo->getData());
104162    ]]>
104163   </programlisting>
104164  </para>
104165  <para>
104166  Structure_Graph nodes can also store metadata, alongside with the main data. Metadata differs from regular data just because it is stored under a key, making it possible to store more than one data reference per node. The metadata getter and setter need the key to perform the operation:
104167   <programlisting>
104168    <![CDATA[
104169$nodeOne->setMetadata('example key', "Node One's Sample Metadata");
104170print("Metadata stored under key 'example key' in node one: " . $nodeOne->getMetadata('example key'));
104171$nodeOne->unsetMetadata('example key');
104172    ]]>
104173   </programlisting>
104174  </para>
104175 </refsect1>
104176 <refsect1 id="{@id package.database.structures_graph.tutorial.querying}">
104177  <title>Querying a Graph</title>
104178  <para>
104179  Structures_Graph provides for basic querying of the graph:
104180   <programlisting>
104181    <![CDATA[
104182// Nodes are able to calculate their indegree and outdegree
104183print("NodeOne's inDegree: " . $nodeOne->inDegree());
104184print("NodeOne's outDegree: " . $nodeOne->outDegree());
104185
104186// and naturally, nodes can report on their arcs
104187$arcs = $nodeOne->getNeighbours();
104188for ($i=0;$i<sizeof($arcs);$i++) {
104189    print("NodeOne has an arc to " . $arcs[$i]->getData());
104190}
104191    ]]>
104192   </programlisting>
104193  </para>
104194 </refsect1>
104195</refentry>
104196Structures_Graph-1.0.4/docs/generate.sh0000755000076600000240000000055511461440275017370 0ustar  bbieberstaff#!/bin/sh
104197(cd ..; tar czf docs/arch.tgz "{arch}")
104198rm -Rf "../{arch}"
104199rm -Rf ./html
104200mkdir -p ./html
104201phpdoc --directory ../Structures,./tutorials --target ./html --title "Structures_Graph Documentation" --output "HTML:frames" --defaultpackagename structures_graph --defaultcategoryname structures --pear 
104202(cd ..; tar --absolute-names -xzf docs/arch.tgz)
104203#rm arch.tgz
104204Structures_Graph-1.0.4/Structures/Graph/Manipulator/AcyclicTest.php0000644000076600000240000001316011461440275024742 0ustar  bbieberstaff<?php
104205/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
104206// +-----------------------------------------------------------------------------+
104207// | Copyright (c) 2003 S�rgio Gon�alves Carvalho                                |
104208// +-----------------------------------------------------------------------------+
104209// | This file is part of Structures_Graph.                                      |
104210// |                                                                             |
104211// | Structures_Graph is free software; you can redistribute it and/or modify    |
104212// | it under the terms of the GNU Lesser General Public License as published by |
104213// | the Free Software Foundation; either version 2.1 of the License, or         |
104214// | (at your option) any later version.                                         |
104215// |                                                                             |
104216// | Structures_Graph is distributed in the hope that it will be useful,         |
104217// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
104218// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
104219// | GNU Lesser General Public License for more details.                         |
104220// |                                                                             |
104221// | You should have received a copy of the GNU Lesser General Public License    |
104222// | along with Structures_Graph; if not, write to the Free Software             |
104223// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
104224// | 02111-1307 USA                                                              |
104225// +-----------------------------------------------------------------------------+
104226// | Author: S�rgio Carvalho <sergio.carvalho@portugalmail.com>                  |
104227// +-----------------------------------------------------------------------------+
104228//
104229/**
104230 * This file contains the definition of the Structures_Graph_Manipulator_AcyclicTest graph manipulator.
104231 * 
104232 * @see Structures_Graph_Manipulator_AcyclicTest
104233 * @package Structures_Graph
104234 */
104235
104236/* dependencies {{{ */
104237/** */
104238require_once 'PEAR.php';
104239/** */
104240require_once 'Structures/Graph.php';
104241/** */
104242require_once 'Structures/Graph/Node.php';
104243/* }}} */
104244
104245/* class Structures_Graph_Manipulator_AcyclicTest {{{ */
104246/**
104247 * The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator
104248 * which tests whether a graph contains a cycle. 
104249 * 
104250 * The definition of an acyclic graph used in this manipulator is that of a 
104251 * DAG. The graph must be directed, or else it is considered cyclic, even when 
104252 * there are no arcs.
104253 *
104254 * @author		S�rgio Carvalho <sergio.carvalho@portugalmail.com> 
104255 * @copyright	(c) 2004 by S�rgio Carvalho
104256 * @package Structures_Graph
104257 */
104258class Structures_Graph_Manipulator_AcyclicTest {
104259    /* _nonVisitedInDegree {{{ */
104260    /**
104261    *
104262    * This is a variant of Structures_Graph::inDegree which does 
104263    * not count nodes marked as visited.
104264    *
104265    * @access   private
104266    * @return	integer	 Number of non-visited nodes that link to this one
104267    */
104268    function _nonVisitedInDegree(&$node) {
104269        $result = 0;
104270        $graphNodes =& $node->_graph->getNodes();
104271        foreach (array_keys($graphNodes) as $key) {
104272            if ((!$graphNodes[$key]->getMetadata('acyclic-test-visited')) && $graphNodes[$key]->connectsTo($node)) $result++;
104273        }
104274        return $result;
104275        
104276    }
104277    /* }}} */
104278
104279    /* _isAcyclic {{{ */
104280    /**
104281    * @access   private
104282    */
104283    function _isAcyclic(&$graph) {
104284        // Mark every node as not visited
104285        $nodes =& $graph->getNodes();
104286        $nodeKeys = array_keys($nodes);
104287        $refGenerator = array();
104288        foreach($nodeKeys as $key) {
104289            $refGenerator[] = false;
104290            $nodes[$key]->setMetadata('acyclic-test-visited', $refGenerator[sizeof($refGenerator) - 1]);
104291        }
104292
104293        // Iteratively peel off leaf nodes
104294        do {
104295            // Find out which nodes are leafs (excluding visited nodes)
104296            $leafNodes = array();
104297            foreach($nodeKeys as $key) {
104298                if ((!$nodes[$key]->getMetadata('acyclic-test-visited')) && Structures_Graph_Manipulator_AcyclicTest::_nonVisitedInDegree($nodes[$key]) == 0) {
104299                    $leafNodes[] =& $nodes[$key];
104300                }
104301            }
104302            // Mark leafs as visited
104303            for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
104304                $visited =& $leafNodes[$i]->getMetadata('acyclic-test-visited');
104305                $visited = true;
104306                $leafNodes[$i]->setMetadata('acyclic-test-visited', $visited);
104307            }
104308        } while (sizeof($leafNodes) > 0);
104309
104310        // If graph is a DAG, there should be no non-visited nodes. Let's try to prove otherwise
104311        $result = true;
104312        foreach($nodeKeys as $key) if (!$nodes[$key]->getMetadata('acyclic-test-visited')) $result = false;
104313        
104314        // Cleanup visited marks
104315        foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('acyclic-test-visited');
104316
104317        return $result;
104318    }
104319    /* }}} */
104320
104321    /* isAcyclic {{{ */
104322    /**
104323    *
104324    * isAcyclic returns true if a graph contains no cycles, false otherwise.
104325    *
104326    * @return	boolean	 true iff graph is acyclic
104327    * @access	public
104328    */
104329    function isAcyclic(&$graph) {
104330        // We only test graphs
104331        if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_AcyclicTest::isAcyclic received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC);
104332        if (!$graph->isDirected()) return false; // Only directed graphs may be acyclic
104333
104334        return Structures_Graph_Manipulator_AcyclicTest::_isAcyclic($graph);
104335    }
104336    /* }}} */
104337}
104338/* }}} */
104339?>
104340Structures_Graph-1.0.4/Structures/Graph/Manipulator/TopologicalSorter.php0000644000076600000240000001504611461440275026213 0ustar  bbieberstaff<?php
104341/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
104342// +-----------------------------------------------------------------------------+
104343// | Copyright (c) 2003 S�rgio Gon�alves Carvalho                                |
104344// +-----------------------------------------------------------------------------+
104345// | This file is part of Structures_Graph.                                      |
104346// |                                                                             |
104347// | Structures_Graph is free software; you can redistribute it and/or modify    |
104348// | it under the terms of the GNU Lesser General Public License as published by |
104349// | the Free Software Foundation; either version 2.1 of the License, or         |
104350// | (at your option) any later version.                                         |
104351// |                                                                             |
104352// | Structures_Graph is distributed in the hope that it will be useful,         |
104353// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
104354// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
104355// | GNU Lesser General Public License for more details.                         |
104356// |                                                                             |
104357// | You should have received a copy of the GNU Lesser General Public License    |
104358// | along with Structures_Graph; if not, write to the Free Software             |
104359// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
104360// | 02111-1307 USA                                                              |
104361// +-----------------------------------------------------------------------------+
104362// | Author: S�rgio Carvalho <sergio.carvalho@portugalmail.com>                  |
104363// +-----------------------------------------------------------------------------+
104364//
104365/**
104366 * This file contains the definition of the Structures_Graph_Manipulator_TopologicalSorter class.
104367 * 
104368 * @see Structures_Graph_Manipulator_TopologicalSorter
104369 * @package Structures_Graph
104370 */
104371
104372/* dependencies {{{ */
104373/** */
104374require_once 'PEAR.php';
104375/** */
104376require_once 'Structures/Graph.php';
104377/** */
104378require_once 'Structures/Graph/Node.php';
104379/** */
104380require_once 'Structures/Graph/Manipulator/AcyclicTest.php';
104381/* }}} */
104382
104383/* class Structures_Graph_Manipulator_TopologicalSorter {{{ */
104384/**
104385 * The Structures_Graph_Manipulator_TopologicalSorter is a manipulator 
104386 * which is able to return the set of nodes in a graph, sorted by topological 
104387 * order.
104388 *
104389 * A graph may only be sorted topologically iff it's a DAG. You can test it
104390 * with the Structures_Graph_Manipulator_AcyclicTest.
104391 * 
104392 * @author		S�rgio Carvalho <sergio.carvalho@portugalmail.com> 
104393 * @copyright	(c) 2004 by S�rgio Carvalho
104394 * @see     Structures_Graph_Manipulator_AcyclicTest
104395 * @package Structures_Graph
104396 */
104397class Structures_Graph_Manipulator_TopologicalSorter {
104398    /* _nonVisitedInDegree {{{ */
104399    /**
104400    *
104401    * This is a variant of Structures_Graph::inDegree which does 
104402    * not count nodes marked as visited.
104403    *
104404    * @access   private
104405    * @return	integer	 Number of non-visited nodes that link to this one
104406    */
104407    function _nonVisitedInDegree(&$node) {
104408        $result = 0;
104409        $graphNodes =& $node->_graph->getNodes();
104410        foreach (array_keys($graphNodes) as $key) {
104411            if ((!$graphNodes[$key]->getMetadata('topological-sort-visited')) && $graphNodes[$key]->connectsTo($node)) $result++;
104412        }
104413        return $result;
104414        
104415    }
104416    /* }}} */
104417
104418    /* _sort {{{ */
104419    /**
104420    * @access   private
104421    */
104422    function _sort(&$graph) {
104423        // Mark every node as not visited
104424        $nodes =& $graph->getNodes();
104425        $nodeKeys = array_keys($nodes);
104426        $refGenerator = array();
104427        foreach($nodeKeys as $key) {
104428            $refGenerator[] = false;
104429            $nodes[$key]->setMetadata('topological-sort-visited', $refGenerator[sizeof($refGenerator) - 1]);
104430        }
104431
104432        // Iteratively peel off leaf nodes
104433        $topologicalLevel = 0;
104434        do {
104435            // Find out which nodes are leafs (excluding visited nodes)
104436            $leafNodes = array();
104437            foreach($nodeKeys as $key) {
104438                if ((!$nodes[$key]->getMetadata('topological-sort-visited')) && Structures_Graph_Manipulator_TopologicalSorter::_nonVisitedInDegree($nodes[$key]) == 0) {
104439                    $leafNodes[] =& $nodes[$key];
104440                }
104441            }
104442            // Mark leafs as visited
104443            $refGenerator[] = $topologicalLevel;
104444            for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
104445                $visited =& $leafNodes[$i]->getMetadata('topological-sort-visited');
104446                $visited = true;
104447                $leafNodes[$i]->setMetadata('topological-sort-visited', $visited);
104448                $leafNodes[$i]->setMetadata('topological-sort-level', $refGenerator[sizeof($refGenerator) - 1]);
104449            }
104450            $topologicalLevel++;
104451        } while (sizeof($leafNodes) > 0);
104452
104453        // Cleanup visited marks
104454        foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('topological-sort-visited');
104455    }
104456    /* }}} */
104457
104458    /* sort {{{ */
104459    /**
104460    *
104461    * sort returns the graph's nodes, sorted by topological order. 
104462    * 
104463    * The result is an array with 
104464    * as many entries as topological levels. Each entry in this array is an array of nodes within
104465    * the given topological level.
104466    *
104467    * @return	array	 The graph's nodes, sorted by topological order.
104468    * @access	public
104469    */
104470    function sort(&$graph) {
104471        // We only sort graphs
104472        if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC);
104473        if (!Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph)) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an graph that has cycles', STRUCTURES_GRAPH_ERROR_GENERIC);
104474
104475        Structures_Graph_Manipulator_TopologicalSorter::_sort($graph);
104476        $result = array();
104477
104478        // Fill out result array
104479        $nodes =& $graph->getNodes();
104480        $nodeKeys = array_keys($nodes);
104481        foreach($nodeKeys as $key) {
104482            if (!array_key_exists($nodes[$key]->getMetadata('topological-sort-level'), $result)) $result[$nodes[$key]->getMetadata('topological-sort-level')] = array();
104483            $result[$nodes[$key]->getMetadata('topological-sort-level')][] =& $nodes[$key];
104484            $nodes[$key]->unsetMetadata('topological-sort-level');
104485        }
104486
104487        return $result;
104488    }
104489    /* }}} */
104490}
104491/* }}} */
104492?>
104493Structures_Graph-1.0.4/Structures/Graph/Node.php0000644000076600000240000002545011461440275021132 0ustar  bbieberstaff<?php
104494/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
104495// +-----------------------------------------------------------------------------+
104496// | Copyright (c) 2003 S�rgio Gon�alves Carvalho                                |
104497// +-----------------------------------------------------------------------------+
104498// | This file is part of Structures_Graph.                                      |
104499// |                                                                             |
104500// | Structures_Graph is free software; you can redistribute it and/or modify    |
104501// | it under the terms of the GNU Lesser General Public License as published by |
104502// | the Free Software Foundation; either version 2.1 of the License, or         |
104503// | (at your option) any later version.                                         |
104504// |                                                                             |
104505// | Structures_Graph is distributed in the hope that it will be useful,         |
104506// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
104507// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
104508// | GNU Lesser General Public License for more details.                         |
104509// |                                                                             |
104510// | You should have received a copy of the GNU Lesser General Public License    |
104511// | along with Structures_Graph; if not, write to the Free Software             |
104512// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
104513// | 02111-1307 USA                                                              |
104514// +-----------------------------------------------------------------------------+
104515// | Author: S�rgio Carvalho <sergio.carvalho@portugalmail.com>                  |
104516// +-----------------------------------------------------------------------------+
104517//
104518/**
104519 * This file contains the definition of the Structures_Graph_Node class
104520 * 
104521 * @see Structures_Graph_Node
104522 * @package Structures_Graph
104523 */
104524
104525/* dependencies {{{ */
104526/** */
104527require_once 'PEAR.php';
104528/** */
104529require_once 'Structures/Graph.php';
104530/* }}} */
104531
104532/* class Structures_Graph_Node {{{ */
104533/**
104534 * The Structures_Graph_Node class represents a Node that can be member of a 
104535 * graph node set.
104536 *
104537 * A graph node can contain data. Under this API, the node contains default data, 
104538 * and key index data. It behaves, thus, both as a regular data node, and as a 
104539 * dictionary (or associative array) node.
104540 * 
104541 * Regular data is accessed via getData and setData. Key indexed data is accessed
104542 * via getMetadata and setMetadata.
104543 *
104544 * @author		S�rgio Carvalho <sergio.carvalho@portugalmail.com> 
104545 * @copyright	(c) 2004 by S�rgio Carvalho
104546 * @package Structures_Graph
104547 */
104548/* }}} */
104549class Structures_Graph_Node {
104550    /* fields {{{ */
104551    /** 
104552     * @access private 
104553     */
104554    var $_data = null;
104555    /** @access private */
104556    var $_metadata = array();
104557    /** @access private */
104558    var $_arcs = array();
104559    /** @access private */
104560    var $_graph = null;
104561    /* }}} */
104562
104563    /* Constructor {{{ */
104564    /**
104565    *
104566    * Constructor
104567    *
104568    * @access	public
104569    */
104570    function Structures_Graph_Node() {
104571    }
104572    /* }}} */
104573
104574    /* getGraph {{{ */
104575    /**
104576    *
104577    * Node graph getter
104578    *
104579    * @return	Structures_Graph	Graph where node is stored
104580    * @access	public
104581    */
104582    function &getGraph() {
104583        return $this->_graph;
104584    }
104585    /* }}} */
104586
104587    /* setGraph {{{ */
104588    /**
104589    *
104590    * Node graph setter. This method should not be called directly. Use Graph::addNode instead.
104591    *
104592    * @param    Structures_Graph   Set the graph for this node. 
104593    * @see      Structures_Graph::addNode()
104594    * @access	public
104595    */
104596    function setGraph(&$graph) {
104597        $this->_graph =& $graph;
104598    }
104599    /* }}} */
104600
104601    /* getData {{{ */
104602    /**
104603    *
104604    * Node data getter.
104605    * 
104606    * Each graph node can contain a reference to one variable. This is the getter for that reference.
104607    *
104608    * @return	mixed	Data stored in node
104609    * @access	public
104610    */
104611    function &getData() {
104612        return $this->_data;
104613    }
104614    /* }}} */
104615
104616    /* setData {{{ */
104617    /**
104618    *
104619    * Node data setter
104620    *
104621    * Each graph node can contain a reference to one variable. This is the setter for that reference.
104622    *
104623    * @return	mixed	Data to store in node
104624    * @access	public
104625    */
104626    function setData($data) {
104627        $this->_data =& $data;
104628    }
104629    /* }}} */
104630
104631    /* metadataKeyExists {{{ */
104632    /**
104633    *
104634    * Test for existence of metadata under a given key.
104635    *
104636    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
104637    * associative array or in a dictionary. This method tests whether a given metadata key exists for this node.
104638    *
104639    * @param    string    Key to test
104640    * @return	boolean	 
104641    * @access	public
104642    */
104643    function metadataKeyExists($key) {
104644        return array_key_exists($key, $this->_metadata);
104645    }
104646    /* }}} */
104647
104648    /* getMetadata {{{ */
104649    /**
104650    *
104651    * Node metadata getter
104652    *
104653    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
104654    * associative array or in a dictionary. This method gets the data under the given key. If the key does
104655    * not exist, an error will be thrown, so testing using metadataKeyExists might be needed.
104656    *
104657    * @param    string  Key
104658    * @param    boolean nullIfNonexistent (defaults to false).
104659    * @return	mixed	Metadata Data stored in node under given key
104660    * @see      metadataKeyExists
104661    * @access	public
104662    */
104663    function &getMetadata($key, $nullIfNonexistent = false) {
104664        if (array_key_exists($key, $this->_metadata)) {
104665            return $this->_metadata[$key];
104666        } else {
104667            if ($nullIfNonexistent) {
104668                $a = null;
104669                return $a;
104670            } else {
104671                $a = Pear::raiseError('Structures_Graph_Node::getMetadata: Requested key does not exist', STRUCTURES_GRAPH_ERROR_GENERIC);
104672                return $a;
104673            }
104674        }
104675    }
104676    /* }}} */
104677
104678    /* unsetMetadata {{{ */
104679    /**
104680    *
104681    * Delete metadata by key
104682    *
104683    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
104684    * associative array or in a dictionary. This method removes any data that might be stored under the provided key.
104685    * If the key does not exist, no error is thrown, so it is safe using this method without testing for key existence.
104686    *
104687    * @param    string  Key
104688    * @access	public
104689    */
104690    function unsetMetadata($key) {
104691        if (array_key_exists($key, $this->_metadata)) unset($this->_metadata[$key]);
104692    }
104693    /* }}} */
104694
104695    /* setMetadata {{{ */
104696    /**
104697    *
104698    * Node metadata setter
104699    *
104700    * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an 
104701    * associative array or in a dictionary. This method stores data under the given key. If the key already exists,
104702    * previously stored data is discarded.
104703    *
104704    * @param    string  Key
104705    * @param    mixed   Data 
104706    * @access	public
104707    */
104708    function setMetadata($key, $data) {
104709        $this->_metadata[$key] =& $data;
104710    }
104711    /* }}} */
104712
104713    /* _connectTo {{{ */
104714    /** @access private */
104715    function _connectTo(&$destinationNode) {
104716        $this->_arcs[] =& $destinationNode;
104717    }
104718    /* }}} */
104719
104720    /* connectTo {{{ */
104721    /**
104722    *
104723    * Connect this node to another one.
104724    * 
104725    * If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created.
104726    *
104727    * @param    Structures_Graph_Node Node to connect to
104728    * @access	public
104729    */
104730    function connectTo(&$destinationNode) {
104731        // We only connect to nodes
104732        if (!is_a($destinationNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph_Node::connectTo received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC);
104733        // Nodes must already be in graphs to be connected
104734        if ($this->_graph == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
104735        if ($destinationNode->getGraph() == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect to a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
104736        // Connect here
104737        $this->_connectTo($destinationNode);
104738        // If graph is undirected, connect back
104739        if (!$this->_graph->isDirected()) {
104740            $destinationNode->_connectTo($this);
104741        }
104742    }
104743    /* }}} */
104744
104745    /* getNeighbours {{{ */
104746    /**
104747    *
104748    * Return nodes connected to this one.
104749    * 
104750    * @return   array   Array of nodes
104751    * @access	public
104752    */
104753    function getNeighbours() {
104754        return $this->_arcs;
104755    }
104756    /* }}} */
104757
104758    /* connectsTo {{{ */
104759    /**
104760    *
104761    * Test wether this node has an arc to the target node
104762    *
104763    * @return	boolean   True if the two nodes are connected
104764    * @access	public
104765    */
104766    function connectsTo(&$target) {
104767        if (version_compare(PHP_VERSION, '5.0.0') >= 0) {
104768            return in_array($target, $this->getNeighbours(), true);
104769        }
104770
104771        $copy = $target;
104772        $arcKeys = array_keys($this->_arcs);
104773        foreach($arcKeys as $key) {
104774            /* ZE1 chokes on this expression:
104775                if ($target === $arc) return true;
104776              so, we'll use more convoluted stuff
104777            */
104778            $arc =& $this->_arcs[$key];
104779            $target = true;
104780            if ($arc === true) {
104781                $target = false;
104782                if ($arc === false) {
104783                    $target = $copy;
104784                    return true;
104785                }
104786            }
104787        }
104788        $target = $copy;
104789        return false;
104790    }
104791    /* }}} */
104792
104793    /* inDegree {{{ */
104794    /**
104795    *
104796    * Calculate the in degree of the node.
104797    * 
104798    * The indegree for a node is the number of arcs entering the node. For non directed graphs, 
104799    * the indegree is equal to the outdegree.
104800    *
104801    * @return	integer	 In degree of the node
104802    * @access	public
104803    */
104804    function inDegree() {
104805        if ($this->_graph == null) return 0;
104806        if (!$this->_graph->isDirected()) return $this->outDegree();
104807        $result = 0;
104808        $graphNodes =& $this->_graph->getNodes();
104809        foreach (array_keys($graphNodes) as $key) {
104810            if ($graphNodes[$key]->connectsTo($this)) $result++;
104811        }
104812        return $result;
104813        
104814    }
104815    /* }}} */
104816
104817    /* outDegree {{{ */
104818    /**
104819    *
104820    * Calculate the out degree of the node.
104821    *
104822    * The outdegree for a node is the number of arcs exiting the node. For non directed graphs,
104823    * the outdegree is always equal to the indegree.
104824    * 
104825    * @return	integer	 Out degree of the node
104826    * @access	public
104827    */
104828    function outDegree() {
104829        if ($this->_graph == null) return 0;
104830        return sizeof($this->_arcs);
104831    }
104832    /* }}} */
104833}
104834?>
104835Structures_Graph-1.0.4/Structures/Graph.php0000644000076600000240000001316311461440275020243 0ustar  bbieberstaff<?php
104836/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
104837// +-----------------------------------------------------------------------------+
104838// | Copyright (c) 2003 S�rgio Gon�alves Carvalho                                |
104839// +-----------------------------------------------------------------------------+
104840// | This file is part of Structures_Graph.                                      |
104841// |                                                                             |
104842// | Structures_Graph is free software; you can redistribute it and/or modify    |
104843// | it under the terms of the GNU Lesser General Public License as published by |
104844// | the Free Software Foundation; either version 2.1 of the License, or         |
104845// | (at your option) any later version.                                         |
104846// |                                                                             |
104847// | Structures_Graph is distributed in the hope that it will be useful,         |
104848// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
104849// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
104850// | GNU Lesser General Public License for more details.                         |
104851// |                                                                             |
104852// | You should have received a copy of the GNU Lesser General Public License    |
104853// | along with Structures_Graph; if not, write to the Free Software             |
104854// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
104855// | 02111-1307 USA                                                              |
104856// +-----------------------------------------------------------------------------+
104857// | Author: S�rgio Carvalho <sergio.carvalho@portugalmail.com>                  |
104858// +-----------------------------------------------------------------------------+
104859//
104860/**
104861 * The Graph.php file contains the definition of the Structures_Graph class 
104862 *
104863 * @see Structures_Graph
104864 * @package Structures_Graph
104865 */
104866
104867/* dependencies {{{ */
104868/** PEAR base classes */
104869require_once 'PEAR.php';
104870/** Graph Node */
104871require_once 'Structures/Graph/Node.php';
104872/* }}} */
104873
104874define('STRUCTURES_GRAPH_ERROR_GENERIC', 100);
104875
104876/* class Structures_Graph {{{ */
104877/**
104878 * The Structures_Graph class represents a graph data structure. 
104879 *
104880 * A Graph is a data structure composed by a set of nodes, connected by arcs.
104881 * Graphs may either be directed or undirected. In a directed graph, arcs are 
104882 * directional, and can be traveled only one way. In an undirected graph, arcs
104883 * are bidirectional, and can be traveled both ways.
104884 *
104885 * @author		S�rgio Carvalho <sergio.carvalho@portugalmail.com> 
104886 * @copyright	(c) 2004 by S�rgio Carvalho
104887 * @package Structures_Graph
104888 */
104889/* }}} */
104890class Structures_Graph {
104891    /* fields {{{ */
104892    /**
104893     * @access private
104894     */
104895    var $_nodes = array();
104896    /**
104897     * @access private
104898     */
104899    var $_directed = false;
104900    /* }}} */
104901
104902    /* Constructor {{{ */
104903    /**
104904    *
104905    * Constructor
104906    *
104907    * @param    boolean    Set to true if the graph is directed. Set to false if it is not directed. (Optional, defaults to true)
104908    * @access	public
104909    */
104910    function Structures_Graph($directed = true) {
104911        $this->_directed = $directed;
104912    }
104913    /* }}} */
104914
104915    /* isDirected {{{ */
104916    /**
104917    *
104918    * Return true if a graph is directed
104919    *
104920    * @return	boolean	 true if the graph is directed
104921    * @access	public
104922    */
104923    function isDirected() {
104924        return (boolean) $this->_directed;
104925    }
104926    /* }}} */
104927
104928    /* addNode {{{ */
104929    /**
104930    *
104931    * Add a Node to the Graph
104932    *
104933    * @param    Structures_Graph_Node   The node to be added.
104934    * @access	public
104935    */
104936    function addNode(&$newNode) {
104937        // We only add nodes
104938        if (!is_a($newNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph::addNode received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC);
104939        // Graphs are node *sets*, so duplicates are forbidden. We allow nodes that are exactly equal, but disallow equal references.
104940        foreach($this->_nodes as $key => $node) {
104941            /*
104942             ZE1 equality operators choke on the recursive cycle introduced by the _graph field in the Node object.
104943             So, we'll check references the hard way (change $this->_nodes[$key] and check if the change reflects in 
104944             $node)
104945            */
104946            $savedData = $this->_nodes[$key];
104947            $referenceIsEqualFlag = false;
104948            $this->_nodes[$key] = true;
104949            if ($node === true) {
104950                $this->_nodes[$key] = false;
104951                if ($node === false) $referenceIsEqualFlag = true;
104952            }
104953            $this->_nodes[$key] = $savedData;
104954            if ($referenceIsEqualFlag) return Pear::raiseError('Structures_Graph::addNode received an object that is a duplicate for this dataset', STRUCTURES_GRAPH_ERROR_GENERIC);
104955        }
104956        $this->_nodes[] =& $newNode;
104957        $newNode->setGraph($this);
104958    }
104959    /* }}} */
104960
104961    /* removeNode (unimplemented) {{{ */
104962    /**
104963    *
104964    * Remove a Node from the Graph
104965    *
104966    * @todo     This is unimplemented
104967    * @param    Structures_Graph_Node   The node to be removed from the graph
104968    * @access	public
104969    */
104970    function removeNode(&$node) {
104971    }
104972    /* }}} */
104973
104974    /* getNodes {{{ */
104975    /**
104976    *
104977    * Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted.
104978    *
104979    * @access   public
104980    * @see      Structures_Graph_Manipulator_TopologicalSorter
104981    * @return   array The set of nodes in this graph
104982    */
104983    function &getNodes() {
104984        return $this->_nodes;
104985    }
104986    /* }}} */
104987}
104988?>
104989Structures_Graph-1.0.4/tests/testCase/BasicGraph.php0000644000076600000240000002161111461440275021734 0ustar  bbieberstaff<?php
104990/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
104991// +-----------------------------------------------------------------------------+
104992// | Copyright (c) 2003 S�rgio Gon�alves Carvalho                                |
104993// +-----------------------------------------------------------------------------+
104994// | This file is part of Structures_Graph.                                      |
104995// |                                                                             |
104996// | Structures_Graph is free software; you can redistribute it and/or modify    |
104997// | it under the terms of the GNU Lesser General Public License as published by |
104998// | the Free Software Foundation; either version 2.1 of the License, or         |
104999// | (at your option) any later version.                                         |
105000// |                                                                             |
105001// | Structures_Graph is distributed in the hope that it will be useful,         |
105002// | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
105003// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
105004// | GNU Lesser General Public License for more details.                         |
105005// |                                                                             |
105006// | You should have received a copy of the GNU Lesser General Public License    |
105007// | along with Structures_Graph; if not, write to the Free Software             |
105008// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                    |
105009// | 02111-1307 USA                                                              |
105010// +-----------------------------------------------------------------------------+
105011// | Author: S�rgio Carvalho <sergio.carvalho@portugalmail.com>                  |
105012// +-----------------------------------------------------------------------------+
105013//
105014
105015require_once 'Structures/Graph.php';
105016require_once 'PHPUnit/Framework.php';
105017
105018/**
105019 * @access private
105020 */
105021class BasicGraph extends PHPUnit_Framework_TestCase
105022{
105023    var $_graph = null;
105024
105025    function test_create_graph() {
105026        $this->_graph = new Structures_Graph();
105027        $this->assertTrue(is_a($this->_graph, 'Structures_Graph')); 
105028    }
105029
105030    function test_add_node() {
105031        $this->_graph = new Structures_Graph();
105032        $data = 1;
105033        $node = new Structures_Graph_Node($data);
105034        $this->_graph->addNode($node);
105035        $node = new Structures_Graph_Node($data);
105036        $this->_graph->addNode($node);
105037        $node = new Structures_Graph_Node($data);
105038        $this->_graph->addNode($node);
105039    }
105040
105041    function test_connect_node() {
105042        $this->_graph = new Structures_Graph();
105043        $data = 1;
105044        $node1 = new Structures_Graph_Node($data);
105045        $node2 = new Structures_Graph_Node($data);
105046        $this->_graph->addNode($node1);
105047        $this->_graph->addNode($node2);
105048        $node1->connectTo($node2);
105049
105050        $node =& $this->_graph->getNodes();
105051        $node =& $node[0];
105052        $node = $node->getNeighbours();
105053        $node =& $node[0];
105054        /* 
105055         ZE1 == and === operators fail on $node,$node2 because of the recursion introduced
105056         by the _graph field in the Node object. So, we'll use the stupid method for reference
105057         testing
105058        */
105059        $node = true;
105060        $this->assertTrue($node2);
105061        $node = false;
105062        $this->assertFalse($node2);
105063    }
105064
105065    function test_data_references() {
105066        $this->_graph = new Structures_Graph();
105067        $data = 1;
105068        $node = new Structures_Graph_Node();
105069        $node->setData(&$data);
105070        $this->_graph->addNode($node);
105071        $data = 2;
105072        $dataInNode =& $this->_graph->getNodes();
105073        $dataInNode =& $dataInNode[0];
105074        $dataInNode =& $dataInNode->getData();
105075        $this->assertTrue($data === $dataInNode);
105076    }
105077
105078    function test_metadata_references() {
105079        $this->_graph = new Structures_Graph();
105080        $data = 1;
105081        $node = new Structures_Graph_Node();
105082        $node->setMetadata('5', &$data);
105083        $data = 2;
105084        $dataInNode =& $node->getMetadata('5');
105085        $this->assertTrue($data === $dataInNode);
105086    }
105087   
105088    function test_metadata_key_exists() {
105089        $this->_graph = new Structures_Graph();
105090        $data = 1;
105091        $node = new Structures_Graph_Node();
105092        $node->setMetadata('5', $data);
105093        $this->assertTrue($node->metadataKeyExists('5'));
105094        $this->assertFalse($node->metadataKeyExists('1'));
105095    }
105096
105097    function test_directed_degree() {
105098        $this->_graph = new Structures_Graph(true);
105099        $node = array();
105100        $node[] = new Structures_Graph_Node();
105101        $node[] = new Structures_Graph_Node();
105102        $node[] = new Structures_Graph_Node();
105103        $this->_graph->addNode($node[0]);
105104        $this->_graph->addNode($node[1]);
105105        $this->_graph->addNode($node[2]);
105106        $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 0 arcs');
105107        $this->assertEquals(0, $node[1]->inDegree(), 'inDegree test failed for node 1 with 0 arcs');
105108        $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 0 arcs');
105109        $this->assertEquals(0, $node[0]->outDegree(), 'outDegree test failed for node 0 with 0 arcs');
105110        $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 0 arcs');
105111        $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 0 arcs');
105112        $node[0]->connectTo($node[1]);
105113        $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 1 arc');
105114        $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 1 arc');
105115        $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 1 arc');
105116        $this->assertEquals(1, $node[0]->outDegree(), 'outDegree test failed for node 0 with 1 arc');
105117        $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 1 arc');
105118        $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 1 arc');
105119        $node[0]->connectTo($node[2]);
105120        $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 2 arcs');
105121        $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 2 arcs');
105122        $this->assertEquals(1, $node[2]->inDegree(), 'inDegree test failed for node 2 with 2 arcs');
105123        $this->assertEquals(2, $node[0]->outDegree(), 'outDegree test failed for node 0 with 2 arcs');
105124        $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 2 arcs');
105125        $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 2 arcs');
105126    }
105127
105128    function test_undirected_degree() {
105129        $this->_graph = new Structures_Graph(false);
105130        $node = array();
105131        $node[] = new Structures_Graph_Node();
105132        $node[] = new Structures_Graph_Node();
105133        $node[] = new Structures_Graph_Node();
105134        $this->_graph->addNode($node[0]);
105135        $this->_graph->addNode($node[1]);
105136        $this->_graph->addNode($node[2]);
105137        $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 0 arcs');
105138        $this->assertEquals(0, $node[1]->inDegree(), 'inDegree test failed for node 1 with 0 arcs');
105139        $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 0 arcs');
105140        $this->assertEquals(0, $node[0]->outDegree(), 'outDegree test failed for node 0 with 0 arcs');
105141        $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 0 arcs');
105142        $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 0 arcs');
105143        $node[0]->connectTo($node[1]);
105144        $this->assertEquals(1, $node[0]->inDegree(), 'inDegree test failed for node 0 with 1 arc');
105145        $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 1 arc');
105146        $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 1 arc');
105147        $this->assertEquals(1, $node[0]->outDegree(), 'outDegree test failed for node 0 with 1 arc');
105148        $this->assertEquals(1, $node[1]->outDegree(), 'outDegree test failed for node 1 with 1 arc');
105149        $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 1 arc');
105150        $node[0]->connectTo($node[2]);
105151        $this->assertEquals(2, $node[0]->inDegree(), 'inDegree test failed for node 0 with 2 arcs');
105152        $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 2 arcs');
105153        $this->assertEquals(1, $node[2]->inDegree(), 'inDegree test failed for node 2 with 2 arcs');
105154        $this->assertEquals(2, $node[0]->outDegree(), 'outDegree test failed for node 0 with 2 arcs');
105155        $this->assertEquals(1, $node[1]->outDegree(), 'outDegree test failed for node 1 with 2 arcs');
105156        $this->assertEquals(1, $node[2]->outDegree(), 'outDegree test failed for node 2 with 2 arcs');
105157    }
105158}
105159?>
105160Structures_Graph-1.0.4/tests/AllTests.php0000644000076600000240000000641411461440275017715 0ustar  bbieberstaff<?php
105161
105162/**
105163 * Master Unit Test Suite file for Structures_Graph
105164 * 
105165 * This top-level test suite file organizes 
105166 * all class test suite files, 
105167 * so that the full suite can be run 
105168 * by PhpUnit or via "pear run-tests -u". 
105169 *
105170 * PHP version 5
105171 *
105172 * @category   XML
105173 * @package    XML_Util
105174 * @subpackage UnitTesting
105175 * @author     Chuck Burgess <ashnazg@php.net>
105176 * @license    http://www.opensource.org/licenses/bsd-license.php New BSD License
105177 * @version    CVS: $Id$
105178 * @link       http://pear.php.net/package/XML_Util
105179 * @since      1.2.0a1
105180 */
105181
105182
105183/**
105184 * Check PHP version... PhpUnit v3+ requires at least PHP v5.1.4
105185 */
105186if (version_compare(PHP_VERSION, "5.1.4") < 0) {
105187    // Cannnot run test suites
105188    echo 'Cannot run test suite via PhpUnit... requires at least PHP v5.1.4.' . PHP_EOL;
105189    echo 'Use "pear run-tests -p xml_util" to run the PHPT tests directly.' . PHP_EOL;
105190    exit(1);
105191}
105192
105193
105194/**
105195 * Derive the "main" method name
105196 * @internal PhpUnit would have to rename PHPUnit_MAIN_METHOD to PHPUNIT_MAIN_METHOD
105197 *           to make this usage meet the PEAR CS... we cannot rename it here.
105198 */
105199if (!defined('PHPUnit_MAIN_METHOD')) {
105200    define('PHPUnit_MAIN_METHOD', 'Structures_Graph_AllTests::main');
105201}
105202
105203
105204/*
105205 * Files needed by PhpUnit
105206 */
105207require_once 'PHPUnit/Framework.php';
105208require_once 'PHPUnit/TextUI/TestRunner.php';
105209require_once 'PHPUnit/Extensions/PhptTestSuite.php';
105210
105211/*
105212 * You must add each additional class-level test suite file here
105213 */
105214require_once dirname(__FILE__).'/testCase/BasicGraph.php';
105215
105216
105217/**
105218 * Master Unit Test Suite class for Structures_Graph
105219 * 
105220 * This top-level test suite class organizes 
105221 * all class test suite files, 
105222 * so that the full suite can be run 
105223 * by PhpUnit or via "pear run-tests -up Structures_Graph". 
105224 *
105225 * @category   Structures
105226 * @package    Structures_Graph
105227 * @subpackage UnitTesting
105228 * @author     Chuck Burgess <ashnazg@php.net>
105229 * @license    http://www.opensource.org/licenses/bsd-license.php New BSD License
105230 * @version    Release: @package_version@
105231 * @link       http://pear.php.net/package/XML_Util
105232 * @since      1.2.0a1
105233 */
105234class Structures_Graph_AllTests
105235{
105236
105237    /**
105238     * Launches the TextUI test runner
105239     *
105240     * @return void
105241     * @uses PHPUnit_TextUI_TestRunner
105242     */
105243    public static function main()
105244    {
105245        PHPUnit_TextUI_TestRunner::run(self::suite());
105246    }
105247
105248
105249    /**
105250     * Adds all class test suites into the master suite
105251     *
105252     * @return PHPUnit_Framework_TestSuite a master test suite
105253     *                                     containing all class test suites
105254     * @uses PHPUnit_Framework_TestSuite
105255     */ 
105256    public static function suite()
105257    {
105258        $suite = new PHPUnit_Framework_TestSuite(
105259            'Structures_Graph Full Suite of Unit Tests');
105260
105261        /*
105262         * You must add each additional class-level test suite name here
105263         */
105264        $suite->addTestSuite('BasicGraph');
105265
105266        return $suite;
105267    }
105268}
105269
105270/**
105271 * Call the main method if this file is executed directly
105272 * @internal PhpUnit would have to rename PHPUnit_MAIN_METHOD to PHPUNIT_MAIN_METHOD
105273 *           to make this usage meet the PEAR CS... we cannot rename it here.
105274 */
105275if (PHPUnit_MAIN_METHOD == 'Structures_Graph_AllTests::main') {
105276    Structures_Graph_AllTests::main();
105277}
105278
105279/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
105280?>
105281Structures_Graph-1.0.4/LICENSE0000644000076600000240000006347611461440275015327 0ustar  bbieberstaff		  GNU LESSER GENERAL PUBLIC LICENSE
105282		       Version 2.1, February 1999
105283
105284 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
105285     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
105286 Everyone is permitted to copy and distribute verbatim copies
105287 of this license document, but changing it is not allowed.
105288
105289[This is the first released version of the Lesser GPL.  It also counts
105290 as the successor of the GNU Library Public License, version 2, hence
105291 the version number 2.1.]
105292
105293			    Preamble
105294
105295  The licenses for most software are designed to take away your
105296freedom to share and change it.  By contrast, the GNU General Public
105297Licenses are intended to guarantee your freedom to share and change
105298free software--to make sure the software is free for all its users.
105299
105300  This license, the Lesser General Public License, applies to some
105301specially designated software packages--typically libraries--of the
105302Free Software Foundation and other authors who decide to use it.  You
105303can use it too, but we suggest you first think carefully about whether
105304this license or the ordinary General Public License is the better
105305strategy to use in any particular case, based on the explanations below.
105306
105307  When we speak of free software, we are referring to freedom of use,
105308not price.  Our General Public Licenses are designed to make sure that
105309you have the freedom to distribute copies of free software (and charge
105310for this service if you wish); that you receive source code or can get
105311it if you want it; that you can change the software and use pieces of
105312it in new free programs; and that you are informed that you can do
105313these things.
105314
105315  To protect your rights, we need to make restrictions that forbid
105316distributors to deny you these rights or to ask you to surrender these
105317rights.  These restrictions translate to certain responsibilities for
105318you if you distribute copies of the library or if you modify it.
105319
105320  For example, if you distribute copies of the library, whether gratis
105321or for a fee, you must give the recipients all the rights that we gave
105322you.  You must make sure that they, too, receive or can get the source
105323code.  If you link other code with the library, you must provide
105324complete object files to the recipients, so that they can relink them
105325with the library after making changes to the library and recompiling
105326it.  And you must show them these terms so they know their rights.
105327
105328  We protect your rights with a two-step method: (1) we copyright the
105329library, and (2) we offer you this license, which gives you legal
105330permission to copy, distribute and/or modify the library.
105331
105332  To protect each distributor, we want to make it very clear that
105333there is no warranty for the free library.  Also, if the library is
105334modified by someone else and passed on, the recipients should know
105335that what they have is not the original version, so that the original
105336author's reputation will not be affected by problems that might be
105337introduced by others.
105338
105339  Finally, software patents pose a constant threat to the existence of
105340any free program.  We wish to make sure that a company cannot
105341effectively restrict the users of a free program by obtaining a
105342restrictive license from a patent holder.  Therefore, we insist that
105343any patent license obtained for a version of the library must be
105344consistent with the full freedom of use specified in this license.
105345
105346  Most GNU software, including some libraries, is covered by the
105347ordinary GNU General Public License.  This license, the GNU Lesser
105348General Public License, applies to certain designated libraries, and
105349is quite different from the ordinary General Public License.  We use
105350this license for certain libraries in order to permit linking those
105351libraries into non-free programs.
105352
105353  When a program is linked with a library, whether statically or using
105354a shared library, the combination of the two is legally speaking a
105355combined work, a derivative of the original library.  The ordinary
105356General Public License therefore permits such linking only if the
105357entire combination fits its criteria of freedom.  The Lesser General
105358Public License permits more lax criteria for linking other code with
105359the library.
105360
105361  We call this license the "Lesser" General Public License because it
105362does Less to protect the user's freedom than the ordinary General
105363Public License.  It also provides other free software developers Less
105364of an advantage over competing non-free programs.  These disadvantages
105365are the reason we use the ordinary General Public License for many
105366libraries.  However, the Lesser license provides advantages in certain
105367special circumstances.
105368
105369  For example, on rare occasions, there may be a special need to
105370encourage the widest possible use of a certain library, so that it becomes
105371a de-facto standard.  To achieve this, non-free programs must be
105372allowed to use the library.  A more frequent case is that a free
105373library does the same job as widely used non-free libraries.  In this
105374case, there is little to gain by limiting the free library to free
105375software only, so we use the Lesser General Public License.
105376
105377  In other cases, permission to use a particular library in non-free
105378programs enables a greater number of people to use a large body of
105379free software.  For example, permission to use the GNU C Library in
105380non-free programs enables many more people to use the whole GNU
105381operating system, as well as its variant, the GNU/Linux operating
105382system.
105383
105384  Although the Lesser General Public License is Less protective of the
105385users' freedom, it does ensure that the user of a program that is
105386linked with the Library has the freedom and the wherewithal to run
105387that program using a modified version of the Library.
105388
105389  The precise terms and conditions for copying, distribution and
105390modification follow.  Pay close attention to the difference between a
105391"work based on the library" and a "work that uses the library".  The
105392former contains code derived from the library, whereas the latter must
105393be combined with the library in order to run.
105394
105395		  GNU LESSER GENERAL PUBLIC LICENSE
105396   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
105397
105398  0. This License Agreement applies to any software library or other
105399program which contains a notice placed by the copyright holder or
105400other authorized party saying it may be distributed under the terms of
105401this Lesser General Public License (also called "this License").
105402Each licensee is addressed as "you".
105403
105404  A "library" means a collection of software functions and/or data
105405prepared so as to be conveniently linked with application programs
105406(which use some of those functions and data) to form executables.
105407
105408  The "Library", below, refers to any such software library or work
105409which has been distributed under these terms.  A "work based on the
105410Library" means either the Library or any derivative work under
105411copyright law: that is to say, a work containing the Library or a
105412portion of it, either verbatim or with modifications and/or translated
105413straightforwardly into another language.  (Hereinafter, translation is
105414included without limitation in the term "modification".)
105415
105416  "Source code" for a work means the preferred form of the work for
105417making modifications to it.  For a library, complete source code means
105418all the source code for all modules it contains, plus any associated
105419interface definition files, plus the scripts used to control compilation
105420and installation of the library.
105421
105422  Activities other than copying, distribution and modification are not
105423covered by this License; they are outside its scope.  The act of
105424running a program using the Library is not restricted, and output from
105425such a program is covered only if its contents constitute a work based
105426on the Library (independent of the use of the Library in a tool for
105427writing it).  Whether that is true depends on what the Library does
105428and what the program that uses the Library does.
105429  
105430  1. You may copy and distribute verbatim copies of the Library's
105431complete source code as you receive it, in any medium, provided that
105432you conspicuously and appropriately publish on each copy an
105433appropriate copyright notice and disclaimer of warranty; keep intact
105434all the notices that refer to this License and to the absence of any
105435warranty; and distribute a copy of this License along with the
105436Library.
105437
105438  You may charge a fee for the physical act of transferring a copy,
105439and you may at your option offer warranty protection in exchange for a
105440fee.
105441
105442  2. You may modify your copy or copies of the Library or any portion
105443of it, thus forming a work based on the Library, and copy and
105444distribute such modifications or work under the terms of Section 1
105445above, provided that you also meet all of these conditions:
105446
105447    a) The modified work must itself be a software library.
105448
105449    b) You must cause the files modified to carry prominent notices
105450    stating that you changed the files and the date of any change.
105451
105452    c) You must cause the whole of the work to be licensed at no
105453    charge to all third parties under the terms of this License.
105454
105455    d) If a facility in the modified Library refers to a function or a
105456    table of data to be supplied by an application program that uses
105457    the facility, other than as an argument passed when the facility
105458    is invoked, then you must make a good faith effort to ensure that,
105459    in the event an application does not supply such function or
105460    table, the facility still operates, and performs whatever part of
105461    its purpose remains meaningful.
105462
105463    (For example, a function in a library to compute square roots has
105464    a purpose that is entirely well-defined independent of the
105465    application.  Therefore, Subsection 2d requires that any
105466    application-supplied function or table used by this function must
105467    be optional: if the application does not supply it, the square
105468    root function must still compute square roots.)
105469
105470These requirements apply to the modified work as a whole.  If
105471identifiable sections of that work are not derived from the Library,
105472and can be reasonably considered independent and separate works in
105473themselves, then this License, and its terms, do not apply to those
105474sections when you distribute them as separate works.  But when you
105475distribute the same sections as part of a whole which is a work based
105476on the Library, the distribution of the whole must be on the terms of
105477this License, whose permissions for other licensees extend to the
105478entire whole, and thus to each and every part regardless of who wrote
105479it.
105480
105481Thus, it is not the intent of this section to claim rights or contest
105482your rights to work written entirely by you; rather, the intent is to
105483exercise the right to control the distribution of derivative or
105484collective works based on the Library.
105485
105486In addition, mere aggregation of another work not based on the Library
105487with the Library (or with a work based on the Library) on a volume of
105488a storage or distribution medium does not bring the other work under
105489the scope of this License.
105490
105491  3. You may opt to apply the terms of the ordinary GNU General Public
105492License instead of this License to a given copy of the Library.  To do
105493this, you must alter all the notices that refer to this License, so
105494that they refer to the ordinary GNU General Public License, version 2,
105495instead of to this License.  (If a newer version than version 2 of the
105496ordinary GNU General Public License has appeared, then you can specify
105497that version instead if you wish.)  Do not make any other change in
105498these notices.
105499
105500  Once this change is made in a given copy, it is irreversible for
105501that copy, so the ordinary GNU General Public License applies to all
105502subsequent copies and derivative works made from that copy.
105503
105504  This option is useful when you wish to copy part of the code of
105505the Library into a program that is not a library.
105506
105507  4. You may copy and distribute the Library (or a portion or
105508derivative of it, under Section 2) in object code or executable form
105509under the terms of Sections 1 and 2 above provided that you accompany
105510it with the complete corresponding machine-readable source code, which
105511must be distributed under the terms of Sections 1 and 2 above on a
105512medium customarily used for software interchange.
105513
105514  If distribution of object code is made by offering access to copy
105515from a designated place, then offering equivalent access to copy the
105516source code from the same place satisfies the requirement to
105517distribute the source code, even though third parties are not
105518compelled to copy the source along with the object code.
105519
105520  5. A program that contains no derivative of any portion of the
105521Library, but is designed to work with the Library by being compiled or
105522linked with it, is called a "work that uses the Library".  Such a
105523work, in isolation, is not a derivative work of the Library, and
105524therefore falls outside the scope of this License.
105525
105526  However, linking a "work that uses the Library" with the Library
105527creates an executable that is a derivative of the Library (because it
105528contains portions of the Library), rather than a "work that uses the
105529library".  The executable is therefore covered by this License.
105530Section 6 states terms for distribution of such executables.
105531
105532  When a "work that uses the Library" uses material from a header file
105533that is part of the Library, the object code for the work may be a
105534derivative work of the Library even though the source code is not.
105535Whether this is true is especially significant if the work can be
105536linked without the Library, or if the work is itself a library.  The
105537threshold for this to be true is not precisely defined by law.
105538
105539  If such an object file uses only numerical parameters, data
105540structure layouts and accessors, and small macros and small inline
105541functions (ten lines or less in length), then the use of the object
105542file is unrestricted, regardless of whether it is legally a derivative
105543work.  (Executables containing this object code plus portions of the
105544Library will still fall under Section 6.)
105545
105546  Otherwise, if the work is a derivative of the Library, you may
105547distribute the object code for the work under the terms of Section 6.
105548Any executables containing that work also fall under Section 6,
105549whether or not they are linked directly with the Library itself.
105550
105551  6. As an exception to the Sections above, you may also combine or
105552link a "work that uses the Library" with the Library to produce a
105553work containing portions of the Library, and distribute that work
105554under terms of your choice, provided that the terms permit
105555modification of the work for the customer's own use and reverse
105556engineering for debugging such modifications.
105557
105558  You must give prominent notice with each copy of the work that the
105559Library is used in it and that the Library and its use are covered by
105560this License.  You must supply a copy of this License.  If the work
105561during execution displays copyright notices, you must include the
105562copyright notice for the Library among them, as well as a reference
105563directing the user to the copy of this License.  Also, you must do one
105564of these things:
105565
105566    a) Accompany the work with the complete corresponding
105567    machine-readable source code for the Library including whatever
105568    changes were used in the work (which must be distributed under
105569    Sections 1 and 2 above); and, if the work is an executable linked
105570    with the Library, with the complete machine-readable "work that
105571    uses the Library", as object code and/or source code, so that the
105572    user can modify the Library and then relink to produce a modified
105573    executable containing the modified Library.  (It is understood
105574    that the user who changes the contents of definitions files in the
105575    Library will not necessarily be able to recompile the application
105576    to use the modified definitions.)
105577
105578    b) Use a suitable shared library mechanism for linking with the
105579    Library.  A suitable mechanism is one that (1) uses at run time a
105580    copy of the library already present on the user's computer system,
105581    rather than copying library functions into the executable, and (2)
105582    will operate properly with a modified version of the library, if
105583    the user installs one, as long as the modified version is
105584    interface-compatible with the version that the work was made with.
105585
105586    c) Accompany the work with a written offer, valid for at
105587    least three years, to give the same user the materials
105588    specified in Subsection 6a, above, for a charge no more
105589    than the cost of performing this distribution.
105590
105591    d) If distribution of the work is made by offering access to copy
105592    from a designated place, offer equivalent access to copy the above
105593    specified materials from the same place.
105594
105595    e) Verify that the user has already received a copy of these
105596    materials or that you have already sent this user a copy.
105597
105598  For an executable, the required form of the "work that uses the
105599Library" must include any data and utility programs needed for
105600reproducing the executable from it.  However, as a special exception,
105601the materials to be distributed need not include anything that is
105602normally distributed (in either source or binary form) with the major
105603components (compiler, kernel, and so on) of the operating system on
105604which the executable runs, unless that component itself accompanies
105605the executable.
105606
105607  It may happen that this requirement contradicts the license
105608restrictions of other proprietary libraries that do not normally
105609accompany the operating system.  Such a contradiction means you cannot
105610use both them and the Library together in an executable that you
105611distribute.
105612
105613  7. You may place library facilities that are a work based on the
105614Library side-by-side in a single library together with other library
105615facilities not covered by this License, and distribute such a combined
105616library, provided that the separate distribution of the work based on
105617the Library and of the other library facilities is otherwise
105618permitted, and provided that you do these two things:
105619
105620    a) Accompany the combined library with a copy of the same work
105621    based on the Library, uncombined with any other library
105622    facilities.  This must be distributed under the terms of the
105623    Sections above.
105624
105625    b) Give prominent notice with the combined library of the fact
105626    that part of it is a work based on the Library, and explaining
105627    where to find the accompanying uncombined form of the same work.
105628
105629  8. You may not copy, modify, sublicense, link with, or distribute
105630the Library except as expressly provided under this License.  Any
105631attempt otherwise to copy, modify, sublicense, link with, or
105632distribute the Library is void, and will automatically terminate your
105633rights under this License.  However, parties who have received copies,
105634or rights, from you under this License will not have their licenses
105635terminated so long as such parties remain in full compliance.
105636
105637  9. You are not required to accept this License, since you have not
105638signed it.  However, nothing else grants you permission to modify or
105639distribute the Library or its derivative works.  These actions are
105640prohibited by law if you do not accept this License.  Therefore, by
105641modifying or distributing the Library (or any work based on the
105642Library), you indicate your acceptance of this License to do so, and
105643all its terms and conditions for copying, distributing or modifying
105644the Library or works based on it.
105645
105646  10. Each time you redistribute the Library (or any work based on the
105647Library), the recipient automatically receives a license from the
105648original licensor to copy, distribute, link with or modify the Library
105649subject to these terms and conditions.  You may not impose any further
105650restrictions on the recipients' exercise of the rights granted herein.
105651You are not responsible for enforcing compliance by third parties with
105652this License.
105653
105654  11. If, as a consequence of a court judgment or allegation of patent
105655infringement or for any other reason (not limited to patent issues),
105656conditions are imposed on you (whether by court order, agreement or
105657otherwise) that contradict the conditions of this License, they do not
105658excuse you from the conditions of this License.  If you cannot
105659distribute so as to satisfy simultaneously your obligations under this
105660License and any other pertinent obligations, then as a consequence you
105661may not distribute the Library at all.  For example, if a patent
105662license would not permit royalty-free redistribution of the Library by
105663all those who receive copies directly or indirectly through you, then
105664the only way you could satisfy both it and this License would be to
105665refrain entirely from distribution of the Library.
105666
105667If any portion of this section is held invalid or unenforceable under any
105668particular circumstance, the balance of the section is intended to apply,
105669and the section as a whole is intended to apply in other circumstances.
105670
105671It is not the purpose of this section to induce you to infringe any
105672patents or other property right claims or to contest validity of any
105673such claims; this section has the sole purpose of protecting the
105674integrity of the free software distribution system which is
105675implemented by public license practices.  Many people have made
105676generous contributions to the wide range of software distributed
105677through that system in reliance on consistent application of that
105678system; it is up to the author/donor to decide if he or she is willing
105679to distribute software through any other system and a licensee cannot
105680impose that choice.
105681
105682This section is intended to make thoroughly clear what is believed to
105683be a consequence of the rest of this License.
105684
105685  12. If the distribution and/or use of the Library is restricted in
105686certain countries either by patents or by copyrighted interfaces, the
105687original copyright holder who places the Library under this License may add
105688an explicit geographical distribution limitation excluding those countries,
105689so that distribution is permitted only in or among countries not thus
105690excluded.  In such case, this License incorporates the limitation as if
105691written in the body of this License.
105692
105693  13. The Free Software Foundation may publish revised and/or new
105694versions of the Lesser General Public License from time to time.
105695Such new versions will be similar in spirit to the present version,
105696but may differ in detail to address new problems or concerns.
105697
105698Each version is given a distinguishing version number.  If the Library
105699specifies a version number of this License which applies to it and
105700"any later version", you have the option of following the terms and
105701conditions either of that version or of any later version published by
105702the Free Software Foundation.  If the Library does not specify a
105703license version number, you may choose any version ever published by
105704the Free Software Foundation.
105705
105706  14. If you wish to incorporate parts of the Library into other free
105707programs whose distribution conditions are incompatible with these,
105708write to the author to ask for permission.  For software which is
105709copyrighted by the Free Software Foundation, write to the Free
105710Software Foundation; we sometimes make exceptions for this.  Our
105711decision will be guided by the two goals of preserving the free status
105712of all derivatives of our free software and of promoting the sharing
105713and reuse of software generally.
105714
105715			    NO WARRANTY
105716
105717  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
105718WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
105719EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
105720OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
105721KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
105722IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
105723PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
105724LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
105725THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
105726
105727  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
105728WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
105729AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
105730FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
105731CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
105732LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
105733RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
105734FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
105735SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
105736DAMAGES.
105737
105738		     END OF TERMS AND CONDITIONS
105739
105740           How to Apply These Terms to Your New Libraries
105741
105742  If you develop a new library, and you want it to be of the greatest
105743possible use to the public, we recommend making it free software that
105744everyone can redistribute and change.  You can do so by permitting
105745redistribution under these terms (or, alternatively, under the terms of the
105746ordinary General Public License).
105747
105748  To apply these terms, attach the following notices to the library.  It is
105749safest to attach them to the start of each source file to most effectively
105750convey the exclusion of warranty; and each file should have at least the
105751"copyright" line and a pointer to where the full notice is found.
105752
105753    <one line to give the library's name and a brief idea of what it does.>
105754    Copyright (C) <year>  <name of author>
105755
105756    This library is free software; you can redistribute it and/or
105757    modify it under the terms of the GNU Lesser General Public
105758    License as published by the Free Software Foundation; either
105759    version 2.1 of the License, or (at your option) any later version.
105760
105761    This library is distributed in the hope that it will be useful,
105762    but WITHOUT ANY WARRANTY; without even the implied warranty of
105763    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
105764    Lesser General Public License for more details.
105765
105766    You should have received a copy of the GNU Lesser General Public
105767    License along with this library; if not, write to the Free Software
105768    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
105769
105770Also add information on how to contact you by electronic and paper mail.
105771
105772You should also get your employer (if you work as a programmer) or your
105773school, if any, to sign a "copyright disclaimer" for the library, if
105774necessary.  Here is a sample; alter the names:
105775
105776  Yoyodyne, Inc., hereby disclaims all copyright interest in the
105777  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
105778
105779  <signature of Ty Coon>, 1 April 1990
105780  Ty Coon, President of Vice
105781
105782That's all there is to it!
105783
105784
105785<?php
105786/**
105787 * File/Directory manipulation
105788 *
105789 * PHP versions 4 and 5
105790 *
105791 * @category   pear
105792 * @package    System
105793 * @author     Tomas V.V.Cox <cox@idecnet.com>
105794 * @copyright  1997-2009 The Authors
105795 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
105796 * @version    CVS: $Id: System.php 313024 2011-07-06 19:51:24Z dufuz $
105797 * @link       http://pear.php.net/package/PEAR
105798 * @since      File available since Release 0.1
105799 */
105800
105801/**
105802 * base class
105803 */
105804require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
105805require_once 'phar://install-pear-nozlib.phar/' . 'Console/Getopt.php';
105806
105807$GLOBALS['_System_temp_files'] = array();
105808
105809/**
105810* System offers cross plattform compatible system functions
105811*
105812* Static functions for different operations. Should work under
105813* Unix and Windows. The names and usage has been taken from its respectively
105814* GNU commands. The functions will return (bool) false on error and will
105815* trigger the error with the PHP trigger_error() function (you can silence
105816* the error by prefixing a '@' sign after the function call, but this
105817* is not recommended practice.  Instead use an error handler with
105818* {@link set_error_handler()}).
105819*
105820* Documentation on this class you can find in:
105821* http://pear.php.net/manual/
105822*
105823* Example usage:
105824* if (!@System::rm('-r file1 dir1')) {
105825*    print "could not delete file1 or dir1";
105826* }
105827*
105828* In case you need to to pass file names with spaces,
105829* pass the params as an array:
105830*
105831* System::rm(array('-r', $file1, $dir1));
105832*
105833* @category   pear
105834* @package    System
105835* @author     Tomas V.V. Cox <cox@idecnet.com>
105836* @copyright  1997-2006 The PHP Group
105837* @license    http://opensource.org/licenses/bsd-license.php New BSD License
105838* @version    Release: 1.9.4
105839* @link       http://pear.php.net/package/PEAR
105840* @since      Class available since Release 0.1
105841* @static
105842*/
105843class System
105844{
105845    /**
105846     * returns the commandline arguments of a function
105847     *
105848     * @param    string  $argv           the commandline
105849     * @param    string  $short_options  the allowed option short-tags
105850     * @param    string  $long_options   the allowed option long-tags
105851     * @return   array   the given options and there values
105852     * @static
105853     * @access private
105854     */
105855    function _parseArgs($argv, $short_options, $long_options = null)
105856    {
105857        if (!is_array($argv) && $argv !== null) {
105858            // Find all items, quoted or otherwise
105859            preg_match_all("/(?:[\"'])(.*?)(?:['\"])|([^\s]+)/", $argv, $av);
105860            $argv = $av[1];
105861            foreach ($av[2] as $k => $a) {
105862                if (empty($a)) {
105863                    continue;
105864                }
105865                $argv[$k] = trim($a) ;
105866            }
105867        }
105868        return Console_Getopt::getopt2($argv, $short_options, $long_options);
105869    }
105870
105871    /**
105872     * Output errors with PHP trigger_error(). You can silence the errors
105873     * with prefixing a "@" sign to the function call: @System::mkdir(..);
105874     *
105875     * @param mixed $error a PEAR error or a string with the error message
105876     * @return bool false
105877     * @static
105878     * @access private
105879     */
105880    function raiseError($error)
105881    {
105882        if (PEAR::isError($error)) {
105883            $error = $error->getMessage();
105884        }
105885        trigger_error($error, E_USER_WARNING);
105886        return false;
105887    }
105888
105889    /**
105890     * Creates a nested array representing the structure of a directory
105891     *
105892     * System::_dirToStruct('dir1', 0) =>
105893     *   Array
105894     *    (
105895     *    [dirs] => Array
105896     *        (
105897     *            [0] => dir1
105898     *        )
105899     *
105900     *    [files] => Array
105901     *        (
105902     *            [0] => dir1/file2
105903     *            [1] => dir1/file3
105904     *        )
105905     *    )
105906     * @param    string  $sPath      Name of the directory
105907     * @param    integer $maxinst    max. deep of the lookup
105908     * @param    integer $aktinst    starting deep of the lookup
105909     * @param    bool    $silent     if true, do not emit errors.
105910     * @return   array   the structure of the dir
105911     * @static
105912     * @access   private
105913     */
105914    function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false)
105915    {
105916        $struct = array('dirs' => array(), 'files' => array());
105917        if (($dir = @opendir($sPath)) === false) {
105918            if (!$silent) {
105919                System::raiseError("Could not open dir $sPath");
105920            }
105921            return $struct; // XXX could not open error
105922        }
105923
105924        $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?
105925        $list = array();
105926        while (false !== ($file = readdir($dir))) {
105927            if ($file != '.' && $file != '..') {
105928                $list[] = $file;
105929            }
105930        }
105931
105932        closedir($dir);
105933        natsort($list);
105934        if ($aktinst < $maxinst || $maxinst == 0) {
105935            foreach ($list as $val) {
105936                $path = $sPath . DIRECTORY_SEPARATOR . $val;
105937                if (is_dir($path) && !is_link($path)) {
105938                    $tmp    = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent);
105939                    $struct = array_merge_recursive($struct, $tmp);
105940                } else {
105941                    $struct['files'][] = $path;
105942                }
105943            }
105944        }
105945
105946        return $struct;
105947    }
105948
105949    /**
105950     * Creates a nested array representing the structure of a directory and files
105951     *
105952     * @param    array $files Array listing files and dirs
105953     * @return   array
105954     * @static
105955     * @see System::_dirToStruct()
105956     */
105957    function _multipleToStruct($files)
105958    {
105959        $struct = array('dirs' => array(), 'files' => array());
105960        settype($files, 'array');
105961        foreach ($files as $file) {
105962            if (is_dir($file) && !is_link($file)) {
105963                $tmp    = System::_dirToStruct($file, 0);
105964                $struct = array_merge_recursive($tmp, $struct);
105965            } else {
105966                if (!in_array($file, $struct['files'])) {
105967                    $struct['files'][] = $file;
105968                }
105969            }
105970        }
105971        return $struct;
105972    }
105973
105974    /**
105975     * The rm command for removing files.
105976     * Supports multiple files and dirs and also recursive deletes
105977     *
105978     * @param    string  $args   the arguments for rm
105979     * @return   mixed   PEAR_Error or true for success
105980     * @static
105981     * @access   public
105982     */
105983    function rm($args)
105984    {
105985        $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-)
105986        if (PEAR::isError($opts)) {
105987            return System::raiseError($opts);
105988        }
105989        foreach ($opts[0] as $opt) {
105990            if ($opt[0] == 'r') {
105991                $do_recursive = true;
105992            }
105993        }
105994        $ret = true;
105995        if (isset($do_recursive)) {
105996            $struct = System::_multipleToStruct($opts[1]);
105997            foreach ($struct['files'] as $file) {
105998                if (!@unlink($file)) {
105999                    $ret = false;
106000                }
106001            }
106002
106003            rsort($struct['dirs']);
106004            foreach ($struct['dirs'] as $dir) {
106005                if (!@rmdir($dir)) {
106006                    $ret = false;
106007                }
106008            }
106009        } else {
106010            foreach ($opts[1] as $file) {
106011                $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
106012                if (!@$delete($file)) {
106013                    $ret = false;
106014                }
106015            }
106016        }
106017        return $ret;
106018    }
106019
106020    /**
106021     * Make directories.
106022     *
106023     * The -p option will create parent directories
106024     * @param    string  $args    the name of the director(y|ies) to create
106025     * @return   bool    True for success
106026     * @static
106027     * @access   public
106028     */
106029    function mkDir($args)
106030    {
106031        $opts = System::_parseArgs($args, 'pm:');
106032        if (PEAR::isError($opts)) {
106033            return System::raiseError($opts);
106034        }
106035
106036        $mode = 0777; // default mode
106037        foreach ($opts[0] as $opt) {
106038            if ($opt[0] == 'p') {
106039                $create_parents = true;
106040            } elseif ($opt[0] == 'm') {
106041                // if the mode is clearly an octal number (starts with 0)
106042                // convert it to decimal
106043                if (strlen($opt[1]) && $opt[1]{0} == '0') {
106044                    $opt[1] = octdec($opt[1]);
106045                } else {
106046                    // convert to int
106047                    $opt[1] += 0;
106048                }
106049                $mode = $opt[1];
106050            }
106051        }
106052
106053        $ret = true;
106054        if (isset($create_parents)) {
106055            foreach ($opts[1] as $dir) {
106056                $dirstack = array();
106057                while ((!file_exists($dir) || !is_dir($dir)) &&
106058                        $dir != DIRECTORY_SEPARATOR) {
106059                    array_unshift($dirstack, $dir);
106060                    $dir = dirname($dir);
106061                }
106062
106063                while ($newdir = array_shift($dirstack)) {
106064                    if (!is_writeable(dirname($newdir))) {
106065                        $ret = false;
106066                        break;
106067                    }
106068
106069                    if (!mkdir($newdir, $mode)) {
106070                        $ret = false;
106071                    }
106072                }
106073            }
106074        } else {
106075            foreach($opts[1] as $dir) {
106076                if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) {
106077                    $ret = false;
106078                }
106079            }
106080        }
106081
106082        return $ret;
106083    }
106084
106085    /**
106086     * Concatenate files
106087     *
106088     * Usage:
106089     * 1) $var = System::cat('sample.txt test.txt');
106090     * 2) System::cat('sample.txt test.txt > final.txt');
106091     * 3) System::cat('sample.txt test.txt >> final.txt');
106092     *
106093     * Note: as the class use fopen, urls should work also (test that)
106094     *
106095     * @param    string  $args   the arguments
106096     * @return   boolean true on success
106097     * @static
106098     * @access   public
106099     */
106100    function &cat($args)
106101    {
106102        $ret = null;
106103        $files = array();
106104        if (!is_array($args)) {
106105            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
106106        }
106107
106108        $count_args = count($args);
106109        for ($i = 0; $i < $count_args; $i++) {
106110            if ($args[$i] == '>') {
106111                $mode = 'wb';
106112                $outputfile = $args[$i+1];
106113                break;
106114            } elseif ($args[$i] == '>>') {
106115                $mode = 'ab+';
106116                $outputfile = $args[$i+1];
106117                break;
106118            } else {
106119                $files[] = $args[$i];
106120            }
106121        }
106122        $outputfd = false;
106123        if (isset($mode)) {
106124            if (!$outputfd = fopen($outputfile, $mode)) {
106125                $err = System::raiseError("Could not open $outputfile");
106126                return $err;
106127            }
106128            $ret = true;
106129        }
106130        foreach ($files as $file) {
106131            if (!$fd = fopen($file, 'r')) {
106132                System::raiseError("Could not open $file");
106133                continue;
106134            }
106135            while ($cont = fread($fd, 2048)) {
106136                if (is_resource($outputfd)) {
106137                    fwrite($outputfd, $cont);
106138                } else {
106139                    $ret .= $cont;
106140                }
106141            }
106142            fclose($fd);
106143        }
106144        if (is_resource($outputfd)) {
106145            fclose($outputfd);
106146        }
106147        return $ret;
106148    }
106149
106150    /**
106151     * Creates temporary files or directories. This function will remove
106152     * the created files when the scripts finish its execution.
106153     *
106154     * Usage:
106155     *   1) $tempfile = System::mktemp("prefix");
106156     *   2) $tempdir  = System::mktemp("-d prefix");
106157     *   3) $tempfile = System::mktemp();
106158     *   4) $tempfile = System::mktemp("-t /var/tmp prefix");
106159     *
106160     * prefix -> The string that will be prepended to the temp name
106161     *           (defaults to "tmp").
106162     * -d     -> A temporary dir will be created instead of a file.
106163     * -t     -> The target dir where the temporary (file|dir) will be created. If
106164     *           this param is missing by default the env vars TMP on Windows or
106165     *           TMPDIR in Unix will be used. If these vars are also missing
106166     *           c:\windows\temp or /tmp will be used.
106167     *
106168     * @param   string  $args  The arguments
106169     * @return  mixed   the full path of the created (file|dir) or false
106170     * @see System::tmpdir()
106171     * @static
106172     * @access  public
106173     */
106174    function mktemp($args = null)
106175    {
106176        static $first_time = true;
106177        $opts = System::_parseArgs($args, 't:d');
106178        if (PEAR::isError($opts)) {
106179            return System::raiseError($opts);
106180        }
106181
106182        foreach ($opts[0] as $opt) {
106183            if ($opt[0] == 'd') {
106184                $tmp_is_dir = true;
106185            } elseif ($opt[0] == 't') {
106186                $tmpdir = $opt[1];
106187            }
106188        }
106189
106190        $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
106191        if (!isset($tmpdir)) {
106192            $tmpdir = System::tmpdir();
106193        }
106194
106195        if (!System::mkDir(array('-p', $tmpdir))) {
106196            return false;
106197        }
106198
106199        $tmp = tempnam($tmpdir, $prefix);
106200        if (isset($tmp_is_dir)) {
106201            unlink($tmp); // be careful possible race condition here
106202            if (!mkdir($tmp, 0700)) {
106203                return System::raiseError("Unable to create temporary directory $tmpdir");
106204            }
106205        }
106206
106207        $GLOBALS['_System_temp_files'][] = $tmp;
106208        if (isset($tmp_is_dir)) {
106209            //$GLOBALS['_System_temp_files'][] = dirname($tmp);
106210        }
106211
106212        if ($first_time) {
106213            PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
106214            $first_time = false;
106215        }
106216
106217        return $tmp;
106218    }
106219
106220    /**
106221     * Remove temporary files created my mkTemp. This function is executed
106222     * at script shutdown time
106223     *
106224     * @static
106225     * @access private
106226     */
106227    function _removeTmpFiles()
106228    {
106229        if (count($GLOBALS['_System_temp_files'])) {
106230            $delete = $GLOBALS['_System_temp_files'];
106231            array_unshift($delete, '-r');
106232            System::rm($delete);
106233            $GLOBALS['_System_temp_files'] = array();
106234        }
106235    }
106236
106237    /**
106238     * Get the path of the temporal directory set in the system
106239     * by looking in its environments variables.
106240     * Note: php.ini-recommended removes the "E" from the variables_order setting,
106241     * making unavaible the $_ENV array, that s why we do tests with _ENV
106242     *
106243     * @static
106244     * @return string The temporary directory on the system
106245     */
106246    function tmpdir()
106247    {
106248        if (OS_WINDOWS) {
106249            if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
106250                return $var;
106251            }
106252            if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
106253                return $var;
106254            }
106255            if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) {
106256                return $var;
106257            }
106258            if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
106259                return $var;
106260            }
106261            return getenv('SystemRoot') . '\temp';
106262        }
106263        if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
106264            return $var;
106265        }
106266        return realpath('/tmp');
106267    }
106268
106269    /**
106270     * The "which" command (show the full path of a command)
106271     *
106272     * @param string $program The command to search for
106273     * @param mixed  $fallback Value to return if $program is not found
106274     *
106275     * @return mixed A string with the full path or false if not found
106276     * @static
106277     * @author Stig Bakken <ssb@php.net>
106278     */
106279    function which($program, $fallback = false)
106280    {
106281        // enforce API
106282        if (!is_string($program) || '' == $program) {
106283            return $fallback;
106284        }
106285
106286        // full path given
106287        if (basename($program) != $program) {
106288            $path_elements[] = dirname($program);
106289            $program = basename($program);
106290        } else {
106291            // Honor safe mode
106292            if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) {
106293                $path = getenv('PATH');
106294                if (!$path) {
106295                    $path = getenv('Path'); // some OSes are just stupid enough to do this
106296                }
106297            }
106298            $path_elements = explode(PATH_SEPARATOR, $path);
106299        }
106300
106301        if (OS_WINDOWS) {
106302            $exe_suffixes = getenv('PATHEXT')
106303                                ? explode(PATH_SEPARATOR, getenv('PATHEXT'))
106304                                : array('.exe','.bat','.cmd','.com');
106305            // allow passing a command.exe param
106306            if (strpos($program, '.') !== false) {
106307                array_unshift($exe_suffixes, '');
106308            }
106309            // is_executable() is not available on windows for PHP4
106310            $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file';
106311        } else {
106312            $exe_suffixes = array('');
106313            $pear_is_executable = 'is_executable';
106314        }
106315
106316        foreach ($exe_suffixes as $suff) {
106317            foreach ($path_elements as $dir) {
106318                $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
106319                if (@$pear_is_executable($file)) {
106320                    return $file;
106321                }
106322            }
106323        }
106324        return $fallback;
106325    }
106326
106327    /**
106328     * The "find" command
106329     *
106330     * Usage:
106331     *
106332     * System::find($dir);
106333     * System::find("$dir -type d");
106334     * System::find("$dir -type f");
106335     * System::find("$dir -name *.php");
106336     * System::find("$dir -name *.php -name *.htm*");
106337     * System::find("$dir -maxdepth 1");
106338     *
106339     * Params implmented:
106340     * $dir            -> Start the search at this directory
106341     * -type d         -> return only directories
106342     * -type f         -> return only files
106343     * -maxdepth <n>   -> max depth of recursion
106344     * -name <pattern> -> search pattern (bash style). Multiple -name param allowed
106345     *
106346     * @param  mixed Either array or string with the command line
106347     * @return array Array of found files
106348     * @static
106349     *
106350     */
106351    function find($args)
106352    {
106353        if (!is_array($args)) {
106354            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
106355        }
106356        $dir = realpath(array_shift($args));
106357        if (!$dir) {
106358            return array();
106359        }
106360        $patterns = array();
106361        $depth = 0;
106362        $do_files = $do_dirs = true;
106363        $args_count = count($args);
106364        for ($i = 0; $i < $args_count; $i++) {
106365            switch ($args[$i]) {
106366                case '-type':
106367                    if (in_array($args[$i+1], array('d', 'f'))) {
106368                        if ($args[$i+1] == 'd') {
106369                            $do_files = false;
106370                        } else {
106371                            $do_dirs = false;
106372                        }
106373                    }
106374                    $i++;
106375                    break;
106376                case '-name':
106377                    $name = preg_quote($args[$i+1], '#');
106378                    // our magic characters ? and * have just been escaped,
106379                    // so now we change the escaped versions to PCRE operators
106380                    $name = strtr($name, array('\?' => '.', '\*' => '.*'));
106381                    $patterns[] = '('.$name.')';
106382                    $i++;
106383                    break;
106384                case '-maxdepth':
106385                    $depth = $args[$i+1];
106386                    break;
106387            }
106388        }
106389        $path = System::_dirToStruct($dir, $depth, 0, true);
106390        if ($do_files && $do_dirs) {
106391            $files = array_merge($path['files'], $path['dirs']);
106392        } elseif ($do_dirs) {
106393            $files = $path['dirs'];
106394        } else {
106395            $files = $path['files'];
106396        }
106397        if (count($patterns)) {
106398            $dsq = preg_quote(DIRECTORY_SEPARATOR, '#');
106399            $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#';
106400            $ret = array();
106401            $files_count = count($files);
106402            for ($i = 0; $i < $files_count; $i++) {
106403                // only search in the part of the file below the current directory
106404                $filepart = basename($files[$i]);
106405                if (preg_match($pattern, $filepart)) {
106406                    $ret[] = $files[$i];
106407                }
106408            }
106409            return $ret;
106410        }
106411        return $files;
106412    }
106413}<?php
106414
106415/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
106416
106417/**
106418 * XML_Util
106419 *
106420 * XML Utilities package
106421 * 
106422 * PHP versions 4 and 5
106423 *
106424 * LICENSE:
106425 *
106426 * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
106427 * All rights reserved.
106428 *
106429 * Redistribution and use in source and binary forms, with or without
106430 * modification, are permitted provided that the following conditions
106431 * are met:
106432 *
106433 *    * Redistributions of source code must retain the above copyright
106434 *      notice, this list of conditions and the following disclaimer.
106435 *    * Redistributions in binary form must reproduce the above copyright
106436 *      notice, this list of conditions and the following disclaimer in the
106437 *      documentation and/or other materials provided with the distribution.
106438 *    * The name of the author may not be used to endorse or promote products
106439 *      derived from this software without specific prior written permission.
106440 *
106441 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
106442 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
106443 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
106444 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
106445 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
106446 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
106447 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
106448 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
106449 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
106450 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
106451 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
106452 *
106453 * @category  XML
106454 * @package   XML_Util
106455 * @author    Stephan Schmidt <schst@php.net>
106456 * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
106457 * @license   http://opensource.org/licenses/bsd-license New BSD License
106458 * @version   CVS: $Id: Util.php,v 1.38 2008/11/13 00:03:38 ashnazg Exp $
106459 * @link      http://pear.php.net/package/XML_Util
106460 */
106461
106462/**
106463 * error code for invalid chars in XML name
106464 */
106465define('XML_UTIL_ERROR_INVALID_CHARS', 51);
106466
106467/**
106468 * error code for invalid chars in XML name
106469 */
106470define('XML_UTIL_ERROR_INVALID_START', 52);
106471
106472/**
106473 * error code for non-scalar tag content
106474 */
106475define('XML_UTIL_ERROR_NON_SCALAR_CONTENT', 60);
106476
106477/**
106478 * error code for missing tag name
106479 */
106480define('XML_UTIL_ERROR_NO_TAG_NAME', 61);
106481
106482/**
106483 * replace XML entities
106484 */
106485define('XML_UTIL_REPLACE_ENTITIES', 1);
106486
106487/**
106488 * embedd content in a CData Section
106489 */
106490define('XML_UTIL_CDATA_SECTION', 5);
106491
106492/**
106493 * do not replace entitites
106494 */
106495define('XML_UTIL_ENTITIES_NONE', 0);
106496
106497/**
106498 * replace all XML entitites
106499 * This setting will replace <, >, ", ' and &
106500 */
106501define('XML_UTIL_ENTITIES_XML', 1);
106502
106503/**
106504 * replace only required XML entitites
106505 * This setting will replace <, " and &
106506 */
106507define('XML_UTIL_ENTITIES_XML_REQUIRED', 2);
106508
106509/**
106510 * replace HTML entitites
106511 * @link http://www.php.net/htmlentities
106512 */
106513define('XML_UTIL_ENTITIES_HTML', 3);
106514
106515/**
106516 * Collapse all empty tags.
106517 */
106518define('XML_UTIL_COLLAPSE_ALL', 1);
106519
106520/**
106521 * Collapse only empty XHTML tags that have no end tag.
106522 */
106523define('XML_UTIL_COLLAPSE_XHTML_ONLY', 2);
106524
106525/**
106526 * utility class for working with XML documents
106527 *
106528
106529 * @category  XML
106530 * @package   XML_Util
106531 * @author    Stephan Schmidt <schst@php.net>
106532 * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
106533 * @license   http://opensource.org/licenses/bsd-license New BSD License
106534 * @version   Release: 1.2.1
106535 * @link      http://pear.php.net/package/XML_Util
106536 */
106537class XML_Util
106538{
106539    /**
106540     * return API version
106541     *
106542     * @return string $version API version
106543     * @access public
106544     * @static
106545     */
106546    function apiVersion()
106547    {
106548        return '1.1';
106549    }
106550
106551    /**
106552     * replace XML entities
106553     *
106554     * With the optional second parameter, you may select, which
106555     * entities should be replaced.
106556     *
106557     * <code>
106558     * require_once 'XML/Util.php';
106559     *
106560     * // replace XML entites:
106561     * $string = XML_Util::replaceEntities('This string contains < & >.');
106562     * </code>
106563     *
106564     * With the optional third parameter, you may pass the character encoding
106565     * <code>
106566     * require_once 'XML/Util.php';
106567     *
106568     * // replace XML entites in UTF-8:
106569     * $string = XML_Util::replaceEntities(
106570     *     'This string contains < & > as well as ä, ö, ß, à and ê',
106571     *     XML_UTIL_ENTITIES_HTML,
106572     *     'UTF-8'
106573     * );
106574     * </code>
106575     *
106576     * @param string $string          string where XML special chars 
106577     *                                should be replaced
106578     * @param int    $replaceEntities setting for entities in attribute values 
106579     *                                (one of XML_UTIL_ENTITIES_XML, 
106580     *                                XML_UTIL_ENTITIES_XML_REQUIRED, 
106581     *                                XML_UTIL_ENTITIES_HTML)
106582     * @param string $encoding        encoding value (if any)...
106583     *                                must be a valid encoding as determined
106584     *                                by the htmlentities() function
106585     *
106586     * @return string string with replaced chars
106587     * @access public
106588     * @static
106589     * @see reverseEntities()
106590     */
106591    function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML,
106592        $encoding = 'ISO-8859-1')
106593    {
106594        switch ($replaceEntities) {
106595        case XML_UTIL_ENTITIES_XML:
106596            return strtr($string, array(
106597                '&'  => '&amp;',
106598                '>'  => '&gt;',
106599                '<'  => '&lt;',
106600                '"'  => '&quot;',
106601                '\'' => '&apos;' ));
106602            break;
106603        case XML_UTIL_ENTITIES_XML_REQUIRED:
106604            return strtr($string, array(
106605                '&' => '&amp;',
106606                '<' => '&lt;',
106607                '"' => '&quot;' ));
106608            break;
106609        case XML_UTIL_ENTITIES_HTML:
106610            return htmlentities($string, ENT_COMPAT, $encoding);
106611            break;
106612        }
106613        return $string;
106614    }
106615
106616    /**
106617     * reverse XML entities
106618     *
106619     * With the optional second parameter, you may select, which
106620     * entities should be reversed.
106621     *
106622     * <code>
106623     * require_once 'XML/Util.php';
106624     *
106625     * // reverse XML entites:
106626     * $string = XML_Util::reverseEntities('This string contains &lt; &amp; &gt;.');
106627     * </code>
106628     *
106629     * With the optional third parameter, you may pass the character encoding
106630     * <code>
106631     * require_once 'XML/Util.php';
106632     *
106633     * // reverse XML entites in UTF-8:
106634     * $string = XML_Util::reverseEntities(
106635     *     'This string contains &lt; &amp; &gt; as well as'
106636     *     . ' &auml;, &ouml;, &szlig;, &agrave; and &ecirc;',
106637     *     XML_UTIL_ENTITIES_HTML,
106638     *     'UTF-8'
106639     * );
106640     * </code>
106641     *
106642     * @param string $string          string where XML special chars 
106643     *                                should be replaced
106644     * @param int    $replaceEntities setting for entities in attribute values 
106645     *                                (one of XML_UTIL_ENTITIES_XML, 
106646     *                                XML_UTIL_ENTITIES_XML_REQUIRED, 
106647     *                                XML_UTIL_ENTITIES_HTML)
106648     * @param string $encoding        encoding value (if any)...
106649     *                                must be a valid encoding as determined
106650     *                                by the html_entity_decode() function
106651     *
106652     * @return string string with replaced chars
106653     * @access public
106654     * @static
106655     * @see replaceEntities()
106656     */
106657    function reverseEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML,
106658        $encoding = 'ISO-8859-1')
106659    {
106660        switch ($replaceEntities) {
106661        case XML_UTIL_ENTITIES_XML:
106662            return strtr($string, array(
106663                '&amp;'  => '&',
106664                '&gt;'   => '>',
106665                '&lt;'   => '<',
106666                '&quot;' => '"',
106667                '&apos;' => '\'' ));
106668            break;
106669        case XML_UTIL_ENTITIES_XML_REQUIRED:
106670            return strtr($string, array(
106671                '&amp;'  => '&',
106672                '&lt;'   => '<',
106673                '&quot;' => '"' ));
106674            break;
106675        case XML_UTIL_ENTITIES_HTML:
106676            return html_entity_decode($string, ENT_COMPAT, $encoding);
106677            break;
106678        }
106679        return $string;
106680    }
106681
106682    /**
106683     * build an xml declaration
106684     *
106685     * <code>
106686     * require_once 'XML/Util.php';
106687     *
106688     * // get an XML declaration:
106689     * $xmlDecl = XML_Util::getXMLDeclaration('1.0', 'UTF-8', true);
106690     * </code>
106691     *
106692     * @param string $version    xml version
106693     * @param string $encoding   character encoding
106694     * @param bool   $standalone document is standalone (or not)
106695     *
106696     * @return string xml declaration
106697     * @access public
106698     * @static
106699     * @uses attributesToString() to serialize the attributes of the XML declaration
106700     */
106701    function getXMLDeclaration($version = '1.0', $encoding = null, 
106702        $standalone = null)
106703    {
106704        $attributes = array(
106705            'version' => $version,
106706        );
106707        // add encoding
106708        if ($encoding !== null) {
106709            $attributes['encoding'] = $encoding;
106710        }
106711        // add standalone, if specified
106712        if ($standalone !== null) {
106713            $attributes['standalone'] = $standalone ? 'yes' : 'no';
106714        }
106715
106716        return sprintf('<?xml%s?>', 
106717            XML_Util::attributesToString($attributes, false));
106718    }
106719
106720    /**
106721     * build a document type declaration
106722     *
106723     * <code>
106724     * require_once 'XML/Util.php';
106725     *
106726     * // get a doctype declaration:
106727     * $xmlDecl = XML_Util::getDocTypeDeclaration('rootTag','myDocType.dtd');
106728     * </code>
106729     *
106730     * @param string $root        name of the root tag
106731     * @param string $uri         uri of the doctype definition 
106732     *                            (or array with uri and public id)
106733     * @param string $internalDtd internal dtd entries
106734     *
106735     * @return string doctype declaration
106736     * @access public
106737     * @static
106738     * @since 0.2
106739     */
106740    function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
106741    {
106742        if (is_array($uri)) {
106743            $ref = sprintf(' PUBLIC "%s" "%s"', $uri['id'], $uri['uri']);
106744        } elseif (!empty($uri)) {
106745            $ref = sprintf(' SYSTEM "%s"', $uri);
106746        } else {
106747            $ref = '';
106748        }
106749
106750        if (empty($internalDtd)) {
106751            return sprintf('<!DOCTYPE %s%s>', $root, $ref);
106752        } else {
106753            return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
106754        }
106755    }
106756
106757    /**
106758     * create string representation of an attribute list
106759     *
106760     * <code>
106761     * require_once 'XML/Util.php';
106762     *
106763     * // build an attribute string
106764     * $att = array(
106765     *              'foo'   =>  'bar',
106766     *              'argh'  =>  'tomato'
106767     *            );
106768     *
106769     * $attList = XML_Util::attributesToString($att);
106770     * </code>
106771     *
106772     * @param array      $attributes attribute array
106773     * @param bool|array $sort       sort attribute list alphabetically, 
106774     *                               may also be an assoc array containing 
106775     *                               the keys 'sort', 'multiline', 'indent', 
106776     *                               'linebreak' and 'entities'
106777     * @param bool       $multiline  use linebreaks, if more than 
106778     *                               one attribute is given
106779     * @param string     $indent     string used for indentation of 
106780     *                               multiline attributes
106781     * @param string     $linebreak  string used for linebreaks of 
106782     *                               multiline attributes
106783     * @param int        $entities   setting for entities in attribute values 
106784     *                               (one of XML_UTIL_ENTITIES_NONE, 
106785     *                               XML_UTIL_ENTITIES_XML, 
106786     *                               XML_UTIL_ENTITIES_XML_REQUIRED, 
106787     *                               XML_UTIL_ENTITIES_HTML)
106788     *
106789     * @return string string representation of the attributes
106790     * @access public
106791     * @static
106792     * @uses replaceEntities() to replace XML entities in attribute values
106793     * @todo allow sort also to be an options array
106794     */
106795    function attributesToString($attributes, $sort = true, $multiline = false, 
106796        $indent = '    ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML)
106797    {
106798        /*
106799         * second parameter may be an array
106800         */
106801        if (is_array($sort)) {
106802            if (isset($sort['multiline'])) {
106803                $multiline = $sort['multiline'];
106804            }
106805            if (isset($sort['indent'])) {
106806                $indent = $sort['indent'];
106807            }
106808            if (isset($sort['linebreak'])) {
106809                $multiline = $sort['linebreak'];
106810            }
106811            if (isset($sort['entities'])) {
106812                $entities = $sort['entities'];
106813            }
106814            if (isset($sort['sort'])) {
106815                $sort = $sort['sort'];
106816            } else {
106817                $sort = true;
106818            }
106819        }
106820        $string = '';
106821        if (is_array($attributes) && !empty($attributes)) {
106822            if ($sort) {
106823                ksort($attributes);
106824            }
106825            if ( !$multiline || count($attributes) == 1) {
106826                foreach ($attributes as $key => $value) {
106827                    if ($entities != XML_UTIL_ENTITIES_NONE) {
106828                        if ($entities === XML_UTIL_CDATA_SECTION) {
106829                            $entities = XML_UTIL_ENTITIES_XML;
106830                        }
106831                        $value = XML_Util::replaceEntities($value, $entities);
106832                    }
106833                    $string .= ' ' . $key . '="' . $value . '"';
106834                }
106835            } else {
106836                $first = true;
106837                foreach ($attributes as $key => $value) {
106838                    if ($entities != XML_UTIL_ENTITIES_NONE) {
106839                        $value = XML_Util::replaceEntities($value, $entities);
106840                    }
106841                    if ($first) {
106842                        $string .= ' ' . $key . '="' . $value . '"';
106843                        $first   = false;
106844                    } else {
106845                        $string .= $linebreak . $indent . $key . '="' . $value . '"';
106846                    }
106847                }
106848            }
106849        }
106850        return $string;
106851    }
106852
106853    /**
106854     * Collapses empty tags.
106855     *
106856     * @param string $xml  XML
106857     * @param int    $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL)
106858     *                      or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones.
106859     *
106860     * @return string XML
106861     * @access public
106862     * @static
106863     * @todo PEAR CS - unable to avoid "space after open parens" error
106864     *       in the IF branch
106865     */
106866    function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL) 
106867    {
106868        if ($mode == XML_UTIL_COLLAPSE_XHTML_ONLY) {
106869            return preg_replace(
106870                '/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|'
106871                . 'param)([^>]*)><\/\\1>/s',
106872                '<\\1\\2 />',
106873                $xml);
106874        } else {
106875            return preg_replace('/<(\w+)([^>]*)><\/\\1>/s', '<\\1\\2 />', $xml);
106876        }
106877    }
106878
106879    /**
106880     * create a tag
106881     *
106882     * This method will call XML_Util::createTagFromArray(), which
106883     * is more flexible.
106884     *
106885     * <code>
106886     * require_once 'XML/Util.php';
106887     *
106888     * // create an XML tag:
106889     * $tag = XML_Util::createTag('myNs:myTag', 
106890     *     array('foo' => 'bar'), 
106891     *     'This is inside the tag', 
106892     *     'http://www.w3c.org/myNs#');
106893     * </code>
106894     *
106895     * @param string $qname           qualified tagname (including namespace)
106896     * @param array  $attributes      array containg attributes
106897     * @param mixed  $content         the content
106898     * @param string $namespaceUri    URI of the namespace
106899     * @param int    $replaceEntities whether to replace XML special chars in 
106900     *                                content, embedd it in a CData section 
106901     *                                or none of both
106902     * @param bool   $multiline       whether to create a multiline tag where 
106903     *                                each attribute gets written to a single line
106904     * @param string $indent          string used to indent attributes 
106905     *                                (_auto indents attributes so they start 
106906     *                                at the same column)
106907     * @param string $linebreak       string used for linebreaks
106908     * @param bool   $sortAttributes  Whether to sort the attributes or not
106909     *
106910     * @return string XML tag
106911     * @access public
106912     * @static
106913     * @see createTagFromArray()
106914     * @uses createTagFromArray() to create the tag
106915     */
106916    function createTag($qname, $attributes = array(), $content = null, 
106917        $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, 
106918        $multiline = false, $indent = '_auto', $linebreak = "\n", 
106919        $sortAttributes = true)
106920    {
106921        $tag = array(
106922            'qname'      => $qname,
106923            'attributes' => $attributes
106924        );
106925
106926        // add tag content
106927        if ($content !== null) {
106928            $tag['content'] = $content;
106929        }
106930
106931        // add namespace Uri
106932        if ($namespaceUri !== null) {
106933            $tag['namespaceUri'] = $namespaceUri;
106934        }
106935
106936        return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, 
106937            $indent, $linebreak, $sortAttributes);
106938    }
106939
106940    /**
106941     * create a tag from an array
106942     * this method awaits an array in the following format
106943     * <pre>
106944     * array(
106945     *     // qualified name of the tag
106946     *     'qname' => $qname        
106947     *
106948     *     // namespace prefix (optional, if qname is specified or no namespace)
106949     *     'namespace' => $namespace    
106950     *
106951     *     // local part of the tagname (optional, if qname is specified)
106952     *     'localpart' => $localpart,   
106953     *
106954     *     // array containing all attributes (optional)
106955     *     'attributes' => array(),      
106956     *
106957     *     // tag content (optional)
106958     *     'content' => $content,     
106959     *
106960     *     // namespaceUri for the given namespace (optional)
106961     *     'namespaceUri' => $namespaceUri 
106962     * )
106963     * </pre>
106964     *
106965     * <code>
106966     * require_once 'XML/Util.php';
106967     *
106968     * $tag = array(
106969     *     'qname'        => 'foo:bar',
106970     *     'namespaceUri' => 'http://foo.com',
106971     *     'attributes'   => array('key' => 'value', 'argh' => 'fruit&vegetable'),
106972     *     'content'      => 'I\'m inside the tag',
106973     * );
106974     * // creating a tag with qualified name and namespaceUri
106975     * $string = XML_Util::createTagFromArray($tag);
106976     * </code>
106977     *
106978     * @param array  $tag             tag definition
106979     * @param int    $replaceEntities whether to replace XML special chars in 
106980     *                                content, embedd it in a CData section 
106981     *                                or none of both
106982     * @param bool   $multiline       whether to create a multiline tag where each 
106983     *                                attribute gets written to a single line
106984     * @param string $indent          string used to indent attributes 
106985     *                                (_auto indents attributes so they start 
106986     *                                at the same column)
106987     * @param string $linebreak       string used for linebreaks
106988     * @param bool   $sortAttributes  Whether to sort the attributes or not
106989     *
106990     * @return string XML tag
106991     * @access public
106992     * @static
106993     * @see createTag()
106994     * @uses attributesToString() to serialize the attributes of the tag
106995     * @uses splitQualifiedName() to get local part and namespace of a qualified name
106996     * @uses createCDataSection()
106997     * @uses raiseError()
106998     */
106999    function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
107000        $multiline = false, $indent = '_auto', $linebreak = "\n", 
107001        $sortAttributes = true)
107002    {
107003        if (isset($tag['content']) && !is_scalar($tag['content'])) {
107004            return XML_Util::raiseError('Supplied non-scalar value as tag content',
107005            XML_UTIL_ERROR_NON_SCALAR_CONTENT);
107006        }
107007
107008        if (!isset($tag['qname']) && !isset($tag['localPart'])) {
107009            return XML_Util::raiseError('You must either supply a qualified name '
107010                . '(qname) or local tag name (localPart).', 
107011                XML_UTIL_ERROR_NO_TAG_NAME);
107012        }
107013
107014        // if no attributes hav been set, use empty attributes
107015        if (!isset($tag['attributes']) || !is_array($tag['attributes'])) {
107016            $tag['attributes'] = array();
107017        }
107018
107019        if (isset($tag['namespaces'])) {
107020            foreach ($tag['namespaces'] as $ns => $uri) {
107021                $tag['attributes']['xmlns:' . $ns] = $uri;
107022            }
107023        }
107024
107025        if (!isset($tag['qname'])) {
107026            // qualified name is not given
107027
107028            // check for namespace
107029            if (isset($tag['namespace']) && !empty($tag['namespace'])) {
107030                $tag['qname'] = $tag['namespace'] . ':' . $tag['localPart'];
107031            } else {
107032                $tag['qname'] = $tag['localPart'];
107033            }
107034        } elseif (isset($tag['namespaceUri']) && !isset($tag['namespace'])) {
107035            // namespace URI is set, but no namespace
107036
107037            $parts = XML_Util::splitQualifiedName($tag['qname']);
107038
107039            $tag['localPart'] = $parts['localPart'];
107040            if (isset($parts['namespace'])) {
107041                $tag['namespace'] = $parts['namespace'];
107042            }
107043        }
107044
107045        if (isset($tag['namespaceUri']) && !empty($tag['namespaceUri'])) {
107046            // is a namespace given
107047            if (isset($tag['namespace']) && !empty($tag['namespace'])) {
107048                $tag['attributes']['xmlns:' . $tag['namespace']] =
107049                    $tag['namespaceUri'];
107050            } else {
107051                // define this Uri as the default namespace
107052                $tag['attributes']['xmlns'] = $tag['namespaceUri'];
107053            }
107054        }
107055
107056        // check for multiline attributes
107057        if ($multiline === true) {
107058            if ($indent === '_auto') {
107059                $indent = str_repeat(' ', (strlen($tag['qname'])+2));
107060            }
107061        }
107062
107063        // create attribute list
107064        $attList = XML_Util::attributesToString($tag['attributes'], 
107065            $sortAttributes, $multiline, $indent, $linebreak, $replaceEntities);
107066        if (!isset($tag['content']) || (string)$tag['content'] == '') {
107067            $tag = sprintf('<%s%s />', $tag['qname'], $attList);
107068        } else {
107069            switch ($replaceEntities) {
107070            case XML_UTIL_ENTITIES_NONE:
107071                break;
107072            case XML_UTIL_CDATA_SECTION:
107073                $tag['content'] = XML_Util::createCDataSection($tag['content']);
107074                break;
107075            default:
107076                $tag['content'] = XML_Util::replaceEntities($tag['content'], 
107077                    $replaceEntities);
107078                break;
107079            }
107080            $tag = sprintf('<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'],
107081                $tag['qname']);
107082        }
107083        return $tag;
107084    }
107085
107086    /**
107087     * create a start element
107088     *
107089     * <code>
107090     * require_once 'XML/Util.php';
107091     *
107092     * // create an XML start element:
107093     * $tag = XML_Util::createStartElement('myNs:myTag', 
107094     *     array('foo' => 'bar') ,'http://www.w3c.org/myNs#');
107095     * </code>
107096     *
107097     * @param string $qname          qualified tagname (including namespace)
107098     * @param array  $attributes     array containg attributes
107099     * @param string $namespaceUri   URI of the namespace
107100     * @param bool   $multiline      whether to create a multiline tag where each 
107101     *                               attribute gets written to a single line
107102     * @param string $indent         string used to indent attributes (_auto indents
107103     *                               attributes so they start at the same column)
107104     * @param string $linebreak      string used for linebreaks
107105     * @param bool   $sortAttributes Whether to sort the attributes or not
107106     *
107107     * @return string XML start element
107108     * @access public
107109     * @static
107110     * @see createEndElement(), createTag()
107111     */
107112    function createStartElement($qname, $attributes = array(), $namespaceUri = null,
107113        $multiline = false, $indent = '_auto', $linebreak = "\n", 
107114        $sortAttributes = true)
107115    {
107116        // if no attributes hav been set, use empty attributes
107117        if (!isset($attributes) || !is_array($attributes)) {
107118            $attributes = array();
107119        }
107120
107121        if ($namespaceUri != null) {
107122            $parts = XML_Util::splitQualifiedName($qname);
107123        }
107124
107125        // check for multiline attributes
107126        if ($multiline === true) {
107127            if ($indent === '_auto') {
107128                $indent = str_repeat(' ', (strlen($qname)+2));
107129            }
107130        }
107131
107132        if ($namespaceUri != null) {
107133            // is a namespace given
107134            if (isset($parts['namespace']) && !empty($parts['namespace'])) {
107135                $attributes['xmlns:' . $parts['namespace']] = $namespaceUri;
107136            } else {
107137                // define this Uri as the default namespace
107138                $attributes['xmlns'] = $namespaceUri;
107139            }
107140        }
107141
107142        // create attribute list
107143        $attList = XML_Util::attributesToString($attributes, $sortAttributes, 
107144            $multiline, $indent, $linebreak);
107145        $element = sprintf('<%s%s>', $qname, $attList);
107146        return  $element;
107147    }
107148
107149    /**
107150     * create an end element
107151     *
107152     * <code>
107153     * require_once 'XML/Util.php';
107154     *
107155     * // create an XML start element:
107156     * $tag = XML_Util::createEndElement('myNs:myTag');
107157     * </code>
107158     *
107159     * @param string $qname qualified tagname (including namespace)
107160     *
107161     * @return string XML end element
107162     * @access public
107163     * @static
107164     * @see createStartElement(), createTag()
107165     */
107166    function createEndElement($qname)
107167    {
107168        $element = sprintf('</%s>', $qname);
107169        return $element;
107170    }
107171
107172    /**
107173     * create an XML comment
107174     *
107175     * <code>
107176     * require_once 'XML/Util.php';
107177     *
107178     * // create an XML start element:
107179     * $tag = XML_Util::createComment('I am a comment');
107180     * </code>
107181     *
107182     * @param string $content content of the comment
107183     *
107184     * @return string XML comment
107185     * @access public
107186     * @static
107187     */
107188    function createComment($content)
107189    {
107190        $comment = sprintf('<!-- %s -->', $content);
107191        return $comment;
107192    }
107193
107194    /**
107195     * create a CData section
107196     *
107197     * <code>
107198     * require_once 'XML/Util.php';
107199     *
107200     * // create a CData section
107201     * $tag = XML_Util::createCDataSection('I am content.');
107202     * </code>
107203     *
107204     * @param string $data data of the CData section
107205     *
107206     * @return string CData section with content
107207     * @access public
107208     * @static
107209     */
107210    function createCDataSection($data)
107211    {
107212        return sprintf('<![CDATA[%s]]>', 
107213            preg_replace('/\]\]>/', ']]]]><![CDATA[>', strval($data)));
107214
107215    }
107216
107217    /**
107218     * split qualified name and return namespace and local part
107219     *
107220     * <code>
107221     * require_once 'XML/Util.php';
107222     *
107223     * // split qualified tag
107224     * $parts = XML_Util::splitQualifiedName('xslt:stylesheet');
107225     * </code>
107226     * the returned array will contain two elements:
107227     * <pre>
107228     * array(
107229     *     'namespace' => 'xslt',
107230     *     'localPart' => 'stylesheet'
107231     * );
107232     * </pre>
107233     *
107234     * @param string $qname     qualified tag name
107235     * @param string $defaultNs default namespace (optional)
107236     *
107237     * @return array array containing namespace and local part
107238     * @access public
107239     * @static
107240     */
107241    function splitQualifiedName($qname, $defaultNs = null)
107242    {
107243        if (strstr($qname, ':')) {
107244            $tmp = explode(':', $qname);
107245            return array(
107246                'namespace' => $tmp[0],
107247                'localPart' => $tmp[1]
107248            );
107249        }
107250        return array(
107251            'namespace' => $defaultNs,
107252            'localPart' => $qname
107253        );
107254    }
107255
107256    /**
107257     * check, whether string is valid XML name
107258     *
107259     * <p>XML names are used for tagname, attribute names and various
107260     * other, lesser known entities.</p>
107261     * <p>An XML name may only consist of alphanumeric characters,
107262     * dashes, undescores and periods, and has to start with a letter
107263     * or an underscore.</p>
107264     *
107265     * <code>
107266     * require_once 'XML/Util.php';
107267     *
107268     * // verify tag name
107269     * $result = XML_Util::isValidName('invalidTag?');
107270     * if (is_a($result, 'PEAR_Error')) {
107271     *    print 'Invalid XML name: ' . $result->getMessage();
107272     * }
107273     * </code>
107274     *
107275     * @param string $string string that should be checked
107276     *
107277     * @return mixed true, if string is a valid XML name, PEAR error otherwise
107278     * @access public
107279     * @static
107280     * @todo support for other charsets
107281     * @todo PEAR CS - unable to avoid 85-char limit on second preg_match
107282     */
107283    function isValidName($string)
107284    {
107285        // check for invalid chars
107286        if (!preg_match('/^[[:alpha:]_]$/', $string{0})) {
107287            return XML_Util::raiseError('XML names may only start with letter '
107288                . 'or underscore', XML_UTIL_ERROR_INVALID_START);
107289        }
107290
107291        // check for invalid chars
107292        if (!preg_match('/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?[[:alpha:]_]([[:alnum:]\_\-\.]+)?$/',
107293            $string)
107294        ) {
107295            return XML_Util::raiseError('XML names may only contain alphanumeric '
107296                . 'chars, period, hyphen, colon and underscores', 
107297                XML_UTIL_ERROR_INVALID_CHARS);
107298        }
107299        // XML name is valid
107300        return true;
107301    }
107302
107303    /**
107304     * replacement for XML_Util::raiseError
107305     *
107306     * Avoids the necessity to always require
107307     * PEAR.php
107308     *
107309     * @param string $msg  error message
107310     * @param int    $code error code
107311     *
107312     * @return PEAR_Error
107313     * @access public
107314     * @static
107315     * @todo PEAR CS - should this use include_once instead?
107316     */
107317    function raiseError($msg, $code)
107318    {
107319        require_once 'phar://install-pear-nozlib.phar/' . 'PEAR.php';
107320        return PEAR::raiseError($msg, $code);
107321    }
107322}
107323?>
107324package.xml100644   1750   1750       36035 11117075467   6455 <?xml version="1.0" encoding="UTF-8"?>
107325<package packagerversion="1.7.2" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0     http://pear.php.net/dtd/tasks-1.0.xsd     http://pear.php.net/dtd/package-2.0     http://pear.php.net/dtd/package-2.0.xsd">
107326 <name>XML_Util</name>
107327 <channel>pear.php.net</channel>
107328 <summary>XML utility class</summary>
107329 <description>Selection of methods that are often needed when working with XML documents.  Functionality includes creating of attribute lists from arrays, creation of tags, validation of XML names and more.</description>
107330 <lead>
107331  <name>Chuck Burgess</name>
107332  <user>ashnazg</user>
107333  <email>ashnazg@php.net</email>
107334  <active>yes</active>
107335 </lead>
107336 <lead>
107337  <name>Stephan Schmidt</name>
107338  <user>schst</user>
107339  <email>schst@php-tools.net</email>
107340  <active>no</active>
107341 </lead>
107342 <helper>
107343  <name>Davey Shafik</name>
107344  <user>davey</user>
107345  <email>davey@php.net</email>
107346  <active>no</active>
107347 </helper>
107348 <date>2008-12-07</date>
107349 <time>19:41:10</time>
107350 <version>
107351  <release>1.2.1</release>
107352  <api>1.2.0</api>
107353 </version>
107354 <stability>
107355  <release>stable</release>
107356  <api>stable</api>
107357 </stability>
107358 <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
107359 <notes>Fixed Bug #14760: Bug in getDocTypeDeclaration() [ashnazg|fpospisil]</notes>
107360 <contents>
107361  <dir baseinstalldir="XML" name="/">
107362   <file baseinstalldir="XML" md5sum="06b6662b91b1a466e7b5113f37d4725f" name="examples/example.php" role="doc" />
107363   <file baseinstalldir="XML" md5sum="77355702c9e861d3fc0a5318ea689eee" name="examples/example2.php" role="doc" />
107364   <file baseinstalldir="XML" md5sum="0af0cff09232a6c275803bb36213cdd9" name="tests/AllTests.php" role="test" />
107365   <file baseinstalldir="XML" md5sum="73088689d58b71cd4f86013c88b72216" name="tests/testBasic_apiVersion.phpt" role="test" />
107366   <file baseinstalldir="XML" md5sum="4b17c0df7fbfb1bb2f3f636ebd6c2cbd" name="tests/testBasic_attributesToString.phpt" role="test" />
107367   <file baseinstalldir="XML" md5sum="dc4202f1451bbeb62a5bc7d56a7c774c" name="tests/testBasic_collapseEmptyTags.phpt" role="test" />
107368   <file baseinstalldir="XML" md5sum="4c87fda94fdfb7a78ba84998d6e28f45" name="tests/testBasic_createCDataSection.phpt" role="test" />
107369   <file baseinstalldir="XML" md5sum="c101844768f146653c59c81978060158" name="tests/testBasic_createComment.phpt" role="test" />
107370   <file baseinstalldir="XML" md5sum="ba75d66c2f0fb0010c71f3bcd1f64eb2" name="tests/testBasic_createEndElement.phpt" role="test" />
107371   <file baseinstalldir="XML" md5sum="ad786fb687e1eea1f6890a7424709c79" name="tests/testBasic_createStartElement.phpt" role="test" />
107372   <file baseinstalldir="XML" md5sum="5f453edadebaa3435c8e6f09a3aaaa9c" name="tests/testBasic_createTag.phpt" role="test" />
107373   <file baseinstalldir="XML" md5sum="d2792da0d9c5f0987ee4587912017aa9" name="tests/testBasic_createTagFromArray.phpt" role="test" />
107374   <file baseinstalldir="XML" md5sum="817882a0ab33e60f17ee71874ad975f0" name="tests/testBasic_getDocTypeDeclaration.phpt" role="test" />
107375   <file baseinstalldir="XML" md5sum="5bc1926a488a6b63ec6366bfcf06a50a" name="tests/testBasic_getXmlDeclaration.phpt" role="test" />
107376   <file baseinstalldir="XML" md5sum="a727860d55c14194fdaffa839a59a540" name="tests/testBasic_isValidName.phpt" role="test" />
107377   <file baseinstalldir="XML" md5sum="300a6192f7cc6f7bc3d8f2576b51e4b0" name="tests/testBasic_raiseError.phpt" role="test" />
107378   <file baseinstalldir="XML" md5sum="e6717d43001775cccd52bd5195cf02e0" name="tests/testBasic_replaceEntities.phpt" role="test" />
107379   <file baseinstalldir="XML" md5sum="431856aa83a23396a9f00915dd2be192" name="tests/testBasic_reverseEntities.phpt" role="test" />
107380   <file baseinstalldir="XML" md5sum="ab035534463cae8cc903e907aead8ffe" name="tests/testBasic_splitQualifiedName.phpt" role="test" />
107381   <file baseinstalldir="XML" md5sum="1850856692ff6c6df5e8acb16e1080ce" name="tests/testBug_4950.phpt" role="test" />
107382   <file baseinstalldir="XML" md5sum="b4127883df40a4b0d1736ad42215ee25" name="tests/testBug_5392.phpt" role="test" />
107383   <file baseinstalldir="XML" md5sum="9bb265dafaaf06c86ca5c48f50368ded" name="Util.php" role="php">
107384    <tasks:replace from="@version@" to="version" type="package-info" />
107385   </file>
107386  </dir>
107387 </contents>
107388 <dependencies>
107389  <required>
107390   <php>
107391    <min>4.3.0</min>
107392   </php>
107393   <pearinstaller>
107394    <min>1.4.3</min>
107395   </pearinstaller>
107396   <extension>
107397    <name>pcre</name>
107398   </extension>
107399  </required>
107400 </dependencies>
107401 <phprelease />
107402 <changelog>
107403  <release>
107404   <version>
107405    <release>1.2.1</release>
107406    <api>1.2.0</api>
107407   </version>
107408   <stability>
107409    <release>stable</release>
107410    <api>stable</api>
107411   </stability>
107412   <date>2008-12-07</date>
107413   <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
107414   <notes>Fixed Bug #14760: Bug in getDocTypeDeclaration() [ashnazg|fpospisil]</notes>
107415  </release>
107416  <release>
107417   <version>
107418    <release>1.2.0</release>
107419    <api>1.2.0</api>
107420   </version>
107421   <stability>
107422    <release>stable</release>
107423    <api>stable</api>
107424   </stability>
107425   <date>2008-07-26</date>
107426   <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
107427   <notes>Changed license to New BSD License (Req #13826 [ashnazg])
107428Added a test suite against all API methods [ashnazg]
107429Switch to package.xml v2 [ashnazg]
107430Added Req #13839: Missing XHTML empty tags to collapse [ashnazg|drry]
107431Fixed Bug #5392: encoding of ISO-8859-1 is the only supported encoding [ashnazg]
107432Fixed Bug #4950: Incorrect CDATA serializing [ashnazg|drry]
107433-- (this fix differs from the one in v1.2.0a1)</notes>
107434  </release>
107435  <release>
107436   <version>
107437    <release>1.2.0RC1</release>
107438    <api>1.2.0</api>
107439   </version>
107440   <stability>
107441    <release>beta</release>
107442    <api>beta</api>
107443   </stability>
107444   <date>2008-07-12</date>
107445   <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
107446   <notes>Changed license to New BSD License (Req #13826 [ashnazg])
107447Added a test suite against all API methods [ashnazg]
107448Switch to package.xml v2 [ashnazg]
107449Added Req #13839: Missing XHTML empty tags to collapse [ashnazg|drry]
107450Fixed Bug #5392: encoding of ISO-8859-1 is the only supported encoding [ashnazg]
107451Fixed Bug #4950: Incorrect CDATA serializing [ashnazg|drry]
107452-- (this fix differs from the one in v1.2.0a1)</notes>
107453  </release>
107454  <release>
107455   <version>
107456    <release>1.2.0a2</release>
107457    <api>1.2.0</api>
107458   </version>
107459   <stability>
107460    <release>alpha</release>
107461    <api>alpha</api>
107462   </stability>
107463   <date>2008-05-22</date>
107464   <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
107465   <notes>Changed license to New BSD License (Req #13826 [ashnazg])
107466Added a test suite against all API methods [ashnazg]
107467Switch to package.xml v2 [ashnazg]
107468Added Req #13839: Missing XHTML empty tags to collapse [ashnazg|drry]
107469Fixed Bug #5392: encoding of ISO-8859-1 is the only supported encoding [ashnazg]
107470Fixed Bug #4950: Incorrect CDATA serializing [ashnazg|drry]
107471-- (this fix differs from the one in v1.2.0a1)</notes>
107472  </release>
107473  <release>
107474   <version>
107475    <release>1.2.0a1</release>
107476    <api>1.2.0</api>
107477   </version>
107478   <stability>
107479    <release>alpha</release>
107480    <api>alpha</api>
107481   </stability>
107482   <date>2008-05-04</date>
107483   <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
107484   <notes>Changed license to New BSD License (Req #13826 [ashnazg])
107485Added a test suite against all API methods [ashnazg]
107486Switch to package.xml v2 [ashnazg]
107487Fixed Bug #4950: Incorrect CDATA serializing [ashnazg|ja.doma]</notes>
107488  </release>
107489  <release>
107490   <version>
107491    <release>1.1.4</release>
107492    <api>1.1.4</api>
107493   </version>
107494   <stability>
107495    <release>stable</release>
107496    <api>stable</api>
107497   </stability>
107498   <date>2006-12-16</date>
107499   <license uri="http://www.php.net/license">PHP License</license>
107500   <notes>- Fixed bug #9561: Not allowing underscores in middle of tags</notes>
107501  </release>
107502  <release>
107503   <version>
107504    <release>1.1.2</release>
107505    <api>1.1.2</api>
107506   </version>
107507   <stability>
107508    <release>stable</release>
107509    <api>stable</api>
107510   </stability>
107511   <date>2006-12-01</date>
107512   <license uri="http://www.php.net/license">PHP License</license>
107513   <notes>- fixed bug #5419: isValidName() now checks for character classes
107514- implemented request #8196: added optional parameter to influence array sorting to createTag() createTagFromArray() and createStartElement()</notes>
107515  </release>
107516  <release>
107517   <version>
107518    <release>1.1.1</release>
107519    <api>1.1.1</api>
107520   </version>
107521   <stability>
107522    <release>stable</release>
107523    <api>stable</api>
107524   </stability>
107525   <date>2004-12-23</date>
107526   <license uri="http://www.php.net/license">PHP License</license>
107527   <notes>- fixed bug in replaceEntities() and reverseEntities() in conjunction with XML_UTIL_ENTITIES_HTML
107528- createTag() and createTagFromArray() now accept XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML, XML_UTIL_ENTITIES_NONE and XML_UTIL_CDATA_SECTION as $replaceEntities parameter</notes>
107529  </release>
107530  <release>
107531   <version>
107532    <release>1.1.0</release>
107533    <api>1.1.0</api>
107534   </version>
107535   <stability>
107536    <release>stable</release>
107537    <api>stable</api>
107538   </stability>
107539   <date>2004-11-19</date>
107540   <license uri="http://www.php.net/license">PHP License</license>
107541   <notes>- Added collapseEmptyTags (patch by Sebastian Bergmann and Thomas Duffey)</notes>
107542  </release>
107543  <release>
107544   <version>
107545    <release>1.0.0</release>
107546    <api>1.0.0</api>
107547   </version>
107548   <stability>
107549    <release>stable</release>
107550    <api>stable</api>
107551   </stability>
107552   <date>2004-10-28</date>
107553   <license uri="http://www.php.net/license">PHP License</license>
107554   <notes>- Added reverseEntities() (request #2639)</notes>
107555  </release>
107556  <release>
107557   <version>
107558    <release>0.6.1</release>
107559    <api>0.6.1</api>
107560   </version>
107561   <stability>
107562    <release>stable</release>
107563    <api>stable</api>
107564   </stability>
107565   <date>2004-10-28</date>
107566   <license uri="http://www.php.net/license">PHP License</license>
107567   <notes>- Added check for tag name (either as local part or qualified name) in createTagFromArray() (bug #1083)</notes>
107568  </release>
107569  <release>
107570   <version>
107571    <release>0.6.0</release>
107572    <api>0.6.0</api>
107573   </version>
107574   <stability>
107575    <release>stable</release>
107576    <api>stable</api>
107577   </stability>
107578   <date>2004-06-07</date>
107579   <license uri="http://www.php.net/license">PHP License</license>
107580   <notes>- Fixed bug 1438 (namespaces not accepted for isValidName()) (thanks to davey)
107581- added optional parameter to replaceEntities() to define the set of entities to replace
107582- added optional parameter to attributesToString() to define, whether entities should be replaced (requested by Sebastian Bergmann)
107583- allowed second parameter to XML_Util::attributesToString() to be an array containing options (easier to use, if you only need to set the last parameter)
107584- introduced XML_Util::raiseError() to avoid the necessity of including PEAR.php, will only be included on error</notes>
107585  </release>
107586  <release>
107587   <version>
107588    <release>0.6.0beta1</release>
107589    <api>0.6.0beta1</api>
107590   </version>
107591   <stability>
107592    <release>beta</release>
107593    <api>beta</api>
107594   </stability>
107595   <date>2004-05-24</date>
107596   <license uri="http://www.php.net/license">PHP License</license>
107597   <notes>- Fixed bug 1438 (namespaces not accepted for isValidName()) (thanks to davey)
107598- added optional parameter to replaceEntities() to define the set of entities to replace
107599- added optional parameter to attributesToString() to define, whether entities should be replaced (requested by Sebastian Bergmann)
107600- allowed second parameter to XML_Util::attributesToString() to be an array containing options (easier to use, if you only need to set the last parameter)
107601- introduced XML_Util::raiseError() to avoid the necessity of including PEAR.php, will only be included on error</notes>
107602  </release>
107603  <release>
107604   <version>
107605    <release>0.5.2</release>
107606    <api>0.5.2</api>
107607   </version>
107608   <stability>
107609    <release>stable</release>
107610    <api>stable</api>
107611   </stability>
107612   <date>2003-11-22</date>
107613   <license uri="http://www.php.net/license">PHP License</license>
107614   <notes>now creates XHTML compliant empty tags (Davey),
107615minor whitespace fixes (Davey)</notes>
107616  </release>
107617  <release>
107618   <version>
107619    <release>0.5.1</release>
107620    <api>0.5.1</api>
107621   </version>
107622   <stability>
107623    <release>stable</release>
107624    <api>stable</api>
107625   </stability>
107626   <date>2003-09-26</date>
107627   <license uri="http://www.php.net/license">PHP License</license>
107628   <notes>added default namespace parameter (optional) in splitQualifiedName() (requested by Sebastian Bergmann)</notes>
107629  </release>
107630  <release>
107631   <version>
107632    <release>0.5</release>
107633    <api>0.5</api>
107634   </version>
107635   <stability>
107636    <release>stable</release>
107637    <api>stable</api>
107638   </stability>
107639   <date>2003-09-23</date>
107640   <license uri="http://www.php.net/license">PHP License</license>
107641   <notes>added support for multiline attributes in attributesToString(), createTag*() and createStartElement (requested by Yavor Shahpasov for XML_Serializer),
107642added createComment</notes>
107643  </release>
107644  <release>
107645   <version>
107646    <release>0.4</release>
107647    <api>0.4</api>
107648   </version>
107649   <stability>
107650    <release>stable</release>
107651    <api>stable</api>
107652   </stability>
107653   <date>2003-09-21</date>
107654   <license uri="http://www.php.net/license">PHP License</license>
107655   <notes>added createCDataSection(),
107656added support for CData sections in createTag* methods,
107657fixed bug #23,
107658fixed bug in splitQualifiedName()</notes>
107659  </release>
107660  <release>
107661   <version>
107662    <release>0.3</release>
107663    <api>0.3</api>
107664   </version>
107665   <stability>
107666    <release>stable</release>
107667    <api>stable</api>
107668   </stability>
107669   <date>2003-09-12</date>
107670   <license uri="http://www.php.net/license">PHP License</license>
107671   <notes>added createStartElement() and createEndElement()</notes>
107672  </release>
107673  <release>
107674   <version>
107675    <release>0.2.1</release>
107676    <api>0.2.1</api>
107677   </version>
107678   <stability>
107679    <release>stable</release>
107680    <api>stable</api>
107681   </stability>
107682   <date>2003-09-05</date>
107683   <license uri="http://www.php.net/license">PHP License</license>
107684   <notes>fixed bug with zero as tag content in createTagFromArray and createTag</notes>
107685  </release>
107686  <release>
107687   <version>
107688    <release>0.2</release>
107689    <api>0.2</api>
107690   </version>
107691   <stability>
107692    <release>stable</release>
107693    <api>stable</api>
107694   </stability>
107695   <date>2003-08-12</date>
107696   <license uri="http://www.php.net/license">PHP License</license>
107697   <notes>added XML_Util::getDocTypeDeclaration()</notes>
107698  </release>
107699  <release>
107700   <version>
107701    <release>0.1.1</release>
107702    <api>0.1.1</api>
107703   </version>
107704   <stability>
107705    <release>stable</release>
107706    <api>stable</api>
107707   </stability>
107708   <date>2003-08-02</date>
107709   <license uri="http://www.php.net/license">PHP License</license>
107710   <notes>bugfix: removed bug in createTagFromArray</notes>
107711  </release>
107712  <release>
107713   <version>
107714    <release>0.1</release>
107715    <api>0.1</api>
107716   </version>
107717   <stability>
107718    <release>stable</release>
107719    <api>stable</api>
107720   </stability>
107721   <date>2003-08-01</date>
107722   <license uri="http://www.php.net/license">PHP License</license>
107723   <notes>inital release</notes>
107724  </release>
107725 </changelog>
107726</package>
107727XML_Util-1.2.1/examples/example.php100644   1750   1750       21775 11117075466  12420 <?php
107728
107729/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
107730
107731/**
107732 * Examples (file #1)
107733 *
107734 * several examples for the methods of XML_Util
107735 * 
107736 * PHP versions 4 and 5
107737 *
107738 * LICENSE:
107739 *
107740 * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
107741 * All rights reserved.
107742 *
107743 * Redistribution and use in source and binary forms, with or without
107744 * modification, are permitted provided that the following conditions
107745 * are met:
107746 *
107747 *    * Redistributions of source code must retain the above copyright
107748 *      notice, this list of conditions and the following disclaimer.
107749 *    * Redistributions in binary form must reproduce the above copyright
107750 *      notice, this list of conditions and the following disclaimer in the
107751 *      documentation and/or other materials provided with the distribution.
107752 *    * The name of the author may not be used to endorse or promote products
107753 *      derived from this software without specific prior written permission.
107754 *
107755 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
107756 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
107757 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
107758 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
107759 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
107760 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
107761 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
107762 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
107763 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
107764 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
107765 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
107766 *
107767 * @category   XML
107768 * @package    XML_Util
107769 * @subpackage Examples
107770 * @author     Stephan Schmidt <schst@php.net>
107771 * @copyright  2003-2008 Stephan Schmidt <schst@php.net>
107772 * @license    http://opensource.org/licenses/bsd-license New BSD License
107773 * @version    CVS: $Id: example.php,v 1.17 2008/05/05 19:05:28 ashnazg Exp $
107774 * @link       http://pear.php.net/package/XML_Util
107775 */
107776
107777    /**
107778     * set error level
107779     */
107780    error_reporting(E_ALL);
107781
107782    require_once 'XML/Util.php';
107783    
107784    /**
107785     * replacing XML entities
107786     */
107787    print 'replace XML entities:<br>';
107788    print XML_Util::replaceEntities('This string contains < & >.');
107789    print "\n<br><br>\n";
107790
107791    /**
107792     * reversing XML entities
107793     */
107794    print 'replace XML entities:<br>';
107795    print XML_Util::reverseEntities('This string contains &lt; &amp; &gt;.');
107796    print "\n<br><br>\n";
107797
107798    /**
107799     * building XML declaration
107800     */
107801    print 'building XML declaration:<br>';
107802    print htmlspecialchars(XML_Util::getXMLDeclaration());
107803    print "\n<br><br>\n";
107804
107805    print 'building XML declaration with additional attributes:<br>';
107806    print htmlspecialchars(XML_Util::getXMLDeclaration('1.0', 'UTF-8', true));
107807    print "\n<br><br>\n";
107808
107809    /**
107810     * building document type declaration
107811     */
107812    print 'building DocType declaration:<br>';
107813    print htmlspecialchars(XML_Util::getDocTypeDeclaration('package', 
107814        'http://pear.php.net/dtd/package-1.0'));
107815    print "\n<br><br>\n";
107816
107817    print 'building DocType declaration with public ID (does not exist):<br>';
107818    print htmlspecialchars(XML_Util::getDocTypeDeclaration('package', 
107819        array('uri' => 'http://pear.php.net/dtd/package-1.0', 
107820            'id' => '-//PHP//PEAR/DTD PACKAGE 0.1')));
107821    print "\n<br><br>\n";
107822
107823    print 'building DocType declaration with internal DTD:<br>';
107824    print '<pre>';
107825    print htmlspecialchars(XML_Util::getDocTypeDeclaration('package', 
107826        'http://pear.php.net/dtd/package-1.0', 
107827        '<!ELEMENT additionalInfo (#PCDATA)>'));
107828    print '</pre>';
107829    print "\n<br><br>\n";
107830
107831    /**
107832     * creating an attribute string
107833     */
107834    $att = array(
107835        'foo'  => 'bar',
107836        'argh' => 'tomato'
107837    );
107838
107839    print 'converting array to string:<br>';
107840    print XML_Util::attributesToString($att);
107841    print "\n<br><br>\n";
107842
107843
107844    /**
107845     * creating an attribute string with linebreaks
107846     */
107847    $att = array(
107848        'foo'  => 'bar',
107849        'argh' => 'tomato'
107850    );
107851
107852    print 'converting array to string (including line breaks):<br>';
107853    print '<pre>';
107854    print XML_Util::attributesToString($att, true, true);
107855    print '</pre>';
107856    print "\n<br><br>\n";
107857
107858
107859    /**
107860     * splitting a qualified tag name
107861     */
107862    print 'splitting qualified tag name:<br>';
107863    print '<pre>';
107864    print_r(XML_Util::splitQualifiedName('xslt:stylesheet'));
107865    print '</pre>';
107866    print "\n<br>\n";
107867
107868
107869    /**
107870     * splitting a qualified tag name (no namespace)
107871     */
107872    print 'splitting qualified tag name (no namespace):<br>';
107873    print '<pre>';
107874    print_r(XML_Util::splitQualifiedName('foo'));
107875    print '</pre>';
107876    print "\n<br>\n";
107877
107878    /**
107879     * splitting a qualified tag name (no namespace, but default namespace specified)
107880     */
107881    print 'splitting qualified tag name '
107882        . '(no namespace, but default namespace specified):<br>';
107883    print '<pre>';
107884    print_r(XML_Util::splitQualifiedName('foo', 'bar'));
107885    print '</pre>';
107886    print "\n<br>\n";
107887
107888    /**
107889     * verifying XML names
107890     */
107891    print 'verifying \'My private tag\':<br>';
107892    print '<pre>';
107893    print_r(XML_Util::isValidname('My Private Tag'));
107894    print '</pre>';
107895    print "\n<br><br>\n";
107896    
107897    print 'verifying \'-MyTag\':<br>';
107898    print '<pre>';
107899    print_r(XML_Util::isValidname('-MyTag'));
107900    print '</pre>';
107901    print "\n<br><br>\n";
107902
107903    /**
107904     * creating an XML tag
107905     */
107906    $tag = array(
107907        'namespace'  => 'foo',
107908        'localPart'  => 'bar',
107909        'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
107910        'content'    => 'I\'m inside the tag'
107911    );
107912
107913    print 'creating a tag with namespace and local part:<br>';
107914    print htmlentities(XML_Util::createTagFromArray($tag));
107915    print "\n<br><br>\n";
107916
107917    /**
107918     * creating an XML tag
107919     */
107920    $tag = array(
107921        'qname'        => 'foo:bar',
107922        'namespaceUri' => 'http://foo.com',
107923        'attributes'   => array('key' => 'value', 'argh' => 'fruit&vegetable'),
107924        'content'      => 'I\'m inside the tag'
107925    );
107926
107927    print 'creating a tag with qualified name and namespaceUri:<br>';
107928    print htmlentities(XML_Util::createTagFromArray($tag));
107929    print "\n<br><br>\n";
107930
107931    /**
107932     * creating an XML tag
107933     */
107934    $tag = array(
107935        'qname'        => 'bar',
107936        'namespaceUri' => 'http://foo.com',
107937        'attributes'   => array('key' => 'value', 'argh' => 'fruit&vegetable')
107938    );
107939
107940    print 'creating an empty tag without namespace but namespace Uri:<br>';
107941    print htmlentities(XML_Util::createTagFromArray($tag));
107942    print "\n<br><br>\n";
107943
107944    /**
107945     * creating an XML tag with more namespaces
107946     */
107947    $tag = array(
107948        'namespace'   => 'foo',
107949        'localPart'   => 'bar',
107950        'attributes'  => array('key' => 'value', 'argh' => 'fruit&vegetable'),
107951        'content'     => 'I\'m inside the tag',
107952        'namespaces'  => array(
107953            'bar'  => 'http://bar.com',
107954            'pear' => 'http://pear.php.net',
107955        )
107956    );
107957
107958    print 'creating an XML tag with more namespaces:<br />';
107959    print htmlentities(XML_Util::createTagFromArray($tag));
107960    print "\n<br><br>\n";
107961
107962    /**
107963     * creating an XML tag with a CData Section
107964     */
107965    $tag = array(
107966        'qname'      => 'foo',
107967        'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
107968        'content'    => 'I\'m inside the tag'
107969    );
107970
107971    print 'creating a tag with CData section:<br>';
107972    print htmlentities(XML_Util::createTagFromArray($tag, XML_UTIL_CDATA_SECTION));
107973    print "\n<br><br>\n";
107974
107975    /**
107976     * creating an XML tag with a CData Section
107977     */
107978    $tag = array(
107979        'qname'      => 'foo',
107980        'attributes' => array('key' => 'value', 'argh' => 't�t�'),
107981        'content'    => 
107982            'Also XHTML-tags can be created '
107983            . 'and HTML entities can be replaced � � � � <>.'
107984    );
107985
107986    print 'creating a tag with HTML entities:<br>';
107987    print htmlentities(XML_Util::createTagFromArray($tag, XML_UTIL_ENTITIES_HTML));
107988    print "\n<br><br>\n";
107989
107990    /**
107991    * creating an XML tag with createTag
107992    */
107993    print 'creating a tag with createTag:<br>';
107994    print htmlentities(XML_Util::createTag('myNs:myTag', 
107995        array('foo' => 'bar'), 
107996        'This is inside the tag', 
107997        'http://www.w3c.org/myNs#'));
107998    print "\n<br><br>\n";
107999
108000    
108001    /**
108002     * trying to create an XML tag with an array as content
108003     */
108004    $tag = array(
108005        'qname'   => 'bar',
108006        'content' => array('foo' => 'bar')
108007    );
108008    print 'trying to create an XML tag with an array as content:<br>';
108009    print '<pre>';
108010    print_r(XML_Util::createTagFromArray($tag));
108011    print '</pre>';
108012    print "\n<br><br>\n";
108013    
108014    /**
108015     * trying to create an XML tag without a name
108016     */
108017    $tag = array(
108018        'attributes' => array('foo' => 'bar'),
108019    );
108020    print 'trying to create an XML tag without a name:<br>';
108021    print '<pre>';
108022    print_r(XML_Util::createTagFromArray($tag));
108023    print '</pre>';
108024    print "\n<br><br>\n";
108025?>
108026XML_Util-1.2.1/examples/example2.php100644   1750   1750       11441 11117075466  12467 <?php
108027
108028/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
108029
108030/**
108031 * Examples (file #2)
108032 *
108033 * several examples for the methods of XML_Util
108034 * 
108035 * PHP versions 4 and 5
108036 *
108037 * LICENSE:
108038 *
108039 * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
108040 * All rights reserved.
108041 *
108042 * Redistribution and use in source and binary forms, with or without
108043 * modification, are permitted provided that the following conditions
108044 * are met:
108045 *
108046 *    * Redistributions of source code must retain the above copyright
108047 *      notice, this list of conditions and the following disclaimer.
108048 *    * Redistributions in binary form must reproduce the above copyright
108049 *      notice, this list of conditions and the following disclaimer in the
108050 *      documentation and/or other materials provided with the distribution.
108051 *    * The name of the author may not be used to endorse or promote products
108052 *      derived from this software without specific prior written permission.
108053 *
108054 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
108055 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
108056 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
108057 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
108058 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
108059 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
108060 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
108061 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
108062 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
108063 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
108064 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
108065 *
108066 * @category   XML
108067 * @package    XML_Util
108068 * @subpackage Examples
108069 * @author     Stephan Schmidt <schst@php.net>
108070 * @copyright  2003-2008 Stephan Schmidt <schst@php.net>
108071 * @license    http://opensource.org/licenses/bsd-license New BSD License
108072 * @version    CVS: $Id: example2.php,v 1.11 2008/05/05 19:03:13 ashnazg Exp $
108073 * @link       http://pear.php.net/package/XML_Util
108074 */
108075
108076    /**
108077     * set error level
108078     */
108079    error_reporting(E_ALL);
108080
108081    require_once 'XML/Util.php';
108082
108083    /**
108084     * creating a start element
108085     */
108086    print 'creating a start element:<br>';
108087    print htmlentities(XML_Util::createStartElement('myNs:myTag', 
108088        array('foo' => 'bar'), 'http://www.w3c.org/myNs#'));
108089    print "\n<br><br>\n";
108090
108091
108092    /**
108093     * creating a start element
108094     */
108095    print 'creating a start element:<br>';
108096    print htmlentities(XML_Util::createStartElement('myTag', 
108097        array(), 'http://www.w3c.org/myNs#'));
108098    print "\n<br><br>\n";
108099
108100    /**
108101     * creating a start element
108102     */
108103    print 'creating a start element:<br>';
108104    print '<pre>';
108105    print htmlentities(XML_Util::createStartElement('myTag', 
108106        array('foo' => 'bar', 'argh' => 'tomato'), 
108107        'http://www.w3c.org/myNs#', true));
108108    print '</pre>';
108109    print "\n<br><br>\n";
108110
108111
108112    /**
108113     * creating an end element
108114     */
108115    print 'creating an end element:<br>';
108116    print htmlentities(XML_Util::createEndElement('myNs:myTag'));
108117    print "\n<br><br>\n";
108118
108119    /**
108120     * creating a CData section
108121     */
108122    print 'creating a CData section:<br>';
108123    print htmlentities(XML_Util::createCDataSection('I am content.'));
108124    print "\n<br><br>\n";
108125
108126    /**
108127     * creating a comment
108128     */
108129    print 'creating a comment:<br>';
108130    print htmlentities(XML_Util::createComment('I am a comment.'));
108131    print "\n<br><br>\n";
108132
108133    /**
108134     * creating an XML tag with multiline mode
108135     */
108136    $tag = array(
108137        'qname'        => 'foo:bar',
108138        'namespaceUri' => 'http://foo.com',
108139        'attributes'   => array('key' => 'value', 'argh' => 'fruit&vegetable'),
108140        'content'      => 'I\'m inside the tag & contain dangerous chars'
108141    );
108142
108143    print 'creating a tag with qualified name and namespaceUri:<br>';
108144    print '<pre>';
108145    print htmlentities(XML_Util::createTagFromArray($tag, 
108146        XML_UTIL_REPLACE_ENTITIES, true));
108147    print '</pre>';
108148    print "\n<br><br>\n";
108149
108150    /**
108151     * create an attribute string without replacing the entities
108152     */
108153    $atts = array('series' => 'Starsky &amp; Hutch', 'channel' => 'ABC');
108154    print 'creating a attribute string, '
108155        . 'entities in values already had been replaced:<br>';
108156    print htmlentities(XML_Util::attributesToString($atts, 
108157        true, false, false, false, XML_UTIL_ENTITIES_NONE));
108158    print "\n<br><br>\n";
108159
108160    /**
108161     * using the array-syntax for attributesToString()
108162     */
108163    $atts = array('series' => 'Starsky &amp; Hutch', 'channel' => 'ABC');
108164    print 'using the array-syntax for attributesToString()<br>';
108165    print htmlentities(XML_Util::attributesToString($atts, 
108166        array('entities' => XML_UTIL_ENTITIES_NONE)));
108167    print "\n<br><br>\n";
108168
108169
108170?>
108171XML_Util-1.2.1/tests/AllTests.php100644   1750   1750        7060 11117075466  12013 <?php
108172
108173/**
108174 * Master Unit Test Suite file for XML_Util
108175 * 
108176 * This top-level test suite file organizes 
108177 * all class test suite files, 
108178 * so that the full suite can be run 
108179 * by PhpUnit or via "pear run-tests -u". 
108180 *
108181 * PHP version 5
108182 *
108183 * @category   XML
108184 * @package    XML_Util
108185 * @subpackage UnitTesting
108186 * @author     Chuck Burgess <ashnazg@php.net>
108187 * @license    http://www.opensource.org/licenses/bsd-license.php New BSD License
108188 * @version    CVS: $Id: AllTests.php,v 1.5 2008/05/30 11:53:09 ashnazg Exp $
108189 * @link       http://pear.php.net/package/XML_Util
108190 * @since      1.2.0a1
108191 */
108192
108193
108194/**
108195 * Check PHP version... PhpUnit v3+ requires at least PHP v5.1.4
108196 */
108197if (version_compare(PHP_VERSION, "5.1.4") < 0) {
108198    // Cannnot run test suites
108199    echo 'Cannot run test suite via PhpUnit... requires at least PHP v5.1.4.' . PHP_EOL;
108200    echo 'Use "pear run-tests -p xml_util" to run the PHPT tests directly.' . PHP_EOL
108201;
108202    exit(1);
108203}
108204
108205
108206/**
108207 * Derive the "main" method name
108208 * @internal PhpUnit would have to rename PHPUnit_MAIN_METHOD to PHPUNIT_MAIN_METHOD
108209 *           to make this usage meet the PEAR CS... we cannot rename it here.
108210 */
108211if (!defined('PHPUnit_MAIN_METHOD')) {
108212    define('PHPUnit_MAIN_METHOD', 'XML_Util_AllTests::main');
108213}
108214
108215
108216/*
108217 * Files needed by PhpUnit
108218 */
108219require_once 'PHPUnit/Framework.php';
108220require_once 'PHPUnit/TextUI/TestRunner.php';
108221require_once 'PHPUnit/Extensions/PhptTestSuite.php';
108222
108223/*
108224 * You must add each additional class-level test suite file here
108225 */
108226// there are no PhpUnit test files... only PHPTs.. so nothing is listed here
108227
108228/**
108229 * directory where PHPT tests are located
108230 */
108231define('XML_UTIL_DIR_PHPT', dirname(__FILE__));
108232
108233/**
108234 * Master Unit Test Suite class for XML_Util
108235 * 
108236 * This top-level test suite class organizes 
108237 * all class test suite files, 
108238 * so that the full suite can be run 
108239 * by PhpUnit or via "pear run-tests -up xml_util". 
108240 *
108241 * @category   XML
108242 * @package    XML_Util
108243 * @subpackage UnitTesting
108244 * @author     Chuck Burgess <ashnazg@php.net>
108245 * @license    http://www.opensource.org/licenses/bsd-license.php New BSD License
108246 * @version    Release: @package_version@
108247 * @link       http://pear.php.net/package/XML_Util
108248 * @since      1.2.0a1
108249 */
108250class XML_Util_AllTests
108251{
108252
108253    /**
108254     * Launches the TextUI test runner
108255     *
108256     * @return void
108257     * @uses PHPUnit_TextUI_TestRunner
108258     */
108259    public static function main()
108260    {
108261        PHPUnit_TextUI_TestRunner::run(self::suite());
108262    }
108263
108264
108265    /**
108266     * Adds all class test suites into the master suite
108267     *
108268     * @return PHPUnit_Framework_TestSuite a master test suite
108269     *                                     containing all class test suites
108270     * @uses PHPUnit_Framework_TestSuite
108271     */ 
108272    public static function suite()
108273    {
108274        $suite = new PHPUnit_Framework_TestSuite(
108275            'XML_Util Full Suite of Unit Tests');
108276
108277        /*
108278         * You must add each additional class-level test suite name here
108279         */
108280        // there are no PhpUnit test files... only PHPTs.. so nothing is listed here
108281
108282        /*
108283         * add PHPT tests
108284         */
108285        $phpt = new PHPUnit_Extensions_PhptTestSuite(XML_UTIL_DIR_PHPT);
108286        $suite->addTestSuite($phpt);
108287
108288        return $suite;
108289    }
108290}
108291
108292/**
108293 * Call the main method if this file is executed directly
108294 * @internal PhpUnit would have to rename PHPUnit_MAIN_METHOD to PHPUNIT_MAIN_METHOD
108295 *           to make this usage meet the PEAR CS... we cannot rename it here.
108296 */
108297if (PHPUnit_MAIN_METHOD == 'XML_Util_AllTests::main') {
108298    XML_Util_AllTests::main();
108299}
108300
108301/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
108302?>
108303XML_Util-1.2.1/tests/testBasic_apiVersion.phpt100644   1750   1750         701 11117075466  14537 --TEST--
108304XML_Util::apiVersion() basic tests
108305--CREDITS--
108306Chuck Burgess <ashnazg@php.net>
108307# created for v1.2.0a1 2008-05-04
108308--FILE--
108309<?php
108310require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
108311echo '=====XML_Util::apiVersion() basic tests=====' . PHP_EOL . PHP_EOL;
108312
108313echo "TEST:  basic apiVersion() call" . PHP_EOL;
108314echo XML_Util::apiVersion() . PHP_EOL;
108315?>
108316--EXPECT--
108317=====XML_Util::apiVersion() basic tests=====
108318
108319TEST:  basic apiVersion() call
1083201.1
108321XML_Util-1.2.1/tests/testBasic_attributesToString.phpt100644   1750   1750        7057 11117075466  16333 --TEST--
108322XML_Util::attributesToString() basic tests
108323--CREDITS--
108324Chuck Burgess <ashnazg@php.net>
108325# created for v1.2.0a1 2008-05-04
108326--FILE--
108327<?php
108328require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
108329echo '=====XML_Util::attributesToString() basic tests=====' . PHP_EOL . PHP_EOL;
108330
108331$att = array("foo" => "bar", "boo" => "baz");
108332$sort1 = array(
108333    'multiline' => true, 
108334    'indent'    => '----', 
108335    'linebreak' => "^", 
108336    'entities'  => XML_UTIL_ENTITIES_XML, 
108337    'sort'      => true
108338);
108339$sort2 = array(
108340    'multiline' => true, 
108341    'indent'    => '----', 
108342    'linebreak' => "^", 
108343    'entities'  => XML_UTIL_ENTITIES_XML, 
108344);
108345
108346echo "TEST:  basic usage" . PHP_EOL;
108347echo XML_Util::attributesToString($att) . PHP_EOL . PHP_EOL;
108348
108349echo "TEST:  explicit \$sort = true" . PHP_EOL;
108350echo XML_Util::attributesToString($att, true) . PHP_EOL . PHP_EOL;
108351
108352echo "TEST:  explicit \$sort = false" . PHP_EOL;
108353echo XML_Util::attributesToString($att, false) . PHP_EOL . PHP_EOL;
108354
108355echo "TEST:  explicit \$multiline = false" . PHP_EOL;
108356echo XML_Util::attributesToString($att, true, false) . PHP_EOL . PHP_EOL;
108357
108358echo "TEST:  explicit \$multiline = true" . PHP_EOL;
108359echo XML_Util::attributesToString($att, true, true) . PHP_EOL . PHP_EOL;
108360
108361echo "TEST:  explicit \$indent = '        ' (8 spaces)" . PHP_EOL;
108362echo XML_Util::attributesToString($att, true, true, '        ') . PHP_EOL . PHP_EOL;
108363
108364echo "TEST:  explicit \$linebreak = '^' (some dummy char)" . PHP_EOL;
108365echo XML_Util::attributesToString($att, true, true, '^') . PHP_EOL . PHP_EOL;
108366
108367echo "TEST:  passing \$sort array of options that includes 'sort'" . PHP_EOL;
108368echo XML_Util::attributesToString($att, $sort1) . PHP_EOL . PHP_EOL;
108369
108370echo "TEST:  passing \$sort array of options that doesn't include 'sort'" . PHP_EOL;
108371echo XML_Util::attributesToString($att, $sort2) . PHP_EOL . PHP_EOL;
108372
108373echo "TEST:  do not replace entities" . PHP_EOL;
108374$arr = array("foo" => "b@&r", "boo" => "b><z");
108375echo XML_Util::attributesToString($arr, true, false, '    ', PHP_EOL, 
108376    XML_UTIL_ENTITIES_NONE) . PHP_EOL . PHP_EOL;
108377
108378echo "TEST:  replace all XML entities" . PHP_EOL;
108379$arr = array("foo" => "b@&r", "boo" => "b><z");
108380echo XML_Util::attributesToString($arr, true, false, '    ', PHP_EOL, 
108381    XML_UTIL_ENTITIES_XML) . PHP_EOL . PHP_EOL;
108382
108383echo "TEST:  replace only required XML entities" . PHP_EOL;
108384$arr = array("foo" => "b@&r", "boo" => "b><z");
108385echo XML_Util::attributesToString($arr, true, false, '    ', PHP_EOL, 
108386    XML_UTIL_ENTITIES_XML_REQUIRED) . PHP_EOL . PHP_EOL;
108387
108388echo "TEST:  replace HTML entities" . PHP_EOL;
108389$arr = array("foo" => "b@&r", "boo" => "b><z");
108390echo XML_Util::attributesToString($arr, true, false, '    ', PHP_EOL, 
108391    XML_UTIL_ENTITIES_HTML) . PHP_EOL . PHP_EOL;
108392?>
108393--EXPECT--
108394=====XML_Util::attributesToString() basic tests=====
108395
108396TEST:  basic usage
108397 boo="baz" foo="bar"
108398
108399TEST:  explicit $sort = true
108400 boo="baz" foo="bar"
108401
108402TEST:  explicit $sort = false
108403 foo="bar" boo="baz"
108404
108405TEST:  explicit $multiline = false
108406 boo="baz" foo="bar"
108407
108408TEST:  explicit $multiline = true
108409 boo="baz"
108410    foo="bar"
108411
108412TEST:  explicit $indent = '        ' (8 spaces)
108413 boo="baz"
108414        foo="bar"
108415
108416TEST:  explicit $linebreak = '^' (some dummy char)
108417 boo="baz"
108418^foo="bar"
108419
108420TEST:  passing $sort array of options that includes 'sort'
108421 boo="baz"
108422----foo="bar"
108423
108424TEST:  passing $sort array of options that doesn't include 'sort'
108425 boo="baz"
108426----foo="bar"
108427
108428TEST:  do not replace entities
108429 boo="b><z" foo="b@&r"
108430
108431TEST:  replace all XML entities
108432 boo="b&gt;&lt;z" foo="b@&amp;r"
108433
108434TEST:  replace only required XML entities
108435 boo="b>&lt;z" foo="b@&amp;r"
108436
108437TEST:  replace HTML entities
108438 boo="b&gt;&lt;z" foo="b@&amp;r"
108439XML_Util-1.2.1/tests/testBasic_collapseEmptyTags.phpt100644   1750   1750        3403 11117075466  16102 --TEST--
108440XML_Util::collapseEmptyTags() basic tests
108441--CREDITS--
108442Chuck Burgess <ashnazg@php.net>
108443# created for v1.2.0a1 2008-05-04
108444--FILE--
108445<?php
108446require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
108447echo '=====XML_Util::collapseEmptyTags() basic tests=====' . PHP_EOL . PHP_EOL;
108448
108449$emptyTag = "<foo></foo>";
108450$otherTag = "<bar>baz</bar>";
108451$xhtmlTag = "<b></b>";
108452
108453echo "TEST:  basic usage" . PHP_EOL;
108454echo XML_Util::collapseEmptyTags($emptyTag) . PHP_EOL . PHP_EOL;
108455
108456echo "TEST:  basic usage alongside non-empty tag" . PHP_EOL;
108457echo XML_Util::collapseEmptyTags($emptyTag . $otherTag) . PHP_EOL . PHP_EOL;
108458
108459echo "TEST:  one empty tag, with COLLAPSE_ALL set" . PHP_EOL;
108460echo XML_Util::collapseEmptyTags($emptyTag, XML_UTIL_COLLAPSE_ALL) . PHP_EOL . PHP_EOL;
108461
108462echo "TEST:  one empty tag alongside non-empty tag, with COLLAPSE_ALL set" . PHP_EOL;
108463echo XML_Util::collapseEmptyTags($emptyTag . $otherTag, XML_UTIL_COLLAPSE_ALL) . PHP_EOL . PHP_EOL;
108464
108465echo "TEST:  one empty tag, with COLLAPSE_XHTML_ONLY set" . PHP_EOL;
108466echo XML_Util::collapseEmptyTags($emptyTag, XML_UTIL_COLLAPSE_XHTML_ONLY) . PHP_EOL . PHP_EOL;
108467
108468echo "TEST:  one empty tag alongside non-empty tag, with COLLAPSE_XHTML_ONLY set" . PHP_EOL;
108469echo XML_Util::collapseEmptyTags($emptyTag . $xhtmlTag . $otherTag, XML_UTIL_COLLAPSE_XHTML_ONLY) . PHP_EOL . PHP_EOL;
108470?>
108471--EXPECT--
108472=====XML_Util::collapseEmptyTags() basic tests=====
108473
108474TEST:  basic usage
108475<foo />
108476
108477TEST:  basic usage alongside non-empty tag
108478<foo /><bar>baz</bar>
108479
108480TEST:  one empty tag, with COLLAPSE_ALL set
108481<foo />
108482
108483TEST:  one empty tag alongside non-empty tag, with COLLAPSE_ALL set
108484<foo /><bar>baz</bar>
108485
108486TEST:  one empty tag, with COLLAPSE_XHTML_ONLY set
108487<foo></foo>
108488
108489TEST:  one empty tag alongside non-empty tag, with COLLAPSE_XHTML_ONLY set
108490<foo></foo><b></b><bar>baz</bar>
108491XML_Util-1.2.1/tests/testBasic_createCDataSection.phpt100644   1750   1750         756 11117075466  16117 --TEST--
108492XML_Util::createCDataSection() basic tests
108493--CREDITS--
108494Chuck Burgess <ashnazg@php.net>
108495# created for v1.2.0a1 2008-05-04
108496--FILE--
108497<?php
108498require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
108499echo '=====XML_Util::createCDataSection() basic tests=====' . PHP_EOL . PHP_EOL;
108500
108501echo "TEST:  basic usage" . PHP_EOL;
108502echo XML_Util::createCDataSection("I am content.") . PHP_EOL;
108503?>
108504--EXPECT--
108505=====XML_Util::createCDataSection() basic tests=====
108506
108507TEST:  basic usage
108508<![CDATA[I am content.]]>
108509XML_Util-1.2.1/tests/testBasic_createComment.phpt100644   1750   1750         727 11117075466  15216 --TEST--
108510XML_Util::createComment() basic tests
108511--CREDITS--
108512Chuck Burgess <ashnazg@php.net>
108513# created for v1.2.0a1 2008-05-04
108514--FILE--
108515<?php
108516require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
108517echo '=====XML_Util::createComment() basic tests=====' . PHP_EOL . PHP_EOL;
108518
108519echo "TEST:  basic usage" . PHP_EOL;
108520echo XML_Util::createComment("I am comment.") . PHP_EOL;
108521?>
108522--EXPECT--
108523=====XML_Util::createComment() basic tests=====
108524
108525TEST:  basic usage
108526<!-- I am comment. -->
108527XML_Util-1.2.1/tests/testBasic_createEndElement.phpt100644   1750   1750        1270 11117075466  15646 --TEST--
108528XML_Util::createEndElement() basic tests
108529--CREDITS--
108530Chuck Burgess <ashnazg@php.net>
108531# created for v1.2.0a1 2008-05-04
108532--FILE--
108533<?php
108534require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
108535echo '=====XML_Util::createEndElement() basic tests=====' . PHP_EOL . PHP_EOL;
108536
108537echo "TEST:  basic usage (myTag)" . PHP_EOL;
108538echo XML_Util::createEndElement("myTag") . PHP_EOL . PHP_EOL;
108539
108540echo "TEST:  basic usage with a namespaced tag (myNs:myTag)" . PHP_EOL;
108541echo XML_Util::createEndElement("myNs:myTag") . PHP_EOL . PHP_EOL;
108542?>
108543--EXPECT--
108544=====XML_Util::createEndElement() basic tests=====
108545
108546TEST:  basic usage (myTag)
108547</myTag>
108548
108549TEST:  basic usage with a namespaced tag (myNs:myTag)
108550</myNs:myTag>
108551XML_Util-1.2.1/tests/testBasic_createStartElement.phpt100644   1750   1750        7275 11117075466  16250 --TEST--
108552XML_Util::createStartElement() basic tests
108553--CREDITS--
108554Chuck Burgess <ashnazg@php.net>
108555# created for v1.2.0a1 2008-05-04
108556--FILE--
108557<?php
108558require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
108559echo '=====XML_Util::createStartElement() basic tests=====' . PHP_EOL . PHP_EOL;
108560
108561echo "TEST:  tag only" . PHP_EOL;
108562echo XML_Util::createStartElement(
108563    "myNs:myTag"
108564) . PHP_EOL . PHP_EOL;
108565
108566echo "TEST:  tag with attributes" . PHP_EOL;
108567echo XML_Util::createStartElement(
108568    "myNs:myTag", 
108569    array("foo" => "bar")
108570) . PHP_EOL . PHP_EOL;
108571
108572echo "TEST:  tag only, passing '' as attribute arg" . PHP_EOL;
108573echo XML_Util::createStartElement(
108574    'myNs:myTag',
108575    ''
108576) . PHP_EOL . PHP_EOL;
108577
108578echo "TEST:  tag with attributes and namespace" . PHP_EOL;
108579echo XML_Util::createStartElement(
108580    "myNs:myTag", 
108581    array("foo" => "bar"),
108582    "http://www.w3c.org/myNs#"
108583) . PHP_EOL . PHP_EOL;
108584
108585echo "TEST:  tag with empty attributes, whose namespaceUri is not a full namespace" . PHP_EOL;
108586echo XML_Util::createStartElement(
108587    'myTag',
108588    '',
108589    'foo'
108590) . PHP_EOL . PHP_EOL;
108591
108592echo "TEST:  tag with attributes, namespace, and multiline = true" . PHP_EOL;
108593echo XML_Util::createStartElement(
108594    "myNs:myTag", 
108595    array("foo" => "bar"),
108596    "http://www.w3c.org/myNs#",
108597    true
108598) . PHP_EOL . PHP_EOL;
108599
108600echo "TEST:  tag with attributes, namespace, multiline = true, and indent = (2 spaces only)" . PHP_EOL;
108601echo XML_Util::createStartElement(
108602    "myNs:myTag", 
108603    array("foo" => "bar"),
108604    "http://www.w3c.org/myNs#",
108605    true,
108606    '  '
108607) . PHP_EOL . PHP_EOL;
108608
108609echo "TEST:  tag with attributes, namespace, multiline = true, indent = (2 spaces only), and linebreak = '^'" . PHP_EOL;
108610echo XML_Util::createStartElement(
108611    "myNs:myTag", 
108612    array("foo" => "bar"),
108613    "http://www.w3c.org/myNs#",
108614    true,
108615    '  ',
108616    '^'
108617) . PHP_EOL . PHP_EOL;
108618
108619echo "TEST:  tag with attributes, namespace, multiline = true, indent = (2 spaces only), linebreak = '^', and sortAttributes = true" . PHP_EOL;
108620echo XML_Util::createStartElement(
108621    "myNs:myTag", 
108622    array("foo" => "bar", "boo" => "baz"),
108623    "http://www.w3c.org/myNs#",
108624    true,
108625    '  ',
108626    '^',
108627    true
108628) . PHP_EOL . PHP_EOL;
108629
108630echo "TEST:  tag with attributes, namespace, multiline = true, indent = (2 spaces only), linebreak = '^', and sortAttributes = false" . PHP_EOL;
108631echo XML_Util::createStartElement(
108632    "myNs:myTag", 
108633    array("foo" => "bar", "boo" => "baz"),
108634    "http://www.w3c.org/myNs#",
108635    true,
108636    '  ',
108637    '^',
108638    false
108639) . PHP_EOL . PHP_EOL;
108640?>
108641--EXPECT--
108642=====XML_Util::createStartElement() basic tests=====
108643
108644TEST:  tag only
108645<myNs:myTag>
108646
108647TEST:  tag with attributes
108648<myNs:myTag foo="bar">
108649
108650TEST:  tag only, passing '' as attribute arg
108651<myNs:myTag>
108652
108653TEST:  tag with attributes and namespace
108654<myNs:myTag foo="bar" xmlns:myNs="http://www.w3c.org/myNs#">
108655
108656TEST:  tag with empty attributes, whose namespaceUri is not a full namespace
108657<myTag xmlns="foo">
108658
108659TEST:  tag with attributes, namespace, and multiline = true
108660<myNs:myTag foo="bar"
108661            xmlns:myNs="http://www.w3c.org/myNs#">
108662
108663TEST:  tag with attributes, namespace, multiline = true, and indent = (2 spaces only)
108664<myNs:myTag foo="bar"
108665  xmlns:myNs="http://www.w3c.org/myNs#">
108666
108667TEST:  tag with attributes, namespace, multiline = true, indent = (2 spaces only), and linebreak = '^'
108668<myNs:myTag foo="bar"^  xmlns:myNs="http://www.w3c.org/myNs#">
108669
108670TEST:  tag with attributes, namespace, multiline = true, indent = (2 spaces only), linebreak = '^', and sortAttributes = true
108671<myNs:myTag boo="baz"^  foo="bar"^  xmlns:myNs="http://www.w3c.org/myNs#">
108672
108673TEST:  tag with attributes, namespace, multiline = true, indent = (2 spaces only), linebreak = '^', and sortAttributes = false
108674<myNs:myTag foo="bar"^  boo="baz"^  xmlns:myNs="http://www.w3c.org/myNs#">
108675XML_Util-1.2.1/tests/testBasic_createTag.phpt100644   1750   1750       13434 11117075466  14366 --TEST--
108676XML_Util::createTag() basic tests
108677--CREDITS--
108678Chuck Burgess <ashnazg@php.net>
108679# created for v1.2.0a1 2008-05-04
108680--FILE--
108681<?php
108682require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
108683echo '=====XML_Util::createTag() basic tests=====' . PHP_EOL . PHP_EOL;
108684
108685echo "TEST:  tag with attribute" . PHP_EOL;
108686echo XML_Util::createTag(
108687    "myNs:myTag", 
108688    array("foo" => "bar")
108689) . PHP_EOL . PHP_EOL;
108690
108691echo "TEST:  tag with attribute and content" . PHP_EOL;
108692echo XML_Util::createTag(
108693    "myNs:myTag", 
108694    array("foo" => "bar"), 
108695    "This is inside the tag"
108696) . PHP_EOL . PHP_EOL;
108697
108698echo "TEST:  tag with attribute, content, and namespace" . PHP_EOL;
108699echo XML_Util::createTag(
108700    "myNs:myTag", 
108701    array("foo" => "bar"), 
108702    "This is inside the tag",
108703    "http://www.w3c.org/myNs#"
108704) . PHP_EOL . PHP_EOL;
108705
108706echo "TEST:  tag with attribute, content, namespace, and REPLACE_ENTITIES" . PHP_EOL;
108707echo XML_Util::createTag(
108708    "myNs:myTag", 
108709    array("foo" => "bar"), 
108710    "This is inside the tag and has < & @ > in it",
108711    "http://www.w3c.org/myNs#",
108712    XML_UTIL_REPLACE_ENTITIES
108713) . PHP_EOL . PHP_EOL;
108714
108715echo "TEST:  tag with attribute, content, namespace, and CDATA_SECTION" . PHP_EOL;
108716echo XML_Util::createTag(
108717    "myNs:myTag", 
108718    array("foo" => "bar"), 
108719    "This is inside the tag and has < & @ > in it",
108720    "http://www.w3c.org/myNs#",
108721    XML_UTIL_CDATA_SECTION
108722) . PHP_EOL . PHP_EOL;
108723
108724echo "TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, and multiline = false" . PHP_EOL;
108725echo XML_Util::createTag(
108726    "myNs:myTag", 
108727    array("foo" => "bar"), 
108728    "This is inside the tag and has < & @ > in it",
108729    "http://www.w3c.org/myNs#",
108730    XML_UTIL_REPLACE_ENTITIES,
108731    false
108732) . PHP_EOL . PHP_EOL;
108733
108734echo "TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, and multiline = true" . PHP_EOL;
108735echo XML_Util::createTag(
108736    "myNs:myTag", 
108737    array("foo" => "bar"), 
108738    "This is inside the tag and has < & @ > in it",
108739    "http://www.w3c.org/myNs#",
108740    XML_UTIL_REPLACE_ENTITIES,
108741    true
108742) . PHP_EOL . PHP_EOL;
108743
108744echo "TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, multiline = true, and indent = (2 spaces)" . PHP_EOL;
108745echo XML_Util::createTag(
108746    "myNs:myTag", 
108747    array("foo" => "bar"), 
108748    "This is inside the tag and has < & @ > in it",
108749    "http://www.w3c.org/myNs#",
108750    XML_UTIL_REPLACE_ENTITIES,
108751    true,
108752    '  '
108753) . PHP_EOL . PHP_EOL;
108754
108755echo "TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, multiline = true, indent = (2 spaces), and linebreak = '^'" . PHP_EOL;
108756echo XML_Util::createTag(
108757    "myNs:myTag", 
108758    array("foo" => "bar"), 
108759    "This is inside the tag and has < & @ > in it",
108760    "http://www.w3c.org/myNs#",
108761    XML_UTIL_REPLACE_ENTITIES,
108762    true,
108763    '  ',
108764    '^'
108765) . PHP_EOL . PHP_EOL;
108766
108767echo "TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, multiline = true, indent = (2 spaces), linebreak = '^', and sortAttributes = true" . PHP_EOL;
108768echo XML_Util::createTag(
108769    "myNs:myTag", 
108770    array("foo" => "bar", "boo" => "baz"), 
108771    "This is inside the tag and has < & @ > in it",
108772    "http://www.w3c.org/myNs#",
108773    XML_UTIL_REPLACE_ENTITIES,
108774    true,
108775    '  ',
108776    '^',
108777    true
108778) . PHP_EOL . PHP_EOL;
108779
108780echo "TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, multiline = true, indent = (2 spaces), linebreak = '^', and sortAttributes = false" . PHP_EOL;
108781echo XML_Util::createTag(
108782    "myNs:myTag", 
108783    array("foo" => "bar", "boo" => "baz"), 
108784    "This is inside the tag and has < & @ > in it",
108785    "http://www.w3c.org/myNs#",
108786    XML_UTIL_REPLACE_ENTITIES,
108787    true,
108788    '  ',
108789    '^',
108790    false
108791) . PHP_EOL . PHP_EOL;
108792?>
108793--EXPECT--
108794=====XML_Util::createTag() basic tests=====
108795
108796TEST:  tag with attribute
108797<myNs:myTag foo="bar" />
108798
108799TEST:  tag with attribute and content
108800<myNs:myTag foo="bar">This is inside the tag</myNs:myTag>
108801
108802TEST:  tag with attribute, content, and namespace
108803<myNs:myTag foo="bar" xmlns:myNs="http://www.w3c.org/myNs#">This is inside the tag</myNs:myTag>
108804
108805TEST:  tag with attribute, content, namespace, and REPLACE_ENTITIES
108806<myNs:myTag foo="bar" xmlns:myNs="http://www.w3c.org/myNs#">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>
108807
108808TEST:  tag with attribute, content, namespace, and CDATA_SECTION
108809<myNs:myTag foo="bar" xmlns:myNs="http://www.w3c.org/myNs#"><![CDATA[This is inside the tag and has < & @ > in it]]></myNs:myTag>
108810
108811TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, and multiline = false
108812<myNs:myTag foo="bar" xmlns:myNs="http://www.w3c.org/myNs#">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>
108813
108814TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, and multiline = true
108815<myNs:myTag foo="bar"
108816            xmlns:myNs="http://www.w3c.org/myNs#">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>
108817
108818TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, multiline = true, and indent = (2 spaces)
108819<myNs:myTag foo="bar"
108820  xmlns:myNs="http://www.w3c.org/myNs#">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>
108821
108822TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, multiline = true, indent = (2 spaces), and linebreak = '^'
108823<myNs:myTag foo="bar"^  xmlns:myNs="http://www.w3c.org/myNs#">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>
108824
108825TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, multiline = true, indent = (2 spaces), linebreak = '^', and sortAttributes = true
108826<myNs:myTag boo="baz"^  foo="bar"^  xmlns:myNs="http://www.w3c.org/myNs#">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>
108827
108828TEST:  tag with attribute, content, namespace, REPLACE_ENTITIES, multiline = true, indent = (2 spaces), linebreak = '^', and sortAttributes = false
108829<myNs:myTag foo="bar"^  boo="baz"^  xmlns:myNs="http://www.w3c.org/myNs#">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>
108830XML_Util-1.2.1/tests/testBasic_createTagFromArray.phpt100644   1750   1750       22131 11117075466  16203 --TEST--
108831XML_Util::createTagFromArray() basic tests
108832--CREDITS--
108833Chuck Burgess <ashnazg@php.net>
108834# created for v1.2.0a1 2008-05-04
108835--FILE--
108836<?php
108837require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
108838echo '=====XML_Util::createTagFromArray() basic tests=====' . PHP_EOL . PHP_EOL;
108839
108840$bad = array(
108841    "foo" => "bar",
108842);
108843$tag1 = array(
108844    "qname"        => "foo:bar",
108845);
108846$tag2 = array(
108847    "qname"        => "foo:bar",
108848    "namespaceUri" => "http://foo.com",
108849);
108850$tag3 = array(
108851    "qname"        => "foo:bar",
108852    "namespaceUri" => "http://foo.com",
108853    "attributes"   => array( "key" => "value", "argh" => "fruit&vegetable" ),
108854);
108855$tag4 = array(
108856    "qname"        => "foo:bar",
108857    "namespaceUri" => "http://foo.com",
108858    "attributes"   => array( "key" => "value", "argh" => "fruit&vegetable" ),
108859    "content"      => "I'm inside the tag",
108860);
108861$tag5 = array(
108862    "qname"        => "foo:bar",
108863    "attributes"   => array( "key" => "value", "argh" => "fruit&vegetable" ),
108864    "content"      => "I'm inside the tag",
108865);
108866$tag6 = array(
108867    "qname"        => "foo:bar",
108868    "namespaceUri" => "http://foo.com",
108869    "content"      => "I'm inside the tag",
108870);
108871$tag7 = array(
108872    "namespaceUri" => "http://foo.com",
108873    "attributes"   => array( "key" => "value", "argh" => "fruit&vegetable" ),
108874    "content"      => "I'm inside the tag",
108875);
108876
108877$tag8 = array(
108878    'content'      => array('foo', 'bar')
108879);
108880
108881$tag9 = array(
108882    'qname'        => 'foo:bar',
108883    'namespaces'   => array('ns1' => 'uri1', 'ns2' => 'uri2')
108884);
108885
108886$tag10 = array(
108887    'namespace'    => 'http://foo.org',
108888    'localPart'    => 'bar'
108889);
108890
108891$tag11 = array(
108892    'namespace'    => '',
108893    'localPart'    => 'bar'
108894);
108895
108896$tag12 = array(
108897    'localPart'    => 'foo',
108898    'namespaceUri' => 'http://bar.org'
108899);
108900
108901echo "TEST:  basic usage with an invalid array" . PHP_EOL;
108902echo XML_Util::createTagFromArray($bad) . PHP_EOL . PHP_EOL;
108903
108904echo "TEST:  basic usage with a valid array (qname only)" . PHP_EOL;
108905echo XML_Util::createTagFromArray($tag1) . PHP_EOL . PHP_EOL;
108906
108907echo "TEST:  basic usage with a valid array (qname and namespaceUri)" . PHP_EOL;
108908echo XML_Util::createTagFromArray($tag2) . PHP_EOL . PHP_EOL;
108909
108910echo "TEST:  basic usage with a valid array (qname, namespaceUri, and attributes)" . PHP_EOL;
108911echo XML_Util::createTagFromArray($tag3) . PHP_EOL . PHP_EOL;
108912
108913echo "TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content)" . PHP_EOL;
108914echo XML_Util::createTagFromArray($tag4) . PHP_EOL . PHP_EOL;
108915
108916echo "TEST:  basic usage with a valid array (qname, attributes, and content)" . PHP_EOL;
108917echo XML_Util::createTagFromArray($tag5) . PHP_EOL . PHP_EOL;
108918
108919echo "TEST:  basic usage with a valid array (qname, namespaceUri, and content)" . PHP_EOL;
108920echo XML_Util::createTagFromArray($tag6) . PHP_EOL . PHP_EOL;
108921
108922echo "TEST:  basic usage with a valid array (namespaceUri, attributes, and content)" . PHP_EOL;
108923echo XML_Util::createTagFromArray($tag7) . PHP_EOL . PHP_EOL;
108924
108925echo "TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), plus REPLACE_ENTITIES" . PHP_EOL;
108926echo XML_Util::createTagFromArray($tag4, XML_UTIL_REPLACE_ENTITIES) . PHP_EOL . PHP_EOL;
108927
108928echo "TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), plus ENTITIES_NONE" . PHP_EOL;
108929echo XML_Util::createTagFromArray($tag4, XML_UTIL_ENTITIES_NONE) . PHP_EOL . PHP_EOL;
108930
108931echo "TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, and multiline = false" . PHP_EOL;
108932echo XML_Util::createTagFromArray($tag4, XML_UTIL_REPLACE_ENTITIES, false) . PHP_EOL . PHP_EOL;
108933
108934echo "TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, and multiline = true" . PHP_EOL;
108935echo XML_Util::createTagFromArray($tag4, XML_UTIL_REPLACE_ENTITIES, true) . PHP_EOL . PHP_EOL;
108936
108937echo "TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, multiline = true, and indent = (2 spaces)" . PHP_EOL;
108938echo XML_Util::createTagFromArray($tag4, XML_UTIL_REPLACE_ENTITIES, true, '  ') . PHP_EOL . PHP_EOL;
108939
108940echo "TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, multiline = true, indent = (2 spaces), and linebreak = '^'" . PHP_EOL;
108941echo XML_Util::createTagFromArray($tag4, XML_UTIL_REPLACE_ENTITIES, true, '  ', '^') . PHP_EOL . PHP_EOL;
108942
108943echo "TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, multiline = true, indent = (2 spaces), linebreak = '^', and sortAttributes = true" . PHP_EOL;
108944echo XML_Util::createTagFromArray($tag4, XML_UTIL_REPLACE_ENTITIES, true, '  ', '^', true) . PHP_EOL . PHP_EOL;
108945
108946echo "TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, multiline = true, indent = (2 spaces), linebreak = '^', and sortAttributes = false" . PHP_EOL;
108947echo XML_Util::createTagFromArray($tag4, XML_UTIL_REPLACE_ENTITIES, true, '  ', '^', false) . PHP_EOL . PHP_EOL;
108948
108949echo 'TEST:  cause a non-scalar error on the content tag' . PHP_EOL;
108950echo XML_Util::createTagFromArray($tag8) . PHP_EOL . PHP_EOL;
108951
108952echo 'TEST:  handle an array of namespaces being passed' . PHP_EOL;
108953echo XML_Util::createTagFromArray($tag9) . PHP_EOL . PHP_EOL;
108954
108955echo 'TEST:  qname is derived from namespace + localPart' . PHP_EOL;
108956echo XML_Util::createTagFromArray($tag10) . PHP_EOL . PHP_EOL;
108957
108958echo 'TEST:  qname is derived from localPart only' . PHP_EOL;
108959echo XML_Util::createTagFromArray($tag11) . PHP_EOL . PHP_EOL;
108960
108961echo 'TEST:  namespaceUri is given, but namespace is not' . PHP_EOL;
108962echo XML_Util::createTagFromArray($tag12) . PHP_EOL . PHP_EOL;
108963?>
108964--EXPECT--
108965=====XML_Util::createTagFromArray() basic tests=====
108966
108967TEST:  basic usage with an invalid array
108968You must either supply a qualified name (qname) or local tag name (localPart).
108969
108970TEST:  basic usage with a valid array (qname only)
108971<foo:bar />
108972
108973TEST:  basic usage with a valid array (qname and namespaceUri)
108974<foo:bar xmlns:foo="http://foo.com" />
108975
108976TEST:  basic usage with a valid array (qname, namespaceUri, and attributes)
108977<foo:bar argh="fruit&amp;vegetable" key="value" xmlns:foo="http://foo.com" />
108978
108979TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content)
108980<foo:bar argh="fruit&amp;vegetable" key="value" xmlns:foo="http://foo.com">I&apos;m inside the tag</foo:bar>
108981
108982TEST:  basic usage with a valid array (qname, attributes, and content)
108983<foo:bar argh="fruit&amp;vegetable" key="value">I&apos;m inside the tag</foo:bar>
108984
108985TEST:  basic usage with a valid array (qname, namespaceUri, and content)
108986<foo:bar xmlns:foo="http://foo.com">I&apos;m inside the tag</foo:bar>
108987
108988TEST:  basic usage with a valid array (namespaceUri, attributes, and content)
108989You must either supply a qualified name (qname) or local tag name (localPart).
108990
108991TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), plus REPLACE_ENTITIES
108992<foo:bar argh="fruit&amp;vegetable" key="value" xmlns:foo="http://foo.com">I&apos;m inside the tag</foo:bar>
108993
108994TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), plus ENTITIES_NONE
108995<foo:bar argh="fruit&vegetable" key="value" xmlns:foo="http://foo.com">I'm inside the tag</foo:bar>
108996
108997TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, and multiline = false
108998<foo:bar argh="fruit&amp;vegetable" key="value" xmlns:foo="http://foo.com">I&apos;m inside the tag</foo:bar>
108999
109000TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, and multiline = true
109001<foo:bar argh="fruit&amp;vegetable"
109002         key="value"
109003         xmlns:foo="http://foo.com">I&apos;m inside the tag</foo:bar>
109004
109005TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, multiline = true, and indent = (2 spaces)
109006<foo:bar argh="fruit&amp;vegetable"
109007  key="value"
109008  xmlns:foo="http://foo.com">I&apos;m inside the tag</foo:bar>
109009
109010TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, multiline = true, indent = (2 spaces), and linebreak = '^'
109011<foo:bar argh="fruit&amp;vegetable"^  key="value"^  xmlns:foo="http://foo.com">I&apos;m inside the tag</foo:bar>
109012
109013TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, multiline = true, indent = (2 spaces), linebreak = '^', and sortAttributes = true
109014<foo:bar argh="fruit&amp;vegetable"^  key="value"^  xmlns:foo="http://foo.com">I&apos;m inside the tag</foo:bar>
109015
109016TEST:  basic usage with a valid array (qname, namespaceUri, attributes, and content), REPLACE_ENTITIES, multiline = true, indent = (2 spaces), linebreak = '^', and sortAttributes = false
109017<foo:bar key="value"^  argh="fruit&amp;vegetable"^  xmlns:foo="http://foo.com">I&apos;m inside the tag</foo:bar>
109018
109019TEST:  cause a non-scalar error on the content tag
109020Supplied non-scalar value as tag content
109021
109022TEST:  handle an array of namespaces being passed
109023<foo:bar xmlns:ns1="uri1" xmlns:ns2="uri2" />
109024
109025TEST:  qname is derived from namespace + localPart
109026<http://foo.org:bar />
109027
109028TEST:  qname is derived from localPart only
109029<bar />
109030
109031TEST:  namespaceUri is given, but namespace is not
109032<foo xmlns="http://bar.org" />
109033XML_Util-1.2.1/tests/testBasic_getDocTypeDeclaration.phpt100644   1750   1750        2754 11117075466  16667 --TEST--
109034XML_Util::getDocTypeDeclaration() basic tests
109035--CREDITS--
109036Chuck Burgess <ashnazg@php.net>
109037# created for v1.2.0a1 2008-05-04
109038--FILE--
109039<?php
109040require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
109041echo '=====XML_Util::getDocTypeDeclaration() basic tests=====' . PHP_EOL . PHP_EOL;
109042
109043echo "TEST:  using root only" . PHP_EOL;
109044echo XML_Util::getDocTypeDeclaration("rootTag") . PHP_EOL . PHP_EOL;
109045
109046echo "TEST:  using root and a string URI" . PHP_EOL;
109047echo XML_Util::getDocTypeDeclaration("rootTag", "myDocType.dtd") . PHP_EOL . PHP_EOL;
109048
109049$uri = array(
109050    'uri' => 'http://pear.php.net/dtd/package-1.0',
109051    'id' => '-//PHP//PEAR/DTD PACKAGE 0.1'
109052);
109053$dtdEntry = '<!ELEMENT additionalInfo (#PCDATA)>';
109054
109055echo "TEST:  using root and an array URI" . PHP_EOL;
109056echo XML_Util::getDocTypeDeclaration("rootTag", $uri) . PHP_EOL . PHP_EOL;
109057
109058echo "TEST:  using root and an array URI and an internal DTD entry" . PHP_EOL;
109059echo XML_Util::getDocTypeDeclaration("rootTag", $uri, $dtdEntry) . PHP_EOL . PHP_EOL;
109060?>
109061--EXPECT--
109062=====XML_Util::getDocTypeDeclaration() basic tests=====
109063
109064TEST:  using root only
109065<!DOCTYPE rootTag>
109066
109067TEST:  using root and a string URI
109068<!DOCTYPE rootTag SYSTEM "myDocType.dtd">
109069
109070TEST:  using root and an array URI
109071<!DOCTYPE rootTag PUBLIC "-//PHP//PEAR/DTD PACKAGE 0.1" "http://pear.php.net/dtd/package-1.0">
109072
109073TEST:  using root and an array URI and an internal DTD entry
109074<!DOCTYPE rootTag PUBLIC "-//PHP//PEAR/DTD PACKAGE 0.1" "http://pear.php.net/dtd/package-1.0" [
109075<!ELEMENT additionalInfo (#PCDATA)>
109076]>
109077XML_Util-1.2.1/tests/testBasic_getXmlDeclaration.phpt100644   1750   1750        2217 11117075466  16052 --TEST--
109078XML_Util::getXmlDeclaration() basic tests
109079--CREDITS--
109080Chuck Burgess <ashnazg@php.net>
109081# created for v1.2.0a1 2008-05-04
109082--FILE--
109083<?php
109084require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
109085echo '=====XML_Util::getXmlDeclaration() basic tests=====' . PHP_EOL . PHP_EOL;
109086
109087echo "TEST:  using version only" . PHP_EOL;
109088echo XML_Util::getXMLDeclaration("1.0") . PHP_EOL . PHP_EOL;
109089
109090echo "TEST:  using version and encoding" . PHP_EOL;
109091echo XML_Util::getXMLDeclaration("1.0", "UTF-8") . PHP_EOL . PHP_EOL;
109092
109093echo "TEST:  using version, encoding, and standalone flag" . PHP_EOL;
109094echo XML_Util::getXMLDeclaration("1.0", "UTF-8", true) . PHP_EOL . PHP_EOL;
109095
109096echo "TEST:  using version and standalone flag" . PHP_EOL;
109097echo XML_Util::getXMLDeclaration("1.0", null, true) . PHP_EOL . PHP_EOL;
109098?>
109099--EXPECT--
109100=====XML_Util::getXmlDeclaration() basic tests=====
109101
109102TEST:  using version only
109103<?xml version="1.0"?>
109104
109105TEST:  using version and encoding
109106<?xml version="1.0" encoding="UTF-8"?>
109107
109108TEST:  using version, encoding, and standalone flag
109109<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
109110
109111TEST:  using version and standalone flag
109112<?xml version="1.0" standalone="yes"?>
109113XML_Util-1.2.1/tests/testBasic_isValidName.phpt100644   1750   1750        2602 11117075466  14636 --TEST--
109114XML_Util::isValidName() basic tests
109115--CREDITS--
109116Chuck Burgess <ashnazg@php.net>
109117# created for v1.2.0a1 2008-05-04
109118--FILE--
109119<?php
109120require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
109121echo '=====XML_Util::isValidName() basic tests=====' . PHP_EOL . PHP_EOL;
109122
109123echo "TEST:  valid tag" . PHP_EOL;
109124$result = XML_Util::isValidName("alpha-x_y_z.123");
109125if (is_a($result, 'PEAR_Error')) {
109126    print "Invalid XML name: " . $result->getMessage() . PHP_EOL . PHP_EOL;
109127} else {
109128    print "Valid XML name." . PHP_EOL . PHP_EOL;
109129}
109130
109131echo "TEST:  invalid tag" . PHP_EOL;
109132$result = XML_Util::isValidName("invalidTag?");
109133if (is_a($result, 'PEAR_Error')) {
109134    print "Invalid XML name: " . $result->getMessage() . PHP_EOL . PHP_EOL;
109135} else {
109136    print "Valid XML name." . PHP_EOL . PHP_EOL;
109137}
109138
109139echo "TEST:  invalid tag that doesn't start with a letter" . PHP_EOL;
109140$result = XML_Util::isValidName("1234five");
109141if (is_a($result, 'PEAR_Error')) {
109142    print "Invalid XML name: " . $result->getMessage() . PHP_EOL . PHP_EOL;
109143} else {
109144    print "Valid XML name." . PHP_EOL . PHP_EOL;
109145}
109146
109147?>
109148--EXPECT--
109149=====XML_Util::isValidName() basic tests=====
109150
109151TEST:  valid tag
109152Valid XML name.
109153
109154TEST:  invalid tag
109155Invalid XML name: XML names may only contain alphanumeric chars, period, hyphen, colon and underscores
109156
109157TEST:  invalid tag that doesn't start with a letter
109158Invalid XML name: XML names may only start with letter or underscore
109159XML_Util-1.2.1/tests/testBasic_raiseError.phpt100644   1750   1750         766 11117075466  14550 --TEST--
109160XML_Util::raiseError() basic tests
109161--CREDITS--
109162Chuck Burgess <ashnazg@php.net>
109163# created for v1.2.0a1 2008-05-04
109164--FILE--
109165<?php
109166require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
109167echo '=====XML_Util::raiseError() basic tests=====' . PHP_EOL . PHP_EOL;
109168
109169$error = XML_Util::raiseError("I am an error", 12345);
109170if (is_a($error, 'PEAR_Error')) {
109171    print "PEAR Error: " . $error->getMessage() . PHP_EOL;
109172}
109173?>
109174--EXPECT--
109175=====XML_Util::raiseError() basic tests=====
109176
109177PEAR Error: I am an error
109178XML_Util-1.2.1/tests/testBasic_replaceEntities.phpt100644   1750   1750        6260 11117075466  15566 --TEST--
109179XML_Util::replaceEntities() basic tests
109180--CREDITS--
109181Chuck Burgess <ashnazg@php.net>
109182# created for v1.2.0a1 2008-05-04
109183--FILE--
109184<?php
109185require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
109186echo '=====XML_Util::replaceEntities() basic tests=====' . PHP_EOL . PHP_EOL;
109187
109188$data = 'This string contains < & >.';
109189$utf8 = 'This data contains special chars like <, >, & and " as well as ä, ö, ß, à and ê';
109190
109191echo "TEST:  basic usage" . PHP_EOL;
109192echo XML_Util::replaceEntities($data) . PHP_EOL . PHP_EOL;
109193
109194echo "TEST:  basic usage but with bogus \$replaceEntities arg" . PHP_EOL;
109195echo XML_Util::replaceEntities($data, 'I_AM_BOGUS') . PHP_EOL . PHP_EOL;
109196
109197echo "TEST:  basic usage with ENTITIES_XML" . PHP_EOL;
109198echo XML_Util::replaceEntities($data, XML_UTIL_ENTITIES_XML) . PHP_EOL . PHP_EOL;
109199
109200echo "TEST:  basic usage with ENTITIES_XML and UTF-8" . PHP_EOL;
109201echo XML_Util::replaceEntities($data, XML_UTIL_ENTITIES_XML, 'UTF-8') . PHP_EOL . PHP_EOL;
109202
109203echo "TEST:  utf8 usage with ENTITIES_XML and UTF-8" . PHP_EOL;
109204echo XML_Util::replaceEntities($utf8, XML_UTIL_ENTITIES_XML, 'UTF-8') . PHP_EOL . PHP_EOL;
109205
109206echo "TEST:  basic usage with ENTITIES_XML_REQUIRED" . PHP_EOL;
109207echo XML_Util::replaceEntities($data, XML_UTIL_ENTITIES_XML_REQUIRED) . PHP_EOL . PHP_EOL;
109208
109209echo "TEST:  basic usage with ENTITIES_XML_REQUIRED and UTF-8" . PHP_EOL;
109210echo XML_Util::replaceEntities($data, XML_UTIL_ENTITIES_XML_REQUIRED, 'UTF-8') . PHP_EOL . PHP_EOL;
109211
109212echo "TEST:  utf8 usage with ENTITIES_XML_REQUIRED and UTF-8" . PHP_EOL;
109213echo XML_Util::replaceEntities($utf8, XML_UTIL_ENTITIES_XML_REQUIRED, 'UTF-8') . PHP_EOL . PHP_EOL;
109214
109215echo "TEST:  basic usage with ENTITIES_HTML" . PHP_EOL;
109216echo XML_Util::replaceEntities($data, XML_UTIL_ENTITIES_HTML) . PHP_EOL . PHP_EOL;
109217
109218echo "TEST:  basic usage with ENTITIES_HTML and UTF-8" . PHP_EOL;
109219echo XML_Util::replaceEntities($data, XML_UTIL_ENTITIES_HTML, 'UTF-8') . PHP_EOL . PHP_EOL;
109220
109221echo "TEST:  utf8 usage with ENTITIES_HTML and UTF-8" . PHP_EOL;
109222echo XML_Util::replaceEntities($utf8, XML_UTIL_ENTITIES_HTML, 'UTF-8') . PHP_EOL . PHP_EOL;
109223?>
109224--EXPECT--
109225=====XML_Util::replaceEntities() basic tests=====
109226
109227TEST:  basic usage
109228This string contains &lt; &amp; &gt;.
109229
109230TEST:  basic usage but with bogus $replaceEntities arg
109231This string contains < & >.
109232
109233TEST:  basic usage with ENTITIES_XML
109234This string contains &lt; &amp; &gt;.
109235
109236TEST:  basic usage with ENTITIES_XML and UTF-8
109237This string contains &lt; &amp; &gt;.
109238
109239TEST:  utf8 usage with ENTITIES_XML and UTF-8
109240This data contains special chars like &lt;, &gt;, &amp; and &quot; as well as ä, ö, ß, à and ê
109241
109242TEST:  basic usage with ENTITIES_XML_REQUIRED
109243This string contains &lt; &amp; >.
109244
109245TEST:  basic usage with ENTITIES_XML_REQUIRED and UTF-8
109246This string contains &lt; &amp; >.
109247
109248TEST:  utf8 usage with ENTITIES_XML_REQUIRED and UTF-8
109249This data contains special chars like &lt;, >, &amp; and &quot; as well as ä, ö, ß, à and ê
109250
109251TEST:  basic usage with ENTITIES_HTML
109252This string contains &lt; &amp; &gt;.
109253
109254TEST:  basic usage with ENTITIES_HTML and UTF-8
109255This string contains &lt; &amp; &gt;.
109256
109257TEST:  utf8 usage with ENTITIES_HTML and UTF-8
109258This data contains special chars like &lt;, &gt;, &amp; and &quot; as well as &auml;, &ouml;, &szlig;, &agrave; and &ecirc;
109259
109260XML_Util-1.2.1/tests/testBasic_reverseEntities.phpt100644   1750   1750        6241 11117075466  15625 --TEST--
109261XML_Util::reverseEntities() basic tests
109262--CREDITS--
109263Chuck Burgess <ashnazg@php.net>
109264# created for v1.2.0a1 2008-05-04
109265--FILE--
109266<?php
109267require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
109268echo '=====XML_Util::reverseEntities() basic tests=====' . PHP_EOL . PHP_EOL;
109269
109270$data = 'This string contains &lt; &amp; &gt;.';
109271$utf8 = 'This data contains special chars like &lt;, &gt;, &amp; and &quot; as well as &auml;, &ouml;, &szlig;, &agrave; and &ecirc;';
109272
109273echo "TEST:  basic usage" . PHP_EOL;
109274echo XML_Util::reverseEntities($data) . PHP_EOL . PHP_EOL;
109275
109276echo "TEST:  basic usage but with bogus \$replaceEntities arg" . PHP_EOL;
109277echo XML_Util::reverseEntities($data, 'I_AM_BOGUS') . PHP_EOL . PHP_EOL;
109278
109279echo "TEST:  basic usage with ENTITIES_XML" . PHP_EOL;
109280echo XML_Util::reverseEntities($data, XML_UTIL_ENTITIES_XML) . PHP_EOL . PHP_EOL;
109281
109282echo "TEST:  basic usage with ENTITIES_XML and UTF-8" . PHP_EOL;
109283echo XML_Util::reverseEntities($data, XML_UTIL_ENTITIES_XML, 'UTF-8') . PHP_EOL . PHP_EOL;
109284
109285echo "TEST:  utf8 usage with ENTITIES_XML and UTF-8" . PHP_EOL;
109286echo XML_Util::reverseEntities($utf8, XML_UTIL_ENTITIES_XML, 'UTF-8') . PHP_EOL . PHP_EOL;
109287
109288echo "TEST:  basic usage with ENTITIES_XML_REQUIRED" . PHP_EOL;
109289echo XML_Util::reverseEntities($data, XML_UTIL_ENTITIES_XML_REQUIRED) . PHP_EOL . PHP_EOL;
109290
109291echo "TEST:  basic usage with ENTITIES_XML_REQUIRED and UTF-8" . PHP_EOL;
109292echo XML_Util::reverseEntities($data, XML_UTIL_ENTITIES_XML_REQUIRED, 'UTF-8') . PHP_EOL . PHP_EOL;
109293
109294echo "TEST:  utf8 usage with ENTITIES_XML_REQUIRED and UTF-8" . PHP_EOL;
109295echo XML_Util::reverseEntities($utf8, XML_UTIL_ENTITIES_XML_REQUIRED, 'UTF-8') . PHP_EOL . PHP_EOL;
109296
109297echo "TEST:  basic usage with ENTITIES_HTML" . PHP_EOL;
109298echo XML_Util::reverseEntities($data, XML_UTIL_ENTITIES_HTML) . PHP_EOL . PHP_EOL;
109299
109300echo "TEST:  basic usage with ENTITIES_HTML and UTF-8" . PHP_EOL;
109301echo XML_Util::reverseEntities($data, XML_UTIL_ENTITIES_HTML, 'UTF-8') . PHP_EOL . PHP_EOL;
109302
109303echo "TEST:  utf8 usage with ENTITIES_HTML and UTF-8" . PHP_EOL;
109304echo XML_Util::reverseEntities($utf8, XML_UTIL_ENTITIES_HTML, 'UTF-8') . PHP_EOL . PHP_EOL;
109305?>
109306--EXPECT--
109307=====XML_Util::reverseEntities() basic tests=====
109308
109309TEST:  basic usage
109310This string contains < & >.
109311
109312TEST:  basic usage but with bogus $replaceEntities arg
109313This string contains &lt; &amp; &gt;.
109314
109315TEST:  basic usage with ENTITIES_XML
109316This string contains < & >.
109317
109318TEST:  basic usage with ENTITIES_XML and UTF-8
109319This string contains < & >.
109320
109321TEST:  utf8 usage with ENTITIES_XML and UTF-8
109322This data contains special chars like <, >, & and " as well as &auml;, &ouml;, &szlig;, &agrave; and &ecirc;
109323
109324TEST:  basic usage with ENTITIES_XML_REQUIRED
109325This string contains < & &gt;.
109326
109327TEST:  basic usage with ENTITIES_XML_REQUIRED and UTF-8
109328This string contains < & &gt;.
109329
109330TEST:  utf8 usage with ENTITIES_XML_REQUIRED and UTF-8
109331This data contains special chars like <, &gt;, & and " as well as &auml;, &ouml;, &szlig;, &agrave; and &ecirc;
109332
109333TEST:  basic usage with ENTITIES_HTML
109334This string contains < & >.
109335
109336TEST:  basic usage with ENTITIES_HTML and UTF-8
109337This string contains < & >.
109338
109339TEST:  utf8 usage with ENTITIES_HTML and UTF-8
109340This data contains special chars like <, >, & and " as well as ä, ö, ß, à and ê
109341XML_Util-1.2.1/tests/testBasic_splitQualifiedName.phpt100644   1750   1750        1727 11117075466  16231 --TEST--
109342XML_Util::splitQualifiedName() basic tests
109343--CREDITS--
109344Chuck Burgess <ashnazg@php.net>
109345# created for v1.2.0a1 2008-05-04
109346--FILE--
109347<?php
109348require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
109349echo '=====XML_Util::splitQualifiedName() basic tests=====' . PHP_EOL . PHP_EOL;
109350
109351echo "TEST:  basic usage without namespace" . PHP_EOL;
109352$return = XML_Util::splitQualifiedName("xslt:stylesheet");
109353echo "namespace => " . $return['namespace'] . PHP_EOL;
109354echo "localPart => " . $return['localPart'] . PHP_EOL;
109355echo PHP_EOL;
109356
109357echo "TEST:  basic usage with namespace" . PHP_EOL;
109358$return = XML_Util::splitQualifiedName("stylesheet", "myNs");
109359echo "namespace => " . $return['namespace'] . PHP_EOL;
109360echo "localPart => " . $return['localPart'] . PHP_EOL;
109361echo PHP_EOL;
109362?>
109363--EXPECT--
109364=====XML_Util::splitQualifiedName() basic tests=====
109365
109366TEST:  basic usage without namespace
109367namespace => xslt
109368localPart => stylesheet
109369
109370TEST:  basic usage with namespace
109371namespace => myNs
109372localPart => stylesheet
109373XML_Util-1.2.1/tests/testBug_4950.phpt100644   1750   1750        1243 11117075466  12537 --TEST--
109374XML_Util tests for Bug #4950 "Incorrect CDATA serializing"
109375--CREDITS--
109376Chuck Burgess <ashnazg@php.net>
109377# created for v1.2.0a1 2008-05-04
109378--FILE--
109379<?php
109380require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
109381echo '=====XML_Util tests for Bug #4950 "Incorrect CDATA serializing"=====' . PHP_EOL . PHP_EOL;
109382
109383echo "TEST:  test case provided in bug report" . PHP_EOL;
109384echo XML_Util::createTag("test", array(), "Content ]]></test> here!",
109385    null, XML_UTIL_CDATA_SECTION) . PHP_EOL;
109386
109387?>
109388--EXPECT--
109389=====XML_Util tests for Bug #4950 "Incorrect CDATA serializing"=====
109390
109391TEST:  test case provided in bug report
109392<test><![CDATA[Content ]]]]><![CDATA[></test> here!]]></test>
109393
109394XML_Util-1.2.1/tests/testBug_5392.phpt100644   1750   1750        2111 11117075466  12533 --TEST--
109395XML_Util tests for Bug #5392 "encoding of ISO-8859-1 is the only supported encoding"
109396--CREDITS--
109397Chuck Burgess <ashnazg@php.net>
109398# created for v1.2.0a1 2008-05-04
109399--FILE--
109400<?php
109401require_once 'XML' . DIRECTORY_SEPARATOR . 'Util.php';
109402echo '=====XML_Util tests for Bug #5392 "encoding of ISO-8859-1 is the only supported encoding"=====' . PHP_EOL . PHP_EOL;
109403
109404echo "TEST:  test case provided in bug report" . PHP_EOL;
109405$data = 'This data contains special chars like <, >, & and " as well as ä, ö, ß, à and ê';
109406
109407$replaced = XML_Util::replaceEntities($data, XML_UTIL_ENTITIES_HTML, 'UTF-8');
109408
109409$reversed = XML_Util::reverseEntities($replaced, XML_UTIL_ENTITIES_HTML, 'UTF-8');
109410
109411echo $replaced . PHP_EOL;
109412echo $reversed . PHP_EOL;
109413
109414?>
109415--EXPECT--
109416=====XML_Util tests for Bug #5392 "encoding of ISO-8859-1 is the only supported encoding"=====
109417
109418TEST:  test case provided in bug report
109419This data contains special chars like &lt;, &gt;, &amp; and &quot; as well as &auml;, &ouml;, &szlig;, &agrave; and &ecirc;
109420This data contains special chars like <, >, & and " as well as ä, ö, ß, à and ê
109421XML_Util-1.2.1/Util.php100644   1750   1750       73350 11117075466  10060 <?php
109422
109423/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
109424
109425/**
109426 * XML_Util
109427 *
109428 * XML Utilities package
109429 * 
109430 * PHP versions 4 and 5
109431 *
109432 * LICENSE:
109433 *
109434 * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
109435 * All rights reserved.
109436 *
109437 * Redistribution and use in source and binary forms, with or without
109438 * modification, are permitted provided that the following conditions
109439 * are met:
109440 *
109441 *    * Redistributions of source code must retain the above copyright
109442 *      notice, this list of conditions and the following disclaimer.
109443 *    * Redistributions in binary form must reproduce the above copyright
109444 *      notice, this list of conditions and the following disclaimer in the
109445 *      documentation and/or other materials provided with the distribution.
109446 *    * The name of the author may not be used to endorse or promote products
109447 *      derived from this software without specific prior written permission.
109448 *
109449 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
109450 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
109451 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
109452 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
109453 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
109454 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
109455 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
109456 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
109457 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
109458 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
109459 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
109460 *
109461 * @category  XML
109462 * @package   XML_Util
109463 * @author    Stephan Schmidt <schst@php.net>
109464 * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
109465 * @license   http://opensource.org/licenses/bsd-license New BSD License
109466 * @version   CVS: $Id: Util.php,v 1.38 2008/11/13 00:03:38 ashnazg Exp $
109467 * @link      http://pear.php.net/package/XML_Util
109468 */
109469
109470/**
109471 * error code for invalid chars in XML name
109472 */
109473define('XML_UTIL_ERROR_INVALID_CHARS', 51);
109474
109475/**
109476 * error code for invalid chars in XML name
109477 */
109478define('XML_UTIL_ERROR_INVALID_START', 52);
109479
109480/**
109481 * error code for non-scalar tag content
109482 */
109483define('XML_UTIL_ERROR_NON_SCALAR_CONTENT', 60);
109484
109485/**
109486 * error code for missing tag name
109487 */
109488define('XML_UTIL_ERROR_NO_TAG_NAME', 61);
109489
109490/**
109491 * replace XML entities
109492 */
109493define('XML_UTIL_REPLACE_ENTITIES', 1);
109494
109495/**
109496 * embedd content in a CData Section
109497 */
109498define('XML_UTIL_CDATA_SECTION', 5);
109499
109500/**
109501 * do not replace entitites
109502 */
109503define('XML_UTIL_ENTITIES_NONE', 0);
109504
109505/**
109506 * replace all XML entitites
109507 * This setting will replace <, >, ", ' and &
109508 */
109509define('XML_UTIL_ENTITIES_XML', 1);
109510
109511/**
109512 * replace only required XML entitites
109513 * This setting will replace <, " and &
109514 */
109515define('XML_UTIL_ENTITIES_XML_REQUIRED', 2);
109516
109517/**
109518 * replace HTML entitites
109519 * @link http://www.php.net/htmlentities
109520 */
109521define('XML_UTIL_ENTITIES_HTML', 3);
109522
109523/**
109524 * Collapse all empty tags.
109525 */
109526define('XML_UTIL_COLLAPSE_ALL', 1);
109527
109528/**
109529 * Collapse only empty XHTML tags that have no end tag.
109530 */
109531define('XML_UTIL_COLLAPSE_XHTML_ONLY', 2);
109532
109533/**
109534 * utility class for working with XML documents
109535 *
109536
109537 * @category  XML
109538 * @package   XML_Util
109539 * @author    Stephan Schmidt <schst@php.net>
109540 * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
109541 * @license   http://opensource.org/licenses/bsd-license New BSD License
109542 * @version   Release: 1.2.1
109543 * @link      http://pear.php.net/package/XML_Util
109544 */
109545class XML_Util
109546{
109547    /**
109548     * return API version
109549     *
109550     * @return string $version API version
109551     * @access public
109552     * @static
109553     */
109554    function apiVersion()
109555    {
109556        return '1.1';
109557    }
109558
109559    /**
109560     * replace XML entities
109561     *
109562     * With the optional second parameter, you may select, which
109563     * entities should be replaced.
109564     *
109565     * <code>
109566     * require_once 'XML/Util.php';
109567     *
109568     * // replace XML entites:
109569     * $string = XML_Util::replaceEntities('This string contains < & >.');
109570     * </code>
109571     *
109572     * With the optional third parameter, you may pass the character encoding
109573     * <code>
109574     * require_once 'XML/Util.php';
109575     *
109576     * // replace XML entites in UTF-8:
109577     * $string = XML_Util::replaceEntities(
109578     *     'This string contains < & > as well as ä, ö, ß, à and ê',
109579     *     XML_UTIL_ENTITIES_HTML,
109580     *     'UTF-8'
109581     * );
109582     * </code>
109583     *
109584     * @param string $string          string where XML special chars 
109585     *                                should be replaced
109586     * @param int    $replaceEntities setting for entities in attribute values 
109587     *                                (one of XML_UTIL_ENTITIES_XML, 
109588     *                                XML_UTIL_ENTITIES_XML_REQUIRED, 
109589     *                                XML_UTIL_ENTITIES_HTML)
109590     * @param string $encoding        encoding value (if any)...
109591     *                                must be a valid encoding as determined
109592     *                                by the htmlentities() function
109593     *
109594     * @return string string with replaced chars
109595     * @access public
109596     * @static
109597     * @see reverseEntities()
109598     */
109599    function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML,
109600        $encoding = 'ISO-8859-1')
109601    {
109602        switch ($replaceEntities) {
109603        case XML_UTIL_ENTITIES_XML:
109604            return strtr($string, array(
109605                '&'  => '&amp;',
109606                '>'  => '&gt;',
109607                '<'  => '&lt;',
109608                '"'  => '&quot;',
109609                '\'' => '&apos;' ));
109610            break;
109611        case XML_UTIL_ENTITIES_XML_REQUIRED:
109612            return strtr($string, array(
109613                '&' => '&amp;',
109614                '<' => '&lt;',
109615                '"' => '&quot;' ));
109616            break;
109617        case XML_UTIL_ENTITIES_HTML:
109618            return htmlentities($string, ENT_COMPAT, $encoding);
109619            break;
109620        }
109621        return $string;
109622    }
109623
109624    /**
109625     * reverse XML entities
109626     *
109627     * With the optional second parameter, you may select, which
109628     * entities should be reversed.
109629     *
109630     * <code>
109631     * require_once 'XML/Util.php';
109632     *
109633     * // reverse XML entites:
109634     * $string = XML_Util::reverseEntities('This string contains &lt; &amp; &gt;.');
109635     * </code>
109636     *
109637     * With the optional third parameter, you may pass the character encoding
109638     * <code>
109639     * require_once 'XML/Util.php';
109640     *
109641     * // reverse XML entites in UTF-8:
109642     * $string = XML_Util::reverseEntities(
109643     *     'This string contains &lt; &amp; &gt; as well as'
109644     *     . ' &auml;, &ouml;, &szlig;, &agrave; and &ecirc;',
109645     *     XML_UTIL_ENTITIES_HTML,
109646     *     'UTF-8'
109647     * );
109648     * </code>
109649     *
109650     * @param string $string          string where XML special chars 
109651     *                                should be replaced
109652     * @param int    $replaceEntities setting for entities in attribute values 
109653     *                                (one of XML_UTIL_ENTITIES_XML, 
109654     *                                XML_UTIL_ENTITIES_XML_REQUIRED, 
109655     *                                XML_UTIL_ENTITIES_HTML)
109656     * @param string $encoding        encoding value (if any)...
109657     *                                must be a valid encoding as determined
109658     *                                by the html_entity_decode() function
109659     *
109660     * @return string string with replaced chars
109661     * @access public
109662     * @static
109663     * @see replaceEntities()
109664     */
109665    function reverseEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML,
109666        $encoding = 'ISO-8859-1')
109667    {
109668        switch ($replaceEntities) {
109669        case XML_UTIL_ENTITIES_XML:
109670            return strtr($string, array(
109671                '&amp;'  => '&',
109672                '&gt;'   => '>',
109673                '&lt;'   => '<',
109674                '&quot;' => '"',
109675                '&apos;' => '\'' ));
109676            break;
109677        case XML_UTIL_ENTITIES_XML_REQUIRED:
109678            return strtr($string, array(
109679                '&amp;'  => '&',
109680                '&lt;'   => '<',
109681                '&quot;' => '"' ));
109682            break;
109683        case XML_UTIL_ENTITIES_HTML:
109684            return html_entity_decode($string, ENT_COMPAT, $encoding);
109685            break;
109686        }
109687        return $string;
109688    }
109689
109690    /**
109691     * build an xml declaration
109692     *
109693     * <code>
109694     * require_once 'XML/Util.php';
109695     *
109696     * // get an XML declaration:
109697     * $xmlDecl = XML_Util::getXMLDeclaration('1.0', 'UTF-8', true);
109698     * </code>
109699     *
109700     * @param string $version    xml version
109701     * @param string $encoding   character encoding
109702     * @param bool   $standalone document is standalone (or not)
109703     *
109704     * @return string xml declaration
109705     * @access public
109706     * @static
109707     * @uses attributesToString() to serialize the attributes of the XML declaration
109708     */
109709    function getXMLDeclaration($version = '1.0', $encoding = null, 
109710        $standalone = null)
109711    {
109712        $attributes = array(
109713            'version' => $version,
109714        );
109715        // add encoding
109716        if ($encoding !== null) {
109717            $attributes['encoding'] = $encoding;
109718        }
109719        // add standalone, if specified
109720        if ($standalone !== null) {
109721            $attributes['standalone'] = $standalone ? 'yes' : 'no';
109722        }
109723
109724        return sprintf('<?xml%s?>', 
109725            XML_Util::attributesToString($attributes, false));
109726    }
109727
109728    /**
109729     * build a document type declaration
109730     *
109731     * <code>
109732     * require_once 'XML/Util.php';
109733     *
109734     * // get a doctype declaration:
109735     * $xmlDecl = XML_Util::getDocTypeDeclaration('rootTag','myDocType.dtd');
109736     * </code>
109737     *
109738     * @param string $root        name of the root tag
109739     * @param string $uri         uri of the doctype definition 
109740     *                            (or array with uri and public id)
109741     * @param string $internalDtd internal dtd entries
109742     *
109743     * @return string doctype declaration
109744     * @access public
109745     * @static
109746     * @since 0.2
109747     */
109748    function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
109749    {
109750        if (is_array($uri)) {
109751            $ref = sprintf(' PUBLIC "%s" "%s"', $uri['id'], $uri['uri']);
109752        } elseif (!empty($uri)) {
109753            $ref = sprintf(' SYSTEM "%s"', $uri);
109754        } else {
109755            $ref = '';
109756        }
109757
109758        if (empty($internalDtd)) {
109759            return sprintf('<!DOCTYPE %s%s>', $root, $ref);
109760        } else {
109761            return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
109762        }
109763    }
109764
109765    /**
109766     * create string representation of an attribute list
109767     *
109768     * <code>
109769     * require_once 'XML/Util.php';
109770     *
109771     * // build an attribute string
109772     * $att = array(
109773     *              'foo'   =>  'bar',
109774     *              'argh'  =>  'tomato'
109775     *            );
109776     *
109777     * $attList = XML_Util::attributesToString($att);
109778     * </code>
109779     *
109780     * @param array      $attributes attribute array
109781     * @param bool|array $sort       sort attribute list alphabetically, 
109782     *                               may also be an assoc array containing 
109783     *                               the keys 'sort', 'multiline', 'indent', 
109784     *                               'linebreak' and 'entities'
109785     * @param bool       $multiline  use linebreaks, if more than 
109786     *                               one attribute is given
109787     * @param string     $indent     string used for indentation of 
109788     *                               multiline attributes
109789     * @param string     $linebreak  string used for linebreaks of 
109790     *                               multiline attributes
109791     * @param int        $entities   setting for entities in attribute values 
109792     *                               (one of XML_UTIL_ENTITIES_NONE, 
109793     *                               XML_UTIL_ENTITIES_XML, 
109794     *                               XML_UTIL_ENTITIES_XML_REQUIRED, 
109795     *                               XML_UTIL_ENTITIES_HTML)
109796     *
109797     * @return string string representation of the attributes
109798     * @access public
109799     * @static
109800     * @uses replaceEntities() to replace XML entities in attribute values
109801     * @todo allow sort also to be an options array
109802     */
109803    function attributesToString($attributes, $sort = true, $multiline = false, 
109804        $indent = '    ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML)
109805    {
109806        /*
109807         * second parameter may be an array
109808         */
109809        if (is_array($sort)) {
109810            if (isset($sort['multiline'])) {
109811                $multiline = $sort['multiline'];
109812            }
109813            if (isset($sort['indent'])) {
109814                $indent = $sort['indent'];
109815            }
109816            if (isset($sort['linebreak'])) {
109817                $multiline = $sort['linebreak'];
109818            }
109819            if (isset($sort['entities'])) {
109820                $entities = $sort['entities'];
109821            }
109822            if (isset($sort['sort'])) {
109823                $sort = $sort['sort'];
109824            } else {
109825                $sort = true;
109826            }
109827        }
109828        $string = '';
109829        if (is_array($attributes) && !empty($attributes)) {
109830            if ($sort) {
109831                ksort($attributes);
109832            }
109833            if ( !$multiline || count($attributes) == 1) {
109834                foreach ($attributes as $key => $value) {
109835                    if ($entities != XML_UTIL_ENTITIES_NONE) {
109836                        if ($entities === XML_UTIL_CDATA_SECTION) {
109837                            $entities = XML_UTIL_ENTITIES_XML;
109838                        }
109839                        $value = XML_Util::replaceEntities($value, $entities);
109840                    }
109841                    $string .= ' ' . $key . '="' . $value . '"';
109842                }
109843            } else {
109844                $first = true;
109845                foreach ($attributes as $key => $value) {
109846                    if ($entities != XML_UTIL_ENTITIES_NONE) {
109847                        $value = XML_Util::replaceEntities($value, $entities);
109848                    }
109849                    if ($first) {
109850                        $string .= ' ' . $key . '="' . $value . '"';
109851                        $first   = false;
109852                    } else {
109853                        $string .= $linebreak . $indent . $key . '="' . $value . '"';
109854                    }
109855                }
109856            }
109857        }
109858        return $string;
109859    }
109860
109861    /**
109862     * Collapses empty tags.
109863     *
109864     * @param string $xml  XML
109865     * @param int    $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL)
109866     *                      or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones.
109867     *
109868     * @return string XML
109869     * @access public
109870     * @static
109871     * @todo PEAR CS - unable to avoid "space after open parens" error
109872     *       in the IF branch
109873     */
109874    function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL) 
109875    {
109876        if ($mode == XML_UTIL_COLLAPSE_XHTML_ONLY) {
109877            return preg_replace(
109878                '/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|'
109879                . 'param)([^>]*)><\/\\1>/s',
109880                '<\\1\\2 />',
109881                $xml);
109882        } else {
109883            return preg_replace('/<(\w+)([^>]*)><\/\\1>/s', '<\\1\\2 />', $xml);
109884        }
109885    }
109886
109887    /**
109888     * create a tag
109889     *
109890     * This method will call XML_Util::createTagFromArray(), which
109891     * is more flexible.
109892     *
109893     * <code>
109894     * require_once 'XML/Util.php';
109895     *
109896     * // create an XML tag:
109897     * $tag = XML_Util::createTag('myNs:myTag', 
109898     *     array('foo' => 'bar'), 
109899     *     'This is inside the tag', 
109900     *     'http://www.w3c.org/myNs#');
109901     * </code>
109902     *
109903     * @param string $qname           qualified tagname (including namespace)
109904     * @param array  $attributes      array containg attributes
109905     * @param mixed  $content         the content
109906     * @param string $namespaceUri    URI of the namespace
109907     * @param int    $replaceEntities whether to replace XML special chars in 
109908     *                                content, embedd it in a CData section 
109909     *                                or none of both
109910     * @param bool   $multiline       whether to create a multiline tag where 
109911     *                                each attribute gets written to a single line
109912     * @param string $indent          string used to indent attributes 
109913     *                                (_auto indents attributes so they start 
109914     *                                at the same column)
109915     * @param string $linebreak       string used for linebreaks
109916     * @param bool   $sortAttributes  Whether to sort the attributes or not
109917     *
109918     * @return string XML tag
109919     * @access public
109920     * @static
109921     * @see createTagFromArray()
109922     * @uses createTagFromArray() to create the tag
109923     */
109924    function createTag($qname, $attributes = array(), $content = null, 
109925        $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, 
109926        $multiline = false, $indent = '_auto', $linebreak = "\n", 
109927        $sortAttributes = true)
109928    {
109929        $tag = array(
109930            'qname'      => $qname,
109931            'attributes' => $attributes
109932        );
109933
109934        // add tag content
109935        if ($content !== null) {
109936            $tag['content'] = $content;
109937        }
109938
109939        // add namespace Uri
109940        if ($namespaceUri !== null) {
109941            $tag['namespaceUri'] = $namespaceUri;
109942        }
109943
109944        return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, 
109945            $indent, $linebreak, $sortAttributes);
109946    }
109947
109948    /**
109949     * create a tag from an array
109950     * this method awaits an array in the following format
109951     * <pre>
109952     * array(
109953     *     // qualified name of the tag
109954     *     'qname' => $qname        
109955     *
109956     *     // namespace prefix (optional, if qname is specified or no namespace)
109957     *     'namespace' => $namespace    
109958     *
109959     *     // local part of the tagname (optional, if qname is specified)
109960     *     'localpart' => $localpart,   
109961     *
109962     *     // array containing all attributes (optional)
109963     *     'attributes' => array(),      
109964     *
109965     *     // tag content (optional)
109966     *     'content' => $content,     
109967     *
109968     *     // namespaceUri for the given namespace (optional)
109969     *     'namespaceUri' => $namespaceUri 
109970     * )
109971     * </pre>
109972     *
109973     * <code>
109974     * require_once 'XML/Util.php';
109975     *
109976     * $tag = array(
109977     *     'qname'        => 'foo:bar',
109978     *     'namespaceUri' => 'http://foo.com',
109979     *     'attributes'   => array('key' => 'value', 'argh' => 'fruit&vegetable'),
109980     *     'content'      => 'I\'m inside the tag',
109981     * );
109982     * // creating a tag with qualified name and namespaceUri
109983     * $string = XML_Util::createTagFromArray($tag);
109984     * </code>
109985     *
109986     * @param array  $tag             tag definition
109987     * @param int    $replaceEntities whether to replace XML special chars in 
109988     *                                content, embedd it in a CData section 
109989     *                                or none of both
109990     * @param bool   $multiline       whether to create a multiline tag where each 
109991     *                                attribute gets written to a single line
109992     * @param string $indent          string used to indent attributes 
109993     *                                (_auto indents attributes so they start 
109994     *                                at the same column)
109995     * @param string $linebreak       string used for linebreaks
109996     * @param bool   $sortAttributes  Whether to sort the attributes or not
109997     *
109998     * @return string XML tag
109999     * @access public
110000     * @static
110001     * @see createTag()
110002     * @uses attributesToString() to serialize the attributes of the tag
110003     * @uses splitQualifiedName() to get local part and namespace of a qualified name
110004     * @uses createCDataSection()
110005     * @uses raiseError()
110006     */
110007    function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
110008        $multiline = false, $indent = '_auto', $linebreak = "\n", 
110009        $sortAttributes = true)
110010    {
110011        if (isset($tag['content']) && !is_scalar($tag['content'])) {
110012            return XML_Util::raiseError('Supplied non-scalar value as tag content',
110013            XML_UTIL_ERROR_NON_SCALAR_CONTENT);
110014        }
110015
110016        if (!isset($tag['qname']) && !isset($tag['localPart'])) {
110017            return XML_Util::raiseError('You must either supply a qualified name '
110018                . '(qname) or local tag name (localPart).', 
110019                XML_UTIL_ERROR_NO_TAG_NAME);
110020        }
110021
110022        // if no attributes hav been set, use empty attributes
110023        if (!isset($tag['attributes']) || !is_array($tag['attributes'])) {
110024            $tag['attributes'] = array();
110025        }
110026
110027        if (isset($tag['namespaces'])) {
110028            foreach ($tag['namespaces'] as $ns => $uri) {
110029                $tag['attributes']['xmlns:' . $ns] = $uri;
110030            }
110031        }
110032
110033        if (!isset($tag['qname'])) {
110034            // qualified name is not given
110035
110036            // check for namespace
110037            if (isset($tag['namespace']) && !empty($tag['namespace'])) {
110038                $tag['qname'] = $tag['namespace'] . ':' . $tag['localPart'];
110039            } else {
110040                $tag['qname'] = $tag['localPart'];
110041            }
110042        } elseif (isset($tag['namespaceUri']) && !isset($tag['namespace'])) {
110043            // namespace URI is set, but no namespace
110044
110045            $parts = XML_Util::splitQualifiedName($tag['qname']);
110046
110047            $tag['localPart'] = $parts['localPart'];
110048            if (isset($parts['namespace'])) {
110049                $tag['namespace'] = $parts['namespace'];
110050            }
110051        }
110052
110053        if (isset($tag['namespaceUri']) && !empty($tag['namespaceUri'])) {
110054            // is a namespace given
110055            if (isset($tag['namespace']) && !empty($tag['namespace'])) {
110056                $tag['attributes']['xmlns:' . $tag['namespace']] =
110057                    $tag['namespaceUri'];
110058            } else {
110059                // define this Uri as the default namespace
110060                $tag['attributes']['xmlns'] = $tag['namespaceUri'];
110061            }
110062        }
110063
110064        // check for multiline attributes
110065        if ($multiline === true) {
110066            if ($indent === '_auto') {
110067                $indent = str_repeat(' ', (strlen($tag['qname'])+2));
110068            }
110069        }
110070
110071        // create attribute list
110072        $attList = XML_Util::attributesToString($tag['attributes'], 
110073            $sortAttributes, $multiline, $indent, $linebreak, $replaceEntities);
110074        if (!isset($tag['content']) || (string)$tag['content'] == '') {
110075            $tag = sprintf('<%s%s />', $tag['qname'], $attList);
110076        } else {
110077            switch ($replaceEntities) {
110078            case XML_UTIL_ENTITIES_NONE:
110079                break;
110080            case XML_UTIL_CDATA_SECTION:
110081                $tag['content'] = XML_Util::createCDataSection($tag['content']);
110082                break;
110083            default:
110084                $tag['content'] = XML_Util::replaceEntities($tag['content'], 
110085                    $replaceEntities);
110086                break;
110087            }
110088            $tag = sprintf('<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'],
110089                $tag['qname']);
110090        }
110091        return $tag;
110092    }
110093
110094    /**
110095     * create a start element
110096     *
110097     * <code>
110098     * require_once 'XML/Util.php';
110099     *
110100     * // create an XML start element:
110101     * $tag = XML_Util::createStartElement('myNs:myTag', 
110102     *     array('foo' => 'bar') ,'http://www.w3c.org/myNs#');
110103     * </code>
110104     *
110105     * @param string $qname          qualified tagname (including namespace)
110106     * @param array  $attributes     array containg attributes
110107     * @param string $namespaceUri   URI of the namespace
110108     * @param bool   $multiline      whether to create a multiline tag where each 
110109     *                               attribute gets written to a single line
110110     * @param string $indent         string used to indent attributes (_auto indents
110111     *                               attributes so they start at the same column)
110112     * @param string $linebreak      string used for linebreaks
110113     * @param bool   $sortAttributes Whether to sort the attributes or not
110114     *
110115     * @return string XML start element
110116     * @access public
110117     * @static
110118     * @see createEndElement(), createTag()
110119     */
110120    function createStartElement($qname, $attributes = array(), $namespaceUri = null,
110121        $multiline = false, $indent = '_auto', $linebreak = "\n", 
110122        $sortAttributes = true)
110123    {
110124        // if no attributes hav been set, use empty attributes
110125        if (!isset($attributes) || !is_array($attributes)) {
110126            $attributes = array();
110127        }
110128
110129        if ($namespaceUri != null) {
110130            $parts = XML_Util::splitQualifiedName($qname);
110131        }
110132
110133        // check for multiline attributes
110134        if ($multiline === true) {
110135            if ($indent === '_auto') {
110136                $indent = str_repeat(' ', (strlen($qname)+2));
110137            }
110138        }
110139
110140        if ($namespaceUri != null) {
110141            // is a namespace given
110142            if (isset($parts['namespace']) && !empty($parts['namespace'])) {
110143                $attributes['xmlns:' . $parts['namespace']] = $namespaceUri;
110144            } else {
110145                // define this Uri as the default namespace
110146                $attributes['xmlns'] = $namespaceUri;
110147            }
110148        }
110149
110150        // create attribute list
110151        $attList = XML_Util::attributesToString($attributes, $sortAttributes, 
110152            $multiline, $indent, $linebreak);
110153        $element = sprintf('<%s%s>', $qname, $attList);
110154        return  $element;
110155    }
110156
110157    /**
110158     * create an end element
110159     *
110160     * <code>
110161     * require_once 'XML/Util.php';
110162     *
110163     * // create an XML start element:
110164     * $tag = XML_Util::createEndElement('myNs:myTag');
110165     * </code>
110166     *
110167     * @param string $qname qualified tagname (including namespace)
110168     *
110169     * @return string XML end element
110170     * @access public
110171     * @static
110172     * @see createStartElement(), createTag()
110173     */
110174    function createEndElement($qname)
110175    {
110176        $element = sprintf('</%s>', $qname);
110177        return $element;
110178    }
110179
110180    /**
110181     * create an XML comment
110182     *
110183     * <code>
110184     * require_once 'XML/Util.php';
110185     *
110186     * // create an XML start element:
110187     * $tag = XML_Util::createComment('I am a comment');
110188     * </code>
110189     *
110190     * @param string $content content of the comment
110191     *
110192     * @return string XML comment
110193     * @access public
110194     * @static
110195     */
110196    function createComment($content)
110197    {
110198        $comment = sprintf('<!-- %s -->', $content);
110199        return $comment;
110200    }
110201
110202    /**
110203     * create a CData section
110204     *
110205     * <code>
110206     * require_once 'XML/Util.php';
110207     *
110208     * // create a CData section
110209     * $tag = XML_Util::createCDataSection('I am content.');
110210     * </code>
110211     *
110212     * @param string $data data of the CData section
110213     *
110214     * @return string CData section with content
110215     * @access public
110216     * @static
110217     */
110218    function createCDataSection($data)
110219    {
110220        return sprintf('<![CDATA[%s]]>', 
110221            preg_replace('/\]\]>/', ']]]]><![CDATA[>', strval($data)));
110222
110223    }
110224
110225    /**
110226     * split qualified name and return namespace and local part
110227     *
110228     * <code>
110229     * require_once 'XML/Util.php';
110230     *
110231     * // split qualified tag
110232     * $parts = XML_Util::splitQualifiedName('xslt:stylesheet');
110233     * </code>
110234     * the returned array will contain two elements:
110235     * <pre>
110236     * array(
110237     *     'namespace' => 'xslt',
110238     *     'localPart' => 'stylesheet'
110239     * );
110240     * </pre>
110241     *
110242     * @param string $qname     qualified tag name
110243     * @param string $defaultNs default namespace (optional)
110244     *
110245     * @return array array containing namespace and local part
110246     * @access public
110247     * @static
110248     */
110249    function splitQualifiedName($qname, $defaultNs = null)
110250    {
110251        if (strstr($qname, ':')) {
110252            $tmp = explode(':', $qname);
110253            return array(
110254                'namespace' => $tmp[0],
110255                'localPart' => $tmp[1]
110256            );
110257        }
110258        return array(
110259            'namespace' => $defaultNs,
110260            'localPart' => $qname
110261        );
110262    }
110263
110264    /**
110265     * check, whether string is valid XML name
110266     *
110267     * <p>XML names are used for tagname, attribute names and various
110268     * other, lesser known entities.</p>
110269     * <p>An XML name may only consist of alphanumeric characters,
110270     * dashes, undescores and periods, and has to start with a letter
110271     * or an underscore.</p>
110272     *
110273     * <code>
110274     * require_once 'XML/Util.php';
110275     *
110276     * // verify tag name
110277     * $result = XML_Util::isValidName('invalidTag?');
110278     * if (is_a($result, 'PEAR_Error')) {
110279     *    print 'Invalid XML name: ' . $result->getMessage();
110280     * }
110281     * </code>
110282     *
110283     * @param string $string string that should be checked
110284     *
110285     * @return mixed true, if string is a valid XML name, PEAR error otherwise
110286     * @access public
110287     * @static
110288     * @todo support for other charsets
110289     * @todo PEAR CS - unable to avoid 85-char limit on second preg_match
110290     */
110291    function isValidName($string)
110292    {
110293        // check for invalid chars
110294        if (!preg_match('/^[[:alpha:]_]$/', $string{0})) {
110295            return XML_Util::raiseError('XML names may only start with letter '
110296                . 'or underscore', XML_UTIL_ERROR_INVALID_START);
110297        }
110298
110299        // check for invalid chars
110300        if (!preg_match('/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?[[:alpha:]_]([[:alnum:]\_\-\.]+)?$/',
110301            $string)
110302        ) {
110303            return XML_Util::raiseError('XML names may only contain alphanumeric '
110304                . 'chars, period, hyphen, colon and underscores', 
110305                XML_UTIL_ERROR_INVALID_CHARS);
110306        }
110307        // XML name is valid
110308        return true;
110309    }
110310
110311    /**
110312     * replacement for XML_Util::raiseError
110313     *
110314     * Avoids the necessity to always require
110315     * PEAR.php
110316     *
110317     * @param string $msg  error message
110318     * @param int    $code error code
110319     *
110320     * @return PEAR_Error
110321     * @access public
110322     * @static
110323     * @todo PEAR CS - should this use include_once instead?
110324     */
110325    function raiseError($msg, $code)
110326    {
110327        require_once 'PEAR.php';
110328        return PEAR::raiseError($msg, $code);
110329    }
110330}
110331?>
110332package.sig100644   1750   1750         305 11117075544   6362 -----BEGIN PGP SIGNATURE-----
110333Version: GnuPG v1.4.9 (GNU/Linux)
110334
110335iEYEABECAAYFAkk8e2QACgkQcqMhusJF8XULZgCg0iwVLWueraJzK5s1AesDkVzv
110336GucAn22sSv3QiTSUWG9BmakiW9hFsb4V
110337=g2mr
110338-----END PGP SIGNATURE-----
110339����\�ڄc�v�83H��GBMB