#! /usr/pkg/bin/perl

use bytes;
use warnings;

# MC extfs for (possibly compressed) Berkeley style mailbox files
# Peter Daum <gator@cs.tu-berlin.de> (Jan 1998, mc-4.1.24)

$zcat="zcat";                 # gunzip to stdout
$bzcat="bzip2 -dc";           # bunzip2 to stdout
$lzipcat="lzip -dc";          # unlzip to stdout
$lz4cat="lz4 -dc";            # unlz4 to stdout
$lzcat="lzma -dc";            # unlzma to stdout
$lzocat="lzop -dc";           # unlzo to stdout
$xzcat="xz -dc";              # unxz to stdout
$zstdcat="zstd -dc";          # unzstd to stdout
$file="file";                 # "file" command
$TZ='GMT';                    # default timezone (for Date module)

if (eval "require Date::Parse") {
    import Date::Parse;
    $parse_date=
	sub {
	    local $ftime = str2time($_[0],$TZ);
	    $_ = localtime($ftime);
	    /^(...) (...) ([ \d]\d) (\d\d:\d\d):\d\d (\d\d\d\d)$/;
	    if ($ftime + 6 * 30 * 24 * 60 * 60 < $now ||
		$ftime + 60 * 60 > $now) {
		return "$2 $3 $5";
	    } else {
		return "$2 $3 $4";
	    }
	}
} elsif (eval "require Date::Manip") {
    import Date::Manip;
    $parse_date=
	sub {
	    return UnixDate($_[0], "%l"); # "ls -l" format
	}
} else {			# use "light" version
    $parse_date= sub {
	local $mstring='GeeJanFebMarAprMayJunJulAugSepOctNovDec';
	# assumes something like: Mon, 5 Jan 1998 16:08:19 +0200 (GMT+0200)
	# if you have mails with another date format, add it here
	if (/(\d\d?) ([A-Z][a-z][a-z]) (\d\d\d\d) (\d\d?):(\d\d)/) {
	    $day = $1;
	    $month = $2;
	    $mon = index($mstring,$month) / 3;
	    $year = $3;
	    $hour = $4;
	    $min = $5;
	    # pass time not year for files younger than roughly 6 months
	    # but not for files with dates more than 1-2 hours in the future
	    if ($year * 12 + $mon > $thisyear * 12 + $thismon - 7 &&
		$year * 12 + $mon <= $thisyear * 12 + $thismon &&
		! (($year * 12 + $mon) * 31 + $day ==
		($thisyear * 12 + $thismon) * 31 + $thisday &&
		$hour > $thishour + 2)) {
		return "$month $day $hour:$min";
	    } else {
		return "$month $day $year";
	    }
	}
	# Y2K bug.
	# Date: Mon, 27 Mar 100 16:30:47 +0000 (GMT)
	if (/(\d\d?) ([A-Z][a-z][a-z]) (1?\d\d) (\d\d?):(\d\d)/) {
	    $day = $1;
	    $month = $2;
	    $mon = index($mstring,$month) / 3;
	    $year = 1900 + $3;
	    $hour = $4;
	    $min = $5;
	    if ($year < 1970) {
		$year += 100;
	    }
	    if ($year * 12 + $mon > $thisyear * 12 + $thismon - 7 &&
		$year * 12 + $mon <= $thisyear * 12 + $thismon &&
		! (($year * 12 + $mon) * 31 + $day ==
		($thisyear * 12 + $thismon) * 31 + $thisday &&
		$hour > $thishour + 2)) {
		return "$month $day $hour:$min";
	    } else {
		return "$month $day $year";
	    }
	}
	# AOLMail(SM).
	# Date: Sat Jul 01 10:06:06 2000
	if (/([A-Z][a-z][a-z]) (\d\d?) (\d\d?):(\d\d)(:\d\d)? (\d\d\d\d)/) {
	    $month = $1;
	    $mon = index($mstring,$month) / 3;
	    $day = $2;
	    $hour = $3;
	    $min = $4;
	    $year = $6;
	    if ($year * 12 + $mon > $thisyear * 12 + $thismon - 7 &&
		$year * 12 + $mon <= $thisyear * 12 + $thismon &&
		! (($year * 12 + $mon) * 31 + $day ==
		($thisyear * 12 + $thismon) * 31 + $thisday &&
		$hour > $thishour + 2)) {
		return "$month $day $hour:$min";
	    } else {
		return "$month $day $year";
	    }
	}
	# Fallback
	return $fallback;
    }
}

sub process_header {
    while (<IN>) {
	$size+=length;
	s/\r$//;
	last if /^$/;
	die "unexpected EOF\n" if eof;
	if (/^date:\s(.*)$/i) {
	    $date=&$parse_date($1);
	} elsif (/^subject:\s(.*)$/i) {
	    $subj=lc($1);
	    $subj=~ s/^(re:\s?)+//gi;  # no leading Re:
	    $subj=~ tr/a-zA-Z0-9//cd; # strip all "special" characters
	} elsif (/^from:\s.*?(\w+)\@/i) {
	    $from=$1;
	} elsif (/^to:\s.*?(\w+)\@/i) {
	    $to=lc($1);
	}
    }
}

sub print_dir_line {
    $from=$to if ($from eq $user); # otherwise, it would look pretty boring
    $date=localtime(time) if (!defined $date);
    printf "-r-------- 1 $< $< %d %s %3.3d_%.25s\n",
    $size, $date, $msg_nr, "${from}_${subj}";

}

sub mailfs_list {
    my $blank = 1;
    $user=$ENV{USER}||getlogin||getpwuid($<) || "nobody";

    while(<IN>) {
	s/\r$//;
	if($blank && /^from\s+\w+(\.\w+)*@/i) { # Start of header
	    print_dir_line unless (!$msg_nr);
	    $size=length;
	    $msg_nr++;
	    ($from,$to,$subj,$date)=("none","none","none", "01-01-80");
	    process_header;
	    $line=$blank=0;
	} else {
	    $size+=length;
	    $line++;
	    $blank= /^$/;
	}
    }
    print_dir_line unless (!$msg_nr);
    exit 0;
}

sub mailfs_copyout {
    my($source,$dest)=@_;
    exit 1 unless (open STDOUT, ">$dest");
    ($nr)= ($source =~ /^(\d+)/); # extract message number from "filename"

    my $blank = 1;
    while(<IN>) {
	s/\r$//;
	if($blank && /^from\s+\w+(\.\w+)*@/i) {
	    $msg_nr++;
	    exit(0) if ($msg_nr > $nr);
	    $blank= 0;
	} else {
	    $blank= /^$/;
	}
	print if ($msg_nr == $nr);
    }
}

# main {
exit 1 unless ($#ARGV >= 1);
$msg_nr=0;
$cmd=shift;
$mbox_name=shift;
my $mbox_qname = quotemeta ($mbox_name);
$_=`$file $mbox_qname`;

if (/gzip/) {
    exit 1 unless (open IN, "$zcat $mbox_qname|");
} elsif (/bzip/) {
    exit 1 unless (open IN, "$bzcat $mbox_qname|");
} elsif (/lzip/) {
    exit 1 unless (open IN, "$lzipcat $mbox_qname|");
} elsif (/lz4/) {
    exit 1 unless (open IN, "$lz4cat $mbox_qname|");
} elsif (/lzma/) {
    exit 1 unless (open IN, "$lzcat $mbox_qname|");
} elsif (/lzo/) {
    exit 1 unless (open IN, "$lzocat $mbox_qname|");
} elsif (/xz/) {
    exit 1 unless (open IN, "$xzcat $mbox_qname|");
} elsif (/zst/) {
    exit 1 unless (open IN, "$zstdcat $mbox_qname|");
} else {
    exit 1 unless (open IN, "<$mbox_name");
}

umask 077;

if($cmd eq "list") {
    $now = time;
    $_ = localtime($now);
    /^... (... [ \d]\d \d\d:\d\d):\d\d \d\d\d\d$/;
    $fallback = $1;
    $nowstring=`date "+%Y %m %d %H"`;
    ($thisyear, $thismon, $thisday, $thishour) = split(/ /, $nowstring);
    &mailfs_list;
    exit 0;
}
elsif($cmd eq "copyout") { &mailfs_copyout(@ARGV); exit 0; }

exit 1;
