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