#! /usr/bin/perl -w
# $Id$

# Copyright (c) 2002 Philip Hands <phil@hands.com>
# See the README file for the license

# This script creates the md5sums files, using the precalculated md5sums
# from the main archive
#
# First arg = the version directory name to target
#
# subsequent optional arguments are the architectures to publish
# If omited, the script will use all the directories in the $OUT
# directory as the architectures to publish.
#
# For example:
#     ./tools/publish_cds 3.0-pre2 i386 src m68k
#

use strict;
use MIME::Base64;
use File::Copy;
use Compress::Zlib ;

my $cd_version = shift @ARGV || die "Usage: $0 <cd_version> [<arch> ...]\n" ;
my %conf;
my %jigdosums;

sub jigsum_to_md5sum ($) {
  my $str = shift;
  
  $str =~ tr%-_%+/% ;                     # convert to  normal base64
  $str =~ tr|A-Za-z0-9+=/||cd;            # remove non-base64 chars
  $str =~ tr|A-Za-z0-9+/| -_|;            # convert to uuencoded format
  
  return unpack('H32', join'',
		map( unpack("u", chr(32 + length($_)*3/4). $_),
		     $str =~ /(.{1,60})/gs));
}

sub mkdirs ($);

sub mkdirs ($) {
  my $path = shift;

  return 1 if (-d $path) ;

  if ($path =~ m|^(.*)/[^/]*$|) {
    mkdirs($1);
  }

  mkdir($path);
}

sub md5sum_file($) {
  open(FILE, $_) or die "Can't open '$_': $!";
  binmode(FILE);
  my ($retval) = Digest::MD5->new->addfile(*FILE)->hexdigest ;
  close(FILE) ;
  return($retval);
}

# Pick up settings from CONF.sh

open(SHELL, "sh -x CONF.sh 2>&1|") || die;
while(<SHELL>) {
  chomp;
  next unless (/^[+] export (.*)$/);
  next unless ($1 =~ /^'([^=]+)=(.*)'$/ || $1 =~ /^([^=]+)=(.*)$/) ;
  $conf{$1} = $2;
}
close(SHELL) ;

my $from_dir = $conf{'OUT'} ;
my $mirror_dir = $conf{'MIRROR'} ;
my $nonus_dir = $conf{'NONUS'} ;
my $tdir = $conf{'TDIR'} ;
my $publish_url = $conf{'PUBLISH_URL'} ;
my $publish_nonus_url = $conf{'PUBLISH_NONUS_URL'} ;
my $publish_path = $conf{'PUBLISH_PATH'} ;

my $to_dir = $publish_path . "/" . $cd_version . "/" ;
my $fallback_url = $publish_url . "/$cd_version/snapshot/" ; 
my $fallback_nonus_url = $publish_nonus_url . "/$cd_version/snapshot/" ; 

my @archs ;
if ($#ARGV >= 0) {
  foreach (@ARGV) {
    push @archs, $from_dir . "/" . $_ ;
  }
} else {
  @archs = <$from_dir/*> ;
}

for my $arch (@archs) {
  my $bad_images ;

  my $to_arch = $arch ;
  $to_arch =~ s|^$from_dir|$to_dir/jigdo| ;
  $to_arch =~ s%/src$%/source% ;
  mkdirs($to_arch) || die "failed to create $to_arch" ;

  open(MD5OUT, ">$to_arch/MD5SUMS");

  for my $jigdo (<$arch/*.jigdo>) {
    my ($http, $filename, $size, $md5) ;

    $jigdo =~ m|/([^-/]*)-([^-/]*)-([^-/]*)\.jigdo$| ;
    my ($distname) = $1 ;
    my ($archname) = $2 ;
    my ($diskname) = $3 ;

    my ($archdesc) = $archname ;
    $archdesc =~ s/src/source/ ;

    # find out the image name
    open (JIGDO, $jigdo) || die;
    printf "Opening %s\n", $jigdo ;

    my ($newjigdo) = $jigdo ;
    $newjigdo =~ s|^.*/([^/]*)$|$to_arch/$1| ;
    my $jigdo_gz = gzopen($newjigdo, "wb") ||
      die "ERROR: failed to open $newjigdo ($!)";

    my $section = "" ;
    while(<JIGDO>) {
      chomp;
      if (/^\[(.*)\]$/) {
	die "ERROR: don't know how to handle multi-image jigdo files"
	  if ($section eq "Image" && $1 eq "Image") ;
	$section = $1;
        $jigdo_gz->gzwrite($_ . "\n") ;
	next ;
      }
      if ($section eq "Image") {
	if (/^Filename=(.*)$/) { $filename = $1 ; }
	if (/^Template=(.*)$/) {
	  $_ = "Template=$publish_url/$cd_version/jigdo/$archdesc/$distname-$archname-$diskname.template" ;
	}
      }
      elsif ($section eq "Parts") {
	if (/^([^=]*)=(.*)$/) {
	  my $jigsum = $1 ;
	  my $file = $2 ;
	  if (defined($jigdosums{$file})) {
	    die "sums don't match for $file" if ($jigdosums{$file} ne $jigsum);
	  } else {
	    $jigdosums{$file} = $jigsum ;
	  }

	  # and now we make the hardlink snapshot,
	  # and check that all the md5's match

	  my $frompath = $file ;
	  my $tsubdir = "$distname-$archname/CD$diskname" ;
	  $frompath =~ s|^Debian:|$tdir/$tsubdir/| ;
	  $frompath =~ s|^Non-US:|$tdir/$tsubdir/| ;

	  if (!-f $frompath) {
	    #print STDERR "WARNING: $frompath is not a file," ;
	    # if it's missing, let's grab it from the mirror
	    $frompath = $file ;
	    $frompath =~ s|^Debian:|$mirror_dir/| ;
	    $frompath =~ s|^Non-US:|$nonus_dir/| ;
	    #print STDERR "lets try $frompath\n" ;
          }

	  my $topath = $file ;
	  $topath =~ s|^Debian:|$to_dir/snapshot/| ;
	  $topath =~ s|^Non-US:|$to_dir/snapshot/| ;

	  $topath =~ m|^(.*)/[^/]*$| ;
	  mkdirs($1) ;
	  if (-f $frompath) {
	    if (!-f $topath) {
	      link ($frompath, $topath) || 
	        die "ERROR: linking $frompath to $topath";
	    } else {
	      use File::Compare;
	      my ($f_dev,$f_ino) = lstat($frompath) ;
	      my ($t_dev,$t_ino) = lstat($topath) ;
	      if ((($f_dev != $t_dev) || ($f_ino != $t_ino)) &&
		  compare($frompath,$topath) != 0) {
		die "ERROR: $frompath != $topath" ;
	      }
	    }
	  } else {
	    print STDERR "ERROR: $frompath is not a file\n" ;
	  }

        }
      }
      $jigdo_gz->gzwrite($_ . "\n") ;
    }
    $jigdo_gz->gzwrite(sprintf "\n[Servers]\nDebian=%s\nNon-US=%s\n",
      			       $fallback_url, $fallback_nonus_url); 

    $jigdo_gz->gzclose() ;
    close(JIGDO);

    # get the checksum & size from the template
    my $template = $jigdo ;
    $template =~ s/.jigdo$/.template/ ;
    open (TPL, "jigdo-file ls --template $template|") || die;
    while (<TPL>) {
      chomp;
      next unless (/^image-info\s+(\S+)\s+(\S*)\s+(\S+)$/) ;
      $size = $1 ;
      $md5 = jigsum_to_md5sum($2);
    }
    if (!defined($md5)) {
      $bad_images++ ;
      print STDERR "ERROR: md5 not available from $template\n";
      next;
    }
    print STDERR "WARNING: image too small ($size in $template)\n"
      if ($size < 10000000) ;
    if ($size >= 680000000) {
      print STDERR "ERROR: image too big ($size in $template)\n" ;
      #exit 1 ;
    }
    printf MD5OUT "%032s  %s\n", $md5, $filename;
    close(TPL) ;

    copy($template, $to_arch) ||
      die "ERROR copying $template to $to_arch ($!)" ;
  }
  close(MD5OUT);
  unlink ("$to_arch/MD5SUMS") if ($bad_images) ;

}