#!/usr/bin/perl
###########################################################################
#
# $Id: rpmproc,v 1.24 2002/05/10 10:06:34 guillomovitch Exp $
# copyright (c) 2000 Vincent Danen <vdanen@mandrakesoft.com>
# copyright (c) 2001 Vincent Danen <vdanen@mandrakesoft.com> and
#                    Guillaume Rousse <rousse@ccr.jussieu.fr>
# 
#
# USAGE:
#
#   flags:
#       -c|--config	use an alternate config file
#       -w|--with	add a config option on the fly
#   actions:
#       --help	display help message and stop
#       --dump	dump configuration and stop
#       --info  display information
#       --build	build rpm
#       --local	upload localy
#       --ftp	upload by ftp
#       --scp	upload by scp
#       --mail	mail changelog
#       --clean clean files
#
###########################################################################
#
# rpmproc is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# rpmproc is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with rpmproc; see the file COPYING.  If not, write to the
# Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
###########################################################################

use strict;
use Mail::Sendmail;
use Net::FTP;
use Net::SCP;
use Net::SSH qw(ssh);
use File::Basename;
use File::Path;
use Getopt::Long;

# Global Variables
my $VERSION  = "2.1.4";
my $DATE     = "Fri May 10 2002";
my $MAIN_CONF = "/etc/rpmproc.conf";
my $HOME_CONF = $ENV{HOME} . "/.rpmprocrc";
my %CONFIG;

# Force US locales to avoid date localization when invoking rpm
$ENV{LC_ALL}="C";
$ENV{LANGUAGE}="C";

# get command line arguments
my ($config, @with, $help, $dump, $info, $build, $local, $scp, $ftp, $mail, $clean);
GetOptions(
  "c|config=s" => \$config,
  "w|with=s" => \@with,
  "help" => \$help,
  "dump" => \$dump,
  "info" => \$info,
  "build" => \$build,
  "local" => \$local,
  "ftp" => \$ftp,
  "scp" => \$scp,
  "mail" => \$mail,
  "clean" => \$clean
);

# parse each configuration file
if (-f $MAIN_CONF) { parse_conf($MAIN_CONF); }
if (-f $HOME_CONF) { parse_conf($HOME_CONF); }
if ($config) { parse_conf($config); }

# add command-line options
foreach my $with (@with) {
  my ($option, $value) = split(/\s*=\s*/, $with, 2);
  $CONFIG{$option} = $value;
}

# check configuration
check_conf($build, $local, $mail, $scp, $ftp);

# let's go...
if ($dump) { dump_config(); exit(0); }
if ($help) { display_help(); exit(0); }
if (!$info && !$build && !$local && !$scp && !$ftp && !$mail && !$clean) {
  print "No action specified, aborting.\n";
  exit(1);
}
if (!@ARGV) {
  print "No rpm specified, aborting.\n";
  exit(1);
}

# build rpms list
my @rpms;
foreach my $arg (@ARGV) {
  my $spec = "$CONFIG{RPM_DIR}/SPECS/$arg.spec";
  if (! -f $spec) {
    print "No spec file found for $arg, skipping.\n";
    next;
  } 

  # rpm is a structure with following fields:
  # spec  => spec file of this rpm
  # tags  => hash of all tags defined for this rpm
  # units => list of all units defined for this rpm
  # files => hash of files composing the rpm indexed by their arch
  # masks => hash of masks composing the rpm indexed by their arch
  my $rpm;
  $rpm->{spec} = $spec;
  compute_tags($rpm);
  compute_units($rpm);
  compute_files($rpm);
  compute_masks($rpm);
  push(@rpms, $rpm);
}

# proceed each action for all rpms
if ($info) { info_rpm(@rpms);      }
if ($build) { build_rpm(@rpms);      }
if ($local) { upload_local(@rpms);   }
if ($scp)   { upload_scp(@rpms);     }
if ($ftp)   { upload_ftp(@rpms);     }
if ($mail)  { mail_changelog(@rpms); }
if ($clean) { clean_rpm(@rpms); }

# Display information about a package
sub info_rpm {
  my @rpms = @_;

  foreach my $rpm (@rpms) {
    print "spec file:\t$rpm->{spec}\n";
    print "\n";
    print "tags:\n";
    foreach my $tag (keys %{$rpm->{tags}}) {
      print "  $tag:\t\t$rpm->{tags}{$tag}\n";
    }
    print "\n";
    print "units:\n";
    foreach my $unit (@{$rpm->{units}}) {
      print "  $unit\n";
    }
    print "\n";
    print "files:\n";
    foreach my $arch (keys %{$rpm->{files}}) {
      print "  $arch:\n";
      foreach my $file (@{$rpm->{files}{$arch}}) {
	print "    $file\n";
      }
    }
    print "\n";
    print "masks:\n";
    foreach my $arch (keys %{$rpm->{masks}}) {
      print "  $arch:\n";
      foreach my $mask (@{$rpm->{masks}{$arch}}) {
	print "    $mask\n";
      }
    }
  }
}

# Build a package
sub build_rpm {
  my @rpms = @_;
  print "Building...\n";

  my $type;
  SWITCH: {
    if ($CONFIG{BUILD_TYPE} eq "binary") { $type = "-bb"; last SWITCH; }
    if ($CONFIG{BUILD_TYPE} eq "source") { $type = "-bs"; last SWITCH; }
    if ($CONFIG{BUILD_TYPE} eq "all") { $type = "-ba"; last SWITCH; }
  }

  my $options;
  if ($CONFIG{BUILD_USE_PGP} == 1) { $options .= " --sign"; }
  if ($CONFIG{BUILD_CLEAN_SOURCE_FILES} == 1) { $options .= " --rmsource --clean"; }

  my ($major, $minor, $revision) = (`rpm --version` =~ /.*(\d+)\.(\d+)\.(\d+)/);
  my $archs = ($major >= 4 && $revision >= 3)? "--target $CONFIG{BUILD_ARCHS}": "--target=$CONFIG{BUILD_ARCHS}";

  my @specs;
  foreach my $rpm (@rpms) {
    push (@specs, $rpm->{spec});
  }
  system("rpm $type $options $archs ". join(" ", @specs));

  if ($CONFIG{BUILD_USE_RPMLINT} == 1) {
    foreach my $rpm (@rpms) {
      foreach my $arch (keys %{$rpm->{files}}) {
	foreach my $file (@{$rpm->{files}{$arch}}) {
	  ($CONFIG{VERBOSE} == 1) && print "Checking rpm $file\n";
	  system("rpmlint $file");
	}
      }
    }
  }
}

# Upload a package localy
sub upload_local {
  my @rpms = @_;
  print "Uploading localy...\n";

  # get upload list
  foreach my $rpm (@rpms) {
    my @uploads = get_uploads($rpm, "LOCAL");

    foreach my $upload (@uploads) {
      # directory check
      if (! -d $upload->{target}) {
	# directory creation
	($CONFIG{VERBOSE} == 1) && print "Creating directory $upload->{target}\n";
	my $options;
	if ($CONFIG{LOCAL_DIR_PERMS}) { $options .= " -m $CONFIG{LOCAL_DIR_PERMS}"; }
	if ($CONFIG{LOCAL_GROUP}) { $options .= " -g $CONFIG{LOCAL_GROUP}"; }
	system("install -d $options $upload->{target}");
      } else {
	# old files cleanup
	if ($CONFIG{LOCAL_CLEAN_OLD_FILES} == 1) {
	  my @files = get_files($upload->{target});
	  my @old_files = filter_files(\@files, $upload->{masks});
	  foreach my $old_file (@old_files) {
	    ($CONFIG{VERBOSE} == 1) && print "Deleting old file $old_file\n";
	    unlink $old_file or warn "Can't delete old file $old_file\n";
	  }
	}
      }

      # files transfer
      ($CONFIG{VERBOSE} == 1) && print "Transfering to directory $upload->{target}\n";
      foreach my $file (@{$upload->{files}}) {
	($CONFIG{VERBOSE} == 1) && print "Transfering file $file\n";
	my $options;
	if ($CONFIG{LOCAL_FILE_PERMS}) { $options .= " -m $CONFIG{LOCAL_FILE_PERMS}"; }
	if ($CONFIG{LOCAL_GROUP}) { $options .= " -g $CONFIG{LOCAL_GROUP}"; }
	system("install $options $file $upload->{target}");
	if ($upload->{md5sum}) {
          my $options;
          if ($CONFIG{LOCAL_FILE_PERMS}) { $options .= " -m $CONFIG{LOCAL_FILE_PERMS}"; }
          if ($CONFIG{LOCAL_GROUP}) { $options .= " -g $CONFIG{LOCAL_GROUP}"; }
          system("install $options $upload->{md5sum} $upload->{target}/md5sum");
        }
      }
    }
  }

  # post action
  if ($CONFIG{LOCAL_RUN_COMMAND}) {
    ($CONFIG{VERBOSE} == 1) && print "Running command $CONFIG{LOCAL_RUN_COMMAND}\n";
    system($CONFIG{LOCAL_RUN_COMMAND});
  }
}

# Upload a package by scp
sub upload_scp {
  my @rpms = @_;
  print "Uploading to $CONFIG{SCP_HOST}...\n";

  # ensure tmp dir exist
  mkpath($CONFIG{TMP_DIR}) unless (-d  $CONFIG{TMP_DIR});

  # begin install script
  my $script = "$CONFIG{TMP_DIR}/install.sh";
  ($CONFIG{VERBOSE} == 1) && print "Creating script\n";
  open(SCRIPT, "> $script") or warn "Can't create script: $!";
  print SCRIPT "#/bin/sh\n";
  print SCRIPT "# install script generated by rpmproc\n";
  print SCRIPT "\n";

  # connection
  ($CONFIG{VERBOSE} == 1) && print "Connection to $CONFIG{SCP_HOST}\n";
  if (my $scp = Net::SCP->new($CONFIG{SCP_HOST}, $CONFIG{SCP_USER})) {

    my $transfer = ($CONFIG{SCP_CONNECTION_DIR}) ? $CONFIG{SCP_CONNECTION_DIR} : "~$CONFIG{SCP_USER}";

    foreach my $rpm (@rpms) {
      # get upload list
      my @uploads = get_uploads($rpm, "SCP");

      foreach my $upload (@uploads) {

	# files transfert
	($CONFIG{VERBOSE} == 1) && print "Transfering to directory $transfer\n";
	foreach my $file (@{$upload->{files}}) {
	  ($CONFIG{VERBOSE} == 1) && print "Transfering file $file\n";
	  $scp->put($file, $transfer) or warn "Can't transfer file $file: $scp->{errstr}\n";
	}
	if ($upload->{md5sum}) {
	  $scp->put($upload->{md5sum}, $transfer) or warn "Can't transfer file $upload->{md5sum}\n";
	}

	# install script continuation
	print SCRIPT "target=$upload->{target}\n";
	print SCRIPT "files=\"";
	foreach my $file (@{$upload->{files}}) {
	  print SCRIPT basename($file) . " ";
	}
	print SCRIPT "\"\n";
	print SCRIPT "masks=\"";
	foreach my $mask (@{$upload->{masks}}) {
	  print SCRIPT $mask . " ";
	}
	print SCRIPT "\"\n";
	if ($upload->{md5sum}) {
	  print SCRIPT "md5sum=" . basename($upload->{md5sum}) . "\n";
	}
	print SCRIPT "\n";
	print SCRIPT "if [ ! -d \$target ]; then\n";
	print SCRIPT "    install -d \$target";
	if ($CONFIG{SCP_DIR_PERMS}) {
	  print SCRIPT " -m $CONFIG{SCP_DIR_PERMS}";
	}
	if ($CONFIG{SCP_GROUP}) {
	  print SCRIPT " -g $CONFIG{SCP_GROUP}";
	}
	print SCRIPT "\n";
	if ($CONFIG{SCP_CLEAN_OLD_FILES} == 1) {
	  print SCRIPT "else\n";
	  print SCRIPT "    for mask in \$masks; do\n";
	  print SCRIPT "        for old_file in `ls \$target | grep -E ^\$mask\$`; do\n";
	  print SCRIPT "            rm -f \$target/\$old_file\n";
	  print SCRIPT "        done\n";
	  print SCRIPT "    done\n";
	}
	print SCRIPT "fi\n";
	print SCRIPT "\n";
	print SCRIPT "for file in \$files; do\n";
	print SCRIPT "    install \$file \$target";
	if ($CONFIG{SCP_FILE_PERMS}) {
	  print SCRIPT " -m $CONFIG{SCP_FILE_PERMS}";
	}
	if ($CONFIG{SCP_GROUP}) {
	  print SCRIPT " -g $CONFIG{SCP_GROUP}";
	}
	print SCRIPT "\n";
	print SCRIPT "    rm -f \$file\n";
	print SCRIPT "done\n";
	if ($upload->{md5sum}) {
	  print SCRIPT "install \$md5sum \$target/md5sum";
	  if ($CONFIG{SCP_FILE_PERMS}) {
	    print SCRIPT " -m $CONFIG{SCP_FILE_PERMS}";
	  }
	  if ($CONFIG{SCP_GROUP}) {
	    print SCRIPT " -g $CONFIG{SCP_GROUP}";
	  }
	  print SCRIPT "\n";
	  print SCRIPT "    rm -f \$md5sum\n";
	}
	print SCRIPT "\n";
      }
    }

    # install script termination
    if ($CONFIG{SCP_RUN_COMMAND}) {
      print SCRIPT "$CONFIG{SCP_RUN_COMMAND}\n";
      print SCRIPT "\n";
    }
    print SCRIPT "rm -f \$0\n";
    close(SCRIPT);

    # install script transfert
    $scp->put($script, $transfer) or warn "Can't transfer install script $script $scp->{errstr}\n";

    # install script execution
    ($CONFIG{VERBOSE} == 1) && print "Executing install script\n";
    ssh("$CONFIG{SCP_USER}\@$CONFIG{SCP_HOST}", "cd $transfer && sh install.sh");
  } else {
    warn "Can't connect to $CONFIG{SCP_HOST}: $@, skipping.\n";
  }

}

# Upload a package by ftp
sub upload_ftp {
  my @rpms = @_;
  print "Uploading to $CONFIG{FTP_HOST}...\n";

  # connection
  ($CONFIG{VERBOSE} == 1) && print "Connecting to $CONFIG{FTP_HOST}\n";
  if (my $ftp = Net::FTP->new($CONFIG{FTP_HOST})) {
    if ($ftp->login($CONFIG{FTP_USER}, $CONFIG{FTP_PASSWORD})) {
      $ftp->binary() or warn "Can't set binary mode\n";

      foreach my $rpm (@rpms) {
	# get upload list
	my @uploads = get_uploads($rpm, "FTP");

	foreach my $upload (@uploads) {
	  # directory check
	  if ($ftp->cwd($upload->{target})) {
	    # old files cleanup
	    if ($CONFIG{FTP_CLEAN_OLD_FILES} == 1) {
	      my @files = $ftp->ls(".");
	      my @old_files = filter_file(\@files, $upload->{masks});
	      foreach my $old_file (@old_files) {
		($CONFIG{VERBOSE} == 1) && print "Deleting old file $old_file\n";
		$ftp->delete($old_file) or warn "Can't delete old file $old_file\n";
	      }
	    }
	  } else {
	    ($CONFIG{VERBOSE} == 1) && print "Creating directory $upload->{target}\n";
	    $ftp->mkdir($upload->{target}, 1) or warn "Can't create directory $upload->{target}\n";
	    $ftp->cwd($upload->{target}) or warn "Can't go in newly created $upload->{target}\n";
	  }

	  # files transfer
	  ($CONFIG{VERBOSE} == 1) && print "Transfering to directory $upload->{target}\n";
	  foreach my $file (@{$upload->{files}}) {
	    ($CONFIG{VERBOSE} == 1) && print "Transfering file $file\n";
	    $ftp->put($file) or warn "Can't transfer file $file\n";
	  }
	  if ($upload->{md5sum}) {
	    $ftp->put($upload->{md5sum}, "md5sum") or warn "Can't transfer file $upload->{md5sum}\n";
	  }
	}
      }

      # quit
      $ftp->quit() or warn "Couldn't quit gracefuly\n";
    } else {
      warn "Can't login as $CONFIG{FTP_USER}, skipping.\n";
    }
  } else {
    warn "Can't connect to $CONFIG{FTP_HOST}: $@, skipping.\n";
  }

}

# Announce a package changelog
sub mail_changelog {
  my @rpms = @_;
  print "Announcing changelog to $CONFIG{MAIL_TO}.\n";

  foreach my $rpm (@rpms) {
    # get src rpm
    my ($file) = @{$rpm->{files}{src}};
    if (-f $file) { 
      my @changelog = split(/\*/,`rpm -qp --changelog $file`);
      my $description = `rpm -qpi $file`;

      my @currtime = localtime(time());
      my @from = split(/@/,$CONFIG{MAIL_FROM});
      my $msgid = "<" . $currtime[5] . $currtime[4] . $currtime[3] . $currtime[2] . $currtime[1] . $currtime[0] . "\@" . $from[1] . ">\n";

      $Mail::Sendmail::mailcfg{mime} = 0;
      my %mail = (
	smtp         => $CONFIG{MAIL_SMTP},
	To           => $CONFIG{MAIL_TO},
	From         => $CONFIG{MAIL_FROM},
	'Reply-To'   => $CONFIG{MAIL_FROM},
	'Message-ID' => $msgid,
	'X-Mailer'   => "rpmproc $VERSION",
      );
      $mail{"Subject: "} = "[RPM] $rpm->{tags}{name}-$rpm->{tags}{version}-$rpm->{tags}{release}";
      $mail{"Message: "} .= $CONFIG{MAIL_HEADER} . "\n\n";
      $mail{"Message: "} .= "-------\n$description\n-------\n\n";
      $mail{"Message: "} .= "$changelog[1]";
      $mail{"Message: "} .= "-- \n$CONFIG{MAIL_TRAILER}\n";
      sendmail(%mail) or warn $Mail::Sendmail::error;
    } else {
      print "No source rpm found for $rpm->{tags}{name}, skipping.\n";
    } 
  }

}

# Clean a package
sub clean_rpm {
  my @rpms = @_;
  print "Cleaning...\n";

  foreach my $rpm (@rpms) {
    # clean packages files
    foreach my $arch (keys %{$rpm->{files}}) {
      foreach my $file (@{$rpm->{files}{$arch}}) {
	($CONFIG{VERBOSE} == 1) && print "Deleting file $file\n";
	unlink $file or warn "Can't delete file $file\n";
      }
    }

    # clean package spec
    ($CONFIG{VERBOSE} == 1) && print "Deleting file $rpm->{spec}\n";
    unlink $rpm->{spec} or warn "Can't delete file $rpm->{spec}\n";
  }
}

# Display help
sub display_help {
  print "\n";
  print "rpmproc v$VERSION    $DATE\n\n";
  print "usage: rpmproc [flags] [actions] [rpms]\n";
  print "\n";
  print "  flags are:\n";
  print "    [-c|--config <file>]         use <file> as an additional config file\n";
  print "    [-w|--with <option>=<value>] add <option> with <value> to current config\n";
  print "\n";
  print "  actions are:\n";
  print "    [--help]          display this help message and stop\n";
  print "    [--dump]          dump configuration information and stop\n";
  print "    [--info]          display information\n";
  print "    [--build]         build\n";
  print "    [--local]         upload to local rpms dir\n";
  print "    [--scp]           upload to remote rpms dir by scp\n";
  print "    [--ftp]           upload to remote rpms dir by ftp\n";
  print "    [--mail]          announce changelog to mailing list\n";
  print "    [--clean]         clean files\n";
  print "\n";
  print "  rpms is the list of rpms to process\n";
  print "\n";
}

# Dump configuration
sub dump_config {
  print "\nrpmproc v$VERSION    $DATE\n\n";
  print "Configuration dump for debugging purposes\n\n";
  print "  General configuration:\n";
  print "    Root RPM directory:   $CONFIG{RPM_DIR}\n";
  print "    Temporary directory:  $CONFIG{TMP_DIR}\n";
  print "    Be verbose:           " . (($CONFIG{VERBOSE} == 1) ? "yes" : "no") . "\n";
  print "\n";
  print "  Build configuration:\n";
  print "    Package type:         $CONFIG{BUILD_TYPE}\n";
  print "    Clean source files:   " . (($CONFIG{BUILD_CLEAN_FILES} == 1) ? "yes" : "no") . "\n";
  print "    Use PGP/GnuPG:        " . (($CONFIG{BUILD_USE_PGP} == 1) ? "yes" : "no") . "\n";
  print "    Use rpmlint:          " . (($CONFIG{BUILD_USE_RPMLINT} == 1) ? "yes" : "no") . "\n";
  print "    Build for archs:      $CONFIG{BUILD_ARCHS}\n";
  print "\n";
  print "  Local upload configuration:\n";
  dump_target("LOCAL");
  print "    Directory rights:     $CONFIG{LOCAL_DIR_PERMS}\n";
  print "    File rights:          $CONFIG{LOCAL_FILE_PERMS}\n";
  print "    Group:                $CONFIG{LOCAL_GROUP}\n";
  print "    Clean old files:      " . (($CONFIG{LOCAL_CLEAN_OLD_FILES} == 1) ? "yes" : "no") . "\n";
  print "    Run command:          $CONFIG{LOCAL_RUN_COMMAND}\n";
  print "    Use md5sum:           " . (($CONFIG{LOCAL_USE_MD5SUM} == 1) ? "yes" : "no") . "\n";
  print "\n";
  print "  SCP upload configuration:\n";
  print "    Host:                 $CONFIG{SCP_HOST}\n";
  print "    Connection directory: $CONFIG{SCP_CONNECTION_DIR}\n";
  dump_target("SCP");
  print "    Directory rights:     $CONFIG{SCP_DIR_PERMS}\n";
  print "    File rights:          $CONFIG{SCP_FILE_PERMS}\n";
  print "    Group:                $CONFIG{SCP_GROUP}\n";
  print "    Clean old files:      " . (($CONFIG{SCP_CLEAN_OLD_FILES} == 1) ? "yes" : "no") . "\n";
  print "    Run command:          $CONFIG{SCP_RUN_COMMAND}\n";
  print "    Use md5sum:           " . (($CONFIG{SCP_USE_MD5SUM} == 1) ? "yes" : "no") . "\n";
  print "\n";
  print "  FTP upload configuration:\n";
  print "    Host:                 $CONFIG{FTP_HOST}\n";
  print "    User:                 $CONFIG{FTP_USER}\n";
  print "    Password:             $CONFIG{FTP_PASSWORD}\n";
  dump_target("FTP");
  print "    Clean old files:      " . (($CONFIG{FTP_CLEAN_OLD_FILES} == 1) ? "yes" : "no") . "\n";
  print "    Use md5sum:           " . (($CONFIG{FTP_USE_MD5SUM} == 1) ? "yes" : "no") . "\n";
  print "\n";
  print "  Changelog configuration:\n";
  print "    SMTP server:          $CONFIG{MAIL_SMTP}\n";
  print "    Email is to:          $CONFIG{MAIL_TO}\n";
  print "    Email is from:        $CONFIG{MAIL_FROM}\n";
  print "    Email header:         $CONFIG{MAIL_HEADER}\n";
  print "    Email trailer:        $CONFIG{MAIL_TRAILER}\n";
  print "\n";
}

sub dump_target {
  my ($prefix) = @_;

  # print each arch-specific target
  foreach my $arch (split(/,/,$CONFIG{BUILD_ARCHS}), "noarch", "src") {
    my $ARCH = uc($arch);
    if (exists($CONFIG{$prefix . "_TARGET_DIR_" . $ARCH})) {
      print "    Target directory ($arch):    " . $CONFIG{$prefix . "_TARGET_DIR_" . $ARCH} . "\n";
    }
  }

  # print default target
  if (exists($CONFIG{$prefix . "_TARGET_DIR"})) {
    print "    Target directory:     " . $CONFIG{$prefix . "_TARGET_DIR"} . "\n";
  }

}

# Parse a configuration file
sub parse_conf {
  my ($conf) = @_;

  if (open(CONF, $conf)) {
    while (my $line = <CONF>) {
      chomp($line);		# no newline
      $line =~ s/#.*//;		# no comments
      $line =~ s/^\s+//;		# no leading white
      $line =~ s/\s+$//;		# no trailing white
      next unless length($line);	# anything left?
      my ($option, $value) = split(/\s*=\s*/, $line, 2);
      $CONFIG{$option} = $value;
    }
    close(CONF);
  } else {
    warn "Can't open configuration file $conf: $!, skipping.\n";
  }
}

# Check global configuration
sub check_conf {
  my ($build, $local, $mail, $scp, $ftp) = @_;

  if (
    ($local && $CONFIG{LOCAL_USE_MD5SUM} == 1)
    || ($scp && $CONFIG{SCP_USE_MD5SUM} == 1)
    || ($ftp && $CONFIG{FTP_USE_MD5SUM} == 1)
  ) {
    if (!-e "/usr/bin/md5sum") {
      print "Configuration error: md5sum not found, aborting.\n";
      exit(1);
    }
  }
  if ($build && $CONFIG{BUILD_USE_RPMLINT} == 1) {
    if (!-e "/usr/bin/rpmlint") {
      print "Configuration error: rpmlint not found, aborting.\n";
      exit(1);
    }
  }
  if ($build && $CONFIG{BUILD_USE_PGP} == 1) {
    if (!-e "/usr/bin/gpg" && !-e "/usr/bin/pgp") {
      print "Configuration error: pgp/gpg not found, aborting.\n";
      exit(1);
    }
  }
  if ($mail && !$CONFIG{MAIL_SMTP}) {
    print "Configuration error: invalid value for MAIL_SMTP, aborting.\n";
    exit(1);
  }
  if ($build && ($CONFIG{BUILD_TYPE} ne "binary" && $CONFIG{BUILD_TYPE} ne "source" && $CONFIG{BUILD_TYPE} ne "all")) {
    print "Configuration error: invalid value for BUILD_TYPE, aborting.\n";
    exit(1);
  }
}

# compute rpm tags
sub compute_tags {
  my ($rpm) = @_;

  # parse spec looking for tags definitions
  if (open (SPEC, "< $rpm->{spec}")) {
    while (my $line = <SPEC>) {
      if ($line =~ /^%define\s+(\w+)\s+(.*)$/) {
	$rpm->{tags}{$1} = resolve_macro($2, $rpm->{tags});
      }
      if ($line =~ /^(\w+):\s+(.*)$/) {
	$rpm->{tags}{lc($1)} = resolve_macro($2, $rpm->{tags});
      }
    }
    close SPEC;

  } else {
    warn "Can't open $rpm->{spec}: $!, skipping.\n";
  }

}

# compute rpm units
sub compute_units {
  my ($rpm) = @_;

  # parse spec looking for unit definitions
  if (open (SPEC, "< $rpm->{spec}")) {
    while (my $line = <SPEC>) {
      if ($line =~ /^%files(\s-(\w))?\s+(.*)/) {
	my $option = $2;
	my $suffix = resolve_macro($3, $rpm->{tags});
	if (!$suffix) {
	  push (@{$rpm->{units}}, $rpm->{tags}{name});
	} elsif ($option eq "n") {
	  push (@{$rpm->{units}}, $suffix);
	} elsif ($option eq "f") {
	  push (@{$rpm->{units}}, $rpm->{tags}{name});
	} else {
	  push (@{$rpm->{units}}, $rpm->{tags}{name} . "-" . $suffix);
	}
      }
    }
    close SPEC;
  } else {
    warn "Can't open $rpm->{spec}: $!, skipping.\n";
  }
}

# compute rpm masks
sub compute_masks {
  my ($rpm) = @_;

  # binary 
  if (exists($rpm->{tags}{buildarch})) {
    my $arch = $rpm->{tags}{buildarch};
    foreach my $unit (@{$rpm->{units}}) {
      push (@{$rpm->{masks}{$arch}}, "$unit-[0-9a-z.]+-[0-9a-z.]+\\.$arch\\.rpm");
    }
  } else {
    foreach my $arch (split(/,/,$CONFIG{BUILD_ARCHS})) {
      foreach my $unit (@{$rpm->{units}}) {
	push (@{$rpm->{masks}{$arch}}, "$unit-[0-9a-z.]+-[0-9a-z.]+\\.$arch\\.rpm");
      }
    }
  }

  # source
  if (exists($rpm->{tags}{nosource})) {
    $rpm->{masks}{src} = [ "$rpm->{tags}{name}-[0-9a-z.]+-[0-9a-z.]+\\.nosrc\\.rpm" ];
  } else {
    $rpm->{masks}{src} = [ "$rpm->{tags}{name}-[0-9a-z.]+-[0-9a-z.]+\\.src\\.rpm" ];
  }
}

# compute rpm files
sub compute_files {
  my ($rpm) = @_;

  # binary 
  if (exists($rpm->{tags}{buildarch})) {
    my $arch = $rpm->{tags}{buildarch};
    foreach my $unit (@{$rpm->{units}}) {
      push (@{$rpm->{files}{$arch}}, "$CONFIG{RPM_DIR}/RPMS/$arch/$unit-$rpm->{tags}{version}-$rpm->{tags}{release}.$arch.rpm");
    }
  } else {
    foreach my $arch (split(/,/,$CONFIG{BUILD_ARCHS})) {
      foreach my $unit (@{$rpm->{units}}) {
	push (@{$rpm->{files}{$arch}}, "$CONFIG{RPM_DIR}/RPMS/$arch/$unit-$rpm->{tags}{version}-$rpm->{tags}{release}.$arch.rpm");
      }
    }
  }

  # source
  if (exists($rpm->{tags}{nosource})) {
    $rpm->{files}{src} = [ "$CONFIG{RPM_DIR}/SRPMS/$rpm->{tags}{name}-$rpm->{tags}{version}-$rpm->{tags}{release}.nosrc.rpm" ];
  } else {
    $rpm->{files}{src} = [ "$CONFIG{RPM_DIR}/SRPMS/$rpm->{tags}{name}-$rpm->{tags}{version}-$rpm->{tags}{release}.src.rpm" ];
  }
}

# resolve a spec macro
sub resolve_macro {
  my ($text, $macros) = @_;

  # get rid of leading/trailing spaces
  $text =~ s/^\s+//;
  $text =~ s/\s+$//;

  while ($text =~ /%/) {
    $text =~ s/%{(\w+)}/$macros->{$1}/;
    $text =~ s/%(\w+)/$macros->{$1}/;
    $text =~ s/%\((.+)\)/`$1`/e;
  }
  return $text;
}

# get directory files
sub get_files {
  my ($dir) = @_;
  my @files;

  if (opendir(DIR, $dir)) {
    foreach my $file (readdir(DIR)) {
      push(@files, "$dir/$file") if (-f "$dir/$file");
    }
    closedir(DIR);
  } else {
    warn "Can't open directory $dir: $!, skipping.\n";
  }

  return @files;
}

# filter files according to a set of masks
sub filter_files {
  my ($files, $masks) = @_;
  my @result;

  foreach my $mask (@{$masks}) {
    push (@result, grep { /^(.*\/)?$mask$/ } @{$files});
  }

  return @result;
}

# get a unique md5sum file
sub get_md5sum {
  my @files = @_;

  # ensure tmp dir exist
  mkpath($CONFIG{TMP_DIR}) unless (-d  $CONFIG{TMP_DIR});

  ($CONFIG{VERBOSE} == 1) && print "Creating md5sum file\n";
  # get an unique identifier for each md5sum file
  my ($uid) = \@files =~ m/ARRAY\(0x(\w+)\)/;
  my $md5sum = "$CONFIG{TMP_DIR}/md5sum-$uid";
  if (open(MD5SUM, "> $md5sum")) {
    foreach my $file (@files) {
      my $dir = dirname($file);
      my $rpm = basename($file);
      print MD5SUM `(cd $dir; md5sum $rpm)`;
    }
    close(MD5SUM);
  } else {
    warn "Can't create md5sum file: $!, skipping.\n";
  }

  return $md5sum;
}

# get rpm uploads
sub get_uploads {
  my ($rpm, $prefix) = @_;
  my @uploads;
  my @files;
  my @masks;

  # add a specific upload for each arch-specific target defined
  foreach my $arch (keys %{$rpm->{files}}) {
    my $ARCH = uc($arch);
    if (exists($CONFIG{$prefix . "_TARGET_DIR_" . $ARCH})) {
      my $target = resolve_macro($CONFIG{$prefix . "_TARGET_DIR_" . $ARCH}, $rpm->{tags});
      my $files = $rpm->{files}{$arch};
      my $masks = $rpm->{masks}{$arch};
      my $upload = { target => $target, files => $files, masks => $masks };
      if ($CONFIG{$prefix . "_USE_MD5SUM"}) { 
	$upload->{md5sum} = get_md5sum(@{$files}) ;
	push(@{$upload->{masks}}, "md5sum");
      }
      push(@uploads, $upload);
    } else {
      push(@files, @{$rpm->{files}{$arch}});
      push(@masks, @{$rpm->{masks}{$arch}});
    }
  }
  # add a default upload if default target defined
  if (exists($CONFIG{$prefix . "_TARGET_DIR"})) {
    my $target = resolve_macro($CONFIG{$prefix . "_TARGET_DIR"}, $rpm->{tags});
    my $upload = { target => $target, files => \@files, masks => \@masks };
    if ($CONFIG{$prefix . "_USE_MD5SUM"}) { 
      $upload->{md5sum} = get_md5sum(@files) ;
      push(@{$upload->{masks}}, "md5sum");
    }
    push(@uploads, $upload);
  }

  return @uploads;
}
