aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Youri/Check/Input/Updates.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Youri/Check/Input/Updates.pm')
-rw-r--r--lib/Youri/Check/Input/Updates.pm275
1 files changed, 275 insertions, 0 deletions
diff --git a/lib/Youri/Check/Input/Updates.pm b/lib/Youri/Check/Input/Updates.pm
new file mode 100644
index 0000000..a61ce5e
--- /dev/null
+++ b/lib/Youri/Check/Input/Updates.pm
@@ -0,0 +1,275 @@
+# $Id: Updates.pm 1179 2006-08-05 08:30:57Z warly $
+package Youri::Check::Input::Updates;
+
+=head1 NAME
+
+Youri::Check::Input::Updates - Check available updates
+
+=head1 DESCRIPTION
+
+This plugin checks available updates for packages, and report existing ones.
+Additional source plugins handle specific sources.
+
+=cut
+
+use warnings;
+use strict;
+use Carp;
+use Memoize;
+use Youri::Utils;
+use base 'Youri::Check::Input';
+
+sub columns {
+ return qw/
+ current
+ available
+ source
+ /;
+}
+
+sub links {
+ return qw/
+ source url
+ /;
+}
+
+memoize('is_newer');
+
+our $VERSION_REGEXP = 'v?([\d._-]*\d)[._ -]*(?:(alpha|beta|pre|rc|pl|rev|cvs|svn|[a-z])[_ -.]*([\d.]*))?([_ -.]*.*)';
+
+=head2 new(%args)
+
+Creates and returns a new Youri::Check::Input::Updates object.
+
+Specific parameters:
+
+=over
+
+=item aliases $aliases
+
+Hash of global aliases definitions
+
+=item sources $sources
+
+Hash of source plugins definitions
+
+=back
+
+=cut
+
+sub _init {
+ my $self = shift;
+ my %options = (
+ aliases => undef,
+ sources => undef,
+ @_
+ );
+
+ croak "No source defined" unless $options{sources};
+ croak "sources should be an hashref" unless ref $options{sources} eq 'HASH';
+ if ($options{aliases}) {
+ croak "aliases should be an hashref" unless ref $options{aliases} eq 'HASH';
+ }
+
+ foreach my $id (keys %{$options{sources}}) {
+ print "Creating source $id\n" if $options{verbose};
+ eval {
+ # add global aliases if defined
+ if ($options{aliases}) {
+ foreach my $alias (keys %{$options{aliases}}) {
+ $options{sources}->{$id}->{aliases}->{$alias} =
+ $options{aliases}->{$alias}
+ }
+ }
+
+ push(
+ @{$self->{_sources}},
+ create_instance(
+ 'Youri::Check::Input::Updates::Source',
+ id => $id,
+ test => $options{test},
+ verbose => $options{verbose},
+ check_id => $options{id},
+ resolver => $options{resolver},
+ preferences => $options{preferences},
+ %{$options{sources}->{$id}}
+ )
+ );
+ };
+ print STDERR "Failed to create source $id: $@\n" if $@;
+ }
+
+ croak "no sources created" unless @{$self->{_sources}};
+}
+
+sub run {
+ my ($self, $media, $resultset) = @_;
+ croak "Not a class method" unless ref $self;
+
+ # this is a source media check only
+ return unless $media->get_type() eq 'source';
+
+ my $callback = sub {
+ my ($package) = @_;
+
+ my $name = $package->get_name();
+ my $version = $package->get_version();
+ my $release = $package->get_release();
+
+ # compute version with rpm subtilities related to preversions
+ my $current_version = ($release =~ /^0\.(\w+)\.\w+$/) ?
+ $version . $1 :
+ $version;
+ my $current_stable = is_stable($current_version);
+
+ my ($max_version, $max_source, $max_url);
+ $max_version = $current_version;
+
+ foreach my $source (@{$self->{_sources}}) {
+ my $available_version = $source->get_version($package);
+ if (
+ $available_version &&
+ (! $current_stable || is_stable($available_version)) &&
+ is_newer($available_version, $max_version)
+ ) {
+ $max_version = $available_version;
+ $max_source = $source->get_id();
+ $max_url = $source->get_url($name);
+ }
+ }
+ $resultset->add_result($self->{_id}, $media, $package, {
+ current => $current_version,
+ available => $max_version,
+ source => $max_source,
+ url => $max_url
+ }) if $max_version ne $current_version;
+ };
+
+ $media->traverse_headers($callback);
+}
+
+=head2 is_stable($version)
+
+Checks if given version is stable.
+
+=cut
+
+sub is_stable {
+ my ($version) = @_;
+ return $version !~ /alpha|beta|pre|rc|cvs|svn/i;
+
+}
+
+=head2 is_newer($v1, $v2)
+
+Checks if $v1 is newer than $v2.
+
+This function will return true only if we are sure this is newer (and not equal).
+If we can't compare the versions, a warning will be displayed.
+
+=cut
+
+sub is_newer {
+ my ($v1, $v2) = @_;
+ return 0 if $v1 eq $v2;
+
+ # Reject strange cases
+ # One is a large number (like date or revision) and the other one not, or
+ # has different length
+ if (($v1 =~ /^\d{3,}$/ || $v2 =~ /^\d{3,}$/)
+ && (join('0',split(/\d/, $v1."X")) ne join('0',split(/\d/, $v2."X")))) {
+ carp "strange : $v1 vs $v2";
+ return 0;
+ }
+
+ my %states = (alpha=>-4,beta=>-3,pre=>-2,rc=>-1);
+ my $i; $states{$_} = ++$i foreach 'a'..'z';
+
+ if ($v1 =~ /^[\d._-]+$/ && $v2 =~ /^[\d._-]+$/) {
+ my @v1 = split(/[._-]/, $v1);
+ my @v2 = split(/[._-]/, $v2);
+ if (join('',@v1) eq (join '',@v2)) {
+ # Might be something like 1.2.0 vs 1.20, usual false positive
+ carp "strange : $v1 vs $v2";
+ return 0;
+ }
+ for my $i (0 .. $#v1) {
+ $v1[$i] ||= 0;
+ $v2[$i] ||= 0;
+ return 1 if $v1[$i] > $v2[$i];
+ return 0 if $v1[$i] < $v2[$i];
+ }
+ # When v2 is longer than v1 but start the same, v1 <= v2
+ return 0;
+ } else {
+ my ($num1, $state1, $statenum1, $other1, $num2, $state2, $statenum2, $other2);
+
+ if ($v1 =~ /^$VERSION_REGEXP$/io) {
+ ($num1, $state1, $statenum1, $other1) = ($1, "\L$2", $3, $4);
+ } else {
+ carp "unknown version format $v1";
+ return 0;
+ }
+
+ if ($v2 =~ /^$VERSION_REGEXP$/io) {
+ ($num2, $state2, $statenum2, $other2) = ($1, "\L$2", $3, $4);
+ } else {
+ carp "unknown version format $v2";
+ return 0;
+ }
+
+ # If we know the format of only one, there might be an issue, do nothing
+
+ if (($other1 && ! $other2 )||(!$other1 && $other2 )) {
+ carp "can't compare $v1 vs $v2";
+ return 0;
+ }
+
+ return 1 if is_newer($num1, $num2);
+ return 0 unless $num1 eq $num2;
+
+ # The numeric part is the same but not the end
+
+ if ($state1 eq '') {
+ return 1 if $state2 =~ /^(alpha|beta|pre|rc)/;
+ return 0 if $state2 =~ /^([a-z]|pl)$/;
+ carp "unknown state format $state2";
+ return 0;
+ }
+
+ if ($state2 eq '') {
+ return 0 if $state1 =~ /^(alpha|beta|pre|rc)/;
+ return 1 if $state1 =~ /^([a-z]|pl)$/;
+ carp "unknown state format $state1";
+ return 0;
+ }
+
+ if ($state1 eq $state2) {
+ return 1 if is_newer($statenum1, $statenum2);
+ return 0 unless $statenum1 eq $statenum2;
+ # If everything is the same except this, just compare it
+ # as we have no idea on the format
+ return "$other1" gt "$other2";
+ }
+
+ my $s1 = 0;
+ my $s2 = 0;
+ $s1=$states{$state1} if exists $states{$state1};
+ $s2=$states{$state2} if exists $states{$state2};
+ return $s1>$s2 if ($s1 != 0 && $s2 != 0);
+ return 1 if $s1<0 && $state2 =~ /^([a-z]|pl)$/;
+ return 0 if $s2<0 && $state1 =~ /^([a-z]|pl)$/;
+ carp "unknown case $v1, $v2";
+ return 0;
+ }
+}
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2002-2006, YOURI project
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=cut
+
+1;