#!/usr/bin/perl # # $Id: 2d89df355cf34f94da27a75902849073a1e26801 $ # use strict; use vars qw($GunzipError); use Getopt::Std; use Carp; use IO::Uncompress::Gunzip; use Digest::SHA qw(sha256); use MIME::Base64; my %opts; getopts('Ehdrvwb:e:i:l:s:',\%opts); my $box_dir = $opts{b} || '/srv/benno/archive/repo'; my $inbox_dir = $opts{i} || '/srv/benno/inbox'; my $logfile = $opts{l} || '/var/log/benno/reimport.log'; my $error_file = $opts{e}; my $rename = $opts{r}; my $suffix = $opts{s} || '.err'; my $verbose = $opts{v}; my $warnings = $opts{w}; my $envelopes = $opts{E}; help_exit() if $opts{h}; help_exit() if not ($box_dir); #help_exit() if not ($error_file); my @error_files; if ($error_file) { @error_files = ($error_file); } else { @error_files = get_filelist($inbox_dir,$suffix); } if (! $error_files[0]) { print STDERR "No files to reimport found.\n"; exit 1; } open LOG, ">>$logfile" or die "Cannot open lotgfile $logfile: $!\n"; foreach my $file (@error_files) { if ($verbose) { print "#############################################################\n"; print "Errorfile : $file\n"; } my $file_digest = mailfile_digest($file,$envelopes); print "Filedigest : $file_digest\n" if $verbose; my ($p1,$p2,$p3,$filename) = $file_digest =~ /^(..)(..)(..)(.+)$/; $filename .= '00'; # check if repodir or boxdir, boxdir contains the "./journal" directory my @boxlist; if (-d "$box_dir/journal") { @boxlist = ($box_dir); } else { @boxlist = get_dirlist($box_dir); } # check all boxes foreach my $box (@boxlist) { next unless -d "$box/journal"; # Boxdir contanins "journal" directory my $repofile_path = $box."/$p1/$p2/$p3/$filename".'.gz'; $repofile_path =~ s/\/\//\//g; check_boxdir($repofile_path,$file_digest,$rename); } # rename errorfile to .eml rename_importfile($file) if ($rename); } print STDERR "\nFiles in repo and inbox *not* changed! Run with \"-r\" to reimport files.\n" if not $rename; close LOG; ### SUBS ### ### get_dirlist sub get_dirlist { my ($dir) = @_; my @entries; opendir(my $dh, $dir) || die "Cannot open directory $dir: $!\n"; while (my $entry = readdir($dh)) { next if $entry =~ /^\.$/; next if $entry =~ /^\.\.$/; next unless -d "$dir/$entry"; push @entries, "$dir/$entry"; } return @entries; } ### get_filelist sub get_filelist { my ($dir,$suffix) = @_; my @entries; $suffix .= '$'; opendir(my $dh, $dir) || die "Cannot open directory $dir: $!\n"; while (my $entry = readdir($dh)) { my $filepath = $dir.'/'.$entry; next unless $filepath =~ /$suffix/; next unless -f $filepath; $filepath =~ s/\/\//\//g; push @entries, $filepath; } return @entries; } ### check_boxdir sub check_boxdir { my ($repofile_path,$errorfile_digest) = @_; my $repofile_digest; eval { $repofile_digest = bennofile_digest($repofile_path); if ($errorfile_digest eq $repofile_digest) { # check ok rename_repofile($repofile_path) if $rename; } else { die "Error in digest of Benno Repofile ($errorfile_digest <> $repofile_digest)\n"; } }; if ($@) { #catch exception if ($@ =~ s/^WARN\s//) { print STDERR "$@\n" if $warnings; } else { print STDERR "$@\n"; } } } ### compute digest of file sub mailfile_digest { my ($file,$envelopes) = @_; my $buffer; open FILE, $file or die "Cannot open $file: $!\n"; while (my $line = ) { if (!$envelopes) { next if $line =~ /^X-REAL-(MAILFROM|RCPTTO):/; } $buffer .= $line; } close FILE; my $filedigest = _calc_digest($buffer); return $filedigest; } ### check digest of file sub bennofile_digest { my ($repofile) = @_; if (! open REPO, $repofile) { die "WARN Cannot check: $repofile: $!\n"; } print "Archive file: " if $verbose; print "$repofile\n"; my $z = new IO::Uncompress::Gunzip $repofile or die "gunzip failed: $GunzipError\n"; # skip header my $headerdigest; while (my $line = $z->getline) { my $hash_re = qr/^===== Hash:\s(.+)$/; last if (($headerdigest) = $line =~ $hash_re); } $headerdigest =~ s/\r//g; if (!$headerdigest) { print STDERR "No bennobox file: $repofile\n"; return 0; } my $buffer; while (my $line = $z->getline) { $buffer .= $line; } my $digest = _calc_digest($buffer); } ### rename file in repository sub rename_repofile { my ($repofile_path) = @_; (my $repofile_prereimport = $repofile_path) =~ s/([^\/]+\.gz)$/PRE-REIMPORT_$1/; if (-e $repofile_prereimport) { die "File exists : $repofile_prereimport\n"; } else { if (rename $repofile_path, $repofile_prereimport) { print_log("Rename $repofile_path -> $repofile_prereimport"); } else { die "Rename of $repofile_path to $repofile_prereimport failed: $!\n"; } } } ### rename_importfile sub rename_importfile { my ($file) = @_; (my $importfile = $file) =~ s/\.err$/.eml/; eval { link $file, $importfile or die "Rename error: $! $importfile\n"; unlink $file or die "Cannot unlink $file: $!\n"; }; print STDERR "$@\n" if $@; } ### print_log sub print_log { my ($message) = @_; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); # 0-11,+1900,0-6 ,0-366,SZ=(+) # ,So-Sa,0-366,WZ=0,NA=(-) $year += 1900; $mon += 1; $mon = sprintf("%02d",$mon); $mday = sprintf("%02d",$mday); $hour = sprintf("%02d",$hour); $min = sprintf("%02d",$min); $sec = sprintf("%02d",$sec); print LOG "$mday.$mon.$year $hour:$min:$sec $message\n"; } ### calc_digest sub _calc_digest { my ($buffer) = @_; my $ctx = Digest::SHA->new('256'); my $digest = $ctx->add($buffer)->hexdigest; my $filedigest = uc $digest; return $filedigest; } sub help_exit { print "Usage: benno-reimport [-h] [-v] [-b ] [-i | -e ]>\n"; print "Renames the Benno MailFile in repo to .error-renamed-* an rename\n"; print ".err file to .eml file\n"; print "\n"; print " -h this help\n"; print " -b box or repo directory (/srv/benno/archvive/repo)\n"; print " -e .err file in inbox\n"; print " -i inbox directory (/srv/benno/inbox)\n"; print " -E envelope header (X-REAL*) in checksum\n"; print " -r rename file (default show files to rename)\n"; print " -s errorfile suffix (.err)\n"; print " -v verbose\n"; print " -w print warnings\n"; print " -l logfile path (/var/log/benno/reimport.log)\n"; print "\n"; exit 0; }