source: branches/version-2_13-dev/data/module/PEAR/Downloader/Package.php @ 23125

Revision 23125, 74.4 KB checked in by kimoto, 11 years ago (diff)

#2275 PEAR更新
不要なrequire_onceの削除
レガシーなPEARモジュールは使わない
SearchReplace?.phpのパスが間違っているので修正

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