#!/usr/bin/perl -w 

# cleanup for F*EX service
#
# run this program via cron-job once at night!
#
# Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
#
# Copyright: GNU General Public License

use Digest::MD5	qw(md5_hex);

# do not run as CGI!
exit if $ENV{SCRIPT_NAME};

# add fex lib
$FEXLIB =
  $0 =~ m:(/.+)/.+/: ? "$1/lib":
  $0 =~ m:(.*/):     ? "$1/../lib":
                       "../lib";
die "$0: no $FEXLIB\n" unless -d $FEXLIB;

# program name
$0 =~ s:.*/::;

$| = 1;

# use fex.ph for site configuration!
our ($logdir,$debug,$autodelete);
$keep_default = 5;

# load common code, local config : $HOME/lib/fex.ph
require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";

$today = time;
$isodate = isodate($today);
$ds = 24*60*60; # day seconds

chdir $spooldir or die "$0: $spooldir - $!\n";
open L,">>$logdir/cleanup.log";

# clean up regular spool
opendir SPOOL,'.' or die "$0: $spooldir - $!\n";
while ($to = readdir SPOOL) {
  next if $to eq '.' or $to eq '..';
  if (-d $to and $to !~ /^\./) {
    opendir TO,$to or die "$0: $spooldir/$to - $!\n";
    while ($from = readdir TO) {
      next if $from eq '.' or $from eq '..';
      if (-d "$to/$from" and $from !~ /^\./) {
        opendir FROM,"$to/$from" or die "$0: $spooldir/$to/$from - $!\n";
        while ($file = readdir FROM) {
          next if $file eq '.' or $file eq '..';
          if (-d "$to/$from/$file" and $file !~ /^\./) {
            cleanup("$to/$from/$file");
            rmdir "$to/$from/$file";
          }
        }
        closedir FROM;
        rmdir "$to/$from";
      }
    }
    closedir TO;
    rmdir $to;
  }
}
closedir SPOOL;

# clean up download key lookup directory
if (chdir "$spooldir/.dkeys" and opendir D,'.') {
  while ($file = readdir D) {
    next if $file eq '.' or $file eq '..';
    if ($link = readlink $file and not -e $link) {
      if (unlink $file and $debug) {
        print ".dkeys/$file deleted\n" if -t;
      }
    }
  }
  closedir D;
}

# clean up upload key lookup directory
if (chdir "$spooldir/.ukeys" and opendir D,'.') {
  while ($file = readdir D) {
    next if $file eq '.' or $file eq '..';
    if (($link = readlink $file and not -e "$link/upload"
         or -f $file and time>mtime($file)+3600*24)
        and unlink $file and $debug) {
      print ".ukeys/$file deleted\n" if -t;
    }
  }
  closedir D;
}

# clean up authorization key lookup directory
if (chdir "$spooldir/.akeys" and opendir D,'.') {
  while ($file = readdir D) {
    next if $file eq '.' or $file eq '..';
    $delete = 1;
    # keep akey if ID is still valid
    if ($link = readlink $file and open F,"$link/@" and $id = <F>) {
      chomp $id;
      close F;
      $akey = "$link:$id";
      $akey =~ s:.*/::;
      $akey = md5_hex($akey);
      $delete = 0 if $file =~ /$akey/;
    }
    if ($delete and unlink $file) {
      print ".akeys/$file deleted\n" if -t;
    }
  }
  closedir D;
}

# clean up error directory
if (chdir "$spooldir/.error" and opendir D,'.') {
  while ($file = readdir D) {
    next if $file eq '.' or $file eq '..';
    if (-f $file) {
      $mtime = mtime($file);
      if ($mtime and $today > 5*$keep_default*$ds+$mtime) {
        if (unlink $file and $debug) {
          prin L "$isodate .error/$file deleted\n" if -t;
        }
      }
    }
  }
  closedir D;
}

# clean up debug directory
if (chdir "$logdir/.debug" and opendir D,'.') {
  while ($file = readdir D) {
    next if $file eq '.' or $file eq '..';
    if (-f $file) {
      $mtime = mtime($file);
      if ($mtime and $today > $keep_default*$ds+$mtime) {
        if (unlink $file and $debug) {
          print ".debug/$file deleted\n" if -t;
        }
      }
    }
  }
  closedir D;
}

# clean up subuser keys directory
if (chdir "$spooldir/.skeys" and opendir D,'.') {
  while ($file = readdir D) {
    next if $file eq '.' or $file eq '..';
    if (-f $file and open F,$file) {
      $delete = 1;
      $from = $to = $id = '';
      while (<F>) {
        if (/^(\w+)=(.+)/) {
          $from = $2 if $1 eq 'from';
          $to   = $2 if $1 eq 'to';
          $id   = $2 if $1 eq 'id';
        }
      }
      close F;
      if ($from and $to and $id and open F,"$spooldir/$to/@") {
        $_ = <F>;
        while (<F>) {
          if (/^\Q$from:$id/) {
            $delete = 0;
            last;
          }
        }
        close F;
      }
      unlink $file if $delete;
    }
  }
  closedir D;
}

# clean up self registration directory
if (chdir "$spooldir/.reg" and opendir D,'.') {
  while ($file = readdir D) {
    next if $file eq '.' or $file eq '..';
    if (-f $file) {
      $mtime = mtime($file);
      if ($mtime and $today > $keep_default*$ds+$mtime) {
        if (unlink $file and $debug) {
          print ".reg/$file deleted\n" if -t;
        }
      }
    }
  }
  closedir D;
}

close L;
exit;


# file clean up
sub cleanup {
  my $file = shift;
  my ($data,$download,$notify,$autodelete,$mtime,$warn,$dir,$filename,$dkey);
  my $keep = $keep_default;
  local (*D,*K,*F);
  local $_;

  if (open K,"$file/keep") {
    $_ = <K>||'';
    close K;
    $keep = $1 if /(\d+)/;
  }

  $data       = "$file/data";
  $download   = "$file/download";
  $notify     = "$file/notify";
  $autodelete = "$file/autodelete";
  
  if (-d $file and not -f $data) {
    if (-f "$file/upload") {
      $mtime = mtime("$file/upload");
      if ($mtime and $today > $ds+$mtime or not $mtime) {
        debuglog("rmrf $file (today=$today mtime_upload=$mtime)");
        if (rmrf($file)) {
          printlog("$file deleted\n");
        } else {
          printlog("$file DEL FAILED\n");
        }
      }
    } elsif (-s "$file/error") {
      $mtime = mtime("$file/error");
      if ($mtime and $today > 3*$keep*$ds+$mtime) {
        debuglog("rmrf $file (today=$today mtime_error=$mtime keep=$keep)");
        if (rmrf($file)) {
          printlog("$file deleted\n");
        } else {
          printlog("$file DEL FAILED\n");
        }
      }
    }
  } elsif (-f $download and -f $data) {
    my $del = 'yes';
    if (open $autodelete,$autodelete) {
      $_ = <$autodelete>||'';
      close $autodelete;
      $del = '' if /^NO/i;
    }
    if ($del) {
      if (unlink $data) { 
        print L "$isodate $data deleted\n";
        debuglog("unlink $data (delayed autodelete)");
        if (open F,">$file/error") {
          $filename = $file;
          $filename =~ s:.*/::;
          printf F "%s has been autodeleted after download at %s\n",
            filename($file),isodate(mtime($download));
          close F;
        }
      } else {
        printlog("$data DEL FAILED\n");
      }
    }
  } 
  if (-f $data) {
    $warn = $keep-2;
    $mtime = mtime($data);
    if (defined $mtime or $autodelete) {
      if ($today > $mtime+$keep*$ds) {
        if (unlink $data) { 
          print L "$isodate $data deleted\n";
          debuglog("unlink $data (today=$today mtime=$mtime keep=$keep)");
          if (open F,">$file/error") {
	    $filename = $file;
            $filename =~ s:.*/::;
            print F "$filename is expired";
            close F;
          }
        } else {
          printlog("$isodate $data DEL FAILED\n");
        }
      } elsif ($file !~ /STDFEX$/ and
               $mtime+$warn*$ds < $today and 
               $dkey = readlink("$file/dkey") and
               not -f $download and not -f $notify) {
        $filename = filename($file);
        notify('remind',$dkey,$filename,$keep,int(($mtime-$today)/$ds)+$keep,
               '',$autodelete);
        open F,'>',$notify;
        close F;
      }
    }
  }
}

sub mtime {
  return (stat shift)[9];
}

sub printlog {
  print L "$isodate @_";
  print   "@_" if -t;
}
