#!/bin/sh
exec perl -wx -- "$0" "$@"
#!perl
# vim: filetype=perl :
use strict qw($debug);
$debug = 0;

if (@ARGV and $ARGV[0] eq "-h") {
  print STDERR "Bedienung:\n",
  "Wechseln Sie in das gewünschte lokale Verzeichnis: \n",
  "% cd /mein/daten/verzeichnis\n\n",
  "Führen Sie aus: sync-ftp RECHNERNAME WURZELVERZEICHNIS_AUF_DEM_SERVER\n",
  "% sync-ftp  www.dr-rohlfs.de  /pub\n";
  exit;
}

## auf default-sprache setzen, de_de gibt anderes datumsformat!
$ENV{LANG}= "C";

################################################################################
# ftp-verbindung aufmachen
################################################################################
use Net::FTP;
my $ftp;
{
  ## parameter bestimmen
  my $REMOTE_HOST = shift;
  $REMOTE_HOST ||= "dr-rohlfs.de";
  my $REMOTE_DIR  = shift;

  ## einloggen auf dem rechner
  $ftp = Net::FTP->new($REMOTE_HOST) 	
    or die "Cannot connect to $REMOTE_HOST: $@";
  
  ## logindaten bestimmen
  my $USERNAME    = "";
  my $PASSWD      = "";
  if (open inF, "<$ENV{HOME}/.netrc") {
    while (<inF>) {
      ($USERNAME, $PASSWD) = (/login\s+(\S+)\spassword\s+(\S+)/i)
	if /machine\s+$REMOTE_HOST/io;
    }
    close inF;
    ##print "$USERNAME    $PASSWD\n"; exit;
  }
  else { 
    warn "kein .netrc!\n" }
  $ftp->login($USERNAME, $PASSWD) 	or die "Cannot login";
  if (defined $REMOTE_DIR) {
    $ftp->cwd($REMOTE_DIR) 		or die "cant chdir $REMOTE_DIR" }
  $ftp->binary				or die "no binary mode?";
}
## wurzelverzeichnis fuer unsere operation bestimmenwurzelverzeichnis fuer unsere operation bestimmen
my $ftp_rootdir = $ftp->pwd		or die "cant pwd";

################################################################################
# dateiinformationen vom ftp-server und lokal anfordern
################################################################################
#{ local $, = "\n"; print ($ftp->ls("-lR")); } exit;
#open lsP, "cat /home/ingo/ls-R|" or die "fehler bei ls: $!";
#my ($f_file) = ls2hash (grep !/\.log$/, <lsP>);
my ($f_file) = ls2hash (grep !/\.log$/, $ftp->ls("-alLR"));
## list-long ohne log-dateien, -a muss sein, sonst gibt .pl aerger!
## aber mit .pl als zentrale datei!
#open lsP, "ls -lRL . .pl|" or die "fehler bei ls: $!";
open lsP, "ls -alRL|" or die "fehler bei ls: $!";
my ($l_file) = ls2hash (grep !/\.log$/, <lsP>);
close lsP;

################################################################################
# abgleich der listen
################################################################################
for (keys %$l_file) {
  if (exists $$f_file{$_}) {
    my @f = @{$$f_file{$_}};
    my @l = @{$$l_file{$_}};

    ## wenn auf beiden rechnern, nicht entfernt loeschen!!
    delete $$f_file{$_};

    if ($f[2] eq "d" 		# immer loeschen, wenn ein verzeichnis!
      or ( $f[0] == $l[0]	# dateien loeschen, wenn gleiche groesse
        and ls2time($f[1]) > ls2time($l[1]) - 3600
    )) {
      delete $$l_file{$_};
    }
  }
}

################################################################################
# ausgeben der daten
################################################################################
if ($debug > 1) {
  print "* l_file:\n";
  for (sort keys %$l_file) {
    print $_, "\t--", $$l_file{$_}[1], "---", $$l_file{$_}[0], "--\n";
  }
  print "* f_file:\n";
  for (sort keys %$f_file) {
    print $_, "\t--", $$f_file{$_}[1], "---", $$f_file{$_}[0], "--\n";
  }
  exit;
}

################################################################################
# dateien/verzeichnisse uebertragen
################################################################################
for (sort keys %$l_file) {		# sort => dirs kommen zuerst
  if ($$l_file{$_}[2] eq "d") {
    print STDERR "mkdir $ftp_rootdir/$_:   ";
    $ftp->mkdir("$ftp_rootdir/$_", 1) or die "FEHLER: ", $ftp->message();
  }
  else {
    print STDERR "put $_  $ftp_rootdir/$_:   ";
    $ftp->put($_, "$ftp_rootdir/$_") or die "FEHLER: ", $ftp->message();
  }
  print STDERR " ok\n";
}

################################################################################
# dateien/verzeichnisse loeschen
################################################################################
$ftp->quit(), exit unless keys %$f_file;
{ 
  local $, = "\n";
  print "**folgende Dateien loeschen j/n?", reverse sort keys %$f_file, "\n";
  exit unless <STDIN> =~ /^[jy]/;
}
for (reverse sort keys %$f_file) { # reverse => dirs kommen nach den dateien
  if ($$f_file{$_}[2] eq "d") {
    print STDERR "rmdir $ftp_rootdir/$_:   ";
    $ftp->rmdir("$ftp_rootdir/$_") or warn "FEHLER: ", $ftp->message();
    ## fehlermeldung auch bei erfolg :-(
  }
  else {
    print STDERR "del $ftp_rootdir/$_:   ";
    $ftp->delete("$ftp_rootdir/$_") or die "FEHLER: ", $ftp->message();
  }
  print STDERR " ok\n";
}

$ftp->quit;

################################################################################
exit;
################################################################################
################################################################################

use Time::Local;
################################################################################
# wandelt zeichenkette von ls für die dateizeit in epochensekunden um
################################################################################
sub ls2time {
  die "ls2time braucht genau ein argument! @_\n" unless 1 == @_;
  my %mon = ( 
    "Jan" => 0,
    "Feb" => 1,
    "Mär" => 2,  "Mar" => 2,
    "Apr" => 3,
    "Mai" => 4,  "May" => 4,
    "Jun" => 5,
    "Jul" => 6,
    "Aug" => 7,
    "Sep" => 8,
    "Okt" => 9,  "Oct" => 9,
    "Nov" => 10,
    "Dez" => 11, "Dec" => 11
  );
  my @in = split /\s+/, $_[0];
  my @t = qw(0 0 0 0 0 0);

  # monatsnummer bestimmen
  $t[4] = $mon{$in[0]};
  die "monat nicht definiert $_[0]\n" unless defined $t[4];

  # tag des monats
  $t[3] = $in[1];       

  # die uhrzeit oder das jahr setzten
  if ($in[2] =~ /:/) {                  # uhrzeit
    @t[1..2] = reverse $in[2] =~ /(\d+):(\d+)/;
    $t[5] = (localtime)[5];
    $t[5]-- if timelocal(@t) > time;
  }
  else {                                # jahr
    $t[5] = $in[2] - 1900;
  }

  # rueckgabe als feld oder skalar
  #if (wantarray) { return @t } else {
    return timelocal(@t);
  #}
}

################################################################################
## spaltet die ausgabe von ls in eine datenstruktur
################################################################################
sub ls2hash {
  my %file;
  my $dir = "";
  if ($debug) {
    open debugF, ">>/tmp/sync-ftp.debug" or die "/tmp/sync-ftp.debug: $!" }

  ## alle zeilen einzeln durchgehen
  while (defined (my $l = shift)) {
    print debugF "$l\n" if $debug;
    chomp ($l);
    $dir = "", next if $l =~ /^\s*$/;
    next if $l =~ /^(total|insgesamt)\s+\d+\s*$/; # zeile mit "total" loeschen

    ## verzeichnisnamen extrahieren
    if ($l =~ /:\s*$/) {
      ($dir) = $l =~ /^(.*):\s*$/;
      die "kein verz-name $l" unless defined $dir;
      $dir .= "/";
      next;
    }

    my $typ;
    warn ("keine datei: $l\n"), next unless ($typ) = $l =~ /^([-d])/;

    ## werte im hash speichern
    $l = (split /\s+/, $l, 5)[-1];
    my $n = (my ($groesse, $datum, $fileN) = 
        $l =~ /^(\d+)\s+(.{12})\s+(.*)/);
    if (3 != $n) {
      warn "cannot parse ($n): $l\n";
      next;
    }
    next if $fileN =~ /^\.*$/;		# . und .. schlecht!
    $fileN = "$dir$fileN";
    $fileN =~ s/^\.\///;		# normalisieren: ./ am anfang weg
    #$fileN =~ s/^\///;			# normalisieren:  / am anfang weg
    $file{$fileN} = [ $groesse, $datum, $typ ];
  }
  return \%file;
}
