Bearsampp 2026.9.23
Loading...
Searching...
No Matches
Installer.php
Go to the documentation of this file.
1<?php
18
22require_once 'OS/Guess.php';
23require_once 'PEAR/Downloader.php';
24
25define('PEAR_INSTALLER_NOBINARY', -240);
43{
44 // {{{ properties
45
50
55
60
65
70 var $installroot = '';
71
75 var $debug = 1;
76
81
87
93
105 var $file_operations = array();
106
107 // }}}
108
109 // {{{ constructor
110
118 function __construct(&$ui)
119 {
120 parent::__construct($ui, array(), null);
121 $this->setFrontendObject($ui);
122 $this->debug = $this->config->get('verbose');
123 }
124
126 {
127 $this->_options = $options;
128 }
129
131 {
132 $this->config = &$config;
133 $this->_registry = &$config->getRegistry();
134 }
135
136 // }}}
137
138 function _removeBackups($files)
139 {
140 foreach ($files as $path) {
141 $this->addFileOperation('removebackup', array($path));
142 }
143 }
144
145 // {{{ _deletePackageFiles()
146
156 function _deletePackageFiles($package, $channel = false, $backup = false)
157 {
158 if (!$channel) {
159 $channel = 'pear.php.net';
160 }
161
162 if (!strlen($package)) {
163 return $this->raiseError("No package to uninstall given");
164 }
165
166 if (strtolower($package) == 'pear' && $channel == 'pear.php.net') {
167 // to avoid race conditions, include all possible needed files
168 require_once 'PEAR/Task/Common.php';
169 require_once 'PEAR/Task/Replace.php';
170 require_once 'PEAR/Task/Unixeol.php';
171 require_once 'PEAR/Task/Windowseol.php';
172 require_once 'PEAR/PackageFile/v1.php';
173 require_once 'PEAR/PackageFile/v2.php';
174 require_once 'PEAR/PackageFile/Generator/v1.php';
175 require_once 'PEAR/PackageFile/Generator/v2.php';
176 }
177
178 $filelist = $this->_registry->packageInfo($package, 'filelist', $channel);
179 if ($filelist == null) {
180 return $this->raiseError("$channel/$package not installed");
181 }
182
183 $ret = array();
184 foreach ($filelist as $file => $props) {
185 if (empty($props['installed_as'])) {
186 continue;
187 }
188
189 $path = $props['installed_as'];
190 if ($backup) {
191 $this->addFileOperation('backup', array($path));
192 $ret[] = $path;
193 }
194
195 $this->addFileOperation('delete', array($path));
196 }
197
198 if ($backup) {
199 return $ret;
200 }
201
202 return true;
203 }
204
205 // }}}
206 // {{{ _installFile()
207
215 function _installFile($file, $atts, $tmp_path, $options)
216 {
217 // {{{ return if this file is meant for another platform
218 static $os;
219 if (!isset($this->_registry)) {
220 $this->_registry = &$this->config->getRegistry();
221 }
222
223 if (isset($atts['platform'])) {
224 if (empty($os)) {
225 $os = new OS_Guess();
226 }
227
228 if (strlen($atts['platform']) && $atts['platform'][0] == '!') {
229 $negate = true;
230 $platform = substr($atts['platform'], 1);
231 } else {
232 $negate = false;
233 $platform = $atts['platform'];
234 }
235
236 if ((bool) $os->matchSignature($platform) === $negate) {
237 $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
239 }
240 }
241 // }}}
242
243 $channel = $this->pkginfo->getChannel();
244 // {{{ assemble the destination paths
245 switch ($atts['role']) {
246 case 'src':
247 case 'extsrc':
248 $this->source_files++;
249 return;
250 case 'doc':
251 case 'data':
252 case 'test':
253 $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) .
254 DIRECTORY_SEPARATOR . $this->pkginfo->getPackage();
255 unset($atts['baseinstalldir']);
256 break;
257 case 'ext':
258 case 'php':
259 $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel);
260 break;
261 case 'script':
262 $dest_dir = $this->config->get('bin_dir', null, $channel);
263 break;
264 default:
265 return $this->raiseError("Invalid role `$atts[role]' for file $file");
266 }
267
268 $save_destdir = $dest_dir;
269 if (!empty($atts['baseinstalldir'])) {
270 $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
271 }
272
273 if (dirname($file) != '.' && empty($atts['install-as'])) {
274 $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
275 }
276
277 if (empty($atts['install-as'])) {
278 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
279 } else {
280 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
281 }
282 $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
283
284 // Clean up the DIRECTORY_SEPARATOR mess
285 $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
286 list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
287 array(DIRECTORY_SEPARATOR,
288 DIRECTORY_SEPARATOR,
289 DIRECTORY_SEPARATOR),
290 array($dest_file, $orig_file));
291 $final_dest_file = $installed_as = $dest_file;
292 if (isset($this->_options['packagingroot'])) {
293 $installedas_dest_dir = dirname($final_dest_file);
294 $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
295 $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']);
296 } else {
297 $installedas_dest_dir = dirname($final_dest_file);
298 $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
299 }
300
301 $dest_dir = dirname($final_dest_file);
302 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
303 if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
304 return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
305 }
306 // }}}
307
308 if (empty($this->_options['register-only']) &&
309 (!file_exists($dest_dir) || !is_dir($dest_dir))) {
310 if (!$this->mkDirHier($dest_dir)) {
311 return $this->raiseError("failed to mkdir $dest_dir",
313 }
314 $this->log(3, "+ mkdir $dest_dir");
315 }
316
317 // pretty much nothing happens if we are only registering the install
318 if (empty($this->_options['register-only'])) {
319 if (empty($atts['replacements'])) {
320 if (!file_exists($orig_file)) {
321 return $this->raiseError("file $orig_file does not exist",
323 }
324
325 if (!@copy($orig_file, $dest_file)) {
326 return $this->raiseError(
327 "failed to write $dest_file: " . error_get_last()["message"],
329 }
330
331 $this->log(3, "+ cp $orig_file $dest_file");
332 if (isset($atts['md5sum'])) {
333 $md5sum = md5_file($dest_file);
334 }
335 } else {
336 // {{{ file with replacements
337 if (!file_exists($orig_file)) {
338 return $this->raiseError("file does not exist",
340 }
341
342 $contents = file_get_contents($orig_file);
343 if ($contents === false) {
344 $contents = '';
345 }
346
347 if (isset($atts['md5sum'])) {
348 $md5sum = md5($contents);
349 }
350
351 $subst_from = $subst_to = array();
352 foreach ($atts['replacements'] as $a) {
353 $to = '';
354 if ($a['type'] == 'php-const') {
355 if (preg_match('/^[a-z0-9_]+\\z/i', $a['to'])) {
356 eval("\$to = $a[to];");
357 } else {
358 if (!isset($options['soft'])) {
359 $this->log(0, "invalid php-const replacement: $a[to]");
360 }
361 continue;
362 }
363 } elseif ($a['type'] == 'pear-config') {
364 if ($a['to'] == 'master_server') {
365 $chan = $this->_registry->getChannel($channel);
366 if (!PEAR::isError($chan)) {
367 $to = $chan->getServer();
368 } else {
369 $to = $this->config->get($a['to'], null, $channel);
370 }
371 } else {
372 $to = $this->config->get($a['to'], null, $channel);
373 }
374 if (is_null($to)) {
375 if (!isset($options['soft'])) {
376 $this->log(0, "invalid pear-config replacement: $a[to]");
377 }
378 continue;
379 }
380 } elseif ($a['type'] == 'package-info') {
381 if ($t = $this->pkginfo->packageInfo($a['to'])) {
382 $to = $t;
383 } else {
384 if (!isset($options['soft'])) {
385 $this->log(0, "invalid package-info replacement: $a[to]");
386 }
387 continue;
388 }
389 }
390 if (!is_null($to)) {
391 $subst_from[] = $a['from'];
392 $subst_to[] = $to;
393 }
394 }
395
396 $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
397 if (sizeof($subst_from)) {
398 $contents = str_replace($subst_from, $subst_to, $contents);
399 }
400
401 $wp = @fopen($dest_file, "wb");
402 if (!is_resource($wp)) {
403 return $this->raiseError(
404 "failed to create $dest_file: " . error_get_last()["message"],
406 }
407
408 if (@fwrite($wp, $contents) === false) {
409 return $this->raiseError(
410 "failed writing to $dest_file: " . error_get_last()["message"],
412 }
413
414 fclose($wp);
415 // }}}
416 }
417
418 // {{{ check the md5
419 if (isset($md5sum)) {
420 if (strtolower($md5sum) === strtolower($atts['md5sum'])) {
421 $this->log(2, "md5sum ok: $final_dest_file");
422 } else {
423 if (empty($options['force'])) {
424 // delete the file
425 if (file_exists($dest_file)) {
426 unlink($dest_file);
427 }
428
429 if (!isset($options['ignore-errors'])) {
430 return $this->raiseError("bad md5sum for file $final_dest_file",
432 }
433
434 if (!isset($options['soft'])) {
435 $this->log(0, "warning : bad md5sum for file $final_dest_file");
436 }
437 } else {
438 if (!isset($options['soft'])) {
439 $this->log(0, "warning : bad md5sum for file $final_dest_file");
440 }
441 }
442 }
443 }
444 // }}}
445 // {{{ set file permissions
446 if (!OS_WINDOWS) {
447 if ($atts['role'] == 'script') {
448 $mode = 0777 & ~(int)octdec($this->config->get('umask'));
449 $this->log(3, "+ chmod +x $dest_file");
450 } else {
451 $mode = 0666 & ~(int)octdec($this->config->get('umask'));
452 }
453
454 if ($atts['role'] != 'src') {
455 $this->addFileOperation("chmod", array($mode, $dest_file));
456 if (!@chmod($dest_file, $mode)) {
457 if (!isset($options['soft'])) {
458 $this->log(0, "failed to change mode of $dest_file: " .
459 error_get_last()["message"]);
460 }
461 }
462 }
463 }
464 // }}}
465
466 if ($atts['role'] == 'src') {
467 rename($dest_file, $final_dest_file);
468 $this->log(2, "renamed source file $dest_file to $final_dest_file");
469 } else {
470 $this->addFileOperation("rename", array($dest_file, $final_dest_file,
471 $atts['role'] == 'ext'));
472 }
473 }
474
475 // Store the full path where the file was installed for easy unistall
476 if ($atts['role'] != 'script') {
477 $loc = $this->config->get($atts['role'] . '_dir');
478 } else {
479 $loc = $this->config->get('bin_dir');
480 }
481
482 if ($atts['role'] != 'src') {
483 $this->addFileOperation("installed_as", array($file, $installed_as,
484 $loc,
485 dirname(substr($installedas_dest_file, strlen($loc)))));
486 }
487
488 //$this->log(2, "installed: $dest_file");
489 return PEAR_INSTALLER_OK;
490 }
491
492 // }}}
493 // {{{ _installFile2()
494
503 function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options)
504 {
505 $atts = $real_atts;
506 if (!isset($this->_registry)) {
507 $this->_registry = &$this->config->getRegistry();
508 }
509
510 $channel = $pkg->getChannel();
511 // {{{ assemble the destination paths
512 if (!in_array($atts['attribs']['role'],
513 PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
514 return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
515 "' for file $file");
516 }
517
518 $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config);
519 $err = $role->setup($this, $pkg, $atts['attribs'], $file);
520 if (PEAR::isError($err)) {
521 return $err;
522 }
523
524 if (!$role->isInstallable()) {
525 return;
526 }
527
528 $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path);
529 if (PEAR::isError($info)) {
530 return $info;
531 }
532
533 list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info;
534 if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
535 return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
536 }
537
538 $final_dest_file = $installed_as = $dest_file;
539 if (isset($this->_options['packagingroot'])) {
540 $final_dest_file = $this->_prependPath($final_dest_file,
541 $this->_options['packagingroot']);
542 }
543
544 $dest_dir = dirname($final_dest_file);
545 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
546 // }}}
547
548 if (empty($this->_options['register-only'])) {
549 if (!file_exists($dest_dir) || !is_dir($dest_dir)) {
550 if (!$this->mkDirHier($dest_dir)) {
551 return $this->raiseError("failed to mkdir $dest_dir",
553 }
554 $this->log(3, "+ mkdir $dest_dir");
555 }
556 }
557
558 $attribs = $atts['attribs'];
559 unset($atts['attribs']);
560 // pretty much nothing happens if we are only registering the install
561 if (empty($this->_options['register-only'])) {
562 if (!count($atts)) { // no tasks
563 if (!file_exists($orig_file)) {
564 return $this->raiseError("file $orig_file does not exist",
566 }
567
568 if (!@copy($orig_file, $dest_file)) {
569 return $this->raiseError(
570 "failed to write $dest_file: " . error_get_last()["message"],
572 }
573
574 $this->log(3, "+ cp $orig_file $dest_file");
575 if (isset($attribs['md5sum'])) {
576 $md5sum = md5_file($dest_file);
577 }
578 } else { // file with tasks
579 if (!file_exists($orig_file)) {
580 return $this->raiseError("file $orig_file does not exist",
582 }
583
584 $contents = file_get_contents($orig_file);
585 if ($contents === false) {
586 $contents = '';
587 }
588
589 if (isset($attribs['md5sum'])) {
590 $md5sum = md5($contents);
591 }
592
593 foreach ($atts as $tag => $raw) {
594 $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), array('', '_'), $tag);
595 $task = "PEAR_Task_$tag";
596 $task = new $task($this->config, $this, PEAR_TASK_INSTALL);
597 if (!$task->isScript()) { // scripts are only handled after installation
598 $task->init($raw, $attribs, $pkg->getLastInstalledVersion());
599 $res = $task->startSession($pkg, $contents, $final_dest_file);
600 if ($res === false) {
601 continue; // skip this file
602 }
603
604 if (PEAR::isError($res)) {
605 return $res;
606 }
607
608 $contents = $res; // save changes
609 }
610
611 $wp = @fopen($dest_file, "wb");
612 if (!is_resource($wp)) {
613 return $this->raiseError(
614 "failed to create $dest_file: " . error_get_last()["message"],
616 }
617
618 if (fwrite($wp, $contents) === false) {
619 return $this->raiseError(
620 "failed writing to $dest_file: " . error_get_last()["message"],
622 }
623
624 fclose($wp);
625 }
626 }
627
628 // {{{ check the md5
629 if (isset($md5sum)) {
630 // Make sure the original md5 sum matches with expected
631 if (strtolower($md5sum) === strtolower($attribs['md5sum'])) {
632 $this->log(2, "md5sum ok: $final_dest_file");
633
634 if (isset($contents)) {
635 // set md5 sum based on $content in case any tasks were run.
636 $real_atts['attribs']['md5sum'] = md5($contents);
637 }
638 } else {
639 if (empty($options['force'])) {
640 // delete the file
641 if (file_exists($dest_file)) {
642 unlink($dest_file);
643 }
644
645 if (!isset($options['ignore-errors'])) {
646 return $this->raiseError("bad md5sum for file $final_dest_file",
648 }
649
650 if (!isset($options['soft'])) {
651 $this->log(0, "warning : bad md5sum for file $final_dest_file");
652 }
653 } else {
654 if (!isset($options['soft'])) {
655 $this->log(0, "warning : bad md5sum for file $final_dest_file");
656 }
657 }
658 }
659 } else {
660 $real_atts['attribs']['md5sum'] = md5_file($dest_file);
661 }
662
663 // }}}
664 // {{{ set file permissions
665 if (!OS_WINDOWS) {
666 if ($role->isExecutable()) {
667 $mode = 0777 & ~(int)octdec($this->config->get('umask'));
668 $this->log(3, "+ chmod +x $dest_file");
669 } else {
670 $mode = 0666 & ~(int)octdec($this->config->get('umask'));
671 }
672
673 if ($attribs['role'] != 'src') {
674 $this->addFileOperation("chmod", array($mode, $dest_file));
675 if (!@chmod($dest_file, $mode)) {
676 if (!isset($options['soft'])) {
677 $this->log(0, "failed to change mode of $dest_file: " .
678 error_get_last()["message"]);
679 }
680 }
681 }
682 }
683 // }}}
684
685 if ($attribs['role'] == 'src') {
686 rename($dest_file, $final_dest_file);
687 $this->log(2, "renamed source file $dest_file to $final_dest_file");
688 } else {
689 $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension()));
690 }
691 }
692
693 // Store the full path where the file was installed for easy uninstall
694 if ($attribs['role'] != 'src') {
695 $loc = $this->config->get($role->getLocationConfig(), null, $channel);
696 $this->addFileOperation('installed_as', array($file, $installed_as,
697 $loc,
698 dirname(substr($installed_as, strlen($loc)))));
699 }
700
701 //$this->log(2, "installed: $dest_file");
702 return PEAR_INSTALLER_OK;
703 }
704
705 // }}}
706 // {{{ addFileOperation()
707
732 function addFileOperation($type, $data)
733 {
734 if (!is_array($data)) {
735 return $this->raiseError('Internal Error: $data in addFileOperation'
736 . ' must be an array, was ' . gettype($data));
737 }
738
739 if ($type == 'chmod') {
740 $octmode = decoct($data[0]);
741 $this->log(3, "adding to transaction: $type $octmode $data[1]");
742 } else {
743 $this->log(3, "adding to transaction: $type " . implode(" ", $data));
744 }
745 $this->file_operations[] = array($type, $data);
746 }
747
748 // }}}
749 // {{{ startFileTransaction()
750
751 function startFileTransaction($rollback_in_case = false)
752 {
753 if (count($this->file_operations) && $rollback_in_case) {
755 }
756 $this->file_operations = array();
757 }
758
759 // }}}
760 // {{{ commitFileTransaction()
761
763 {
764 // {{{ first, check permissions and such manually
765 $errors = array();
766 foreach ($this->file_operations as $key => $tr) {
767 list($type, $data) = $tr;
768 switch ($type) {
769 case 'rename':
770 if (!file_exists($data[0])) {
771 $errors[] = "cannot rename file $data[0], doesn't exist";
772 }
773
774 // check that dest dir. is writable
775 if (!is_writable(dirname($data[1]))) {
776 $errors[] = "permission denied ($type): $data[1]";
777 }
778 break;
779 case 'chmod':
780 // check that file is writable
781 if (!is_writable($data[1])) {
782 $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
783 }
784 break;
785 case 'delete':
786 if (!file_exists($data[0])) {
787 $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
788 }
789 // check that directory is writable
790 if (file_exists($data[0])) {
791 if (!is_writable(dirname($data[0]))) {
792 $errors[] = "permission denied ($type): $data[0]";
793 } else {
794 // make sure the file to be deleted can be opened for writing
795 $fp = false;
796 if (!is_dir($data[0]) &&
797 (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) {
798 $errors[] = "permission denied ($type): $data[0]";
799 } elseif ($fp) {
800 fclose($fp);
801 }
802 }
803
804 /* Verify we are not deleting a file owned by another package
805 * This can happen when a file moves from package A to B in
806 * an upgrade ala http://pear.php.net/17986
807 */
808 $info = array(
809 'package' => strtolower($this->pkginfo->getName()),
810 'channel' => strtolower($this->pkginfo->getChannel()),
811 );
812 $result = $this->_registry->checkFileMap($data[0], $info, '1.1');
813 if (is_array($result)) {
814 $res = array_diff($result, $info);
815 if (!empty($res)) {
816 $new = $this->_registry->getPackage($result[1], $result[0]);
817 $this->file_operations[$key] = false;
818 $pkginfoName = $this->pkginfo->getName();
819 $newChannel = $new->getChannel();
820 $newPackage = $new->getName();
821 $this->log(3, "file $data[0] was scheduled for removal from $pkginfoName but is owned by $newChannel/$newPackage, removal has been cancelled.");
822 }
823 }
824 }
825 break;
826 }
827
828 }
829 // }}}
830
831 $n = count($this->file_operations);
832 $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName());
833
834 $m = count($errors);
835 if ($m > 0) {
836 foreach ($errors as $error) {
837 if (!isset($this->_options['soft'])) {
838 $this->log(1, $error);
839 }
840 }
841
842 if (!isset($this->_options['ignore-errors'])) {
843 return false;
844 }
845 }
846
847 $this->_dirtree = array();
848 // {{{ really commit the transaction
849 foreach ($this->file_operations as $i => $tr) {
850 if (!$tr) {
851 // support removal of non-existing backups
852 continue;
853 }
854
855 list($type, $data) = $tr;
856 switch ($type) {
857 case 'backup':
858 if (!file_exists($data[0])) {
859 $this->file_operations[$i] = false;
860 break;
861 }
862
863 if (!@copy($data[0], $data[0] . '.bak')) {
864 $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] .
865 '.bak ' . error_get_last()["message"]);
866 return false;
867 }
868 $this->log(3, "+ backup $data[0] to $data[0].bak");
869 break;
870 case 'removebackup':
871 if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
872 unlink($data[0] . '.bak');
873 $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
874 }
875 break;
876 case 'rename':
877 $test = file_exists($data[1]) ? @unlink($data[1]) : null;
878 if (!$test && file_exists($data[1])) {
879 if ($data[2]) {
880 $extra = ', this extension must be installed manually. Rename to "' .
881 basename($data[1]) . '"';
882 } else {
883 $extra = '';
884 }
885
886 if (!isset($this->_options['soft'])) {
887 $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' .
888 $data[0] . $extra);
889 }
890
891 if (!isset($this->_options['ignore-errors'])) {
892 return false;
893 }
894 }
895
896 // permissions issues with rename - copy() is far superior
897 $perms = @fileperms($data[0]);
898 if (!@copy($data[0], $data[1])) {
899 $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] .
900 ' ' . error_get_last()["message"]);
901 return false;
902 }
903
904 // copy over permissions, otherwise they are lost
905 @chmod($data[1], $perms);
906 @unlink($data[0]);
907 $this->log(3, "+ mv $data[0] $data[1]");
908 break;
909 case 'chmod':
910 if (!@chmod($data[1], $data[0])) {
911 $this->log(1, 'Could not chmod ' . $data[1] . ' to ' .
912 decoct($data[0]) . ' ' . error_get_last()["message"]);
913 return false;
914 }
915
916 $octmode = decoct($data[0]);
917 $this->log(3, "+ chmod $octmode $data[1]");
918 break;
919 case 'delete':
920 if (file_exists($data[0])) {
921 if (!@unlink($data[0])) {
922 $this->log(1, 'Could not delete ' . $data[0] . ' ' .
923 error_get_last()["message"]);
924 return false;
925 }
926 $this->log(3, "+ rm $data[0]");
927 }
928 break;
929 case 'rmdir':
930 if (file_exists($data[0])) {
931 do {
932 $testme = opendir($data[0]);
933 while (false !== ($entry = readdir($testme))) {
934 if ($entry == '.' || $entry == '..') {
935 continue;
936 }
937 closedir($testme);
938 break 2; // this directory is not empty and can't be
939 // deleted
940 }
941
942 closedir($testme);
943 if (!@rmdir($data[0])) {
944 $this->log(1, 'Could not rmdir ' . $data[0] . ' ' .
945 error_get_last()["message"]);
946 return false;
947 }
948 $this->log(3, "+ rmdir $data[0]");
949 } while (false);
950 }
951 break;
952 case 'installed_as':
953 $this->pkginfo->setInstalledAs($data[0], $data[1]);
954 if (!isset($this->_dirtree[dirname($data[1])])) {
955 $this->_dirtree[dirname($data[1])] = true;
956 $this->pkginfo->setDirtree(dirname($data[1]));
957
958 while(!empty($data[3]) && dirname($data[3]) != $data[3] &&
959 $data[3] != '/' && $data[3] != '\\') {
960 $this->pkginfo->setDirtree($pp =
961 $this->_prependPath($data[3], $data[2]));
962 $this->_dirtree[$pp] = true;
963 $data[3] = dirname($data[3]);
964 }
965 }
966 break;
967 }
968 }
969 // }}}
970 $this->log(2, "successfully committed $n file operations");
971 $this->file_operations = array();
972 return true;
973 }
974
975 // }}}
976 // {{{ rollbackFileTransaction()
977
979 {
980 $n = count($this->file_operations);
981 $this->log(2, "rolling back $n file operations");
982 foreach ($this->file_operations as $tr) {
983 list($type, $data) = $tr;
984 switch ($type) {
985 case 'backup':
986 if (file_exists($data[0] . '.bak')) {
987 if (file_exists($data[0] && is_writable($data[0]))) {
988 unlink($data[0]);
989 }
990 @copy($data[0] . '.bak', $data[0]);
991 $this->log(3, "+ restore $data[0] from $data[0].bak");
992 }
993 break;
994 case 'removebackup':
995 if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
996 unlink($data[0] . '.bak');
997 $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
998 }
999 break;
1000 case 'rename':
1001 @unlink($data[0]);
1002 $this->log(3, "+ rm $data[0]");
1003 break;
1004 case 'mkdir':
1005 @rmdir($data[0]);
1006 $this->log(3, "+ rmdir $data[0]");
1007 break;
1008 case 'chmod':
1009 break;
1010 case 'delete':
1011 break;
1012 case 'installed_as':
1013 $this->pkginfo->setInstalledAs($data[0], false);
1014 break;
1015 }
1016 }
1017 $this->pkginfo->resetDirtree();
1018 $this->file_operations = array();
1019 }
1020
1021 // }}}
1022 // {{{ mkDirHier($dir)
1023
1024 function mkDirHier($dir)
1025 {
1026 $this->addFileOperation('mkdir', array($dir));
1027 return parent::mkDirHier($dir);
1028 }
1029
1030 // }}}
1031 // {{{ _parsePackageXml()
1032
1033 function _parsePackageXml(&$descfile)
1034 {
1035 // Parse xml file -----------------------------------------------
1036 $pkg = new PEAR_PackageFile($this->config, $this->debug);
1038 $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
1040 if (PEAR::isError($p)) {
1041 if (is_array($p->getUserInfo())) {
1042 foreach ($p->getUserInfo() as $err) {
1043 $loglevel = $err['level'] == 'error' ? 0 : 1;
1044 if (!isset($this->_options['soft'])) {
1045 $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']);
1046 }
1047 }
1048 }
1049 return $this->raiseError('Installation failed: invalid package file');
1050 }
1051
1052 $descfile = $p->getPackageFile();
1053 return $p;
1054 }
1055
1056 // }}}
1062 function setDownloadedPackages(&$pkgs)
1063 {
1064 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1065 $err = $this->analyzeDependencies($pkgs);
1066 PEAR::popErrorHandling();
1067 if (PEAR::isError($err)) {
1068 return $err;
1069 }
1070 $this->_downloadedPackages = &$pkgs;
1071 }
1072
1078 function setUninstallPackages(&$pkgs)
1079 {
1080 $this->_downloadedPackages = &$pkgs;
1081 }
1082
1084 {
1086 }
1087
1088 // {{{ install()
1089
1108 function install($pkgfile, $options = array())
1109 {
1110 $this->_options = $options;
1111 $this->_registry = &$this->config->getRegistry();
1112 if (is_object($pkgfile)) {
1113 $dlpkg = &$pkgfile;
1114 $pkg = $pkgfile->getPackageFile();
1115 $pkgfile = $pkg->getArchiveFile();
1116 $descfile = $pkg->getPackageFile();
1117 } else {
1118 $descfile = $pkgfile;
1119 $pkg = $this->_parsePackageXml($descfile);
1120 if (PEAR::isError($pkg)) {
1121 return $pkg;
1122 }
1123 }
1124
1125 $tmpdir = dirname($descfile);
1126 if (realpath($descfile) != realpath($pkgfile)) {
1127 // Use the temp_dir since $descfile can contain the download dir path
1128 $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net');
1129 $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"');
1130
1131 $tar = new Archive_Tar($pkgfile);
1132 if (!$tar->extract($tmpdir)) {
1133 return $this->raiseError("unable to unpack $pkgfile");
1134 }
1135 }
1136
1137 $pkgname = $pkg->getName();
1138 $channel = $pkg->getChannel();
1139
1140 if (isset($options['installroot'])) {
1141 $this->config->setInstallRoot($options['installroot']);
1142 $this->_registry = &$this->config->getRegistry();
1143 $installregistry = &$this->_registry;
1144 $this->installroot = ''; // all done automagically now
1145 $php_dir = $this->config->get('php_dir', null, $channel);
1146 } else {
1147 $this->config->setInstallRoot(false);
1148 $this->_registry = &$this->config->getRegistry();
1149 if (isset($this->_options['packagingroot'])) {
1150 $regdir = $this->_prependPath(
1151 $this->config->get('php_dir', null, 'pear.php.net'),
1152 $this->_options['packagingroot']);
1153
1154 $metadata_dir = $this->config->get('metadata_dir', null, 'pear.php.net');
1155 if ($metadata_dir) {
1156 $metadata_dir = $this->_prependPath(
1157 $metadata_dir,
1158 $this->_options['packagingroot']);
1159 }
1160 $packrootphp_dir = $this->_prependPath(
1161 $this->config->get('php_dir', null, $channel),
1162 $this->_options['packagingroot']);
1163
1164 $installregistry = new PEAR_Registry($regdir, false, false, $metadata_dir);
1165 if (!$installregistry->channelExists($channel, true)) {
1166 // we need to fake a channel-discover of this channel
1167 $chanobj = $this->_registry->getChannel($channel, true);
1168 $installregistry->addChannel($chanobj);
1169 }
1170 $php_dir = $packrootphp_dir;
1171 } else {
1172 $installregistry = &$this->_registry;
1173 $php_dir = $this->config->get('php_dir', null, $channel);
1174 }
1175 $this->installroot = '';
1176 }
1177
1178 // {{{ checks to do when not in "force" mode
1179 if (empty($options['force']) &&
1180 (file_exists($this->config->get('php_dir')) &&
1181 is_dir($this->config->get('php_dir')))) {
1182 $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname);
1183 $instfilelist = $pkg->getInstallationFileList(true);
1184 if (PEAR::isError($instfilelist)) {
1185 return $instfilelist;
1186 }
1187
1188 // ensure we have the most accurate registry
1189 $installregistry->flushFileMap();
1190 $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1');
1191 if (PEAR::isError($test)) {
1192 return $test;
1193 }
1194
1195 if (sizeof($test)) {
1196 $pkgs = $this->getInstallPackages();
1197 $found = false;
1198 foreach ($pkgs as $param) {
1199 if ($pkg->isSubpackageOf($param)) {
1200 $found = true;
1201 break;
1202 }
1203 }
1204
1205 if ($found) {
1206 // subpackages can conflict with earlier versions of parent packages
1207 $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel());
1208 $tmp = $test;
1209 foreach ($tmp as $file => $info) {
1210 if (is_array($info)) {
1211 if (strtolower($info[1]) == strtolower($param->getPackage()) &&
1212 strtolower($info[0]) == strtolower($param->getChannel())
1213 ) {
1214 if (isset($parentreg['filelist'][$file])) {
1215 unset($parentreg['filelist'][$file]);
1216 } else{
1217 $pos = strpos($file, '/');
1218 $basedir = substr($file, 0, $pos);
1219 $file2 = substr($file, $pos + 1);
1220 if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
1221 && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
1222 ) {
1223 unset($parentreg['filelist'][$file2]);
1224 }
1225 }
1226
1227 unset($test[$file]);
1228 }
1229 } else {
1230 if (strtolower($param->getChannel()) != 'pear.php.net') {
1231 continue;
1232 }
1233
1234 if (strtolower($info) == strtolower($param->getPackage())) {
1235 if (isset($parentreg['filelist'][$file])) {
1236 unset($parentreg['filelist'][$file]);
1237 } else{
1238 $pos = strpos($file, '/');
1239 $basedir = substr($file, 0, $pos);
1240 $file2 = substr($file, $pos + 1);
1241 if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
1242 && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
1243 ) {
1244 unset($parentreg['filelist'][$file2]);
1245 }
1246 }
1247
1248 unset($test[$file]);
1249 }
1250 }
1251 }
1252
1253 $pfk = new PEAR_PackageFile($this->config);
1254 $parentpkg = &$pfk->fromArray($parentreg);
1255 $installregistry->updatePackage2($parentpkg);
1256 }
1257
1258 if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) {
1259 $tmp = $test;
1260 foreach ($tmp as $file => $info) {
1261 if (is_string($info)) {
1262 // pear.php.net packages are always stored as strings
1263 if (strtolower($info) == strtolower($param->getPackage())) {
1264 // upgrading existing package
1265 unset($test[$file]);
1266 }
1267 }
1268 }
1269 }
1270
1271 if (count($test)) {
1272 $msg = "$channel/$pkgname: conflicting files found:\n";
1273 $longest = max(array_map("strlen", array_keys($test)));
1274 $fmt = "%{$longest}s (%s)\n";
1275 foreach ($test as $file => $info) {
1276 if (!is_array($info)) {
1277 $info = array('pear.php.net', $info);
1278 }
1279 $info = $info[0] . '/' . $info[1];
1280 $msg .= sprintf($fmt, $file, $info);
1281 }
1282
1283 if (!isset($options['ignore-errors'])) {
1284 return $this->raiseError($msg);
1285 }
1286
1287 if (!isset($options['soft'])) {
1288 $this->log(0, "WARNING: $msg");
1289 }
1290 }
1291 }
1292 }
1293 // }}}
1294
1295 $this->startFileTransaction();
1296
1297 $usechannel = $channel;
1298 if ($channel == 'pecl.php.net') {
1299 $test = $installregistry->packageExists($pkgname, $channel);
1300 if (!$test) {
1301 $test = $installregistry->packageExists($pkgname, 'pear.php.net');
1302 $usechannel = 'pear.php.net';
1303 }
1304 } else {
1305 $test = $installregistry->packageExists($pkgname, $channel);
1306 }
1307
1308 if (empty($options['upgrade']) && empty($options['soft'])) {
1309 // checks to do only when installing new packages
1310 if (empty($options['force']) && $test) {
1311 return $this->raiseError("$channel/$pkgname is already installed");
1312 }
1313 } else {
1314 // Upgrade
1315 if ($test) {
1316 $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel);
1317 $v2 = $pkg->getVersion();
1318 $cmp = version_compare("$v1", "$v2", 'gt');
1319 if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
1320 return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
1321 }
1322 }
1323 }
1324
1325 // Do cleanups for upgrade and install, remove old release's files first
1326 if ($test && empty($options['register-only'])) {
1327 // when upgrading, remove old release's files first:
1328 if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel,
1329 true))) {
1330 if (!isset($options['ignore-errors'])) {
1331 return $this->raiseError($err);
1332 }
1333
1334 if (!isset($options['soft'])) {
1335 $this->log(0, 'WARNING: ' . $err->getMessage());
1336 }
1337 } else {
1338 $backedup = $err;
1339 }
1340 }
1341
1342 // {{{ Copy files to dest dir ---------------------------------------
1343
1344 // info from the package it self we want to access from _installFile
1345 $this->pkginfo = &$pkg;
1346 // used to determine whether we should build any C code
1347 $this->source_files = 0;
1348
1349 $savechannel = $this->config->get('default_channel');
1350 if (empty($options['register-only']) && !is_dir($php_dir)) {
1351 if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) {
1352 return $this->raiseError("no installation destination directory '$php_dir'\n");
1353 }
1354 }
1355
1356 if (substr($pkgfile, -4) != '.xml') {
1357 $tmpdir .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion();
1358 }
1359
1360 $this->configSet('default_channel', $channel);
1361 // {{{ install files
1362
1363 $ver = $pkg->getPackagexmlVersion();
1364 if (version_compare($ver, '2.0', '>=')) {
1365 $filelist = $pkg->getInstallationFilelist();
1366 } else {
1367 $filelist = $pkg->getFileList();
1368 }
1369
1370 if (PEAR::isError($filelist)) {
1371 return $filelist;
1372 }
1373
1374 $p = &$installregistry->getPackage($pkgname, $channel);
1375 $dirtree = (empty($options['register-only']) && $p) ? $p->getDirTree() : false;
1376
1377 $pkg->resetFilelist();
1378 $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(),
1379 'version', $pkg->getChannel()));
1380 foreach ($filelist as $file => $atts) {
1382 if ($pkg->getPackagexmlVersion() == '1.0') {
1383 $res = $this->_installFile($file, $atts, $tmpdir, $options);
1384 } else {
1385 $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options);
1386 }
1387 $this->popExpect();
1388
1389 if (PEAR::isError($res)) {
1390 if (empty($options['ignore-errors'])) {
1391 $this->rollbackFileTransaction();
1392 if ($res->getMessage() == "file does not exist") {
1393 $this->raiseError("file $file in package.xml does not exist");
1394 }
1395
1396 return $this->raiseError($res);
1397 }
1398
1399 if (!isset($options['soft'])) {
1400 $this->log(0, "Warning: " . $res->getMessage());
1401 }
1402 }
1403
1404 $real = isset($atts['attribs']) ? $atts['attribs'] : $atts;
1405 if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') {
1406 // Register files that were installed
1407 $pkg->installedFile($file, $atts);
1408 }
1409 }
1410 // }}}
1411
1412 // {{{ compile and install source files
1413 if ($this->source_files > 0 && empty($options['nobuild'])) {
1414 $configureoptions = empty($options['configureoptions']) ? '' : $options['configureoptions'];
1415 if (PEAR::isError($err =
1416 $this->_compileSourceFiles($savechannel, $pkg, $configureoptions))) {
1417 return $err;
1418 }
1419 }
1420 // }}}
1421
1422 if (isset($backedup)) {
1423 $this->_removeBackups($backedup);
1424 }
1425
1426 if (!$this->commitFileTransaction()) {
1427 $this->rollbackFileTransaction();
1428 $this->configSet('default_channel', $savechannel);
1429 return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
1430 }
1431 // }}}
1432
1433 $ret = false;
1434 $installphase = 'install';
1435 $oldversion = false;
1436 // {{{ Register that the package is installed -----------------------
1437 if (empty($options['upgrade'])) {
1438 // if 'force' is used, replace the info in registry
1439 $usechannel = $channel;
1440 if ($channel == 'pecl.php.net') {
1441 $test = $installregistry->packageExists($pkgname, $channel);
1442 if (!$test) {
1443 $test = $installregistry->packageExists($pkgname, 'pear.php.net');
1444 $usechannel = 'pear.php.net';
1445 }
1446 } else {
1447 $test = $installregistry->packageExists($pkgname, $channel);
1448 }
1449
1450 if (!empty($options['force']) && $test) {
1451 $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel);
1452 $installregistry->deletePackage($pkgname, $usechannel);
1453 }
1454 $ret = $installregistry->addPackage2($pkg);
1455 } else {
1456 if ($dirtree) {
1457 $this->startFileTransaction();
1458 // attempt to delete empty directories
1459 uksort($dirtree, array($this, '_sortDirs'));
1460 foreach($dirtree as $dir => $notused) {
1461 $this->addFileOperation('rmdir', array($dir));
1462 }
1463 $this->commitFileTransaction();
1464 }
1465
1466 $usechannel = $channel;
1467 if ($channel == 'pecl.php.net') {
1468 $test = $installregistry->packageExists($pkgname, $channel);
1469 if (!$test) {
1470 $test = $installregistry->packageExists($pkgname, 'pear.php.net');
1471 $usechannel = 'pear.php.net';
1472 }
1473 } else {
1474 $test = $installregistry->packageExists($pkgname, $channel);
1475 }
1476
1477 // new: upgrade installs a package if it isn't installed
1478 if (!$test) {
1479 $ret = $installregistry->addPackage2($pkg);
1480 } else {
1481 if ($usechannel != $channel) {
1482 $installregistry->deletePackage($pkgname, $usechannel);
1483 $ret = $installregistry->addPackage2($pkg);
1484 } else {
1485 $ret = $installregistry->updatePackage2($pkg);
1486 }
1487 $installphase = 'upgrade';
1488 }
1489 }
1490
1491 if (!$ret) {
1492 $this->configSet('default_channel', $savechannel);
1493 return $this->raiseError("Adding package $channel/$pkgname to registry failed");
1494 }
1495 // }}}
1496
1497 $this->configSet('default_channel', $savechannel);
1498 if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist
1501 }
1502 }
1503
1504 return $pkg->toArray(true);
1505 }
1506
1507 // }}}
1508
1509 // {{{ _compileSourceFiles()
1515 function _compileSourceFiles($savechannel, &$filelist, $configureoptions)
1516 {
1517 require_once 'PEAR/Builder.php';
1518 $this->log(1, "$this->source_files source files, building");
1519 $bob = new PEAR_Builder($configureoptions, $this->ui);
1520 $bob->debug = $this->debug;
1521 $built = $bob->build($filelist, array(&$this, '_buildCallback'));
1522 if (PEAR::isError($built)) {
1523 $this->rollbackFileTransaction();
1524 $this->configSet('default_channel', $savechannel);
1525 return $built;
1526 }
1527
1528 $this->log(1, "\nBuild process completed successfully");
1529 foreach ($built as $ext) {
1530 $bn = basename($ext['file']);
1531 list($_ext_name, $_ext_suff) = explode('.', $bn);
1532 if ($_ext_suff == 'so' || $_ext_suff == 'dll') {
1533 if (extension_loaded($_ext_name)) {
1534 return $this->raiseError("Extension '$_ext_name' already loaded. " .
1535 'Please unload it in your php.ini file ' .
1536 'prior to install or upgrade');
1537 }
1538 $role = 'ext';
1539 } else {
1540 $role = 'src';
1541 }
1542
1543 $dest = $ext['dest'];
1544 $packagingroot = '';
1545 if (isset($this->_options['packagingroot'])) {
1546 $packagingroot = $this->_options['packagingroot'];
1547 }
1548
1549 $copyto = $this->_prependPath($dest, $packagingroot);
1550 $extra = $copyto != $dest ? " as '$copyto'" : '';
1551 $this->log(1, "Installing '$dest'$extra");
1552
1553 $copydir = dirname($copyto);
1554 // pretty much nothing happens if we are only registering the install
1555 if (empty($this->_options['register-only'])) {
1556 if (!file_exists($copydir) || !is_dir($copydir)) {
1557 if (!$this->mkDirHier($copydir)) {
1558 return $this->raiseError("failed to mkdir $copydir",
1560 }
1561
1562 $this->log(3, "+ mkdir $copydir");
1563 }
1564
1565 if (!@copy($ext['file'], $copyto)) {
1566 return $this->raiseError(
1567 "failed to write $copyto (" . error_get_last()["message"] . ")",
1569 }
1570
1571 $this->log(3, "+ cp $ext[file] $copyto");
1572 $this->addFileOperation('rename', array($ext['file'], $copyto));
1573 if (!OS_WINDOWS) {
1574 $mode = 0666 & ~(int)octdec($this->config->get('umask'));
1575 $this->addFileOperation('chmod', array($mode, $copyto));
1576 if (!@chmod($copyto, $mode)) {
1577 $this->log(0, "failed to change mode of $copyto (" .
1578 error_get_last()["message"] . ")");
1579 }
1580 }
1581 }
1582
1583
1584 $data = array(
1585 'role' => $role,
1586 'name' => $bn,
1587 'installed_as' => $dest,
1588 'php_api' => $ext['php_api'],
1589 'zend_mod_api' => $ext['zend_mod_api'],
1590 'zend_ext_api' => $ext['zend_ext_api'],
1591 );
1592
1593 if ($filelist->getPackageXmlVersion() == '1.0') {
1594 $filelist->installedFile($bn, $data);
1595 } else {
1596 $filelist->installedFile($bn, array('attribs' => $data));
1597 }
1598 }
1599 }
1600
1601 // }}}
1603 {
1605 }
1606 // {{{ uninstall()
1607
1621 function uninstall($package, $options = array())
1622 {
1623 $installRoot = isset($options['installroot']) ? $options['installroot'] : '';
1624 $this->config->setInstallRoot($installRoot);
1625
1626 $this->installroot = '';
1627 $this->_registry = &$this->config->getRegistry();
1628 if (is_object($package)) {
1629 $channel = $package->getChannel();
1630 $pkg = $package;
1631 $package = $pkg->getPackage();
1632 } else {
1633 $pkg = false;
1634 $info = $this->_registry->parsePackageName($package,
1635 $this->config->get('default_channel'));
1636 $channel = $info['channel'];
1637 $package = $info['package'];
1638 }
1639
1640 $savechannel = $this->config->get('default_channel');
1641 $this->configSet('default_channel', $channel);
1642 if (!is_object($pkg)) {
1643 $pkg = $this->_registry->getPackage($package, $channel);
1644 }
1645
1646 if (!$pkg) {
1647 $this->configSet('default_channel', $savechannel);
1648 return $this->raiseError($this->_registry->parsedPackageNameToString(
1649 array(
1650 'channel' => $channel,
1651 'package' => $package
1652 ), true) . ' not installed');
1653 }
1654
1655 if ($pkg->getInstalledBinary()) {
1656 // this is just an alias for a binary package
1657 return $this->_registry->deletePackage($package, $channel);
1658 }
1659
1660 $filelist = $pkg->getFilelist();
1662 if (!class_exists('PEAR_Dependency2')) {
1663 require_once 'PEAR/Dependency2.php';
1664 }
1665
1666 $depchecker = new PEAR_Dependency2($this->config, $options,
1667 array('channel' => $channel, 'package' => $package),
1669 $e = $depchecker->validatePackageUninstall($this);
1671 if (PEAR::isError($e)) {
1672 if (!isset($options['ignore-errors'])) {
1673 return $this->raiseError($e);
1674 }
1675
1676 if (!isset($options['soft'])) {
1677 $this->log(0, 'WARNING: ' . $e->getMessage());
1678 }
1679 } elseif (is_array($e)) {
1680 if (!isset($options['soft'])) {
1681 $this->log(0, $e[0]);
1682 }
1683 }
1684
1685 $this->pkginfo = &$pkg;
1686 // pretty much nothing happens if we are only registering the uninstall
1687 if (empty($options['register-only'])) {
1688 // {{{ Delete the files
1689 $this->startFileTransaction();
1690 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1691 if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
1692 PEAR::popErrorHandling();
1693 $this->rollbackFileTransaction();
1694 $this->configSet('default_channel', $savechannel);
1695 if (!isset($options['ignore-errors'])) {
1696 return $this->raiseError($err);
1697 }
1698
1699 if (!isset($options['soft'])) {
1700 $this->log(0, 'WARNING: ' . $err->getMessage());
1701 }
1702 } else {
1703 PEAR::popErrorHandling();
1704 }
1705
1706 if (!$this->commitFileTransaction()) {
1707 $this->rollbackFileTransaction();
1708 if (!isset($options['ignore-errors'])) {
1709 return $this->raiseError("uninstall failed");
1710 }
1711
1712 if (!isset($options['soft'])) {
1713 $this->log(0, 'WARNING: uninstall failed');
1714 }
1715 } else {
1716 $this->startFileTransaction();
1717 $dirtree = $pkg->getDirTree();
1718 if ($dirtree === false) {
1719 $this->configSet('default_channel', $savechannel);
1720 return $this->_registry->deletePackage($package, $channel);
1721 }
1722
1723 // attempt to delete empty directories
1724 uksort($dirtree, array($this, '_sortDirs'));
1725 foreach($dirtree as $dir => $notused) {
1726 $this->addFileOperation('rmdir', array($dir));
1727 }
1728
1729 if (!$this->commitFileTransaction()) {
1730 $this->rollbackFileTransaction();
1731 if (!isset($options['ignore-errors'])) {
1732 return $this->raiseError("uninstall failed");
1733 }
1734
1735 if (!isset($options['soft'])) {
1736 $this->log(0, 'WARNING: uninstall failed');
1737 }
1738 }
1739 }
1740 // }}}
1741 }
1742
1743 $this->configSet('default_channel', $savechannel);
1744 // Register that the package is no longer installed
1745 return $this->_registry->deletePackage($package, $channel);
1746 }
1747
1755 function sortPackagesForUninstall(&$packages)
1756 {
1757 $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config);
1758 if (PEAR::isError($this->_dependencyDB)) {
1759 return $this->_dependencyDB;
1760 }
1761 usort($packages, array(&$this, '_sortUninstall'));
1762 }
1763
1764 function _sortUninstall($a, $b)
1765 {
1766 if (!$a->getDeps() && !$b->getDeps()) {
1767 return 0; // neither package has dependencies, order is insignificant
1768 }
1769 if ($a->getDeps() && !$b->getDeps()) {
1770 return -1; // $a must be installed after $b because $a has dependencies
1771 }
1772 if (!$a->getDeps() && $b->getDeps()) {
1773 return 1; // $b must be installed after $a because $b has dependencies
1774 }
1775 // both packages have dependencies
1776 if ($this->_dependencyDB->dependsOn($a, $b)) {
1777 return -1;
1778 }
1779 if ($this->_dependencyDB->dependsOn($b, $a)) {
1780 return 1;
1781 }
1782 return 0;
1783 }
1784
1785 // }}}
1786 // {{{ _sortDirs()
1787 function _sortDirs($a, $b)
1788 {
1789 if (strnatcmp($a, $b) == -1) return 1;
1790 if (strnatcmp($a, $b) == 1) return -1;
1791 return 0;
1792 }
1793
1794 // }}}
1795
1796 // {{{ _buildCallback()
1797
1798 function _buildCallback($what, $data)
1799 {
1800 if (($what == 'cmdoutput' && $this->debug > 1) ||
1801 ($what == 'output' && $this->debug > 0)) {
1802 $this->ui->outputData(rtrim($data), 'build');
1803 }
1804 }
1805
1806 // }}}
1807}
const PEAR_INSTALLER_OK
const PEAR_INSTALLER_SKIPPED
const PEAR_INSTALLER_FAILED
const PEAR_ERROR_RETURN
Definition PEAR.php:24
const PEAR_TASK_INSTALL
Definition Common.php:24
const PEAR_VALIDATE_UNINSTALLING
Definition Validate.php:19
const PEAR_VALIDATE_INSTALLING
Definition Validate.php:18
$result
log($level, $msg, $append_crlf=true)
Definition Common.php:244
setFrontendObject(&$ui)
Definition Common.php:294
static & singleton(&$config, $depdb=false)
configSet($key, $value, $layer='user', $channel=false)
_prependPath($path, $prepend)
analyzeDependencies(&$params, $force=false)
static & factory($pkg, $role, &$config)
Definition Role.php:60
static getValidRoles($release, $clear=false)
Definition Role.php:88
_sortDirs($a, $b)
_parsePackageXml(&$descfile)
_installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options)
sortPackagesForUninstall(&$packages)
setUninstallPackages(&$pkgs)
startFileTransaction($rollback_in_case=false)
setDownloadedPackages(&$pkgs)
_buildCallback($what, $data)
_compileSourceFiles($savechannel, &$filelist, $configureoptions)
setConfig(&$config)
setOptions($options)
_installFile($file, $atts, $tmp_path, $options)
install($pkgfile, $options=array())
_removeBackups($files)
_sortUninstall($a, $b)
addFileOperation($type, $data)
uninstall($package, $options=array())
_deletePackageFiles($package, $channel=false, $backup=false)
static hasPostinstallTasks()
Definition Common.php:170
static runPostinstallTasks()
Definition Common.php:178
static staticPopErrorHandling()
Definition PEAR.php:654
expectError($code=' *')
Definition PEAR.php:415
popExpect()
Definition PEAR.php:431
static staticPushErrorHandling($mode, $options=null)
Definition PEAR.php:619
static isError($data, $code=null)
Definition PEAR.php:306
$options
Definition pearcmd.php:145
$raw
Definition pearcmd.php:116