#!/usr/bin/perl
# (c) Copyright 2002-2012. CodeWeavers, Inc.
use warnings;
use strict;
my $builtin_productid="cxoffice";
# Portable which(1) implementation
sub cxwhich($$;$)
{
my ($dirs, $app, $noexec)=@_;
if ($app =~ /^\//)
{
return $app if ((-x $app or $noexec) and -f $app);
}
elsif ($app =~ /\//)
{
require Cwd;
my $path=Cwd::cwd() . "/$app";
return $path if ((-x $path or $noexec) and -f $path);
}
else
{
foreach my $dir (split /:/, $dirs)
{
return "$dir/$app" if ($dir ne "" and (-x "$dir/$app" or $noexec) and -f "$dir/$app");
}
}
return undef;
}
# Fast dirname() implementation
sub _cxdirname($)
{
my ($path)=@_;
return undef if (!defined $path);
return "." if ($path !~ s!/+[^/]+/*$!!s);
return "/" if ($path eq "");
return $path;
}
# Locate where CrossOver is installed by looking for the directory
# where the cxmenu script is located, unwinding symlinks on the way
sub locate_cx_root(;$)
{
my ($fallback)=@_;
my $argv0=cxwhich($ENV{PATH},$0);
$argv0=$0 if (!defined $argv0);
if ($argv0 !~ m+^/+)
{
require Cwd;
$argv0=Cwd::cwd() . "/$argv0";
}
my $dir=_cxdirname($argv0);
my $bindir=$dir;
$bindir =~ s%/lib$%/bin%;
while (!-x "$bindir/cxmenu" or !-f "$bindir/cxmenu")
{
last if (!-l $argv0);
$argv0=readlink($argv0);
$argv0="$dir/$argv0" if ($argv0 !~ m+^/+);
$dir=_cxdirname($argv0);
$bindir=$dir;
$bindir =~ s%/lib$%/bin%;
}
$bindir =~ s%/(?:\./)+%/%g;
$bindir =~ s%/\.$%%;
$ENV{CX_ROOT}=_cxdirname($bindir);
if ((!-x "$ENV{CX_ROOT}/bin/cxmenu" or !-f "$ENV{CX_ROOT}/bin/cxmenu") and
$fallback)
{
$ENV{CX_ROOT}=$fallback;
}
if (!-x "$ENV{CX_ROOT}/bin/cxmenu" or !-f "$ENV{CX_ROOT}/bin/cxmenu")
{
my $name0=$0;
$name0 =~ s+^.*/++;
print STDERR "$name0:error: could not find CrossOver in '$ENV{CX_ROOT}'\n";
exit 1;
}
return $ENV{CX_ROOT};
}
BEGIN {
unshift @INC, locate_cx_root() . "/lib/perl";
}
use CXLog;
use CXUtils;
my $productid=CXUtils::get_product_id();
my $productname=CXUtils::get_product_name();
# Process command-line options
my $opt_verbose;
my $opt_force;
my $opt_help;
require CXOpts;
my $cxopts=CXOpts->new();
$cxopts->add_options(["force" => \$opt_force,
"verbose!" => \$opt_verbose,
"?|h|help" => \$opt_help
]);
my $err=$cxopts->parse();
CXLog::fdopen(2) if ($opt_verbose);
# Validate the command line options
my $usage;
if ($err)
{
cxerr("$err\n");
$usage=2;
}
elsif ($opt_help)
{
$usage=0;
}
if (defined $usage)
{
my $name0=cxname0();
if ($usage)
{
cxerr("try '$name0 --help' for more information\n");
exit $usage;
}
print "Usage: $name0 [--force] [--verbose] [--help]\n";
print "\n";
print "Uninstalls CrossOver or removes its files from your account.\n";
print "\n";
print "Options:\n";
print " --force If set, no confirmation is asked before uninstalling CrossOver\n";
print " --verbose Print more information about what is going on\n";
print " --help, -h Shows this help message\n";
exit 0;
}
# Determine what mode we should be operating in
my $pre_msg;
my $mode;
my $post_msg;
my $err_msg;
my $button_list;
my $btn_uninstall_user=cxgettext("Delete Bottles");
my $btn_uninstall=cxgettext("Uninstall");
my $btn_cancel=cxgettext("Cancel");
if (-w $ENV{CX_ROOT})
{
$mode="uninstall";
if ($> == 0)
{
# Check if there is any managed bottle
my $has_bottle;
foreach my $dir ("$ENV{CX_ROOT}/support",
"$ENV{HOME}/.$productid")
{
next if (!-d $dir);
if (opendir(my $dh, $dir))
{
foreach my $dentry (readdir $dh)
{
next if ($dentry =~ /^\.\.?$/);
if (-f "$ENV{CX_ROOT}/support/$dentry/cxbottle.conf")
{
$has_bottle=1;
last;
}
}
closedir($dh);
}
last if ($has_bottle);
}
if ($has_bottle)
{
$pre_msg=cxgettext("Windows applications and C: drives are contained in bottles. Some of these bottles are maybe used system-wide, so removing them will impact each user.\n");
}
else
{
$pre_msg="";
$button_list="$btn_cancel:102,$btn_uninstall:101";
}
$pre_msg=cxgettext("\%1\$sBefore uninstalling \%2\$s, make sure to uninstall it from each user's environment. To do so, run '\%3\$s' in their account.", $pre_msg, $productname, "$ENV{CX_ROOT}/bin/cxuninstall");
$pre_msg=cxgettext("\%1\$s Do you want to delete root's bottles in addition to uninstalling \%2\$s?", $pre_msg, $productname);
}
else
{
# Single user
$pre_msg=cxgettext("This will remove \%s from your system.", $productname);
$pre_msg=cxgettext("\%1\$s\n\nThis installation of CrossOver includes Windows applications and data contained in one or more bottles. Do you want to delete these bottles in addition to uninstalling \%2\$s?", $pre_msg, $productname);
}
$post_msg=cxgettext("\%s has been removed.", $productname);
$err_msg=cxgettext("An error occurred while uninstalling \%s.", $productname);
my $btn_delete=cxgettext("Uninstall and delete bottles");
my $btn_keep=cxgettext("Uninstall but keep bottles");
$button_list="$btn_delete:0,$btn_cancel:102,$btn_keep:101";
}
else
{
$mode="user";
$pre_msg=cxgettext("This operation will remove all files created by \%s in your account.", $productname);
$pre_msg=cxgettext("\%s\nYou will still be able to use Windows applications that were installed system-wide, if any. But all the Windows applications you installed as well as the C: drives contained in your bottles will be deleted.", $pre_msg);
$post_msg=cxgettext("\%1\$s has been removed from your account.\nTo fully uninstall it, run '\%2\$s' as root.",
$productname, "$ENV{CX_ROOT}/bin/cxuninstall");
$err_msg=cxgettext("An error occurred while removing the \%s files.", $productname);
$button_list="$btn_cancel:102,$btn_uninstall_user:0";
}
if (!$opt_force)
{
# Warn the user
my $rc=cxmessage(
"-title", "\%s Uninstall", "-title", "$productname",
"-buttons", $button_list,
"-default", $btn_cancel,
"-image", "cxuninstall",
"-wm-class", "cxuninstall",
"\%s", $pre_msg);
if ($rc == 0)
{
$ENV{CX_PURGE}="all";
}
elsif ($rc == 256*101)
{
$ENV{CX_PURGE}="desktopdata";
}
else
{
if ($rc != 256*102 and $rc != 256*252)
{
# The user did not hit Cancel or close the window
my $name0=cxname0();
cxwarn("cxmessage died. You may want to try '$name0 --help' for alternatives\n");
}
exit 1;
}
}
elsif (!defined $ENV{CX_PURGE})
{
$ENV{CX_PURGE}=($button_list =~ /:101/ ? "desktopdata" : "all");
}
# Uninstall
sub stop_applications($)
{
my ($scopes)=@_;
my ($failed, $wait_pid);
foreach my $scope (@$scopes)
{
my $path=($scope eq "private" ? $ENV{CX_BOTTLE_PATH} : $ENV{CX_MANAGED_BOTTLE_PATH});
foreach my $dir (split /:+/, $path)
{
next if ($dir eq "");
my $dh;
next if (!opendir($dh, $dir));
foreach my $dentry (readdir $dh)
{
next if ($dentry =~ /^(?:\.\.?|default)$/);
next if (!-f "$dir/$dentry/cxbottle.conf");
if (cxsystem("$ENV{CX_ROOT}/bin/wine", "--bottle", $dentry, "--scope", $scope, "--no-update", "--ux-app", "wineserver", "-k0") == 0)
{
# Ask the running Windows applications to shut down
$wait_pid=cxwait(0, "--no-focus", "Shutting down the Windows applications.");
$failed=cxsystem("$ENV{CX_ROOT}/bin/wine",
"--bottle", $dentry, "--scope", $scope,
"--no-update", "--wl-app", "wineboot.exe",
"--", "--end-session", "--shutdown",
"--force", "--kill");
last if ($failed);
}
}
closedir($dh);
}
}
kill(15, $wait_pid) if (defined $wait_pid);
if ($failed)
{
cxmessage("Aborting the uninstallation because some Windows applications could not be stopped.\n");
return 0;
}
return 1;
}
# Uninstall
sub uninstall($)
{
my ($cxconfig)=@_;
my $pkginfo=$cxconfig->get("CrossOver", "ProductPackage", "");
my ($pkgtype, $pkgname)=split /:/, $pkginfo;
if (($pkgtype || "mojo") eq "mojo" and
-d "$ENV{CX_ROOT}/.mojosetup" and -f "$ENV{CX_ROOT}/.uninstall")
{
# This is a MojoSetup package
cxlog("Starting the MojoSetup uninstaller\n");
my $mojo_uninstaller="uninstall-$builtin_productid.sh";
if (!rename("$ENV{CX_ROOT}/.uninstall", "$ENV{CX_ROOT}/$mojo_uninstaller"))
{
cxerr("unable to rename '.uninstall' to '$mojo_uninstaller'\n");
return (1, undef, 1);
}
if (CXLog::is_on())
{
$ENV{MOJOSETUP_LOG}=$ENV{CX_LOG} if (!defined $ENV{MOJOSETUP_LOG});
$ENV{MOJOSETUP_LOGLEVEL}="all" if (!defined $ENV{MOJOSETUP_LOGLEVEL});
}
my $rc=cxsystem("$ENV{CX_ROOT}/$mojo_uninstaller", "--force");
if ($rc != 0)
{
rename("$ENV{CX_ROOT}/$mojo_uninstaller", "$ENV{CX_ROOT}/.uninstall");
return (1, undef, 0);
}
return (0, undef, 0);
}
# The other methods are not interactive
my $wait_pid=cxwait(0, "--no-focus", "Uninstalling");
if ($pkgtype eq "rpm" and $pkgname)
{
return (cxsystem("rpm", "-e", $pkgname), $wait_pid, 1);
}
if ($pkgtype eq "deb" and $pkgname)
{
return (cxsystem("dpkg", ($ENV{CX_PURGE} eq "desktopdata" ? "--remove" : "--purge"), $pkgname), $wait_pid, 1);
}
if ($pkgtype eq "sunpkg" and $pkgname)
{
my $cxpurge="$ENV{CX_ROOT}/cxpurge";
my $fh;
if (!open($fh, ">", $cxpurge))
{
cxerr("unable to open '$cxpurge' for writing: $!\n");
return (1, $wait_pid, 1);
Loading ...