Subversion Repositories Applications.gtt

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
94 jpm 1
<?php
2
/**
3
 * PEAR_PackageFile_v2, package.xml version 2.0
4
 *
5
 * PHP versions 4 and 5
6
 *
7
 * LICENSE: This source file is subject to version 3.0 of the PHP license
8
 * that is available through the world-wide-web at the following URI:
9
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
10
 * the PHP License and are unable to obtain it through the web, please
11
 * send a note to license@php.net so we can mail you a copy immediately.
12
 *
13
 * @category   pear
14
 * @package    PEAR
15
 * @author     Greg Beaver <cellog@php.net>
16
 * @copyright  1997-2006 The PHP Group
17
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
18
 * @version    CVS: $Id: v2.php,v 1.136 2007/02/20 00:16:12 cellog Exp $
19
 * @link       http://pear.php.net/package/PEAR
20
 * @since      File available since Release 1.4.0a1
21
 */
22
/**
23
 * For error handling
24
 */
25
require_once 'PEAR/ErrorStack.php';
26
/**
27
 * @category   pear
28
 * @package    PEAR
29
 * @author     Greg Beaver <cellog@php.net>
30
 * @copyright  1997-2006 The PHP Group
31
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
32
 * @version    Release: 1.5.1
33
 * @link       http://pear.php.net/package/PEAR
34
 * @since      Class available since Release 1.4.0a1
35
 */
36
class PEAR_PackageFile_v2
37
{
38
 
39
    /**
40
     * Parsed package information
41
     * @var array
42
     * @access private
43
     */
44
    var $_packageInfo = array();
45
 
46
    /**
47
     * path to package .tgz or false if this is a local/extracted package.xml
48
     * @var string|false
49
     * @access private
50
     */
51
    var $_archiveFile;
52
 
53
    /**
54
     * path to package .xml or false if this is an abstract parsed-from-string xml
55
     * @var string|false
56
     * @access private
57
     */
58
    var $_packageFile;
59
 
60
    /**
61
     * This is used by file analysis routines to log progress information
62
     * @var PEAR_Common
63
     * @access protected
64
     */
65
    var $_logger;
66
 
67
    /**
68
     * This is set to the highest validation level that has been validated
69
     *
70
     * If the package.xml is invalid or unknown, this is set to 0.  If
71
     * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL.  If
72
     * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING
73
     * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING.  This allows validation
74
     * "caching" to occur, which is particularly important for package validation, so
75
     * that PHP files are not validated twice
76
     * @var int
77
     * @access private
78
     */
79
    var $_isValid = 0;
80
 
81
    /**
82
     * True if the filelist has been validated
83
     * @param bool
84
     */
85
    var $_filesValid = false;
86
 
87
    /**
88
     * @var PEAR_Registry
89
     * @access protected
90
     */
91
    var $_registry;
92
 
93
    /**
94
     * @var PEAR_Config
95
     * @access protected
96
     */
97
    var $_config;
98
 
99
    /**
100
     * Optional Dependency group requested for installation
101
     * @var string
102
     * @access private
103
     */
104
    var $_requestedGroup = false;
105
 
106
    /**
107
     * @var PEAR_ErrorStack
108
     * @access protected
109
     */
110
    var $_stack;
111
 
112
    /**
113
     * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible
114
     */
115
    var $_tasksNs;
116
 
117
    /**
118
     * Determines whether this packagefile was initialized only with partial package info
119
     *
120
     * If this package file was constructed via parsing REST, it will only contain
121
     *
122
     * - package name
123
     * - channel name
124
     * - dependencies
125
     * @var boolean
126
     * @access private
127
     */
128
    var $_incomplete = true;
129
 
130
    /**
131
     * @var PEAR_PackageFile_v2_Validator
132
     */
133
    var $_v2Validator;
134
 
135
    /**
136
     * The constructor merely sets up the private error stack
137
     */
138
    function PEAR_PackageFile_v2()
139
    {
140
        $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null);
141
        $this->_isValid = false;
142
    }
143
 
144
    /**
145
     * To make unit-testing easier
146
     * @param PEAR_Frontend_*
147
     * @param array options
148
     * @param PEAR_Config
149
     * @return PEAR_Downloader
150
     * @access protected
151
     */
152
    function &getPEARDownloader(&$i, $o, &$c)
153
    {
154
        $z = &new PEAR_Downloader($i, $o, $c);
155
        return $z;
156
    }
157
 
158
    /**
159
     * To make unit-testing easier
160
     * @param PEAR_Config
161
     * @param array options
162
     * @param array package name as returned from {@link PEAR_Registry::parsePackageName()}
163
     * @param int PEAR_VALIDATE_* constant
164
     * @return PEAR_Dependency2
165
     * @access protected
166
     */
167
    function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING)
168
    {
169
        if (!class_exists('PEAR_Dependency2')) {
170
            require_once 'PEAR/Dependency2.php';
171
        }
172
        $z = &new PEAR_Dependency2($c, $o, $p, $s);
173
        return $z;
174
    }
175
 
176
    function getInstalledBinary()
177
    {
178
        return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] :
179
            false;
180
    }
181
 
182
    /**
183
     * Installation of source package has failed, attempt to download and install the
184
     * binary version of this package.
185
     * @param PEAR_Installer
186
     * @return array|false
187
     */
188
    function installBinary(&$installer)
189
    {
190
        if (!OS_WINDOWS) {
191
            $a = false;
192
            return $a;
193
        }
194
        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
195
            $releasetype = $this->getPackageType() . 'release';
196
            if (!is_array($installer->getInstallPackages())) {
197
                $a = false;
198
                return $a;
199
            }
200
            foreach ($installer->getInstallPackages() as $p) {
201
                if ($p->isExtension($this->_packageInfo['providesextension'])) {
202
                    if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') {
203
                        $a = false;
204
                        return $a; // the user probably downloaded it separately
205
                    }
206
                }
207
            }
208
            if (isset($this->_packageInfo[$releasetype]['binarypackage'])) {
209
                $installer->log(0, 'Attempting to download binary version of extension "' .
210
                    $this->_packageInfo['providesextension'] . '"');
211
                $params = $this->_packageInfo[$releasetype]['binarypackage'];
212
                if (!is_array($params) || !isset($params[0])) {
213
                    $params = array($params);
214
                }
215
                if (isset($this->_packageInfo['channel'])) {
216
                    foreach ($params as $i => $param) {
217
                        $params[$i] = array('channel' => $this->_packageInfo['channel'],
218
                            'package' => $param, 'version' => $this->getVersion());
219
                    }
220
                }
221
                $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(),
222
                    $installer->config);
223
                $verbose = $dl->config->get('verbose');
224
                $dl->config->set('verbose', -1);
225
                foreach ($params as $param) {
226
                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
227
                    $ret = $dl->download(array($param));
228
                    PEAR::popErrorHandling();
229
                    if (is_array($ret) && count($ret)) {
230
                        break;
231
                    }
232
                }
233
                $dl->config->set('verbose', $verbose);
234
                if (is_array($ret)) {
235
                    if (count($ret) == 1) {
236
                        $pf = $ret[0]->getPackageFile();
237
                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
238
                        $err = $installer->install($ret[0]);
239
                        PEAR::popErrorHandling();
240
                        if (is_array($err)) {
241
                            $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage();
242
                            // "install" self, so all dependencies will work transparently
243
                            $this->_registry->addPackage2($this);
244
                            $installer->log(0, 'Download and install of binary extension "' .
245
                                $this->_registry->parsedPackageNameToString(
246
                                    array('channel' => $pf->getChannel(),
247
                                          'package' => $pf->getPackage()), true) . '" successful');
248
                            $a = array($ret[0], $err);
249
                            return $a;
250
                        }
251
                        $installer->log(0, 'Download and install of binary extension "' .
252
                            $this->_registry->parsedPackageNameToString(
253
                                    array('channel' => $pf->getChannel(),
254
                                          'package' => $pf->getPackage()), true) . '" failed');
255
                    }
256
                }
257
            }
258
        }
259
        $a = false;
260
        return $a;
261
    }
262
 
263
    /**
264
     * @return string|false Extension name
265
     */
266
    function getProvidesExtension()
267
    {
268
        if (in_array($this->getPackageType(),
269
              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
270
            if (isset($this->_packageInfo['providesextension'])) {
271
                return $this->_packageInfo['providesextension'];
272
            }
273
        }
274
        return false;
275
    }
276
 
277
    /**
278
     * @param string Extension name
279
     * @return bool
280
     */
281
    function isExtension($extension)
282
    {
283
        if (in_array($this->getPackageType(),
284
              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
285
            return $this->_packageInfo['providesextension'] == $extension;
286
        }
287
        return false;
288
    }
289
 
290
    /**
291
     * Tests whether every part of the package.xml 1.0 is represented in
292
     * this package.xml 2.0
293
     * @param PEAR_PackageFile_v1
294
     * @return bool
295
     */
296
    function isEquivalent($pf1)
297
    {
298
        if (!$pf1) {
299
            return true;
300
        }
301
        if ($this->getPackageType() == 'bundle') {
302
            return false;
303
        }
304
        $this->_stack->getErrors(true);
305
        if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) {
306
            return false;
307
        }
308
        $pass = true;
309
        if ($pf1->getPackage() != $this->getPackage()) {
310
            $this->_differentPackage($pf1->getPackage());
311
            $pass = false;
312
        }
313
        if ($pf1->getVersion() != $this->getVersion()) {
314
            $this->_differentVersion($pf1->getVersion());
315
            $pass = false;
316
        }
317
        if (trim($pf1->getSummary()) != $this->getSummary()) {
318
            $this->_differentSummary($pf1->getSummary());
319
            $pass = false;
320
        }
321
        if (preg_replace('/\s+/', '', $pf1->getDescription()) !=
322
              preg_replace('/\s+/', '', $this->getDescription())) {
323
            $this->_differentDescription($pf1->getDescription());
324
            $pass = false;
325
        }
326
        if ($pf1->getState() != $this->getState()) {
327
            $this->_differentState($pf1->getState());
328
            $pass = false;
329
        }
330
        if (!strstr(preg_replace('/\s+/', '', $this->getNotes()),
331
              preg_replace('/\s+/', '', $pf1->getNotes()))) {
332
            $this->_differentNotes($pf1->getNotes());
333
            $pass = false;
334
        }
335
        $mymaintainers = $this->getMaintainers();
336
        $yourmaintainers = $pf1->getMaintainers();
337
        for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) {
338
            $reset = false;
339
            for ($i2 = 0; $i2 < count($mymaintainers); $i2++) {
340
                if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) {
341
                    if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) {
342
                        $this->_differentRole($mymaintainers[$i2]['handle'],
343
                            $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']);
344
                        $pass = false;
345
                    }
346
                    if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
347
                        $this->_differentEmail($mymaintainers[$i2]['handle'],
348
                            $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
349
                        $pass = false;
350
                    }
351
                    if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
352
                        $this->_differentName($mymaintainers[$i2]['handle'],
353
                            $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
354
                        $pass = false;
355
                    }
356
                    unset($mymaintainers[$i2]);
357
                    $mymaintainers = array_values($mymaintainers);
358
                    unset($yourmaintainers[$i1]);
359
                    $yourmaintainers = array_values($yourmaintainers);
360
                    $reset = true;
361
                    break;
362
                }
363
            }
364
            if ($reset) {
365
                $i1 = -1;
366
            }
367
        }
368
        $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers);
369
        $filelist = $this->getFilelist();
370
        foreach ($pf1->getFilelist() as $file => $atts) {
371
            if (!isset($filelist[$file])) {
372
                $this->_missingFile($file);
373
                $pass = false;
374
            }
375
        }
376
        return $pass;
377
    }
378
 
379
    function _differentPackage($package)
380
    {
381
        $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
382
            'self' => $this->getPackage()),
383
            'package.xml 1.0 package "%package%" does not match "%self%"');
384
    }
385
 
386
    function _differentVersion($version)
387
    {
388
        $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
389
            'self' => $this->getVersion()),
390
            'package.xml 1.0 version "%version%" does not match "%self%"');
391
    }
392
 
393
    function _differentState($state)
394
    {
395
        $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
396
            'self' => $this->getState()),
397
            'package.xml 1.0 state "%state%" does not match "%self%"');
398
    }
399
 
400
    function _differentRole($handle, $role, $selfrole)
401
    {
402
        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
403
            'role' => $role, 'self' => $selfrole),
404
            'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"');
405
    }
406
 
407
    function _differentEmail($handle, $email, $selfemail)
408
    {
409
        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
410
            'email' => $email, 'self' => $selfemail),
411
            'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"');
412
    }
413
 
414
    function _differentName($handle, $name, $selfname)
415
    {
416
        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
417
            'name' => $name, 'self' => $selfname),
418
            'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"');
419
    }
420
 
421
    function _unmatchedMaintainers($my, $yours)
422
    {
423
        if ($my) {
424
            array_walk($my, create_function('&$i, $k', '$i = $i["handle"];'));
425
            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my),
426
                'package.xml 2.0 has unmatched extra maintainers "%handles%"');
427
        }
428
        if ($yours) {
429
            array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];'));
430
            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours),
431
                'package.xml 1.0 has unmatched extra maintainers "%handles%"');
432
        }
433
    }
434
 
435
    function _differentNotes($notes)
436
    {
437
        $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...';
438
        $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() :
439
            substr($this->getNotes(), 0, 24) . '...';
440
        $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes,
441
            'self' => $truncmynotes),
442
            'package.xml 1.0 release notes "%notes%" do not match "%self%"');
443
    }
444
 
445
    function _differentSummary($summary)
446
    {
447
        $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...';
448
        $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() :
449
            substr($this->getsummary(), 0, 24) . '...';
450
        $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary,
451
            'self' => $truncmysummary),
452
            'package.xml 1.0 summary "%summary%" does not match "%self%"');
453
    }
454
 
455
    function _differentDescription($description)
456
    {
457
        $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...');
458
        $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() :
459
            substr($this->getdescription(), 0, 24) . '...');
460
        $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription,
461
            'self' => $truncmydescription),
462
            'package.xml 1.0 description "%description%" does not match "%self%"');
463
    }
464
 
465
    function _missingFile($file)
466
    {
467
        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
468
            'package.xml 1.0 file "%file%" is not present in <contents>');
469
    }
470
 
471
    /**
472
     * WARNING - do not use this function unless you know what you're doing
473
     */
474
    function setRawState($state)
475
    {
476
        $this->_packageInfo['stability']['release'] = $state;
477
    }
478
 
479
    /**
480
     * WARNING - do not use this function unless you know what you're doing
481
     */
482
    function setRawCompatible($compatible)
483
    {
484
        $this->_packageInfo['compatible'] = $compatible;
485
    }
486
 
487
    /**
488
     * WARNING - do not use this function unless you know what you're doing
489
     */
490
    function setRawPackage($package)
491
    {
492
        $this->_packageInfo['name'] = $package;
493
    }
494
 
495
    /**
496
     * WARNING - do not use this function unless you know what you're doing
497
     */
498
    function setRawChannel($channel)
499
    {
500
        $this->_packageInfo['channel'] = $channel;
501
    }
502
 
503
    function setRequestedGroup($group)
504
    {
505
        $this->_requestedGroup = $group;
506
    }
507
 
508
    function getRequestedGroup()
509
    {
510
        if (isset($this->_requestedGroup)) {
511
            return $this->_requestedGroup;
512
        }
513
        return false;
514
    }
515
 
516
    /**
517
     * For saving in the registry.
518
     *
519
     * Set the last version that was installed
520
     * @param string
521
     */
522
    function setLastInstalledVersion($version)
523
    {
524
        $this->_packageInfo['_lastversion'] = $version;
525
    }
526
 
527
    /**
528
     * @return string|false
529
     */
530
    function getLastInstalledVersion()
531
    {
532
        if (isset($this->_packageInfo['_lastversion'])) {
533
            return $this->_packageInfo['_lastversion'];
534
        }
535
        return false;
536
    }
537
 
538
    /**
539
     * Determines whether this package.xml has post-install scripts or not
540
     * @return array|false
541
     */
542
    function listPostinstallScripts()
543
    {
544
        $filelist = $this->getFilelist();
545
        $contents = $this->getContents();
546
        $contents = $contents['dir']['file'];
547
        if (!is_array($contents) || !isset($contents[0])) {
548
            $contents = array($contents);
549
        }
550
        $taskfiles = array();
551
        foreach ($contents as $file) {
552
            $atts = $file['attribs'];
553
            unset($file['attribs']);
554
            if (count($file)) {
555
                $taskfiles[$atts['name']] = $file;
556
            }
557
        }
558
        $common = new PEAR_Common;
559
        $common->debug = $this->_config->get('verbose');
560
        $this->_scripts = array();
561
        $ret = array();
562
        foreach ($taskfiles as $name => $tasks) {
563
            if (!isset($filelist[$name])) {
564
                // ignored files will not be in the filelist
565
                continue;
566
            }
567
            $atts = $filelist[$name];
568
            foreach ($tasks as $tag => $raw) {
569
                $task = $this->getTask($tag);
570
                $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL);
571
                if ($task->isScript()) {
572
                    $ret[] = $filelist[$name]['installed_as'];
573
                }
574
            }
575
        }
576
        if (count($ret)) {
577
            return $ret;
578
        }
579
        return false;
580
    }
581
 
582
    /**
583
     * Initialize post-install scripts for running
584
     *
585
     * This method can be used to detect post-install scripts, as the return value
586
     * indicates whether any exist
587
     * @return bool
588
     */
589
    function initPostinstallScripts()
590
    {
591
        $filelist = $this->getFilelist();
592
        $contents = $this->getContents();
593
        $contents = $contents['dir']['file'];
594
        if (!is_array($contents) || !isset($contents[0])) {
595
            $contents = array($contents);
596
        }
597
        $taskfiles = array();
598
        foreach ($contents as $file) {
599
            $atts = $file['attribs'];
600
            unset($file['attribs']);
601
            if (count($file)) {
602
                $taskfiles[$atts['name']] = $file;
603
            }
604
        }
605
        $common = new PEAR_Common;
606
        $common->debug = $this->_config->get('verbose');
607
        $this->_scripts = array();
608
        foreach ($taskfiles as $name => $tasks) {
609
            if (!isset($filelist[$name])) {
610
                // file was not installed due to installconditions
611
                continue;
612
            }
613
            $atts = $filelist[$name];
614
            foreach ($tasks as $tag => $raw) {
615
                $taskname = $this->getTask($tag);
616
                $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL);
617
                if (!$task->isScript()) {
618
                    continue; // scripts are only handled after installation
619
                }
620
                $lastversion = isset($this->_packageInfo['_lastversion']) ?
621
                    $this->_packageInfo['_lastversion'] : null;
622
                $task->init($raw, $atts, $lastversion);
623
                $res = $task->startSession($this, $atts['installed_as']);
624
                if (!$res) {
625
                    continue; // skip this file
626
                }
627
                if (PEAR::isError($res)) {
628
                    return $res;
629
                }
630
                $assign = &$task;
631
                $this->_scripts[] = &$assign;
632
            }
633
        }
634
        if (count($this->_scripts)) {
635
            return true;
636
        }
637
        return false;
638
    }
639
 
640
    function runPostinstallScripts()
641
    {
642
        if ($this->initPostinstallScripts()) {
643
            $ui = &PEAR_Frontend::singleton();
644
            if ($ui) {
645
                $ui->runPostinstallScripts($this->_scripts, $this);
646
            }
647
        }
648
    }
649
 
650
 
651
    /**
652
     * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
653
     * <file> tags.
654
     */
655
    function flattenFilelist()
656
    {
657
        if (isset($this->_packageInfo['bundle'])) {
658
            return;
659
        }
660
        $filelist = array();
661
        if (isset($this->_packageInfo['contents']['dir']['dir'])) {
662
            $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']);
663
            if (!isset($filelist[1])) {
664
                $filelist = $filelist[0];
665
            }
666
            $this->_packageInfo['contents']['dir']['file'] = $filelist;
667
            unset($this->_packageInfo['contents']['dir']['dir']);
668
        } else {
669
            // else already flattened but check for baseinstalldir propagation
670
            if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) {
671
                if (isset($this->_packageInfo['contents']['dir']['file'][0])) {
672
                    foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) {
673
                        if (isset($file['attribs']['baseinstalldir'])) {
674
                            continue;
675
                        }
676
                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
677
                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
678
                    }
679
                } else {
680
                    if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) {
681
                       $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir']
682
                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
683
                    }
684
                }
685
            }
686
        }
687
    }
688
 
689
    /**
690
     * @param array the final flattened file list
691
     * @param array the current directory being processed
692
     * @param string|false any recursively inherited baeinstalldir attribute
693
     * @param string private recursion variable
694
     * @return array
695
     * @access protected
696
     */
697
    function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
698
    {
699
        if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
700
            $baseinstall = $dir['attribs']['baseinstalldir'];
701
        }
702
        if (isset($dir['dir'])) {
703
            if (!isset($dir['dir'][0])) {
704
                $dir['dir'] = array($dir['dir']);
705
            }
706
            foreach ($dir['dir'] as $subdir) {
707
                if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
708
                    $name = '*unknown*';
709
                } else {
710
                    $name = $subdir['attribs']['name'];
711
                }
712
                $newpath = empty($path) ? $name :
713
                    $path . '/' . $name;
714
                $this->_getFlattenedFilelist($files, $subdir,
715
                    $baseinstall, $newpath);
716
            }
717
        }
718
        if (isset($dir['file'])) {
719
            if (!isset($dir['file'][0])) {
720
                $dir['file'] = array($dir['file']);
721
            }
722
            foreach ($dir['file'] as $file) {
723
                $attrs = $file['attribs'];
724
                $name = $attrs['name'];
725
                if ($baseinstall && !isset($attrs['baseinstalldir'])) {
726
                    $attrs['baseinstalldir'] = $baseinstall;
727
                }
728
                $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
729
                $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
730
                    $attrs['name']);
731
                $file['attribs'] = $attrs;
732
                $files[] = $file;
733
            }
734
        }
735
    }
736
 
737
    function setConfig(&$config)
738
    {
739
        $this->_config = &$config;
740
        $this->_registry = &$config->getRegistry();
741
    }
742
 
743
    function setLogger(&$logger)
744
    {
745
        if (!is_object($logger) || !method_exists($logger, 'log')) {
746
            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
747
        }
748
        $this->_logger = &$logger;
749
    }
750
 
751
    /**
752
     * WARNING - do not use this function directly unless you know what you're doing
753
     */
754
    function setDeps($deps)
755
    {
756
        $this->_packageInfo['dependencies'] = $deps;
757
    }
758
 
759
    /**
760
     * WARNING - do not use this function directly unless you know what you're doing
761
     */
762
    function setCompatible($compat)
763
    {
764
        $this->_packageInfo['compatible'] = $compat;
765
    }
766
 
767
    function setPackagefile($file, $archive = false)
768
    {
769
        $this->_packageFile = $file;
770
        $this->_archiveFile = $archive ? $archive : $file;
771
    }
772
 
773
    /**
774
     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
775
     * @param boolean determines whether to purge the error stack after retrieving
776
     * @return array
777
     */
778
    function getValidationWarnings($purge = true)
779
    {
780
        return $this->_stack->getErrors($purge);
781
    }
782
 
783
    function getPackageFile()
784
    {
785
        return $this->_packageFile;
786
    }
787
 
788
    function getArchiveFile()
789
    {
790
        return $this->_archiveFile;
791
    }
792
 
793
 
794
    /**
795
     * Directly set the array that defines this packagefile
796
     *
797
     * WARNING: no validation.  This should only be performed by internal methods
798
     * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2
799
     * @param array
800
     */
801
    function fromArray($pinfo)
802
    {
803
        unset($pinfo['old']);
804
        unset($pinfo['xsdversion']);
805
        $this->_incomplete = false;
806
        $this->_packageInfo = $pinfo;
807
    }
808
 
809
    function isIncomplete()
810
    {
811
        return $this->_incomplete;
812
    }
813
 
814
    /**
815
     * @return array
816
     */
817
    function toArray($forreg = false)
818
    {
819
        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
820
            return false;
821
        }
822
        return $this->getArray($forreg);
823
    }
824
 
825
    function getArray($forReg = false)
826
    {
827
        if ($forReg) {
828
            $arr = $this->_packageInfo;
829
            $arr['old'] = array();
830
            $arr['old']['version'] = $this->getVersion();
831
            $arr['old']['release_date'] = $this->getDate();
832
            $arr['old']['release_state'] = $this->getState();
833
            $arr['old']['release_license'] = $this->getLicense();
834
            $arr['old']['release_notes'] = $this->getNotes();
835
            $arr['old']['release_deps'] = $this->getDeps();
836
            $arr['old']['maintainers'] = $this->getMaintainers();
837
            $arr['xsdversion'] = '2.0';
838
            return $arr;
839
        } else {
840
            $info = $this->_packageInfo;
841
            unset($info['dirtree']);
842
            if (isset($info['_lastversion'])) {
843
                unset($info['_lastversion']);
844
            }
845
            if (isset($info['#binarypackage'])) {
846
                unset($info['#binarypackage']);
847
            }
848
            return $info;
849
        }
850
    }
851
 
852
    function packageInfo($field)
853
    {
854
        $arr = $this->getArray(true);
855
        if ($field == 'state') {
856
            return $arr['stability']['release'];
857
        }
858
        if ($field == 'api-version') {
859
            return $arr['version']['api'];
860
        }
861
        if ($field == 'api-state') {
862
            return $arr['stability']['api'];
863
        }
864
        if (isset($arr['old'][$field])) {
865
            if (!is_string($arr['old'][$field])) {
866
                return null;
867
            }
868
            return $arr['old'][$field];
869
        }
870
        if (isset($arr[$field])) {
871
            if (!is_string($arr[$field])) {
872
                return null;
873
            }
874
            return $arr[$field];
875
        }
876
        return null;
877
    }
878
 
879
    function getName()
880
    {
881
        return $this->getPackage();
882
    }
883
 
884
    function getPackage()
885
    {
886
        if (isset($this->_packageInfo['name'])) {
887
            return $this->_packageInfo['name'];
888
        }
889
        return false;
890
    }
891
 
892
    function getChannel()
893
    {
894
        if (isset($this->_packageInfo['uri'])) {
895
            return '__uri';
896
        }
897
        if (isset($this->_packageInfo['channel'])) {
898
            return strtolower($this->_packageInfo['channel']);
899
        }
900
        return false;
901
    }
902
 
903
    function getUri()
904
    {
905
        if (isset($this->_packageInfo['uri'])) {
906
            return $this->_packageInfo['uri'];
907
        }
908
        return false;
909
    }
910
 
911
    function getExtends()
912
    {
913
        if (isset($this->_packageInfo['extends'])) {
914
            return $this->_packageInfo['extends'];
915
        }
916
        return false;
917
    }
918
 
919
    function getSummary()
920
    {
921
        if (isset($this->_packageInfo['summary'])) {
922
            return $this->_packageInfo['summary'];
923
        }
924
        return false;
925
    }
926
 
927
    function getDescription()
928
    {
929
        if (isset($this->_packageInfo['description'])) {
930
            return $this->_packageInfo['description'];
931
        }
932
        return false;
933
    }
934
 
935
    function getMaintainers($raw = false)
936
    {
937
        if (!isset($this->_packageInfo['lead'])) {
938
            return false;
939
        }
940
        if ($raw) {
941
            $ret = array('lead' => $this->_packageInfo['lead']);
942
            (isset($this->_packageInfo['developer'])) ?
943
                $ret['developer'] = $this->_packageInfo['developer'] :null;
944
            (isset($this->_packageInfo['contributor'])) ?
945
                $ret['contributor'] = $this->_packageInfo['contributor'] :null;
946
            (isset($this->_packageInfo['helper'])) ?
947
                $ret['helper'] = $this->_packageInfo['helper'] :null;
948
            return $ret;
949
        } else {
950
            $ret = array();
951
            $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
952
                array($this->_packageInfo['lead']);
953
            foreach ($leads as $lead) {
954
                $s = $lead;
955
                $s['handle'] = $s['user'];
956
                unset($s['user']);
957
                $s['role'] = 'lead';
958
                $ret[] = $s;
959
            }
960
            if (isset($this->_packageInfo['developer'])) {
961
                $leads = isset($this->_packageInfo['developer'][0]) ?
962
                    $this->_packageInfo['developer'] :
963
                    array($this->_packageInfo['developer']);
964
                foreach ($leads as $maintainer) {
965
                    $s = $maintainer;
966
                    $s['handle'] = $s['user'];
967
                    unset($s['user']);
968
                    $s['role'] = 'developer';
969
                    $ret[] = $s;
970
                }
971
            }
972
            if (isset($this->_packageInfo['contributor'])) {
973
                $leads = isset($this->_packageInfo['contributor'][0]) ?
974
                    $this->_packageInfo['contributor'] :
975
                    array($this->_packageInfo['contributor']);
976
                foreach ($leads as $maintainer) {
977
                    $s = $maintainer;
978
                    $s['handle'] = $s['user'];
979
                    unset($s['user']);
980
                    $s['role'] = 'contributor';
981
                    $ret[] = $s;
982
                }
983
            }
984
            if (isset($this->_packageInfo['helper'])) {
985
                $leads = isset($this->_packageInfo['helper'][0]) ?
986
                    $this->_packageInfo['helper'] :
987
                    array($this->_packageInfo['helper']);
988
                foreach ($leads as $maintainer) {
989
                    $s = $maintainer;
990
                    $s['handle'] = $s['user'];
991
                    unset($s['user']);
992
                    $s['role'] = 'helper';
993
                    $ret[] = $s;
994
                }
995
            }
996
            return $ret;
997
        }
998
        return false;
999
    }
1000
 
1001
    function getLeads()
1002
    {
1003
        if (isset($this->_packageInfo['lead'])) {
1004
            return $this->_packageInfo['lead'];
1005
        }
1006
        return false;
1007
    }
1008
 
1009
    function getDevelopers()
1010
    {
1011
        if (isset($this->_packageInfo['developer'])) {
1012
            return $this->_packageInfo['developer'];
1013
        }
1014
        return false;
1015
    }
1016
 
1017
    function getContributors()
1018
    {
1019
        if (isset($this->_packageInfo['contributor'])) {
1020
            return $this->_packageInfo['contributor'];
1021
        }
1022
        return false;
1023
    }
1024
 
1025
    function getHelpers()
1026
    {
1027
        if (isset($this->_packageInfo['helper'])) {
1028
            return $this->_packageInfo['helper'];
1029
        }
1030
        return false;
1031
    }
1032
 
1033
    function setDate($date)
1034
    {
1035
        if (!isset($this->_packageInfo['date'])) {
1036
            // ensure that the extends tag is set up in the right location
1037
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
1038
                array('time', 'version',
1039
                    'stability', 'license', 'notes', 'contents', 'compatible',
1040
                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
1041
                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
1042
                    'zendextbinrelease', 'bundle', 'changelog'), array(), 'date');
1043
        }
1044
        $this->_packageInfo['date'] = $date;
1045
        $this->_isValid = 0;
1046
    }
1047
 
1048
    function setTime($time)
1049
    {
1050
        $this->_isValid = 0;
1051
        if (!isset($this->_packageInfo['time'])) {
1052
            // ensure that the time tag is set up in the right location
1053
            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
1054
                    array('version',
1055
                    'stability', 'license', 'notes', 'contents', 'compatible',
1056
                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
1057
                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
1058
                    'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
1059
        }
1060
        $this->_packageInfo['time'] = $time;
1061
    }
1062
 
1063
    function getDate()
1064
    {
1065
        if (isset($this->_packageInfo['date'])) {
1066
            return $this->_packageInfo['date'];
1067
        }
1068
        return false;
1069
    }
1070
 
1071
    function getTime()
1072
    {
1073
        if (isset($this->_packageInfo['time'])) {
1074
            return $this->_packageInfo['time'];
1075
        }
1076
        return false;
1077
    }
1078
 
1079
    /**
1080
     * @param package|api version category to return
1081
     */
1082
    function getVersion($key = 'release')
1083
    {
1084
        if (isset($this->_packageInfo['version'][$key])) {
1085
            return $this->_packageInfo['version'][$key];
1086
        }
1087
        return false;
1088
    }
1089
 
1090
    function getStability()
1091
    {
1092
        if (isset($this->_packageInfo['stability'])) {
1093
            return $this->_packageInfo['stability'];
1094
        }
1095
        return false;
1096
    }
1097
 
1098
    function getState($key = 'release')
1099
    {
1100
        if (isset($this->_packageInfo['stability'][$key])) {
1101
            return $this->_packageInfo['stability'][$key];
1102
        }
1103
        return false;
1104
    }
1105
 
1106
    function getLicense($raw = false)
1107
    {
1108
        if (isset($this->_packageInfo['license'])) {
1109
            if ($raw) {
1110
                return $this->_packageInfo['license'];
1111
            }
1112
            if (is_array($this->_packageInfo['license'])) {
1113
                return $this->_packageInfo['license']['_content'];
1114
            } else {
1115
                return $this->_packageInfo['license'];
1116
            }
1117
        }
1118
        return false;
1119
    }
1120
 
1121
    function getLicenseLocation()
1122
    {
1123
        if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
1124
            return false;
1125
        }
1126
        return $this->_packageInfo['license']['attribs'];
1127
    }
1128
 
1129
    function getNotes()
1130
    {
1131
        if (isset($this->_packageInfo['notes'])) {
1132
            return $this->_packageInfo['notes'];
1133
        }
1134
        return false;
1135
    }
1136
 
1137
    /**
1138
     * Return the <usesrole> tag contents, if any
1139
     * @return array|false
1140
     */
1141
    function getUsesrole()
1142
    {
1143
        if (isset($this->_packageInfo['usesrole'])) {
1144
            return $this->_packageInfo['usesrole'];
1145
        }
1146
        return false;
1147
    }
1148
 
1149
    /**
1150
     * Return the <usestask> tag contents, if any
1151
     * @return array|false
1152
     */
1153
    function getUsestask()
1154
    {
1155
        if (isset($this->_packageInfo['usestask'])) {
1156
            return $this->_packageInfo['usestask'];
1157
        }
1158
        return false;
1159
    }
1160
 
1161
    /**
1162
     * This should only be used to retrieve filenames and install attributes
1163
     */
1164
    function getFilelist($preserve = false)
1165
    {
1166
        if (isset($this->_packageInfo['filelist']) && !$preserve) {
1167
            return $this->_packageInfo['filelist'];
1168
        }
1169
        $this->flattenFilelist();
1170
        if ($contents = $this->getContents()) {
1171
            $ret = array();
1172
            if (!isset($contents['dir']['file'][0])) {
1173
                $contents['dir']['file'] = array($contents['dir']['file']);
1174
            }
1175
            foreach ($contents['dir']['file'] as $file) {
1176
                $name = $file['attribs']['name'];
1177
                if (!$preserve) {
1178
                    $file = $file['attribs'];
1179
                }
1180
                $ret[$name] = $file;
1181
            }
1182
            if (!$preserve) {
1183
                $this->_packageInfo['filelist'] = $ret;
1184
            }
1185
            return $ret;
1186
        }
1187
        return false;
1188
    }
1189
 
1190
    /**
1191
     * Return configure options array, if any
1192
     *
1193
     * @return array|false
1194
     */
1195
    function getConfigureOptions()
1196
    {
1197
        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
1198
            return false;
1199
        }
1200
        $releases = $this->getReleases();
1201
        if (isset($releases[0])) {
1202
            $releases = $releases[0];
1203
        }
1204
        if (isset($releases['configureoption'])) {
1205
            if (!isset($releases['configureoption'][0])) {
1206
                $releases['configureoption'] = array($releases['configureoption']);
1207
            }
1208
            for ($i = 0; $i < count($releases['configureoption']); $i++) {
1209
                $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
1210
            }
1211
            return $releases['configureoption'];
1212
        }
1213
        return false;
1214
    }
1215
 
1216
    /**
1217
     * This is only used at install-time, after all serialization
1218
     * is over.
1219
     */
1220
    function resetFilelist()
1221
    {
1222
        $this->_packageInfo['filelist'] = array();
1223
    }
1224
 
1225
    /**
1226
     * Retrieve a list of files that should be installed on this computer
1227
     * @return array
1228
     */
1229
    function getInstallationFilelist($forfilecheck = false)
1230
    {
1231
        $contents = $this->getFilelist(true);
1232
        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
1233
            $base = $contents['dir']['attribs']['baseinstalldir'];
1234
        }
1235
        if (isset($this->_packageInfo['bundle'])) {
1236
            return PEAR::raiseError(
1237
                'Exception: bundles should be handled in download code only');
1238
        }
1239
        $release = $this->getReleases();
1240
        if ($release) {
1241
            if (!isset($release[0])) {
1242
                if (!isset($release['installconditions']) && !isset($release['filelist'])) {
1243
                    if ($forfilecheck) {
1244
                        return $this->getFilelist();
1245
                    }
1246
                    return $contents;
1247
                }
1248
                $release = array($release);
1249
            }
1250
            $depchecker = &$this->getPEARDependency2($this->_config, array(),
1251
                array('channel' => $this->getChannel(), 'package' => $this->getPackage()),
1252
                PEAR_VALIDATE_INSTALLING);
1253
            foreach ($release as $instance) {
1254
                if (isset($instance['installconditions'])) {
1255
                    $installconditions = $instance['installconditions'];
1256
                    if (is_array($installconditions)) {
1257
                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1258
                        foreach ($installconditions as $type => $conditions) {
1259
                            if (!isset($conditions[0])) {
1260
                                $conditions = array($conditions);
1261
                            }
1262
                            foreach ($conditions as $condition) {
1263
                                $ret = $depchecker->{"validate{$type}Dependency"}($condition);
1264
                                if (PEAR::isError($ret)) {
1265
                                    PEAR::popErrorHandling();
1266
                                    continue 3; // skip this release
1267
                                }
1268
                            }
1269
                        }
1270
                        PEAR::popErrorHandling();
1271
                    }
1272
                }
1273
                // this is the release to use
1274
                if (isset($instance['filelist'])) {
1275
                    // ignore files
1276
                    if (isset($instance['filelist']['ignore'])) {
1277
                        $ignore = isset($instance['filelist']['ignore'][0]) ?
1278
                            $instance['filelist']['ignore'] :
1279
                            array($instance['filelist']['ignore']);
1280
                        foreach ($ignore as $ig) {
1281
                            unset ($contents[$ig['attribs']['name']]);
1282
                        }
1283
                    }
1284
                    // install files as this name
1285
                    if (isset($instance['filelist']['install'])) {
1286
                        $installas = isset($instance['filelist']['install'][0]) ?
1287
                            $instance['filelist']['install'] :
1288
                            array($instance['filelist']['install']);
1289
                        foreach ($installas as $as) {
1290
                            $contents[$as['attribs']['name']]['attribs']['install-as'] =
1291
                                $as['attribs']['as'];
1292
                        }
1293
                    }
1294
                }
1295
                if ($forfilecheck) {
1296
                    foreach ($contents as $file => $attrs) {
1297
                        $contents[$file] = $attrs['attribs'];
1298
                    }
1299
                }
1300
                return $contents;
1301
            }
1302
        } else { // simple release - no installconditions or install-as
1303
            if ($forfilecheck) {
1304
                return $this->getFilelist();
1305
            }
1306
            return $contents;
1307
        }
1308
        // no releases matched
1309
        return PEAR::raiseError('No releases in package.xml matched the existing operating ' .
1310
            'system, extensions installed, or architecture, cannot install');
1311
    }
1312
 
1313
    /**
1314
     * This is only used at install-time, after all serialization
1315
     * is over.
1316
     * @param string file name
1317
     * @param string installed path
1318
     */
1319
    function setInstalledAs($file, $path)
1320
    {
1321
        if ($path) {
1322
            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
1323
        }
1324
        unset($this->_packageInfo['filelist'][$file]['installed_as']);
1325
    }
1326
 
1327
    function getInstalledLocation($file)
1328
    {
1329
        if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
1330
            return $this->_packageInfo['filelist'][$file]['installed_as'];
1331
        }
1332
        return false;
1333
    }
1334
 
1335
    /**
1336
     * This is only used at install-time, after all serialization
1337
     * is over.
1338
     */
1339
    function installedFile($file, $atts)
1340
    {
1341
        if (isset($this->_packageInfo['filelist'][$file])) {
1342
            $this->_packageInfo['filelist'][$file] =
1343
                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
1344
        } else {
1345
            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
1346
        }
1347
    }
1348
 
1349
    /**
1350
     * Retrieve the contents tag
1351
     */
1352
    function getContents()
1353
    {
1354
        if (isset($this->_packageInfo['contents'])) {
1355
            return $this->_packageInfo['contents'];
1356
        }
1357
        return false;
1358
    }
1359
 
1360
    /**
1361
     * @param string full path to file
1362
     * @param string attribute name
1363
     * @param string attribute value
1364
     * @param int risky but fast - use this to choose a file based on its position in the list
1365
     *            of files.  Index is zero-based like PHP arrays.
1366
     * @return bool success of operation
1367
     */
1368
    function setFileAttribute($filename, $attr, $value, $index = false)
1369
    {
1370
        $this->_isValid = 0;
1371
        if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
1372
            $this->_filesValid = false;
1373
        }
1374
        if ($index !== false &&
1375
              isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
1376
            $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
1377
            return true;
1378
        }
1379
        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
1380
            return false;
1381
        }
1382
        $files = $this->_packageInfo['contents']['dir']['file'];
1383
        if (!isset($files[0])) {
1384
            $files = array($files);
1385
            $ind = false;
1386
        } else {
1387
            $ind = true;
1388
        }
1389
        foreach ($files as $i => $file) {
1390
            if (isset($file['attribs'])) {
1391
                if ($file['attribs']['name'] == $filename) {
1392
                    if ($ind) {
1393
                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
1394
                    } else {
1395
                        $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
1396
                    }
1397
                    return true;
1398
                }
1399
            }
1400
        }
1401
        return false;
1402
    }
1403
 
1404
    function setDirtree($path)
1405
    {
1406
        if (!isset($this->_packageInfo['dirtree'])) {
1407
            $this->_packageInfo['dirtree'] = array();
1408
        }
1409
        $this->_packageInfo['dirtree'][$path] = true;
1410
    }
1411
 
1412
    function getDirtree()
1413
    {
1414
        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
1415
            return $this->_packageInfo['dirtree'];
1416
        }
1417
        return false;
1418
    }
1419
 
1420
    function resetDirtree()
1421
    {
1422
        unset($this->_packageInfo['dirtree']);
1423
    }
1424
 
1425
    /**
1426
     * Determines whether this package claims it is compatible with the version of
1427
     * the package that has a recommended version dependency
1428
     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package
1429
     * @return boolean
1430
     */
1431
    function isCompatible($pf)
1432
    {
1433
        if (!isset($this->_packageInfo['compatible'])) {
1434
            return false;
1435
        }
1436
        if (!isset($this->_packageInfo['channel'])) {
1437
            return false;
1438
        }
1439
        $me = $pf->getVersion();
1440
        $compatible = $this->_packageInfo['compatible'];
1441
        if (!isset($compatible[0])) {
1442
            $compatible = array($compatible);
1443
        }
1444
        $found = false;
1445
        foreach ($compatible as $info) {
1446
            if (strtolower($info['name']) == strtolower($pf->getPackage())) {
1447
                if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
1448
                    $found = true;
1449
                    break;
1450
                }
1451
            }
1452
        }
1453
        if (!$found) {
1454
            return false;
1455
        }
1456
        if (isset($info['exclude'])) {
1457
            if (!isset($info['exclude'][0])) {
1458
                $info['exclude'] = array($info['exclude']);
1459
            }
1460
            foreach ($info['exclude'] as $exclude) {
1461
                if (version_compare($me, $exclude, '==')) {
1462
                    return false;
1463
                }
1464
            }
1465
        }
1466
        if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
1467
            return true;
1468
        }
1469
        return false;
1470
    }
1471
 
1472
    /**
1473
     * @return array|false
1474
     */
1475
    function getCompatible()
1476
    {
1477
        if (isset($this->_packageInfo['compatible'])) {
1478
            return $this->_packageInfo['compatible'];
1479
        }
1480
        return false;
1481
    }
1482
 
1483
    function getDependencies()
1484
    {
1485
        if (isset($this->_packageInfo['dependencies'])) {
1486
            return $this->_packageInfo['dependencies'];
1487
        }
1488
        return false;
1489
    }
1490
 
1491
    function isSubpackageOf($p)
1492
    {
1493
        return $p->isSubpackage($this);
1494
    }
1495
 
1496
    /**
1497
     * Determines whether the passed in package is a subpackage of this package.
1498
     *
1499
     * No version checking is done, only name verification.
1500
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
1501
     * @return bool
1502
     */
1503
    function isSubpackage($p)
1504
    {
1505
        $sub = array();
1506
        if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
1507
            $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
1508
            if (!isset($sub[0])) {
1509
                $sub = array($sub);
1510
            }
1511
        }
1512
        if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
1513
            $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
1514
            if (!isset($sub1[0])) {
1515
                $sub1 = array($sub1);
1516
            }
1517
            $sub = array_merge($sub, $sub1);
1518
        }
1519
        if (isset($this->_packageInfo['dependencies']['group'])) {
1520
            $group = $this->_packageInfo['dependencies']['group'];
1521
            if (!isset($group[0])) {
1522
                $group = array($group);
1523
            }
1524
            foreach ($group as $deps) {
1525
                if (isset($deps['subpackage'])) {
1526
                    $sub2 = $deps['subpackage'];
1527
                    if (!isset($sub2[0])) {
1528
                        $sub2 = array($sub2);
1529
                    }
1530
                    $sub = array_merge($sub, $sub2);
1531
                }
1532
            }
1533
        }
1534
        foreach ($sub as $dep) {
1535
            if (strtolower($dep['name']) == strtolower($p->getPackage())) {
1536
                if (isset($dep['channel'])) {
1537
                    if (strtolower($dep['channel']) == strtolower($p->getChannel())) {
1538
                        return true;
1539
                    }
1540
                } else {
1541
                    if ($dep['uri'] == $p->getURI()) {
1542
                        return true;
1543
                    }
1544
                }
1545
            }
1546
        }
1547
        return false;
1548
    }
1549
 
1550
    function dependsOn($package, $channel)
1551
    {
1552
        if (!($deps = $this->getDependencies())) {
1553
            return false;
1554
        }
1555
        foreach (array('package', 'subpackage') as $type) {
1556
            foreach (array('required', 'optional') as $needed) {
1557
                if (isset($deps[$needed][$type])) {
1558
                    if (!isset($deps[$needed][$type][0])) {
1559
                        $deps[$needed][$type] = array($deps[$needed][$type]);
1560
                    }
1561
                    foreach ($deps[$needed][$type] as $dep) {
1562
                        $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
1563
                        if (strtolower($dep['name']) == strtolower($package) &&
1564
                              $depchannel == $channel) {
1565
                            return true;
1566
                        }
1567
                    }
1568
                }
1569
            }
1570
            if (isset($deps['group'])) {
1571
                if (!isset($deps['group'][0])) {
1572
                    $dep['group'] = array($deps['group']);
1573
                }
1574
                foreach ($deps['group'] as $group) {
1575
                    if (isset($group[$type])) {
1576
                        if (!is_array($group[$type])) {
1577
                            $group[$type] = array($group[$type]);
1578
                        }
1579
                        foreach ($group[$type] as $dep) {
1580
                            $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
1581
                            if (strtolower($dep['name']) == strtolower($package) &&
1582
                                  $depchannel == $channel) {
1583
                                return true;
1584
                            }
1585
                        }
1586
                    }
1587
                }
1588
            }
1589
        }
1590
        return false;
1591
    }
1592
 
1593
    /**
1594
     * Get the contents of a dependency group
1595
     * @param string
1596
     * @return array|false
1597
     */
1598
    function getDependencyGroup($name)
1599
    {
1600
        $name = strtolower($name);
1601
        if (!isset($this->_packageInfo['dependencies']['group'])) {
1602
            return false;
1603
        }
1604
        $groups = $this->_packageInfo['dependencies']['group'];
1605
        if (!isset($groups[0])) {
1606
            $groups = array($groups);
1607
        }
1608
        foreach ($groups as $group) {
1609
            if (strtolower($group['attribs']['name']) == $name) {
1610
                return $group;
1611
            }
1612
        }
1613
        return false;
1614
    }
1615
 
1616
    /**
1617
     * Retrieve a partial package.xml 1.0 representation of dependencies
1618
     *
1619
     * a very limited representation of dependencies is returned by this method.
1620
     * The <exclude> tag for excluding certain versions of a dependency is
1621
     * completely ignored.  In addition, dependency groups are ignored, with the
1622
     * assumption that all dependencies in dependency groups are also listed in
1623
     * the optional group that work with all dependency groups
1624
     * @param boolean return package.xml 2.0 <dependencies> tag
1625
     * @return array|false
1626
     */
1627
    function getDeps($raw = false, $nopearinstaller = false)
1628
    {
1629
        if (isset($this->_packageInfo['dependencies'])) {
1630
            if ($raw) {
1631
                return $this->_packageInfo['dependencies'];
1632
            }
1633
            $ret = array();
1634
            $map = array(
1635
                'php' => 'php',
1636
                'package' => 'pkg',
1637
                'subpackage' => 'pkg',
1638
                'extension' => 'ext',
1639
                'os' => 'os',
1640
                'pearinstaller' => 'pkg',
1641
                );
1642
            foreach (array('required', 'optional') as $type) {
1643
                $optional = ($type == 'optional') ? 'yes' : 'no';
1644
                if (!isset($this->_packageInfo['dependencies'][$type])) {
1645
                    continue;
1646
                }
1647
                foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
1648
                    if ($dtype == 'pearinstaller' && $nopearinstaller) {
1649
                        continue;
1650
                    }
1651
                    if (!isset($deps[0])) {
1652
                        $deps = array($deps);
1653
                    }
1654
                    foreach ($deps as $dep) {
1655
                        if (!isset($map[$dtype])) {
1656
                            // no support for arch type
1657
                            continue;
1658
                        }
1659
                        if ($dtype == 'pearinstaller') {
1660
                            $dep['name'] = 'PEAR';
1661
                            $dep['channel'] = 'pear.php.net';
1662
                        }
1663
                        $s = array('type' => $map[$dtype]);
1664
                        if (isset($dep['channel'])) {
1665
                            $s['channel'] = $dep['channel'];
1666
                        }
1667
                        if (isset($dep['uri'])) {
1668
                            $s['uri'] = $dep['uri'];
1669
                        }
1670
                        if (isset($dep['name'])) {
1671
                            $s['name'] = $dep['name'];
1672
                        }
1673
                        if (isset($dep['conflicts'])) {
1674
                            $s['rel'] = 'not';
1675
                        } else {
1676
                            if (!isset($dep['min']) &&
1677
                                  !isset($dep['max'])) {
1678
                                $s['rel'] = 'has';
1679
                                $s['optional'] = $optional;
1680
                            } elseif (isset($dep['min']) &&
1681
                                  isset($dep['max'])) {
1682
                                $s['rel'] = 'ge';
1683
                                $s1 = $s;
1684
                                $s1['rel'] = 'le';
1685
                                $s['version'] = $dep['min'];
1686
                                $s1['version'] = $dep['max'];
1687
                                if (isset($dep['channel'])) {
1688
                                    $s1['channel'] = $dep['channel'];
1689
                                }
1690
                                if ($dtype != 'php') {
1691
                                    $s['name'] = $dep['name'];
1692
                                    $s1['name'] = $dep['name'];
1693
                                }
1694
                                $s['optional'] = $optional;
1695
                                $s1['optional'] = $optional;
1696
                                $ret[] = $s1;
1697
                            } elseif (isset($dep['min'])) {
1698
                                if (isset($dep['exclude']) &&
1699
                                      $dep['exclude'] == $dep['min']) {
1700
                                    $s['rel'] = 'gt';
1701
                                } else {
1702
                                    $s['rel'] = 'ge';
1703
                                }
1704
                                $s['version'] = $dep['min'];
1705
                                $s['optional'] = $optional;
1706
                                if ($dtype != 'php') {
1707
                                    $s['name'] = $dep['name'];
1708
                                }
1709
                            } elseif (isset($dep['max'])) {
1710
                                if (isset($dep['exclude']) &&
1711
                                      $dep['exclude'] == $dep['max']) {
1712
                                    $s['rel'] = 'lt';
1713
                                } else {
1714
                                    $s['rel'] = 'le';
1715
                                }
1716
                                $s['version'] = $dep['max'];
1717
                                $s['optional'] = $optional;
1718
                                if ($dtype != 'php') {
1719
                                    $s['name'] = $dep['name'];
1720
                                }
1721
                            }
1722
                        }
1723
                        $ret[] = $s;
1724
                    }
1725
                }
1726
            }
1727
            if (count($ret)) {
1728
                return $ret;
1729
            }
1730
        }
1731
        return false;
1732
    }
1733
 
1734
    /**
1735
     * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
1736
     */
1737
    function getPackageType()
1738
    {
1739
        if (isset($this->_packageInfo['phprelease'])) {
1740
            return 'php';
1741
        }
1742
        if (isset($this->_packageInfo['extsrcrelease'])) {
1743
            return 'extsrc';
1744
        }
1745
        if (isset($this->_packageInfo['extbinrelease'])) {
1746
            return 'extbin';
1747
        }
1748
        if (isset($this->_packageInfo['zendextsrcrelease'])) {
1749
            return 'zendextsrc';
1750
        }
1751
        if (isset($this->_packageInfo['zendextbinrelease'])) {
1752
            return 'zendextbin';
1753
        }
1754
        if (isset($this->_packageInfo['bundle'])) {
1755
            return 'bundle';
1756
        }
1757
        return false;
1758
    }
1759
 
1760
    /**
1761
     * @return array|false
1762
     */
1763
    function getReleases()
1764
    {
1765
        $type = $this->getPackageType();
1766
        if ($type != 'bundle') {
1767
            $type .= 'release';
1768
        }
1769
        if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
1770
            return $this->_packageInfo[$type];
1771
        }
1772
        return false;
1773
    }
1774
 
1775
    /**
1776
     * @return array
1777
     */
1778
    function getChangelog()
1779
    {
1780
        if (isset($this->_packageInfo['changelog'])) {
1781
            return $this->_packageInfo['changelog'];
1782
        }
1783
        return false;
1784
    }
1785
 
1786
    function hasDeps()
1787
    {
1788
        return isset($this->_packageInfo['dependencies']);
1789
    }
1790
 
1791
    function getPackagexmlVersion()
1792
    {
1793
        if (isset($this->_packageInfo['zendextsrcrelease'])) {
1794
            return '2.1';
1795
        }
1796
        if (isset($this->_packageInfo['zendextbinrelease'])) {
1797
            return '2.1';
1798
        }
1799
        return '2.0';
1800
    }
1801
 
1802
    /**
1803
     * @return array|false
1804
     */
1805
    function getSourcePackage()
1806
    {
1807
        if (isset($this->_packageInfo['extbinrelease']) ||
1808
              isset($this->_packageInfo['zendextbinrelease'])) {
1809
            return array('channel' => $this->_packageInfo['srcchannel'],
1810
                         'package' => $this->_packageInfo['srcpackage']);
1811
        }
1812
        return false;
1813
    }
1814
 
1815
    function getBundledPackages()
1816
    {
1817
        if (isset($this->_packageInfo['bundle'])) {
1818
            return $this->_packageInfo['contents']['bundledpackage'];
1819
        }
1820
        return false;
1821
    }
1822
 
1823
    function getLastModified()
1824
    {
1825
        if (isset($this->_packageInfo['_lastmodified'])) {
1826
            return $this->_packageInfo['_lastmodified'];
1827
        }
1828
        return false;
1829
    }
1830
 
1831
    /**
1832
     * Get the contents of a file listed within the package.xml
1833
     * @param string
1834
     * @return string
1835
     */
1836
    function getFileContents($file)
1837
    {
1838
        if ($this->_archiveFile == $this->_packageFile) { // unpacked
1839
            $dir = dirname($this->_packageFile);
1840
            $file = $dir . DIRECTORY_SEPARATOR . $file;
1841
            $file = str_replace(array('/', '\\'),
1842
                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
1843
            if (file_exists($file) && is_readable($file)) {
1844
                return implode('', file($file));
1845
            }
1846
        } else { // tgz
1847
            $tar = &new Archive_Tar($this->_archiveFile);
1848
            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
1849
            if ($file != 'package.xml' && $file != 'package2.xml') {
1850
                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
1851
            }
1852
            $file = $tar->extractInString($file);
1853
            $tar->popErrorHandling();
1854
            if (PEAR::isError($file)) {
1855
                return PEAR::raiseError("Cannot locate file '$file' in archive");
1856
            }
1857
            return $file;
1858
        }
1859
    }
1860
 
1861
    function &getRW()
1862
    {
1863
        if (!class_exists('PEAR_PackageFile_v2_rw')) {
1864
            require_once 'PEAR/PackageFile/v2/rw.php';
1865
        }
1866
        $a = new PEAR_PackageFile_v2_rw;
1867
        foreach (get_object_vars($this) as $name => $unused) {
1868
            if (!isset($this->$name)) {
1869
                continue;
1870
            }
1871
            if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
1872
                  $name == '_stack') {
1873
                $a->$name = &$this->$name;
1874
            } else {
1875
                $a->$name = $this->$name;
1876
            }
1877
        }
1878
        return $a;
1879
    }
1880
 
1881
    function &getDefaultGenerator()
1882
    {
1883
        if (!class_exists('PEAR_PackageFile_Generator_v2')) {
1884
            require_once 'PEAR/PackageFile/Generator/v2.php';
1885
        }
1886
        $a = &new PEAR_PackageFile_Generator_v2($this);
1887
        return $a;
1888
    }
1889
 
1890
    function analyzeSourceCode($file, $string = false)
1891
    {
1892
        if (!isset($this->_v2Validator) ||
1893
              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
1894
            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
1895
                require_once 'PEAR/PackageFile/v2/Validator.php';
1896
            }
1897
            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
1898
        }
1899
        return $this->_v2Validator->analyzeSourceCode($file, $string);
1900
    }
1901
 
1902
    function validate($state = PEAR_VALIDATE_NORMAL)
1903
    {
1904
        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
1905
            return false;
1906
        }
1907
        if (!isset($this->_v2Validator) ||
1908
              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
1909
            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
1910
                require_once 'PEAR/PackageFile/v2/Validator.php';
1911
            }
1912
            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
1913
        }
1914
        if (isset($this->_packageInfo['xsdversion'])) {
1915
            unset($this->_packageInfo['xsdversion']);
1916
        }
1917
        return $this->_v2Validator->validate($this, $state);
1918
    }
1919
 
1920
    function getTasksNs()
1921
    {
1922
        if (!isset($this->_tasksNs)) {
1923
            if (isset($this->_packageInfo['attribs'])) {
1924
                foreach ($this->_packageInfo['attribs'] as $name => $value) {
1925
                    if ($value == 'http://pear.php.net/dtd/tasks-1.0') {
1926
                        $this->_tasksNs = str_replace('xmlns:', '', $name);
1927
                        break;
1928
                    }
1929
                }
1930
            }
1931
        }
1932
        return $this->_tasksNs;
1933
    }
1934
 
1935
    /**
1936
     * Determine whether a task name is a valid task.  Custom tasks may be defined
1937
     * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task>
1938
     *
1939
     * Note that this method will auto-load the task class file and test for the existence
1940
     * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class
1941
     * PEAR_Task_mycustom_task
1942
     * @param string
1943
     * @return boolean
1944
     */
1945
    function getTask($task)
1946
    {
1947
        $this->getTasksNs();
1948
        // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace
1949
        $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task);
1950
        $task = str_replace(' ', '/', ucwords($task));
1951
        $ps = (strtolower(substr(PHP_OS, 0, 3)) == 'win') ? ';' : ':';
1952
        foreach (explode($ps, ini_get('include_path')) as $path) {
1953
            if (file_exists($path . "/PEAR/Task/$task.php")) {
1954
                include_once "PEAR/Task/$task.php";
1955
                $task = str_replace('/', '_', $task);
1956
                if (class_exists("PEAR_Task_$task")) {
1957
                    return "PEAR_Task_$task";
1958
                }
1959
            }
1960
        }
1961
        return false;
1962
    }
1963
 
1964
    /**
1965
     * Key-friendly array_splice
1966
     * @param tagname to splice a value in before
1967
     * @param mixed the value to splice in
1968
     * @param string the new tag name
1969
     */
1970
    function _ksplice($array, $key, $value, $newkey)
1971
    {
1972
        $offset = array_search($key, array_keys($array));
1973
        $after = array_slice($array, $offset);
1974
        $before = array_slice($array, 0, $offset);
1975
        $before[$newkey] = $value;
1976
        return array_merge($before, $after);
1977
    }
1978
 
1979
    /**
1980
     * @param array a list of possible keys, in the order they may occur
1981
     * @param mixed contents of the new package.xml tag
1982
     * @param string tag name
1983
     * @access private
1984
     */
1985
    function _insertBefore($array, $keys, $contents, $newkey)
1986
    {
1987
        foreach ($keys as $key) {
1988
            if (isset($array[$key])) {
1989
                return $array = $this->_ksplice($array, $key, $contents, $newkey);
1990
            }
1991
        }
1992
        $array[$newkey] = $contents;
1993
        return $array;
1994
    }
1995
 
1996
    /**
1997
     * @param subsection of {@link $_packageInfo}
1998
     * @param array|string tag contents
1999
     * @param array format:
2000
     * <pre>
2001
     * array(
2002
     *   tagname => array(list of tag names that follow this one),
2003
     *   childtagname => array(list of child tag names that follow this one),
2004
     * )
2005
     * </pre>
2006
     *
2007
     * This allows construction of nested tags
2008
     * @access private
2009
     */
2010
    function _mergeTag($manip, $contents, $order)
2011
    {
2012
        if (count($order)) {
2013
            foreach ($order as $tag => $curorder) {
2014
                if (!isset($manip[$tag])) {
2015
                    // ensure that the tag is set up
2016
                    $manip = $this->_insertBefore($manip, $curorder, array(), $tag);
2017
                }
2018
                if (count($order) > 1) {
2019
                    $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
2020
                    return $manip;
2021
                }
2022
            }
2023
        } else {
2024
            return $manip;
2025
        }
2026
        if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
2027
            $manip[$tag][] = $contents;
2028
        } else {
2029
            if (!count($manip[$tag])) {
2030
                $manip[$tag] = $contents;
2031
            } else {
2032
                $manip[$tag] = array($manip[$tag]);
2033
                $manip[$tag][] = $contents;
2034
            }
2035
        }
2036
        return $manip;
2037
    }
2038
}
2039
?>