#!/usr/local/bin/perl

# Original from J Greely <jgreely@cis.ohio-state.edu>, 9/30/92
#
# Heavily modified by Brent Chapman <Brent@GreatCircle.COM>

# $Source: /sources/cvsrepos/majordomo/digest/digest,v $
# $Revision: 1.7 $
# $Date: 1994/05/09 00:00:05 $
# $Author: rouilj $
# $State: Exp $
# 
# $Header: /sources/cvsrepos/majordomo/digest/digest,v 1.7 1994/05/09 00:00:05 rouilj Exp $
# 
# $Locker:  $
# 
# $Log: digest,v $
# Revision 1.7  1994/05/09  00:00:05  rouilj
# Added code to smash list name tolowercase.
#
# Revision 1.6  1994/05/08  01:16:44  rouilj
# Fixed bug in file limiting code.
#
# Revision 1.5  1994/05/07  22:01:15  rouilj
# Added code to only include files with the name [0-9]* in a digest.
# It warns if there are files that aren't supposed to be in the
# incomming directory.
#
# Revision 1.4  1994/04/20  23:19:42  rouilj
# Fixed typo that affected the name of the archive dir.
#
# Revision 1.3  1994/03/06  22:47:06  rouilj
# set $main'main_program to mj_digest. Fixed bug that caused precedence
# header to be improperly set up. Fixed fronter and footer values
# obtained from config file.
#
# Revision 1.2  1994/02/21  18:27:38  rouilj
# Made digest part of the config file code enhancements.
#
# Revision 1.8  1992/10/16  21:33:27  brent
# Made RFC1153 compliant.  -Brent
#
# Revision 1.7  1992/10/02  17:03:33  brent
# Cleaned up RCS headers.  -Brent
#
# Revision 1.6  1992/10/02  17:00:13  brent
# Added author credits, RCS headers.  -Brent
#
# Revision 1.5	1992/10/02  16:46:08  brent
# Added blank line after encapsulation boundary.
# Added "Precedence: bulk" header
# 
# Revision 1.4	1992/10/02  16:44:19  brent
# Added "chdir($HOME)" to make everything happy.  -Brent
# 
# Revision 1.3	1992/10/01  23:12:55  brent
# Extensive modifications for Firewalls-Digest.  -Brent
# 
# Revision 1.2	1992/10/01  21:13:21  brent
# Revised .cf file handling; made keys match header names.
# Added "-v" and "-n" switches
# 
# Revision 1.1	1992/10/01  20:42:17  brent
# Initial revision
# 

# Before doing anything else tell the world I am majordomo
# The mj_ prefix is reserved for tools that are part of majordomo proper.
$main'program_name = 'mj_digest';

&init;
&readconfig;

require "shlock.pl";

&set_lock;

if (defined($opt_r)) {
    &receive_message;
} elsif (defined($opt_m)) {
    &make_digest;
} else {
    &abort("Usage: digest {-r|-m} [-c config|(-C -l list)]\nStopped");
}

&free_lock;

exit(0);

sub receive_message {
    $sum = 0;
    $i = 0;
    do {
	$i++;
	$file = sprintf("%s/%03d", $V{'INCOMING'}, $i);
	$sum += (-s $file);
    } until (! -e $file);
    print STDERR "Receiving $i\n";
    open(MSG, ">$file") || &abort("open(MSG, \">$file\"): $!");
    while (<STDIN>) {
	print MSG $_;
    }
    close(MSG);
    $sum += (-s $file);
    if ($sum > $V{'DIGEST_SIZE'}) {
	&make_digest;
    }
    return(1);
}


sub make_digest {
    @files=<$V{'INCOMING'}/*>;
    if ($#files < $[) {
	&abort("No messages.\nStopped ");
    }
    open(TEMP,">$TEMP") || &abort("$TEMP: $!\n");
    print STDERR "producing $V{'NAME'} V$VOLUME #$NUMBER\n";
    foreach $message (@files) {
	print STDERR "non digest input file $message", next
	    if $message !~ m#/\d+$#;
	    open(message) || &abort("$message: $!\n");
	    #side note: "open message or die"?
	    print STDERR "\tprocessing $message\n";

	    $/ = '';
	    $head = <message>;
	    $head =~ s/\n\s+/ /g;
	    $body = "";
	    ($subj) = ($head =~ /^subject:\s+(.*)/i);
	    $subj = "[none]" unless $subj;
	    ($from) = ($head =~ /^from:\s+(.*)/i);
	    ($date) = ($head =~ /^date:\s+(.*)/i);

	    $/ = "\n";
	    while (<message>) {
		    s/^-/- -/; #escape encapsulation boundaries in message
		    $body .= $_;
	    }
	    close(message);
	    $body =~ s/\n+$/\n/;

	    push(@subj,$subj);
	    print TEMP <<EOF;
From: $from
Date: $date
Subject: $subj

$body
$EB

EOF
    }
    close(TEMP);

    $DIGEST = sprintf("%s/v%02d.n%03d", $V{'ARCHIVE'}, $VOLUME, $NUMBER);
    open(DIGEST, ">$DIGEST") || &abort("open(DIGEST, \">$DIGEST\"): $!");

    print DIGEST <<EOF;
From:      $V{'FROM'}
To:        $V{'TO'}
Subject:   $V{'NAME'} V$VOLUME #$NUMBER
Reply-To:  $V{'REPLY-TO'}
Errors-To: $V{'ERRORS-TO'}
Precedence: $Precedence
$HEADERS

EOF

    $PDATE = &getdate();
    printf DIGEST ("%-20s",$V{'NAME'});
    $center = " " x int(18 - length($PDATE) / 2);
    print  DIGEST $center,$PDATE,$center;
    printf DIGEST ("Volume %02d : Number %03d\n\n",$VOLUME,$NUMBER);

    foreach (split(/\n/,$HEADER)) {
	    if (/_SUBJECTS_/) {
		    $pre = $`;
		    foreach $subj (@subj) {
			    print DIGEST $pre,$subj,"\n";
		    }
	    }else{
		    print DIGEST "$_\n";
	    }
    }
    print DIGEST "\n";
    print DIGEST "-" x 70,"\n\n";

    open(TEMP);
    print DIGEST <TEMP>;
    close(TEMP);
    unlink($TEMP);

    $end = sprintf("End of %s V%d #%d", $V{'NAME'}, $VOLUME, $NUMBER);
    print DIGEST $end, "\n";
    print DIGEST "*" x length($end), "\n";
    print DIGEST "\n";
    print DIGEST $TRAILER;

    close(DIGEST);

    system("/usr/lib/sendmail -f$V{'ERRORS-TO'} $V{'REALLY-TO'} < $DIGEST");

    if ( ! defined($opt_C) ) {
	open(NUM_FILE, ">$V{'NUM_FILE'}") ||
		&abort("open(NUM_FILE, \">$NUM_FILE\"): $!");
    	printf NUM_FILE "%d\n", $NUMBER + 1;
	close(NUM_FILE);
    } else { # hurrah we are using the majordomo config file
	$config_opts{$opt_l,"digest_issue"} += 1;
	&config'writeconfig($listdir, $opt_l);
    }

    unlink(@files);

    return 0;
}

sub init {
	$* = 1;
	$HOME = $ENV{"HOME"} || (getpwuid($>))[7];
	chdir($HOME);
	&getopt("rmc:Cl:") ||
	    &abort("Usage: digest {-r|-m} [-c config|(-C -l list)]\nStopped");
	$config = $opt_c || "$HOME/.digestrc";
	$TEMP = "/tmp/digest.$$";
	$SIG{'INT'} = 'cleanup';
	@MONTHS = ("January","February","March","April","May","June","July",
	           "August","September","October","November","December");
	@DAYS = ("Sunday","Monday","Tuesday","Wednesday","Thursday",
	         "Friday","Saturday");
	$TEMP = "/tmp/digest.$$";
	$EB = "-" x 30;
}

sub readconfig {
	if (defined($opt_C)) {
	   if (!defined($opt_l)) {
		&abort("-C used without -l");
	    } else {
		# Read and execute the .cf file
		$cf = $ENV{"MAJORDOMO_CF"} || 
			"/etc/majordomo.cf";
		if (! -r $cf) {
		    &abort("$cf not readable; stopped");
		}
		eval(`cat $cf`) || die "eval of majordomo.cf failed $@";

		chdir($homedir);

		$opt_l =~ tr/A-Z/a-z/;

	   	require "config_parse.pl";
		# get the digest config file
		&get_config($listdir, $opt_l);

		# map config opts to internal variables and $V array
		$HEADER = $config_opts{$opt_l,"message_fronter"};
		$HEADER =~ s/\001/\n/g;
		$TRAILER = $config_opts{$opt_l,"message_footer"};
		$TRAILER =~ s/\001/\n/g;
		$VOLUME = $config_opts{$opt_l,"digest_volume"};
		$NUMBER = $config_opts{$opt_l,"digest_issue"};
		$Precedence = $config_opts{$opt_l,"precedence"};
		$Precedence = "bulk" if ($Precedence eq "");
		$V{'ARCHIVE'} = "$filedir/$opt_l$filedir_suffix";
		$V{'DIGEST_SIZE'} = $config_opts{$opt_l, "maxlength"};
		$V{'ERRORS-TO'} = $config_opts{$opt_l,"sender"};
		$V{'FROM'} = $config_opts{$opt_l, "sender"};
		$V{'INCOMING'} = "$digest_work_dir/$opt_l";
		$V{'NAME'} = $config_opts{$opt_l,"digest_name"};
		$V{'REALLY-TO'} = $ARGV[0];
		$V{'REPLY-TO'} = $config_opts{$opt_l,"reply_to"};
		$V{'TO'} = "$opt_l@$whereami";

		# make the headers keyword work
		if ( $config_opts{$opt_l,"message_headers"} ne '' ) {
			$from = $V{'FROM'};
			$HEADERS = &config'substitute_values (
			$config_opts{$opt_l,"message_headers"}, $opt_l);
			$HEADERS =~ s/\001/\n/g;
		}
            } # list is defined
	 } else { # not using -C 
	   open(config) || &abort("$config: $!\n");
	   while (<config>) {
		next if /^\s*$|^\s*#/;
		chop;
		($key,$value) = split(/\s*=\s*/,$_,2);
		$V{$key} = $value;
	   }
	   close(config);

	   open(header,$V{'HEADER'}) || &abort("$V{'HEADER'}: $!\n");
	   $HEADER = join("",<header>);
	   close(header);

	   open(trailer,$V{'TRAILER'}) || &abort("$V{'TRAILER'}: $!\n");
	   $TRAILER = join("",<trailer>);
	   close(trailer);

	   open(VOL_FILE,$V{'VOL_FILE'}) || &abort("$V{'VOL_FILE'}: $!\n");
	   $VOLUME = join("",<VOL_FILE>);
	   chop($VOLUME);
	   close(VOL_FILE);

	   open(NUM_FILE,$V{'NUM_FILE'}) || &abort("$V{'NUM_FILE'}: $!\n");
	   $NUMBER = join("",<NUM_FILE>);
	   chop($NUMBER);
	   close(NUM_FILE);

	   if (defined($V{'HOME'})) {
	       unshift(@INC, $V{'HOME'});
	   } 

	} # end not using -C
}

#my favorite of the existing getopt routines; twisted
#
sub getopt {
	local($_,%opt,$rest) = (split(/([^:])/,$_[0]),'');
	while ($_ = $ARGV[0], /^-(.)/ && shift(@ARGV)) {
		$rest = $';
		last if $1 eq '-';
		if (!defined $opt{$1}) {
			warn "Unrecognized switch \"-$1\".\n";
			return 0;
		}elsif ($opt{$1}) {
			$rest = shift(@ARGV) if $rest eq '';
			eval "\$opt_$1 = \$rest";
		}else{
			eval "\$opt_$1 = 1";
			$rest =~ /^(.)/;
			redo if $rest ne '';
		}
	}
	return 1;
}

sub cleanup {
	unlink($TEMP);
	exit(1);
}

sub getdate {
  local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  return($DAYS[$wday] . ", $mday " . $MONTHS[$mon] . " 19$year");
}

sub set_lock {
    for ($tries = 0 ; $tries < 600 ; $tries++) {
	if (&shlock($V{'INCOMING'} . "/.LOCK")) {
	    # got the lock
	    $lock_set = 1;
	    return(1);
	} else {
	    # didn't get the lock; wait 1 second and try again.
	    sleep(1);
	}
    }
    # if we got this far, we ran out of tries on the lock.
    &abort("unable to create lock \"$V{'INCOMING'}/.LOCK\"; giving up");
    return undef;
}

sub free_lock {
    if (defined($lock_set)) {
	undef($lock_set);
	return unlink($V{'INCOMING'} . "/.LOCK");
    } else {
	return undef;
    }
}

sub abort {
    local($msg) = shift;

    &free_lock;
    die($msg);
}
