Редакция 69 | Содержимое файла | Сравнить с предыдущей | Последнее изменение | Открыть журнал | RSS
Редакция | Автор | № строки | Строка |
---|---|---|---|
69 | alex-w | 1 | <?php |
2 | /** |
||
3 | * PEAR_Installer |
||
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 Stig Bakken <ssb@php.net> |
||
16 | * @author Tomas V.V. Cox <cox@idecnet.com> |
||
17 | * @author Martin Jansen <mj@php.net> |
||
18 | * @author Greg Beaver <cellog@php.net> |
||
19 | * @copyright 1997-2008 The PHP Group |
||
20 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
21 | * @version CVS: $Id: Installer.php,v 1.253 2008/05/13 22:46:07 cellog Exp $ |
||
22 | * @link http://pear.php.net/package/PEAR |
||
23 | * @since File available since Release 0.1 |
||
24 | */ |
||
25 | |||
26 | /** |
||
27 | * Used for installation groups in package.xml 2.0 and platform exceptions |
||
28 | */ |
||
29 | require_once 'OS/Guess.php'; |
||
30 | require_once 'PEAR/Downloader.php'; |
||
31 | |||
32 | define('PEAR_INSTALLER_NOBINARY', -240); |
||
33 | /** |
||
34 | * Administration class used to install PEAR packages and maintain the |
||
35 | * installed package database. |
||
36 | * |
||
37 | * @category pear |
||
38 | * @package PEAR |
||
39 | * @author Stig Bakken <ssb@php.net> |
||
40 | * @author Tomas V.V. Cox <cox@idecnet.com> |
||
41 | * @author Martin Jansen <mj@php.net> |
||
42 | * @author Greg Beaver <cellog@php.net> |
||
43 | * @copyright 1997-2008 The PHP Group |
||
44 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
45 | * @version Release: 1.7.2 |
||
46 | * @link http://pear.php.net/package/PEAR |
||
47 | * @since Class available since Release 0.1 |
||
48 | */ |
||
49 | class PEAR_Installer extends PEAR_Downloader |
||
50 | { |
||
51 | // {{{ properties |
||
52 | |||
53 | /** name of the package directory, for example Foo-1.0 |
||
54 | * @var string |
||
55 | */ |
||
56 | var $pkgdir; |
||
57 | |||
58 | /** directory where PHP code files go |
||
59 | * @var string |
||
60 | */ |
||
61 | var $phpdir; |
||
62 | |||
63 | /** directory where PHP extension files go |
||
64 | * @var string |
||
65 | */ |
||
66 | var $extdir; |
||
67 | |||
68 | /** directory where documentation goes |
||
69 | * @var string |
||
70 | */ |
||
71 | var $docdir; |
||
72 | |||
73 | /** installation root directory (ala PHP's INSTALL_ROOT or |
||
74 | * automake's DESTDIR |
||
75 | * @var string |
||
76 | */ |
||
77 | var $installroot = ''; |
||
78 | |||
79 | /** debug level |
||
80 | * @var int |
||
81 | */ |
||
82 | var $debug = 1; |
||
83 | |||
84 | /** temporary directory |
||
85 | * @var string |
||
86 | */ |
||
87 | var $tmpdir; |
||
88 | |||
89 | /** |
||
90 | * PEAR_Registry object used by the installer |
||
91 | * @var PEAR_Registry |
||
92 | */ |
||
93 | var $registry; |
||
94 | |||
95 | /** |
||
96 | * array of PEAR_Downloader_Packages |
||
97 | * @var array |
||
98 | */ |
||
99 | var $_downloadedPackages; |
||
100 | |||
101 | /** List of file transactions queued for an install/upgrade/uninstall. |
||
102 | * |
||
103 | * Format: |
||
104 | * array( |
||
105 | * 0 => array("rename => array("from-file", "to-file")), |
||
106 | * 1 => array("delete" => array("file-to-delete")), |
||
107 | * ... |
||
108 | * ) |
||
109 | * |
||
110 | * @var array |
||
111 | */ |
||
112 | var $file_operations = array(); |
||
113 | |||
114 | // }}} |
||
115 | |||
116 | // {{{ constructor |
||
117 | |||
118 | /** |
||
119 | * PEAR_Installer constructor. |
||
120 | * |
||
121 | * @param object $ui user interface object (instance of PEAR_Frontend_*) |
||
122 | * |
||
123 | * @access public |
||
124 | */ |
||
125 | function PEAR_Installer(&$ui) |
||
126 | { |
||
127 | parent::PEAR_Common(); |
||
128 | $this->setFrontendObject($ui); |
||
129 | $this->debug = $this->config->get('verbose'); |
||
130 | } |
||
131 | |||
132 | function setOptions($options) |
||
133 | { |
||
134 | $this->_options = $options; |
||
135 | } |
||
136 | |||
137 | function setConfig(&$config) |
||
138 | { |
||
139 | $this->config = &$config; |
||
140 | $this->_registry = &$config->getRegistry(); |
||
141 | } |
||
142 | |||
143 | // }}} |
||
144 | |||
145 | function _removeBackups($files) |
||
146 | { |
||
147 | foreach ($files as $path) { |
||
148 | $this->addFileOperation('removebackup', array($path)); |
||
149 | } |
||
150 | } |
||
151 | |||
152 | // {{{ _deletePackageFiles() |
||
153 | |||
154 | /** |
||
155 | * Delete a package's installed files, does not remove empty directories. |
||
156 | * |
||
157 | * @param string package name |
||
158 | * @param string channel name |
||
159 | * @param bool if true, then files are backed up first |
||
160 | * @return bool TRUE on success, or a PEAR error on failure |
||
161 | * @access protected |
||
162 | */ |
||
163 | function _deletePackageFiles($package, $channel = false, $backup = false) |
||
164 | { |
||
165 | if (!$channel) { |
||
166 | $channel = 'pear.php.net'; |
||
167 | } |
||
168 | if (!strlen($package)) { |
||
169 | return $this->raiseError("No package to uninstall given"); |
||
170 | } |
||
171 | if (strtolower($package) == 'pear' && $channel == 'pear.php.net') { |
||
172 | // to avoid race conditions, include all possible needed files |
||
173 | require_once 'PEAR/Task/Common.php'; |
||
174 | require_once 'PEAR/Task/Replace.php'; |
||
175 | require_once 'PEAR/Task/Unixeol.php'; |
||
176 | require_once 'PEAR/Task/Windowseol.php'; |
||
177 | require_once 'PEAR/PackageFile/v1.php'; |
||
178 | require_once 'PEAR/PackageFile/v2.php'; |
||
179 | require_once 'PEAR/PackageFile/Generator/v1.php'; |
||
180 | require_once 'PEAR/PackageFile/Generator/v2.php'; |
||
181 | } |
||
182 | $filelist = $this->_registry->packageInfo($package, 'filelist', $channel); |
||
183 | if ($filelist == null) { |
||
184 | return $this->raiseError("$channel/$package not installed"); |
||
185 | } |
||
186 | $ret = array(); |
||
187 | foreach ($filelist as $file => $props) { |
||
188 | if (empty($props['installed_as'])) { |
||
189 | continue; |
||
190 | } |
||
191 | $path = $props['installed_as']; |
||
192 | if ($backup) { |
||
193 | $this->addFileOperation('backup', array($path)); |
||
194 | $ret[] = $path; |
||
195 | } |
||
196 | $this->addFileOperation('delete', array($path)); |
||
197 | } |
||
198 | if ($backup) { |
||
199 | return $ret; |
||
200 | } |
||
201 | return true; |
||
202 | } |
||
203 | |||
204 | // }}} |
||
205 | // {{{ _installFile() |
||
206 | |||
207 | /** |
||
208 | * @param string filename |
||
209 | * @param array attributes from <file> tag in package.xml |
||
210 | * @param string path to install the file in |
||
211 | * @param array options from command-line |
||
212 | * @access private |
||
213 | */ |
||
214 | function _installFile($file, $atts, $tmp_path, $options) |
||
215 | { |
||
216 | // {{{ return if this file is meant for another platform |
||
217 | static $os; |
||
218 | if (!isset($this->_registry)) { |
||
219 | $this->_registry = &$this->config->getRegistry(); |
||
220 | } |
||
221 | if (isset($atts['platform'])) { |
||
222 | if (empty($os)) { |
||
223 | $os = new OS_Guess(); |
||
224 | } |
||
225 | if (strlen($atts['platform']) && $atts['platform']{0} == '!') { |
||
226 | $negate = true; |
||
227 | $platform = substr($atts['platform'], 1); |
||
228 | } else { |
||
229 | $negate = false; |
||
230 | $platform = $atts['platform']; |
||
231 | } |
||
232 | if ((bool) $os->matchSignature($platform) === $negate) { |
||
233 | $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")"); |
||
234 | return PEAR_INSTALLER_SKIPPED; |
||
235 | } |
||
236 | } |
||
237 | // }}} |
||
238 | |||
239 | $channel = $this->pkginfo->getChannel(); |
||
240 | // {{{ assemble the destination paths |
||
241 | switch ($atts['role']) { |
||
242 | case 'src': |
||
243 | case 'extsrc': |
||
244 | $this->source_files++; |
||
245 | return; |
||
246 | case 'doc': |
||
247 | case 'data': |
||
248 | case 'test': |
||
249 | $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) . |
||
250 | DIRECTORY_SEPARATOR . $this->pkginfo->getPackage(); |
||
251 | unset($atts['baseinstalldir']); |
||
252 | break; |
||
253 | case 'ext': |
||
254 | case 'php': |
||
255 | $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel); |
||
256 | break; |
||
257 | case 'script': |
||
258 | $dest_dir = $this->config->get('bin_dir', null, $channel); |
||
259 | break; |
||
260 | default: |
||
261 | return $this->raiseError("Invalid role `$atts[role]' for file $file"); |
||
262 | } |
||
263 | $save_destdir = $dest_dir; |
||
264 | if (!empty($atts['baseinstalldir'])) { |
||
265 | $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; |
||
266 | } |
||
267 | if (dirname($file) != '.' && empty($atts['install-as'])) { |
||
268 | $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); |
||
269 | } |
||
270 | if (empty($atts['install-as'])) { |
||
271 | $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); |
||
272 | } else { |
||
273 | $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; |
||
274 | } |
||
275 | $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; |
||
276 | |||
277 | // Clean up the DIRECTORY_SEPARATOR mess |
||
278 | $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; |
||
279 | list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), |
||
280 | array(DIRECTORY_SEPARATOR, |
||
281 | DIRECTORY_SEPARATOR, |
||
282 | DIRECTORY_SEPARATOR), |
||
283 | array($dest_file, $orig_file)); |
||
284 | $final_dest_file = $installed_as = $dest_file; |
||
285 | if (isset($this->_options['packagingroot'])) { |
||
286 | $installedas_dest_dir = dirname($final_dest_file); |
||
287 | $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); |
||
288 | $final_dest_file = $this->_prependPath($final_dest_file, |
||
289 | $this->_options['packagingroot']); |
||
290 | } else { |
||
291 | $installedas_dest_dir = dirname($final_dest_file); |
||
292 | $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); |
||
293 | } |
||
294 | $dest_dir = dirname($final_dest_file); |
||
295 | $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); |
||
296 | if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) { |
||
297 | return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED); |
||
298 | } |
||
299 | // }}} |
||
300 | |||
301 | if (empty($this->_options['register-only']) && |
||
302 | (!file_exists($dest_dir) || !is_dir($dest_dir))) { |
||
303 | if (!$this->mkDirHier($dest_dir)) { |
||
304 | return $this->raiseError("failed to mkdir $dest_dir", |
||
305 | PEAR_INSTALLER_FAILED); |
||
306 | } |
||
307 | $this->log(3, "+ mkdir $dest_dir"); |
||
308 | } |
||
309 | // pretty much nothing happens if we are only registering the install |
||
310 | if (empty($this->_options['register-only'])) { |
||
311 | if (empty($atts['replacements'])) { |
||
312 | if (!file_exists($orig_file)) { |
||
313 | return $this->raiseError("file $orig_file does not exist", |
||
314 | PEAR_INSTALLER_FAILED); |
||
315 | } |
||
316 | if (!@copy($orig_file, $dest_file)) { |
||
317 | return $this->raiseError("failed to write $dest_file: $php_errormsg", |
||
318 | PEAR_INSTALLER_FAILED); |
||
319 | } |
||
320 | $this->log(3, "+ cp $orig_file $dest_file"); |
||
321 | if (isset($atts['md5sum'])) { |
||
322 | $md5sum = md5_file($dest_file); |
||
323 | } |
||
324 | } else { |
||
325 | // {{{ file with replacements |
||
326 | if (!file_exists($orig_file)) { |
||
327 | return $this->raiseError("file does not exist", |
||
328 | PEAR_INSTALLER_FAILED); |
||
329 | } |
||
330 | $contents = file_get_contents($orig_file); |
||
331 | if ($contents === false) { |
||
332 | $contents = ''; |
||
333 | } |
||
334 | if (isset($atts['md5sum'])) { |
||
335 | $md5sum = md5($contents); |
||
336 | } |
||
337 | $subst_from = $subst_to = array(); |
||
338 | foreach ($atts['replacements'] as $a) { |
||
339 | $to = ''; |
||
340 | if ($a['type'] == 'php-const') { |
||
341 | if (preg_match('/^[a-z0-9_]+\\z/i', $a['to'])) { |
||
342 | eval("\$to = $a[to];"); |
||
343 | } else { |
||
344 | if (!isset($options['soft'])) { |
||
345 | $this->log(0, "invalid php-const replacement: $a[to]"); |
||
346 | } |
||
347 | continue; |
||
348 | } |
||
349 | } elseif ($a['type'] == 'pear-config') { |
||
350 | if ($a['to'] == 'master_server') { |
||
351 | $chan = $this->_registry->getChannel($channel); |
||
352 | if (!PEAR::isError($chan)) { |
||
353 | $to = $chan->getServer(); |
||
354 | } else { |
||
355 | $to = $this->config->get($a['to'], null, $channel); |
||
356 | } |
||
357 | } else { |
||
358 | $to = $this->config->get($a['to'], null, $channel); |
||
359 | } |
||
360 | if (is_null($to)) { |
||
361 | if (!isset($options['soft'])) { |
||
362 | $this->log(0, "invalid pear-config replacement: $a[to]"); |
||
363 | } |
||
364 | continue; |
||
365 | } |
||
366 | } elseif ($a['type'] == 'package-info') { |
||
367 | if ($t = $this->pkginfo->packageInfo($a['to'])) { |
||
368 | $to = $t; |
||
369 | } else { |
||
370 | if (!isset($options['soft'])) { |
||
371 | $this->log(0, "invalid package-info replacement: $a[to]"); |
||
372 | } |
||
373 | continue; |
||
374 | } |
||
375 | } |
||
376 | if (!is_null($to)) { |
||
377 | $subst_from[] = $a['from']; |
||
378 | $subst_to[] = $to; |
||
379 | } |
||
380 | } |
||
381 | $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file"); |
||
382 | if (sizeof($subst_from)) { |
||
383 | $contents = str_replace($subst_from, $subst_to, $contents); |
||
384 | } |
||
385 | $wp = @fopen($dest_file, "wb"); |
||
386 | if (!is_resource($wp)) { |
||
387 | return $this->raiseError("failed to create $dest_file: $php_errormsg", |
||
388 | PEAR_INSTALLER_FAILED); |
||
389 | } |
||
390 | if (@fwrite($wp, $contents) === false) { |
||
391 | return $this->raiseError("failed writing to $dest_file: $php_errormsg", |
||
392 | PEAR_INSTALLER_FAILED); |
||
393 | } |
||
394 | fclose($wp); |
||
395 | // }}} |
||
396 | } |
||
397 | // {{{ check the md5 |
||
398 | if (isset($md5sum)) { |
||
399 | if (strtolower($md5sum) === strtolower($atts['md5sum'])) { |
||
400 | $this->log(2, "md5sum ok: $final_dest_file"); |
||
401 | } else { |
||
402 | if (empty($options['force'])) { |
||
403 | // delete the file |
||
404 | if (file_exists($dest_file)) { |
||
405 | unlink($dest_file); |
||
406 | } |
||
407 | if (!isset($options['ignore-errors'])) { |
||
408 | return $this->raiseError("bad md5sum for file $final_dest_file", |
||
409 | PEAR_INSTALLER_FAILED); |
||
410 | } else { |
||
411 | if (!isset($options['soft'])) { |
||
412 | $this->log(0, "warning : bad md5sum for file $final_dest_file"); |
||
413 | } |
||
414 | } |
||
415 | } else { |
||
416 | if (!isset($options['soft'])) { |
||
417 | $this->log(0, "warning : bad md5sum for file $final_dest_file"); |
||
418 | } |
||
419 | } |
||
420 | } |
||
421 | } |
||
422 | // }}} |
||
423 | // {{{ set file permissions |
||
424 | if (!OS_WINDOWS) { |
||
425 | if ($atts['role'] == 'script') { |
||
426 | $mode = 0777 & ~(int)octdec($this->config->get('umask')); |
||
427 | $this->log(3, "+ chmod +x $dest_file"); |
||
428 | } else { |
||
429 | $mode = 0666 & ~(int)octdec($this->config->get('umask')); |
||
430 | } |
||
431 | if ($atts['role'] != 'src') { |
||
432 | $this->addFileOperation("chmod", array($mode, $dest_file)); |
||
433 | if (!@chmod($dest_file, $mode)) { |
||
434 | if (!isset($options['soft'])) { |
||
435 | $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); |
||
436 | } |
||
437 | } |
||
438 | } |
||
439 | } |
||
440 | // }}} |
||
441 | if ($atts['role'] == 'src') { |
||
442 | rename($dest_file, $final_dest_file); |
||
443 | $this->log(2, "renamed source file $dest_file to $final_dest_file"); |
||
444 | } else { |
||
445 | $this->addFileOperation("rename", array($dest_file, $final_dest_file, |
||
446 | $atts['role'] == 'ext')); |
||
447 | } |
||
448 | } |
||
449 | // Store the full path where the file was installed for easy unistall |
||
450 | if ($atts['role'] != 'script') { |
||
451 | $loc = $this->config->get($atts['role'] . '_dir'); |
||
452 | } else { |
||
453 | $loc = $this->config->get('bin_dir'); |
||
454 | } |
||
455 | if ($atts['role'] != 'src') { |
||
456 | $this->addFileOperation("installed_as", array($file, $installed_as, |
||
457 | $loc, |
||
458 | dirname(substr($installedas_dest_file, strlen($loc))))); |
||
459 | } |
||
460 | |||
461 | //$this->log(2, "installed: $dest_file"); |
||
462 | return PEAR_INSTALLER_OK; |
||
463 | } |
||
464 | |||
465 | // }}} |
||
466 | // {{{ _installFile2() |
||
467 | |||
468 | /** |
||
469 | * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 |
||
470 | * @param string filename |
||
471 | * @param array attributes from <file> tag in package.xml |
||
472 | * @param string path to install the file in |
||
473 | * @param array options from command-line |
||
474 | * @access private |
||
475 | */ |
||
476 | function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options) |
||
477 | { |
||
478 | $atts = $real_atts; |
||
479 | if (!isset($this->_registry)) { |
||
480 | $this->_registry = &$this->config->getRegistry(); |
||
481 | } |
||
482 | |||
483 | $channel = $pkg->getChannel(); |
||
484 | // {{{ assemble the destination paths |
||
485 | if (!in_array($atts['attribs']['role'], |
||
486 | PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) { |
||
487 | return $this->raiseError('Invalid role `' . $atts['attribs']['role'] . |
||
488 | "' for file $file"); |
||
489 | } |
||
490 | $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config); |
||
491 | $err = $role->setup($this, $pkg, $atts['attribs'], $file); |
||
492 | if (PEAR::isError($err)) { |
||
493 | return $err; |
||
494 | } |
||
495 | if (!$role->isInstallable()) { |
||
496 | return; |
||
497 | } |
||
498 | $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path); |
||
499 | if (PEAR::isError($info)) { |
||
500 | return $info; |
||
501 | } else { |
||
502 | list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info; |
||
503 | } |
||
504 | if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) { |
||
505 | return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED); |
||
506 | } |
||
507 | $final_dest_file = $installed_as = $dest_file; |
||
508 | if (isset($this->_options['packagingroot'])) { |
||
509 | $final_dest_file = $this->_prependPath($final_dest_file, |
||
510 | $this->_options['packagingroot']); |
||
511 | } |
||
512 | $dest_dir = dirname($final_dest_file); |
||
513 | $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); |
||
514 | // }}} |
||
515 | |||
516 | if (empty($this->_options['register-only'])) { |
||
517 | if (!file_exists($dest_dir) || !is_dir($dest_dir)) { |
||
518 | if (!$this->mkDirHier($dest_dir)) { |
||
519 | return $this->raiseError("failed to mkdir $dest_dir", |
||
520 | PEAR_INSTALLER_FAILED); |
||
521 | } |
||
522 | $this->log(3, "+ mkdir $dest_dir"); |
||
523 | } |
||
524 | } |
||
525 | $attribs = $atts['attribs']; |
||
526 | unset($atts['attribs']); |
||
527 | // pretty much nothing happens if we are only registering the install |
||
528 | if (empty($this->_options['register-only'])) { |
||
529 | if (!count($atts)) { // no tasks |
||
530 | if (!file_exists($orig_file)) { |
||
531 | return $this->raiseError("file $orig_file does not exist", |
||
532 | PEAR_INSTALLER_FAILED); |
||
533 | } |
||
534 | if (!@copy($orig_file, $dest_file)) { |
||
535 | return $this->raiseError("failed to write $dest_file: $php_errormsg", |
||
536 | PEAR_INSTALLER_FAILED); |
||
537 | } |
||
538 | $this->log(3, "+ cp $orig_file $dest_file"); |
||
539 | if (isset($attribs['md5sum'])) { |
||
540 | $md5sum = md5_file($dest_file); |
||
541 | } |
||
542 | } else { // file with tasks |
||
543 | if (!file_exists($orig_file)) { |
||
544 | return $this->raiseError("file $orig_file does not exist", |
||
545 | PEAR_INSTALLER_FAILED); |
||
546 | } |
||
547 | $contents = file_get_contents($orig_file); |
||
548 | if ($contents === false) { |
||
549 | $contents = ''; |
||
550 | } |
||
551 | if (isset($attribs['md5sum'])) { |
||
552 | $md5sum = md5($contents); |
||
553 | } |
||
554 | foreach ($atts as $tag => $raw) { |
||
555 | $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), |
||
556 | array('', '_'), $tag); |
||
557 | $task = "PEAR_Task_$tag"; |
||
558 | $task = &new $task($this->config, $this, PEAR_TASK_INSTALL); |
||
559 | if (!$task->isScript()) { // scripts are only handled after installation |
||
560 | $task->init($raw, $attribs, $pkg->getLastInstalledVersion()); |
||
561 | $res = $task->startSession($pkg, $contents, $final_dest_file); |
||
562 | if ($res === false) { |
||
563 | continue; // skip this file |
||
564 | } |
||
565 | if (PEAR::isError($res)) { |
||
566 | return $res; |
||
567 | } |
||
568 | $contents = $res; // save changes |
||
569 | } |
||
570 | $wp = @fopen($dest_file, "wb"); |
||
571 | if (!is_resource($wp)) { |
||
572 | return $this->raiseError("failed to create $dest_file: $php_errormsg", |
||
573 | PEAR_INSTALLER_FAILED); |
||
574 | } |
||
575 | if (fwrite($wp, $contents) === false) { |
||
576 | return $this->raiseError("failed writing to $dest_file: $php_errormsg", |
||
577 | PEAR_INSTALLER_FAILED); |
||
578 | } |
||
579 | fclose($wp); |
||
580 | } |
||
581 | } |
||
582 | // {{{ check the md5 |
||
583 | if (isset($md5sum)) { |
||
584 | if (strtolower($md5sum) === strtolower($attribs['md5sum'])) { |
||
585 | $this->log(2, "md5sum ok: $final_dest_file"); |
||
586 | } else { |
||
587 | if (empty($options['force'])) { |
||
588 | // delete the file |
||
589 | if (file_exists($dest_file)) { |
||
590 | unlink($dest_file); |
||
591 | } |
||
592 | if (!isset($options['ignore-errors'])) { |
||
593 | return $this->raiseError("bad md5sum for file $final_dest_file", |
||
594 | PEAR_INSTALLER_FAILED); |
||
595 | } else { |
||
596 | if (!isset($options['soft'])) { |
||
597 | $this->log(0, "warning : bad md5sum for file $final_dest_file"); |
||
598 | } |
||
599 | } |
||
600 | } else { |
||
601 | if (!isset($options['soft'])) { |
||
602 | $this->log(0, "warning : bad md5sum for file $final_dest_file"); |
||
603 | } |
||
604 | } |
||
605 | } |
||
606 | } else { |
||
607 | $real_atts['attribs']['md5sum'] = md5_file($dest_file); |
||
608 | } |
||
609 | // }}} |
||
610 | // {{{ set file permissions |
||
611 | if (!OS_WINDOWS) { |
||
612 | if ($role->isExecutable()) { |
||
613 | $mode = 0777 & ~(int)octdec($this->config->get('umask')); |
||
614 | $this->log(3, "+ chmod +x $dest_file"); |
||
615 | } else { |
||
616 | $mode = 0666 & ~(int)octdec($this->config->get('umask')); |
||
617 | } |
||
618 | if ($attribs['role'] != 'src') { |
||
619 | $this->addFileOperation("chmod", array($mode, $dest_file)); |
||
620 | if (!@chmod($dest_file, $mode)) { |
||
621 | if (!isset($options['soft'])) { |
||
622 | $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); |
||
623 | } |
||
624 | } |
||
625 | } |
||
626 | } |
||
627 | // }}} |
||
628 | if ($attribs['role'] == 'src') { |
||
629 | rename($dest_file, $final_dest_file); |
||
630 | $this->log(2, "renamed source file $dest_file to $final_dest_file"); |
||
631 | } else { |
||
632 | $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension())); |
||
633 | } |
||
634 | } |
||
635 | // Store the full path where the file was installed for easy uninstall |
||
636 | if ($attribs['role'] != 'src') { |
||
637 | $loc = $this->config->get($role->getLocationConfig(), null, $channel); |
||
638 | $this->addFileOperation("installed_as", array($file, $installed_as, |
||
639 | $loc, |
||
640 | dirname(substr($installed_as, strlen($loc))))); |
||
641 | } |
||
642 | |||
643 | //$this->log(2, "installed: $dest_file"); |
||
644 | return PEAR_INSTALLER_OK; |
||
645 | } |
||
646 | |||
647 | // }}} |
||
648 | // {{{ addFileOperation() |
||
649 | |||
650 | /** |
||
651 | * Add a file operation to the current file transaction. |
||
652 | * |
||
653 | * @see startFileTransaction() |
||
654 | * @param string $type This can be one of: |
||
655 | * - rename: rename a file ($data has 3 values) |
||
656 | * - backup: backup an existing file ($data has 1 value) |
||
657 | * - removebackup: clean up backups created during install ($data has 1 value) |
||
658 | * - chmod: change permissions on a file ($data has 2 values) |
||
659 | * - delete: delete a file ($data has 1 value) |
||
660 | * - rmdir: delete a directory if empty ($data has 1 value) |
||
661 | * - installed_as: mark a file as installed ($data has 4 values). |
||
662 | * @param array $data For all file operations, this array must contain the |
||
663 | * full path to the file or directory that is being operated on. For |
||
664 | * the rename command, the first parameter must be the file to rename, |
||
665 | * the second its new name, the third whether this is a PHP extension. |
||
666 | * |
||
667 | * The installed_as operation contains 4 elements in this order: |
||
668 | * 1. Filename as listed in the filelist element from package.xml |
||
669 | * 2. Full path to the installed file |
||
670 | * 3. Full path from the php_dir configuration variable used in this |
||
671 | * installation |
||
672 | * 4. Relative path from the php_dir that this file is installed in |
||
673 | */ |
||
674 | function addFileOperation($type, $data) |
||
675 | { |
||
676 | if (!is_array($data)) { |
||
677 | return $this->raiseError('Internal Error: $data in addFileOperation' |
||
678 | . ' must be an array, was ' . gettype($data)); |
||
679 | } |
||
680 | if ($type == 'chmod') { |
||
681 | $octmode = decoct($data[0]); |
||
682 | $this->log(3, "adding to transaction: $type $octmode $data[1]"); |
||
683 | } else { |
||
684 | $this->log(3, "adding to transaction: $type " . implode(" ", $data)); |
||
685 | } |
||
686 | $this->file_operations[] = array($type, $data); |
||
687 | } |
||
688 | |||
689 | // }}} |
||
690 | // {{{ startFileTransaction() |
||
691 | |||
692 | function startFileTransaction($rollback_in_case = false) |
||
693 | { |
||
694 | if (count($this->file_operations) && $rollback_in_case) { |
||
695 | $this->rollbackFileTransaction(); |
||
696 | } |
||
697 | $this->file_operations = array(); |
||
698 | } |
||
699 | |||
700 | // }}} |
||
701 | // {{{ commitFileTransaction() |
||
702 | |||
703 | function commitFileTransaction() |
||
704 | { |
||
705 | $n = count($this->file_operations); |
||
706 | $this->log(2, "about to commit $n file operations"); |
||
707 | // {{{ first, check permissions and such manually |
||
708 | $errors = array(); |
||
709 | foreach ($this->file_operations as $tr) { |
||
710 | list($type, $data) = $tr; |
||
711 | switch ($type) { |
||
712 | case 'rename': |
||
713 | if (!file_exists($data[0])) { |
||
714 | $errors[] = "cannot rename file $data[0], doesn't exist"; |
||
715 | } |
||
716 | // check that dest dir. is writable |
||
717 | if (!is_writable(dirname($data[1]))) { |
||
718 | $errors[] = "permission denied ($type): $data[1]"; |
||
719 | } |
||
720 | break; |
||
721 | case 'chmod': |
||
722 | // check that file is writable |
||
723 | if (!is_writable($data[1])) { |
||
724 | $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]); |
||
725 | } |
||
726 | break; |
||
727 | case 'delete': |
||
728 | if (!file_exists($data[0])) { |
||
729 | $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted"); |
||
730 | } |
||
731 | // check that directory is writable |
||
732 | if (file_exists($data[0])) { |
||
733 | if (!is_writable(dirname($data[0]))) { |
||
734 | $errors[] = "permission denied ($type): $data[0]"; |
||
735 | } else { |
||
736 | // make sure the file to be deleted can be opened for writing |
||
737 | $fp = false; |
||
738 | if (!is_dir($data[0]) && |
||
739 | (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) { |
||
740 | $errors[] = "permission denied ($type): $data[0]"; |
||
741 | } elseif ($fp) { |
||
742 | fclose($fp); |
||
743 | } |
||
744 | } |
||
745 | } |
||
746 | break; |
||
747 | } |
||
748 | |||
749 | } |
||
750 | // }}} |
||
751 | $m = sizeof($errors); |
||
752 | if ($m > 0) { |
||
753 | foreach ($errors as $error) { |
||
754 | if (!isset($this->_options['soft'])) { |
||
755 | $this->log(1, $error); |
||
756 | } |
||
757 | } |
||
758 | if (!isset($this->_options['ignore-errors'])) { |
||
759 | return false; |
||
760 | } |
||
761 | } |
||
762 | $this->_dirtree = array(); |
||
763 | // {{{ really commit the transaction |
||
764 | foreach ($this->file_operations as $i => $tr) { |
||
765 | if (!$tr) { |
||
766 | // support removal of non-existing backups |
||
767 | continue; |
||
768 | } |
||
769 | list($type, $data) = $tr; |
||
770 | switch ($type) { |
||
771 | case 'backup': |
||
772 | if (!file_exists($data[0])) { |
||
773 | $this->file_operations[$i] = false; |
||
774 | break; |
||
775 | } |
||
776 | if (!@copy($data[0], $data[0] . '.bak')) { |
||
777 | $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] . |
||
778 | '.bak ' . $php_errormsg); |
||
779 | return false; |
||
780 | } |
||
781 | $this->log(3, "+ backup $data[0] to $data[0].bak"); |
||
782 | break; |
||
783 | case 'removebackup': |
||
784 | if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) { |
||
785 | unlink($data[0] . '.bak'); |
||
786 | $this->log(3, "+ rm backup of $data[0] ($data[0].bak)"); |
||
787 | } |
||
788 | break; |
||
789 | case 'rename': |
||
790 | if (file_exists($data[1])) { |
||
791 | $test = @unlink($data[1]); |
||
792 | } else { |
||
793 | $test = null; |
||
794 | } |
||
795 | if (!$test && file_exists($data[1])) { |
||
796 | if ($data[2]) { |
||
797 | $extra = ', this extension must be installed manually. Rename to "' . |
||
798 | basename($data[1]) . '"'; |
||
799 | } else { |
||
800 | $extra = ''; |
||
801 | } |
||
802 | if (!isset($this->_options['soft'])) { |
||
803 | $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' . |
||
804 | $data[0] . $extra); |
||
805 | } |
||
806 | if (!isset($this->_options['ignore-errors'])) { |
||
807 | return false; |
||
808 | } |
||
809 | } |
||
810 | // permissions issues with rename - copy() is far superior |
||
811 | $perms = @fileperms($data[0]); |
||
812 | if (!@copy($data[0], $data[1])) { |
||
813 | $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] . |
||
814 | ' ' . $php_errormsg); |
||
815 | return false; |
||
816 | } |
||
817 | // copy over permissions, otherwise they are lost |
||
818 | @chmod($data[1], $perms); |
||
819 | @unlink($data[0]); |
||
820 | $this->log(3, "+ mv $data[0] $data[1]"); |
||
821 | break; |
||
822 | case 'chmod': |
||
823 | if (!@chmod($data[1], $data[0])) { |
||
824 | $this->log(1, 'Could not chmod ' . $data[1] . ' to ' . |
||
825 | decoct($data[0]) . ' ' . $php_errormsg); |
||
826 | return false; |
||
827 | } |
||
828 | $octmode = decoct($data[0]); |
||
829 | $this->log(3, "+ chmod $octmode $data[1]"); |
||
830 | break; |
||
831 | case 'delete': |
||
832 | if (file_exists($data[0])) { |
||
833 | if (!@unlink($data[0])) { |
||
834 | $this->log(1, 'Could not delete ' . $data[0] . ' ' . |
||
835 | $php_errormsg); |
||
836 | return false; |
||
837 | } |
||
838 | $this->log(3, "+ rm $data[0]"); |
||
839 | } |
||
840 | break; |
||
841 | case 'rmdir': |
||
842 | if (file_exists($data[0])) { |
||
843 | do { |
||
844 | $testme = opendir($data[0]); |
||
845 | while (false !== ($entry = readdir($testme))) { |
||
846 | if ($entry == '.' || $entry == '..') { |
||
847 | continue; |
||
848 | } |
||
849 | closedir($testme); |
||
850 | break 2; // this directory is not empty and can't be |
||
851 | // deleted |
||
852 | } |
||
853 | closedir($testme); |
||
854 | if (!@rmdir($data[0])) { |
||
855 | $this->log(1, 'Could not rmdir ' . $data[0] . ' ' . |
||
856 | $php_errormsg); |
||
857 | return false; |
||
858 | } |
||
859 | $this->log(3, "+ rmdir $data[0]"); |
||
860 | } while (false); |
||
861 | } |
||
862 | break; |
||
863 | case 'installed_as': |
||
864 | $this->pkginfo->setInstalledAs($data[0], $data[1]); |
||
865 | if (!isset($this->_dirtree[dirname($data[1])])) { |
||
866 | $this->_dirtree[dirname($data[1])] = true; |
||
867 | $this->pkginfo->setDirtree(dirname($data[1])); |
||
868 | |||
869 | while(!empty($data[3]) && dirname($data[3]) != $data[3] && |
||
870 | $data[3] != '/' && $data[3] != '\\') { |
||
871 | $this->pkginfo->setDirtree($pp = |
||
872 | $this->_prependPath($data[3], $data[2])); |
||
873 | $this->_dirtree[$pp] = true; |
||
874 | $data[3] = dirname($data[3]); |
||
875 | } |
||
876 | } |
||
877 | break; |
||
878 | } |
||
879 | } |
||
880 | // }}} |
||
881 | $this->log(2, "successfully committed $n file operations"); |
||
882 | $this->file_operations = array(); |
||
883 | return true; |
||
884 | } |
||
885 | |||
886 | // }}} |
||
887 | // {{{ rollbackFileTransaction() |
||
888 | |||
889 | function rollbackFileTransaction() |
||
890 | { |
||
891 | $n = count($this->file_operations); |
||
892 | $this->log(2, "rolling back $n file operations"); |
||
893 | foreach ($this->file_operations as $tr) { |
||
894 | list($type, $data) = $tr; |
||
895 | switch ($type) { |
||
896 | case 'backup': |
||
897 | if (file_exists($data[0] . '.bak')) { |
||
898 | if (file_exists($data[0] && is_writable($data[0]))) { |
||
899 | unlink($data[0]); |
||
900 | } |
||
901 | @copy($data[0] . '.bak', $data[0]); |
||
902 | $this->log(3, "+ restore $data[0] from $data[0].bak"); |
||
903 | } |
||
904 | break; |
||
905 | case 'removebackup': |
||
906 | if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) { |
||
907 | unlink($data[0] . '.bak'); |
||
908 | $this->log(3, "+ rm backup of $data[0] ($data[0].bak)"); |
||
909 | } |
||
910 | break; |
||
911 | case 'rename': |
||
912 | @unlink($data[0]); |
||
913 | $this->log(3, "+ rm $data[0]"); |
||
914 | break; |
||
915 | case 'mkdir': |
||
916 | @rmdir($data[0]); |
||
917 | $this->log(3, "+ rmdir $data[0]"); |
||
918 | break; |
||
919 | case 'chmod': |
||
920 | break; |
||
921 | case 'delete': |
||
922 | break; |
||
923 | case 'installed_as': |
||
924 | $this->pkginfo->setInstalledAs($data[0], false); |
||
925 | break; |
||
926 | } |
||
927 | } |
||
928 | $this->pkginfo->resetDirtree(); |
||
929 | $this->file_operations = array(); |
||
930 | } |
||
931 | |||
932 | // }}} |
||
933 | // {{{ mkDirHier($dir) |
||
934 | |||
935 | function mkDirHier($dir) |
||
936 | { |
||
937 | $this->addFileOperation('mkdir', array($dir)); |
||
938 | return parent::mkDirHier($dir); |
||
939 | } |
||
940 | |||
941 | // }}} |
||
942 | // {{{ download() |
||
943 | |||
944 | /** |
||
945 | * Download any files and their dependencies, if necessary |
||
946 | * |
||
947 | * @param array a mixed list of package names, local files, or package.xml |
||
948 | * @param PEAR_Config |
||
949 | * @param array options from the command line |
||
950 | * @param array this is the array that will be populated with packages to |
||
951 | * install. Format of each entry: |
||
952 | * |
||
953 | * <code> |
||
954 | * array('pkg' => 'package_name', 'file' => '/path/to/local/file', |
||
955 | * 'info' => array() // parsed package.xml |
||
956 | * ); |
||
957 | * </code> |
||
958 | * @param array this will be populated with any error messages |
||
959 | * @param false private recursion variable |
||
960 | * @param false private recursion variable |
||
961 | * @param false private recursion variable |
||
962 | * @deprecated in favor of PEAR_Downloader |
||
963 | */ |
||
964 | function download($packages, $options, &$config, &$installpackages, |
||
965 | &$errors, $installed = false, $willinstall = false, $state = false) |
||
966 | { |
||
967 | // trickiness: initialize here |
||
968 | parent::PEAR_Downloader($this->ui, $options, $config); |
||
969 | $ret = parent::download($packages); |
||
970 | $errors = $this->getErrorMsgs(); |
||
971 | $installpackages = $this->getDownloadedPackages(); |
||
972 | trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " . |
||
973 | "in favor of PEAR_Downloader class", E_USER_WARNING); |
||
974 | return $ret; |
||
975 | } |
||
976 | |||
977 | // }}} |
||
978 | // {{{ _parsePackageXml() |
||
979 | |||
980 | function _parsePackageXml(&$descfile, &$tmpdir) |
||
981 | { |
||
982 | if (substr($descfile, -4) == '.xml') { |
||
983 | $tmpdir = false; |
||
984 | } else { |
||
985 | // {{{ Decompress pack in tmp dir ------------------------------------- |
||
986 | |||
987 | // To allow relative package file names |
||
988 | $descfile = realpath($descfile); |
||
989 | |||
990 | if (PEAR::isError($tmpdir = System::mktemp('-d'))) { |
||
991 | return $tmpdir; |
||
992 | } |
||
993 | $this->log(3, '+ tmp dir created at ' . $tmpdir); |
||
994 | // }}} |
||
995 | } |
||
996 | // Parse xml file ----------------------------------------------- |
||
997 | $pkg = new PEAR_PackageFile($this->config, $this->debug, $tmpdir); |
||
998 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
||
999 | $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING); |
||
1000 | PEAR::staticPopErrorHandling(); |
||
1001 | if (PEAR::isError($p)) { |
||
1002 | if (is_array($p->getUserInfo())) { |
||
1003 | foreach ($p->getUserInfo() as $err) { |
||
1004 | $loglevel = $err['level'] == 'error' ? 0 : 1; |
||
1005 | if (!isset($this->_options['soft'])) { |
||
1006 | $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']); |
||
1007 | } |
||
1008 | } |
||
1009 | } |
||
1010 | return $this->raiseError('Installation failed: invalid package file'); |
||
1011 | } else { |
||
1012 | $descfile = $p->getPackageFile(); |
||
1013 | } |
||
1014 | return $p; |
||
1015 | } |
||
1016 | |||
1017 | // }}} |
||
1018 | /** |
||
1019 | * Set the list of PEAR_Downloader_Package objects to allow more sane |
||
1020 | * dependency validation |
||
1021 | * @param array |
||
1022 | */ |
||
1023 | function setDownloadedPackages(&$pkgs) |
||
1024 | { |
||
1025 | PEAR::pushErrorHandling(PEAR_ERROR_RETURN); |
||
1026 | $err = $this->analyzeDependencies($pkgs); |
||
1027 | PEAR::popErrorHandling(); |
||
1028 | if (PEAR::isError($err)) { |
||
1029 | return $err; |
||
1030 | } |
||
1031 | $this->_downloadedPackages = &$pkgs; |
||
1032 | } |
||
1033 | |||
1034 | /** |
||
1035 | * Set the list of PEAR_Downloader_Package objects to allow more sane |
||
1036 | * dependency validation |
||
1037 | * @param array |
||
1038 | */ |
||
1039 | function setUninstallPackages(&$pkgs) |
||
1040 | { |
||
1041 | $this->_downloadedPackages = &$pkgs; |
||
1042 | } |
||
1043 | |||
1044 | function getInstallPackages() |
||
1045 | { |
||
1046 | return $this->_downloadedPackages; |
||
1047 | } |
||
1048 | |||
1049 | // {{{ install() |
||
1050 | |||
1051 | /** |
||
1052 | * Installs the files within the package file specified. |
||
1053 | * |
||
1054 | * @param string|PEAR_Downloader_Package $pkgfile path to the package file, |
||
1055 | * or a pre-initialized packagefile object |
||
1056 | * @param array $options |
||
1057 | * recognized options: |
||
1058 | * - installroot : optional prefix directory for installation |
||
1059 | * - force : force installation |
||
1060 | * - register-only : update registry but don't install files |
||
1061 | * - upgrade : upgrade existing install |
||
1062 | * - soft : fail silently |
||
1063 | * - nodeps : ignore dependency conflicts/missing dependencies |
||
1064 | * - alldeps : install all dependencies |
||
1065 | * - onlyreqdeps : install only required dependencies |
||
1066 | * |
||
1067 | * @return array|PEAR_Error package info if successful |
||
1068 | */ |
||
1069 | |||
1070 | function install($pkgfile, $options = array()) |
||
1071 | { |
||
1072 | $this->_options = $options; |
||
1073 | $this->_registry = &$this->config->getRegistry(); |
||
1074 | if (is_object($pkgfile)) { |
||
1075 | $dlpkg = &$pkgfile; |
||
1076 | $pkg = $pkgfile->getPackageFile(); |
||
1077 | $pkgfile = $pkg->getArchiveFile(); |
||
1078 | $descfile = $pkg->getPackageFile(); |
||
1079 | $tmpdir = dirname($descfile); |
||
1080 | } else { |
||
1081 | $descfile = $pkgfile; |
||
1082 | $tmpdir = ''; |
||
1083 | if (PEAR::isError($pkg = &$this->_parsePackageXml($descfile, $tmpdir))) { |
||
1084 | return $pkg; |
||
1085 | } |
||
1086 | } |
||
1087 | |||
1088 | if (realpath($descfile) != realpath($pkgfile)) { |
||
1089 | $tar = new Archive_Tar($pkgfile); |
||
1090 | if (!$tar->extract($tmpdir)) { |
||
1091 | return $this->raiseError("unable to unpack $pkgfile"); |
||
1092 | } |
||
1093 | } |
||
1094 | |||
1095 | $pkgname = $pkg->getName(); |
||
1096 | $channel = $pkg->getChannel(); |
||
1097 | if (isset($this->_options['packagingroot'])) { |
||
1098 | $regdir = $this->_prependPath( |
||
1099 | $this->config->get('php_dir', null, 'pear.php.net'), |
||
1100 | $this->_options['packagingroot']); |
||
1101 | $packrootphp_dir = $this->_prependPath( |
||
1102 | $this->config->get('php_dir', null, $channel), |
||
1103 | $this->_options['packagingroot']); |
||
1104 | } |
||
1105 | |||
1106 | if (isset($options['installroot'])) { |
||
1107 | $this->config->setInstallRoot($options['installroot']); |
||
1108 | $this->_registry = &$this->config->getRegistry(); |
||
1109 | $installregistry = &$this->_registry; |
||
1110 | $this->installroot = ''; // all done automagically now |
||
1111 | $php_dir = $this->config->get('php_dir', null, $channel); |
||
1112 | } else { |
||
1113 | $this->config->setInstallRoot(false); |
||
1114 | $this->_registry = &$this->config->getRegistry(); |
||
1115 | if (isset($this->_options['packagingroot'])) { |
||
1116 | $installregistry = &new PEAR_Registry($regdir); |
||
1117 | if (!$installregistry->channelExists($channel, true)) { |
||
1118 | // we need to fake a channel-discover of this channel |
||
1119 | $chanobj = $this->_registry->getChannel($channel, true); |
||
1120 | $installregistry->addChannel($chanobj); |
||
1121 | } |
||
1122 | $php_dir = $packrootphp_dir; |
||
1123 | } else { |
||
1124 | $installregistry = &$this->_registry; |
||
1125 | $php_dir = $this->config->get('php_dir', null, $channel); |
||
1126 | } |
||
1127 | $this->installroot = ''; |
||
1128 | } |
||
1129 | |||
1130 | // {{{ checks to do when not in "force" mode |
||
1131 | if (empty($options['force']) && |
||
1132 | (file_exists($this->config->get('php_dir')) && |
||
1133 | is_dir($this->config->get('php_dir')))) { |
||
1134 | $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname); |
||
1135 | $instfilelist = $pkg->getInstallationFileList(true); |
||
1136 | if (PEAR::isError($instfilelist)) { |
||
1137 | return $instfilelist; |
||
1138 | } |
||
1139 | // ensure we have the most accurate registry |
||
1140 | $installregistry->flushFileMap(); |
||
1141 | $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1'); |
||
1142 | if (PEAR::isError($test)) { |
||
1143 | return $test; |
||
1144 | } |
||
1145 | if (sizeof($test)) { |
||
1146 | $pkgs = $this->getInstallPackages(); |
||
1147 | $found = false; |
||
1148 | foreach ($pkgs as $param) { |
||
1149 | if ($pkg->isSubpackageOf($param)) { |
||
1150 | $found = true; |
||
1151 | break; |
||
1152 | } |
||
1153 | } |
||
1154 | if ($found) { |
||
1155 | // subpackages can conflict with earlier versions of parent packages |
||
1156 | $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel()); |
||
1157 | $tmp = $test; |
||
1158 | foreach ($tmp as $file => $info) { |
||
1159 | if (is_array($info)) { |
||
1160 | if (strtolower($info[1]) == strtolower($param->getPackage()) && |
||
1161 | strtolower($info[0]) == strtolower($param->getChannel())) { |
||
1162 | unset($test[$file]); |
||
1163 | unset($parentreg['filelist'][$file]); |
||
1164 | } |
||
1165 | } else { |
||
1166 | if (strtolower($param->getChannel()) != 'pear.php.net') { |
||
1167 | continue; |
||
1168 | } |
||
1169 | if (strtolower($info) == strtolower($param->getPackage())) { |
||
1170 | unset($test[$file]); |
||
1171 | unset($parentreg['filelist'][$file]); |
||
1172 | } |
||
1173 | } |
||
1174 | } |
||
1175 | $pfk = &new PEAR_PackageFile($this->config); |
||
1176 | $parentpkg = &$pfk->fromArray($parentreg); |
||
1177 | $installregistry->updatePackage2($parentpkg); |
||
1178 | } |
||
1179 | if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) { |
||
1180 | $tmp = $test; |
||
1181 | foreach ($tmp as $file => $info) { |
||
1182 | if (is_string($info)) { |
||
1183 | // pear.php.net packages are always stored as strings |
||
1184 | if (strtolower($info) == strtolower($param->getPackage())) { |
||
1185 | // upgrading existing package |
||
1186 | unset($test[$file]); |
||
1187 | } |
||
1188 | } |
||
1189 | } |
||
1190 | } |
||
1191 | if (sizeof($test)) { |
||
1192 | $msg = "$channel/$pkgname: conflicting files found:\n"; |
||
1193 | $longest = max(array_map("strlen", array_keys($test))); |
||
1194 | $fmt = "%${longest}s (%s)\n"; |
||
1195 | foreach ($test as $file => $info) { |
||
1196 | if (!is_array($info)) { |
||
1197 | $info = array('pear.php.net', $info); |
||
1198 | } |
||
1199 | $info = $info[0] . '/' . $info[1]; |
||
1200 | $msg .= sprintf($fmt, $file, $info); |
||
1201 | } |
||
1202 | if (!isset($options['ignore-errors'])) { |
||
1203 | return $this->raiseError($msg); |
||
1204 | } else { |
||
1205 | if (!isset($options['soft'])) { |
||
1206 | $this->log(0, "WARNING: $msg"); |
||
1207 | } |
||
1208 | } |
||
1209 | } |
||
1210 | } |
||
1211 | } |
||
1212 | // }}} |
||
1213 | |||
1214 | $this->startFileTransaction(); |
||
1215 | |||
1216 | if (empty($options['upgrade']) && empty($options['soft'])) { |
||
1217 | // checks to do only when installing new packages |
||
1218 | if ($channel == 'pecl.php.net') { |
||
1219 | $test = $installregistry->packageExists($pkgname, $channel); |
||
1220 | if (!$test) { |
||
1221 | $test = $installregistry->packageExists($pkgname, 'pear.php.net'); |
||
1222 | } |
||
1223 | } else { |
||
1224 | $test = $installregistry->packageExists($pkgname, $channel); |
||
1225 | } |
||
1226 | if (empty($options['force']) && $test) { |
||
1227 | return $this->raiseError("$channel/$pkgname is already installed"); |
||
1228 | } |
||
1229 | } else { |
||
1230 | $usechannel = $channel; |
||
1231 | if ($channel == 'pecl.php.net') { |
||
1232 | $test = $installregistry->packageExists($pkgname, $channel); |
||
1233 | if (!$test) { |
||
1234 | $test = $installregistry->packageExists($pkgname, 'pear.php.net'); |
||
1235 | $usechannel = 'pear.php.net'; |
||
1236 | } |
||
1237 | } else { |
||
1238 | $test = $installregistry->packageExists($pkgname, $channel); |
||
1239 | } |
||
1240 | if ($test) { |
||
1241 | $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel); |
||
1242 | $v2 = $pkg->getVersion(); |
||
1243 | $cmp = version_compare("$v1", "$v2", 'gt'); |
||
1244 | if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) { |
||
1245 | return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)"); |
||
1246 | } |
||
1247 | if (empty($options['register-only'])) { |
||
1248 | // when upgrading, remove old release's files first: |
||
1249 | if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel, |
||
1250 | true))) { |
||
1251 | if (!isset($options['ignore-errors'])) { |
||
1252 | return $this->raiseError($err); |
||
1253 | } else { |
||
1254 | if (!isset($options['soft'])) { |
||
1255 | $this->log(0, 'WARNING: ' . $err->getMessage()); |
||
1256 | } |
||
1257 | } |
||
1258 | } else { |
||
1259 | $backedup = $err; |
||
1260 | } |
||
1261 | } |
||
1262 | } |
||
1263 | } |
||
1264 | |||
1265 | // {{{ Copy files to dest dir --------------------------------------- |
||
1266 | |||
1267 | // info from the package it self we want to access from _installFile |
||
1268 | $this->pkginfo = &$pkg; |
||
1269 | // used to determine whether we should build any C code |
||
1270 | $this->source_files = 0; |
||
1271 | |||
1272 | $savechannel = $this->config->get('default_channel'); |
||
1273 | if (empty($options['register-only']) && !is_dir($php_dir)) { |
||
1274 | if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) { |
||
1275 | return $this->raiseError("no installation destination directory '$php_dir'\n"); |
||
1276 | } |
||
1277 | } |
||
1278 | |||
1279 | $tmp_path = dirname($descfile); |
||
1280 | if (substr($pkgfile, -4) != '.xml') { |
||
1281 | $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion(); |
||
1282 | } |
||
1283 | |||
1284 | $this->configSet('default_channel', $channel); |
||
1285 | // {{{ install files |
||
1286 | |||
1287 | $ver = $pkg->getPackagexmlVersion(); |
||
1288 | if (version_compare($ver, '2.0', '>=')) { |
||
1289 | $filelist = $pkg->getInstallationFilelist(); |
||
1290 | } else { |
||
1291 | $filelist = $pkg->getFileList(); |
||
1292 | } |
||
1293 | if (PEAR::isError($filelist)) { |
||
1294 | return $filelist; |
||
1295 | } |
||
1296 | $p = &$installregistry->getPackage($pkgname, $channel); |
||
1297 | if (empty($options['register-only']) && $p) { |
||
1298 | $dirtree = $p->getDirTree(); |
||
1299 | } else { |
||
1300 | $dirtree = false; |
||
1301 | } |
||
1302 | $pkg->resetFilelist(); |
||
1303 | $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(), |
||
1304 | 'version', $pkg->getChannel())); |
||
1305 | foreach ($filelist as $file => $atts) { |
||
1306 | if ($pkg->getPackagexmlVersion() == '1.0') { |
||
1307 | $this->expectError(PEAR_INSTALLER_FAILED); |
||
1308 | $res = $this->_installFile($file, $atts, $tmp_path, $options); |
||
1309 | $this->popExpect(); |
||
1310 | } else { |
||
1311 | $this->expectError(PEAR_INSTALLER_FAILED); |
||
1312 | $res = $this->_installFile2($pkg, $file, $atts, $tmp_path, $options); |
||
1313 | $this->popExpect(); |
||
1314 | } |
||
1315 | if (PEAR::isError($res)) { |
||
1316 | if (empty($options['ignore-errors'])) { |
||
1317 | $this->rollbackFileTransaction(); |
||
1318 | if ($res->getMessage() == "file does not exist") { |
||
1319 | $this->raiseError("file $file in package.xml does not exist"); |
||
1320 | } |
||
1321 | return $this->raiseError($res); |
||
1322 | } else { |
||
1323 | if (!isset($options['soft'])) { |
||
1324 | $this->log(0, "Warning: " . $res->getMessage()); |
||
1325 | } |
||
1326 | } |
||
1327 | } |
||
1328 | $real = isset($atts['attribs']) ? $atts['attribs'] : $atts; |
||
1329 | if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') { |
||
1330 | // Register files that were installed |
||
1331 | $pkg->installedFile($file, $atts); |
||
1332 | } |
||
1333 | } |
||
1334 | // }}} |
||
1335 | |||
1336 | // {{{ compile and install source files |
||
1337 | if ($this->source_files > 0 && empty($options['nobuild'])) { |
||
1338 | if (PEAR::isError($err = |
||
1339 | $this->_compileSourceFiles($savechannel, $pkg))) { |
||
1340 | return $err; |
||
1341 | } |
||
1342 | } |
||
1343 | // }}} |
||
1344 | |||
1345 | if (isset($backedup)) { |
||
1346 | $this->_removeBackups($backedup); |
||
1347 | } |
||
1348 | if (!$this->commitFileTransaction()) { |
||
1349 | $this->rollbackFileTransaction(); |
||
1350 | $this->configSet('default_channel', $savechannel); |
||
1351 | return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED); |
||
1352 | } |
||
1353 | // }}} |
||
1354 | |||
1355 | $ret = false; |
||
1356 | $installphase = 'install'; |
||
1357 | $oldversion = false; |
||
1358 | // {{{ Register that the package is installed ----------------------- |
||
1359 | if (empty($options['upgrade'])) { |
||
1360 | // if 'force' is used, replace the info in registry |
||
1361 | $usechannel = $channel; |
||
1362 | if ($channel == 'pecl.php.net') { |
||
1363 | $test = $installregistry->packageExists($pkgname, $channel); |
||
1364 | if (!$test) { |
||
1365 | $test = $installregistry->packageExists($pkgname, 'pear.php.net'); |
||
1366 | $usechannel = 'pear.php.net'; |
||
1367 | } |
||
1368 | } else { |
||
1369 | $test = $installregistry->packageExists($pkgname, $channel); |
||
1370 | } |
||
1371 | if (!empty($options['force']) && $test) { |
||
1372 | $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel); |
||
1373 | $installregistry->deletePackage($pkgname, $usechannel); |
||
1374 | } |
||
1375 | $ret = $installregistry->addPackage2($pkg); |
||
1376 | } else { |
||
1377 | if ($dirtree) { |
||
1378 | $this->startFileTransaction(); |
||
1379 | // attempt to delete empty directories |
||
1380 | uksort($dirtree, array($this, '_sortDirs')); |
||
1381 | foreach($dirtree as $dir => $notused) { |
||
1382 | $this->addFileOperation('rmdir', array($dir)); |
||
1383 | } |
||
1384 | $this->commitFileTransaction(); |
||
1385 | } |
||
1386 | $usechannel = $channel; |
||
1387 | if ($channel == 'pecl.php.net') { |
||
1388 | $test = $installregistry->packageExists($pkgname, $channel); |
||
1389 | if (!$test) { |
||
1390 | $test = $installregistry->packageExists($pkgname, 'pear.php.net'); |
||
1391 | $usechannel = 'pear.php.net'; |
||
1392 | } |
||
1393 | } else { |
||
1394 | $test = $installregistry->packageExists($pkgname, $channel); |
||
1395 | } |
||
1396 | // new: upgrade installs a package if it isn't installed |
||
1397 | if (!$test) { |
||
1398 | $ret = $installregistry->addPackage2($pkg); |
||
1399 | } else { |
||
1400 | if ($usechannel != $channel) { |
||
1401 | $installregistry->deletePackage($pkgname, $usechannel); |
||
1402 | $ret = $installregistry->addPackage2($pkg); |
||
1403 | } else { |
||
1404 | $ret = $installregistry->updatePackage2($pkg); |
||
1405 | } |
||
1406 | $installphase = 'upgrade'; |
||
1407 | } |
||
1408 | } |
||
1409 | if (!$ret) { |
||
1410 | $this->configSet('default_channel', $savechannel); |
||
1411 | return $this->raiseError("Adding package $channel/$pkgname to registry failed"); |
||
1412 | } |
||
1413 | // }}} |
||
1414 | $this->configSet('default_channel', $savechannel); |
||
1415 | if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist |
||
1416 | if (PEAR_Task_Common::hasPostinstallTasks()) { |
||
1417 | PEAR_Task_Common::runPostinstallTasks($installphase); |
||
1418 | } |
||
1419 | } |
||
1420 | return $pkg->toArray(true); |
||
1421 | } |
||
1422 | |||
1423 | // }}} |
||
1424 | |||
1425 | // {{{ _compileSourceFiles() |
||
1426 | /** |
||
1427 | * @param string |
||
1428 | * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 |
||
1429 | */ |
||
1430 | function _compileSourceFiles($savechannel, &$filelist) |
||
1431 | { |
||
1432 | require_once 'PEAR/Builder.php'; |
||
1433 | $this->log(1, "$this->source_files source files, building"); |
||
1434 | $bob = &new PEAR_Builder($this->ui); |
||
1435 | $bob->debug = $this->debug; |
||
1436 | $built = $bob->build($filelist, array(&$this, '_buildCallback')); |
||
1437 | if (PEAR::isError($built)) { |
||
1438 | $this->rollbackFileTransaction(); |
||
1439 | $this->configSet('default_channel', $savechannel); |
||
1440 | return $built; |
||
1441 | } |
||
1442 | $this->log(1, "\nBuild process completed successfully"); |
||
1443 | foreach ($built as $ext) { |
||
1444 | $bn = basename($ext['file']); |
||
1445 | list($_ext_name, $_ext_suff) = explode('.', $bn); |
||
1446 | if ($_ext_suff == '.so' || $_ext_suff == '.dll') { |
||
1447 | if (extension_loaded($_ext_name)) { |
||
1448 | $this->raiseError("Extension '$_ext_name' already loaded. " . |
||
1449 | 'Please unload it in your php.ini file ' . |
||
1450 | 'prior to install or upgrade'); |
||
1451 | } |
||
1452 | $role = 'ext'; |
||
1453 | } else { |
||
1454 | $role = 'src'; |
||
1455 | } |
||
1456 | $dest = $ext['dest']; |
||
1457 | $packagingroot = ''; |
||
1458 | if (isset($this->_options['packagingroot'])) { |
||
1459 | $packagingroot = $this->_options['packagingroot']; |
||
1460 | } |
||
1461 | $copyto = $this->_prependPath($dest, $packagingroot); |
||
1462 | if ($copyto != $dest) { |
||
1463 | $this->log(1, "Installing '$dest' as '$copyto'"); |
||
1464 | } else { |
||
1465 | $this->log(1, "Installing '$dest'"); |
||
1466 | } |
||
1467 | $copydir = dirname($copyto); |
||
1468 | // pretty much nothing happens if we are only registering the install |
||
1469 | if (empty($this->_options['register-only'])) { |
||
1470 | if (!file_exists($copydir) || !is_dir($copydir)) { |
||
1471 | if (!$this->mkDirHier($copydir)) { |
||
1472 | return $this->raiseError("failed to mkdir $copydir", |
||
1473 | PEAR_INSTALLER_FAILED); |
||
1474 | } |
||
1475 | $this->log(3, "+ mkdir $copydir"); |
||
1476 | } |
||
1477 | if (!@copy($ext['file'], $copyto)) { |
||
1478 | return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED); |
||
1479 | } |
||
1480 | $this->log(3, "+ cp $ext[file] $copyto"); |
||
1481 | $this->addFileOperation('rename', array($ext['file'], $copyto)); |
||
1482 | if (!OS_WINDOWS) { |
||
1483 | $mode = 0666 & ~(int)octdec($this->config->get('umask')); |
||
1484 | $this->addFileOperation('chmod', array($mode, $copyto)); |
||
1485 | if (!@chmod($copyto, $mode)) { |
||
1486 | $this->log(0, "failed to change mode of $copyto ($php_errormsg)"); |
||
1487 | } |
||
1488 | } |
||
1489 | } |
||
1490 | |||
1491 | if ($filelist->getPackageXmlVersion() == '1.0') { |
||
1492 | $filelist->installedFile($bn, array( |
||
1493 | 'role' => $role, |
||
1494 | 'name' => $bn, |
||
1495 | 'installed_as' => $dest, |
||
1496 | 'php_api' => $ext['php_api'], |
||
1497 | 'zend_mod_api' => $ext['zend_mod_api'], |
||
1498 | 'zend_ext_api' => $ext['zend_ext_api'], |
||
1499 | )); |
||
1500 | } else { |
||
1501 | $filelist->installedFile($bn, array('attribs' => array( |
||
1502 | 'role' => $role, |
||
1503 | 'name' => $bn, |
||
1504 | 'installed_as' => $dest, |
||
1505 | 'php_api' => $ext['php_api'], |
||
1506 | 'zend_mod_api' => $ext['zend_mod_api'], |
||
1507 | 'zend_ext_api' => $ext['zend_ext_api'], |
||
1508 | ))); |
||
1509 | } |
||
1510 | } |
||
1511 | } |
||
1512 | |||
1513 | // }}} |
||
1514 | function &getUninstallPackages() |
||
1515 | { |
||
1516 | return $this->_downloadedPackages; |
||
1517 | } |
||
1518 | // {{{ uninstall() |
||
1519 | |||
1520 | /** |
||
1521 | * Uninstall a package |
||
1522 | * |
||
1523 | * This method removes all files installed by the application, and then |
||
1524 | * removes any empty directories. |
||
1525 | * @param string package name |
||
1526 | * @param array Command-line options. Possibilities include: |
||
1527 | * |
||
1528 | * - installroot: base installation dir, if not the default |
||
1529 | * - register-only : update registry but don't remove files |
||
1530 | * - nodeps: do not process dependencies of other packages to ensure |
||
1531 | * uninstallation does not break things |
||
1532 | */ |
||
1533 | function uninstall($package, $options = array()) |
||
1534 | { |
||
1535 | if (isset($options['installroot'])) { |
||
1536 | $this->config->setInstallRoot($options['installroot']); |
||
1537 | $this->installroot = ''; |
||
1538 | } else { |
||
1539 | $this->config->setInstallRoot(''); |
||
1540 | $this->installroot = ''; |
||
1541 | } |
||
1542 | $this->_registry = &$this->config->getRegistry(); |
||
1543 | if (is_object($package)) { |
||
1544 | $channel = $package->getChannel(); |
||
1545 | $pkg = $package; |
||
1546 | $package = $pkg->getPackage(); |
||
1547 | } else { |
||
1548 | $pkg = false; |
||
1549 | $info = $this->_registry->parsePackageName($package, |
||
1550 | $this->config->get('default_channel')); |
||
1551 | $channel = $info['channel']; |
||
1552 | $package = $info['package']; |
||
1553 | } |
||
1554 | $savechannel = $this->config->get('default_channel'); |
||
1555 | $this->configSet('default_channel', $channel); |
||
1556 | if (!is_object($pkg)) { |
||
1557 | $pkg = $this->_registry->getPackage($package, $channel); |
||
1558 | } |
||
1559 | if (!$pkg) { |
||
1560 | $this->configSet('default_channel', $savechannel); |
||
1561 | return $this->raiseError($this->_registry->parsedPackageNameToString( |
||
1562 | array( |
||
1563 | 'channel' => $channel, |
||
1564 | 'package' => $package |
||
1565 | ), true) . ' not installed'); |
||
1566 | } |
||
1567 | if ($pkg->getInstalledBinary()) { |
||
1568 | // this is just an alias for a binary package |
||
1569 | return $this->_registry->deletePackage($package, $channel); |
||
1570 | } |
||
1571 | $filelist = $pkg->getFilelist(); |
||
1572 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
||
1573 | if (!class_exists('PEAR_Dependency2')) { |
||
1574 | require_once 'PEAR/Dependency2.php'; |
||
1575 | } |
||
1576 | $depchecker = &new PEAR_Dependency2($this->config, $options, |
||
1577 | array('channel' => $channel, 'package' => $package), |
||
1578 | PEAR_VALIDATE_UNINSTALLING); |
||
1579 | $e = $depchecker->validatePackageUninstall($this); |
||
1580 | PEAR::staticPopErrorHandling(); |
||
1581 | if (PEAR::isError($e)) { |
||
1582 | if (!isset($options['ignore-errors'])) { |
||
1583 | return $this->raiseError($e); |
||
1584 | } else { |
||
1585 | if (!isset($options['soft'])) { |
||
1586 | $this->log(0, 'WARNING: ' . $e->getMessage()); |
||
1587 | } |
||
1588 | } |
||
1589 | } elseif (is_array($e)) { |
||
1590 | if (!isset($options['soft'])) { |
||
1591 | $this->log(0, $e[0]); |
||
1592 | } |
||
1593 | } |
||
1594 | $this->pkginfo = &$pkg; |
||
1595 | // pretty much nothing happens if we are only registering the uninstall |
||
1596 | if (empty($options['register-only'])) { |
||
1597 | // {{{ Delete the files |
||
1598 | $this->startFileTransaction(); |
||
1599 | PEAR::pushErrorHandling(PEAR_ERROR_RETURN); |
||
1600 | if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) { |
||
1601 | PEAR::popErrorHandling(); |
||
1602 | $this->rollbackFileTransaction(); |
||
1603 | $this->configSet('default_channel', $savechannel); |
||
1604 | if (!isset($options['ignore-errors'])) { |
||
1605 | return $this->raiseError($err); |
||
1606 | } else { |
||
1607 | if (!isset($options['soft'])) { |
||
1608 | $this->log(0, 'WARNING: ' . $err->getMessage()); |
||
1609 | } |
||
1610 | } |
||
1611 | } else { |
||
1612 | PEAR::popErrorHandling(); |
||
1613 | } |
||
1614 | if (!$this->commitFileTransaction()) { |
||
1615 | $this->rollbackFileTransaction(); |
||
1616 | if (!isset($options['ignore-errors'])) { |
||
1617 | return $this->raiseError("uninstall failed"); |
||
1618 | } elseif (!isset($options['soft'])) { |
||
1619 | $this->log(0, 'WARNING: uninstall failed'); |
||
1620 | } |
||
1621 | } else { |
||
1622 | $this->startFileTransaction(); |
||
1623 | if ($dirtree = $pkg->getDirTree()) { |
||
1624 | // attempt to delete empty directories |
||
1625 | uksort($dirtree, array($this, '_sortDirs')); |
||
1626 | foreach($dirtree as $dir => $notused) { |
||
1627 | $this->addFileOperation('rmdir', array($dir)); |
||
1628 | } |
||
1629 | } else { |
||
1630 | $this->configSet('default_channel', $savechannel); |
||
1631 | return $this->_registry->deletePackage($package, $channel); |
||
1632 | } |
||
1633 | if (!$this->commitFileTransaction()) { |
||
1634 | $this->rollbackFileTransaction(); |
||
1635 | if (!isset($options['ignore-errors'])) { |
||
1636 | return $this->raiseError("uninstall failed"); |
||
1637 | } elseif (!isset($options['soft'])) { |
||
1638 | $this->log(0, 'WARNING: uninstall failed'); |
||
1639 | } |
||
1640 | } |
||
1641 | } |
||
1642 | // }}} |
||
1643 | } |
||
1644 | |||
1645 | $this->configSet('default_channel', $savechannel); |
||
1646 | // Register that the package is no longer installed |
||
1647 | return $this->_registry->deletePackage($package, $channel); |
||
1648 | } |
||
1649 | |||
1650 | /** |
||
1651 | * Sort a list of arrays of array(downloaded packagefilename) by dependency. |
||
1652 | * |
||
1653 | * It also removes duplicate dependencies |
||
1654 | * @param array an array of PEAR_PackageFile_v[1/2] objects |
||
1655 | * @return array|PEAR_Error array of array(packagefilename, package.xml contents) |
||
1656 | */ |
||
1657 | function sortPackagesForUninstall(&$packages) |
||
1658 | { |
||
1659 | $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config); |
||
1660 | if (PEAR::isError($this->_dependencyDB)) { |
||
1661 | return $this->_dependencyDB; |
||
1662 | } |
||
1663 | usort($packages, array(&$this, '_sortUninstall')); |
||
1664 | } |
||
1665 | |||
1666 | function _sortUninstall($a, $b) |
||
1667 | { |
||
1668 | if (!$a->getDeps() && !$b->getDeps()) { |
||
1669 | return 0; // neither package has dependencies, order is insignificant |
||
1670 | } |
||
1671 | if ($a->getDeps() && !$b->getDeps()) { |
||
1672 | return -1; // $a must be installed after $b because $a has dependencies |
||
1673 | } |
||
1674 | if (!$a->getDeps() && $b->getDeps()) { |
||
1675 | return 1; // $b must be installed after $a because $b has dependencies |
||
1676 | } |
||
1677 | // both packages have dependencies |
||
1678 | if ($this->_dependencyDB->dependsOn($a, $b)) { |
||
1679 | return -1; |
||
1680 | } |
||
1681 | if ($this->_dependencyDB->dependsOn($b, $a)) { |
||
1682 | return 1; |
||
1683 | } |
||
1684 | return 0; |
||
1685 | } |
||
1686 | |||
1687 | // }}} |
||
1688 | // {{{ _sortDirs() |
||
1689 | function _sortDirs($a, $b) |
||
1690 | { |
||
1691 | if (strnatcmp($a, $b) == -1) return 1; |
||
1692 | if (strnatcmp($a, $b) == 1) return -1; |
||
1693 | return 0; |
||
1694 | } |
||
1695 | |||
1696 | // }}} |
||
1697 | |||
1698 | // {{{ _buildCallback() |
||
1699 | |||
1700 | function _buildCallback($what, $data) |
||
1701 | { |
||
1702 | if (($what == 'cmdoutput' && $this->debug > 1) || |
||
1703 | ($what == 'output' && $this->debug > 0)) { |
||
1704 | $this->ui->outputData(rtrim($data), 'build'); |
||
1705 | } |
||
1706 | } |
||
1707 | |||
1708 | // }}} |
||
1709 | } |
||
1710 | |||
1711 | // {{{ md5_file() utility function |
||
1712 | if (!function_exists("md5_file")) { |
||
1713 | function md5_file($filename) { |
||
1714 | if (!$fd = @fopen($file, 'r')) { |
||
1715 | return false; |
||
1716 | } |
||
1717 | fclose($fd); |
||
1718 | return md5(file_get_contents($filename)); |
||
1719 | } |
||
1720 | } |
||
1721 | // }}} |
||
1722 | |||
1723 | ?> |