Хранилища Subversion ant

Редакция

Содержимое файла | Последнее изменение | Открыть журнал | RSS

Редакция Автор № строки Строка
69 alex-w 1
<?php
2
/**
3
 * PEAR_Downloader_Package
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-2008 The PHP Group
17
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
18
 * @version    CVS: $Id: Package.php,v 1.113 2008/03/29 14:18:36 dufuz Exp $
19
 * @link       http://pear.php.net/package/PEAR
20
 * @since      File available since Release 1.4.0a1
21
 */
22
/**
23
 * Error code when parameter initialization fails because no releases
24
 * exist within preferred_state, but releases do exist
25
 */
26
define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003);
27
/**
28
 * Error code when parameter initialization fails because no releases
29
 * exist that will work with the existing PHP version
30
 */
31
define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004);
32
/**
33
 * Coordinates download parameters and manages their dependencies
34
 * prior to downloading them.
35
 *
36
 * Input can come from three sources:
37
 *
38
 * - local files (archives or package.xml)
39
 * - remote files (downloadable urls)
40
 * - abstract package names
41
 *
42
 * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires
43
 * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the
44
 * format returned of dependencies is slightly different from that used in package.xml.
45
 *
46
 * This class hides the differences between these elements, and makes automatic
47
 * dependency resolution a piece of cake.  It also manages conflicts when
48
 * two classes depend on incompatible dependencies, or differing versions of the same
49
 * package dependency.  In addition, download will not be attempted if the php version is
50
 * not supported, PEAR installer version is not supported, or non-PECL extensions are not
51
 * installed.
52
 * @category   pear
53
 * @package    PEAR
54
 * @author     Greg Beaver <cellog@php.net>
55
 * @copyright  1997-2008 The PHP Group
56
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
57
 * @version    Release: 1.7.2
58
 * @link       http://pear.php.net/package/PEAR
59
 * @since      Class available since Release 1.4.0a1
60
 */
61
class PEAR_Downloader_Package
62
{
63
    /**
64
     * @var PEAR_Downloader
65
     */
66
    var $_downloader;
67
    /**
68
     * @var PEAR_Config
69
     */
70
    var $_config;
71
    /**
72
     * @var PEAR_Registry
73
     */
74
    var $_registry;
75
    /**
76
     * Used to implement packagingroot properly
77
     * @var PEAR_Registry
78
     */
79
    var $_installRegistry;
80
    /**
81
     * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2
82
     */
83
    var $_packagefile;
84
    /**
85
     * @var array
86
     */
87
    var $_parsedname;
88
    /**
89
     * @var array
90
     */
91
    var $_downloadURL;
92
    /**
93
     * @var array
94
     */
95
    var $_downloadDeps = array();
96
    /**
97
     * @var boolean
98
     */
99
    var $_valid = false;
100
    /**
101
     * @var boolean
102
     */
103
    var $_analyzed = false;
104
    /**
105
     * if this or a parent package was invoked with Package-state, this is set to the
106
     * state variable.
107
     *
108
     * This allows temporary reassignment of preferred_state for a parent package and all of
109
     * its dependencies.
110
     * @var string|false
111
     */
112
    var $_explicitState = false;
113
    /**
114
     * If this package is invoked with Package#group, this variable will be true
115
     */
116
    var $_explicitGroup = false;
117
    /**
118
     * Package type local|url|xmlrpc
119
     * @var string
120
     */
121
    var $_type;
122
    /**
123
     * Contents of package.xml, if downloaded from a remote channel
124
     * @var string|false
125
     * @access private
126
     */
127
    var $_rawpackagefile;
128
    /**
129
     * @var boolean
130
     * @access private
131
     */
132
    var $_validated = false;
133
 
134
    /**
135
     * @param PEAR_Downloader
136
     */
137
    function PEAR_Downloader_Package(&$downloader)
138
    {
139
        $this->_downloader = &$downloader;
140
        $this->_config = &$this->_downloader->config;
141
        $this->_registry = &$this->_config->getRegistry();
142
        $options = $downloader->getOptions();
143
        if (isset($options['packagingroot'])) {
144
            $this->_config->setInstallRoot($options['packagingroot']);
145
            $this->_installRegistry = &$this->_config->getRegistry();
146
            $this->_config->setInstallRoot(false);
147
        } else {
148
            $this->_installRegistry = &$this->_registry;
149
        }
150
        $this->_valid = $this->_analyzed = false;
151
    }
152
 
153
    /**
154
     * Parse the input and determine whether this is a local file, a remote uri, or an
155
     * abstract package name.
156
     *
157
     * This is the heart of the PEAR_Downloader_Package(), and is used in
158
     * {@link PEAR_Downloader::download()}
159
     * @param string
160
     * @return bool|PEAR_Error
161
     */
162
    function initialize($param)
163
    {
164
        $origErr = $this->_fromFile($param);
165
        if (!$this->_valid) {
166
            $options = $this->_downloader->getOptions();
167
            if (isset($options['offline'])) {
168
                if (PEAR::isError($origErr)) {
169
                    if (!isset($options['soft'])) {
170
                        $this->_downloader->log(0, $origErr->getMessage());
171
                    }
172
                }
173
                return PEAR::raiseError('Cannot download non-local package "' . $param . '"');
174
            }
175
            $err = $this->_fromUrl($param);
176
            if (PEAR::isError($err) || !$this->_valid) {
177
                if ($this->_type == 'url') {
178
                    if (PEAR::isError($err)) {
179
                        if (!isset($options['soft'])) {
180
                            $this->_downloader->log(0, $err->getMessage());
181
                        }
182
                    }
183
                    return PEAR::raiseError("Invalid or missing remote package file");
184
                }
185
                $err = $this->_fromString($param);
186
                if (PEAR::isError($err) || !$this->_valid) {
187
                    if (PEAR::isError($err) &&
188
                          $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) {
189
                        return false; // instruct the downloader to silently skip
190
                    }
191
                    if (isset($this->_type) && $this->_type == 'local' &&
192
                          PEAR::isError($origErr)) {
193
                        if (is_array($origErr->getUserInfo())) {
194
                            foreach ($origErr->getUserInfo() as $err) {
195
                                if (is_array($err)) {
196
                                    $err = $err['message'];
197
                                }
198
                                if (!isset($options['soft'])) {
199
                                    $this->_downloader->log(0, $err);
200
                                }
201
                            }
202
                        }
203
                        if (!isset($options['soft'])) {
204
                            $this->_downloader->log(0, $origErr->getMessage());
205
                        }
206
                        if (is_array($param)) {
207
                            $param = $this->_registry->parsedPackageNameToString($param,
208
                                true);
209
                        }
210
                        return PEAR::raiseError(
211
                            "Cannot initialize '$param', invalid or missing package file");
212
                    }
213
                    if (PEAR::isError($err)) {
214
                        if (!isset($options['soft'])) {
215
                            $this->_downloader->log(0, $err->getMessage());
216
                        }
217
                    }
218
                    if (is_array($param)) {
219
                        $param = $this->_registry->parsedPackageNameToString($param, true);
220
                    }
221
                    return PEAR::raiseError(
222
                        "Cannot initialize '$param', invalid or missing package file");
223
                }
224
            }
225
        }
226
        return true;
227
    }
228
 
229
    /**
230
     * Retrieve any non-local packages
231
     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error
232
     */
233
    function &download()
234
    {
235
        if (isset($this->_packagefile)) {
236
            return $this->_packagefile;
237
        }
238
        if (isset($this->_downloadURL['url'])) {
239
            $this->_isvalid = false;
240
            $info = $this->getParsedPackage();
241
            foreach ($info as $i => $p) {
242
                $info[$i] = strtolower($p);
243
            }
244
            $err = $this->_fromUrl($this->_downloadURL['url'],
245
                $this->_registry->parsedPackageNameToString($this->_parsedname, true));
246
            $newinfo = $this->getParsedPackage();
247
            foreach ($newinfo as $i => $p) {
248
                $newinfo[$i] = strtolower($p);
249
            }
250
            if ($info != $newinfo) {
251
                do {
252
                    if ($info['package'] == 'pecl.php.net' && $newinfo['package'] == 'pear.php.net') {
253
                        $info['package'] = 'pear.php.net';
254
                        if ($info == $newinfo) {
255
                            // skip the channel check if a pecl package says it's a PEAR package
256
                            break;
257
                        }
258
                    }
259
                    return PEAR::raiseError('CRITICAL ERROR: We are ' .
260
                        $this->_registry->parsedPackageNameToString($info) . ', but the file ' .
261
                        'downloaded claims to be ' .
262
                        $this->_registry->parsedPackageNameToString($this->getParsedPackage()));
263
                } while (false);
264
            }
265
            if (PEAR::isError($err) || !$this->_valid) {
266
                return $err;
267
            }
268
        }
269
        $this->_type = 'local';
270
        return $this->_packagefile;
271
    }
272
 
273
    function &getPackageFile()
274
    {
275
        return $this->_packagefile;
276
    }
277
 
278
    function &getDownloader()
279
    {
280
        return $this->_downloader;
281
    }
282
 
283
    function getType()
284
    {
285
        return $this->_type;
286
    }
287
 
288
    /**
289
     * Like {@link initialize()}, but operates on a dependency
290
     */
291
    function fromDepURL($dep)
292
    {
293
        $this->_downloadURL = $dep;
294
        if (isset($dep['uri'])) {
295
            $options = $this->_downloader->getOptions();
296
            if (!extension_loaded("zlib") || isset($options['nocompress'])) {
297
                $ext = '.tar';
298
            } else {
299
                $ext = '.tgz';
300
            }
301
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
302
            $err = $this->_fromUrl($dep['uri'] . $ext);
303
            PEAR::popErrorHandling();
304
            if (PEAR::isError($err)) {
305
                if (!isset($options['soft'])) {
306
                    $this->_downloader->log(0, $err->getMessage());
307
                }
308
                return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' .
309
                    'cannot download');
310
            }
311
        } else {
312
            $this->_parsedname =
313
                array(
314
                    'package' => $dep['info']->getPackage(),
315
                    'channel' => $dep['info']->getChannel(),
316
                    'version' => $dep['version']
317
                );
318
            if (!isset($dep['nodefault'])) {
319
                $this->_parsedname['group'] = 'default'; // download the default dependency group
320
                $this->_explicitGroup = false;
321
            }
322
            $this->_rawpackagefile = $dep['raw'];
323
        }
324
    }
325
 
326
    function detectDependencies($params)
327
    {
328
        $options = $this->_downloader->getOptions();
329
        if (isset($options['downloadonly'])) {
330
            return;
331
        }
332
        if (isset($options['offline'])) {
333
            $this->_downloader->log(3, 'Skipping dependency download check, --offline specified');
334
            return;
335
        }
336
        $pname = $this->getParsedPackage();
337
        if (!$pname) {
338
            return;
339
        }
340
        $deps = $this->getDeps();
341
        if (!$deps) {
342
            return;
343
        }
344
        if (isset($deps['required'])) { // package.xml 2.0
345
            return $this->_detect2($deps, $pname, $options, $params);
346
        } else {
347
            return $this->_detect1($deps, $pname, $options, $params);
348
        }
349
    }
350
 
351
    function setValidated()
352
    {
353
        $this->_validated = true;
354
    }
355
 
356
    function alreadyValidated()
357
    {
358
        return $this->_validated;
359
    }
360
 
361
    /**
362
     * Remove packages to be downloaded that are already installed
363
     * @param array of PEAR_Downloader_Package objects
364
     * @static
365
     */
366
    function removeInstalled(&$params)
367
    {
368
        if (!isset($params[0])) {
369
            return;
370
        }
371
        $options = $params[0]->_downloader->getOptions();
372
        if (!isset($options['downloadonly'])) {
373
            foreach ($params as $i => $param) {
374
                // remove self if already installed with this version
375
                // this does not need any pecl magic - we only remove exact matches
376
                if ($param->_installRegistry->packageExists($param->getPackage(), $param->getChannel())) {
377
                    if (version_compare($param->_installRegistry->packageInfo($param->getPackage(), 'version',
378
                          $param->getChannel()), $param->getVersion(), '==')) {
379
                        if (!isset($options['force'])) {
380
                            $info = $param->getParsedPackage();
381
                            unset($info['version']);
382
                            unset($info['state']);
383
                            if (!isset($options['soft'])) {
384
                                $param->_downloader->log(1, 'Skipping package "' .
385
                                    $param->getShortName() .
386
                                    '", already installed as version ' .
387
                                    $param->_installRegistry->packageInfo($param->getPackage(),
388
                                        'version', $param->getChannel()));
389
                            }
390
                            $params[$i] = false;
391
                        }
392
                    } elseif (!isset($options['force']) && !isset($options['upgrade']) &&
393
                          !isset($options['soft'])) {
394
                        $info = $param->getParsedPackage();
395
                        $param->_downloader->log(1, 'Skipping package "' .
396
                            $param->getShortName() .
397
                            '", already installed as version ' .
398
                            $param->_installRegistry->packageInfo($param->getPackage(), 'version',
399
                                $param->getChannel()));
400
                        $params[$i] = false;
401
                    }
402
                }
403
            }
404
        }
405
        PEAR_Downloader_Package::removeDuplicates($params);
406
    }
407
 
408
    function _detect2($deps, $pname, $options, $params)
409
    {
410
        $this->_downloadDeps = array();
411
        $groupnotfound = false;
412
        foreach (array('package', 'subpackage') as $packagetype) {
413
            // get required dependency group
414
            if (isset($deps['required'][$packagetype])) {
415
                if (isset($deps['required'][$packagetype][0])) {
416
                    foreach ($deps['required'][$packagetype] as $dep) {
417
                        if (isset($dep['conflicts'])) {
418
                            // skip any package that this package conflicts with
419
                            continue;
420
                        }
421
                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
422
                        if (is_array($ret)) {
423
                            $this->_downloadDeps[] = $ret;
424
                        }
425
                    }
426
                } else {
427
                    $dep = $deps['required'][$packagetype];
428
                    if (!isset($dep['conflicts'])) {
429
                        // skip any package that this package conflicts with
430
                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
431
                        if (is_array($ret)) {
432
                            $this->_downloadDeps[] = $ret;
433
                        }
434
                    }
435
                }
436
            }
437
            // get optional dependency group, if any
438
            if (isset($deps['optional'][$packagetype])) {
439
                $skipnames = array();
440
                if (!isset($deps['optional'][$packagetype][0])) {
441
                    $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]);
442
                }
443
                foreach ($deps['optional'][$packagetype] as $dep) {
444
                    $skip = false;
445
                    if (!isset($options['alldeps'])) {
446
                        $dep['package'] = $dep['name'];
447
                        if (!isset($options['soft'])) {
448
                            $this->_downloader->log(3, 'Notice: package "' .
449
                              $this->_registry->parsedPackageNameToString($this->getParsedPackage(),
450
                                    true) . '" optional dependency "' .
451
                                $this->_registry->parsedPackageNameToString(array('package' =>
452
                                    $dep['name'], 'channel' => 'pear.php.net'), true) .
453
                                '" will not be automatically downloaded');
454
                        }
455
                        $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true);
456
                        $skip = true;
457
                        unset($dep['package']);
458
                    }
459
                    if (!($ret = $this->_detect2Dep($dep, $pname, 'optional', $params))) {
460
                        $dep['package'] = $dep['name'];
461
                        $skip = count($skipnames) ?
462
                            $skipnames[count($skipnames) - 1] : '';
463
                        if ($skip ==
464
                              $this->_registry->parsedPackageNameToString($dep, true)) {
465
                            array_pop($skipnames);
466
                        }
467
                    }
468
                    if (!$skip && is_array($ret)) {
469
                        $this->_downloadDeps[] = $ret;
470
                    }
471
                }
472
                if (count($skipnames)) {
473
                    if (!isset($options['soft'])) {
474
                        $this->_downloader->log(1, 'Did not download optional dependencies: ' .
475
                            implode(', ', $skipnames) .
476
                            ', use --alldeps to download automatically');
477
                    }
478
                }
479
            }
480
            // get requested dependency group, if any
481
            $groupname = $this->getGroup();
482
            $explicit = $this->_explicitGroup;
483
            if (!$groupname) {
484
                if ($this->canDefault()) {
485
                    $groupname = 'default'; // try the default dependency group
486
                } else {
487
                    continue;
488
                }
489
            }
490
            if ($groupnotfound) {
491
                continue;
492
            }
493
            if (isset($deps['group'])) {
494
                if (isset($deps['group']['attribs'])) {
495
                    if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) {
496
                        $group = $deps['group'];
497
                    } elseif ($explicit) {
498
                        if (!isset($options['soft'])) {
499
                            $this->_downloader->log(0, 'Warning: package "' .
500
                                $this->_registry->parsedPackageNameToString($pname, true) .
501
                                '" has no dependency ' . 'group named "' . $groupname . '"');
502
                        }
503
                        $groupnotfound = true;
504
                        continue;
505
                    }
506
                } else {
507
                    $found = false;
508
                    foreach ($deps['group'] as $group) {
509
                        if (strtolower($group['attribs']['name']) == strtolower($groupname)) {
510
                            $found = true;
511
                            break;
512
                        }
513
                    }
514
                    if (!$found) {
515
                        if ($explicit) {
516
                            if (!isset($options['soft'])) {
517
                                $this->_downloader->log(0, 'Warning: package "' .
518
                                    $this->_registry->parsedPackageNameToString($pname, true) .
519
                                    '" has no dependency ' . 'group named "' . $groupname . '"');
520
                            }
521
                        }
522
                        $groupnotfound = true;
523
                        continue;
524
                    }
525
                }
526
            }
527
            if (isset($group)) {
528
                if (isset($group[$packagetype])) {
529
                    if (isset($group[$packagetype][0])) {
530
                        foreach ($group[$packagetype] as $dep) {
531
                            $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' .
532
                                $group['attribs']['name'] . '"', $params);
533
                            if (is_array($ret)) {
534
                                $this->_downloadDeps[] = $ret;
535
                            }
536
                        }
537
                    } else {
538
                        $ret = $this->_detect2Dep($group[$packagetype], $pname,
539
                            'dependency group "' .
540
                            $group['attribs']['name'] . '"', $params);
541
                        if (is_array($ret)) {
542
                            $this->_downloadDeps[] = $ret;
543
                        }
544
                    }
545
                }
546
            }
547
        }
548
    }
549
 
550
    function _detect2Dep($dep, $pname, $group, $params)
551
    {
552
        if (isset($dep['conflicts'])) {
553
            return true;
554
        }
555
        $options = $this->_downloader->getOptions();
556
        if (isset($dep['uri'])) {
557
            return array('uri' => $dep['uri'], 'dep' => $dep);;
558
        }
559
        $testdep = $dep;
560
        $testdep['package'] = $dep['name'];
561
        if (PEAR_Downloader_Package::willDownload($testdep, $params)) {
562
            $dep['package'] = $dep['name'];
563
            if (!isset($options['soft'])) {
564
                $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group .
565
                    ' dependency "' .
566
                    $this->_registry->parsedPackageNameToString($dep, true) .
567
                    '", will be installed');
568
            }
569
            return false;
570
        }
571
        $options = $this->_downloader->getOptions();
572
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
573
        if ($this->_explicitState) {
574
            $pname['state'] = $this->_explicitState;
575
        }
576
        $url =
577
            $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
578
        if (PEAR::isError($url)) {
579
            PEAR::popErrorHandling();
580
            return $url;
581
        }
582
        $dep['package'] = $dep['name'];
583
        $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' &&
584
            !isset($options['alldeps']), true);
585
        PEAR::popErrorHandling();
586
        if (PEAR::isError($ret)) {
587
            if (!isset($options['soft'])) {
588
                $this->_downloader->log(0, $ret->getMessage());
589
            }
590
            return false;
591
        } else {
592
            // check to see if a dep is already installed and is the same or newer
593
            if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) {
594
                $oper = 'has';
595
            } else {
596
                $oper = 'gt';
597
            }
598
            // do not try to move this before getDepPackageDownloadURL
599
            // we can't determine whether upgrade is necessary until we know what
600
            // version would be downloaded
601
            if (!isset($options['force']) && $this->isInstalled($ret, $oper)) {
602
                $version = $this->_installRegistry->packageInfo($dep['name'], 'version',
603
                    $dep['channel']);
604
                $dep['package'] = $dep['name'];
605
                if (!isset($options['soft'])) {
606
                    $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
607
                        ' dependency "' .
608
                    $this->_registry->parsedPackageNameToString($dep, true) .
609
                        '" version ' . $url['version'] . ', already installed as version ' .
610
                        $version);
611
                }
612
                return false;
613
            }
614
        }
615
        if (isset($dep['nodefault'])) {
616
            $ret['nodefault'] = true;
617
        }
618
        return $ret;
619
    }
620
 
621
    function _detect1($deps, $pname, $options, $params)
622
    {
623
        $this->_downloadDeps = array();
624
        $skipnames = array();
625
        foreach ($deps as $dep) {
626
            $nodownload = false;
627
            if ($dep['type'] == 'pkg') {
628
                $dep['channel'] = 'pear.php.net';
629
                $dep['package'] = $dep['name'];
630
                switch ($dep['rel']) {
631
                    case 'not' :
632
                        continue 2;
633
                    case 'ge' :
634
                    case 'eq' :
635
                    case 'gt' :
636
                    case 'has' :
637
                        $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
638
                            'required' :
639
                            'optional';
640
                        if (PEAR_Downloader_Package::willDownload($dep, $params)) {
641
                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
642
                                . ' dependency "' .
643
                                $this->_registry->parsedPackageNameToString($dep, true) .
644
                                '", will be installed');
645
                            continue 2;
646
                        }
647
                        $fakedp = new PEAR_PackageFile_v1;
648
                        $fakedp->setPackage($dep['name']);
649
                        // skip internet check if we are not upgrading (bug #5810)
650
                        if (!isset($options['upgrade']) && $this->isInstalled(
651
                              $fakedp, $dep['rel'])) {
652
                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
653
                                . ' dependency "' .
654
                                $this->_registry->parsedPackageNameToString($dep, true) .
655
                                '", is already installed');
656
                            continue 2;
657
                        }
658
                }
659
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
660
                if ($this->_explicitState) {
661
                    $pname['state'] = $this->_explicitState;
662
                }
663
                $url =
664
                    $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
665
                $chan = 'pear.php.net';
666
                if (PEAR::isError($url)) {
667
                    // check to see if this is a pecl package that has jumped
668
                    // from pear.php.net to pecl.php.net channel
669
                    if (!class_exists('PEAR_Dependency2')) {
670
                        require_once 'PEAR/Dependency2.php';
671
                    }
672
                    $newdep = PEAR_Dependency2::normalizeDep($dep);
673
                    $newdep = $newdep[0];
674
                    $newdep['channel'] = 'pecl.php.net';
675
                    $chan = 'pecl.php.net';
676
                    $url =
677
                        $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname);
678
                    $obj = &$this->_installRegistry->getPackage($dep['name']);
679
                    if (PEAR::isError($url)) {
680
                        PEAR::popErrorHandling();
681
                        if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) {
682
                            $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
683
                                'required' :
684
                                'optional';
685
                            $dep['package'] = $dep['name'];
686
                            if (!isset($options['soft'])) {
687
                                $this->_downloader->log(3, $this->getShortName() .
688
                                    ': Skipping ' . $group . ' dependency "' .
689
                                    $this->_registry->parsedPackageNameToString($dep, true) .
690
                                    '", already installed as version ' . $obj->getVersion());
691
                            }
692
                            $skip = count($skipnames) ?
693
                                $skipnames[count($skipnames) - 1] : '';
694
                            if ($skip ==
695
                                  $this->_registry->parsedPackageNameToString($dep, true)) {
696
                                array_pop($skipnames);
697
                            }
698
                            continue;
699
                        } else {
700
                            if (isset($dep['optional']) && $dep['optional'] == 'yes') {
701
                                $this->_downloader->log(2, $this->getShortName() .
702
                                    ': Skipping optional dependency "' .
703
                                    $this->_registry->parsedPackageNameToString($dep, true) .
704
                                    '", no releases exist');
705
                                continue;
706
                            } else {
707
                                return $url;
708
                            }
709
                        }
710
                    }
711
                }
712
                PEAR::popErrorHandling();
713
                if (!isset($options['alldeps'])) {
714
                    if (isset($dep['optional']) && $dep['optional'] == 'yes') {
715
                        if (!isset($options['soft'])) {
716
                            $this->_downloader->log(3, 'Notice: package "' .
717
                                $this->getShortName() .
718
                                '" optional dependency "' .
719
                                $this->_registry->parsedPackageNameToString(
720
                                    array('channel' => $chan, 'package' =>
721
                                    $dep['name']), true) .
722
                                '" will not be automatically downloaded');
723
                        }
724
                        $skipnames[] = $this->_registry->parsedPackageNameToString(
725
                                array('channel' => $chan, 'package' =>
726
                                $dep['name']), true);
727
                        $nodownload = true;
728
                    }
729
                }
730
                if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) {
731
                    if (!isset($dep['optional']) || $dep['optional'] == 'no') {
732
                        if (!isset($options['soft'])) {
733
                            $this->_downloader->log(3, 'Notice: package "' .
734
                                $this->getShortName() .
735
                                '" required dependency "' .
736
                                $this->_registry->parsedPackageNameToString(
737
                                    array('channel' => $chan, 'package' =>
738
                                    $dep['name']), true) .
739
                                '" will not be automatically downloaded');
740
                        }
741
                        $skipnames[] = $this->_registry->parsedPackageNameToString(
742
                                array('channel' => $chan, 'package' =>
743
                                $dep['name']), true);
744
                        $nodownload = true;
745
                    }
746
                }
747
                // check to see if a dep is already installed
748
                // do not try to move this before getDepPackageDownloadURL
749
                // we can't determine whether upgrade is necessary until we know what
750
                // version would be downloaded
751
                if (!isset($options['force']) && $this->isInstalled(
752
                        $url, $dep['rel'])) {
753
                    $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
754
                        'required' :
755
                        'optional';
756
                    $dep['package'] = $dep['name'];
757
                    if (isset($newdep)) {
758
                        $version = $this->_installRegistry->packageInfo($newdep['name'], 'version',
759
                            $newdep['channel']);
760
                    } else {
761
                        $version = $this->_installRegistry->packageInfo($dep['name'], 'version');
762
                    }
763
                    $dep['version'] = $url['version'];
764
                    if (!isset($options['soft'])) {
765
                        $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
766
                            ' dependency "' .
767
                            $this->_registry->parsedPackageNameToString($dep, true) .
768
                            '", already installed as version ' . $version);
769
                    }
770
                    $skip = count($skipnames) ?
771
                        $skipnames[count($skipnames) - 1] : '';
772
                    if ($skip ==
773
                          $this->_registry->parsedPackageNameToString($dep, true)) {
774
                        array_pop($skipnames);
775
                    }
776
                    continue;
777
                }
778
                if ($nodownload) {
779
                    continue;
780
                }
781
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
782
                if (isset($newdep)) {
783
                    $dep = $newdep;
784
                }
785
                $dep['package'] = $dep['name'];
786
                $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params,
787
                    isset($dep['optional']) && $dep['optional'] == 'yes' &&
788
                    !isset($options['alldeps']), true);
789
                PEAR::popErrorHandling();
790
                if (PEAR::isError($ret)) {
791
                    if (!isset($options['soft'])) {
792
                        $this->_downloader->log(0, $ret->getMessage());
793
                    }
794
                    continue;
795
                }
796
                $this->_downloadDeps[] = $ret;
797
            }
798
        }
799
        if (count($skipnames)) {
800
            if (!isset($options['soft'])) {
801
                $this->_downloader->log(1, 'Did not download dependencies: ' .
802
                    implode(', ', $skipnames) .
803
                    ', use --alldeps or --onlyreqdeps to download automatically');
804
            }
805
        }
806
    }
807
 
808
    function setDownloadURL($pkg)
809
    {
810
        $this->_downloadURL = $pkg;
811
    }
812
 
813
    /**
814
     * Set the package.xml object for this downloaded package
815
     *
816
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg
817
     */
818
    function setPackageFile(&$pkg)
819
    {
820
        $this->_packagefile = &$pkg;
821
    }
822
 
823
    function getShortName()
824
    {
825
        return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(),
826
            'package' => $this->getPackage()), true);
827
    }
828
 
829
    function getParsedPackage()
830
    {
831
        if (isset($this->_packagefile) || isset($this->_parsedname)) {
832
            return array('channel' => $this->getChannel(),
833
                'package' => $this->getPackage(),
834
                'version' => $this->getVersion());
835
        }
836
        return false;
837
    }
838
 
839
    function getDownloadURL()
840
    {
841
        return $this->_downloadURL;
842
    }
843
 
844
    function canDefault()
845
    {
846
        if (isset($this->_downloadURL)) {
847
            if (isset($this->_downloadURL['nodefault'])) {
848
                return false;
849
            }
850
        }
851
        return true;
852
    }
853
 
854
    function getPackage()
855
    {
856
        if (isset($this->_packagefile)) {
857
            return $this->_packagefile->getPackage();
858
        } elseif (isset($this->_downloadURL['info'])) {
859
            return $this->_downloadURL['info']->getPackage();
860
        } else {
861
            return false;
862
        }
863
    }
864
 
865
    /**
866
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
867
     */
868
    function isSubpackage(&$pf)
869
    {
870
        if (isset($this->_packagefile)) {
871
            return $this->_packagefile->isSubpackage($pf);
872
        } elseif (isset($this->_downloadURL['info'])) {
873
            return $this->_downloadURL['info']->isSubpackage($pf);
874
        } else {
875
            return false;
876
        }
877
    }
878
 
879
    function getPackageType()
880
    {
881
        if (isset($this->_packagefile)) {
882
            return $this->_packagefile->getPackageType();
883
        } elseif (isset($this->_downloadURL['info'])) {
884
            return $this->_downloadURL['info']->getPackageType();
885
        } else {
886
            return false;
887
        }
888
    }
889
 
890
    function isBundle()
891
    {
892
        if (isset($this->_packagefile)) {
893
            return $this->_packagefile->getPackageType() == 'bundle';
894
        } else {
895
            return false;
896
        }
897
    }
898
 
899
    function getPackageXmlVersion()
900
    {
901
        if (isset($this->_packagefile)) {
902
            return $this->_packagefile->getPackagexmlVersion();
903
        } elseif (isset($this->_downloadURL['info'])) {
904
            return $this->_downloadURL['info']->getPackagexmlVersion();
905
        } else {
906
            return '1.0';
907
        }
908
    }
909
 
910
    function getChannel()
911
    {
912
        if (isset($this->_packagefile)) {
913
            return $this->_packagefile->getChannel();
914
        } elseif (isset($this->_downloadURL['info'])) {
915
            return $this->_downloadURL['info']->getChannel();
916
        } else {
917
            return false;
918
        }
919
    }
920
 
921
    function getURI()
922
    {
923
        if (isset($this->_packagefile)) {
924
            return $this->_packagefile->getURI();
925
        } elseif (isset($this->_downloadURL['info'])) {
926
            return $this->_downloadURL['info']->getURI();
927
        } else {
928
            return false;
929
        }
930
    }
931
 
932
    function getVersion()
933
    {
934
        if (isset($this->_packagefile)) {
935
            return $this->_packagefile->getVersion();
936
        } elseif (isset($this->_downloadURL['version'])) {
937
            return $this->_downloadURL['version'];
938
        } else {
939
            return false;
940
        }
941
    }
942
 
943
    function isCompatible($pf)
944
    {
945
        if (isset($this->_packagefile)) {
946
            return $this->_packagefile->isCompatible($pf);
947
        } elseif (isset($this->_downloadURL['info'])) {
948
            return $this->_downloadURL['info']->isCompatible($pf);
949
        } else {
950
            return true;
951
        }
952
    }
953
 
954
    function setGroup($group)
955
    {
956
        $this->_parsedname['group'] = $group;
957
    }
958
 
959
    function getGroup()
960
    {
961
        if (isset($this->_parsedname['group'])) {
962
            return $this->_parsedname['group'];
963
        } else {
964
            return '';
965
        }
966
    }
967
 
968
    function isExtension($name)
969
    {
970
        if (isset($this->_packagefile)) {
971
            return $this->_packagefile->isExtension($name);
972
        } elseif (isset($this->_downloadURL['info'])) {
973
                        if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') {
974
                                return $this->_downloadURL['info']->getProvidesExtension() == $name;
975
                        } else {
976
                                return false;
977
                        }
978
        } else {
979
            return false;
980
        }
981
    }
982
 
983
    function getDeps()
984
    {
985
        if (isset($this->_packagefile)) {
986
            $ver = $this->_packagefile->getPackagexmlVersion();
987
            if (version_compare($ver, '2.0', '>=')) {
988
                return $this->_packagefile->getDeps(true);
989
            } else {
990
                return $this->_packagefile->getDeps();
991
            }
992
        } elseif (isset($this->_downloadURL['info'])) {
993
            $ver = $this->_downloadURL['info']->getPackagexmlVersion();
994
            if (version_compare($ver, '2.0', '>=')) {
995
                return $this->_downloadURL['info']->getDeps(true);
996
            } else {
997
                return $this->_downloadURL['info']->getDeps();
998
            }
999
        } else {
1000
            return array();
1001
        }
1002
    }
1003
 
1004
    /**
1005
     * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency
1006
     *                     returned from getDepDownloadURL()
1007
     */
1008
    function isEqual($param)
1009
    {
1010
        if (is_object($param)) {
1011
            $channel = $param->getChannel();
1012
            $package = $param->getPackage();
1013
            if ($param->getURI()) {
1014
                $param = array(
1015
                    'channel' => $param->getChannel(),
1016
                    'package' => $param->getPackage(),
1017
                    'version' => $param->getVersion(),
1018
                    'uri' => $param->getURI(),
1019
                );
1020
            } else {
1021
                $param = array(
1022
                    'channel' => $param->getChannel(),
1023
                    'package' => $param->getPackage(),
1024
                    'version' => $param->getVersion(),
1025
                );
1026
            }
1027
        } else {
1028
            if (isset($param['uri'])) {
1029
                if ($this->getChannel() != '__uri') {
1030
                    return false;
1031
                }
1032
                return $param['uri'] == $this->getURI();
1033
            }
1034
            $package = isset($param['package']) ? $param['package'] :
1035
                $param['info']->getPackage();
1036
            $channel = isset($param['channel']) ? $param['channel'] :
1037
                $param['info']->getChannel();
1038
            if (isset($param['rel'])) {
1039
                if (!class_exists('PEAR_Dependency2')) {
1040
                    require_once 'PEAR/Dependency2.php';
1041
                }
1042
                $newdep = PEAR_Dependency2::normalizeDep($param);
1043
                $newdep = $newdep[0];
1044
            } elseif (isset($param['min'])) {
1045
                $newdep = $param;
1046
            }
1047
        }
1048
        if (isset($newdep)) {
1049
            if (!isset($newdep['min'])) {
1050
                $newdep['min'] = '0';
1051
            }
1052
            if (!isset($newdep['max'])) {
1053
                $newdep['max'] = '100000000000000000000';
1054
            }
1055
            // use magic to support pecl packages suddenly jumping to the pecl channel
1056
            // we need to support both dependency possibilities
1057
            if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') {
1058
                if ($package == $this->getPackage()) {
1059
                    $channel = 'pecl.php.net';
1060
                }
1061
            }
1062
            if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
1063
                if ($package == $this->getPackage()) {
1064
                    $channel = 'pear.php.net';
1065
                }
1066
            }
1067
            return (strtolower($package) == strtolower($this->getPackage()) &&
1068
                $channel == $this->getChannel() &&
1069
                version_compare($newdep['min'], $this->getVersion(), '<=') &&
1070
                version_compare($newdep['max'], $this->getVersion(), '>='));
1071
        }
1072
        // use magic to support pecl packages suddenly jumping to the pecl channel
1073
        if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
1074
            if (strtolower($package) == strtolower($this->getPackage())) {
1075
                $channel = 'pear.php.net';
1076
            }
1077
        }
1078
        if (isset($param['version'])) {
1079
            return (strtolower($package) == strtolower($this->getPackage()) &&
1080
                $channel == $this->getChannel() &&
1081
                $param['version'] == $this->getVersion());
1082
        } else {
1083
            return strtolower($package) == strtolower($this->getPackage()) &&
1084
                $channel == $this->getChannel();
1085
        }
1086
    }
1087
 
1088
    function isInstalled($dep, $oper = '==')
1089
    {
1090
        if (!$dep) {
1091
            return false;
1092
        }
1093
        if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') {
1094
            return false;
1095
        }
1096
        if (is_object($dep)) {
1097
            $package = $dep->getPackage();
1098
            $channel = $dep->getChannel();
1099
            if ($dep->getURI()) {
1100
                $dep = array(
1101
                    'uri' => $dep->getURI(),
1102
                    'version' => $dep->getVersion(),
1103
                );
1104
            } else {
1105
                $dep = array(
1106
                    'version' => $dep->getVersion(),
1107
                );
1108
            }
1109
        } else {
1110
            if (isset($dep['uri'])) {
1111
                $channel = '__uri';
1112
                $package = $dep['dep']['name'];
1113
            } else {
1114
                $channel = $dep['info']->getChannel();
1115
                $package = $dep['info']->getPackage();
1116
            }
1117
        }
1118
        $options = $this->_downloader->getOptions();
1119
        $test = $this->_installRegistry->packageExists($package, $channel);
1120
        if (!$test && $channel == 'pecl.php.net') {
1121
            // do magic to allow upgrading from old pecl packages to new ones
1122
            $test = $this->_installRegistry->packageExists($package, 'pear.php.net');
1123
            $channel = 'pear.php.net';
1124
        }
1125
        if ($test) {
1126
            if (isset($dep['uri'])) {
1127
                if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) {
1128
                    return true;
1129
                }
1130
            }
1131
            if (isset($options['upgrade'])) {
1132
                if ($oper == 'has') {
1133
                    if (version_compare($this->_installRegistry->packageInfo(
1134
                          $package, 'version', $channel),
1135
                          $dep['version'], '>=')) {
1136
                        return true;
1137
                    } else {
1138
                        return false;
1139
                    }
1140
                } else {
1141
                    if (version_compare($this->_installRegistry->packageInfo(
1142
                          $package, 'version', $channel),
1143
                          $dep['version'], '>=')) {
1144
                        return true;
1145
                    }
1146
                    return false;
1147
                }
1148
            }
1149
            return true;
1150
        }
1151
        return false;
1152
    }
1153
 
1154
    /**
1155
     * Detect duplicate package names with differing versions
1156
     *
1157
     * If a user requests to install Date 1.4.6 and Date 1.4.7,
1158
     * for instance, this is a logic error.  This method
1159
     * detects this situation.
1160
     *
1161
     * @param array $params array of PEAR_Downloader_Package objects
1162
     * @param array $errorparams empty array
1163
     * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts
1164
     */
1165
    function detectStupidDuplicates($params, &$errorparams)
1166
    {
1167
        $existing = array();
1168
        foreach ($params as $i => $param) {
1169
            $package = $param->getPackage();
1170
            $channel = $param->getChannel();
1171
            $group   = $param->getGroup();
1172
            if (!isset($existing[$channel . '/' . $package])) {
1173
                $existing[$channel . '/' . $package] = array();
1174
            }
1175
            if (!isset($existing[$channel . '/' . $package][$group])) {
1176
                $existing[$channel . '/' . $package][$group] = array();
1177
            }
1178
            $existing[$channel . '/' . $package][$group][] = $i;
1179
        }
1180
 
1181
        $indices = array();
1182
        foreach ($existing as $package => $groups) {
1183
            foreach ($groups as $group => $dupes) {
1184
                if (count($dupes) > 1) {
1185
                    $indices = $indices + $dupes;
1186
                }
1187
            }
1188
        }
1189
 
1190
        $indices = array_unique($indices);
1191
        foreach ($indices as $index) {
1192
            $errorparams[] = $params[$index];
1193
        }
1194
        return count($errorparams);
1195
    }
1196
 
1197
    /**
1198
     * @param array
1199
     * @param bool ignore install groups - for final removal of dupe packages
1200
     * @static
1201
     */
1202
    function removeDuplicates(&$params, $ignoreGroups = false)
1203
    {
1204
        $pnames = array();
1205
        foreach ($params as $i => $param) {
1206
            if (!$param) {
1207
                continue;
1208
            }
1209
            if ($param->getPackage()) {
1210
                if ($ignoreGroups) {
1211
                    $group = '';
1212
                } else {
1213
                    $group = $param->getGroup();
1214
                }
1215
                $pnames[$i] = $param->getChannel() . '/' .
1216
                    $param->getPackage() . '-' . $param->getVersion() . '#' . $group;
1217
            }
1218
        }
1219
        $pnames = array_unique($pnames);
1220
        $unset = array_diff(array_keys($params), array_keys($pnames));
1221
        $testp = array_flip($pnames);
1222
        foreach ($params as $i => $param) {
1223
            if (!$param) {
1224
                $unset[] = $i;
1225
                continue;
1226
            }
1227
            if (!is_a($param, 'PEAR_Downloader_Package')) {
1228
                $unset[] = $i;
1229
                continue;
1230
            }
1231
            if ($ignoreGroups) {
1232
                $group = '';
1233
            } else {
1234
                $group = $param->getGroup();
1235
            }
1236
            if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' .
1237
                  $param->getVersion() . '#' . $group])) {
1238
                $unset[] = $i;
1239
            }
1240
        }
1241
        foreach ($unset as $i) {
1242
            unset($params[$i]);
1243
        }
1244
        $ret = array();
1245
        foreach ($params as $i => $param) {
1246
            $ret[] = &$params[$i];
1247
        }
1248
        $params = array();
1249
        foreach ($ret as $i => $param) {
1250
            $params[] = &$ret[$i];
1251
        }
1252
    }
1253
 
1254
    function explicitState()
1255
    {
1256
        return $this->_explicitState;
1257
    }
1258
 
1259
    function setExplicitState($s)
1260
    {
1261
        $this->_explicitState = $s;
1262
    }
1263
 
1264
    /**
1265
     * @static
1266
     */
1267
    function mergeDependencies(&$params)
1268
    {
1269
        $newparams = array();
1270
        $bundles = array();
1271
        foreach ($params as $i => $param) {
1272
            if (!$param->isBundle()) {
1273
                continue;
1274
            }
1275
            $bundles[] = $i;
1276
            $pf = &$param->getPackageFile();
1277
            $newdeps = array();
1278
            $contents = $pf->getBundledPackages();
1279
            if (!is_array($contents)) {
1280
                $contents = array($contents);
1281
            }
1282
            foreach ($contents as $file) {
1283
                $filecontents = $pf->getFileContents($file);
1284
                $dl = &$param->getDownloader();
1285
                $options = $dl->getOptions();
1286
                if (PEAR::isError($dir = $dl->getDownloadDir())) {
1287
                    return $dir;
1288
                }
1289
                $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb');
1290
                if (!$fp) {
1291
                    continue;
1292
                }
1293
                fwrite($fp, $filecontents, strlen($filecontents));
1294
                fclose($fp);
1295
                if ($s = $params[$i]->explicitState()) {
1296
                    $obj->setExplicitState($s);
1297
                }
1298
                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
1299
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1300
                if (PEAR::isError($dir = $dl->getDownloadDir())) {
1301
                    PEAR::popErrorHandling();
1302
                    return $dir;
1303
                }
1304
                $e = $obj->_fromFile($a = $dir . DIRECTORY_SEPARATOR . $file);
1305
                PEAR::popErrorHandling();
1306
                if (PEAR::isError($e)) {
1307
                    if (!isset($options['soft'])) {
1308
                        $dl->log(0, $e->getMessage());
1309
                    }
1310
                    continue;
1311
                }
1312
                $j = &$obj;
1313
                if (!PEAR_Downloader_Package::willDownload($j,
1314
                      array_merge($params, $newparams)) && !$param->isInstalled($j)) {
1315
                    $newparams[] = &$j;
1316
                }
1317
            }
1318
        }
1319
        foreach ($bundles as $i) {
1320
            unset($params[$i]); // remove bundles - only their contents matter for installation
1321
        }
1322
        PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices
1323
        if (count($newparams)) { // add in bundled packages for install
1324
            foreach ($newparams as $i => $unused) {
1325
                $params[] = &$newparams[$i];
1326
            }
1327
            $newparams = array();
1328
        }
1329
        foreach ($params as $i => $param) {
1330
            $newdeps = array();
1331
            foreach ($param->_downloadDeps as $dep) {
1332
                if (!PEAR_Downloader_Package::willDownload($dep,
1333
                      array_merge($params, $newparams)) && !$param->isInstalled($dep)) {
1334
                    $newdeps[] = $dep;
1335
                } else {
1336
                    // detect versioning conflicts here
1337
                }
1338
            }
1339
            // convert the dependencies into PEAR_Downloader_Package objects for the next time
1340
            // around
1341
            $params[$i]->_downloadDeps = array();
1342
            foreach ($newdeps as $dep) {
1343
                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
1344
                if ($s = $params[$i]->explicitState()) {
1345
                    $obj->setExplicitState($s);
1346
                }
1347
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1348
                $e = $obj->fromDepURL($dep);
1349
                PEAR::popErrorHandling();
1350
                if (PEAR::isError($e)) {
1351
                    if (!isset($options['soft'])) {
1352
                        $obj->_downloader->log(0, $e->getMessage());
1353
                    }
1354
                    continue;
1355
                }
1356
                $e = $obj->detectDependencies($params);
1357
                if (PEAR::isError($e)) {
1358
                    if (!isset($options['soft'])) {
1359
                        $obj->_downloader->log(0, $e->getMessage());
1360
                    }
1361
                }
1362
                $j = &$obj;
1363
                $newparams[] = &$j;
1364
            }
1365
        }
1366
        if (count($newparams)) {
1367
            foreach ($newparams as $i => $unused) {
1368
                $params[] = &$newparams[$i];
1369
            }
1370
            return true;
1371
        } else {
1372
            return false;
1373
        }
1374
    }
1375
 
1376
 
1377
    /**
1378
     * @static
1379
     */
1380
    function willDownload($param, $params)
1381
    {
1382
        if (!is_array($params)) {
1383
            return false;
1384
        }
1385
        foreach ($params as $obj) {
1386
            if ($obj->isEqual($param)) {
1387
                return true;
1388
            }
1389
        }
1390
        return false;
1391
    }
1392
 
1393
    /**
1394
     * For simpler unit-testing
1395
     * @param PEAR_Config
1396
     * @param int
1397
     * @param string
1398
     */
1399
    function &getPackagefileObject(&$c, $d, $t = false)
1400
    {
1401
        $a = &new PEAR_PackageFile($c, $d, $t);
1402
        return $a;
1403
    }
1404
 
1405
 
1406
    /**
1407
     * This will retrieve from a local file if possible, and parse out
1408
     * a group name as well.  The original parameter will be modified to reflect this.
1409
     * @param string|array can be a parsed package name as well
1410
     * @access private
1411
     */
1412
    function _fromFile(&$param)
1413
    {
1414
        $saveparam = $param;
1415
        if (is_string($param)) {
1416
            if (!@file_exists($param)) {
1417
                $test = explode('#', $param);
1418
                $group = array_pop($test);
1419
                if (@file_exists(implode('#', $test))) {
1420
                    $this->setGroup($group);
1421
                    $param = implode('#', $test);
1422
                    $this->_explicitGroup = true;
1423
                }
1424
            }
1425
            if (@is_file($param)) {
1426
                $this->_type = 'local';
1427
                $options = $this->_downloader->getOptions();
1428
                if (isset($options['downloadonly'])) {
1429
                    $pkg = &$this->getPackagefileObject($this->_config,
1430
                        $this->_downloader->_debug);
1431
                } else {
1432
                    if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
1433
                        return $dir;
1434
                    }
1435
                    $pkg = &$this->getPackagefileObject($this->_config,
1436
                        $this->_downloader->_debug, $dir);
1437
                }
1438
                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1439
                $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING);
1440
                PEAR::popErrorHandling();
1441
                if (PEAR::isError($pf)) {
1442
                    $this->_valid = false;
1443
                    $param = $saveparam;
1444
                    return $pf;
1445
                }
1446
                $this->_packagefile = &$pf;
1447
                if (!$this->getGroup()) {
1448
                    $this->setGroup('default'); // install the default dependency group
1449
                }
1450
                return $this->_valid = true;
1451
            }
1452
        }
1453
        $param = $saveparam;
1454
        return $this->_valid = false;
1455
    }
1456
 
1457
    function _fromUrl($param, $saveparam = '')
1458
    {
1459
        if (!is_array($param) &&
1460
              (preg_match('#^(http|ftp)://#', $param))) {
1461
            $options = $this->_downloader->getOptions();
1462
            $this->_type = 'url';
1463
            $callback = $this->_downloader->ui ?
1464
                array(&$this->_downloader, '_downloadCallback') : null;
1465
            $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN);
1466
            if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
1467
                $this->_downloader->popErrorHandling();
1468
                return $dir;
1469
            }
1470
            $this->_downloader->log(3, 'Downloading "' . $param . '"');
1471
            $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui,
1472
                $dir, $callback, null, false, $this->getChannel());
1473
            $this->_downloader->popErrorHandling();
1474
            if (PEAR::isError($file)) {
1475
                if (!empty($saveparam)) {
1476
                    $saveparam = ", cannot download \"$saveparam\"";
1477
                }
1478
                $err = PEAR::raiseError('Could not download from "' . $param .
1479
                    '"' . $saveparam . ' (' . $file->getMessage() . ')');
1480
                    return $err;
1481
            }
1482
            if ($this->_rawpackagefile) {
1483
                require_once 'Archive/Tar.php';
1484
                $tar = &new Archive_Tar($file);
1485
                $packagexml = $tar->extractInString('package2.xml');
1486
                if (!$packagexml) {
1487
                    $packagexml = $tar->extractInString('package.xml');
1488
                }
1489
                if (str_replace(array("\n", "\r"), array('',''), $packagexml) !=
1490
                      str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) {
1491
                    if ($this->getChannel() == 'pear.php.net') {
1492
                        // be more lax for the existing PEAR packages that have not-ok
1493
                        // characters in their package.xml
1494
                        $this->_downloader->log(0, 'CRITICAL WARNING: The "' .
1495
                            $this->getPackage() . '" package has invalid characters in its ' .
1496
                            'package.xml.  The next version of PEAR may not be able to install ' .
1497
                            'this package for security reasons.  Please open a bug report at ' .
1498
                            'http://pear.php.net/package/' . $this->getPackage() . '/bugs');
1499
                    } else {
1500
                        return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' .
1501
                            'not match value returned from xml-rpc');
1502
                    }
1503
                }
1504
            }
1505
            // whew, download worked!
1506
            if (isset($options['downloadonly'])) {
1507
                $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug);
1508
            } else {
1509
                if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
1510
                    return $dir;
1511
                }
1512
                $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug,
1513
                    $dir);
1514
            }
1515
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1516
            $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING);
1517
            PEAR::popErrorHandling();
1518
            if (PEAR::isError($pf)) {
1519
                if (is_array($pf->getUserInfo())) {
1520
                    foreach ($pf->getUserInfo() as $err) {
1521
                        if (is_array($err)) {
1522
                            $err = $err['message'];
1523
                        }
1524
                        if (!isset($options['soft'])) {
1525
                            $this->_downloader->log(0, "Validation Error: $err");
1526
                        }
1527
                    }
1528
                }
1529
                if (!isset($options['soft'])) {
1530
                    $this->_downloader->log(0, $pf->getMessage());
1531
                }
1532
                $err = PEAR::raiseError('Download of "' . ($saveparam ? $saveparam :
1533
                    $param) . '" succeeded, but it is not a valid package archive');
1534
                $this->_valid = false;
1535
                return $err;
1536
            }
1537
            $this->_packagefile = &$pf;
1538
            $this->setGroup('default'); // install the default dependency group
1539
            return $this->_valid = true;
1540
        }
1541
        return $this->_valid = false;
1542
    }
1543
 
1544
    /**
1545
     *
1546
     * @param string|array pass in an array of format
1547
     *                     array(
1548
     *                      'package' => 'pname',
1549
     *                     ['channel' => 'channame',]
1550
     *                     ['version' => 'version',]
1551
     *                     ['state' => 'state',])
1552
     *                     or a string of format [channame/]pname[-version|-state]
1553
     */
1554
    function _fromString($param)
1555
    {
1556
        $options = $this->_downloader->getOptions();
1557
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1558
        $pname = $this->_registry->parsePackageName($param,
1559
            $this->_config->get('default_channel'));
1560
        PEAR::popErrorHandling();
1561
        if (PEAR::isError($pname)) {
1562
            if ($pname->getCode() == 'invalid') {
1563
                $this->_valid = false;
1564
                return false;
1565
            }
1566
            if ($pname->getCode() == 'channel') {
1567
                $parsed = $pname->getUserInfo();
1568
                if ($this->_downloader->discover($parsed['channel'])) {
1569
                    if ($this->_config->get('auto_discover')) {
1570
                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1571
                        $pname = $this->_registry->parsePackageName($param,
1572
                            $this->_config->get('default_channel'));
1573
                        PEAR::popErrorHandling();
1574
                    } else {
1575
                        if (!isset($options['soft'])) {
1576
                            $this->_downloader->log(0, 'Channel "' . $parsed['channel'] .
1577
                                '" is not initialized, use ' .
1578
                                '"pear channel-discover ' . $parsed['channel'] . '" to initialize' .
1579
                                'or pear config-set auto_discover 1');
1580
                        }
1581
                    }
1582
                }
1583
                if (PEAR::isError($pname)) {
1584
                    if (!isset($options['soft'])) {
1585
                        $this->_downloader->log(0, $pname->getMessage());
1586
                    }
1587
                    if (is_array($param)) {
1588
                        $param = $this->_registry->parsedPackageNameToString($param);
1589
                    }
1590
                    $err = PEAR::raiseError('invalid package name/package file "' .
1591
                        $param . '"');
1592
                    $this->_valid = false;
1593
                    return $err;
1594
                }
1595
            } else {
1596
                if (!isset($options['soft'])) {
1597
                    $this->_downloader->log(0, $pname->getMessage());
1598
                }
1599
                $err = PEAR::raiseError('invalid package name/package file "' .
1600
                    $param . '"');
1601
                $this->_valid = false;
1602
                return $err;
1603
            }
1604
        }
1605
        if (!isset($this->_type)) {
1606
            $this->_type = 'xmlrpc';
1607
        }
1608
        $this->_parsedname = $pname;
1609
        if (isset($pname['state'])) {
1610
            $this->_explicitState = $pname['state'];
1611
        } else {
1612
            $this->_explicitState = false;
1613
        }
1614
        if (isset($pname['group'])) {
1615
            $this->_explicitGroup = true;
1616
        } else {
1617
            $this->_explicitGroup = false;
1618
        }
1619
        $info = $this->_downloader->_getPackageDownloadUrl($pname);
1620
        if (PEAR::isError($info)) {
1621
            if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') {
1622
                // try pecl
1623
                $pname['channel'] = 'pecl.php.net';
1624
                if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) {
1625
                    if (!PEAR::isError($test)) {
1626
                        $info = PEAR::raiseError($info->getMessage() . ' - package ' .
1627
                            $this->_registry->parsedPackageNameToString($pname, true) .
1628
                            ' can be installed with "pecl install ' . $pname['package'] .
1629
                            '"');
1630
                    } else {
1631
                        $pname['channel'] = 'pear.php.net';
1632
                    }
1633
                } else {
1634
                    $pname['channel'] = 'pear.php.net';
1635
                }
1636
            }
1637
            return $info;
1638
        }
1639
        $this->_rawpackagefile = $info['raw'];
1640
        $ret = $this->_analyzeDownloadURL($info, $param, $pname);
1641
        if (PEAR::isError($ret)) {
1642
            return $ret;
1643
        }
1644
        if ($ret) {
1645
            $this->_downloadURL = $ret;
1646
            return $this->_valid = (bool) $ret;
1647
        }
1648
    }
1649
 
1650
    /**
1651
     * @param array output of package.getDownloadURL
1652
     * @param string|array|object information for detecting packages to be downloaded, and
1653
     *                            for errors
1654
     * @param array name information of the package
1655
     * @param array|null packages to be downloaded
1656
     * @param bool is this an optional dependency?
1657
     * @param bool is this any kind of dependency?
1658
     * @access private
1659
     */
1660
    function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false,
1661
                                 $isdependency = false)
1662
    {
1663
        if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) {
1664
            return false;
1665
        }
1666
        if (!$info) {
1667
            if (!is_string($param)) {
1668
                $saveparam = ", cannot download \"$param\"";
1669
            } else {
1670
                $saveparam = '';
1671
            }
1672
            // no releases exist
1673
            return PEAR::raiseError('No releases for package "' .
1674
                $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam);
1675
        }
1676
        if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) {
1677
            $err = false;
1678
            if ($pname['channel'] == 'pecl.php.net') {
1679
                if ($info['info']->getChannel() != 'pear.php.net') {
1680
                    $err = true;
1681
                }
1682
            } elseif ($info['info']->getChannel() == 'pecl.php.net') {
1683
                if ($pname['channel'] != 'pear.php.net') {
1684
                    $err = true;
1685
                }
1686
            } else {
1687
                $err = true;
1688
            }
1689
            if ($err) {
1690
                return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] .
1691
                    '" retrieved another channel\'s name for download! ("' .
1692
                    $info['info']->getChannel() . '")');
1693
            }
1694
        }
1695
        if (!isset($info['url'])) {
1696
            if ($this->isInstalled($info)) {
1697
                if ($isdependency && version_compare($info['version'],
1698
                      $this->_registry->packageInfo($info['info']->getPackage(),
1699
                            'version', $info['info']->getChannel()), '<=')) {
1700
                    // ignore bogus errors of "failed to download dependency"
1701
                    // if it is already installed and the one that would be
1702
                    // downloaded is older or the same version (Bug #7219)
1703
                    return false;
1704
                }
1705
            }
1706
            $instead =  ', will instead download version ' . $info['version'] .
1707
                        ', stability "' . $info['info']->getState() . '"';
1708
            // releases exist, but we failed to get any
1709
            if (isset($this->_downloader->_options['force'])) {
1710
                if (isset($pname['version'])) {
1711
                    $vs = ', version "' . $pname['version'] . '"';
1712
                } elseif (isset($pname['state'])) {
1713
                    $vs = ', stability "' . $pname['state'] . '"';
1714
                } elseif ($param == 'dependency') {
1715
                    if (!class_exists('PEAR_Common')) {
1716
                        require_once 'PEAR/Common.php';
1717
                    }
1718
                    if (!in_array($info['info']->getState(),
1719
                          PEAR_Common::betterStates($this->_config->get('preferred_state'), true))) {
1720
                        if ($optional) {
1721
                            // don't spit out confusing error message
1722
                            return $this->_downloader->_getPackageDownloadUrl(
1723
                                array('package' => $pname['package'],
1724
                                      'channel' => $pname['channel'],
1725
                                      'version' => $info['version']));
1726
                        }
1727
                        $vs = ' within preferred state "' . $this->_config->get('preferred_state') .
1728
                            '"';
1729
                    } else {
1730
                        if (!class_exists('PEAR_Dependency2')) {
1731
                            require_once 'PEAR/Dependency2.php';
1732
                        }
1733
                        if ($optional) {
1734
                            // don't spit out confusing error message
1735
                            return $this->_downloader->_getPackageDownloadUrl(
1736
                                array('package' => $pname['package'],
1737
                                      'channel' => $pname['channel'],
1738
                                      'version' => $info['version']));
1739
                        }
1740
                        $vs = PEAR_Dependency2::_getExtraString($pname);
1741
                        $instead = '';
1742
                    }
1743
                } else {
1744
                    $vs = ' within preferred state "' . $this->_config->get(
1745
                        'preferred_state') . '"';
1746
                }
1747
                if (!isset($options['soft'])) {
1748
                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
1749
                        '/' . $pname['package'] . $vs . $instead);
1750
                }
1751
                // download the latest release
1752
                return $this->_downloader->_getPackageDownloadUrl(
1753
                    array('package' => $pname['package'],
1754
                          'channel' => $pname['channel'],
1755
                          'version' => $info['version']));
1756
            } else {
1757
                if (isset($info['php']) && $info['php']) {
1758
                    $err = PEAR::raiseError('Failed to download ' .
1759
                        $this->_registry->parsedPackageNameToString(
1760
                            array('channel' => $pname['channel'],
1761
                                  'package' => $pname['package']),
1762
                                true) .
1763
                        ', latest release is version ' . $info['php']['v'] .
1764
                        ', but it requires PHP version "' .
1765
                        $info['php']['m'] . '", use "' .
1766
                        $this->_registry->parsedPackageNameToString(
1767
                            array('channel' => $pname['channel'], 'package' => $pname['package'],
1768
                            'version' => $info['php']['v'])) . '" to install',
1769
                            PEAR_DOWNLOADER_PACKAGE_PHPVERSION);
1770
                    return $err;
1771
                }
1772
                // construct helpful error message
1773
                if (isset($pname['version'])) {
1774
                    $vs = ', version "' . $pname['version'] . '"';
1775
                } elseif (isset($pname['state'])) {
1776
                    $vs = ', stability "' . $pname['state'] . '"';
1777
                } elseif ($param == 'dependency') {
1778
                    if (!class_exists('PEAR_Common')) {
1779
                        require_once 'PEAR/Common.php';
1780
                    }
1781
                    if (!in_array($info['info']->getState(),
1782
                          PEAR_Common::betterStates($this->_config->get('preferred_state'), true))) {
1783
                        if ($optional) {
1784
                            // don't spit out confusing error message, and don't die on
1785
                            // optional dep failure!
1786
                            return $this->_downloader->_getPackageDownloadUrl(
1787
                                array('package' => $pname['package'],
1788
                                      'channel' => $pname['channel'],
1789
                                      'version' => $info['version']));
1790
                        }
1791
                        $vs = ' within preferred state "' . $this->_config->get('preferred_state') .
1792
                            '"';
1793
                    } else {
1794
                        if (!class_exists('PEAR_Dependency2')) {
1795
                            require_once 'PEAR/Dependency2.php';
1796
                        }
1797
                        if ($optional) {
1798
                            // don't spit out confusing error message, and don't die on
1799
                            // optional dep failure!
1800
                            return $this->_downloader->_getPackageDownloadUrl(
1801
                                array('package' => $pname['package'],
1802
                                      'channel' => $pname['channel'],
1803
                                      'version' => $info['version']));
1804
                        }
1805
                        $vs = PEAR_Dependency2::_getExtraString($pname);
1806
                    }
1807
                } else {
1808
                    $vs = ' within preferred state "' . $this->_downloader->config->get(
1809
                        'preferred_state') . '"';
1810
                }
1811
                $options = $this->_downloader->getOptions();
1812
                // this is only set by the "download-all" command
1813
                if (isset($options['ignorepreferred_state'])) {
1814
                    $err = PEAR::raiseError(
1815
                        'Failed to download ' . $this->_registry->parsedPackageNameToString(
1816
                            array('channel' => $pname['channel'], 'package' => $pname['package']),
1817
                                true)
1818
                         . $vs .
1819
                        ', latest release is version ' . $info['version'] .
1820
                        ', stability "' . $info['info']->getState() . '", use "' .
1821
                        $this->_registry->parsedPackageNameToString(
1822
                            array('channel' => $pname['channel'], 'package' => $pname['package'],
1823
                            'version' => $info['version'])) . '" to install',
1824
                            PEAR_DOWNLOADER_PACKAGE_STATE);
1825
                    return $err;
1826
                }
1827
                $err = PEAR::raiseError(
1828
                    'Failed to download ' . $this->_registry->parsedPackageNameToString(
1829
                        array('channel' => $pname['channel'], 'package' => $pname['package']),
1830
                            true)
1831
                     . $vs .
1832
                    ', latest release is version ' . $info['version'] .
1833
                    ', stability "' . $info['info']->getState() . '", use "' .
1834
                    $this->_registry->parsedPackageNameToString(
1835
                        array('channel' => $pname['channel'], 'package' => $pname['package'],
1836
                        'version' => $info['version'])) . '" to install');
1837
                return $err;
1838
            }
1839
        }
1840
        if (isset($info['deprecated']) && $info['deprecated']) {
1841
            $this->_downloader->log(0,
1842
                'WARNING: "' .
1843
                    $this->_registry->parsedPackageNameToString(
1844
                            array('channel' => $info['info']->getChannel(),
1845
                                  'package' => $info['info']->getPackage()), true) .
1846
                '" is deprecated in favor of "' .
1847
                    $this->_registry->parsedPackageNameToString($info['deprecated'], true) .
1848
                '"');
1849
        }
1850
        return $info;
1851
    }
1852
}
1853
?>