From 1be510f9529cb082f802408b472a77d074b394c0 Mon Sep 17 00:00:00 2001 From: Nicolas Vigier Date: Sun, 14 Apr 2013 13:46:12 +0000 Subject: Add zarb MLs html archives --- zarb-ml/mageia-sysadm/2011-January/001513.html | 11447 +++++++++++++++++++++++ 1 file changed, 11447 insertions(+) create mode 100644 zarb-ml/mageia-sysadm/2011-January/001513.html (limited to 'zarb-ml/mageia-sysadm/2011-January/001513.html') diff --git a/zarb-ml/mageia-sysadm/2011-January/001513.html b/zarb-ml/mageia-sysadm/2011-January/001513.html new file mode 100644 index 000000000..46a37a5b0 --- /dev/null +++ b/zarb-ml/mageia-sysadm/2011-January/001513.html @@ -0,0 +1,11447 @@ + + + + [Mageia-sysadm] [210] add mandriva version of youri-core, downloaded from http://svn. mandriva.com/svn/soft/build_system/youri/core/trunk/ at revision 271600 + + + + + + + + + +

[Mageia-sysadm] [210] add mandriva version of youri-core, downloaded from http://svn. mandriva.com/svn/soft/build_system/youri/core/trunk/ at revision 271600

+ root at mageia.org + root at mageia.org +
+ Wed Jan 5 14:23:45 CET 2011 +

+
+ +
Revision: 210
+Author:   boklm
+Date:     2011-01-05 14:23:45 +0100 (Wed, 05 Jan 2011)
+Log Message:
+-----------
+add mandriva version of youri-core, downloaded from http://svn.mandriva.com/svn/soft/build_system/youri/core/trunk/ at revision 271600
+
+Added Paths:
+-----------
+    build_system/mdv-youri-core/
+    build_system/mdv-youri-core/branches/
+    build_system/mdv-youri-core/tags/
+    build_system/mdv-youri-core/trunk/
+    build_system/mdv-youri-core/trunk/ChangeLog
+    build_system/mdv-youri-core/trunk/MANIFEST.SKIP
+    build_system/mdv-youri-core/trunk/Makefile.PL
+    build_system/mdv-youri-core/trunk/README
+    build_system/mdv-youri-core/trunk/TODO
+    build_system/mdv-youri-core/trunk/bin/
+    build_system/mdv-youri-core/trunk/bin/fillbugzilla
+    build_system/mdv-youri-core/trunk/cgi/
+    build_system/mdv-youri-core/trunk/cgi/maintainers.cgi
+    build_system/mdv-youri-core/trunk/etc/
+    build_system/mdv-youri-core/trunk/etc/bash_completion.d/
+    build_system/mdv-youri-core/trunk/etc/bash_completion.d/youri
+    build_system/mdv-youri-core/trunk/etc/check.conf
+    build_system/mdv-youri-core/trunk/etc/upload.conf
+    build_system/mdv-youri-core/trunk/lib/
+    build_system/mdv-youri-core/trunk/lib/Youri/
+    build_system/mdv-youri-core/trunk/lib/Youri/Bugzilla.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Age.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source/Iurt.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source/LBD.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Conflicts.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Dependencies.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/MandrivaConflicts.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Missing.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Orphans.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Rpmlint.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Signature.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/CPAN.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Debian.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Fedora.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Freshmeat.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/GNOME.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Gentoo.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/NetBSD.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/RAA.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Sourceforge.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Input.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Preferences/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Preferences/File.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Preferences.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver/Bugzilla.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver/CGI.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/HTML.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/RSS.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/Text.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format/HTML.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format/Text.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Output.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset/
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset/DBI.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset/Iterator.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Config.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Media/
+    build_system/mdv-youri-core/trunk/lib/Youri/Media/URPM.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Media.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Package/
+    build_system/mdv-youri-core/trunk/lib/Youri/Package/RPM.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Package/RPM4.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Package/Test.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Package/URPM.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Package.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Repository/
+    build_system/mdv-youri-core/trunk/lib/Youri/Repository/Mandriva_upload.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Repository/Mandriva_upload_pre.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Repository/PLF.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Repository.pm
+    build_system/mdv-youri-core/trunk/lib/Youri/Utils.pm
+    build_system/mdv-youri-core/trunk/t/
+    build_system/mdv-youri-core/trunk/t/00distribution.t
+    build_system/mdv-youri-core/trunk/t/cowsay-3.03-11mdv2007.0.noarch.rpm
+    build_system/mdv-youri-core/trunk/t/gpghome/
+    build_system/mdv-youri-core/trunk/t/gpghome/pubring.gpg
+    build_system/mdv-youri-core/trunk/t/gpghome/secring.gpg
+    build_system/mdv-youri-core/trunk/t/gpghome/trustdb.gpg
+    build_system/mdv-youri-core/trunk/t/package.t
+    build_system/mdv-youri-core/trunk/t/version.t
+
+Added: build_system/mdv-youri-core/trunk/ChangeLog
+===================================================================
+--- build_system/mdv-youri-core/trunk/ChangeLog	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/ChangeLog	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,2 @@
++2006-04-23 Guillaume Rousse <guillomovitch at zarb.org> 0.9
++	* initial release
+
+Added: build_system/mdv-youri-core/trunk/MANIFEST.SKIP
+===================================================================
+--- build_system/mdv-youri-core/trunk/MANIFEST.SKIP	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/MANIFEST.SKIP	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,14 @@
++CVS/.*
++\.svn/.*
++^cover_db/
++^blib/
++\.bak$
++\.swp$
++\.tar$
++\.tgz$
++\.tar\.gz$
++\.SKIP$
++~$
++^pm_to_blib$
++^Makefile$
++^Makefile\.old$
+
+Added: build_system/mdv-youri-core/trunk/Makefile.PL
+===================================================================
+--- build_system/mdv-youri-core/trunk/Makefile.PL	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/Makefile.PL	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,31 @@
++# $Id: Makefile.PL 1724 2006-10-17 13:55:27Z warly $
++use ExtUtils::MakeMaker;
++
++WriteMakefile(
++    NAME       => 'youri-core',
++    VERSION    => 0.9,
++    AUTHOR     => 'Youri project <youri at zarb.org>',
++    PREREQ_PM  => {
++        'AppConfig'              => 0,
++        'YAML'                   => 0,
++        'Pod::Simple::HTMLBatch' => 0,
++        'Test::Exception'        => 0,
++        'Exception'              => 0,
++        'RPM4'                   => 0,
++        'URPM'                   => 0
++    }
++);
++
++package MY;
++
++sub top_targets {
++    my ($self) = @_;
++    my $top_targets = $self->SUPER::top_targets(@_);
++    $top_targets =~ s/all :: pure_all manifypods/all :: pure_all manifypods htmlifypods/;
++    $top_targets .= <<'EOF';
++htmlifypods : $(TO_INST_PM)
++	if [ ! -d blib/html ]; then mkdir blib/html; fi
++	perl -MPod::Simple::HTMLBatch -e Pod::Simple::HTMLBatch::go lib blib/html
++EOF
++     return $top_targets;
++}
+
+Added: build_system/mdv-youri-core/trunk/README
+===================================================================
+--- build_system/mdv-youri-core/trunk/README	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/README	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,33 @@
++youri-core
++----------
++
++Youri core libraries.
++
++Description
++-----------
++YOURI stands for "Youri Offers an Upload & Repository Infrastucture". It aims
++to build tools making management of a coherent set of packages easier.
++
++This package provides basic components used by other youri programs.
++
++Installation
++------------
++To install, just use:
++perl Makefile.PL
++make
++make test
++
++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.
++
++Authors
++-------
++Guillaume Rousse <guillomovitch at zarb.org>,
++Pascal Terjan <pterjan at zarb.org>
++Damien Krotkine <dams at zarb.org>
++Olivier Thauvin <nanardon at zarb.org>
++Ville Skytt\xE4 <ville.skytta at iki.fi>
+
+Added: build_system/mdv-youri-core/trunk/TODO
+===================================================================
+--- build_system/mdv-youri-core/trunk/TODO	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/TODO	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,7 @@
++1.0 Goals 
++=========
++
++library:
++- API-based bugzilla interface, instead of SQL-based one
++- more generic check-specific options handling in medias (don't use a
++specific attribute for each of them)
+
+Added: build_system/mdv-youri-core/trunk/bin/fillbugzilla
+===================================================================
+--- build_system/mdv-youri-core/trunk/bin/fillbugzilla	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/bin/fillbugzilla	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,81 @@
++#!/usr/bin/perl
++# fillbugzilla
++# copyright (c) 2002 Guillaume Rousse <guillomovitch at zarb.org>
++# $Id: fillbugzilla 1179 2006-08-05 08:30:57Z warly $
++
++use strict;
++use warnings;
++use Getopt::Long;
++use Bugzilla;
++use Mail::Sendmail;
++
++# constants
++my $name    = "fillbugzilla";
++my $version = "1.0";
++
++# command-line parameters
++my ($base, $user, $pass, $project, $mode, $help);
++GetOptions(
++    "base=s"    => \$base,
++    "user=s"    => \$user,
++    "pass=s"    => \$pass,
++    "mode=s"    => \$mode,
++    "help"      => \$help,
++);
++
++# mandatory argument
++die usage() unless ($base && $user && $pass);
++die usage() unless ($mode eq 'package' || $mode eq 'packager');
++
++usage() && exit 0 if $help;
++
++my $bugzilla = Bugzilla->new('localhost', $base, $user, $pass);
++
++if ($mode eq 'packager') {
++    while (my $packager = <>) {
++            chomp $packager;
++        my ($name, $login) = split(/\t/, $packager);
++
++        # random passwd
++        my @chars = (0..9, 'A'..'Z', 'a'..'z', '-', '_', '!', '@', '#', '$', '%', '^', '&', '*');
++        my $password = join('', map { $chars[rand(scalar @chars)] } 1 .. 8);
++
++        # insert into database
++        $bugzilla->add_packager($name, $login, $password);
++
++        # mail user
++        my %mail = (
++            smtp       => 'localhost',
++            To         => $login,
++            From       => 'bugmaster at zarb.org',
++            Subject    => 'bugzilla password',
++            'X-Mailer' => "$name $version",
++        );
++        $mail{Message} .= "login: $login\n";
++        $mail{Message} .= "password: $password\n";
++        sendmail(%mail) or warn $Mail::Sendmail::error;
++    }
++}
++
++if ($mode eq 'package') {
++    while (my $line = <>) {
++        chomp $line;
++        my ($name, $summary, $version, $maintainer) = split(/\t/, $line);
++        $bugzilla->add_package($name, $summary, $version, $maintainer);
++    }
++}
++
++sub usage {
++    print <<EOF;
++$name $version
++
++Usage:
++$name --base <base> --user <user> --pass <pass> --mode <mode> < $file
++
++Options:
++--base <base> bugzilla base name
++--user <user> bugzilla base user
++--pass <pass> bugzilla base password
++--mode <mode> package or packager
++EOF
++}
+
+
+Property changes on: build_system/mdv-youri-core/trunk/bin/fillbugzilla
+___________________________________________________________________
+Added: svn:executable
+   + *
+
+Added: build_system/mdv-youri-core/trunk/cgi/maintainers.cgi
+===================================================================
+--- build_system/mdv-youri-core/trunk/cgi/maintainers.cgi	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/cgi/maintainers.cgi	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,65 @@
++#!/usr/bin/perl
++# $Id: maintainers.cgi 1179 2006-08-05 08:30:57Z warly $
++
++=head1 NAME
++
++maintainers.cgi - youri CGI interface to maintainers list
++
++=head1 VERSION
++
++Version 1.0
++
++=head1 DESCRIPTION
++
++This script allows to get package maintainers list through CGI interface.
++
++=head1 SYNOPSIS
++
++=head1 COPYRIGHT AND LICENSE
++
++Copyright (C) 2004-2005, YOURI project
++
++This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
++
++=head1 AUTHORS
++
++Guillaume Rousse <guillomovitch at zarb.org>,
++
++=cut
++
++use Youri::Bugzilla;
++use CGI;
++use AppConfig qw/:argcount :expand/;
++use strict;
++use warnings;
++
++my $config = AppConfig->new(
++    {
++        GLOBAL => {
++           DEFAULT  => undef,
++           EXPAND   => EXPAND_ALL,
++           ARGCOUNT => ARGCOUNT_ONE,
++       }
++    },
++    host => { ARGCOUNT => ARGCOUNT_ONE },
++    base => { ARGCOUNT => ARGCOUNT_ONE },
++    user => { ARGCOUNT => ARGCOUNT_ONE },
++    pass => { ARGCOUNT => ARGCOUNT_ONE },
++);
++
++my $home = (getpwnam($ENV{PROJECT}))[7];
++foreach my $file ("/etc/youri/maintainers.conf", "$home/.youri/maintainers.conf") {
++    $config->file($file) if -f $file && -r $file;
++}
++
++my $bugzilla = Bugzilla->new(
++    $config->host(),
++    $config->base(),
++    $config->user(),
++    $config->pass(),
++);
++
++my $cgi = CGI->new();
++print $cgi->header(-type=>'text/plain');
++
++$bugzilla->browse_packages(sub { print "$_[0]\t$_[2]\n"; });
+
+
+Property changes on: build_system/mdv-youri-core/trunk/cgi/maintainers.cgi
+___________________________________________________________________
+Added: svn:executable
+   + *
+
+Added: build_system/mdv-youri-core/trunk/etc/bash_completion.d/youri
+===================================================================
+--- build_system/mdv-youri-core/trunk/etc/bash_completion.d/youri	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/etc/bash_completion.d/youri	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,141 @@
++# youri tools completion
++# $Id$
++
++_youri-check()
++{
++
++    local cur prev config i mode
++
++    COMPREPLY=()
++    cur=${COMP_WORDS[COMP_CWORD]}
++    prev=${COMP_WORDS[COMP_CWORD-1]}
++
++    case "$prev" in
++	--config)
++	    _filedir
++	    return 0
++	    ;;
++	--skip-plugin)
++	    _find_config check.conf
++	    if [ -n "$config" ]; then
++		# try to guess mode
++		for (( i=1; i < COMP_CWORD; i++ )); do
++		    if [[ "${COMP_WORDS[i]}" != -* ]]; then
++			mode=${COMP_WORDS[i]}
++			break
++		    fi
++		done
++
++		if [ -n $mode ]; then
++		    COMPREPLY=( $( awk -F=  '/^'$mode's/ {print $2}' $config \
++			    | grep "^$cur" ) )
++		fi
++	    fi
++	    return 0
++	    ;;
++	--skip-media)
++	    _find_config check.conf
++	    if [ -n "$config" ]; then
++		COMPREPLY=( $( awk -F=  '/^medias/ {print $2}' $config \
++			| grep "^$cur" ) )
++	    fi
++	    return 0
++	    ;;
++    esac
++
++    if [[ "$cur" == -* ]]; then
++	COMPREPLY=( $( compgen -W '--config --skip-plugin --skip-media -h \
++		--help -t --test -v --verbose' -- $cur ) )
++    else
++    	_count_args
++	case $args in
++	    1)
++	    	COMPREPLY=( $( compgen -W 'input output' -- $cur ) )
++		;;
++	esac
++    fi
++
++}
++complete -F _youri-check youri-check
++
++_youri-upload()
++{
++
++    local cur prev config
++
++    COMPREPLY=()
++    cur=${COMP_WORDS[COMP_CWORD]}
++    prev=${COMP_WORDS[COMP_CWORD-1]}
++
++    case "$prev" in
++	--config)
++	    _filedir
++	    return 0
++	    ;;
++	--skip-check)
++	    _find_config upload.conf
++	    if [ -n "$config" ]; then
++		COMPREPLY=( $( awk -F=  '/^checks/ {print $2}' $config \
++		    | grep "^$cur" ) )
++	    fi
++	    return 0
++	    ;;
++	--skip-action)
++	    _find_config upload.conf
++	    if [ -n "$config" ]; then
++		COMPREPLY=( $( awk -F=  '/^actions/ {print $2}' $config \
++		    | grep "^$cur" ) )
++	    fi
++	    return 0
++	    ;;
++    esac
++
++    if [[ "$cur" == -* ]]; then
++	COMPREPLY=( $( compgen -W '--config --skip-check --skip-action \
++	    --define -h --help -t --test -v --verbose' -- $cur ) )
++    else
++    	_count_args
++	case $args in
++	    1)
++		_find_config upload.conf
++		if [ -n "$config" ]; then
++		    COMPREPLY=( $( awk -F=  '/^targets/ {print $2}' $config \
++			| grep "^$cur" ) )
++		fi
++		;;
++	    *)
++		_filedir
++		;;
++	esac
++    fi
++
++}
++complete -F _youri-upload youri-upload
++
++_find_config()
++{
++    local name i
++
++    name=$1
++
++    for (( i=1; i < COMP_CWORD; i++ )); do
++	if [[ "${COMP_WORDS[i]}" == --config ]]; then
++	    config=${COMP_WORDS[i+1]}
++	    break
++	fi
++    done
++    if [ -f "$config" ]; then
++	return 0
++    fi
++
++    if [ -f "$HOME/.youri/$name" ]; then
++	config=$HOME/.youri/$name
++	return 0
++    fi
++
++    if [ -f "/etc/youri/$name" ]; then
++	config=/etc/youri/$name
++	return 0
++    fi
++
++}
+
+Added: build_system/mdv-youri-core/trunk/etc/check.conf
+===================================================================
+--- build_system/mdv-youri-core/trunk/etc/check.conf	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/etc/check.conf	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,300 @@
++# youri-check sample configuration file
++# $Id: check.conf 1179 2006-08-05 08:30:57Z warly $
++
++# resolver declaration
++resolver = cgi
++
++# preferences declaration
++preferences = file_pref
++
++# resultset declaration
++resultset = dbi
++
++# input plugins declaration
++inputs = rpmlint \
++         age \
++         updates \
++         build \
++         conflicts \
++         dependencies \
++         missing \
++         orphans
++
++# output plugins declaration
++outputs = file mail
++
++# medias declaration
++medias = main.i586 \
++	 main.x86_64 \
++	 main.sources \
++	 contrib.i586 \
++	 contrib.x86_64 \
++	 contrib.sources \
++	 free \
++	 non-free \
++	 free.sources \
++	 non-free.sources
++
++# helper variables
++mirror        = ftp://ftp.free.fr/pub/Distributions_Linux/Mandrakelinux/devel/cooker
++mirror_i586   = $mirror/i586/media
++mirror_x86_64 = $mirror/x86_64/media
++
++# resolver definition
++[cgi]
++class = Youri::Check::Maintainer::Resolver::CGI
++url   = http://plf.zarb.org/cgi-bin/maintainers.cgi
++
++# preferences definition
++[file_pref]
++class = Youri::Check::Maintainer::Preferences::File
++
++# resultset definition
++[dbi]
++class  = Youri::Check::Resultset::DBI
++driver = mysql
++host   = localhost
++base   = plf_youri
++user   = plf
++pass   = s3kr3t
++
++# checks definitions
++[updates]
++class    = Youri::Check::Input::Updates
++aliases  = <<EOF
++--- #YAML:1.0
++libfame0.8: ~
++EOF
++sources = <<EOF
++--- #YAML:1.0
++debian:
++    class: Youri::Check::Input::Updates::Source::Debian
++    aliases:
++        fuse-emulator: ~
++cpan:
++    class: Youri::Check::Input::Updates::Source::CPAN
++fedora:
++    class: Youri::Check::Input::Updates::Source::Fedora
++gentoo:
++    class: Youri::Check::Input::Updates::Source::Gentoo
++freshmeat:
++    class: Youri::Check::Input::Updates::Source::Freshmeat
++netbsd:
++    class: Youri::Check::Input::Updates::Source::NetBSD
++raa:
++    class: Youri::Check::Input::Updates::Source::RAA
++sourceforge:
++    class: Youri::Check::Input::Updates::Source::Sourceforge
++    aliases:
++        openquicktime: ~
++        klibido: ~
++EOF
++
++[rpmlint]
++class  = Youri::Check::Input::Rpmlint
++
++[age]
++class   = Youri::Check::Input::Age
++max_age = 12 months
++pattern = %m months
++
++[dependencies]
++class = Youri::Check::Input::Dependencies
++
++[conflicts]
++class = Youri::Check::Input::Conflicts
++
++[build]
++class    = Youri::Check::Input::Build
++sources = <<EOF
++--- #YAML:1.0
++stefan:
++    class: Youri::Check::Input::Build::Source::LBD
++    url: http://eijk.homelinux.org/build/
++    medias:
++        - cooker_plf-free
++        - cooker_plf-non-free
++    archs:
++        - i586
++EOF
++
++[missing]
++class    = Youri::Check::Input::Missing
++
++[orphans]
++class    = Youri::Check::Input::Orphans
++
++# reports definitions
++[file]
++class      = Youri::Check::Output::File
++to         = ${HOME}/www/qa
++global     = 1
++individual = 1
++formats    = <<EOF
++--- #YAML:1.0
++html:
++    class: Youri::Check::Output::File::Format::HTML
++text:
++    class: Youri::Check::Output::File::Format::Text
++rss:
++    class: Youri::Check::Output::File::Format::RSS
++EOF
++
++[mail]
++class      = Youri::Check::Output::Mail
++mta        = /usr/sbin/sendmail
++to         = plf-admin at zarb.org
++from       = plf at zarb.org
++reply_to   = plf-admin at zarb.org
++formats    = <<EOF
++--- #YAML:1.0
++text:
++    class: Youri::Check::Output::Mail::Format::Text
++EOF
++
++# media definitions
++[main.i586]
++class       = Youri::Media::URPM
++name        = main
++type        = binary
++path        = $mirror_i586/main
++hdlist      = $mirror_i586/media_info/hdlist_main.cz
++skip_inputs = <<EOF
++--- #YAML:1.0
++- all
++EOF
++
++[main.x86_64]
++class       = Youri::Media::URPM
++name        = main
++type        = binary
++path        = $mirror_x86_64/main
++hdlist      = $mirror_x86_64/media_info/hdlist_main.cz
++skip_inputs = <<EOF
++--- #YAML:1.0
++- all
++EOF
++
++[main.sources]
++class       = Youri::Media::URPM
++name        = main
++type        = source
++path        = $mirror_i586/main
++hdlist      = $mirror_i586/media_info/hdlist_main.src.cz
++skip_inputs = <<EOF
++--- #YAML:1.0
++- all
++EOF
++
++[contrib.i586]
++class       = Youri::Media::URPM
++name        = contrib
++type        = binary
++path        = $mirror_i586/contrib
++hdlist      = $mirror_i586/media_info/hdlist_contrib.cz
++skip_inputs = <<EOF
++--- #YAML:1.0
++- all
++EOF
++
++[contrib.x86_64]
++class       = Youri::Media::URPM
++name        = contrib
++type        = binary
++path        = $mirror_x86_64/contrib
++hdlist      = $mirror_x86_64/media_info/hdlist_contrib.cz
++skip_inputs = <<EOF
++--- #YAML:1.0
++- all
++EOF
++
++[contrib.sources]
++class       = Youri::Media::URPM
++name        = contrib
++type        = source
++path        = $mirror_i586/contrib
++hdlist      = $mirror_i586/media_info/hdlist_contrib.src.cz
++skip_inputs = <<EOF
++--- #YAML:1.0
++- all
++EOF
++
++[free]
++class          = Youri::Media::URPM
++name           = free
++type           = binary
++path           = ${HOME}/ftp/mandrake/free/cooker/i586
++hdlist         = ${HOME}/ftp/mandrake/free/cooker/i586/hdlist.cz
++rpmlint_config = ${HOME}/etc/rpmlint-free.conf
++allow_deps     = <<EOF
++--- #YAML:1.0
++- main.i586
++- main.x86_64
++- contrib.i586
++- contrib.x86_64
++- free
++EOF
++allow_srcs      = <<EOF
++--- #YAML:1.0
++- free.sources
++- main.sources
++- contrib.sources
++EOF
++skip_archs     = <<EOF
++--- #YAML:1.0
++- ppc
++EOF
++
++[free.sources]
++class          = Youri::Media::URPM
++name           = free
++type           = source
++path           = ${HOME}/ftp/mandrake/free/src
++hdlist         = ${HOME}/ftp/mandrake/free/src/hdlist.cz
++rpmlint_config = ${HOME}/etc/rpmlint-free.conf
++allow_deps     = <<EOF
++--- #YAML:1.0
++- main.i586
++- contrib.i586
++- free
++EOF
++
++[non-free]
++class          = Youri::Media::URPM
++name           = non-free
++type           = binary
++path           = ${HOME}/ftp/mandrake/non-free/cooker/i586
++hdlist         = ${HOME}/ftp/mandrake/non-free/cooker/i586/hdlist.cz
++rpmlint_config = ${HOME}/etc/rpmlint-non-free.conf
++allow_deps     = <<EOF
++--- #YAML:1.0
++- main.i586
++- main.x86_64
++- contrib.i586
++- contrib.x86_64
++- free
++- non-free
++EOF
++allow_srcs      = <<EOF
++--- #YAML:1.0
++- non-free.sources
++EOF
++skip_archs     = <<EOF
++--- #YAML:1.0
++- ppc
++EOF
++
++[non-free.sources]
++class          = Youri::Media::URPM
++name           = non-free
++type           = source
++path           = ${HOME}/ftp/mandrake/non-free/src
++hdlist         = ${HOME}/ftp/mandrake/non-free/src/hdlist.cz
++rpmlint_config = ${HOME}/etc/rpmlint-non-free.conf
++allow_deps     = <<EOF
++--- #YAML:1.0
++- main.i586
++- contrib.i586
++- free
++- non-free
++EOF
+
+Added: build_system/mdv-youri-core/trunk/etc/upload.conf
+===================================================================
+--- build_system/mdv-youri-core/trunk/etc/upload.conf	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/etc/upload.conf	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,139 @@
++# youri-upload sample configuration file
++# $Id: upload.conf 1179 2006-08-05 08:30:57Z warly $
++
++# repository declaration
++repository = plf
++
++# targets declaration
++targets = cooker 2006.0
++
++# repository definition
++[plf]
++class        = Youri::Repository::PLF
++install_root = ${HOME}/ftp/mandriva
++version_root = ${HOME}/cvs
++archive_root = ${HOME}/backup/mandriva
++noarch       = i586
++
++# targets definition
++[cooker]
++checks = <<EOF
++--- #YAML:1.0
++- tag
++- recency
++- history
++EOF
++actions = <<EOF
++--- #YAML:1.0
++- sign
++- install
++- link
++- archive
++- clean
++- bugzilla
++- cvs
++- mail
++- rss
++EOF
++
++[2006.0]
++checks = <<EOF
++--- #YAML:1.0
++- type
++- tag
++- recency
++- history
++- precedence
++EOF
++actions = <<EOF
++--- #YAML:1.0
++- sign
++- install
++- link
++- archive
++- clean
++EOF
++
++# checks definition
++[tag]
++class        = Youri::Upload::Check::Tag
++tags = <<EOF
++--- #YAML:1.0
++release: 'plf$'
++packager: '<\w+ at zarb\.org>$'
++distribution: '^Mandriva Linux$'
++vendor: '^Penguin Liberation Front$'
++EOF
++
++[recency]
++class = Youri::Upload::Check::Recency
++
++[history]
++class = Youri::Upload::Check::History
++
++[precedence]
++class  = Youri::Upload::Check::Precedence
++target = cooker
++
++[type]
++class = Youri::Upload::Check::Type
++type  = binary
++
++# actions definitions
++[sign]
++class      = Youri::Upload::Action::Sign
++name       = plf at zarb.org
++path       = ${HOME}/.gnupg
++passphrase = s3kr3t
++
++[install]
++class = Youri::Upload::Action::Install
++
++[link]
++class = Youri::Upload::Action::Link
++
++[archive]
++class = Youri::Upload::Action::Archive
++
++[clean]
++class = Youri::Upload::Action::Clean
++
++[mail]
++class    = Youri::Upload::Action::Mail
++mta      = /usr/sbin/sendmail
++to       = plf-announce at zarb.org
++reply_to = plf-discuss at zarb.org
++from     = plf at zarb.org
++prefix   = RPM
++cc       = <<EOF
++--- #YAML:1.0
++hot-base: david at dindinx.org bellamy at neverland.net
++dcgui: mathen at ketelhot.de
++dclib: mathen at ketelhot.de
++Video-DVDRip: dvdrip-users at exit1.org
++hackVideo-DVDRip: dvdrip-users at exit1.org
++goosnes: tak at bard.sytes.net
++avidemux: fixounet at free.fr
++vobcopy: robos at muon.de
++drip: drip-devel at lists.sourceforge.net
++libdscaler: vektor at dumbterm.net
++xawdecode: pingus77 at ifrance.com
++EOF
++
++[rss]
++class       = Youri::Upload::Action::RSS
++file        = ${HOME}/www/changelog.rss
++title       = PLF packages updates
++link        = http://plf.zarb.org/
++description = ChangeLog for PLF packages
++
++[cvs]
++class = Youri::Upload::Action::CVS
++
++[bugzilla]
++class   = Youri::Upload::Action::Bugzilla
++host    = localhost
++base    = plf_bugs
++user    = plf
++pass    = s3kr3t
++contact = plf at zarb.org
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Bugzilla.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Bugzilla.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Bugzilla.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,482 @@
++# $Id: Bugzilla.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Bugzilla;
++
++=head1 NAME
++
++Youri::Bugzilla - Youri Bugzilla interface
++
++=head1 SYNOPSIS
++
++    use Youri::Bugzilla;
++
++    my $bugzilla = Youri::Bugzilla->new($host, $base, $user, $pass);
++
++    print $bugzilla->get_maintainer('foobar');
++
++=head1 DESCRIPTION
++
++This module implement a database-level Bugzilla interface for managing packages.
++
++The legacy Bugzilla database model is mapped this way:
++
++=over
++
++=item *
++
++a maintainer is a user
++
++=item *
++
++a package is a product
++
++=item *
++
++each package has two pseudo components "program" and "package", owned by the package maintainer
++
++=back
++
++=cut
++
++use DBI;
++use Carp;
++use strict;
++use warnings;
++
++my %queries = (
++    get_package_id    => 'SELECT id FROM products WHERE name = ?',
++    get_maintainer_id => 'SELECT userid FROM profiles WHERE login_name = ?',
++    get_versions      => 'SELECT value FROM versions WHERE product_id = ?',
++    get_components    => 'SELECT name FROM components WHERE product_id = ?',
++    add_package       => 'INSERT INTO products (name, description) VALUES (?, ?)',
++    add_maintainer    => 'INSERT INTO profiles (login_name, cryptpassword, realname, emailflags, refreshed_when) VALUES (?, ENCRYPT(?), ?, ?, SYSDATE())',
++    add_component     => 'INSERT INTO components (product_id, name, description,initialowner, initialqacontact) VALUES (?, ?, ?, ?, ?)',
++    add_version       => 'INSERT INTO versions (product_id, value) VALUES (?, ?)',
++    del_package       => 'DELETE FROM products WHERE product = ?',
++    del_maintainer    => 'DELETE FROM profiles WHERE login_name = ?',
++    del_components    => 'DELETE FROM components WHERE program = ?',
++    del_versions      => 'DELETE FROM versions WHERE program = ?',
++    reset_password    => 'UPDATE profiles SET cryptpassword = ENCRYPT(?) WHERE login_name = ?',
++    browse_packages => <<EOF,
++SELECT products.name, max(versions.value), login_name
++FROM products, versions, profiles, components
++WHERE versions.product_id = products.id
++    AND components.product_id = products.id
++    AND profiles.userid = components.initialowner
++    AND components.name = 'package'
++GROUP BY name
++EOF
++    get_maintainer => <<EOF
++SELECT login_name
++FROM profiles, components, products
++WHERE profiles.userid = components.initialowner
++    AND components.name = 'package' 
++    AND components.product_id = products.id
++    AND products.name = ?
++EOF
++);
++
++my @default_flags = qw/
++    ExcludeSelf
++    FlagRequestee
++    FlagRequester
++    emailOwnerRemoveme
++    emailOwnerComments
++    emailOwnerAttachments
++    emailOwnerStatus
++    emailOwnerResolved
++    emailOwnerKeywords
++    emailOwnerCC
++    emailOwnerOther
++    emailOwnerUnconfirmed
++    emailReporterRemoveme
++    emailReporterComments
++    emailReporterAttachments
++    emailReporterStatus
++    emailReporterResolved
++    emailReporterKeywords
++    emailReporterCC
++    emailReporterOther
++    emailReporterUnconfirmed
++    emailQAcontactRemoveme
++    emailQAcontactComments
++    emailQAcontactAttachments
++    emailQAcontactStatus
++    emailQAcontactResolved
++    emailQAcontactKeywords
++    emailQAcontactCC
++    emailQAcontactOther
++    emailQAcontactUnconfirmed
++    emailCClistRemoveme
++    emailCClistComments
++    emailCClistAttachments
++    emailCClistStatus
++    emailCClistResolved
++    emailCClistKeywords
++    emailCClistCC
++    emailCClistOther
++    emailCClistUnconfirmed
++    emailVoterRemoveme
++    emailVoterComments
++    emailVoterAttachments
++    emailVoterStatus
++    emailVoterResolved
++    emailVoterKeywords
++    emailVoterCC
++    emailVoterOther
++    emailVoterUnconfirmed
++/;
++
++my $default_flags = join('~', map { "$_~on" }  @default_flags);
++
++=head1 CLASS METHODS
++
++Except stated otherwise, maintainers are specified by their login, and packages
++are specified by their name.
++
++=head2 new($host, $base, $user, $password)
++
++Creates a new L<Youri::Bugzilla> object, wrapping bugzilla database I<$base>
++hosted on I<$host>, and accessed by user I<$user> with password I<$password>.
++
++=cut
++
++sub new {
++    my ($class, $host, $base, $user, $pass) = @_;
++
++    my $dbh = DBI->connect("DBI:mysql:database=$base;host=$host", $user, $pass) or croak "Unable to connect: $DBI::errstr";
++
++    my $self = bless {
++        _dbh => $dbh
++    }, $class;
++
++    return $self;
++}
++
++=head1 INSTANCE METHODS
++
++=head2 has_package($package)
++
++Return true if bugzilla contains given package.
++
++=cut
++
++sub has_package {
++    my ($self, $package) = @_;
++    return $self->_get_package_id($package);
++}
++
++=head2 has_maintainer($maintainer)
++
++Return true if bugzilla contains given maintainer.
++
++=cut
++
++sub has_maintainer {
++    my ($self, $maintainer) = @_;
++    return $self->_get_maintainer_id($maintainer);
++}
++
++=head2 get_maintainer($package)
++
++Return maintainer of given package.
++
++=cut
++
++sub get_maintainer {
++    my ($self, $package) = @_;
++    return $self->_get_single('get_maintainer', $package);
++}
++
++=head2 get_versions($package)
++
++Return versions from given package.
++
++=cut
++
++sub get_versions {
++    my ($self, $package) = @_;
++    return $self->_get_multiple(
++        'get_versions',
++        $self->_get_package_id($package)
++    );
++}
++
++=head2 get_components($package)
++
++Return components from given package.
++
++=cut
++
++sub get_components {
++    my ($self, $package) = @_;
++    return $self->_get_multiple(
++        'get_components',
++        $self->_get_package_id($package)
++    );
++}
++
++=head2 get_packages()
++
++Return all packages from the database.
++
++=cut
++
++sub get_packages {
++    my ($self) = @_;
++    return $self->_get_multiple('get_packages');
++}
++
++sub _get_package_id {
++    my ($self, $package) = @_;
++    return $self->_get_single('get_package_id', $package);
++}
++
++sub _get_maintainer_id {
++    my ($self, $maintainer) = @_;
++    return $self->_get_single('get_maintainer_id', $maintainer);
++}
++
++sub _get_single {
++    my ($self, $type, $value) = @_;
++    return unless ref $self;
++
++    my $query = $self->{_queries}->{$type};
++    unless ($query) {
++        $query = $self->{_dbh}->prepare($queries{$type});
++        $self->{_queries}->{$type} = $query;
++    }
++
++    $query->execute($value);
++
++    my @row = $query->fetchrow_array();
++    return @row ? $row[0]: undef;
++}
++
++sub _get_multiple {
++    my ($self, $type, $value) = @_;
++    return unless ref $self;
++
++    my $query = $self->{_queries}->{$type};
++    unless ($query) {
++        $query = $self->{_dbh}->prepare($queries{$type});
++        $self->{_queries}->{$type} = $query;
++    }
++
++    $query->execute($value);
++
++    my @results;
++    while (my @row = $query->fetchrow_array()) {
++        push @results, $row[0];
++    }
++    return @results;
++}
++
++=head2 add_package($name, $summary, $version, $maintainer, $contact)
++
++Adds a new package in the database, with given name, summary, version,
++maintainer and initial QA contact.
++
++=cut
++
++sub add_package {
++    my ($self, $name, $summary, $version, $maintainer, $contact) = @_;
++    return unless ref $self;
++
++    my $maintainer_id  = $self->_get_maintainer_id($maintainer);
++    unless ($maintainer_id) {
++        carp "Unknown maintainer $maintainer, aborting";
++        return;
++    }
++
++    my $contact_id  = $self->_get_maintainer_id($contact);
++    unless ($contact_id) {
++        carp "Unknown QA contact $contact, aborting";
++        return;
++    }
++
++    my $query = $self->{_queries}->{add_package};
++    unless ($query) {
++        $query = $self->{_dbh}->prepare($queries{add_package});
++        $self->{_queries}->{add_package} = $query;
++    }
++
++    $query->execute($name, $summary);
++
++    my $package_id  = $self->_get_package_id($name);
++
++    $self->_add_version($package_id, $version);
++    $self->_add_component(
++        $package_id,
++        'package',
++        'problem related to the package',
++        $maintainer_id,
++        $contact_id
++    );
++    $self->_add_component(
++        $package_id,
++        'program',
++        'problem related to the program',
++        $maintainer_id,
++        $contact_id
++    );
++}
++
++=head2 add_version($package, $version)
++
++Adds a new version to given package.
++
++=cut
++
++sub add_version {
++    my ($self, $package, $version) = @_;
++    return unless ref $self;
++
++    my $package_id  = $self->_get_package_id($package);
++    $self->_add_version($package_id, $version);
++}
++
++sub _add_version {
++    my ($self, $package_id, $version) = @_;
++    return unless ref $self;
++
++    my $query = $self->{_queries}->{add_version};
++    unless ($query) {
++        $query = $self->{_dbh}->prepare($queries{add_version});
++        $self->{_queries}->{add_version} = $query;
++    }
++
++    $query->execute($package_id, $version);
++}
++
++
++=head2 add_maintainer($name, $login, $password)
++
++Adds a new maintainer in the database, with given name, login and password.
++
++=cut
++
++sub add_maintainer {
++    my ($self, $name, $login, $pass) = @_;
++    return unless ref $self;
++
++    my $query = $self->{_queries}->{add_maintainer};
++    unless ($query) {
++        $query = $self->{_dbh}->prepare($queries{add_maintainer});
++        $self->{_queries}->{add_maintainer} = $query;
++    }
++
++    $query->execute($login, $pass, $name, $default_flags);
++}
++
++sub _add_component {
++    my ($self, $package_id, $name, $description, $maintainer_id, $contact_id) = @_;
++
++    my $query = $self->{_queries}->{add_component};
++    unless ($query) {
++        $query = $self->{_dbh}->prepare($queries{add_component});
++        $self->{_queries}->{add_component} = $query;
++    }
++
++    $query->execute($package_id, $name, $description, $maintainer_id, $contact_id);
++}
++
++=head2 del_package($package)
++
++Delete given package from database.
++
++=cut
++
++sub del_package {
++    my ($self, $package) = @_;
++    $self->_delete('del_package', $package);
++    $self->_delete('del_versions', $package);
++    $self->_delete('del_components', $package);
++}
++
++=head2 del_maintainer($maintainer)
++
++Delete given maintainer from database.
++
++=cut
++
++sub del_maintainer {
++    my ($self, $maintainer) = @_;
++    $self->_delete('del_maintainer', $maintainer);
++}
++
++sub _delete {
++    my ($self, $type, $value) = @_;
++    return unless ref $self;
++
++    my $query = $self->{_queries}->{$type};
++    unless ($query) {
++        $query = $self->{_dbh}->prepare($queries{$type});
++        $self->{_queries}->{$type} = $query;
++    }
++
++    $query->execute($value);
++}
++
++=head2 reset_password(I<$maintainer>, I<$password>)
++
++Reset password of a maintainer to given password.
++
++=cut
++
++sub reset_password {
++    my ($self, $login, $pass) = @_;
++    return unless ref $self;
++
++    my $query = $self->{_queries}->{reset_password};
++    unless ($query) {
++        $query = $self->{_dbh}->prepare($queries{reset_password});
++        $self->{_queries}->{reset_password} = $query;
++    }
++
++    $query->execute($pass, $login);
++}
++
++=head2 browse_packages($callback)
++
++Browse all packages from bugzilla, and execute given callback with name and
++maintainer as argument for each of them.
++
++=cut
++
++sub browse_packages {
++    my ($self, $callback) = @_;
++    return unless ref $self;
++
++    my $query = $self->{_queries}->{browse_packages};
++    unless ($query) {
++        $query = $self->{_dbh}->prepare($queries{browse_packages});
++        $self->{_queries}->{browse_packages} = $query;
++    }
++
++    $query->execute();
++
++    while (my @row = $query->fetchrow_array()) {
++        $callback->(@row);
++    }
++}
++
++# close database connection
++sub DESTROY {
++    my ($self) = @_;
++
++    foreach my $query (values %{$self->{_queries}}) {
++        $query->finish();
++    }
++
++    $self->{_dbh}->disconnect();
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Age.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Age.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Age.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,110 @@
++# $Id: Age.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Age;
++
++=head1 NAME
++
++Youri::Check::Input::Age - Check maximum age
++
++=head1 DESCRIPTION
++
++This plugin checks packages age, and report the ones exceeding maximum limit.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use DateTime;
++use DateTime::Format::Duration;
++use base 'Youri::Check::Input';
++
++sub columns {
++    return qw/
++        arch
++        buildtime
++    /;
++}
++
++sub links {
++    return qw//;
++}
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Age object.
++
++Specific parameters:
++
++=over
++
++=item max_age $age
++
++Maximum age allowed (default: 1 year)
++
++=item pattern $pattern
++
++Pattern used to describe age (default: %Y year)
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        max_age => '1 year',
++        pattern => '%Y year',
++        @_
++    );
++
++    $self->{_format} = DateTime::Format::Duration->new(
++        pattern => $options{pattern}
++    );
++
++    $self->{_now} = DateTime->from_epoch(
++        epoch => time()
++    );
++
++    $self->{_max_age} = $options{max_age};
++}
++
++sub run {
++    my ($self, $media, $resultset) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $max_age_string = $media->max_age() ?
++        $media->max_age() :
++        $self->{_max_age};
++
++    my $max_age = $self->{_format}->parse_duration($max_age_string);
++
++    my $check = sub {
++        my ($package) = @_;
++
++        my $buildtime = DateTime->from_epoch(
++            epoch => $package->get_age()
++        );
++        
++        my $age = $self->{_now}->subtract_datetime($buildtime);
++
++        if (DateTime::Duration->compare($age, $max_age) > 0) {
++            my $date = $buildtime->strftime("%a %d %b %G");
++
++            $resultset->add_result($self->{_id}, $media, $package, {
++                arch      => $package->get_arch(),
++                buildtime => $date
++            });
++        }
++    };
++    $media->traverse_headers($check);
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source/Iurt.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source/Iurt.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source/Iurt.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,117 @@
++# $Id: LBD.pm 574 2005-12-27 14:31:16Z guillomovitch $
++package Youri::Check::Input::Build::Source::Iurt;
++
++=head1 NAME
++
++Youri::Check::Input::Build::Source::Iurt - Iurt build log source
++
++=head1 DESCRIPTION
++
++This source plugin for L<Youri::Check::Input::Build> collects build logs
++available from a iurt build bot.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use LWP::UserAgent;
++use HTML::TokeParser;
++use base 'Youri::Check::Input::Build::Source';
++
++my %status = (
++    install_deps => 0,
++    build        => 1,
++    binary_test  => 2
++);
++
++my $pattern = '^('
++    . join('|', keys %status)
++    . ')_\S+-[^-]+-[^-]+\.src\.rpm\.\d+\.log$';
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Build::LBD object.
++
++Specific parameters:
++
++=over
++
++=item url $url
++
++URL of logs for this iurt instance (default:
++http://qa.mandriva.com/build/iurt/cooker)
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        url    => 'http://qa.mandriva.com/build/iurt/cooker',
++        @_
++    );
++
++    $self->{_agent} = LWP::UserAgent->new();
++
++    # try to connect to base URL directly, and abort if not available
++    my $response = $self->{_agent}->head($options{url});
++    die "Unavailable URL $options{url}: " . $response->status_line()
++        unless $response->is_success();
++
++    $self->{_url} = $options{url};
++}
++
++sub fails {
++    my ($self, $name, $version, $release, $arch) = @_;
++
++    my $result;
++    my $url = "$self->{_url}/$arch/log/$name-$version-$release.src.rpm";
++    print "Fetching URL $url: " if $self->{_verbose} > 1;
++    my $response = $self->{_agent}->get($url);
++    print $response->status_line() . "\n" if $self->{_verbose} > 1;
++    if ($response->is_success()) {
++        my $parser = HTML::TokeParser->new(\$response->content());
++        while (my $token = $parser->get_tag('a')) {
++            my $href = $token->[1]->{href};
++            next unless $href =~ /$pattern/o;
++            my $status  = $1;
++            if (
++                !$result->{status} ||
++                $status{$result->{status}} < $status{$status}
++            ) {
++                $result->{status} = $status;
++                $result->{url}    = $url . '/' . $href;
++            }
++        }
++    }
++
++    $self->{_results}->{$name}->{$version}->{$release}->{$arch} = $result;
++
++    return $result->{status} && $result->{status} ne 'binary_test';
++}
++
++sub status {
++    my ($self, $name, $version, $release, $arch) = @_;
++    return
++        $self->{_results}->{$name}->{$version}->{$release}->{$arch}->{status};
++}
++
++sub url {
++    my ($self, $name, $version, $release, $arch) = @_;
++    return
++        $self->{_results}->{$name}->{$version}->{$release}->{$arch}->{url};
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source/LBD.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source/LBD.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source/LBD.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,135 @@
++# $Id: LBD.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Build::Source::LBD;
++
++=head1 NAME
++
++Youri::Check::Input::Build::Source::LBD - LBD build log source
++
++=head1 DESCRIPTION
++
++This source plugin for L<Youri::Check::Input::Build> collects build logs
++available from a LBD build bot.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use LWP::UserAgent;
++use HTML::TokeParser;
++use base 'Youri::Check::Input::Build::Source';
++
++my @status = qw/
++    OK
++    arch_excl
++    broken
++    cannot_be_installed
++    debug
++    dependency
++    file_not_found
++    multiarch
++    problem
++    unpackaged_files
++/;
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Build::LBD object.
++
++Specific parameters:
++
++=over
++
++=item url $url
++
++URL of logs for this LBD instance (default: http://eijk.homelinux.org/build)
++
++=item medias $medias
++
++List of medias monitored by this LBD instance
++
++=item archs $archs
++
++List of architectures monitored by this LBD instance
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        url    => 'http://eijk.homelinux.org/build',
++        medias => undef,
++        archs  => undef,
++        @_
++    );
++
++    my $agent   = LWP::UserAgent->new();
++
++    # try to connect to base URL directly, and abort if not available
++    my $response = $agent->head($options{url});
++    die "Unavailable URL $options{url}: " . $response->status_line()
++        unless $response->is_success();
++
++    my $pattern = '^(\S+)-([^-]+)-([^-]+)(?:\.gz)?$';
++
++    foreach my $arch (@{$options{archs}}) {
++        foreach my $media (@{$options{medias}}) {
++            my $url_base = "$options{url}/$arch/$media/BO";
++            foreach my $status (@status) {
++                my $url = "$url_base/$status/";
++                print "Fetching URL $url: " if $self->{_verbose} > 1;
++                my $response = $agent->get($url);
++                print $response->status_line() . "\n" if $self->{_verbose} > 1;
++                if ($response->is_success()) {
++                    my $parser = HTML::TokeParser->new(\$response->content());
++                    while (my $token = $parser->get_tag('a')) {
++                        my $href = $token->[1]->{href};
++                        next unless $href =~ /$pattern/o;
++                        my $name    = $1;
++                        my $version = $2;
++                        my $release = $3;
++                        my $result;
++                        $result->{status} = $status;
++                        $result->{url}    = $url . '/' . $href;
++                        $self->{_results}->{$name}->{$version}->{$release}->{$arch} = $result;
++                    }
++                }
++            }
++        }
++    }
++}
++
++sub fails {
++    my ($self, $name, $version, $release, $arch) = @_;
++
++    my $status =
++        $self->{_results}->{$name}->{$version}->{$release}->{$arch}->{status};
++
++    return $status && $status ne 'OK' && $status ne 'arch_excl';
++}
++
++sub status {
++    my ($self, $name, $version, $release, $arch) = @_;
++    return
++        $self->{_results}->{$name}->{$version}->{$release}->{$arch}->{status};
++}
++
++sub url {
++    my ($self, $name, $version, $release, $arch) = @_;
++    return
++        $self->{_results}->{$name}->{$version}->{$release}->{$arch}->{url};
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build/Source.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,109 @@
++# $Id: Source.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Build::Source;
++
++=head1 NAME
++
++Youri::Check::Input::Build::Source - Abstract build log source
++
++=head1 DESCRIPTION
++
++This abstract class defines the updates source interface for
++L<Youri::Check::Input::Build>.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Build object.
++
++No generic parameters (subclasses may define additional ones).
++
++Warning: do not call directly, call subclass constructor instead.
++
++=cut
++
++sub new {
++    my $class = shift;
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my %options = (
++        id        => '',    # object id
++        test      => 0,     # test mode
++        verbose   => 0,     # verbose mode
++        @_
++    );
++
++    my $self = bless {
++        _id        => $options{id},
++        _test      => $options{test},
++        _verbose   => $options{verbose},
++    }, $class;
++
++    $self->_init(%options);
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head1 INSTANCE METHODS
++
++=head2 get_id()
++
++Returns source identity.
++
++=cut
++
++sub get_id {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_id};
++}
++
++=head2 fails($name, $version, $release, $arch)
++
++Returns true if build fails for package with given name, version and release on
++given architecture.
++
++=head2 status($name, $version, $release, $arch)
++
++Returns exact build status for package with given name, version and release on
++given architecture. It has to be called after fails().
++
++=head2 url($name, $version, $release, $arch)
++
++Returns URL of information source for package with given name, version and
++release on given architecture. It has to be called after fails().
++
++=head1 SUBCLASSING
++
++The following methods have to be implemented:
++
++=over
++
++=item fails
++
++=item status
++
++=item url
++
++=back
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Build.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,128 @@
++# $Id: Build.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Build;
++
++=head1 NAME
++
++Youri::Check::Input::Build - Check build outputs
++
++=head1 DESCRIPTION
++
++This plugin checks build outputs of packages, and report failures. Additional
++source plugins handle specific sources.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Youri::Utils;
++use base 'Youri::Check::Input';
++
++sub columns {
++    return qw/
++        arch
++        bot
++        status
++    /;
++}
++
++sub links {
++    return qw/
++        status url
++    /;
++}
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Build object.
++
++Specific parameters:
++
++=over
++
++=item sources $sources
++
++Hash of source plugins definitions
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        sources => undef,
++        @_
++    );
++
++    croak "No source defined" unless $options{sources};
++    croak "sources should be an hashref" unless ref $options{sources} eq 'HASH';
++
++    foreach my $id (keys %{$options{sources}}) {
++        print "Creating source $id\n" if $options{verbose};
++        eval {
++            push(
++            @{$self->{_sources}},
++            create_instance(
++                    'Youri::Check::Input::Build::Source',
++                    id        => $id,
++                    test      => $options{test},
++                    verbose   => $options{verbose},
++                    %{$options{sources}->{$id}}
++                )
++            );
++            # register monitored archs
++            $self->{_archs}->{$_}->{$id} = 1
++                foreach @{$options{sources}->{$id}->{archs}};
++        };
++        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();
++
++        foreach my $source (@{$self->{_sources}}) {
++            my $id = $source->get_id();
++            foreach my $arch (keys %{$self->{_archs}}) {
++                next unless $self->{_archs}->{$arch}->{$id};
++                $resultset->add_result($self->{_id}, $media, $package, { 
++                    arch   => $arch,
++                    bot    => $id,
++                    status => $source->status($name, $version, $release, $arch),
++                    url    => $source->url($name, $version, $release, $arch),
++                }) if $source->fails(
++                    $name,
++                    $version,
++                    $release,
++                    $arch,
++                );
++            }
++        }
++    };
++
++    $media->traverse_headers($callback);
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Conflicts.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Conflicts.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Conflicts.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,231 @@
++# $Id: Conflicts.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Conflicts;
++
++=head1 NAME
++
++Youri::Check::Input::Conflicts - Check file conflicts
++
++=head1 DESCRIPTION
++
++This plugin checks packages files, and report conflict and duplications.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use constant;
++use Youri::Package;
++use base 'Youri::Check::Input';
++
++use constant TYPE_MASK => 0170000; 
++use constant TYPE_DIR  => 0040000;
++
++use constant PACKAGE => 0;
++use constant MODE    => 1;
++use constant MD5SUM  => 2;
++
++my $compatibility = {
++    x86_64  => 'i586',
++    i586    => 'x86_64',
++    sparc64 => 'sparc',
++    sparc   => 'sparc64',
++    ppc64   => 'ppc',
++    ppc     => 'ppc64'
++};
++
++sub columns {
++    return qw/
++        arch
++        file
++        error
++        level
++    /;
++}
++
++sub links {
++    return qw//;
++}
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Conflicts object.
++
++No specific parameters.
++
++=cut
++
++sub prepare {
++    my ($self, @medias) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $index = sub {
++        my ($package) = @_;
++
++        # index files
++        foreach my $file ($package->get_files()) {
++            push(
++                @{$self->{_files}->{$file->[Youri::Package::FILE_NAME]}},
++                [ $package, $file->[Youri::Package::FILE_MODE], $file->[Youri::Package::FILE_MD5SUM] ]
++            );
++        }
++    };
++
++    foreach my $media (@medias) {
++        # don't index source media files
++        next unless $media->get_type() eq 'binary';
++
++        my $media_id = $media->get_id();
++        $self->{_medias}->{$media_id} = 1;
++        print STDERR "Indexing media $media_id files\n"
++            if $self->{_verbose};
++
++        $media->traverse_headers($index);
++    }
++}
++
++sub run {
++    my ($self, $media, $result) = @_;
++    croak "Not a class method" unless ref $self;
++
++    # this is a binary media check only
++    return unless $media->get_type() eq 'binary';
++
++    my $check = sub {
++        my ($package) = @_;
++
++        return if $package->get_arch() eq 'src';
++
++        my $arch = $package->get_arch();
++        my $name = $package->get_name();
++
++        foreach my $file ($package->get_files()) {
++
++            my $found =
++                $self->{_files}->{$file->[Youri::Package::FILE_NAME]};
++
++            my @found = $found ? @$found : ();
++
++            foreach my $found (@found) {
++                next if $found->[PACKAGE] == $package;
++                next unless compatible($found->[PACKAGE], $package);
++                next if conflict($found->[PACKAGE], $package);
++                next if replace($found->[PACKAGE], $package);
++                if (
++                    ($file->[Youri::Package::FILE_MODE] & TYPE_MASK) == TYPE_DIR &&
++                    ($found->[MODE] & TYPE_MASK) == TYPE_DIR
++                ) {
++                    $result->add_result($self->{_id}, $media, $package, {
++                        arch  => $arch,
++                        file  => $name,
++                        error => "directory $file->[Youri::Package::FILE_NAME] duplicated with package " . $found->[PACKAGE]->get_name(),
++                        level => Youri::Check::Input::WARNING
++                    }) unless $self->_directory_duplicate_exception(
++                        $package,
++                        $found->[PACKAGE],
++                        $file
++                    );
++                } else {
++                    if ($found->[MD5SUM] eq $file->[Youri::Package::FILE_MD5SUM]) {
++                        $result->add_result($self->{_id}, $media, $package, {
++                            arch  => $arch,
++                            file  => $name,
++                            error => "file $file->[Youri::Package::FILE_NAME] duplicated with package " . $found->[PACKAGE]->get_name(),
++                            level => Youri::Check::Input::WARNING
++                        }) unless $self->_file_duplicate_exception(
++                            $package,
++                            $found->[PACKAGE],
++                            $file
++                        );
++                    } else {
++                        $result->add_result($self->{_id}, $media, $package, {
++                            arch  => $arch,
++                            file  => $name,
++                            error => "non-explicit conflict on file $file->[Youri::Package::FILE_NAME] with package " . $found->[PACKAGE]->get_name(),
++                            level => Youri::Check::Input::ERROR
++                        }) unless $self->_file_conflict_exception(
++                            $package,
++                            $found->[PACKAGE],
++                            $file
++                        );
++                    }
++                }
++            }
++        }
++    };
++
++    $media->traverse_headers($check);
++}
++
++# return true if $package1 is arch-compatible with $package2
++sub compatible {
++    my ($package1, $package2) = @_;
++
++    my $arch1 = $package1->get_arch();
++    my $arch2 = $package2->get_arch();
++
++    return 1 if $arch1 eq $arch2;
++
++    return 1 if $compatibility->{$arch1} && $compatibility->{$arch1} eq $arch2;
++
++    return 0;
++}
++
++# return true if $package1 conflict with $package2
++# or the other way around
++sub conflict {
++    my ($package1, $package2) = @_;
++
++    my $name2 = $package2->get_name();
++
++    foreach my $conflict ($package1->get_conflicts()) {
++        return 1 if $conflict eq $name2;
++    }
++
++    my $name1 = $package1->get_name();
++
++    foreach my $conflict ($package2->get_conflicts()) {
++        return 1 if $conflict eq $name1;
++    }
++
++    return 0;
++}
++
++# return true if $package1 replace $package2
++sub replace {
++    my ($package1, $package2) = @_;
++
++
++    my $name1 = $package1->get_name();
++    my $name2 = $package2->get_name();
++
++    return 1 if $name1 eq $name2;
++
++    foreach my $obsolete ($package1->get_obsoletes()) {
++        return 1 if $obsolete->[Youri::Package::DEPENDENCY_NAME] eq $name2;
++    }
++
++    return 0;
++}
++
++sub _directory_duplicate_exception {
++    return 0;
++}
++
++sub _file_duplicate_exception {
++    return 0;
++}
++
++sub _file_conflict_exception {
++    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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Dependencies.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Dependencies.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Dependencies.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,162 @@
++# $Id: Dependencies.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Dependencies;
++
++=head1 NAME
++
++Youri::Check::Input::Dependencies - Check dependencies consistency
++
++=head1 DESCRIPTION
++
++This class checks dependencies consistency.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Youri::Package;
++use base 'Youri::Check::Input';
++
++use constant MEDIA => 0;
++use constant RANGE => 1;
++
++sub columns {
++    return qw/
++        arch
++        file
++        error
++        level
++    /;
++}
++
++sub links {
++    return qw//;
++}
++
++sub prepare {
++    my ($self, @medias) = @_;
++    croak "Not a class method" unless ref $self;
++
++    foreach my $media (@medias) {
++        my $media_id = $media->get_id();
++        $self->{_medias}->{$media_id} = 1;
++        print STDERR "Indexing media $media_id dependencies\n"
++            if $self->{_verbose};
++
++        my $index = sub {
++            my ($package) = @_;
++
++            # index provides
++            foreach my $provide ($package->get_provides()) {
++                push(
++                    @{$self->{_provides}->{$provide->[Youri::Package::DEPENDENCY_NAME]}},
++                    [ $media_id, $provide->[Youri::Package::DEPENDENCY_RANGE] ]
++                );
++            }
++
++            # index files
++            foreach my $file ($package->get_files()) {
++                push(
++                    @{$self->{_files}->{$file->[Youri::Package::FILE_NAME]}},
++                    [ $media_id, undef ]
++                );
++            }
++        };
++        $media->traverse_headers($index);
++    }
++}
++
++sub run {
++    my ($self, $media, $resultset) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my @allowed_ids = $media->allow_deps();
++
++    # abort unless all allowed medias are present
++    foreach my $id (@allowed_ids) {
++        unless ($self->{_medias}->{$id}) {
++            carp "Missing media $id, aborting";
++            return;
++        }
++    }
++
++    # index allowed medias
++    my %allowed_ids = map { $_ => 1 } @allowed_ids;
++    my $allowed_ids = join(",", @allowed_ids);
++
++    my $class = $media->get_package_class();
++
++    my $check = sub {
++        my ($package) = @_;
++
++        my $arch = $package->get_arch();
++        my $name = $package->get_name();
++
++        foreach my $require ($package->get_requires()) {
++
++            my $found =
++                substr($require->[Youri::Package::DEPENDENCY_NAME], 0, 1) eq '/' ?
++                    $self->{_files}->{$require->[Youri::Package::DEPENDENCY_NAME]} :
++                    $self->{_provides}->{$require->[Youri::Package::DEPENDENCY_NAME]};
++
++            my @found = $found ? @$found : ();
++
++            if (!@found) {
++                $resultset->add_result($self->{_id}, $media, $package, {
++                    arch  => $arch,
++                    file  => $name,
++                    error => "$require->[Youri::Package::DEPENDENCY_NAME] not found",
++                    level => Youri::Check::Input::ERROR
++                });
++                next;
++            }
++
++            my @found_in_media =
++                grep { $allowed_ids{$_->[MEDIA]} }
++                @found;
++
++            if (!@found_in_media) {
++                $resultset->add_result($self->{_id}, $media, $package, {
++                    arch  => $arch,
++                    file  => $name,
++                    error => "$require->[Youri::Package::DEPENDENCY_NAME] found in incorrect media $_->[MEDIA] (allowed $allowed_ids)",
++                    level => Youri::Check::Input::ERROR
++                }) foreach @found;
++                next;
++            }
++
++            next unless $require->[Youri::Package::DEPENDENCY_RANGE];
++
++            my @found_in_range =
++                grep {
++                    !$_->[RANGE] ||
++                    $class->compare_ranges(
++                        $require->[Youri::Package::DEPENDENCY_RANGE],
++                        $_->[RANGE]
++                    )
++                } @found_in_media;
++
++            if (!@found_in_range) {
++                $resultset->add_result($self->{_id}, $media, $package, {
++                    arch  => $arch,
++                    file  => $name,
++                    error => "$require->[Youri::Package::DEPENDENCY_NAME] found with incorrect range $_->[RANGE] (needed $require->[Youri::Package::DEPENDENCY_RANGE])",
++                    level => Youri::Check::Input::ERROR
++                }) foreach @found_in_media;
++                next;
++            }
++        }
++    };
++
++    $media->traverse_headers($check);
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/MandrivaConflicts.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/MandrivaConflicts.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/MandrivaConflicts.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,63 @@
++# $Id: Conflicts.pm 533 2005-10-20 07:08:03Z guillomovitch $
++package Youri::Check::Input::MandrivaConflicts;
++
++=head1 NAME
++
++Youri::Check::Input::MandrivaConflicts - Check file conflicts on Mandriva
++
++=head1 DESCRIPTION
++
++This class checks file conflicts between packages, taking care of Mandriva
++packaging policy.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Youri::Package;
++use base 'Youri::Check::Input::Conflicts';
++
++sub _directory_duplicate_exception {
++    my ($self, $package1, $package2, $file) = @_;
++
++    # allow shared directories between devel packages of different arch
++    return 1 if _multiarch_exception($package1, $package2);
++
++    # allow shared modules directories between perl packages
++    return 1 if 
++        $file->[Youri::Package::FILE_NAME] =~ /^\/usr\/lib\/perl5\/vendor_perl\// &&
++        $file->[Youri::Package::FILE_NAME] !~ /^(auto|[^\/]+-linux)$/;
++
++    return 0;
++}
++
++sub _file_duplicate_exception {
++    my ($self, $package1, $package2, $file) = @_;
++
++    # allow shared files between devel packages of different arch
++    return 1 if _multiarch_exception($package1, $package2);
++
++    return 0;
++}
++
++sub _multiarch_exception {
++    my ($package1, $package2) = @_;
++
++    return 1 if
++        $package1->get_canonical_name() eq $package2->get_canonical_name()
++        && $package1->get_name() =~ /-devel$/
++        && $package2->get_name() =~ /-devel$/;
++
++    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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Missing.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Missing.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Missing.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,138 @@
++package Youri::Check::Input::Missing;
++
++=head1 NAME
++
++Youri::Check::Input::Missing - Check components consistency
++
++=head1 DESCRIPTION
++
++This plugin checks consistency between package components, and report outdated
++ones.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use List::MoreUtils qw/all any/;
++use base 'Youri::Check::Input';
++
++sub columns {
++    return qw/
++        component
++        arch
++        revision
++        error
++    /;
++}
++
++sub links {
++    return qw//;
++}
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Missing object.
++
++No specific parameters.
++
++=cut
++
++sub prepare {
++    my ($self, @medias)  = @_;
++    croak "Not a class method" unless ref $self;
++    $self->{_srcs} = ();
++    foreach my $media (@medias) {
++        # only index source media
++        next unless $media->get_type() eq 'source';
++
++        my $media_id = $media->get_id();
++        $self->{_medias}->{$media_id} = 1;
++        print STDERR "Indexing media $media_id packages\n" if $self->{_verbose};
++
++        my $index = sub {
++            my ($package) = @_;
++            $self->{_srcs}->{$media_id}->{$package->get_name()} =
++                $package->get_version() . '-' . $package->get_release();
++        };
++
++        $media->traverse_headers($index);
++    }
++}
++
++sub run {
++    my ($self, $media, $resultset) = @_;
++    croak "Not a class method" unless ref $self;
++
++    # this is a binary media check only
++    return unless $media->get_type() eq 'binary';
++
++    my @allowed_ids = $media->allow_srcs();
++
++    # abort unless all allowed medias are present
++    foreach my $id (@allowed_ids) {
++    unless ($self->{_medias}->{$id}) {
++            carp "Missing media $id, aborting";
++            return;
++        }
++    }
++
++    my $class = $media->get_package_class();
++
++    my $check_package = sub {
++        my ($package) = @_;
++        my $canonical_name = $package->get_canonical_name();
++
++        my $bin_revision =
++            $package->get_version() . '-' . $package->get_release();
++
++        my $src_revision;
++        foreach my $id (@allowed_ids) {
++            $src_revision = $self->{_srcs}->{$id}->{$canonical_name};
++            last if $src_revision;
++        }
++
++        if ($src_revision) {
++            # check if revision match
++            unless ($src_revision eq $bin_revision) {
++                if ($class->compare_versions($src_revision, $bin_revision) > 0) {
++                    # binary package is obsolete
++                    $resultset->add_result($self->{_id}, $media, $package, {
++                        component => $package->get_name(),
++                        arch      => $package->get_arch(),
++                        revision  => $bin_revision,
++                        error     => "Obsolete binaries (source $src_revision found)",
++                    });
++                } else {
++                    # source package is obsolete
++                    $resultset->add_result($self->{_id}, $media, $package, {
++                        component => $package->get_canonical_name(),
++                        arch      => 'src',
++                        revision  => $src_revision,
++                        error     => "Obsolete source (binaries $bin_revision found)",
++                    });
++                }
++            }
++        } else {
++            $resultset->add_result($self->{_id}, $media, $package, {
++                component => $package->get_name(),
++                arch      => $package->get_arch(),
++                revision  => $bin_revision,
++                error     => "Missing source package",
++            });
++        }
++    };
++
++    $media->traverse_headers($check_package);
++}
++
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Orphans.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Orphans.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Orphans.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,74 @@
++package Youri::Check::Input::Orphans;
++
++=head1 NAME
++
++Youri::Check::Input::Orphans - Check maintainance
++
++=head1 DESCRIPTION
++
++This plugin checks maintainance status of packages, and reports unmaintained
++ones.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use base 'Youri::Check::Input';
++
++sub columns {
++    return qw/
++        error
++    /;
++}
++
++sub links {
++    return qw//;
++}
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Orphans object.
++
++No specific parameters.
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        resolver => undef,
++        @_
++    );
++
++    croak "No resolver defined" unless $options{resolver};
++
++    $self->{_resolver} = $options{resolver};
++}
++
++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 $check = sub {
++        my ($package) = @_;
++        $resultset->add_result($self->{_id}, $media, $package, {
++            error => "unmaintained package"
++        }) unless $self->{_resolver}->get_maintainer($package);
++    };
++
++    $media->traverse_headers($check);
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Rpmlint.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Rpmlint.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Rpmlint.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,113 @@
++# $Id: Rpmlint.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Rpmlint;
++
++=head1 NAME
++
++Youri::Check::Input::Rpmlint - Check packages with rpmlint
++
++=head1 DESCRIPTION
++
++This plugins checks packages with rpmlint, and reports output.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use base 'Youri::Check::Input';
++
++sub columns {
++    return qw/
++        arch
++        file
++        error
++        level
++    /;
++}
++
++sub links {
++    return qw//;
++}
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Rpmlint object.
++
++Specific parameters:
++
++=over
++
++=item path $path
++
++Path to the rpmlint executable (default: /usr/bin/rpmlint)
++
++=item config $config
++
++Specific rpmlint configuration.
++
++=back
++
++=cut
++
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        path   => '/usr/bin/rpmlint', # path to rpmlint
++        config => '',                 # default rpmlint configuration
++        @_
++    );
++
++    $self->{_path}   = $options{path};
++    $self->{_config} = $options{config};
++}
++
++sub run {
++    my ($self, $media, $resultset) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $config = $media->rpmlint_config() ?
++        $media->rpmlint_config() :
++        $self->{_config};
++
++    my $check = sub {
++        my ($file, $package) = @_;
++
++        my $arch = $package->get_arch();
++        my $name = $package->get_name();
++
++        my $command = "$self->{_path} -f $config $file";
++        open(RPMLINT, "$command |") or die "Can't run $command: $!";
++        while (<RPMLINT>) {
++            chomp;
++            if (/^E: \Q$name\E (.+)/) {
++                $resultset->add_result($self->{_id}, $media, $package, { 
++                    arch  => $arch,
++                    file  => $name,
++                    error => $1,
++                    level => Youri::Check::Input::ERROR
++                });
++            } elsif (/^W: \Q$name\E (.+)/) {
++                $resultset->add_result($self->{_id}, $media, $package, { 
++                    arch  => $arch,
++                    file  => $name,
++                    error => $1,
++                    level => Youri::Check::Input::WARNING
++                });
++            }
++        }
++        close(RPMLINT);
++    };
++
++    $media->traverse_files($check);
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Signature.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Signature.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Signature.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,96 @@
++# $Id: Rpmlint.pm 567 2005-12-12 21:24:56Z guillomovitch $
++package Youri::Check::Input::Signature;
++
++=head1 NAME
++
++Youri::Check::Input::Signature - Check signature
++
++=head1 DESCRIPTION
++
++This plugin checks packages signature, and report unsigned ones. 
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use base 'Youri::Check::Input';
++
++sub columns {
++    return qw/
++        arch
++        file
++        error
++    /;
++}
++
++sub links {
++    return qw//;
++}
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Signature object.
++
++Specific parameters:
++
++=over
++
++=item key $key
++
++Expected GPG key identity
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        key => '',
++        @_
++    );
++
++    $self->{_key} = $options{key};
++}
++
++sub run {
++    my ($self, $media, $resultset) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $check = sub {
++        my ($package) = @_;
++
++        my $arch = $package->get_arch();
++        my $name = $package->get_name();
++
++        my $key = $package->get_gpg_key();
++
++        if (!$key) {
++            $resultset->add_result($self->{_id}, $media, $package, { 
++                arch  => $arch,
++                file  => $name,
++                error => "unsigned package $name"
++            });
++        } elsif ($key ne $self->{_key}) {
++            $resultset->add_result($self->{_id}, $media, $package, { 
++                arch  => $arch,
++                file  => $name,
++                error => "invalid key id $key for package $name (allowed $self->{_key})"
++            });
++        }
++        
++    };
++
++    $media->traverse_headers($check);
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/CPAN.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/CPAN.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/CPAN.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,75 @@
++# $Id: CPAN.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Updates::Source::CPAN;
++
++=head1 NAME
++
++Youri::Check::Input::Updates::Source::CPAN - CPAN updates source
++
++=head1 DESCRIPTION
++
++This source plugin for L<Youri::Check::Input::Updates> collects updates
++available from CPAN.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use base 'Youri::Check::Input::Updates::Source';
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Updates::Source::CPAN object.  
++
++Specific parameters:
++
++=over
++
++=item url $url
++
++URL to CPAN full modules list (default:
++http://www.cpan.org/modules/01modules.index.html)
++
++=back
++
++=cut
++
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        url => 'http://www.cpan.org/modules/01modules.index.html',
++        @_
++    );
++
++    my $versions;
++    open(INPUT, "GET $options{url} |") or croak "Can't fetch $options{url}: $!";
++    while (<INPUT>) {
++        next unless $_ =~ />([\w-]+)-([\d\.]+)\.tar\.gz<\/a>/;
++        $versions->{$1} = $2;
++    }
++    close(INPUT);
++
++    $self->{_versions} = $versions;
++}
++
++sub _url {
++    my ($self, $name) = @_;
++    return "http://search.cpan.org/dist/$name";
++}
++
++sub _name {
++    my ($self, $name) = @_;
++    $name =~ s/^perl-//g;
++    return $name;
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Debian.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Debian.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Debian.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,82 @@
++# $Id: Debian.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Updates::Source::Debian;
++
++=head1 NAME
++
++Youri::Check::Input::Updates::Source::Debian - Debian source for updates
++
++=head1 DESCRIPTION
++
++This source plugin for L<Youri::Check::Input::Updates> collects updates
++ available from Debian.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use base 'Youri::Check::Input::Updates::Source';
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Updates::Source::Debian object.
++
++Specific parameters:
++
++=over
++
++=item url $url
++
++URL to Debian mirror content file (default: http://ftp.debian.org/ls-lR.gz)
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        url => 'http://ftp.debian.org/ls-lR.gz',
++        @_
++    );
++
++    my $versions;
++    open(INPUT, "GET $options{url} | zcat |") or croak "Can't fetch $options{url}: $!";
++    while (my $line = <INPUT>) {
++        next unless $line =~ /([\w\.-]+)_([\d\.]+)\.orig\.tar\.gz$/;
++        my $name = $1;
++        my $version = $2;
++        $versions->{$name} = $version;
++    }
++    close(INPUT);
++
++    $self->{_versions} = $versions;
++}
++
++sub _url {
++    my ($self, $name) = @_;
++    return "http://packages.debian.org/$name";
++}
++
++sub _name {
++    my ($self, $name) = @_;
++    
++    if ($name =~ /^(perl|ruby)-([-\w]+)$/) {
++        $name = lc("lib$2-$1");
++    } elsif ($name =~ /^apache-([-\w]+)$/) {
++        $name = "libapache-$1";
++        $name =~ s/_/-/g;
++    }
++
++    return $name;
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Fedora.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Fedora.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Fedora.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,63 @@
++# $Id: Fedora.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Updates::Source::Fedora;
++
++=head1 NAME
++
++Youri::Check::Input::Updates::Source::Fedora - Fedora updates source
++
++=head1 DESCRIPTION
++
++This source plugin for L<Youri::Check::Input::Updates> collects updates
++available from Fedora.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use base 'Youri::Check::Input::Updates::Source';
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Updates::Source::Fedora object.
++
++Specific parameters:
++
++=over
++
++=item url $url
++
++URL to Fedora development SRPMS directory (default:
++http://fr.rpmfind.net/linux/fedora/core/development/SRPMS)
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        url => 'http://fr.rpmfind.net/linux/fedora/core/development/SRPMS',
++        @_
++    );
++
++    my $versions;
++    open(INPUT, "GET $options{url} |") or die "Can't fetch $options{url}: $!\n";
++    while (<INPUT>) {
++        next unless $_ =~ />([\w-]+)-([\w\.]+)-[\w\.]+\.src\.rpm<\/a>/;
++        $versions->{$1} = $2;
++    }
++    close(INPUT);
++
++    $self->{_versions} = $versions;
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Freshmeat.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Freshmeat.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Freshmeat.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,111 @@
++# $Id: Freshmeat.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Updates::Source::Freshmeat;
++
++=head1 NAME
++
++Youri::Check::Input::Updates::Source::Freshmeat - Freshmeat source for updates
++
++=head1 DESCRIPTION
++
++This source plugin for L<Youri::Check::Input::Updates> collects updates
++available from Freshmeat.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use XML::Twig;
++use LWP::UserAgent;
++use base 'Youri::Check::Input::Updates::Source';
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Updates::Source::Freshmeat
++object.
++
++Specific parameters:
++
++=over
++
++=item preload true/false
++
++Allows to load full Freshmeat catalogue at once instead of checking each software independantly (default: false)
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        preload => 0,
++        @_
++    );
++
++    if ($options{preload}) {
++        my $versions;
++        
++        my $project = sub {
++            my ($twig, $project) = @_;
++            my $name    = $project->first_child('projectname_short')->text();
++            my $version = $project->first_child('latest_release')->first_child('latest_release_version')->text();
++            $versions->{$name} = $version;
++            $twig->purge();
++        };
++
++        my $twig = XML::Twig->new(
++           TwigRoots => { project => $project }
++        );
++
++        my $url = 'http://download.freshmeat.net/backend/fm-projects.rdf.bz2';
++
++        open(INPUT, "GET $url | bzcat |") or die "Can't fetch $url: $!\n";
++        $twig->parse(\*INPUT);
++        close(INPUT);
++
++        $self->{_versions} = $versions;
++    }
++}
++
++sub _version {
++    my ($self, $name) = @_;
++
++    if ($self->{_versions}) {
++        return $self->{_versions}->{$name};
++    } else {
++        my $version;
++
++        my $latest_release_version = sub {
++            $version = $_[1]->text();
++        };
++
++        my $twig = XML::Twig->new(
++            TwigRoots => { latest_release_version => $latest_release_version }
++        );
++
++        my $url = "http://freshmeat.net/projects-xml/$name";
++        
++        open(INPUT, "GET $url |") or die "Can't fetch $url: $!\n";
++        # freshmeat answer with an HTML page when project doesn't exist
++        $twig->safe_parse(\*INPUT);
++        close(INPUT);
++
++        return $version;
++    }
++}
++
++sub _url {
++    my ($self, $name) = @_;
++    return "http://freshmeat.net/projects/$name";
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/GNOME.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/GNOME.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/GNOME.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,104 @@
++# $Id$
++package Youri::Check::Input::Updates::Source::GNOME;
++
++=head1 NAME
++
++Youri::Check::Input::Updates::Source::GNOME - GNOME updates source
++
++=head1 DESCRIPTION
++
++This source plugin for L<Youri::Check::Input::Updates> collects updates
++available from GNOME.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use LWP::UserAgent;
++use HTML::TokeParser;
++use List::MoreUtils 'any';
++use base 'Youri::Check::Input::Updates::Source';
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Updates::Source::Gnome object.
++
++Specific parameters:
++
++=over
++
++=item url $url
++
++URL to GNOME sources directory (default:
++http://fr2.rpmfind.net/linux/gnome.org/sources)
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        url => 'http://fr2.rpmfind.net/linux/gnome.org/sources/', # default url
++	# We use HTTP as it offers a better sorting (1.2 < 1.10)
++        @_
++    );
++
++    $self->{_agent} = LWP::UserAgent->new();
++    my $response = $self->{_agent}->get($options{url});
++    if($response->is_success()) {
++        my $parser = HTML::TokeParser->new(\$response->content());
++        while (my $token = $parser->get_tag('a')) {
++            my $href = $token->[1]->{href};
++            next unless $href =~ /^([-\w]+)\/$/o;
++            $self->{_names}->{$1} = 1;
++        }
++    }
++
++    $self->{_url} = $options{url};
++}
++
++sub _version {
++    my ($self, $name) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return unless $self->{_names}->{$name};
++    
++    my $response = $self->{_agent}->get("$self->{_url}/$name/");
++    if($response->is_success()) {
++        my $major;
++        my $parser = HTML::TokeParser->new(\$response->content());
++        while (my $token = $parser->get_tag('a')) {
++            my $href = $token->[1]->{href};
++            next unless $href =~ /^([.\d]+)\/$/o;
++            $major = $1;
++        }
++        return unless $major;
++
++        $response = $self->{_agent}->get("$self->{_url}/$name/$major/");
++        if($response->is_success()) {
++            $parser = HTML::TokeParser->new(\$response->content());
++            while (my $token = $parser->get_tag('a')) {
++                my $href = $token->[1]->{href};
++                next unless $href =~ /^LATEST-IS-([.\d]+)$/o;
++                return $1;
++            }
++        }
++    }
++}
++
++sub _url {
++    my ($self, $name) = @_;
++    return $self->{_url}."$name/";
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Gentoo.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Gentoo.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Gentoo.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,75 @@
++# $Id: Gentoo.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Updates::Source::Gentoo;
++
++=head1 NAME
++
++Youri::Check::Input::Updates::Source::Gentoo - Gentoo updates source
++
++=head1 DESCRIPTION
++
++This source plugin for L<Youri::Check::Input::Updates> collects updates
++available from Gentoo.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use LWP::Simple;
++use base 'Youri::Check::Input::Updates::Source';
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Updates::Source::Gentoo object.
++
++Specific parameters:
++
++=over
++
++=item url $url
++
++URL to Gentoo snapshots directory (default:
++http://gentoo.mirror.sdv.fr/snapshots)
++
++=back
++
++=cut
++
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        url => 'http://gentoo.mirror.sdv.fr/snapshots', # default URL
++        @_
++    );
++
++    my $versions;
++    my $content = get($options{url});
++    my $file;
++    while ($content =~ /<A HREF="(portage-\d{8}.tar.bz2)">/g) {
++        $file = $1;
++    }
++    open(INPUT, "GET $options{url}/$file | tar tjf - |") or croak "Can't fetch $options{url}/$file: $!";
++    while (my $line = <INPUT>) {
++        next unless $line =~ /.*\/([\w-]+)-([\d\.]+)(:?-r\d)?\.ebuild$/;
++        $versions->{$1} = $2;
++    }
++    close(INPUT);
++
++    $self->{_versions} = $versions;
++}
++
++sub _url {
++    my ($self, $name) = @_;
++    return "http://packages.gentoo.org/search/?sstring=$name";
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/NetBSD.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/NetBSD.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/NetBSD.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,75 @@
++# $Id$
++package Youri::Check::Input::Updates::Source::NetBSD;
++
++=head1 NAME
++
++Youri::Check::Input::Updates::Source::NetBSD - NetBSD source for updates
++
++=head1 DESCRIPTION
++
++This source plugin for L<Youri::Check::Input::Updates> collects updates
++ available from NetBSD.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use base 'Youri::Check::Input::Updates::Source';
++use IO::Ftp;
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Updates::Source::NetBSD object.
++
++Specific parameters:
++
++=over
++
++=item url $url
++
++URL to NetBSD mirror content file, without ftp: (default: //ftp.free.fr/mirrors/ftp.netbsd.org/NetBSD-current/pkgsrc/README-all.html)
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        url => '//ftp.free.fr/mirrors/ftp.netbsd.org/NetBSD-current/pkgsrc/README-all.html',
++        @_
++    );
++
++    my $versions;
++    my $urls;
++
++    my $in = IO::Ftp->new('<',$options{url}) or croak "Can't fetch $options{url}: $!";
++    while (my $line = <$in>) {
++        next unless $line =~ /<!-- (.+)-([^-]*?)(nb\d*)? \(for sorting\).*?href="([^"]+)"/;
++        my $name = $1;
++        my $version = $2;
++        $versions->{$name} = $version;
++        $urls->{$name} = $4;
++    }
++    close($in);
++
++    $self->{_versions} = $versions;
++    $self->{_urls} = $urls;
++    $self->{_url} = $options{url};
++}
++
++sub _url {
++    my ($self, $name) = @_;
++    return $self->{_urls}->{$name};
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/RAA.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/RAA.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/RAA.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,121 @@
++# $Id: RAA.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Updates::Source::RAA;
++
++=head1 NAME
++
++Youri::Check::Input::Updates::Source::RAA - RAA updates source
++
++=head1 DESCRIPTION
++
++This source plugin for L<Youri::Check::Input::Updates> collects updates
++available from RAA.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use SOAP::Lite;
++use List::MoreUtils 'any';
++use Youri::Package;
++use base 'Youri::Check::Input::Updates::Source';
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Updates::Source::RAA object.
++
++Specific parameters:
++
++=over
++
++=item url $url
++
++URL to RAA SOAP interface (default:
++http://www.ruby-lang.org/xmlns/soap/interface/RAA/0.0.4)
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        url => 'http://www.ruby-lang.org/xmlns/soap/interface/RAA/0.0.4/',
++        @_
++    );
++
++    my $raa = SOAP::Lite->service($options{url})
++        or croak "Can't connect to $options{url}";
++    
++    $self->{_raa}   = $raa;
++    $self->{_names} = $raa->names();
++}
++
++sub get_version {
++    my ($self, $package) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $name;
++    if (ref $package && $package->isa('Youri::Package')) {
++        # don't bother checking for non-ruby packages
++        if (
++            any { $_->[Youri::Package::DEPENDENCY_NAME] =~ /ruby/ }
++            $package->get_requires()
++        ) {
++            $name = $package->get_canonical_name();
++        } else {
++            return;
++        }
++    } else {
++        $name = $package;
++    }
++
++    # translate in grabber namespace
++    $name = $self->get_name($name);
++
++    # return if aliased to null 
++    return unless $name;
++
++    # susceptible to throw exception for timeout
++    eval {
++        my $gem = $self->{_raa}->gem($name);
++        return $gem->{project}->{version} if $gem;
++    };
++
++    return;
++}
++
++sub _url {
++    my ($self, $name) = @_;
++    return "http://raa.ruby-lang.org/project/$name/";
++}
++
++sub _name {
++    my ($self, $name) = @_;
++
++    if (ref $self) {
++        my $match = $name;
++        $match =~ s/^ruby[-_]//;
++        $match =~ s/[-_]ruby$//;
++        my @results =
++            grep { /^(ruby[-_])?\Q$match\E([-_]ruby)$/ }
++            @{$self->{_names}};
++        if (@results) {
++            return $results[0];
++        } else {
++            return $name;
++        }
++    } else {
++        return $name;
++    }
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Sourceforge.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Sourceforge.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source/Sourceforge.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,103 @@
++# $Id: Sourceforge.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Updates::Source::Sourceforge;
++
++=head1 NAME
++
++Youri::Check::Input::Updates::Source::Sourceforge - Sourceforge updates source
++
++=head1 DESCRIPTION
++
++This source plugin for L<Youri::Check::Input::Updates> collects updates
++available from Sourceforge.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use LWP::UserAgent;
++use HTML::TokeParser;
++use Youri::Check::Input::Updates;
++use base 'Youri::Check::Input::Updates::Source';
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Updates::Source::Sourceforge
++object.  
++
++No specific parameters.
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        @_
++    );
++
++    $self->{_agent} = LWP::UserAgent->new();
++}
++
++sub get_version {
++    my ($self, $package) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $name;
++    if (ref $package && $package->isa('Youri::Package')) {
++        # don't bother checking for packages without sf.net URL
++        my $url = $package->get_url();
++        if (
++            $url =~ /http:\/\/(.*)\.sourceforge\.net/ ||
++            $url =~ /http:\/\/.*sourceforge\.net\/projects\/([^\/]+)/
++        ) {
++            $name = $package->get_canonical_name();
++        } else {
++            return;
++        }
++    } else {
++        $name = $package;
++    }
++
++    # translate in grabber namespace
++    $name = $self->get_name($name);
++
++    # return if aliased to null 
++    return unless $name;
++
++    my $response = $self->{_agent}->get($self->_url($name));
++    if($response->is_success()) {
++        my $max = 0;
++        my $parser = HTML::TokeParser->new(\$response->content());
++        while (my $token = $parser->get_tag('a')) {
++            my $text = $parser->get_trimmed_text("/$token->[0]");
++            next unless $text;
++            next unless $text =~ /^
++                \Q$name\E
++                [._-]?($Youri::Check::Input::Updates::VERSION_REGEXP)
++                [._-]?(w(?:in)?(?:32)?|mips|sparc|bin|ppc|i\d86|src|sources?)?
++                \.(?:tar\.(?:gz|bz2)|tgz|zip)
++                $/iox;
++            my $version = $1;
++            my $arch    = $2;
++            next if $arch && $arch !~ /(src|sources?)/;
++            $max = $version if Youri::Check::Input::Updates::is_newer($version, $max);
++        }
++        return $max if $max;
++    }
++    return;
++}
++
++sub _url {
++    my ($self, $name) = @_;
++    return "http://prdownloads.sourceforge.net/$name/";
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates/Source.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,240 @@
++# $Id: Source.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input::Updates::Source;
++
++=head1 NAME
++
++Youri::Check::Input::Updates::Source - Abstract updates source
++
++=head1 DESCRIPTION
++
++This abstract class defines the updates source interface for
++L<Youri::Check::Input::Updates>.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input::Updates object.
++
++Generic parameters (subclasses may define additional ones):
++
++=over
++
++=item aliases $aliases
++
++Hash of package aliases.
++
++=back
++
++Warning: do not call directly, call subclass constructor instead.
++
++=cut
++
++sub new {
++    my $class = shift;
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my %options = (
++        id          => '',    # object id
++        test        => 0,     # test mode
++        verbose     => 0,     # verbose mode
++        aliases     => undef, # aliases
++        resolver    => undef, # maintainer resolver
++        preferences => undef, # maintainer preferences
++        check_id    => '',    # parent check id
++        @_
++    );
++
++    if ($options{aliases}) {
++        croak "aliases should be an hashref" unless ref $options{aliases} eq 'HASH';
++    }
++    if ($options{resolver}) {
++        croak "resolver should be a Youri::Check::Maintainer::Resolver object" unless $options{resolver}->isa("Youri::Check::Maintainer::Resolver");
++    }
++    if ($options{preferences}) {
++        croak "preferences should be a Youri::Check::Maintainer::Preferences object" unless $options{preferences}->isa("Youri::Check::Maintainer::Preferences");
++    }
++
++    my $self = bless {
++        _id          => $options{id},
++        _test        => $options{test},
++        _verbose     => $options{verbose},
++        _aliases     => $options{aliases},
++        _resolver    => $options{resolver},
++        _preferences => $options{preferences},
++        _check_id    => $options{check_id},
++    }, $class;
++
++    $self->_init(%options);
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head1 INSTANCE METHODS
++
++Excepted explicit statement, package name is expressed with Mandriva naming
++conventions.
++
++=head2 get_id()
++
++Returns source identity.
++
++=cut
++
++sub get_id {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_id};
++}
++
++=head2 get_version($package)
++
++Returns available version for given package, which can be either a full
++L<Youri::Package> object or just a package name.
++
++=cut
++
++sub get_version {
++    my ($self, $package) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $name = ref $package && $package->isa('Youri::Package') ?
++        $package->get_canonical_name() :
++        $package;
++
++    # translate in grabber namespace
++    $name = $self->get_name($name);
++
++    # return if aliased to null 
++    return unless $name;
++
++    # return subclass computation
++    return $self->_version($name);
++}
++
++=head2 get_url($name)
++
++Returns the URL of information source for package with given name.
++
++=cut
++
++sub get_url {
++    my ($self, $name) = @_;
++
++    # retun subclass computation
++    return $self->_url($self->get_name($name));
++}
++
++=head2 name($name)
++
++Returns name converted to specific source naming conventions for package with given name.
++
++=cut
++
++sub get_name {
++    my ($self, $name) = @_;
++    croak "Not a class method" unless ref $self;
++
++    # return config aliases if it exists
++    if ($self->{_aliases} ) {
++        return $self->{_aliases}->{$name} if exists $self->{_aliases}->{$name};
++    }
++
++    # return maintainer aliases if it exists
++    if ($self->{_resolver} && $self->{_preferences}) {
++        my $maintainer = $self->{_resolver}->get_maintainer($name);
++        if ($maintainer) {
++            my $aliases = $self->{_preferences}->get_preference(
++                $maintainer,
++                $self->{_check_id},
++                'aliases'
++            );
++            if ($aliases) {
++                if ($aliases->{all}) {
++                    return $aliases->{all}->{$name} if exists $aliases->{all}->{$name};
++                }
++                if ($aliases->{$self->{_id}}) {
++                    return $aliases->{$self->{_id}}->{$name} if exists $aliases->{$self->{_id}}->{$name};
++                }
++            }
++        }
++    }
++
++    # return return subclass computation
++    return $self->_name($name);
++}
++
++=head2 _version($name)
++
++Hook called by default B<version()> implementation after name translation.
++
++=cut
++
++sub _version {
++    my ($self, $name) = @_;
++    return $self->{_versions}->{$name};
++}
++
++=head2 _url($name)
++
++Hook called by default B<url()> implementation after name translation.
++
++=cut
++
++sub _url {
++    my ($self, $name) = @_;
++    return undef;
++}
++
++=head2 _name($name)
++
++Hook called by default B<name()> implementation if given name was not found in
++the aliases.
++
++=cut
++
++sub _name {
++    my ($self, $name) = @_;
++    return $name;
++}
++
++=head1 SUBCLASSING
++
++The following methods have to be implemented:
++
++=over
++
++=item version
++
++As an alternative, the B<_version()> hook can be implemented.
++
++=item url
++
++As an alternative, the <_url()> hook can be implemented.
++
++=item name
++
++As an alternative, the B<_name()> hook can be implemented.
++
++=back
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input/Updates.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -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('', at v1) eq (join '', at 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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Input.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Input.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Input.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,120 @@
++# $Id: Input.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Input;
++
++=head1 NAME
++
++Youri::Check::Input - Abstract input plugin
++
++=head1 DESCRIPTION
++
++This abstract class defines input plugin interface.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Youri::Utils;
++
++use constant WARNING => 'warning';
++use constant ERROR => 'error';
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Input object.
++
++No generic parameters (subclasses may define additional ones).
++
++Warning: do not call directly, call subclass constructor instead.
++
++=cut
++
++sub new {
++    my $class = shift;
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my %options = (
++        id          => '',    # object id
++        test        => 0,     # test mode
++        verbose     => 0,     # verbose mode
++        resolver    => undef, # maintainer resolver
++        preferences => undef, # maintainer preferences
++        @_
++    );
++
++    if ($options{resolver}) {
++        croak "resolver should be a Youri::Check::Maintainer::Resolver object" unless $options{resolver}->isa("Youri::Check::Maintainer::Resolver");
++    }
++    if ($options{preferences}) {
++        croak "preferences should be a Youri::Check::Maintainer::Preferences object" unless $options{preferences}->isa("Youri::Check::Maintainer::Preferences");
++    }
++
++    my $self = bless {
++        _id          => $options{id},
++        _test        => $options{test},
++        _verbose     => $options{verbose},
++        _resolver    => $options{resolver},
++        _preferences => $options{preferences},
++    }, $class;
++
++    $self->_init(%options);
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head1 INSTANCE METHODS
++
++=head2 get_id()
++
++Returns plugin identity.
++
++=cut
++
++sub get_id {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_id};
++}
++
++=head2 prepare(@medias)
++
++Perform optional preliminary initialisation, using given list of
++<Youri::Media> objects.
++
++=cut
++
++sub prepare {
++    # do nothing
++}
++
++=head2 run($media, $resultset)
++
++Check the packages from given L<Youri::Media> object, and store the
++result in given L<Youri::Check::Resultset> object.
++
++=head1 SUBCLASSING
++
++The following methods have to be implemented:
++
++=over
++
++=item run
++
++=back
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Preferences/File.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Preferences/File.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Preferences/File.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,87 @@
++# $Id: File.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Maintainer::Preferences::File;
++
++=head1 NAME
++
++Youri::Check::Maintainer::Preferences::File - File-based maintainer preferences implementation
++
++=head1 DESCRIPTION
++
++This is a file-based L<Youri::Check::Maintainer::Preferences> implementation.
++
++It uses files in maintainer home directories.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Youri::Config;
++use base 'Youri::Check::Maintainer::Preferences';
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Maintainer::Preferences::File object.
++
++No specific parameters.
++
++=cut
++
++sub get_preference {
++    my ($self, $maintainer, $plugin, $value) = @_;
++    croak "Not a class method" unless ref $self;
++    return unless $maintainer && $plugin && $value;
++
++    print "Retrieving maintainer $maintainer preferences\n"
++        if $self->{_verbose} > 0;
++
++    $self->_load_config($maintainer)
++        unless exists $self->{_config}->{$maintainer};
++
++    return $self->{_config}->{$maintainer} ?
++        $self->{_config}->{$maintainer}->get($plugin . '_' . $value) :
++        undef;
++}
++
++sub _load_config {
++    my ($self, $maintainer) = @_;
++
++    print "Attempting to load maintainers preferences for $maintainer\n" if $self->{_verbose} > 1;
++
++
++    my ($login) = $maintainer =~ /^(\S+)\@\S+$/;
++    my $home = (getpwnam($login))[7];
++    my $file = "$home/.youri/check.prefs";
++
++    if (-f $file && -r $file) {
++        print "Found, loading\n" if $self->{_verbose} > 1;
++        my $config = Youri::Config->new(
++            {
++                CREATE => 1,
++                GLOBAL => {
++                   DEFAULT  => undef,
++                   EXPAND   => EXPAND_VAR | EXPAND_ENV,
++                   ARGCOUNT => ARGCOUNT_ONE,
++                }
++            }
++        );
++        $config->file($file);
++        $self->{_config}->{$maintainer} = $config;
++    } else {
++        print "Not found, aborting\n" if $self->{_verbose} > 1;
++        $self->{_config}->{$maintainer} = undef;
++    }
++
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Preferences.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Preferences.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Preferences.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,80 @@
++# $Id: Preferences.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Maintainer::Preferences;
++
++=head1 NAME
++
++Youri::Check::Maintainer::Preferences - Abstract maintainer preferences
++
++=head1 DESCRIPTION
++
++This abstract class defines Youri::Check::Maintainer::Preferences interface.
++
++=head1 SYNOPSIS
++
++    use Youri::Check::Maintainer::Preferences::Foo;
++
++    my $preferences = Youri::Check::Maintainer::Preferences::Foo->new();
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Maintainer::Preferences object.
++
++Warning: do not call directly, call subclass constructor instead.
++
++=cut
++
++sub new {
++    my $class = shift;
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my %options = (
++        test    => 0,     # test mode
++        verbose => 0,     # verbose mode
++        @_
++    );
++
++    my $self = bless {
++        _test    => $options{test},
++        _verbose => $options{verbose},
++    }, $class;
++
++    $self->_init(%options);
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head2 get_preference($maintainer, $plugin, $item)
++
++Returns preference of given maintainer for given plugin and configuration item.
++
++=head1 SUBCLASSING
++
++The following methods have to be implemented:
++
++=over
++
++=item get
++
++=back
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver/Bugzilla.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver/Bugzilla.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver/Bugzilla.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,100 @@
++# $Id: Bugzilla.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Maintainer::Resolver::Bugzilla;
++
++=head1 NAME
++
++Youri::Check::Maintainer::Resolver::Bugzilla - Bugzilla-based maintainer resolver
++
++=head1 DESCRIPTION
++
++This is a Bugzilla-based L<Youri::Check::Maintainer::Resolver> implementation.
++
++It uses Bugzilla SQL database for resolving maintainers.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Youri::Bugzilla;
++use base 'Youri::Check::Maintainer::Resolver';
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Maintainer::Resolver::Bugzilla object.
++
++Specific parameters:
++
++=over
++
++=item host $host
++
++Bugzilla database host.
++
++=item base $base
++
++Bugzilla database name.
++
++=item user $user
++
++Bugzilla database user.
++
++=item pass $pass
++
++Bugzilla database password.
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        host => '', # host of the bug database
++        base => '', # name of the bug database
++        user => '', # user of the bug database
++        pass => '', # pass of the bug database
++        @_
++    );
++
++    croak "No host given" unless $options{host};
++    croak "No base given" unless $options{base};
++    croak "No user given" unless $options{user};
++    croak "No pass given" unless $options{pass};
++
++    my $bugzilla = Youri::Bugzilla->new(
++        $options{host},
++        $options{base},
++        $options{user},
++        $options{pass}
++    );
++
++    $self->{_bugzilla} = $bugzilla;
++}
++
++sub get_maintainer {
++    my ($self, $package) = @_;
++    croak "Not a class method" unless ref $self;
++
++     my $name = ref $package && $package->isa('Youri::Package') ?
++        $package->get_canonical_name() :
++        $package;
++
++    $self->{_maintainers}->{$name} =
++        $self->{_bugzilla}->get_maintainer($name)
++        unless exists $self->{_maintainers}->{$name};
++
++    return $self->{_maintainers}->{$name};
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver/CGI.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver/CGI.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver/CGI.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,79 @@
++# $Id: CGI.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Maintainer::Resolver::CGI;
++
++=head1 NAME
++
++Youri::Check::Maintainer::Resolver::CGI - CGI-based maintainer resolver
++
++=head1 DESCRIPTION
++
++This is a CGI-based L<Youri::Check::Maintainer::Resolver> implementation.
++
++It uses a remote CGI to resolve maintainers.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use base 'Youri::Check::Maintainer::Resolver';
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Maintainer::Resolver::CGI object.
++
++Specific parameters:
++
++=over
++
++=item url $url
++
++CGI's URL.
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        url => '', # url to fetch maintainers
++        @_
++    );
++
++    croak "No URL given" unless $options{url};
++
++    open (INPUT, "GET $options{url} |");
++    while (<INPUT>) {
++        chomp;
++        my ($package, $maintainer) = split(/\t/, $_);
++        $self->{_maintainers}->{$package} = $maintainer if $maintainer;
++    }
++    close(INPUT);
++}
++
++sub get_maintainer {
++    my ($self, $package) = @_;
++    croak "Not a class method" unless ref $self;
++
++    print "Retrieving package $package maintainer\n"
++        if $self->{_verbose} > 0;
++
++    my $name = ref $package && $package->isa('Youri::Package') ?
++        $package->get_canonical_name() :
++        $package;
++
++    return $self->{_maintainers}->{$name};
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Maintainer/Resolver.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,86 @@
++# $Id: Resolver.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Maintainer::Resolver;
++
++=head1 NAME
++
++Youri::Check::Maintainer::Resolver - Abstract maintainer resolver
++
++=head1 DESCRIPTION
++
++This abstract class defines Youri::Check::Maintainer::Resolver interface.
++
++=head1 SYNOPSIS
++
++    use Youri::Check::Maintainer::Resolver::Foo;
++
++    my $resolver = Youri::Check::Maintainer::Resolver::Foo->new();
++
++    print $resolver->get_maintainer('foo');
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Youri::Utils;
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Maintainer::Resolver object.
++
++No generic parameters (subclasses may define additional ones).
++
++Warning: do not call directly, call subclass constructor instead.
++
++=cut
++
++sub new {
++    my $class = shift;
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my %options = (
++        test    => 0,  # test mode
++        verbose => 0,  # verbose mode
++        @_
++    );
++
++    my $self = bless {
++        _test    => $options{test},
++        _verbose => $options{verbose}
++    }, $class;
++
++    $self->_init(%options);
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head2 get_maintainer($package)
++
++Returns maintainer for given package, which can be either a full
++L<Youri::Package> object or just a package name.
++
++=head1 SUBCLASSING
++
++The following methods have to be implemented:
++
++=over
++
++=item get_maintainer
++
++=back
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/HTML.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/HTML.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/HTML.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,222 @@
++# $Id: HTML.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Output::File::Format::HTML;
++
++=head1 NAME
++
++Youri::Check::Output::File::Format::HTML - File HTML format support
++
++=head1 DESCRIPTION
++
++This format plugin for L<Youri::Check::Output::File> provides HTML format
++support.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use CGI;
++use base 'Youri::Check::Output::File::Format';
++
++sub extension {
++    return 'html';
++}
++
++sub _init {
++    my $self = shift;
++    my %options = (
++        style => <<EOF, # css style
++h1 {
++    text-align:center;
++}
++table {
++    border-style:solid; 
++    border-width:1px; 
++    border-color:black;
++    width:100%;
++}
++tr.odd { 
++    background-color:white;
++}
++tr.even { 
++    background-color:silver;
++}
++p.footer {
++    font-size:smaller;
++    text-align:center;
++}
++EOF
++        @_
++    );
++
++    $self->{_style} = $options{style};
++    $self->{_cgi}   = CGI->new();
++}
++
++sub get_report {
++    my ($self, $time, $title, $iterator, $type, $columns, $links, $maintainer) = @_;
++
++    my $content;
++    my $lead_columns = [ 
++        $maintainer ?
++            qw/package media/ :
++            qw/package media maintainer/
++        ];
++    my $line;
++    my @results;
++    $content .= $self->{_cgi}->start_table();
++    $content .= $self->{_cgi}->Tr([
++        $self->{_cgi}->th([ 
++            @$lead_columns,
++            @$columns
++        ])
++    ]);
++    while (my $result = $iterator->get_result()) {
++        if (@results && $result->{package} ne $results[0]->{package}) {
++            $content .= $self->_get_formated_results(
++                $lead_columns,
++                $columns,
++                $links,
++                $line++ % 2 ? 'odd' : 'even',
++                \@results
++            );
++            @results = ();
++        }
++        push(@results, $result);
++    }
++    $content .= $self->_get_formated_results(
++        $lead_columns,
++        $columns,
++        $links,
++        $line++ % 2 ? 'odd' : 'even',
++        \@results
++    );
++    $content .= $self->{_cgi}->end_table();
++
++    return $self->_get_html_page($time, $title, \$content);
++}
++
++sub get_index {
++    my ($self, $time, $title, $reports, $maintainers) = @_;
++
++    my $content;
++
++    if ($reports) {
++        $content .= $self->{_cgi}->h2("Reports");
++        my @types = keys %{$reports};
++
++        $content .= $self->{_cgi}->start_ul();
++        foreach my $type (sort @types) {
++            my $item;
++            $item = $self->{_cgi}->a(
++                { href => "$type.html" },
++                $type
++            );
++            foreach my $extension (@{$reports->{$type}}) {
++                next if ($extension eq extension());
++                $item .= " ".$self->{_cgi}->a(
++                        { href => "$type.$extension" },
++                        "[$extension]"
++                );
++            }
++            $content .= $self->{_cgi}->li($item);
++        }
++        $content .= $self->{_cgi}->end_ul();
++    }
++
++    if ($maintainers) {
++        $content .= $self->{_cgi}->h2("Individual reports");
++
++        $content .= $self->{_cgi}->start_ul();
++        foreach my $maintainer (sort @{$maintainers}) {
++            $content .= $self->{_cgi}->li(
++                $self->{_cgi}->a(
++                    { href => "$maintainer/index.html" },
++                    _obfuscate($maintainer)
++                )
++            );
++        }
++        $content .= $self->{_cgi}->end_ul();
++    }
++
++    return $self->_get_html_page($time, $title, \$content);
++}
++
++sub _get_formated_results {
++    my ($self, $lead_columns, $columns, $links, $class, $results) = @_;
++
++    my $content;
++    $content .= $self->{_cgi}->end_Tr();
++    for my $i (0 .. $#$results) {
++        $content .= $self->{_cgi}->start_Tr(
++            { class => $class }
++        );
++        if ($i == 0) {
++            # first line contains spanned cells
++            $content .= $self->{_cgi}->td(
++                { rowspan => scalar @$results },
++                [
++                    map { $results->[$i]->{$_} }
++                    @$lead_columns
++                ]
++            );
++        }
++        $content .= $self->{_cgi}->td(
++            [ 
++                map { 
++                    $links->{$_} && $results->[$i]->{$links->{$_}} ?
++                        $self->{_cgi}->a(
++                            { href => $results->[$i]->{$links->{$_}} },
++                            $self->{_cgi}->escapeHTML($results->[$i]->{$_})
++                        ) :
++                        $self->{_cgi}->escapeHTML($results->[$i]->{$_})
++                } @$columns
++            ]
++        );
++        $content .= $self->{_cgi}->end_Tr();
++    }
++
++    return $content;
++}
++
++
++sub _get_html_page {
++    my ($self, $time, $title, $body) = @_;
++
++    my $content;
++    $content .= $self->{_cgi}->start_html(
++        -title => $title,
++        -style => { code => $self->{_style} }
++    );
++    $content .= $self->{_cgi}->h1($title);
++    $content .= $$body;
++    $content .= $self->{_cgi}->hr();
++    $content .= $self->{_cgi}->p(
++        { class => 'footer' },
++        "Page generated $time"
++    );
++    $content .= $self->{_cgi}->end_html();
++
++    return \$content;
++}
++
++sub _obfuscate {
++    my ($email) = @_;
++
++    return unless $email;
++
++    $email =~ s/\@/ at /;
++    $email =~ s/\./ dot /;
++
++    return $email;
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/RSS.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/RSS.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/RSS.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,68 @@
++# $Id$
++package Youri::Check::Output::File::Format::RSS;
++
++=head1 NAME
++
++Youri::Check::Output::File::Format::RSS - File RSS format support
++
++=head1 DESCRIPTION
++
++This format plugin for L<Youri::Check::Output::File> provides RSS format
++support.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use XML::RSS;
++use base 'Youri::Check::Output::File::Format';
++
++sub extension {
++    return 'rss';
++}
++
++sub get_report {
++    my ($self, $time, $title, $iterator, $type, $columns, $links, $maintainer) = @_;
++
++    return unless $maintainer;
++
++    my $rss = new XML::RSS (version => '2.0');
++    $rss->channel(
++        title       => $title,
++        description => $title,
++        language    => 'en',
++        ttl         => 1440
++    );
++
++    while (my $result = $iterator->get_result()) {
++        if ($type eq 'updates') {
++            $rss->add_item(
++                title       => "$result->{package} $result->{available} is available",
++                description => "Current version is $result->{current}",
++                link        => $result->{url} ?
++                    $result->{url} : $result->{source},
++                guid => "$result->{package}-$result->{available}"
++            );
++        } else {
++            $rss->add_item(
++                title       => "[$type] $result->{package}",
++                description => join("\n", (map { $result->{$_} || '' } @$columns)),
++		link        => $result->{url},
++                guid        => "$type-$result->{package}"
++            );
++        }
++    }
++
++    return \$rss->as_string();
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/Text.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/Text.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format/Text.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,88 @@
++# $Id: Text.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Output::File::Format::Text;
++
++=head1 NAME
++
++Youri::Check::Output::File::Format::Text - File text format support
++
++=head1 DESCRIPTION
++
++This format plugin for L<Youri::Check::Output::File> provides text format
++support.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use base 'Youri::Check::Output::File::Format';
++
++sub extension {
++    return 'txt';
++}
++
++sub get_report {
++    my ($self, $time, $title, $iterator, $type, $columns, $links, $maintainer) = @_;
++    
++    my $content;
++    $content .= $title;
++    $content .= "\n";
++
++    my $lead_columns = [ 
++        $maintainer ?
++            qw/package media/ :
++            qw/package media maintainer/
++        ];
++    my @results;
++    $content .= join("\t", @$lead_columns, @$columns) . "\n";
++    while (my $result = $iterator->get_result()) {
++        if (@results && $result->{package} ne $results[0]->{package}) {
++            $content .= $self->_get_formated_results(
++                $lead_columns,
++                $columns,
++                \@results
++            );
++            @results = ();
++        }
++        push(@results, $result);
++    }
++    $content .= $self->_get_formated_results(
++        $lead_columns,
++        $columns,
++        \@results
++    );
++
++    $content .= "\n";
++    $content .= "Page generated $time\n";
++
++    return \$content;
++}
++
++sub _get_formated_results {
++    my ($self, $lead_columns, $columns, $results) = @_;
++
++    my $content;
++    $content .= join(
++        "\t",
++        (map { $results->[0]->{$_} || '' } @$lead_columns),
++        (map { $results->[0]->{$_} || '' } @$columns)
++    ) . "\n";
++    for my $i (1 .. $#$results) {
++        $content .= join(
++            "\t",
++            (map { '' } @$lead_columns),
++            (map { $results->[$i]->{$_} || '' } @$columns)
++        ) . "\n";
++    }
++    return $content;
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File/Format.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,66 @@
++# $Id: Base.pm 579 2006-01-09 21:17:54Z guillomovitch $
++package Youri::Check::Output::File::Format;
++
++=head1 NAME
++
++Youri::Check::Output::File::Format - Abstract file format support
++
++=head1 DESCRIPTION
++
++This abstract class defines the format support interface for
++L<Youri::Check::Output::File>.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++
++sub new {
++    my $class = shift;
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my %options = (
++        id      => '',
++        test    => 0,
++        verbose => 0,
++        @_
++    );
++
++    my $self = bless {
++        _id         => $options{id},
++        _test       => $options{test},
++        _verbose    => $options{verbose},
++    }, $class;
++
++    $self->_init(%options);
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head2 get_id()
++
++Returns format handler identity.
++
++=cut
++
++sub get_id {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_id};
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/File.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,203 @@
++# $Id: Text.pm 523 2005-10-11 08:36:49Z misc $
++package Youri::Check::Output::File;
++
++=head1 NAME
++
++Youri::Check::Output::File - Report results in files
++
++=head1 DESCRIPTION
++
++This plugin reports results in files. Additional subplugins handle specific
++formats.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use File::Basename;
++use File::Path;
++use DateTime;
++use Youri::Utils;
++use base 'Youri::Check::Output';
++
++sub _init {
++    my $self = shift;
++    my %options = (
++        to      => '', # target directory
++        noclean => 0,  # don't clean up target directory
++        noempty => 0,  # don't generate empty reports
++        formats => undef,
++        @_
++    );
++
++    croak "no format defined" unless $options{formats};
++    croak "formats should be an hashref" unless ref $options{formats} eq 'HASH';
++
++    my $now = DateTime->now(time_zone => 'local');
++    my $time = "the " . $now->ymd() . " at " . $now->hms();
++
++    $self->{_to}      = $options{to};
++    $self->{_noclean} = $options{noclean};
++    $self->{_noempty} = $options{noempty};
++    $self->{_time}    = $time;
++
++    foreach my $id (keys %{$options{formats}}) {
++        print "Creating format $id\n" if $options{verbose};
++        eval {
++            push(
++                @{$self->{_formats}},
++                create_instance(
++                    'Youri::Check::Output::File::Format',
++                    id        => $id,
++                    test      => $options{test},
++                    verbose   => $options{verbose},
++                    %{$options{formats}->{$id}}
++                )
++            );
++        };
++        print STDERR "Failed to create format $id: $@\n" if $@;
++    }
++
++    croak "no formats created" unless @{$self->{_formats}};
++}
++
++sub _init_report {
++    my ($self) = @_;
++
++    # clean up output directory
++    unless ($self->{_test} || $self->{_noclean} || !$self->{_to}) {
++        my @files = glob($self->{_to} . '/*');
++        rmtree(\@files) if @files;
++    }
++}
++
++sub _global_report {
++    my ($self, $resultset, $type, $columns, $links) = @_;
++
++    foreach my $format (@{$self->{_formats}}) {
++        my $iterator = $resultset->get_iterator(
++            $type,
++            [ 'package' ]
++        );
++
++        return if $self->{_noempty} && ! $iterator->has_results();
++
++        my $content = $format->get_report(
++            $self->{_time},
++            "$type global report",
++            $iterator,
++            $type,
++            $columns,
++            $links,
++            undef
++        );
++
++        # create and register file
++        my $extension = $format->extension();
++        $self->_write_file(
++            "$self->{_to}/$type.$extension",
++            $content
++        );
++        push(
++            @{$self->{_files}->{global}->{$type}},
++            $extension
++        );
++    }
++}
++
++sub _individual_report {
++    my ($self, $resultset, $type, $columns, $links, $maintainer) = @_;
++
++    foreach my $format (@{$self->{_formats}}) {
++        my $iterator = $resultset->get_iterator(
++            $type,
++            [ 'package' ],
++            { maintainer => [ $maintainer ] }
++        );
++
++        return if $self->{_noempty} && ! $iterator->has_results();
++
++        my $content = $format->get_report(
++            $self->{_time},
++            "$type individual report for $maintainer",
++            $iterator,
++            $type,
++            $columns,
++            $links,
++            $maintainer
++        );
++
++        # create and register file
++        my $extension = $format->extension();
++        $self->_write_file(
++            "$self->{_to}/$maintainer/$type.$extension",
++            $content
++        );
++        push(
++            @{$self->{_files}->{maintainers}->{$maintainer}->{$type}},
++            $extension
++        );
++    }
++}
++
++sub _finish_report {
++    my ($self, $types, $maintainers) = @_;
++
++    foreach my $format (@{$self->{_formats}}) {
++        next unless $format->can('get_index');
++        my $extension = $format->extension();
++        print STDERR "writing global index page\n" if $self->{_verbose};
++        $self->_write_file(
++            "$self->{_to}/index.$extension",
++            $format->get_index(
++                $self->{_time},
++                "QA global report",
++                $self->{_files}->{global},
++                [ keys %{$self->{_files}->{maintainers}} ],
++            )
++        );
++        foreach my $maintainer (@$maintainers) {
++            print STDERR "writing index page for $maintainer\n" if $self->{_verbose};
++
++            $self->_write_file(
++                "$self->{_to}/$maintainer/index.$extension",
++                $format->get_index(
++                    $self->{_time},
++                    "QA report for $maintainer",
++                    $self->{_files}->{maintainers}->{$maintainer},
++                    undef,
++                )
++            );
++        }
++    }
++}
++
++sub _write_file {
++    my ($self, $file, $content) = @_;
++
++    return unless $content;
++
++    my $dirname = dirname($file);
++    mkpath($dirname) unless -d $dirname;
++    
++    if ($self->{_test}) {
++        *OUT = *STDOUT;
++    } else {
++        open(OUT, ">$file") or die "Can't open file $file: $!";
++    }
++
++    print OUT $$content;
++
++    close(OUT) unless $self->{_test};
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format/HTML.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format/HTML.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format/HTML.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,158 @@
++# $Id: Mail.pm 580 2006-01-11 22:59:36Z guillomovitch $
++package Youri::Check::Output::Mail::Format::HTML;
++
++=head1 NAME
++
++Youri::Check::Output::Mail::Format::HTML - Mail HTML format support
++
++=head1 DESCRIPTION
++
++This format plugin for L<Youri::Check::Output::Mail> provides HTML format
++support.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use CGI;
++use base 'Youri::Check::Output::Mail::Format';
++
++sub type {
++    return 'text/html';
++}
++
++sub _init {
++    my $self = shift;
++    my %options = (
++        style => <<EOF, # css style
++h1 {
++    text-align:center;
++}
++table {
++    border-style:solid; 
++    border-width:1px; 
++    border-color:black;
++    width:100%;
++}
++tr.odd { 
++    background-color:white;
++}
++tr.even { 
++    background-color:silver;
++}
++p.footer {
++    font-size:smaller;
++    text-align:center;
++}
++EOF
++        @_
++    );
++
++    $self->{_style} = $options{style};
++    $self->{_cgi}   = CGI->new();
++}
++
++sub get_report {
++    my ($self, $time, $title, $iterator, $type, $columns, $links, $maintainer) = @_;
++
++    my $body;
++    my $lead_columns = [ 
++        $maintainer ?
++            qw/package media/ :
++            qw/package media maintainer/
++    ];
++    my $line;
++    my @results;
++    $body .= $self->{_cgi}->start_table();
++    $body .= $self->{_cgi}->Tr([
++        $self->{_cgi}->th([ 
++            @$lead_columns,
++            @$columns
++        ])
++    ]);
++    while (my $result = $iterator->get_result()) {
++        if (@results && $result->{package} ne $results[0]->{package}) {
++            $body .= $self->_get_formated_results(
++                $lead_columns,
++                $columns,
++                $links,
++                $line++ % 2 ? 'odd' : 'even',
++                \@results
++            );
++            @results = ();
++        }
++        push(@results, $result);
++    }
++    $body .= $self->_get_formated_results(
++        $lead_columns,
++        $columns,
++        $links,
++        $line++ % 2 ? 'odd' : 'even',
++        \@results
++    );
++    $body .= $self->{_cgi}->end_table();
++
++    my $content;
++    $content .= $self->{_cgi}->start_html(
++        -title => $title,
++        -style => { code => $self->{_style} }
++    );
++    $content .= $self->{_cgi}->h1($title);
++    $content .= $body;
++    $content .= $self->{_cgi}->hr();
++    $content .= $self->{_cgi}->p(
++        { class => 'footer' },
++        "Page generated $time"
++    );
++    $content .= $self->{_cgi}->end_html();
++
++    return \$content;
++}
++
++sub _get_formated_results {
++    my ($self, $lead_columns, $columns, $links, $class, $results) = @_;
++
++    my $content;
++    $content .= $self->{_cgi}->end_Tr();
++    for my $i (0 .. $#$results) {
++        $content .= $self->{_cgi}->start_Tr(
++            { class => $class }
++        );
++        if ($i == 0) {
++            # first line contains spanned cells
++            $content .= $self->{_cgi}->td(
++                { rowspan => scalar @$results },
++                [
++                    map { $results->[$i]->{$_} }
++                    @$lead_columns
++                ]
++            );
++        }
++        $content .= $self->{_cgi}->td(
++            [ 
++                map { 
++                    $links->{$_} && $results->[$i]->{$links->{$_}} ?
++                        $self->{_cgi}->a(
++                            { href => $results->[$i]->{$links->{$_}} },
++                            $self->{_cgi}->escapeHTML($results->[$i]->{$_})
++                        ) :
++                        $self->{_cgi}->escapeHTML($results->[$i]->{$_})
++                } @$columns
++            ]
++        );
++        $content .= $self->{_cgi}->end_Tr();
++    }
++
++    return $content;
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format/Text.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format/Text.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format/Text.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,83 @@
++# $Id: Mail.pm 580 2006-01-11 22:59:36Z guillomovitch $
++package Youri::Check::Output::Mail::Format::Text;
++
++=head1 NAME
++
++Youri::Check::Output::Mail::Format::Text - Mail text format support
++
++=head1 DESCRIPTION
++
++This format plugin for L<Youri::Check::Output::Mail> provides text format
++support.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use base 'Youri::Check::Output::Mail::Format';
++
++sub type {
++    return 'text/plain';
++}
++
++sub get_report {
++    my ($self, $time, $title, $iterator, $type, $columns, $links, $maintainer) = @_;
++
++    my $content;
++    my $lead_columns = [ 
++        $maintainer ?
++            qw/package media/ :
++            qw/package media maintainer/
++        ];
++    my @results;
++    $content .= join("\t", @$lead_columns, @$columns) . "\n";
++    while (my $result = $iterator->get_result()) {
++        if (@results && $result->{package} ne $results[0]->{package}) {
++            $content .= $self->_get_formated_results(
++                $lead_columns,
++                $columns,
++                \@results
++            );
++            @results = ();
++        }
++        push(@results, $result);
++    }
++
++    $content .= $self->_get_formated_results(
++        $lead_columns,
++        $columns,
++        \@results
++    );
++
++    return \$content;
++}
++
++sub _get_formated_results {
++    my ($self, $lead_columns, $columns, $results) = @_;
++
++    my $content;
++    $content .= join(
++        "\t",
++        (map { $results->[0]->{$_} || '' } @$lead_columns),
++        (map { $results->[0]->{$_} || '' } @$columns)
++    ) . "\n";
++    for my $i (1 .. $#$results) {
++        $content .= join(
++            "\t",
++            (map { '' } @$lead_columns),
++            (map { $results->[$i]->{$_} || '' } @$columns)
++        ) . "\n";
++    }
++    return $content;
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail/Format.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,66 @@
++# $Id: Base.pm 579 2006-01-09 21:17:54Z guillomovitch $
++package Youri::Check::Output::Mail::Format;
++
++=head1 NAME
++
++Youri::Check::Output::Mail::Format - Abstract mail format support
++
++=head1 DESCRIPTION
++
++This abstract class defines the format support interface for
++L<Youri::Check::Output::Mail>.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++
++sub new {
++    my $class = shift;
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my %options = (
++        id      => '',
++        test    => 0,
++        verbose => 0,
++        @_
++    );
++
++    my $self = bless {
++        _id         => $options{id},
++        _test       => $options{test},
++        _verbose    => $options{verbose},
++    }, $class;
++
++    $self->_init(%options);
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head2 get_id()
++
++Returns format handler identity.
++
++=cut
++
++sub get_id {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_id};
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Output/Mail.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,156 @@
++# $Id: Mail.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Output::Mail;
++
++=head1 NAME
++
++Youri::Check::Output::Mail - Report results by mail
++
++=head1 DESCRIPTION
++
++This plugin reports results by mail. Additional subplugins handle specific
++formats.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use MIME::Entity;
++use Youri::Utils;
++use base 'Youri::Check::Output';
++
++sub _init {
++    my $self = shift;
++    my %options = (
++        from     => '', # mail from header
++        to       => '', # mail to header
++        reply_to => '', # mail reply-to header
++        mta      => '', # mta path
++        noempty  => 1,  # don't generate empty reports
++        formats  => {},
++        @_
++    );
++
++    croak "no format defined" unless $options{formats};
++    croak "formats should be an hashref" unless ref $options{formats} eq 'HASH';
++
++    $self->{_from}       = $options{from};
++    $self->{_to}         = $options{to};
++    $self->{_reply_to}   = $options{reply_to};
++    $self->{_mta}        = $options{mta};
++    $self->{_noempty}    = $options{noempty};
++
++    foreach my $id (keys %{$options{formats}}) {
++        print "Creating format $id\n" if $options{verbose};
++        eval {
++            push(
++                @{$self->{_formats}},
++                create_instance(
++                    'Youri::Check::Output::Mail::Format',
++                    id        => $id,
++                    test      => $options{test},
++                    verbose   => $options{verbose},
++                    %{$options{formats}->{$id}}
++                )
++            );
++        };
++        print STDERR "Failed to create format $id: $@\n" if $@;
++    }
++
++    croak "no formats created" unless @{$self->{_formats}};
++}
++
++sub _global_report {
++    my ($self, $resultset, $type, $columns, $links) = @_;
++
++    foreach my $format (@{$self->{_formats}}) {
++        my $iterator = $resultset->get_iterator(
++            $type,
++            [ 'package' ]
++        );
++
++        return if $self->{_noempty} && ! $iterator->has_results();
++
++        my $content = $format->get_report(
++            $self->{_time},
++            "$type global report",
++            $iterator,
++            $type,
++            $columns,
++            $links,
++            undef
++        );
++
++        $self->_send_mail(
++            $format->type(),
++            $self->{_to},
++            "$type global report",
++            $content,
++        );
++    }
++}
++
++sub _individual_report {
++    my ($self, $resultset, $type, $columns, $links, $maintainer) = @_;
++
++    foreach my $format (@{$self->{_formats}}) {
++        my $iterator = $resultset->get_iterator(
++            $type,
++            [ 'package' ],
++            { maintainer => [ $maintainer ] }
++        );
++
++        return if $self->{_noempty} && ! $iterator->has_results();
++
++        my $content = $format->get_report(
++            $self->{_time},
++            "$type individual report for $maintainer",
++            $iterator,
++            $type,
++            $columns,
++            $links,
++            $maintainer
++        );
++
++        $self->_send_mail(
++            $format->type(),
++            $maintainer,
++            "$type individual report for $maintainer",
++            $content,
++        );
++    }
++
++}
++
++sub _send_mail {
++    my ($self, $type, $to, $subject, $content) = @_;
++
++    return unless $content;
++
++    my $mail = MIME::Entity->build(
++        'Type'     => $type,
++        'From'     => $self->{_from},
++        'Reply-To' => $self->{_reply_to},
++        'To'       => $to,
++        'Subject'  => $subject,
++        'Data'     => $$content
++    );
++
++    if ($self->{_test}) {
++        $mail->print(\*STDOUT);
++    } else {
++        open(MAIL, "| $self->{_mta} -t -oi -oem") or die "Can't open MTA program: $!";
++        $mail->print(\*MAIL);
++        close MAIL;
++    }
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Output.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Output.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Output.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,190 @@
++# $Id: Output.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Check::Output;
++
++=head1 NAME
++
++Youri::Check::Output - Abstract output plugin
++
++=head1 DESCRIPTION
++
++This abstract class defines output plugin interface.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Youri::Utils;
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Check::Output object.
++
++Generic parameters (subclasses may define additional ones):
++
++=over
++
++=item global true/false
++
++Global reports generation (default: true).
++
++=item individual true/false
++
++Individual reports generation (default: true).
++
++=back
++
++Warning: do not call directly, call subclass constructor instead.
++
++=cut
++
++sub new {
++    my $class = shift;
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my %options = (
++        id         => '',
++        test       => 0,
++        verbose    => 0,
++        global     => 1,
++        individual => 1,
++        config     => undef,
++        @_
++    );
++
++    croak "Neither global nor individual reporting selected" unless $options{global} || $options{individual};
++
++    my $self = bless {
++        _id         => $options{id},
++        _test       => $options{test},
++        _verbose    => $options{verbose},
++        _global     => $options{global},
++        _individual => $options{individual},
++        _config     => $options{config}
++    }, $class;
++
++    $self->_init(%options);
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head1 INSTANCE METHODS
++
++=head2 get_id()
++
++Returns plugin identity.
++
++=cut
++
++sub get_id {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_id};
++}
++
++=head2 run($resultset)
++
++Reports the result stored in given L<Youri::Check::Resultset> object.
++
++=cut
++
++sub run {
++    my ($self, $resultset) = @_;
++
++    $self->_init_report();
++
++    # get types and maintainers list from resultset
++    my @maintainers = $resultset->get_maintainers();
++    my @types       = $resultset->get_types();
++
++    foreach my $type (@types) {
++        # get formatting instructions from class
++        my $class = $self->{_config}->get($type . '_class');
++        load($class);
++        my @columns = $class->columns();
++        my %links   = $class->links();
++
++        if ($self->{_global}) {
++            print STDERR "generating global report for $type\n" if $self->{_verbose};
++            $self->_global_report(
++                $resultset,
++                $type,
++                \@columns,
++                \%links
++            );
++        }
++
++        if ($self->{_individual}) {
++            foreach my $maintainer (@maintainers) {
++                print STDERR "generating individual report for $type and $maintainer\n" if $self->{_verbose};
++
++                $self->_individual_report(
++                    $resultset,
++                    $type,
++                    \@columns,
++                    \%links,
++                    $maintainer
++                );
++            }
++        }
++    }
++
++    $self->_finish_report(\@types, \@maintainers);
++}
++
++sub _init_report {
++    # do nothing
++}
++
++sub _global_report {
++    # do nothing
++}
++
++sub _individual_report {
++    # do nothing
++}
++
++sub _finish_report {
++    # do nothing
++}
++
++=head1 SUBCLASSING
++
++The following methods have to be implemented:
++
++=over
++
++=item run
++
++As an alternative, the following hooks can be implemented:
++
++=over
++
++=item _init_report
++
++=item _global_report
++
++=item _individual_report
++
++=item _finish_report
++
++=back
++
++=back
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset/DBI.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset/DBI.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset/DBI.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,372 @@
++# $Id: Result.pm 485 2005-08-01 21:48:21Z guillomovitch $
++package Youri::Check::Resultset::DBI;
++
++=head1 NAME
++
++Youri::Check::Resultset::DBI - DBI-based resultset
++
++=head1 DESCRIPTION
++
++This is a DBI-based L<Youri::Check::Resultset> implementation.
++
++It can be created with any DBI-supported database.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use DBI 1.38;
++use base 'Youri::Check::Resultset';
++
++my %tables = (
++    packages => {
++        id         => 'SERIAL PRIMARY KEY',
++        package    => 'TEXT',
++        media      => 'TEXT',
++        maintainer => 'TEXT',
++    }
++);
++
++my %queries = (
++    add_package =>
++        'INSERT INTO packages (package, media, maintainer) VALUES (?, ?, ?)',
++    get_package_id =>
++        'SELECT id FROM packages WHERE package = ?',
++    get_maintainers =>
++        'SELECT DISTINCT(maintainer) FROM packages WHERE maintainer IS NOT NULL',
++);
++
++=head1 CLASS METHODS
++
++=head2 new(%hash)
++
++Creates and returns a new Youri::Check::Resultset::DBI object.
++
++Specific parameters:
++
++=over
++
++=item driver $driver
++
++Use given string as DBI driver.
++
++=item base $base
++
++Use given string as database name.
++
++=item port $port
++
++Use given string as database port.
++
++=item user $user
++
++Use given string as database user.
++
++=item pass $pass
++
++Use given string as database password.
++
++=back
++
++=cut
++
++sub _init {
++    my $self    = shift;
++    my %options = (
++        driver => '', # driver
++        base   => '', # base
++        port   => '', # port
++        user   => '', # user
++        pass   => '', # pass
++        @_
++    );
++
++    croak "No driver defined" unless $options{driver};
++    croak "No base defined" unless $options{base};
++
++    my $datasource = "DBI:$options{driver}:dbname=$options{base}";
++    $datasource .= ";host=$options{host}" if $options{host};
++    $datasource .= ";port=$options{port}" if $options{port};
++
++    $self->{_dbh} = DBI->connect($datasource, $options{user}, $options{pass}, {
++        RaiseError => 1,
++        PrintError => 0,
++        AutoCommit => 1
++    }) or croak "Unable to connect: $DBI::errstr";
++
++   $self->{_dbh}->trace($options{verbose} - 1) if $options{verbose} > 1;
++}
++
++sub clone {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $clone = bless {
++        _test     => $self->{_test},
++        _verbose  => $self->{_verbose},
++        _resolver => $self->{_resolver},
++        _dbh      => $self->{_dbh}->clone()
++    }, ref $self;
++
++    return $clone;
++}
++
++sub reset {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    foreach my $table ($self->_get_tables()) {
++        my $query = "DROP TABLE $table";
++        $self->{_dbh}->do($query); 
++    }
++
++    foreach my $table (keys %tables) {
++        $self->_create_table($table, $tables{$table});
++    }
++}
++
++sub _get_tables {
++    my ($self) = @_;
++    my @tables = $self->{_dbh}->tables(undef, undef, '%', 'TABLE');
++    # unquote table name if needed
++    my $char = $self->{_dbh}->get_info(29);
++    @tables = map { substr($_, 1 , -1) } @tables if $char;
++    return @tables;
++}
++
++sub _get_columns {
++    my ($self, $table) = @_;
++    # proper way would be to use column_info(), but unfortunatly DBD::SQLite 
++    # doesn't support it :(
++    return 
++        keys
++        %{$self->{_dbh}->selectrow_hashref("SELECT * from $table")};
++}
++
++sub _create_table {
++    my ($self, $name, $fields) = @_;
++
++    my $query = "CREATE TABLE $name (" .
++        join(',',
++            map { "$_ $fields->{$_}" }
++            keys %$fields
++        ) .
++        ")";
++    $self->{_dbh}->do($query);
++}
++
++sub add_result {
++    my ($self, $type, $media, $package, $values) = @_;
++    croak "Not a class method" unless ref $self;
++    croak "No type defined" unless $type;
++    croak "No package defined" unless $package;
++    croak "No values defined" unless $values;
++
++    my $key = "add_$type";
++    my $sth = $self->{_sths}->{$key};
++
++    unless ($sth) {
++        my @fields = keys %$values;
++        $self->_create_table($type, {
++            'package_id' => 'INT',
++            map { $_ => 'TEXT' } @fields
++        });
++        my $query = "INSERT INTO $type (" .
++           join(',', 'package_id', @fields) .
++          ") VALUES (" .
++           join(',', '?', map { '?' } @fields) .
++          ")";
++        $sth = $self->{_dbh}->prepare($query);
++        $self->{_sths}->{$key} = $sth;
++    }
++
++    print "adding result for type $type and package $package\n"
++        if $self->{_verbose} > 0;
++
++    $sth->execute(
++        $self->_get_package_id(
++            $package->get_canonical_name(),
++            $media->get_name(),
++        ),
++        values %$values
++    );
++}
++
++sub get_types {
++    my ($self) = @_;
++
++    return
++        grep { ! $tables{$_} }
++        $self->_get_tables();
++}
++
++sub get_maintainers {
++    my ($self) = @_;
++
++    return $self->_get_multiple_values('get_maintainers');
++}
++
++sub get_iterator {
++    my ($self, $id, $sort, $filter) = @_;
++
++    die 'No id given, aborting'
++        unless $id;
++    die 'sort should be an arrayref'
++        if $sort and ref $sort ne 'ARRAY';
++    die 'filter should be an hashref'
++        if $filter and ref $filter ne 'HASH';
++        
++    my $query = $self->_get_iterator_query($id, $sort, $filter);
++
++    my $sth = $self->{_dbh}->prepare($query);
++    $sth->execute();
++
++    return Youri::Check::Resultset::DBI::Iterator->new($sth);
++}
++
++sub _get_iterator_query {
++    my ($self, $table, $sort, $filter) = @_;
++
++    my @fields =
++        grep { ! /package_id/ }
++        $self->_get_columns($table);
++
++    my $query = "SELECT DISTINCT " .
++        join(',', qw/package media maintainer/, @fields) .
++        " FROM $table, packages" .
++        " WHERE packages.id = $table.package_id";
++
++    if ($filter) {
++        foreach my $column (keys %{$filter}) {
++            foreach my $value (@{$filter->{$column}}) {
++                $query .= " AND $column = " . $self->{_dbh}->quote($value);
++            }
++        }
++    }
++
++    if ($sort) {
++        $query .= " ORDER BY " . join(', ', @{$sort});
++    }
++
++    return $query;
++}
++
++sub _get_package_id {
++    my ($self, $package, $media) = @_;
++
++    my $id = $self->_get_single_value(
++        'get_package_id',
++        $package
++    );
++    $id = $self->_add_package($package, $media) unless $id;
++
++    return $id;
++}
++
++sub _add_package {
++    my ($self, $package, $media) = @_;
++
++    my $maintainer = $self->{_resolver} ? 
++        $self->{_resolver}->get_maintainer($package) :
++        undef;
++
++    my $sth =
++        $self->{_sths}->{add_package} ||=
++        $self->{_dbh}->prepare($queries{add_package});
++
++    $sth->execute(
++        $package,
++        $media,
++        $maintainer
++    );
++
++    my $id = $self->{_dbh}->last_insert_id(undef, undef, 'packages', 'id');
++
++    return $id;
++}
++
++sub _get_single_value {
++    my ($self, $query, @values) = @_;
++
++    my $sth =
++        $self->{_sths}->{$query} ||=
++        $self->{_dbh}->prepare($queries{$query});
++
++    $sth->execute(@values);
++
++    my @row = $sth->fetchrow_array();
++    return @row ? $row[0]: undef;
++}
++
++sub _get_multiple_values {
++    my ($self, $query, @values) = @_;
++
++    my $sth =
++        $self->{_sths}->{$query} ||=
++        $self->{_dbh}->prepare($queries{$query});
++
++    $sth->execute(@values);
++
++    my @results;
++    while (my @row = $sth->fetchrow_array()) {
++        push @results, $row[0];
++    }
++    return @results;
++}
++
++# close database connection
++sub DESTROY {
++    my ($self) = @_;
++
++    foreach my $sth (values %{$self->{_sths}}) {
++        $sth->finish() if $sth;
++    }
++
++    # warning, may be called before _dbh is created
++    $self->{_dbh}->disconnect() if $self->{_dbh};
++}
++
++package Youri::Check::Resultset::DBI::Iterator;
++
++sub new {
++    my ($class, $sth) = @_;
++
++    my $self = bless {
++        _sth    => $sth,
++        _queue  => []
++    }, $class;
++
++    return $self;
++}
++
++sub has_results {
++    my ($self) = @_;
++
++    return 1 if @{$self->{_queue}};
++
++    push(
++        @{$self->{_queue}},
++        $self->{_sth}->fetchrow_hashref()
++    );
++    
++    return defined $self->{_queue}->[-1];
++}
++
++sub get_result {
++    my ($self) = @_;
++    
++    return @{$self->{_queue}} ?
++        shift @{$self->{_queue}}:
++        $self->{_sth}->fetchrow_hashref();
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset/Iterator.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset/Iterator.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset/Iterator.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,22 @@
++# $Id: Base.pm 483 2005-08-01 21:39:05Z guillomovitch $
++package Youri::Check::Resultset::Iterator;
++
++=head1 INSTANCE METHODS
++
++=head2 has_results()
++
++Returns true if results are available.
++
++=head2 get_result()
++
++Returns next available result, as an field => value hash reference.
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Check/Resultset.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,116 @@
++# $Id: Base.pm 483 2005-08-01 21:39:05Z guillomovitch $
++package Youri::Check::Resultset;
++
++=head1 NAME
++
++Youri::Check::Resultset - Abstract resultset
++
++=head1 DESCRIPTION
++
++This abstract class defines Youri::Check::Resultset interface
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Scalar::Util qw/blessed/;
++use Youri::Utils;
++
++=head1 CLASS METHODS
++
++=head2 new(%hash)
++
++Creates and returns a new Youri::Check::Resultset object.
++
++No generic parameters (subclasses may define additional ones).
++
++Warning: do not call directly, call subclass constructor instead.
++
++=cut
++
++sub new {
++    my $class   = shift;
++    my %options = (
++        test     => 0,     # test mode
++        verbose  => 0,     # verbose mode
++        resolver => undef,  # maintainer resolver, 
++        mode     => 'output', # access mode
++        @_
++    );
++
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my $self = bless {
++        _test     => $options{test},
++        _verbose  => $options{verbose},
++        _resolver => $options{resolver},
++        _mode     => $options{mode}
++    }, $class;
++
++    $self->_init(%options);
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head1 INSTANCE METHODS
++
++=head2 set_resolver()
++
++Set L<Youri::Check::Maintainer::Resolver> object used to resolve package
++maintainers.
++
++=cut
++
++sub set_resolver {
++    my ($self, $resolver) = @_;
++    croak "Not a class method" unless ref $self;
++
++    croak "resolver should be a Youri::Check::Maintainer::Resolver object"
++        unless blessed $resolver &&
++        $resolver->isa("Youri::Check::Maintainer::Resolver");
++
++    $self->{_resolver} = $resolver;
++}
++
++=head2 clone()
++
++Clone resultset object.
++
++=head2 reset()
++
++Reset resultset object, by deleting all contained results.
++
++=head2 add_result($type, $media, $package, $values)
++
++Add given hash reference as a new result for given type and L<Youri::Package> object.
++
++=head2 get_maintainers()
++
++Returns the list of all maintainers with results.
++
++=head2 get_iterator($id, $sort, $filter)
++
++Returns a L<Youri::Check::Resultset::Iterator> object over results for given input it, with optional sort and filter directives.
++
++sort must be an arrayref of column names, such as [ 'package' ].
++
++filter must be a hashref of arrayref of acceptables values indexed by column names, such as { level => [ 'warning', 'error'] }.
++
++=head1 SUBCLASSING
++
++All instances methods have to be implemented.
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Config.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Config.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Config.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,202 @@
++# $Id: Config.pm 1709 2006-10-16 16:33:43Z warly $
++package Youri::Config;
++
++=head1 NAME
++
++Youri::Application - Youri application handler
++
++=head1 SYNOPSIS
++
++    use Youri::Application;
++
++    my $app = Youri::Application->new(
++        options => {
++            help => '|h!'
++        },
++        directories => [ '/etc/youri', "$ENV{HOME}/.youri" ],
++        file        => 'app.conf',
++    );
++
++    # get command line argument
++    my $foo = $app->get_arg('foo');
++
++    # get configuration file parameter
++    my $bar = $app->get_param('bar');
++
++=head1 DESCRIPTION
++
++This class handle configuration for all YOURI applications.
++
++The command line specification is used to manage arguments through
++Getopt::Long. Unless  B<--config> argument is given, the list of directories is
++then scanned for a file with given name, and halt as soon as it find one. If no
++readable file is found, an exception is thrown. The file is then processed
++through YAML::AppConfig. If parsing fails, an exception is thrown.
++
++=head1 CONFIGURATION FILE FORMAT
++
++=head2 SHARED KEYS
++
++In addition to the application-specific optional or mandatory parameters, all
++YOURI applications support the following optional top-level parameters:
++
++=over
++
++=item B<includes>
++
++A list of additional configuration files.
++
++=item B<foo>
++
++An arbitrary variable, usable everywhere else in the file.
++
++=back
++
++=head2 PLUGIN DEFINITION
++
++All YOURI application heavily rely on plugins defined in their configuration
++files. A plugin definition is composed from the following parameters:
++
++=over
++
++=item B<class>
++
++The class of this plugin.
++
++=item B<options>
++
++The options of this plugin.
++
++=back
++
++=head1 SEE ALSO
++
++YAML::AppConfig, Getopt::Long
++
++=cut
++
++use strict;
++use warnings;
++use YAML::AppConfig;
++use Getopt::Long;
++use File::Spec;
++use Pod::Usage;
++use Carp;
++
++sub new {
++    my ($class, %options) = @_;
++
++
++    # command line arguments
++    my $args = {
++        verbose => 0
++    };
++    my @args;
++    if ($options{args}) {
++        while (my ($arg, $spec) = each %{$options{args}}) {
++            push(@args, ($arg . $spec) => \$args->{$arg});
++        }
++    }
++    push(@args,
++        'config=s'   => \$args->{config},
++        'h|help'     => \$args->{help},
++        'v|verbose+' => \$args->{verbose}
++    );
++    GetOptions(@args);
++
++    if ($args->{help}) {
++        if (!@ARGV) {
++            # standard help, available immediatly
++            my $filename = (caller)[1];
++            pod2usage(
++                -input   => $filename,
++                -verbose => 0
++            );
++        }
++    }
++
++    # config files parameters
++    
++    # find configuration file to use
++    my $main_file;
++    if ($args->{config}) {
++        if (! -f $args->{config}) {
++            croak "Non-existing file $args->{config}";
++        } elsif (! -r $args->{config}) {
++            croak "Non-readable file $args->{config}";
++        } else {
++            $main_file = $args->{config};
++        }
++    } else {
++        foreach my $directory (@{$options{directories}}) {
++            my $file = "$directory/$options{file}";
++            next unless -f $file && -r $file;
++            $main_file = $file;
++            last;
++        }
++        croak 'No config file found, aborting' unless $main_file;
++    }
++
++    my $params;
++    eval {
++        $params = YAML::AppConfig->new(file => $main_file);
++    };
++    if ($@) {
++        croak "Invalid configuration file $main_file, aborting";
++    }
++
++    # process inclusions
++    my $includes = $params->get('includes');
++    if ($includes) {
++        foreach my $include_file (@{$includes}) {
++            # convert relative path to absolute ones
++            $include_file = File::Spec->rel2abs(
++                $include_file, (File::Spec->splitpath($main_file))[1]
++            );
++
++            if (! -f $include_file) {
++                warn "Non-existing file $include_file, skipping";
++            } elsif (! -r $include_file) {
++                warn "Non-readable file $include_file, skipping";
++            } else {
++                eval {
++                    $params->merge(file => $include_file);
++                };
++                if ($@) {
++                    carp "Invalid included configuration file $include_file, skipping";
++                }
++            }
++        }
++    }
++
++    my $self = bless {
++        _args   => $args,
++        _params => $params
++    }, $class;
++
++    return $self;
++}
++
++sub get_arg {
++    my ($self, $arg) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_args}->{$arg};
++}
++
++sub get_param {
++    my ($self, $param) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_params}->get($param);
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Media/URPM.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Media/URPM.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Media/URPM.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,273 @@
++# $Id: URPM.pm 1179 2006-08-05 08:30:57Z warly $
++package Youri::Media::URPM;
++
++=head1 NAME
++
++Youri::Media::URPM - URPM-based media implementation
++
++=head1 DESCRIPTION
++
++This is an URPM-based L<Youri::Media> implementation.
++
++It can be created either from local or remote full (hdlist) or partial
++(synthesis) compressed header files, or from a package directory. File-based
++inputs are only usable with this latest option.
++
++=cut
++
++use URPM;
++use File::Find;
++use File::Temp ();
++use Youri::Utils;
++use LWP::Simple;
++use Carp;
++use strict;
++use warnings;
++use Youri::Package::URPM;
++
++use base 'Youri::Media';
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Media::URPM object.
++
++Specific parameters:
++
++=over
++
++=item synthesis $synthesis
++
++Path, URL or list of path or URL of synthesis file used for creating
++this media. If a list is given, the first successfully accessed will be used,
++so as to allow better reliability.
++
++=item hdlist $hdlist
++
++Path, URL or list of path or URL of hdlist file used for creating
++this media. If a list is given, the first successfully accessed will be used,
++so as to allow better reliability.
++
++=item path $path
++
++Path or list of pathes of package directory used for creating this
++media. If a list is given, the first successfully accessed will be used, so as
++to allow better reliability.
++
++=item max_age $age
++
++Maximum age of packages for this media.
++
++=item rpmlint_config $file
++
++rpmlint configuration file for this media.
++
++=back
++
++In case of multiple B<synthesis>, B<hdlist> and B<path> options given, they
++will be tried in this order, so as to minimize parsing time.
++
++=cut
++
++sub _init {
++    my $self   = shift;
++
++    my %options = (
++        hdlist         => '',    # hdlist from which to create this media
++        synthesis      => '',    # synthesis from which to create this media
++        path           => '',    # directory from which to create this media
++        max_age        => '',    # maximum build age for packages
++        rpmlint_config => '',    # rpmlint configuration for packages
++        @_
++    );
++
++    my $urpm = URPM->new();
++    SOURCE: {
++        if ($options{synthesis}) {
++            foreach my $file (
++                ref $options{synthesis} eq 'ARRAY' ?
++                    @{$options{synthesis}} :
++                    $options{synthesis}
++            ) {
++                print "Attempting to retrieve synthesis $file\n"
++                    if $options{verbose};
++                my $synthesis = $self->_get_file($file);
++                if ($synthesis) {
++                    $urpm->parse_synthesis($synthesis, keep_all_tags => 1);
++                    last SOURCE;
++                }
++            }
++        }
++
++        if ($options{hdlist}) { 
++            foreach my $file (
++                ref $options{hdlist} eq 'ARRAY' ?
++                    @{$options{hdlist}} :
++                    $options{hdlist}
++            ) {
++                print "Attempting to retrieve hdlist $file\n"
++                    if $options{verbose};
++                my $hdlist = $self->_get_file($file);
++                if ($hdlist) {
++                    $urpm->parse_hdlist($hdlist, keep_all_tags => 1);
++                    last SOURCE;
++                }
++            }
++        }
++
++        if ($options{path}) {
++            foreach my $path (
++                ref $options{path} eq 'ARRAY' ?
++                    @{$options{path}} :
++                    $options{path}
++            ) {
++                print "Attempting to scan directory $path\n"
++                    if $options{verbose};
++                unless (-d $path) {
++                    carp "non-existing directory $path";
++                    next;
++                }
++                unless (-r $path) {
++                    carp "non-readable directory $path";
++                    next;
++                }
++
++                my $parse = sub {
++                    return unless -f $File::Find::name;
++                    return unless -r $File::Find::name;
++                    return unless /\.rpm$/;
++
++                    $urpm->parse_rpm($File::Find::name, keep_all_tags => 1);
++                };
++
++                find($parse, $path);
++                last SOURCE;
++            }
++        }
++        
++        croak "no source specified";
++    }
++
++    $self->{_urpm}           = $urpm;
++    $self->{_path}           = $options{path};
++    $self->{_max_age}        = $options{max_age};
++    $self->{_rpmlint_config} = $options{rpmlint_config};
++
++    return $self;
++}
++
++sub _remove_all_archs {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    $self->{_urpm}->{depslist} = [];
++}
++
++sub _remove_archs {
++    my ($self, $skip_archs) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $urpm = $self->{_urpm};
++    $urpm->{depslist} = [
++         grep { ! $skip_archs->{$_->arch()} } @{$urpm->{depslist}}
++    ];
++}
++
++=head1 INSTANCE METHODS
++
++=head2 max_age()
++
++Returns maximum age of packages for this media.
++
++=cut
++
++sub max_age {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_max_age};
++}
++
++=head2 rpmlint_config()
++
++Returns rpmlint configuration file for this media.
++
++=cut
++
++sub rpmlint_config {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_rpmlint_config};
++}
++
++sub get_package_class {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return "Youri::Package::URPM";
++}
++
++sub traverse_files {
++    my ($self, $function) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $callback = sub {
++        return unless -f $File::Find::name;
++        return unless -r $File::Find::name;
++        return unless $_ =~ /\.rpm$/;
++
++        my $package = Youri::Package::URPM->new(file => $File::Find::name);
++        return if $self->{_skip_archs}->{$package->get_arch()};
++
++        $function->($File::Find::name, $package);
++    };
++
++    find($callback, $self->{_path});
++}
++
++sub traverse_headers {
++    my ($self, $function) = @_;
++    croak "Not a class method" unless ref $self;
++
++    $self->{_urpm}->traverse(sub {
++        local $_; # workaround mysterious problem between URPM and AppConfig
++        $function->(Youri::Package::URPM->new(header => $_[0]));
++    });
++    
++}
++
++sub _get_file {
++    my ($self, $file) = @_;
++
++    if ($file =~ /^(?:http|ftp):\/\/.*$/) {
++        my $tempfile = File::Temp->new();
++        my $status = getstore($file, $tempfile->filename());
++        unless (is_success($status)) {
++            carp "invalid URL $file: $status";
++            return;
++        }
++        return $tempfile;
++    } else {
++        unless (-f $file) {
++            carp "non-existing file $file";
++            return;
++        }
++        unless (-r $file) {
++            carp "non-readable file $file";
++            return;
++        }
++        return $file;
++    }
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Media.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Media.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Media.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,311 @@
++# $Id: Media.pm 1710 2006-10-16 16:35:11Z warly $
++package Youri::Media;
++
++=head1 NAME
++
++Youri::Media - Abstract media class
++
++=head1 DESCRIPTION
++
++This abstract class defines Youri::Media interface.
++
++=cut
++
++use Carp;
++use strict;
++use warnings;
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Media object.
++
++Generic parameters:
++
++=over
++
++=item id $id
++
++Media id.
++
++=item name $name
++
++Media name.
++
++=item type $type (source/binary)
++
++Media type.
++
++=item test true/false
++
++Test mode (default: false).
++
++=item verbose true/false
++
++Verbose mode (default: false).
++
++=item allow_deps $media_ids
++
++list of ids of medias allowed to provide dependencies.
++
++=item skip_tests $test_ids
++
++list of ids of test plugins to skip.
++
++=item skip_archs $arches
++
++list of arches to skip.
++
++=back
++
++Subclass may define additional parameters.
++
++Warning: do not call directly, call subclass constructor instead.
++
++=cut
++
++sub new {
++    my $class = shift;
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my %options = (
++        name           => '',    # media name
++        canonical_name => '',    # media canonical name
++        type           => '',    # media type
++        test           => 0,     # test mode
++        verbose        => 0,     # verbose mode
++        allow_deps     => undef, # list of media ids from which deps are allowed
++        allow_srcs     => undef, # list of media ids from which packages can be built		
++        skip_tests    => undef, # list of tests ids to skip
++        skip_archs     => undef, # list of archs for which to skip tests
++        @_
++    );
++
++
++    croak "No type given" unless $options{type};
++    croak "Wrong value for type: $options{type}"
++        unless $options{type} =~ /^(?:binary|source)$/o;
++
++    # some options need to be arrays. Check it and convert to hashes
++    foreach my $option (qw(allow_deps allow_srcs skip_archs skip_tests)) {
++        next unless defined $options{$option};
++        croak "$option should be an arrayref" unless ref $options{$option} eq 'ARRAY';
++        $options{$option}  = {
++            map { $_ => 1 } @{$options{$option}}
++        };
++    }
++
++    my $self = bless {
++        _id             => $options{id}, 
++        _name           => $options{name} || $options{id}, 
++        _type           => $options{type}, 
++        _allow_deps     => $options{allow_deps}, 
++        _allow_srcs     => $options{allow_srcs},
++        _skip_archs     => $options{skip_archs},
++        _skip_tests    => $options{skip_tests},
++    }, $class;
++
++    $self->_init(%options);
++
++    # remove unwanted archs
++    if ($options{skip_archs}->{all}) {
++        $self->_remove_all_archs()
++    } elsif ($options{skip_archs}) {
++        $self->_remove_archs($options{skip_archs});
++    }
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head1 INSTANCE METHODS
++
++=head2 get_id()
++
++Returns media identity.
++
++=cut
++
++sub get_id {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_id};
++}
++
++=head2 get_name()
++
++Returns the name of this media.
++
++=cut
++
++sub get_name {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_name};
++}
++
++=head2 get_type()
++
++Returns the type of this media.
++
++=cut
++
++sub get_type {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_type};
++}
++
++=head2 allow_deps()
++
++Returns the list of id of medias allowed to provide dependencies for this
++media. 
++
++=cut
++
++sub allow_deps {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return keys %{$self->{_allow_deps}};
++}
++
++=head2 allow_dep($media_id)
++
++Tells wether media with given id is allowed to provide dependencies for
++this media.
++
++=cut
++
++sub allow_dep {
++    my ($self, $dep) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return
++        $self->{_allow_deps}->{all} ||
++        $self->{_allow_deps}->{$dep};
++}
++
++=head2 allow_srcs()
++
++Returns the list medias where the source packages can be
++
++=cut
++
++sub allow_srcs {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return keys %{$self->{_allow_srcs}};
++}
++
++=head2 allow_src($media_id)
++
++Tells wether media with given id is allowed to host sources dependencies for
++this media.
++
++=cut
++
++sub allow_src {
++    my ($self, $src) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_allow_srcs}->{all} || $self->{_allow_srcs}->{$src};
++}
++
++=head2 skip_archs()
++
++Returns the list of arch which are to be skipped for this media.
++
++=cut
++
++sub skip_archs {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return keys %{$self->{_skip_archs}};
++}
++
++=head2 skip_arch($arch)
++
++Tells wether given arch is to be skipped for this media.
++
++=cut
++
++sub skip_arch {
++    my ($self, $arch) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return
++        $self->{_skip_archs}->{all} ||
++        $self->{_skip_archs}->{$arch};
++}
++
++=head2 skip_tests()
++
++Returns the list of id of test which are to be skipped for this media.
++
++=cut
++
++sub skip_tests {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return keys %{$self->{_skip_tests}};
++}
++
++=head2 skip_test($test_id)
++
++Tells wether test with given id is to be skipped for this media.
++
++=cut
++
++sub skip_test {
++    my ($self, $test) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return
++        $self->{_skip_tests}->{all} ||
++        $self->{_skip_tests}->{$test};
++}
++
++=head2 get_package_class()
++
++Return package class for this media.
++
++=head2 traverse_files($function)
++
++Apply given function to all files of this media.
++
++=head2 traverse_headers($function)
++
++Apply given function to all headers of this media.
++
++=head1 SUBCLASSING
++
++The following methods have to be implemented:
++
++=over
++
++=item traverse_headers
++
++=item traverse_files
++
++=back
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Package/RPM.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Package/RPM.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Package/RPM.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,58 @@
++# $Id: /local/youri/soft/trunk/lib/Youri/Package/URPM.pm 2257 2006-07-05T09:22:47.088572Z guillaume  $
++package Youri::Package::RPM;
++
++=head1 NAME
++
++Youri::Package::RPM - Base class for all RPM-based package implementation
++
++=head1 DESCRIPTION
++
++This bases class factorize code between various RPM-based package
++implementation.
++
++=cut
++
++use strict;
++use warnings;
++use base 'Youri::Package';
++use Carp;
++
++sub get_pattern {
++    my ($class, $name, $version, $release, $arch) = @_;
++
++    return 
++        ($name ? quotemeta($name) : '[\w-]+' ).
++        '-' .
++        ($version ? quotemeta($version) : '[^-]+' ).
++        '-' .
++        ($release ? quotemeta($release) : '[^-]+' ). 
++        '\.' .
++        ($arch ? quotemeta($arch) : '\w+' ).
++        '\.rpm';
++}
++
++sub as_file {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_file};
++}
++
++sub is_debug {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $name = $self->get_name();
++    my $group = $self->get_tag('group');
++
++    # debug packages' names must end in -debug, except kernel
++    if ($group =~ m,^Development/Debug$, &&
++        ($name =~ /-debug$/o || $name =~ /^kernel-.*-debug/o)) {
++        return 1;
++    }
++    else {
++        return 0;
++    }
++}
++
++1;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Package/RPM4.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Package/RPM4.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Package/RPM4.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,424 @@
++# $Id: /local/youri/soft/trunk/lib/Youri/Package/URPM.pm 2129 2006-06-23T09:41:01.599329Z guillomovitch  $
++package Youri::Package::RPM4;
++
++=head1 NAME
++
++Youri::Package::RPM4 - URPM-based rpm package implementation
++
++=head1 DESCRIPTION
++
++This is an RPM4-based L<Youri::Package> implementation for rpm.
++
++=cut
++
++use strict;
++use warnings;
++use Carp;
++use RPM4;
++use RPM4::Header;
++use RPM4::Sign;
++use File::Spec;
++use Scalar::Util qw/refaddr/;
++use base 'Youri::Package::RPM';
++use overload
++    '""'     => 'as_string',
++    '0+'     => '_to_number',
++    fallback => 1;
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Package::RPM4 object.
++
++Specific parameters:
++
++=over
++
++=item file $file
++
++Path of file to use for creating this package.
++
++=item header $header
++
++L<RPM4::Header> object to use for creating this package.
++
++=back
++
++=cut
++
++sub _init {
++    my ($self, %options) = @_;
++
++    my $header;
++    HEADER: {
++        if (exists $options{header}) {
++            croak "undefined header"
++                unless $options{header};
++            croak "invalid header"
++                unless $options{header}->isa('RPM4::Header');
++            $header = $options{header};
++            last HEADER;
++        }
++
++        if (exists $options{file}) {
++            croak "undefined file"
++                unless $options{file};
++            croak "non-existing file $options{file}"
++                unless -f $options{file};
++            croak "non-readable file $options{file}"
++                unless -r $options{file};
++            $header = RPM4::Header->new($options{file});
++            croak "Can't get header from file $options{file}" if (!$header); 
++
++            last HEADER;
++        }
++
++        croak "no way to extract header from arguments";
++    }
++
++    $self->{_header} = $header;
++    $self->{_file}   = File::Spec->rel2abs($options{file});
++}
++
++sub compare_versions {
++    my ($class, $version1, $version2) = @_;
++
++    return RPM4::rpmvercmp($version1, $version2);
++}
++
++sub _depsense2flag {
++    my ($string) = @_;
++    my @flags = 0;
++    push(@flags, 'EQUAL') if ($string =~ /=/);
++    push(@flags, 'LESS') if ($string =~ /</);
++    push(@flags, 'GREATER') if ($string =~ />/);
++    return \@flags;
++}
++
++sub check_ranges_compatibility {
++    my ($class, $range1, $range2) = @_;
++    my @deps1 = split(/ /, $range1);
++    my @deps2 = split(/ /, $range2);
++    $deps1[1] = _depsense2flag($range1); 
++    $deps2[1] = _depsense2flag($range2); 
++    my $dep1 = RPM4::Header::Dependencies(
++        "PROVIDENAME",
++        \@deps1,
++    );
++    my $dep2 = RPM4::Header::Dependencies(
++        "PROVIDENAME",
++        \@deps2,
++    );
++
++    return $dep1->overlap($dep2);
++}
++
++sub get_name {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->tag('name');
++}
++
++sub get_version {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->tag('version');
++}
++
++sub get_release {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->tag('release');
++}
++
++sub get_revision {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->queryformat('%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}');
++}
++
++sub get_file_name {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->queryformat('%{NAME}-%{VERSION}-%{RELEASE}.%|SOURCERPM?{%{ARCH}}:{src}|.rpm');
++}
++
++
++sub get_arch {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->queryformat('%|SOURCERPM?{%{ARCH}}:{src}|');
++}
++
++sub get_url {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->tag('url');
++}
++
++sub get_summary {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->tag('summary');
++}
++
++sub get_description {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->tag('description');
++}
++
++sub get_packager {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->tag('packager');
++}
++
++sub is_source {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->issrc();
++}
++
++sub is_binary {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return !$self->{_header}->issrc();
++}
++
++sub get_type {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return
++        $self->{_header}->issrc() ?
++        "source" :
++        "binary";
++}
++
++sub get_age {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->tag('buildtime');
++}
++
++sub get_source_package {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->tag('sourcerpm');
++}
++
++sub get_canonical_name {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    $self->{_header}->sourcerpmname() =~ /^(\S+)-[^-]+-[^-]+\.src\.rpm$/;
++    return $1;
++}
++
++sub get_tag {
++    my ($self, $tag) = @_;
++    croak "Not a class method" unless ref $self;
++    #croak "invalid tag $tag" unless $self->{_header}->can($tag);
++    return $self->{_header}->tag($tag);
++}
++
++
++sub _get_dependencies {
++    my ($self, $deptype) = @_;
++    my $deps = $self->{_header}->dep($deptype);
++    my @deps_list;
++    if ($deps) {
++    $deps->init();
++    while ($deps->next() >= 0) {
++        my @deps = $deps->info();
++        $deps[1] =~ m/^rpmlib\(/ and next; # skipping internal rpmlib dep
++        $deps[2] =~ s/^=$/==/; # rpm say foo = 1, not foo == 1, == come from URPM, which sucks
++        my $range = $deps[3] ? ($deps[2] . ' ' . $deps[3]) : undef;
++        push(@deps_list, [ $deps[1], $range ]);
++    }
++    }
++    @deps_list
++}
++
++sub get_requires {
++    my ($self) = @_;
++
++    return $self->_get_dependencies('REQUIRENAME');
++}
++
++sub get_provides {
++    my ($self) = @_;
++
++    return $self->_get_dependencies('PROVIDENAME');
++}
++
++sub get_obsoletes {
++    my ($self) = @_;
++   
++    return $self->_get_dependencies('OBSOLETENAME');
++}
++
++sub get_conflicts {
++    my ($self) = @_;
++
++    return $self->_get_dependencies('CONFLICTNAME');
++}
++
++sub get_files {
++    my ($self) = @_;
++
++    my $files = $self->{_header}->files();
++    my @fileslist;
++    if ($files) {
++        $files->init();
++        while ($files->next() >= 0) {
++            my $smode = $files->mode();
++            my $umode = 0;
++            foreach (0..15) { # converting unsigned to signed int :\
++                $umode |= $smode & (1 << $_);
++            }
++            push(@fileslist, [ $files->filename(), $umode, $files->md5() || '' ]);
++        }
++    }
++    @fileslist
++}
++
++sub get_gpg_key {
++    my ($self) = @_;
++    
++    my $signature = $self->{_header}->queryformat('%{SIGGPG:pgpsig}');
++    
++    return if $signature eq '(not a blob)';
++
++    my $key_id = (split(/\s+/, $signature))[-1];
++
++    return substr($key_id, 8);
++}
++
++sub get_information {
++    my ($self) = @_;
++
++    return $self->{_header}->queryformat(<<EOF);
++Name        : %-27{NAME}  Relocations: %|PREFIXES?{[%{PREFIXES} ]}:{(not relocatable)}|
++Version     : %-27{VERSION}       Vendor: %{VENDOR}
++Release     : %-27{RELEASE}   Build Date: %{BUILDTIME:date}
++Install Date: %|INSTALLTIME?{%-27{INSTALLTIME:date}}:{(not installed)         }|      Build Host: %{BUILDHOST}
++Group       : %-27{GROUP}   Source RPM: %{SOURCERPM}
++Size        : %-27{SIZE}%|LICENSE?{      License: %{LICENSE}}|
++Signature   : %|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|
++%|PACKAGER?{Packager    : %{PACKAGER}\n}|%|URL?{URL         : %{URL}\n}|Summary     : %{SUMMARY}
++Description :\n%{DESCRIPTION}
++EOF
++}
++
++sub get_changes {
++    my ($self) = @_;
++
++    my @names = $self->{_header}->tag('changelogname');
++    my @time  = $self->{_header}->tag('changelogtime');
++    my @text  = $self->{_header}->tag('changelogtext');
++
++    my @changes;
++    foreach my $i (0 .. $#names) {
++        $changes[$i] = [
++            $names[$i],
++            $time[$i],
++	    $text[$i],
++        ];
++    }
++
++    return @changes;
++}
++
++sub get_last_change {
++    my ($self) = @_;
++
++    return [
++        ($self->{_header}->tag('changelogname'))[0],
++        ($self->{_header}->tag('changelogtime'))[0],
++	($self->{_header}->tag('changelogtext'))[0],
++    ];
++}
++
++sub as_string {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->fullname();
++}
++
++sub as_formated_string {
++    my ($self, $format) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->queryformat($format);
++}
++
++sub _to_number {
++    return refaddr($_[0]);
++}
++
++sub compare {
++    my ($self, $package) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->compare($package->{_header}) || 0;
++}
++
++sub satisfy_range {
++    my ($self, $range) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->check_range_compatibility($self->get_revision(), $range);
++}
++
++sub sign {
++    my ($self, $name, $path, $passphrase) = @_;
++    croak "Not a class method" unless ref $self;
++
++    # check if parent directory is writable
++    my $parent = (File::Spec->splitpath($self->{_file}))[1];
++    croak "Unsignable package, parent directory is read-only"
++        unless -w $parent;
++
++    my $sign = RPM4::Sign->new(
++        name => $name,
++        path => $path,
++    );
++    $sign->{passphrase} = $passphrase;
++
++    $sign->rpmssign($self->{_file})
++}
++
++sub extract {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    system("rpm2cpio $self->{_file} | cpio -id >/dev/null 2>&1");
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Package/Test.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Package/Test.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Package/Test.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,151 @@
++# $Id: /local/youri/soft/core/trunk/lib/Youri/Package/URPM.pm 2133 2006-09-20T21:40:20.575763Z guillaume  $
++package Youri::Package::Test;
++
++=head1 NAME
++
++Youri::Package::Test - Fake test package
++
++=head1 DESCRIPTION
++
++This is just a fake package object, intended for testing purposes.
++
++=cut
++
++use strict;
++use warnings;
++use Carp;
++use base 'Youri::Package::RPM';
++use overload
++    '""'     => 'as_string',
++    '0+'     => '_to_number',
++    fallback => 1;
++
++our $AUTOLOAD;
++
++my @tags = qw/
++    name
++    version
++    release
++    filename
++    arch
++    url
++    summary
++    description
++    packager
++    buildtime
++    sourcerpm
++/;
++
++my %tags = map { $_ => 1 } @tags;
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Package::Test object.
++
++Specific parameters:
++
++=over
++
++=item tag $tag
++
++Use given value for given tag
++
++=back
++
++=cut
++
++sub _init {
++    my ($self, %options) = @_;
++
++    $self->{"_$_"} = $options{$_} foreach keys %options;
++}
++
++sub get_revision {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_epoch} ?
++        "$self->{_epoch}:$self->{_version}-$self->{_release}" :
++        "$self->{_version}-$self->{_release}";
++}
++
++sub get_tag {
++    my ($self, $tag) = @_;
++    croak "Not a class method" unless ref $self;
++    croak "invalid tag $tag" unless $tags{$tag};
++    return $self->{'_' . $tag};
++}
++
++sub is_source {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_arch} eq 'src';
++}
++
++sub is_binary {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_arch} ne 'src';
++}
++
++sub get_type {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return
++        $self->{_arch} eq 'src' ?
++        "source" :
++        "binary";
++}
++
++sub get_canonical_name {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    if ($self->{_arch} eq 'src') {
++       return $self->{_name};
++    } else {
++       if ($self->{_sourcerpm}) {
++           $self->{_sourcerpm} =~ /^(\S+)-[^-]+-[^-]+\.src\.rpm$/;
++           return $1;
++       } else {
++           return undef;
++       }
++    }
++}
++
++sub as_string {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return 
++        $self->{_name} ? $self->{_name} : '' .
++        '-' .
++        $self->{_version} ? $self->{_version} : '' .
++        '-' .
++        $self->{_release} ? $self->{_release} : '';
++}
++
++sub _to_number {
++    return refaddr($_[0]);
++}
++
++sub AUTOLOAD {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my $method = $AUTOLOAD;
++    $method =~ s/.*:://;
++    return if $method eq 'DESTROY';
++    croak "invalid method" unless $method =~ /^get_(\w+)$/;
++
++    my $tag = $1;
++    croak "invalid tag $tag" unless $tags{$tag};
++    return $self->{'_' . $tag};
++}
++
++1;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Package/URPM.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Package/URPM.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Package/URPM.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,399 @@
++# $Id: URPM.pm 266577 2010-03-02 14:51:24Z bogdano $
++package Youri::Package::URPM;
++
++=head1 NAME
++
++Youri::Package::URPM - URPM-based rpm package implementation
++
++=head1 DESCRIPTION
++
++This is an URPM-based L<Youri::Package> implementation for rpm.
++
++It is merely a wrapper over URPM::Package class, with a more structured
++interface.
++
++=cut
++
++use strict;
++use warnings;
++use Carp;
++use URPM;
++use File::Spec;
++use Expect;
++use Scalar::Util qw/refaddr/;
++use base 'Youri::Package::RPM';
++use overload
++    '""'     => 'as_string',
++    '0+'     => '_to_number',
++    fallback => 1;
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Package::URPM object.
++
++Specific parameters:
++
++=over
++
++=item file $file
++
++Path of file to use for creating this package.
++
++=item header $header
++
++L<URPM::Package> object to use for creating this package.
++
++=back
++
++=cut
++
++sub _init {
++    my ($self, %options) = @_;
++
++    my $header;
++    HEADER: {
++        if (exists $options{header}) {
++            croak "undefined header"
++                unless $options{header};
++            croak "invalid header"
++                unless $options{header}->isa('URPM::Package');
++            $header = $options{header};
++            last HEADER;
++        }
++
++        if (exists $options{file}) {
++            croak "undefined file"
++                unless $options{file};
++            croak "non-existing file $options{file}"
++                unless -f $options{file};
++            croak "non-readable file $options{file}"
++                unless -r $options{file};
++            my $urpm = URPM->new();
++            $urpm->parse_rpm($options{file}, keep_all_tags => 1);
++            $header = $urpm->{depslist}->[0];
++            croak "non-rpm file $options{file}" unless $header;
++            last HEADER;
++        }
++
++        croak "no way to extract header from arguments";
++    }
++
++    $self->{_header} = $header;
++    $self->{_file}   = File::Spec->rel2abs($options{file});
++}
++
++sub compare_versions {
++    my ($class, $version1, $version2) = @_;
++
++    return URPM::rpmvercmp($version1, $version2);
++}
++
++sub check_ranges_compatibility {
++    my ($class, $range1, $range2) = @_;
++
++    return URPM::ranges_overlap($range1, $range2);
++}
++
++sub get_name {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->name();
++}
++
++sub get_version {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->version();
++}
++
++sub get_release {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->release();
++}
++
++sub get_revision {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->queryformat('%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}');
++}
++
++sub get_file_name {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_file} || die "_file is not defined in header-only objects!\n";
++}
++
++sub get_arch {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->arch();
++}
++
++sub get_url {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->url();
++}
++
++sub get_summary {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->summary();
++}
++
++sub get_description {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->description();
++}
++
++sub get_packager {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->packager();
++}
++
++sub is_source {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->arch() eq 'src';
++}
++
++sub is_binary {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->arch() ne 'src';
++}
++
++sub get_type {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return
++        $self->{_header}->arch() eq 'src' ?
++        "source" :
++        "binary";
++}
++
++sub get_age {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->buildtime();
++}
++
++sub get_source_package {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->sourcerpm();
++}
++
++sub get_canonical_name {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    if ($self->{_header}->arch() eq 'src') {
++       return $self->{_header}->name();
++    } else {
++       $self->{_header}->sourcerpm() =~ /^(\S+)-[^-]+-[^-]+\.src\.rpm$/;
++       return $1;
++    }
++}
++
++sub get_tag {
++    my ($self, $tag) = @_;
++    croak "Not a class method" unless ref $self;
++    croak "invalid tag $tag" unless $self->{_header}->can($tag);
++    return $self->{_header}->$tag();
++}
++
++sub get_requires {
++    my ($self) = @_;
++
++    return map {
++        $_ =~ /^([^[]+)(?:\[\*\])?(?:\[(.+)\])?$/;
++        [ $1, $2 ]
++    } $self->{_header}->requires();
++}
++
++sub get_provides {
++    my ($self) = @_;
++
++    return map {
++        $_ =~ /^([^[]+)(?:\[(.+)\])?$/;
++        [ $1, $2 && $2 ne '*' ?  $2 : undef ]
++    } $self->{_header}->provides();
++}
++
++sub get_obsoletes {
++    my ($self) = @_;
++
++    return map {
++        $_ =~ /^([^[]+)(?:\[(.+)\])?$/;
++        [ $1, $2 && $2 ne '*' ?  $2 : undef ]
++    } $self->{_header}->obsoletes();
++}
++
++sub get_conflicts {
++    my ($self) = @_;
++
++    return $self->{_header}->conflicts();
++}
++
++sub get_files {
++    my ($self) = @_;
++
++    my @modes   = $self->{_header}->files_mode();
++    my @md5sums = $self->{_header}->files_md5sum();
++
++    return map {
++        [ $_, shift @modes, shift @md5sums ]
++    } $self->{_header}->files();
++}
++
++sub get_gpg_key {
++    my ($self) = @_;
++    
++    my $signature = $self->{_header}->queryformat('%{SIGGPG:pgpsig}');
++    
++    return if $signature eq '(not a blob)';
++
++    my $key_id = (split(/\s+/, $signature))[-1];
++
++    return substr($key_id, 8);
++}
++
++sub get_information {
++    my ($self) = @_;
++
++    return $self->{_header}->queryformat(<<EOF);
++Name        : %-27{NAME}  Relocations: %|PREFIXES?{[%{PREFIXES} ]}:{(not relocatable)}|
++Version     : %-27{VERSION}       Vendor: %{VENDOR}
++Release     : %-27{RELEASE}   Build Date: %{BUILDTIME:date}
++Install Date: %|INSTALLTIME?{%-27{INSTALLTIME:date}}:{(not installed)         }|      Build Host: %{BUILDHOST}
++Group       : %-27{GROUP}   Source RPM: %{SOURCERPM}
++Size        : %-27{SIZE}%|LICENSE?{      License: %{LICENSE}}|
++Signature   : %|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|
++%|PACKAGER?{Packager    : %{PACKAGER}\n}|%|URL?{URL         : %{URL}\n}|Summary     : %{SUMMARY}
++Description :\n%{DESCRIPTION}
++EOF
++}
++
++sub get_changes {
++    my ($self) = @_;
++
++    my @names = $self->{_header}->changelog_name();
++    my @time  = $self->{_header}->changelog_time();
++    my @text  = $self->{_header}->changelog_text();
++
++    my @changes;
++    foreach my $i (0 .. $#names) {
++        $changes[$i] = [
++            $names[$i],
++            $time[$i],
++            $text[$i],
++        ];
++    }
++
++    return @changes;
++}
++
++sub get_last_change {
++    my ($self) = @_;
++
++    return [
++        ($self->{_header}->changelog_name())[0],
++        ($self->{_header}->changelog_time())[0],
++        ($self->{_header}->changelog_text())[0],
++    ];
++}
++
++sub as_string {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->fullname();
++}
++
++sub as_formated_string {
++    my ($self, $format) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->queryformat($format);
++}
++
++sub _to_number {
++    return refaddr($_[0]);
++}
++
++sub compare {
++    my ($self, $package) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_header}->compare_pkg($package->{_header});
++}
++
++sub satisfy_range {
++    my ($self, $range) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->check_ranges_compatibility("== " . $self->get_revision(), $range);
++}
++
++sub sign {
++    my ($self, $name, $path, $passphrase, $target) = @_;
++    croak "Not a class method" unless ref $self;
++
++    # check if parent directory is writable
++    my $parent = (File::Spec->splitpath($self->{_file}))[1];
++    croak "Unsignable package, parent directory is read-only"
++        unless -w $parent;
++    
++    # FIXME Will have to change that
++    # we sign with cooker key even fro 2007.0 because this is for testing section
++    return !system("sudo -H /root/bin/resign_cooker $self->{_file}");
++
++    my $command =
++        'LC_ALL=C rpm --resign ' . $self->{_file} .
++        ' --define "_gpg_name ' . $name . '"' .
++        ' --define "_gpg_path ' . $path . '"';
++    my $expect = Expect->spawn($command) or die "Couldn't spawn command $command: $!\n";
++    $expect->log_stdout(0);
++    $expect->expect(20, -re => 'Enter pass phrase:');
++    $expect->send("$passphrase\n");
++
++    $expect->soft_close();
++}
++
++sub extract {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    system("rpm2cpio $self->{_file} | cpio -id >/dev/null 2>&1");
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Package.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Package.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Package.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,336 @@
++# $Id: Package.pm 223952 2007-06-23 13:54:13Z pixel $
++package Youri::Package;
++
++=head1 NAME
++
++Youri::Package - Abstract package class
++
++=head1 DESCRIPTION
++
++This abstract class defines Youri::Package interface.
++
++=cut
++
++use Carp;
++use strict;
++use warnings;
++
++use constant DEPENDENCY_NAME  => 0;
++use constant DEPENDENCY_RANGE => 1;
++
++use constant FILE_NAME   => 0;
++use constant FILE_MODE   => 1;
++use constant FILE_MD5SUM => 2;
++
++use constant CHANGE_AUTHOR => 0;
++use constant CHANGE_TIME   => 1;
++use constant CHANGE_TEXT   => 2;
++
++=head1 CLASS METHODS
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Package object.
++
++Warning: do not call directly, call subclass constructor instead.
++
++=cut
++
++sub new {
++    my $class = shift;
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my %options = (
++        @_
++    );
++
++    my $self = bless {
++    }, $class;
++
++    $self->_init(%options);
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head2 get_pattern($name, $version, $release, $arch)
++
++Returns a pattern matching a file for a package, using available informations.
++
++=head2 compare_revisions($revision1, $revision2)
++
++Compares two revision tokens, and returns a numeric value:
++
++=over
++
++=item positive if first revision is higher
++
++=item null if both revisions are equal
++
++=item negative if first revision is lower
++
++=back
++
++=head2 check_ranges_compatibility($range1, $range2)
++
++Returns a true value if given revision ranges are compatible.
++
++=head1 INSTANCE METHODS
++
++=head2 as_file()
++
++Returns the file corresponding to this package.
++
++=head2 as_string()
++
++Returns a string representation of this package.
++
++=head2 as_formated_string(I<format>)
++
++Returns a string representation of this package, formated according to
++I<format>. Format is a string, where each %{foo} token will get replaced by
++equivalent tag value.
++
++=head2 get_name()
++
++Returns the name of this package.
++
++=head2 get_version()
++
++Returns the version of this package.
++
++=head2 get_release()
++
++Returns the release of this package.
++
++=head2 get_revision()
++
++Returns the revision of this package.
++
++=head2 get_arch()
++
++Returns the architecture of this package.
++
++=head2 get_file_name()
++
++Returns the file name of this package (name-version-release.arch.extension).
++
++=head2 is_source()
++
++Returns true if this package is a source package.
++
++=head2 is_binary()
++
++Returns true if this package is a binary package.
++
++=head2 is_debug()
++
++Returns true if this package is a debug package.
++
++=head2 get_type()
++
++Returns the type (binary/source) of this package.
++
++=head2 get_age()
++
++Returns the age of this package
++
++=head2 get_url()
++
++Returns the URL of this package
++
++=head2 get_summary()
++
++Returns the summary of this package
++
++=head2 get_description()
++
++Returns the description of this package
++
++=head2 get_packager()
++
++Returns the packager of this package.
++
++=head2 get_source_package()
++
++Returns the name of the source package of this package.
++
++=head2 get_tag($tag)
++
++Returns the value of tag $tag of this package.
++
++=head2 get_canonical_name()
++
++Returns the canonical name of this package, shared by its multiple components,
++usually the one from the source package.
++
++=head2 get_requires()
++
++Returns the list of dependencies required by this package, each dependency
++being represented as an array reference, with the following informations:
++
++=over
++
++=item B<name>
++
++Name of the dependency (index DEPENDENCY_NAME)
++
++=item B<range>
++
++Range of the dependency (index DEPENDENCY_RANGE)
++
++=back
++
++For more conveniency, fields index are available as constant in this package.
++
++=head2 get_provides()
++
++Returns the list of dependencies provided by this package, each dependency
++being represented as an array reference, using the same structure as previous method.
++
++=head2 get_obsoletes()
++
++Returns the list of other packages obsoleted by this one, each one
++being represented as an array reference, using the same structure as previous method.
++
++=head2 get_conflicts()
++
++Returns the list of other packages conflicting with this one.
++
++=head2 get_files()
++
++Returns the list of files contained in this package, each file being
++represented as an array reference, with the following informations:
++
++=over
++
++=item B<name>
++
++Name of the file (index FILE_NAME).
++
++=item B<mode>
++
++Mode of the file (index FILE_MODE).
++
++=item B<md5sum>
++
++Md5sum of the file (index FILE_MD5SUM).
++
++=back
++
++For more conveniency, fields index are available as constant in this package.
++
++=head2 get_gpg_key()
++
++Returns the gpg key id of package signature.
++
++=head2 get_information()
++
++Returns formated informations about the package.
++
++=head2 get_changes()
++
++Returns the list of changes for this package, each change being
++represented as an array reference, with the following informations:
++
++=over
++
++=item B<author>
++
++Author of the change (index CHANGE_AUTHOR).
++
++=item B<time>
++
++Time of the change (index CHANGE_TIME).
++
++=item B<text>
++
++Raw textual description of the change (index CHANGE_TEXT).
++
++=back
++
++For more conveniency, fields index are available as constant in this package.
++
++=head2 get_last_change()
++
++Returns the last change for this package, as as structure described before.
++
++=head2 compare($package)
++
++Compares ordering with other package, according to their corresponding revision
++tokens, and returns a numeric value:
++
++=over
++
++=item positive if this package is newer
++
++=item null if both have same revision
++
++=item negative if this package is older
++
++=back
++
++=head2 satisfy_range($range)
++
++Returns a true value if this package revision satisfies given revision range.
++
++=head2 sign($name, $path, $passphrase)
++
++Signs the package with given name, keyring path and passphrase.
++
++=head2 extract()
++
++Extract package content in local directory.
++
++=head1 SUBCLASSING
++
++All instances methods have to be implemented.
++
++=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
++
++sub get_file {
++    my ($self) = @_;
++    carp "Deprecated method, use as_file now";
++
++    return $self->as_file();
++}
++
++sub get_full_name {
++    my ($self) = @_;
++    carp "Deprecated method, use as_string now";
++
++    return $self->as_string();
++}
++
++sub compare_versions {
++    my ($self, $version1, $version2) = @_;
++    carp "Deprecated method, use compare_revisions now";
++
++    return $self->compare_revisions($version1, $version2);
++}
++
++sub compare_ranges {
++    my ($self, $range1, $range2) = @_;
++    carp "Deprecated method, use are_range_compatible now";
++
++    return $self->check_ranges_compatibility($range1, $range2);
++}
++
++sub get_revision_name {
++    my ($self) = @_;
++    carp "Deprecated method, use as_formated_string('%name-%version-%release') now";
++
++    return $self->as_formated_string('%{name}-%{version}-%{release}');
++}
++
++
++1;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Repository/Mandriva_upload.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Repository/Mandriva_upload.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Repository/Mandriva_upload.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,546 @@
++# $Id: /local/youri/soft/trunk/lib/Youri/Upload/Action/RSS.pm 857 2006-01-29T10:15:43.298856Z guillaume  $
++package Youri::Repository::Mandriva_upload;
++
++=head1 NAME
++
++Youri::Repository::PLF - PLF repository implementation
++
++=head1 DESCRIPTION
++
++This module implements PLF repository.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Memoize;
++use File::Find 'find';
++use base qw/Youri::Repository/;
++use MDV::Distribconf::Build;
++use SVN::Client;
++
++use constant {
++    PACKAGE_CLASS   => 'Youri::Package::URPM',
++    PACKAGE_CHARSET => 'utf8'
++};
++
++memoize('_get_media_config');
++
++my %translate_arch = (
++    i386 => 'i586',
++    sparc64 => 'sparcv9',
++);
++
++sub _init {
++    my $self   = shift;
++    my %options = (
++        noarch => 'i586', # noarch packages policy
++	src => 'i586',
++	install_root => '',
++        test          => 0,  # test mode
++        verbose       => 0,  # verbose mode
++	queue	      => '',
++	rejected      => '',
++        @_
++    );
++    foreach my $var ('upload_state') {
++	$self->{"_$var"} = [];
++	foreach my $value (split ' ', $options{$var}) {
++	    push @{$self->{"_$var"}}, $value
++	}
++    }
++    print "Initializing repository\n";
++    foreach my $v ('rejected', 'svn', 'queue', 'noarch', 'install_root', 'upload_root', 'verbose') {
++        $self->{"_$v"}  = $options{$v}
++    }
++    foreach my $target (@{$options{targets}}) {
++	$self->{$target} = [];
++	print "Adding $target ($options{$target}{arch})\n" if $self->{_verbose};
++	foreach my $value (split ' ', $options{$target}{arch}) {
++	    push @{$self->{_arch}{$target}}, $value;
++	    push @{$self->{_extra_arches}}, $value
++	}
++    }
++    $self
++}
++
++sub get_group_id {
++    my ($user) = @_;
++    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
++    $year+=1900;
++    $mon++;
++    my $hostname = `hostname`;
++    my ($host) = $hostname =~ /([^.]*)/;
++    sprintf "$year%02d%02d%02d%02d%02d.$user.$host.${$}_", $mon, $mday, $hour, $min, $sec;
++}
++
++sub get_target_arch {
++    my ($self, $target) = $_;
++    return $self->{_arch}{$target}
++}
++
++sub set_arch_changed {
++    my ($self, $target, $arch) = @_;
++    if ($arch eq 'noarch') {
++	    $self->{_arch_changed}{$_} = 1 foreach @{$self->{_arch}{$target}}
++    } elsif ($arch eq 'src') {
++	    $self->{_arch_changed} = $self->{_src}
++    } else {
++	$self->{_arch_changed}{$arch} = 1
++    }
++}
++
++sub get_arch_changed {
++    my ($self, $target) = @_;
++    return [ keys %{$self->{_arch_changed}} ]
++}
++
++sub set_install_dir_changed {
++    my ($self, $install_dir) = @_;
++    $self->{_install_dir_changed}{$install_dir} = 1;
++}
++
++sub get_install_dir_changed {
++    my ($self) = @_;
++    return [ keys %{$self->{_install_dir_changed}} ];
++}
++
++sub _get_media_config {
++    my ($self, $target) = @_;
++    my %media;
++    my $real_target = $target;
++    $real_target =~ s/_force//;
++    foreach my $arch (@{$self->{_arch}{$target}}) {
++	my $root = "$self->{_install_root}/$real_target/$arch";
++	my $distrib = MDV::Distribconf::Build->new($root);
++	print "Getting media config from $root\n" if $self->{_verbose};
++	$self->{distrib}{$arch} = $distrib;
++	$distrib->loadtree or die "$root does not seem to be a distribution tree\n";
++	$distrib->parse_mediacfg;
++	foreach my $media ($distrib->listmedia) {
++	    my $rpms = $distrib->getvalue($media, 'rpms');
++	    my $debug_for = $distrib->getvalue($media, 'debug_for');
++	    my $srpms = $distrib->getvalue($media, 'srpms');
++	    my $path = $distrib->getfullpath($media, 'path');
++	    if (!$rpms) {
++		if (-d $path) {
++		    print "MEDIA defining $media in $path\n" if $self->{_verbose} > 1;
++		    $media{$arch}{$media} = $path
++		} else {
++		    print "ERROR $path does not exist for media $media on $arch\n"
++		}
++	    } else {
++		my ($media) = split ' ', $rpms;
++		if (-d $path) {
++		    print "MEDIA defining SOURCE media for $media in $path\n" if $self->{_verbose} > 1;
++		    $media{src}{$media} = $path
++		} else {
++		    print "ERROR $path does not exist for source media $media on $arch\n"
++		}
++	    }
++	}
++    }
++    \%media
++}
++
++sub get_package_class {
++    return PACKAGE_CLASS;
++}
++
++sub get_package_charset {
++    return PACKAGE_CHARSET;
++}
++
++sub get_upload_dir {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++    my $arch = $package->get_arch();
++    return
++        $self->{_upload_root} .
++        "/$self->{_queue}/$target/" .
++        _get_section($self, $package, $target, $user_context, $app_context) .
++	'/' . 
++	($user_context->{prefix} ? '' : get_group_id($user_context->{user}))
++}
++
++sub get_install_path {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++
++    return $self->_get_path($package, $target, $user_context, $app_context);
++}
++
++
++sub get_distribution_paths {
++    my ($self, $package, $target) = @_;
++
++    return $self->_get_distribution_paths($package, $target);
++}
++
++sub get_archive_path {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++
++    return $self->_get_path($package, $target, $user_context, $app_context);
++}
++
++sub get_reject_path {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++
++    return $self->{_rejected};
++}
++
++
++sub _get_path {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++
++    my $section = $self->_get_section($package, $target, $user_context, $app_context);
++    my $arch = $app_context->{arch} || $package->get_arch();
++    $arch = $translate_arch{$arch} || $arch;
++    if ($arch eq 'noarch') {
++	$arch = $self->{_noarch} 
++    } elsif ($arch eq 'src') {
++	return "$target/SRPMS/$section"
++    }
++    "$target/$arch/media/$section"
++}
++
++sub _get_distribution_paths {
++    my ($self, $package, $target) = @_;
++
++    my $arch = $package->get_arch();
++    $arch = $translate_arch{$arch} || $arch;
++    if ($arch eq 'noarch') {
++	map { "$target/$_" } $self->get_extra_arches;
++    } elsif ($arch eq 'src') {
++	die "no way to get distribution path using a $arch package";
++    } else {
++	"$target/$arch";
++    }
++}
++
++sub get_arch {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    my $arch = $package->get_arch();
++    $arch = $translate_arch{$arch} || $arch;
++    if ($arch eq 'noarch') {
++	$arch = $self->{_noarch} 
++    }
++    $arch
++}
++
++sub get_version_path {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++
++    my $section = $self->_get_section($package, $target, $user_context, $app_context);
++
++    return "$self->{_module}/$section";
++}
++
++=head2 get_replaced_packages($package, $target, $user_context, $app_context)
++
++Overrides parent method to add libified packages.
++
++=cut
++
++sub get_replaced_packages {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my @replaced_packages = 
++        $self->SUPER::get_replaced_packages($package, $target, $user_context, $app_context);
++
++    # mandriva lib policy:
++    # library package names change with revision, making mandatory to
++    # duplicate older revisions search with a custom pattern
++    my $name = $package->get_name();
++    if ($name =~ /^(lib\w+[a-zA-Z_])[\d_\.]+([-\w]*)$/) {
++        push(@replaced_packages,
++            grep { $package->compare($_) > 0 }
++            map { PACKAGE_CLASS->new(file => $_) }
++            $self->get_files(
++                $self->{_install_root},
++                $self->get_install_path($package, $target, $user_context, $app_context),
++                PACKAGE_CLASS->get_pattern(
++                    $1 . '[\d_\.]+' . $2, # custom name pattern
++                    undef,
++                    undef,
++                    $package->get_arch()
++                ),
++            )
++        );
++    }
++
++    # kernel packages have the version in the name
++    # binary dkms built for old kernels have to be removed too
++    if ($name =~ /^kernel-([^\d]*-)?([\d.]*)-(.*)$/) { # "desktop", "2.6.28", "2mnb"
++        push(@replaced_packages,
++            map { PACKAGE_CLASS->new(file => $_) }
++            $self->get_files(
++                $self->{_install_root},
++                $self->get_install_path($package, $target, $user_context, $app_context),
++                PACKAGE_CLASS->get_pattern(
++                    '(kernel-' . $1 . '\d.*|.*-kernel-[\d.]*-' . $1 . '\d.*)',
++                    undef,
++                    undef,
++                    $package->get_arch()
++                ),
++            )
++        );
++    }
++
++    return @replaced_packages;
++       
++}
++
++sub _get_main_section {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++
++    my $section = $self->_get_section($package, $target, $user_context, $app_context);
++    my ($main_section) = $section =~ m,^([^/]+),;
++    $main_section
++}
++
++sub _get_section {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++
++    my $name = $package->get_name();
++    my $cname = $package->get_canonical_name();
++    my $version = $package->get_version();
++    my $release = $package->get_release();
++    my $section = $user_context->{section};
++    my $media = $self->_get_media_config($target);
++    my $arch = $package->get_arch();
++    my $file = $package->as_file();
++    $file =~ s,/+,/,g; # unneeded?
++    # FIXME: use $self->get_arch()
++    $arch = $self->{_noarch} if $arch eq 'noarch';
++    $arch = $translate_arch{$arch} || $arch;
++
++    if (!$section) {
++        $section = $self->{packages}{$file}{section};
++        print "Section undefined, repository says it is '$section' for '$file'\n" if $self->{_verbose};
++    }
++    if ($section && $section !~ /debug_/ && $package->is_debug()) {
++	$section = "debug_$section"
++    }
++
++    # if have section already, check if it exists, and may return immediately
++    if ($section) {
++	print "Using requested section $section\n";
++	if ($media->{$arch}{$section}) { 
++	    return $section
++	} else {
++	    die "FATAL youri: unknown section $section for target $target for arch $arch\n"
++	}
++    }
++    # else, try to find section automatically
++
++    # pattern for search of src package with specific version-release,
++    # should be searched first, because we prefer to find the precise
++    # section a package is already in
++    my $specific_source_pattern = PACKAGE_CLASS->get_pattern(
++        $cname,
++        $version,
++        $release,
++        'src'
++    );
++
++    my $source_pattern = PACKAGE_CLASS->get_pattern(
++        $cname,
++        undef,
++        undef,
++        'src'
++    );
++
++    # if a media has no source media configured, or if it is a debug
++    # package, we search in binary media
++
++    # pattern for search when a binary media has no src media configured
++    my $specific_binary_pattern = PACKAGE_CLASS->get_pattern(
++          $name,
++          $version,
++          $release,
++          $arch
++    );
++
++    # last resort pattern: previous existing binary packages
++    my $binary_pattern = PACKAGE_CLASS->get_pattern(
++          $name,
++          undef,
++          undef,
++          $arch
++    );
++
++    # first try to find section for the specific version, as it is possibly already there;
++    # this is the case for when called in Youri::Submit::Action::Archive, to find the
++    # section the package got installed
++    print "Looking for package $name with version $version-$release\n";
++    foreach my $m (keys %{$media->{$arch}}) {
++        print " .. section '$m' path '".$media->{$arch}{$m}."'\n" if $self->{_verbose};
++        # - prefer source for non-debug packages, use binary if there is no source media configured
++        # - debug packages must be searched in binary medias, due to their
++        #   src section != binary section; NOTE: should/need we search in
++        #   src medias and add the 'debug_' prefix?
++        if (!$package->is_debug() && $media->{src}{$m}) {
++            next unless $self->get_files('', $media->{src}{$m}, $specific_source_pattern);
++        } else {
++            next unless $self->get_files('', $media->{$arch}{$m}, $specific_binary_pattern);
++        }
++        $section = $m;
++        last;
++    }
++
++    # if still not found, try finding any version of the package in a
++    # /release subsection (safe default: /release is default for cooker,
++    # should be locked for released distros, and we don't risk wrongly
++    # choosing /backports, /testing, or /updates);
++    # this is the case for when called at submit, to find the section where
++    # the package already resides
++    if (!$section) {
++        # debug packages should be found by previous specific version search
++        # NOTE: as above, should/need we search here and add the 'debug_' prefix?
++        # ... probably... as at least mdv-youri-submit-force will process debug packages
++        if ($package->is_debug() && $self->{_verbose}) {
++            print "Warning: debug package $name with version $version-$release not found.\n";
++        }
++
++        print "Warning: Looking for any section with a package $name of any version\n";
++        foreach my $m (keys %{$media->{$arch}}) {
++            print " .. section '$m' path '".$media->{$arch}{$m}."'\n" if $self->{_verbose};
++            # NOTE: !$package->is_debug() test is here to prevent when above FATAL error is removed
++            next if $m !~ /release/ || ($m =~ /debug/ && !$package->is_debug());
++            # - prefer source
++            if ($media->{src}{$m}) {
++                next unless $self->get_files('', $media->{src}{$m}, $source_pattern);
++            } else {
++                next unless $self->get_files('', $media->{$arch}{$m}, $binary_pattern);
++            }
++            $section = $m;
++            last;
++        }
++    }
++
++    # FIXME: doing this here is wrong; this way the caller can never know if
++    # a section was actually found or not; should return undef and let the
++    # caller set a default (Note: IIRC PLF|Zarb has this right, see there) -spuk
++    print STDERR "Warning: Can't guess destination: section missing, defaulting to contrib/release\n" unless $section;
++    $section ||= 'contrib/release';
++
++    # next time we don't need to search everything again
++    $self->{packages}{$file}{section} = $section;
++
++    print "Section is '$section'.\n";
++
++    return $section;
++}
++
++sub get_upload_newer_revisions {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++    my $arch = $package->get_arch();
++    my $name = $package->get_full_name;
++    $name =~ s/^\@\d+://;
++    my $pattern = $self->get_package_class()->get_pattern($package->get_name(), undef, undef, $arch);
++    my $media = $self->_get_media_config($target);
++    my @packages;
++    foreach my $state (@{$self->{_upload_state}}) {
++	foreach my $m (keys %{$media->{$arch}}) {
++	    my $path = "$self->{_upload_root}/$state/$target/$m";
++	    print "Looking for package $package revisions for $target in $path (pattern $pattern)\n" if $self->{_verbose};
++	    find(
++		sub { 
++		    s/\d{14}\.[^.]*\.[^.]*\.\d+_//; 
++		    s/^\@\d+://;
++		    return if ! /^$pattern/; 
++		    return if /\.info$/; 
++		    print "Find $_\n"; 
++		    push @packages, $File::Find::name if $package->check_ranges_compatibility("== $name", "< $_")
++		}, $path);
++	}
++    }
++    return
++        @packages;
++}
++
++sub package_in_svn {
++    my ($self, $srpm_name) = @_;
++    my $ctx = new SVN::Client(
++	auth => [SVN::Client::get_simple_provider(),
++	SVN::Client::get_simple_prompt_provider(\&simple_prompt,2),
++	SVN::Client::get_username_provider()]
++    );
++
++    my $svn_entry = $ctx->ls("$self->{_svn}/$srpm_name", 'HEAD', 0);
++    if ($svn_entry) {
++	print "Package $srpm_name is in the SVN\n";
++	return 1
++    }
++}
++
++sub get_svn_url {
++    my ($self) = @_;
++    $self->{_svn}
++}
++
++sub get_revisions {
++    my ($self, $package, $target, $user_context, $app_context, $filter) = @_;
++    croak "Not a class method" unless ref $self;
++    print "Looking for package $package revisions for $target\n" if $self->{_verbose} > 0;
++
++    my $arch = $app_context->{arch} || $user_context->{arch} || $package->get_arch();
++    my $media_arch = $arch eq 'noarch' ? $self->{_noarch} : $arch;
++    my $path = $arch eq 'src' ? "$target/SRPMS/" : "$target/$media_arch/media";
++    my $media = $self->_get_section($package, $target, $user_context, $app_context);
++    my $name = $package->get_name();
++    my @packages = map { $self->get_package_class()->new(file => $_) }
++	$self->get_files(
++	    $self->{_install_root},
++	    "$path/$media",
++	    $self->get_package_class()->get_pattern(
++		    $name,
++		undef,
++		undef,
++		$package->get_arch(),
++	    )
++	);
++
++    @packages = grep { $filter->($_) } @packages if $filter;
++
++    return
++        sort { $b->compare($a) } # sort by revision order
++        @packages;
++}
++
++sub reject {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++
++
++}
++
++sub get_archive_dir {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return
++        $self->{_archive_root}
++}
++
++
++# 20060801 warly
++#
++# Upload steps
++# SRPMS are uploaded in /home/mandrake/uploads/todo/$target/$media/group_id
++#
++# 
++#
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Repository/Mandriva_upload_pre.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Repository/Mandriva_upload_pre.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Repository/Mandriva_upload_pre.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,274 @@
++# $Id: /local/youri/soft/trunk/lib/Youri/Upload/Action/RSS.pm 857 2006-01-29T10:15:43.298856Z guillaume  $
++package Youri::Repository::Mandriva_upload_pre;
++
++=head1 NAME
++
++Youri::Repository::PLF - PLF repository implementation
++
++=head1 DESCRIPTION
++
++This module implements PLF repository.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Memoize;
++use File::Find 'find';
++use base qw/Youri::Repository/;
++use SVN::Client;
++use constant {
++    PACKAGE_CLASS   => 'Youri::Package::URPM',
++    PACKAGE_CHARSET => 'utf8'
++};
++
++memoize('_get_section');
++
++sub _init {
++    my $self   = shift;
++    my %options = (
++        module => 'SPECS', # CVS module
++        noarch => 'i586', # noarch packages policy
++	svn    => '',
++	upload_root => '',
++        @_
++    );
++
++    $self->{_module} = $options{module};
++    $self->{_noarch} = $options{noarch};
++    $self->{_svn} = $options{svn};
++    $self->{_upload_root} = $options{upload_root};
++
++    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
++    $year+=1900;
++    my $hostname = `hostname`;
++    my ($host) = $hostname =~ /([^.]*)/;
++    $self->{group_dir} = sprintf "$ENV{SUDO_USER}.$host.$$.$year%02d%02d%02d%02d%02d", $mon, $mday, $hour, $min, $sec;
++}
++
++sub get_package_class {
++    return PACKAGE_CLASS;
++}
++
++sub package_in_svn {
++    my ($self, $srpm_name) = @_;
++    my $ctx = new SVN::Client(
++	auth => [SVN::Client::get_simple_provider(),
++	SVN::Client::get_simple_prompt_provider(\&simple_prompt,2),
++	SVN::Client::get_username_provider()]
++    );
++
++    my $svn_entry = $ctx->ls("$self->{_svn}/", 'HEAD', 0);
++    foreach (keys %{$svn_entry}) {
++	if ($srpm_name eq $_) {
++	    print "Package $_ is in the SVN\n";
++	    return 1
++	}
++    }
++}
++
++sub get_svn_url {
++    my ($self) = @_;
++    $self->{_svn}
++}
++
++sub get_revisions {
++    my ($self, $package, $target, $define, $filter) = @_;
++    croak "Not a class method" unless ref $self;
++    print "Looking for package $package revisions for $target\n"
++        if $self->{_verbose} > 0;
++
++    my $arch = $define->{arch} || $package->get_arch;
++    if ($arch eq 'src') {
++	$arch = 'SRPMS'
++    } else {
++	$arch .= '/media'
++    }
++    my @packages;
++    foreach my $dir ('main', 'contrib') {
++	print "Looking into $self->{_install_root}/$target/$arch/$dir/release\n";
++	push @packages, 
++	map { $self->get_package_class()->new(file => $_) }
++	$self->get_files(
++	    $self->{_install_root},
++	    "$target/$arch/$dir/release" ,
++	    $self->get_package_class()->get_pattern($package->get_name(),undef, undef, $arch)
++	);
++    }
++
++    @packages = grep { $filter->($_) } @packages if $filter;
++
++    return
++        sort { $b->compare($a) } # sort by revision order
++        @packages;
++}
++
++sub get_package_charset {
++    return PACKAGE_CHARSET;
++}
++
++sub get_upload_dir {
++    my ($self, $package, $target, $define) = @_;
++    croak "Not a class method" unless ref $self;
++    my $arch = $package->get_arch();
++    my $section = $self->_get_section($package, $target, $define);
++    my $media_path = $section eq 'main' ? $target  : $target =~ /^cooker/ ? "contrib" : "$target/contrib";
++    my $arch_path = $arch eq 'src' ? 'SRPMS' : 'RPMS';
++    my $force = $target =~ /_force/ ? 'force' : '';
++    $self->{_upload_root} .  "/$media_path/$force/$arch_path/"
++}
++
++sub get_arch {
++    my ($self, $package, $target, $define) = @_;
++    my $arch = $package->get_arch();
++    if ($arch eq 'noarch') {
++	$arch = $self->{_noarch} 
++    }
++    $arch
++}
++
++sub get_install_path {
++    my ($self, $package, $target, $define) = @_;
++
++    return $self->_get_path($package, $target, $define);
++}
++
++sub get_archive_path {
++    my ($self, $package, $target, $define) = @_;
++
++    return $self->_get_path($package, $target, $define);
++}
++
++sub _get_path {
++    my ($self, $package, $target, $define) = @_;
++
++    my $arch = $package->get_arch;
++    if ($arch eq 'src') {
++	$arch = 'SRPMS'
++    } else {
++	$arch .= '/media'
++    }
++    my $section = $self->_get_section($package, $target, $define);
++
++    return "$target/$arch/$section/release/";
++}
++
++
++sub get_version_path {
++    my ($self, $package, $target, $define) = @_;
++
++    my $section = $self->_get_section($package, $target, $define);
++
++    return "$self->{_module}/$section/release/";
++}
++
++=head2 get_replaced_packages($package, $target, $define)
++
++Overrides parent method to add libified packages.
++
++=cut
++
++sub get_replaced_packages {
++    my ($self, $package, $target, $define) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my @replaced_packages = 
++        $self->SUPER::get_replaced_packages($package, $target, $define);
++
++    # mandriva lib policy:
++    # library package names change with revision, making mandatory to
++    # duplicate older revisions search with a custom pattern
++    my $name = $package->get_name();
++    if ($name =~ /^(lib\w+[a-zA-Z_])[\d_\.]+([-\w]*)$/) {
++        push(@replaced_packages,
++            grep { $package->compare($_) > 0 }
++            map { PACKAGE_CLASS->new(file => $_) }
++            $self->get_files(
++                $self->{_install_root},
++                $self->get_install_path($package, $target, $define),
++                PACKAGE_CLASS->get_pattern(
++                    $1 . '[\d_\.]+' . $2, # custom name pattern
++                    undef,
++                    undef,
++                    $package->get_arch()
++                ),
++            )
++        );
++    }
++
++    return @replaced_packages;
++       
++}
++
++sub _get_section {
++    my ($self, $package, $target, $define) = @_;
++
++    my $section;
++
++    # try to find section automatically
++    my $arch = $package->get_arch();
++    $arch = $self->{_noarch} if $arch eq 'noarch';
++
++    my $source_pattern = PACKAGE_CLASS->get_pattern(
++        $package->get_canonical_name(),
++        undef,
++        undef,
++        'src'
++    );
++
++    my $binary_pattern = PACKAGE_CLASS->get_pattern(
++        $package->get_name(),
++        undef,
++        undef,
++        $arch
++    );
++
++    # for each potential section, try to match
++    # a suitable source patten in source directory
++    # a suitable binary patten in binary directory
++    foreach my $dir (qw/main contrib/) {
++        next unless
++            $self->get_files(
++                $self->{_install_root},
++                "$target/SRPMS/$dir/release",
++                $source_pattern
++            ) || $self->get_files(
++                $self->{_install_root},
++                "$target/$arch/media/$dir/release",
++                $binary_pattern
++            );
++	print "Section is $dir\n";
++        $section = $dir;
++        last;
++    }
++
++    # use defined section if not found
++    $section = $define->{section} unless $section;
++
++    $section || 'contrib'
++}
++
++sub get_upload_newer_revisions {
++    my ($self, $package, $target, $define) = @_;
++    croak "Not a class method" unless ref $self;
++    my $arch = $package->get_arch();
++    my $pattern = $self->get_package_class()->get_pattern($package->get_name(), undef, undef, $arch);
++    print "Looking for package $package revisions for $target in $self->{_upload_root} (pattern $pattern)\n";
++    my @packages;
++    foreach my $dir ('cooker', 'contrib') {
++	find(sub { return if ! /^$pattern/; print "Find $_\n"; push @packages, $File::Find::name if $package->compare($self->get_package_class()->new(file => $File::Find::name)) <= 0 }, "$self->{_upload_root}/$dir");
++    }
++    return
++        @packages;
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Repository/PLF.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Repository/PLF.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Repository/PLF.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,196 @@
++# $Id: /local/youri/soft/trunk/lib/Youri/Upload/Action/RSS.pm 857 2006-01-29T10:15:43.298856Z guillaume  $
++package Youri::Repository::PLF;
++
++=head1 NAME
++
++Youri::Repository::PLF - PLF repository implementation
++
++=head1 DESCRIPTION
++
++This module implements PLF repository.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use Memoize;
++use base qw/Youri::Repository/;
++use constant {
++    PACKAGE_CLASS   => 'Youri::Package::URPM',
++    PACKAGE_CHARSET => 'utf8'
++};
++
++memoize('_get_section');
++
++
++sub _init {
++    my $self   = shift;
++    my %options = (
++        module => 'SPECS', # CVS module
++        noarch => 'noarch', # noarch packages policy
++        @_
++    );
++
++    $self->{_module} = $options{module};
++    $self->{_noarch} = $options{noarch};
++}
++
++sub get_package_class {
++    return PACKAGE_CLASS;
++}
++
++sub get_package_charset {
++    return PACKAGE_CHARSET;
++}
++
++sub get_install_path {
++    my ($self, $package, $target, $define) = @_;
++
++    return $self->_get_path($package, $target, $define);
++}
++
++sub get_archive_path {
++    my ($self, $package, $target, $define) = @_;
++
++    return $self->_get_path($package, $target, $define);
++}
++
++sub _get_path {
++    my ($self, $package, $target, $define) = @_;
++
++    my $section = $self->_get_section($package, $target, $define);
++
++    my $subpath = $self->_get_subpath($package, $target);
++
++    return "$section/$subpath";
++}
++
++
++sub get_version_path {
++    my ($self, $package, $target, $define) = @_;
++
++    my $section = $self->_get_section($package, $target, $define);
++
++    return "$self->{_module}/$section";
++}
++
++=head2 get_replaced_packages($package, $target, $define)
++
++Overrides parent method to add libified packages.
++
++=cut
++
++sub get_replaced_packages {
++    my ($self, $package, $target, $define) = @_;
++    croak "Not a class method" unless ref $self;
++
++    my @replaced_packages = 
++        $self->SUPER::get_replaced_packages($package, $target, $define);
++
++    # mandriva lib policy:
++    # library package names change with revision, making mandatory to
++    # duplicate older revisions search with a custom pattern
++    my $name = $package->get_name();
++    if ($name =~ /^(lib\w+[a-zA-Z_])[\d_\.]+([-\w]*)$/) {
++        push(@replaced_packages,
++            grep { $package->compare($_) > 0 }
++            map { PACKAGE_CLASS->new(file => $_) }
++            $self->get_files(
++                $self->{_install_root},
++                $self->get_install_path($package, $target, $define),
++                PACKAGE_CLASS->get_pattern(
++                    $1 . '[\d_\.]+' . $2, # custom name pattern
++                    undef,
++                    undef,
++                    $package->get_arch()
++                ),
++            )
++        );
++    }
++
++    return @replaced_packages;
++       
++}
++
++sub _get_section {
++    my ($self, $package, $target, $define) = @_;
++
++    my $section;
++
++    # try to find section automatically
++    my $arch = $package->get_arch();
++
++    my $source_pattern = PACKAGE_CLASS->get_pattern(
++        $package->get_canonical_name(),
++        undef,
++        undef,
++        'src'
++    );
++
++    my $binary_pattern = PACKAGE_CLASS->get_pattern(
++        $package->get_name(),
++        undef,
++        undef,
++        $arch
++    );
++
++    my $source_subpath = $self->_get_subpath($package, $target, 'src');
++    my $binary_subpath = $self->_get_subpath($package, $target, $arch);
++
++    # for each potential section, try to match
++    # a suitable source patten in source directory
++    # a suitable binary patten in binary directory
++    foreach my $dir (qw/free non-free/) {
++        next unless
++            $self->get_files(
++                $self->{_install_root},
++                "$dir/$source_subpath",
++                $source_pattern
++            ) || $self->get_files(
++                $self->{_install_root},
++                "$dir/$binary_subpath",
++                $binary_pattern
++            );
++        $section = $dir;
++        last;
++    }
++
++    # use defined section if not found
++    $section = $define->{section} unless $section;
++
++    die "Can't guess destination: section missing" unless $section;
++
++    return $section;
++}
++
++sub _get_subpath {
++    my ($self, $package, $target, $arch) = @_;
++
++    my $subpath;
++
++    # use package arch if not specified
++    $arch = $package->get_arch() unless $arch;
++
++    if ($arch eq 'src') {
++        $subpath = 'src';
++    } else {
++        if ($arch eq 'noarch') {
++            $subpath = "$target/$self->{_noarch}";
++        } else {
++            $subpath = "$target/$arch";
++        }
++    }
++
++    return $subpath;
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Repository.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Repository.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Repository.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,492 @@
++# $Id: Base.pm 631 2006-01-26 22:22:23Z guillomovitch $
++package Youri::Repository;
++
++=head1 NAME
++
++Youri::Repository - Abstract repository
++
++=head1 DESCRIPTION
++
++This abstract class defines Youri::Repository interface.
++
++=cut
++
++use warnings;
++use strict;
++use Carp;
++use File::Basename;
++use Youri::Package;
++
++=head1 CLASS METHODS
++
++
++=head2 new(%args)
++
++Creates and returns a new Youri::Repository object.
++
++No generic parameters (subclasses may define additional ones).
++
++Warning: do not call directly, call subclass constructor instead.
++
++=cut
++
++sub new {
++    my $class = shift;
++    croak "Abstract class" if $class eq __PACKAGE__;
++
++    my %options = (
++        install_root  => '', # path to top-level directory
++        archive_root  => '', # path to top-level directory
++        version_root  => '', # path to top-level directory
++        test          => 0,  # test mode
++        verbose       => 0,  # verbose mode
++        @_
++    );
++
++
++    croak "no install root" unless $options{install_root};
++    croak "invalid install root" unless -d $options{install_root};
++
++    my $self = bless {
++        _install_root  => $options{install_root},
++        _archive_root  => $options{archive_root},
++        _version_root  => $options{version_root},
++        _test          => $options{test},
++        _verbose       => $options{verbose},
++    }, $class;
++
++    $self->_init(%options);
++
++    return $self;
++}
++
++sub _init {
++    # do nothing
++}
++
++=head1 INSTANCE METHODS
++
++=head2 get_package_class()
++
++Return package class for this repository.
++
++=cut
++
++sub get_package_class {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++    return $self->{_package_class};
++}
++
++=head2 get_package_charset()
++
++Return package charset for this repository.
++
++=cut
++
++sub get_package_charset {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++    return $self->{_package_charset};
++}
++
++=head2 get_extra_arches()
++
++Return the list of additional archictectures to handle when dealing with noarch
++packages.
++
++=cut
++
++sub get_extra_arches {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++    return @{$self->{_extra_arches}};
++}
++
++
++=head2 get_older_revisions($package, $target, $user_context, $app_context)
++
++Get all older revisions from a package found in its installation directory, as a
++list of L<Youri::Package> objects.
++
++=cut
++
++sub get_older_revisions {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++    print "Looking for package $package older revisions for $target\n"
++        if $self->{_verbose} > 0;
++
++    return $self->get_revisions(
++        $package,
++        $target,
++        $user_context,
++        $app_context,
++        sub { return $package->compare($_[0]) > 0 }
++    );
++}
++
++=head2 get_last_older_revision($package, $target, $user_context, $app_context)
++
++Get last older revision from a package found in its installation directory, as a
++single L<Youri::Package> object.
++
++=cut
++
++sub get_last_older_revision {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++    print "Looking for package $package last older revision for $target\n"
++        if $self->{_verbose} > 0;
++
++    return (
++        $self->get_older_revisions(
++            $package,
++            $target,
++            $user_context,
++            $app_context
++        )
++    )[0];
++}
++
++=head2 get_newer_revisions($package, $target, $user_context, $app_context)
++
++Get all newer revisions from a package found in its installation directory, as
++a list of L<Youri::Package> objects.
++
++=cut
++
++sub get_newer_revisions {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++    print "Looking for package $package newer revisions for $target\n"
++        if $self->{_verbose} > 0;
++
++    return $self->get_revisions(
++        $package,
++        $target,
++        $user_context,
++        $app_context,
++        sub { return $_[0]->compare($package) > 0 }
++    );
++}
++
++
++=head2 get_revisions($package, $target, $user_context, $app_context, $filter)
++
++Get all revisions from a package found in its installation directory, using an
++optional filter, as a list of L<Youri::Package> objects.
++
++=cut
++
++sub get_revisions {
++    my ($self, $package, $target, $user_context, $app_context, $filter) = @_;
++    croak "Not a class method" unless ref $self;
++    print "Looking for package $package revisions for $target\n"
++        if $self->{_verbose} > 0;
++
++    my @packages = 
++        map { $self->get_package_class()->new(file => $_) }
++        $self->get_files(
++            $self->{_install_root},
++            $self->get_install_path(
++                $package,
++                $target,
++                $user_context,
++                $app_context
++            ),
++            $self->get_package_class()->get_pattern(
++                $package->get_name(),
++                undef,
++                undef,
++                $package->get_arch(),
++            )
++        );
++    @packages = grep { $filter->($_) } @packages if $filter;
++
++    return
++        sort { $b->compare($a) } # sort by revision order
++        @packages;
++}
++
++=head2 get_obsoleted_packages($package, $target, $user_context, $app_context)
++
++Get all packages obsoleted by given one, as a list of L<Youri::Package>
++objects.
++
++=cut
++
++sub get_obsoleted_packages {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++    print "Looking for packages obsoleted by $package for $target\n"
++        if $self->{_verbose} > 0;
++
++    my @packages;
++    foreach my $obsolete ($package->get_obsoletes()) {
++        my $pattern = $self->get_package_class()->get_pattern($obsolete->[Youri::Package::DEPENDENCY_NAME]);
++        my $range = $obsolete->[Youri::Package::DEPENDENCY_RANGE];
++        push(@packages,
++            grep { $range ? $_->satisfy_range($range) : 1 } 
++            map { $self->get_package_class()->new(file => $_) }
++            $self->get_files(
++                $self->{_install_root},
++                $self->get_install_path(
++                    $package, $target,
++                    $user_context,
++                    $app_context
++                ),
++                $pattern
++            )
++        );
++    }
++
++    return @packages;
++}
++
++=head2 get_replaced_packages($package, $target, $user_context, $app_context)
++
++Get all packages replaced by given one, as a list of L<Youri::Package>
++objects.
++
++=cut
++
++sub get_replaced_packages {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++    print "Looking for packages replaced by $package for $target\n"
++        if $self->{_verbose} > 0;
++
++    my @list;
++
++    # collect all older revisions
++    push(@list, $self->get_older_revisions(
++        $package,
++        $target,
++        $user_context,
++        $app_context
++    ));
++
++    # noarch packages are potentially linked from other directories
++    if ($package->get_arch() eq 'noarch') {
++        foreach my $arch ($self->get_extra_arches()) {
++            push(@list, $self->get_older_revisions(
++                $package,
++                $target,
++                $user_context,
++                { arch => $arch }
++            ));
++        }
++    }
++
++    # collect all obsoleted packages
++    push(@list, $self->get_obsoleted_packages(
++        $package,
++        $target,
++        $user_context,
++        $app_context
++    ));
++
++    return @list;
++}
++
++=head2 get_files($path, $pattern)
++
++Get all files found in a directory, using an optional filtering pattern
++(applied to the whole file name), as a list of files.
++
++=cut
++
++sub get_files {
++    my ($self, $root, $path, $pattern) = @_;
++    croak "Not a class method" unless ref $self;
++    # debugging for bug 34999
++    print "Looking for files matching $pattern in $root/$path\n";
++#        if $self->{_verbose} > 1;
++
++    my $grep = "";
++    $grep = "-regextype posix-egrep -regex '.*\/$pattern'" if ($pattern);
++    # XXX: run find in a directory the user is guaranteed to have read
++    # permissions! find simply exits with error if the user doesn't have
++    # read permission on the *current* dir; as this code is run thru many
++    # sudo invocations, sometimes the user calling it has $HOME chmoded to
++    # 0700, making find fail when run as mandrake
++    # debugging for bug 34999
++    print ".. running command: find -L $root/$path $grep -type f\n";
++    my @files = map { chop; $_; } `cd && find -L $root/$path $grep -type f`;
++    die "FATAL: get_files(): find failed!" if ($?);
++
++    return @files;
++}
++
++=head2 get_install_root()
++
++Returns installation root
++
++=cut
++
++sub get_install_root {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_install_root};
++}
++
++
++=head2 get_distribution_roots()
++
++Returns distribution roots (ie install_root + target + arch)
++(it returns a list in case of noarch)
++
++=cut
++
++sub get_distribution_roots {
++    my ($self, $package, $target) = @_;
++    croak "Not a class method" unless ref $self;
++
++    map { 
++	$self->_get_dir($self->{_install_root}, $_);
++    } $self->get_distribution_paths($package, $target);
++}
++
++=head2 get_install_dir($package, $target, $user_context, $app_context)
++
++Returns install destination directory for given L<Youri::Package> object
++and given target.
++
++=cut
++
++sub get_install_dir {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->_get_dir(
++        $self->{_install_root},
++        $self->get_install_path($package, $target, $user_context, $app_context)
++    );
++}
++
++=head2 get_archive_root()
++
++Returns archiving root
++
++=cut
++
++sub get_archive_root {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_archive_root};
++}
++
++=head2 get_archive_dir($package, $target, $user_context, $app_context)
++
++Returns archiving destination directory for given L<Youri::Package> object
++and given target.
++
++=cut
++
++sub get_archive_dir {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->_get_dir(
++        $self->{_archive_root},
++        $self->get_archive_path($package, $target, $user_context, $app_context)
++    );
++}
++
++
++=head2 get_version_root()
++
++Returns versionning root
++
++=cut
++
++sub get_version_root {
++    my ($self) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->{_version_root};
++}
++
++=head2 get_version_dir($package, $target, $user_context, $app_context)
++
++Returns versioning destination directory for given L<Youri::Package>
++object and given target.
++
++=cut
++
++sub get_version_dir {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return $self->_get_dir(
++        $self->{_version_root},
++        $self->get_version_path($package, $target, $user_context, $app_context)
++    );
++}
++
++sub _get_dir {
++    my ($self, $root, $path) = @_;
++
++    return substr($path, 0, 1) eq '/' ?
++        $path :
++        $root . '/' . $path;
++}
++
++=head2 get_install_file($package, $target, $user_context, $app_context)
++
++Returns install destination file for given L<Youri::Package> object and
++given target.
++
++=cut
++
++sub get_install_file {
++    my ($self, $package, $target, $user_context, $app_context) = @_;
++    croak "Not a class method" unless ref $self;
++
++    return 
++        $self->get_install_dir($package, $target, $user_context, $app_context) .
++        '/' .
++        $package->get_file_name();
++}
++
++=head2 get_install_path($package, $target, $user_context, $app_context)
++
++Returns installation destination path (relative to repository root) for given
++L<Youri::Package> object and given target.
++
++=head2 get_archive_path($package, $target, $user_context, $app_context)
++
++Returns archiving destination path (relative to repository root) for given
++L<Youri::Package> object and given target.
++
++=head2 get_version_path($package, $target, $user_context, $app_context)
++
++Returns versioning destination path (relative to repository root) for given
++L<Youri::Package> object and given target.
++
++=head1 SUBCLASSING
++
++The following methods have to be implemented:
++
++=over
++
++=item get_install_path
++
++=item get_archive_path
++
++=item get_version_path
++
++=back
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/lib/Youri/Utils.pm
+===================================================================
+--- build_system/mdv-youri-core/trunk/lib/Youri/Utils.pm	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/lib/Youri/Utils.pm	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,98 @@
++# $Id: Utils.pm 1713 2006-10-16 16:39:53Z warly $
++package Youri::Utils;
++
++=head1 NAME
++
++Youri::Utils - Youri shared functions
++
++=head1 DESCRIPTION
++
++This module implement some helper functions for all youri applications.
++
++=cut
++
++use base qw(Exporter);
++use Carp;
++use strict;
++use warnings;
++
++our @EXPORT = qw(
++    create_instance
++    load_class
++    add2hash
++    add2hash_
++);
++
++=head2 create_instance($class, $config, $options)
++
++Create an instance from a plugin implementing given interface, using given
++configuration and local options.
++Returns a plugin instance, or undef if something went wrong.
++
++=cut
++
++sub create_instance {
++    my ($interface, $config, $options) = @_;
++
++    croak 'No interface given' unless $interface;
++    croak 'No config given' unless $config;
++
++    my $class = $config->{class};
++    if (!$class) {
++        carp "No class given, can't load plugin";
++        return;
++    }
++
++    # ensure loaded
++    load_class($class);
++
++    # check interface
++    if (!$class->isa($interface)) {
++        carp "$class is not a $interface";
++        return;
++    }
++
++    # instantiate
++    no strict 'refs';
++
++    return $class->new(
++        $config->{options} ? %{$config->{options}} : (),
++        $options ? %{$options} : (),
++    );
++}
++
++sub load_class {
++    my ($class) = @_;
++
++    $class .= '.pm';
++    $class =~ s/::/\//g;
++    require $class;
++}
++
++# structure helpers
++
++sub add2hash  {
++    my ($a, $b) = @_;
++    while (my ($k, $v) = each %{$b || {}}) {
++        $a->{$k} ||= $v;
++    }
++    return $a;
++}
++
++sub add2hash_ {
++    my ($a, $b) = @_;
++    while (my ($k, $v) = each %{$b || {}}) {
++        exists $a->{$k} or $a->{$k} = $v;
++    }
++    return $a;
++}
++
++=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;
+
+Added: build_system/mdv-youri-core/trunk/t/00distribution.t
+===================================================================
+--- build_system/mdv-youri-core/trunk/t/00distribution.t	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/t/00distribution.t	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,15 @@
++#!/usr/bin/perl
++# $Id: 00distribution.t 1179 2006-08-05 08:30:57Z warly $
++
++use Test::More;
++
++BEGIN {
++    eval {
++        require Test::Distribution;
++    };
++    if($@) {
++        plan skip_all => 'Test::Distribution not installed';
++    } else {
++        import Test::Distribution only => [ qw/use pod description/ ];
++    }
++}
+
+
+Property changes on: build_system/mdv-youri-core/trunk/t/00distribution.t
+___________________________________________________________________
+Added: svn:executable
+   + *
+
+Added: build_system/mdv-youri-core/trunk/t/cowsay-3.03-11mdv2007.0.noarch.rpm
+===================================================================
+(Binary files differ)
+
+
+Property changes on: build_system/mdv-youri-core/trunk/t/cowsay-3.03-11mdv2007.0.noarch.rpm
+___________________________________________________________________
+Added: svn:mime-type
+   + application/octet-stream
+
+Added: build_system/mdv-youri-core/trunk/t/gpghome/pubring.gpg
+===================================================================
+(Binary files differ)
+
+
+Property changes on: build_system/mdv-youri-core/trunk/t/gpghome/pubring.gpg
+___________________________________________________________________
+Added: svn:mime-type
+   + application/octet-stream
+
+Added: build_system/mdv-youri-core/trunk/t/gpghome/secring.gpg
+===================================================================
+(Binary files differ)
+
+
+Property changes on: build_system/mdv-youri-core/trunk/t/gpghome/secring.gpg
+___________________________________________________________________
+Added: svn:mime-type
+   + application/octet-stream
+
+Added: build_system/mdv-youri-core/trunk/t/gpghome/trustdb.gpg
+===================================================================
+(Binary files differ)
+
+
+Property changes on: build_system/mdv-youri-core/trunk/t/gpghome/trustdb.gpg
+___________________________________________________________________
+Added: svn:mime-type
+   + application/octet-stream
+
+Added: build_system/mdv-youri-core/trunk/t/package.t
+===================================================================
+--- build_system/mdv-youri-core/trunk/t/package.t	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/t/package.t	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,482 @@
++#!/usr/bin/perl
++# $Id: /local/youri/soft/trunk/t/version.t 2257 2006-07-05T09:22:47.088572Z guillaume  $
++
++use Test::More;
++use Test::Exception;
++use Youri::Utils;
++use File::Temp qw/tempdir/;
++use File::Basename;
++use strict;
++
++my @classes = qw/
++    Youri::Package::URPM
++    Youri::Package::RPM4
++/;
++my $dir      = dirname($0);
++my $rpm      = 'cowsay-3.03-11mdv2007.0.noarch.rpm';
++my $fake_rpm = 'foobar.rpm';
++plan(tests => 37 * scalar @classes);
++
++foreach my $class (@classes) {
++    load_class($class);
++
++    my $temp_dir  = tempdir(CLEANUP => 1);
++    my $file      = "$dir/$rpm";
++    my $fake_file = "$temp_dir/$fake_rpm";
++
++    # instanciation errors
++    dies_ok { $class->new(file => undef) } 'undefined file';
++    dies_ok { $class->new(file => $fake_file) } 'non-existant file';
++    system('touch', $fake_file);
++    chmod 0000, $fake_file;
++    dies_ok { $class->new(file => $fake_file) } 'non-readable file';
++    chmod 0644, $fake_file;
++    dies_ok { $class->new(file => $fake_file) } 'non-rpm file';
++
++    my $package = $class->new(file => $file);
++    isa_ok($package, $class);
++
++    # tag value access
++    is($package->get_name(), 'cowsay', 'get name directly');
++    is($package->get_tag('name'), 'cowsay', 'get name indirectly');
++    is($package->get_version(), '3.03', 'get version directly');
++    is($package->get_tag('version'), '3.03', 'get version indirectly');
++    is($package->get_release(), '11mdv2007.0', 'get release directly');
++    is($package->get_tag('release'), '11mdv2007.0', 'get release indirectly');
++    is($package->get_arch(), 'noarch', 'get arch directly');
++    is($package->get_tag('arch'), 'noarch', 'get arch indirectly');
++    is($package->get_summary(), 'Configurable talking cow', 'get summary directly');
++    is($package->get_tag('summary'), 'Configurable talking cow', 'get summary indirectly');
++    is($package->get_url(), 'http://www.nog.net/~tony/warez/cowsay.shtml', 'get url directly');
++    is($package->get_tag('url'), 'http://www.nog.net/~tony/warez/cowsay.shtml', 'get url indirectly');
++    is($package->get_packager(), 'Guillaume Rousse <guillomovitch at mandriva.org>', 'get packager directly');
++    is($package->get_tag('packager'), 'Guillaume Rousse <guillomovitch at mandriva.org>', 'get packager indirectly');
++    is($package->get_file_name(), 'cowsay-3.03-11mdv2007.0.noarch.rpm', 'file name');
++    is($package->get_revision(), '3.03-11mdv2007.0', 'revision');
++
++    # name formating
++    is($package->as_formated_string('%{name}-%{version}-%{release}'), 'cowsay-3.03-11mdv2007.0', 'formated string name');
++    is($package->as_string(), 'cowsay-3.03-11mdv2007.0.noarch', 'default string');
++    is($package, 'cowsay-3.03-11mdv2007.0.noarch', 'stringification');
++
++    # type
++    ok(!$package->is_source(), 'not a source package');
++    ok($package->is_binary(), 'a binary package');
++    is($package->get_type(), 'binary', 'a binary package');
++
++    # gpg key
++    is($package->get_gpg_key(), '26752624', 'get gpg key');
++
++    # dependencies
++    is_deeply(
++        [ $package->get_requires() ],
++        [
++            [ 'perl-base', undef ], 
++            [ 'perl(Cwd)', undef ],
++            [ 'perl(File::Basename)', undef ],
++            [ 'perl(Getopt::Std)', undef ],
++            [ 'perl(Text::Tabs)', undef ],
++            [ 'perl(Text::Wrap)', undef ]
++        ],
++        'requires'
++    );
++    is_deeply(
++        [ $package->get_provides() ],
++        [
++            [ 'cowsay', '== 3.03-11mdv2007.0']
++        ],
++        'provides'
++    );
++    is_deeply(
++        [ $package->get_obsoletes() ],
++        [ ],
++        'obsoletes'
++    );
++    is_deeply(
++        [ $package->get_conflicts() ],
++        [ ],
++        'conflicts'
++    );
++
++    # files
++    is_deeply(
++        [ $package->get_files() ],
++        [
++            [
++                '/etc/bash_completion.d/cowsay',
++                33188,
++                '6048be1dd827011c15cab0c3db1f438d'
++            ],
++            [
++                '/usr/bin/cowsay',
++                33261,
++                'b405026c6040eeb4781ca5c523129fe4'
++            ],
++            [
++                '/usr/bin/cowthink',
++                41471,
++                ''
++            ],
++            [
++                '/usr/share/cows',
++                16877,
++                ''
++            ],
++            [
++                '/usr/share/cows/beavis.zen.cow',
++                33188,
++                '582b2ddb72122d3aa078730abd0456b3'
++            ],
++            [
++                '/usr/share/cows/bong.cow',
++                33188,
++                '045f9bf39c027dded9a7145f619bac02'
++            ],
++            [
++                '/usr/share/cows/bud-frogs.cow',
++                33188,
++                '5c61632eb06305d613061882e1955cd2'
++            ],
++            [
++                '/usr/share/cows/bunny.cow',
++                33188,
++                '05eb914d3b96aea903542cb29f5c42c7'
++            ],
++            [
++                '/usr/share/cows/cheese.cow',
++                33188,
++                'f3618110a22d8e9ecde888c1f5e38b61'
++            ],
++            [
++                '/usr/share/cows/cower.cow',
++                33188,
++                'd73ea60eec692555a34a9f3eec981578'
++            ],
++            [
++                '/usr/share/cows/daemon.cow',
++                33188,
++                'a7dd7588ee0386a0f29e88e4881885ee'
++            ],
++            [
++                '/usr/share/cows/default.cow',
++                33188,
++                'f1206515a0f27e9d5cf09c188e46bc82'
++            ],
++            [
++                '/usr/share/cows/dragon-and-cow.cow',
++                33188,
++                '0ca99b8edd1a9d14fd231a88d9746b39'
++            ],
++            [
++                '/usr/share/cows/dragon.cow',
++                33188,
++                '448f736bf56dccafa2635e71e7485345'
++            ],
++            [
++                '/usr/share/cows/duck.cow',
++                33188,
++                'd8ffcd64667d2e3697a3e8b65e8bea9d'
++            ],
++            [
++                '/usr/share/cows/elephant-in-snake.cow',
++                33188,
++                'c5a9f406277e0e8a674bd3ffb503738f'
++            ],
++            [
++                '/usr/share/cows/elephant.cow',
++                33188,
++                'e355c72e893787376c047805d4a1fe9d'
++            ],
++            [
++                '/usr/share/cows/eyes.cow',
++                33188,
++                'b2eb5b612fae17877895aa6edafa0a5f'
++            ],
++            [
++                '/usr/share/cows/flaming-sheep.cow',
++                33188,
++                '3213cfa04a069f42d71115ca623a2f95'
++            ],
++            [
++                '/usr/share/cows/ghostbusters.cow',
++                33188,
++                'df294e6278bcb275aecb0fbd6b2546ba'
++            ],
++            [
++                '/usr/share/cows/girafe.cow',
++                33188,
++                '6d2e142313109b6a5a0a45dba0f11351'
++            ],
++            [
++                '/usr/share/cows/head-in.cow',
++                33188,
++                '365287a5d1f34a53f8716285e79c28df'
++            ],
++            [
++                '/usr/share/cows/hellokitty.cow',
++                33188,
++                'e0bbea69c4cbcfb3d799740ccc8a0b0e'
++            ],
++            [
++                '/usr/share/cows/kenny.cow',
++                33188,
++                '16ce8c334a7547197ac4c9e8a1d6ae90'
++            ],
++            [
++                '/usr/share/cows/kiss.cow',
++                33188,
++                '2a7bdd4a20741b7769af463bf09e64e8'
++            ],
++            [
++                '/usr/share/cows/kitty.cow',
++                33188,
++                '76d65a3ebfbacb16a654c1aa1af6ed27'
++            ],
++            [
++                '/usr/share/cows/koala.cow',
++                33188,
++                'cc524706707f32253dd06fc548334f11'
++            ],
++            [
++                '/usr/share/cows/kosh.cow',
++                33188,
++                'e4e28e0f472bd524fd1b44c67ae357c2'
++            ],
++            [
++                '/usr/share/cows/luke-koala.cow',
++                33188,
++                '63bbc35da73cd22b8cf25f86dcf9f870'
++            ],
++            [
++                '/usr/share/cows/mech-and-cow',
++                33188,
++                '12c0320b33704d8564dd97278d056204'
++            ],
++            [
++                '/usr/share/cows/meow.cow',
++                33188,
++                'a6092008647ed37cfe1663d10e388cbb'
++            ],
++            [
++                '/usr/share/cows/milk.cow',
++                33188,
++                'd26ac36e13e77dabb408e104fc8e0167'
++            ],
++            [
++                '/usr/share/cows/moofasa.cow',
++                33188,
++                '5fcdd4a9f3bf521c337af0a066b14512'
++            ],
++            [
++                '/usr/share/cows/moose.cow',
++                33188,
++                'dcfa09df7d2b9afa112dab374bf06e99'
++            ],
++            [
++                '/usr/share/cows/mutilated.cow',
++                33188,
++                '24cdaef0a29fb44dc673abf19a8ba631'
++            ],
++            [
++                '/usr/share/cows/phaco.cow',
++                33188,
++                'f277c1bf92ce2a3f6058955ba93758aa'
++            ],
++            [
++                '/usr/share/cows/pumpkin.cow',
++                33188,
++                'c661ea78714c1ce31559f77d73694473'
++            ],
++            [
++                '/usr/share/cows/ren.cow',
++                33188,
++                '3d7941d454779e000adc1c91e5f0b20b'
++            ],
++            [
++                '/usr/share/cows/satanic.cow',
++                33188,
++                'a69ca42a31486757ddcb322a1e68f886'
++            ],
++            [
++                '/usr/share/cows/shark.cow',
++                33188,
++                'd8950ec63abb00bbd9d96ec63637c1ac'
++            ],
++            [
++                '/usr/share/cows/sheep.cow',
++                33188,
++                '543b75f295cbd51326f5a40f111469f1'
++            ],
++            [
++                '/usr/share/cows/skeleton.cow',
++                33188,
++                '64f6ec1a0c170508e72269d533492e57'
++            ],
++            [
++                '/usr/share/cows/small.cow',
++                33188,
++                '50cb1c55628c439fc81f96db9d855252'
++            ],
++            [
++                '/usr/share/cows/sodomized.cow',
++                33188,
++                'b4888afcca51629cc3138b283608b837'
++            ],
++            [
++                '/usr/share/cows/stegosaurus.cow',
++                33188,
++                'fb0e45d101a3ecba9cf6e112facbbc7e'
++            ],
++            [
++                '/usr/share/cows/stimpy.cow',
++                33188,
++                '9b4ec6e0750ba0eeaaa432d8d3413559'
++            ],
++            [
++                '/usr/share/cows/supermilker.cow',
++                33188,
++                '316573fb585e4a6b375373c85be025b1'
++            ],
++            [
++                '/usr/share/cows/surgery.cow',
++                33188,
++                '7f25005083c1fde19d4e548c005ef000'
++            ],
++            [
++                '/usr/share/cows/telebears.cow',
++                33188,
++                '15f00abb070d9018ce6ef3441e936ef4'
++            ],
++            [
++                '/usr/share/cows/three-eyes.cow',
++                33188,
++                'c85faef9496f4a5b111bd92bfd7e7528'
++            ],
++            [
++                '/usr/share/cows/turkey.cow',
++                33188,
++                '484b5bc69c09d420d7fd5586d8570f04'
++            ],
++            [
++                '/usr/share/cows/turtle.cow',
++                33188,
++                '87eed5a00e88860b78dbec04efcdede3'
++            ],
++            [
++                '/usr/share/cows/tux.cow',
++                33188,
++                'dc1db4eac66c99179ef6adb15dd75bda'
++            ],
++            [
++                '/usr/share/cows/udder.cow',
++                33188,
++                'd97f78887c3b218a54876edc51f2963b'
++            ],
++            [
++                '/usr/share/cows/vader-koala.cow',
++                33188,
++                '7b5dd51278f0fa217a70a9b499f97a07'
++            ],
++            [
++                '/usr/share/cows/vader.cow',
++                33188,
++                '97b4ef9fc4c26082f253e9f0f35c4590'
++            ],
++            [
++                '/usr/share/cows/www.cow',
++                33188,
++                'ef4c0bc8330f329666e1705f97f283cc'
++            ],
++            [
++                '/usr/share/doc/cowsay-3.03',
++                16877,
++                ''
++            ],
++            [
++                '/usr/share/doc/cowsay-3.03/INSTALL',
++                33188,
++                '3333fd2865107626d5dffc0dbfb7e244'
++            ],
++            [
++                '/usr/share/doc/cowsay-3.03/LICENSE',
++                33188,
++                'f879dda90a5a9928253a63ecd76406e6'
++            ],
++            [
++                '/usr/share/doc/cowsay-3.03/README',
++                33188,
++                'a5c1c61e4920c278a735cdaaca62453e'
++            ],
++            [
++                '/usr/share/man/man1/cowsay.1.bz2',
++                33188,
++                '01fdd49d0b477f20099aae384fe8c1b2'
++            ],
++            [
++                '/usr/share/man/man1/cowthink.1.bz2',
++                41471,
++                ''
++            ]
++        ],
++        'files'
++    );
++
++    # changelog
++    is_deeply(
++        [ $package->get_changes() ],
++        [
++            [
++                'Guillaume Rousse <guillomovitch at mandriva.org> 3.03-11mdv2007.0',
++                1149847200,
++		'- %mkrel' . "\n" .
++		'- rpmbuildupdate aware',
++            ],
++            [
++                'Guillaume Rousse <guillomovitch at mandriva.org> 3.03-10mdk ',
++                1117879200,
++		'- fix man page (fix #16291)',
++            ],
++            [
++                'Guillaume Rousse <guillomovitch at mandrake.org> 3.03-9mdk ',
++                1090058400,
++		'- hurry businesman compliant (aka two new wonderful cows)',
++            ],
++            [
++                'Guillaume Rousse <guillomovitch at mandrake.org> 3.03-8mdk ',
++                1089540000,
++		'- apologies to the girafes (with one only f)',
++            ],
++            [
++                'Guillaume Rousse <guillomovitch at mandrake.org> 3.03-7mdk ',
++                  1086429600,
++		'- #mandrakefr compliant (aka four new additional cows)',
++            ],
++            [
++                'Guillaume Rousse <guillomovitch at linux-mandrake.com> 3.03-6mdk',
++                1061460000,
++		'- save.the.world patch',
++            ]
++        ],
++        'changelog'
++    );
++    is_deeply(
++        $package->get_last_change(),
++        [
++            'Guillaume Rousse <guillomovitch at mandriva.org> 3.03-11mdv2007.0',
++            1149847200,
++	    '- %mkrel' . "\n" .
++	    '- rpmbuildupdate aware',
++        ],
++        'last change'
++    );
++    is($package->compare($package), 0, 'compare');
++
++    # signature test
++    system('cp', $file, $temp_dir);
++    $package = $class->new(file => "$temp_dir/$rpm");
++
++    $package->sign('Youri', 't/gpghome', 'Youri rulez');
++
++    $package = $class->new(file => "$temp_dir/$rpm");
++    is($package->get_gpg_key(), '2333e817', 'get gpg key');
++}
+
+
+Property changes on: build_system/mdv-youri-core/trunk/t/package.t
+___________________________________________________________________
+Added: svn:executable
+   + *
+
+Added: build_system/mdv-youri-core/trunk/t/version.t
+===================================================================
+--- build_system/mdv-youri-core/trunk/t/version.t	                        (rev 0)
++++ build_system/mdv-youri-core/trunk/t/version.t	2011-01-05 13:23:45 UTC (rev 210)
+@@ -0,0 +1,71 @@
++#!/usr/bin/perl
++# $Id: version.t 1179 2006-08-05 08:30:57Z warly $
++
++use Test::More;
++use Youri::Check::Input::Updates;
++use strict;
++
++my @differents = (
++    [ '3.0.0', '1.0.0' ],
++    [ '3.0.0', '1.99.9' ],
++    [ '3.0.1', '3.0' ],
++    [ '3.0pl1', '3.0' ],
++    [ '3.0', '3.0beta1' ],
++    [ '3.0', '3.0beta' ],
++    [ '3.0', '3.0alpha1' ],
++    [ '3.0', '3.0alpha' ],
++    [ '3.0', '3.0pre1' ],
++    [ '3.0', '3.0pre' ],
++    [ '3.0pre', '3.0beta' ],
++    [ '3.0beta', '3.0alpha' ],
++    [ '1.0.0-p1', '1.0.0RC1' ],
++    [ '0.9.7f', '0.9.7e' ],
++    [ '10', '9' ],
++);
++
++my @equals = (
++    [ '1.0.0', '1.0.0' ],
++    [ '0.9Beta1', '0.9beta1' ],
++    [ '0.9beta1', '0.9 beta 1' ],
++    [ '0.3-alpha', '0.3_alpha' ],
++    [ '0.02', '.02' ],
++    [ '2.0.11', '15aug2000' ],
++    [ '2.0.11', '20060401' ],
++    [ '20', '20060401' ],
++);
++
++plan tests => 2 * @differents + 2 * @equals;
++
++foreach my $different (@differents) {
++    ok(
++        Youri::Check::Input::Updates::is_newer(
++	    $different->[0],
++	    $different->[1]
++	),
++        "$different->[0] is newer as $different->[1]"
++    );
++    ok(
++        !Youri::Check::Input::Updates::is_newer(
++	    $different->[1],
++	    $different->[0]
++	),
++        "$different->[1] is older as $different->[0]"
++    );
++}
++
++foreach my $equal (@equals) {
++    ok(
++        !Youri::Check::Input::Updates::is_newer(
++	    $equal->[0],
++	    $equal->[1]
++	),
++        "$equal->[0] is equal as $equal->[1]"
++    );
++    ok(
++        !Youri::Check::Input::Updates::is_newer(
++	    $equal->[1],
++	    $equal->[0]
++	),
++        "$equal->[1] is equal as $equal->[0]"
++    );
++}
+
+
+Property changes on: build_system/mdv-youri-core/trunk/t/version.t
+___________________________________________________________________
+Added: svn:executable
+   + *
+-------------- next part --------------
+An HTML attachment was scrubbed...
+URL: </pipermail/mageia-sysadm/attachments/20110105/797a51f6/attachment-0001.html>
+
+ + + + + + + + + + +
+

+ +
+More information about the Mageia-sysadm +mailing list
+ -- cgit v1.2.1