Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

beebox / crossover   deb

Repository URL to install this package:

/ opt / cxoffice / bin / cxreportusage

#!/usr/bin/perl
# (c) Copyright 2009, 2010. CodeWeavers, Inc.
use warnings;
use strict;


# 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;


# Process command-line options
my $opt_verbose;
my $opt_dry_run;
my $opt_help;
require CXOpts;
my $cxopts=CXOpts->new();
$cxopts->add_options(["dry-run!"      => \$opt_dry_run,
                      "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;
}

# Print usage
if (defined $usage)
{
    my $name0=cxname0();
    if ($usage)
    {
        cxerr("try '$name0 --help' for more information\n");
        exit $usage;
    }
    print "Usage: $name0 [--dry-run] [--verbose] [--help]\n";

    print "\n";
    print "Sends statistics to CodeWeavers.\n";

    print "\n";
    print "Options:\n";
    print "  --dry-run       Don't modify the usage log or send any data\n";
    print "  --verbose       Output more information about what is going on\n";
    print "  --help, -h      Shows this help message\n";
    exit 0;
}

sub get_os_distro()
{
    my $platform_info = "";
    my $fh;
    if (open($fh, "-|", "cat /etc/*-release"))
    {
        $platform_info = join"",<$fh>;
        close($fh);
    }
    return $platform_info;
}

# First lock the file to avoid races
require CXBottle;
my ($cxrwconfig, $lock)=CXBottle::lock_and_get_rw_config();

sub release_lock()
{
    CXUtils::cxunlock($lock);

    # $cxrwconfig must really not be reused once the lock has been released,
    # otherwise we will have races with non-cxreportusage scripts.
    $cxrwconfig=undef;
}

my $user_dir=CXBottle::get_user_dir();
if (!$user_dir)
{
    cxerr("unable to get the user directory\n");
    exit 1;
}
my $log="$user_dir/usage.log";
my $tosend="$log.tosend";

# Don't read settings from cxrwconfig. See CXBottle::lock_and_get_rw_config()
my $cxconfig=CXBottle::get_crossover_config();
if (!$cxconfig->get("CrossOver", "ReportWineUsage"))
{
    # The user changed his mind and does not want the reporting anymore.
    # So delete the usage log.
    if ($opt_dry_run)
    {
        print "Reporting has been disabled so the log will be deleted and nothing will be sent\n";
    }
    else
    {
        unlink $log;
        unlink $tosend;
    }
    release_lock();
    exit 0;
}

my $now=time();
my $next_report=$cxconfig->get("CrossOver", "NextUsageReportDate", "0");
$next_report =~ s/^0*(?=.)//;          # treat as a decimal integer
my $report=eval "$now > $next_report"; # catch integer overflows, etc.
$report=1 if ($@);
if (!$report)
{
    # Probably this script is already running -- we don't want to do it twice.
    release_lock();
    exit 0;
}

# Report more often in trial mode because some trial users may not 
# even use it a full week - if there are broken things during that first 
# week, it's still useful to know what so we can fix it.
if (! CXUtils::license_file_present())
{
    my $cxconfig=CXBottle::get_crossover_config();
    my $firstUsageReportDate = $cxconfig->get("CrossOver", "FirstUsageReportDate", 0);
    if (! $firstUsageReportDate)
    {
        $cxrwconfig->set("CrossOver", "FirstUsageReportDate", $now);
    }
    elsif( ($firstUsageReportDate + 60*60) > $now )
    {
        # Every 10 min for the first hour, in case someone only uses
        # the trial for a very brief period.
        $cxrwconfig->set("CrossOver", "NextUsageReportDate", $now + 10*60);
    }
    elsif( ($firstUsageReportDate + 24*60*60) > $now )
    {
        # Once per hour the first day.
        $cxrwconfig->set("CrossOver", "NextUsageReportDate", $now + 60*60);
    }
    elsif( ($firstUsageReportDate + 15*24*60*60) > $now )
    {
        # Once per day until the end fo the demo period.
        $cxrwconfig->set("CrossOver", "NextUsageReportDate", $now + 24*60*60);
    }
    else
    {
        # Shouldn't really get here ...
        $cxrwconfig->set("CrossOver", "NextUsageReportDate", $now + 7*24*60*60);
    }
}
else
{
    # For now, we will mark the date of the next report a week in the future.
    # If this reporting fails, we'll reschedule for tomorrow instead.
    $cxrwconfig->set("CrossOver", "NextUsageReportDate", $now + 7*24*60*60);
}

my $anonymousid=$cxconfig->get("CrossOver", "AnonymousId");
if (!$anonymousid)
{
    $anonymousid=CXUtils::get_unique_id($ENV{CX_ROOT});
    $cxrwconfig->set("CrossOver", "AnonymousId", $anonymousid);
}
if (!$opt_dry_run and !$cxrwconfig->save())
{
    cxwarn("unable to save '", $cxrwconfig->get_filename(), "': $!\n");
}

# We can release the lock now: sending the log may take us some time but we
# have one week before another process tries to send this log.
release_lock();

# 'Rotate' the log
if (!-f $tosend)
{
    if ($opt_dry_run)
    {
        $tosend=$log;
    }
    elsif (-f $log and !rename($log, $tosend))
    {
        cxerr("unable to rename '$log': $!\n");
        exit 1;
    }
    if (!-f $tosend)
    {
        # Presumably no application was run since last time.
        print "Nothing to send\n" if ($opt_dry_run);
        exit 0;
    }
}

if (open(my $fh, "<", $tosend))
{
    require CXXMLDOM;
    my $err=CXXMLDOM::get_xml_load_error();
    if ($err)
    {
        cxerr("unable to use XML::DOM:\n$err\n");
        exit 1;
    }
    my $doc=CXXMLDOM::parse_xml_string("<wineusage version=\"2\"></wineusage>");
    my $xml=$doc->getFirstChild();

    while (my $line=<$fh>)
    {
        if ($line =~ m!^exit ([0-9a-fA-F]+) ([0-9]+) ([0-9]+) (.*)[\\/]([^\\/]+) (.*)\n$!)
        {
            my ($exitcode, $duration, $bottleid, $path, $exe, $appid)=($1, $2, $3, $4, $5, $6);
            if ($exe !~ /^(?:explorer|regedit|services|uninstaller|winedevice|winepath|winewrapper)\.exe$/i)
            {
                my $run=CXXMLDOM::add_new_element($xml, "run");
                CXXMLDOM::add_new_element($run, "bottle", $bottleid);
                CXXMLDOM::add_new_element($run, "exe", $exe);
                CXXMLDOM::add_new_element($run, "path", $path);
                CXXMLDOM::add_new_element($run, "exitcode", $exitcode);
                CXXMLDOM::add_new_element($run, "duration", $duration);
                CXXMLDOM::add_new_element($run, "appid", $appid);
            }
        }
        elsif ($line =~ m!^install ([0-9a-zA-Z\.]+) ([0-9]+) ([0-9]+)\n$!)
        {
            my $install=CXXMLDOM::add_new_element($xml, "install");
            my ($appid, $cancelled, $installfailure)=($1, $2, $3);
            CXXMLDOM::add_new_element($install, "appid", $appid);
            CXXMLDOM::add_new_element($install, "cancelled", $cancelled);
            CXXMLDOM::add_new_element($install, "failluredetected", $installfailure);
        }
        else
        {
            cxlog("unknown line format: $line");
        }
    }
    close($fh);

    my $pos=$xml->getFirstChild();
    if ($pos)
    {
        # We found application runs to report
        CXXMLDOM::add_new_element($xml, "date", time(), $pos);
        CXXMLDOM::add_new_element($xml, "product", CXUtils::get_builtin_product_id(), $pos);
        CXXMLDOM::add_new_element($xml, "cxversion", CXUtils::get_product_version(), $pos);
        CXXMLDOM::add_new_element($xml, "platform", "Linux", $pos);
        CXXMLDOM::add_new_element($xml, "osversion", get_os_distro(), $pos);
        CXXMLDOM::add_new_element($xml, "anonymousid", $anonymousid, $pos);
        CXXMLDOM::add_new_element($xml, "bottlecount", CXBottle::count_user_bottles(), $pos);
        # This doesn't check the validity of the license file, 
        # just uses the file's presence as a proxy for whether 
        # we are in trial mode (so we can learn what programs may fail
        # during trial periods, and / or how many users experience 
        # failures during trial and give up).
        my $licensefilepresent = CXUtils::license_file_present() ? "yes" : "no";
        CXXMLDOM::add_new_element($xml, "trialmodelikely", $licensefilepresent, $pos);
        
        my $xml_pi=$doc->createXMLDecl("1.0", "UTF-8", "yes");
        my $xml_string=join("", $xml_pi->toString(), "\n", $doc->toString());

        if ($opt_dry_run)
        {
            print $xml_string;
Loading ...