#!/usr/local/bin/perl -s
#
# This is a partially finished command line program to automate
# creating eternity documents.  I decided it would be better to 
# have a cgi interface, so if you want to do this, put the code
# in eternity.  Then the submit script can become a wrapper which
# fools the eternity script into thinking it's being called as a cgi
# script with appropriate "forms" filled in.

my $eternity_dir = "/home/www/eternity";
my $config_file = "eternity.conf";

######################################################################
# defaults for the ones we need here

my $pgp_exe = "/usr/local/bin/pgp";
my $sha1_exe = "/usr/local/bin/sha1";
my $spool_dir = "news";
my $default_passphrase = "eternity";
my $newsgroups_file = "newsgroups";
my $tmp_dir = "/tmp";
my $tmp_file = "et";
my $ntmp = 0;			# tmpnam count

######################################################################
# misc vars

my $enc, $doc, $n;
my $url, $url_sha1, $url_password;

######################################################################
# read the config file, if there is one
chdir( $eternity_dir );

if ( -e $config_file )
{
    parse_conf_file( $config_file );
}

$n = 1;
print STDERR <<EOT;
These questions are in reverse, inner most packet first

$n. Which file do you want to submit?

EOT

while ( 1 )
{
    $doc = question( "Name of document to submit:", "" );
    if ( open( DOC, $doc ) )
    {
	last;
    }
    print STDERR "Can't open file $doc\n";
}
close( DOC );

$n++;
print STDERR <<EOT;
$n. Where shall I save the Eternity post?

EOT
while ( 1 )
{
    $out = question( "Name of output document:", "" );
    if ( open( OUT, ">$out" ) )
    {
	last;
    }
    print STDERR "Can't create file $out\n";
}
close( DOC );

$n++;
print STDERR <<EOT;
$n. What url do you want to offer the document as?  Should be of the 
   form "http://eternity/document/path".

EOT

$url = question( "Enter URL:", "" );
$url_sha1 = sha1( $url );
$url_password = sha1( "1$url" );

$n++;
print STDERR <<EOT;
$n. The actual document the Eternity server serves can be encrypted so that
   the user is required to give a passphrase to decrypt.

EOT

$enc = question( "Do you want the document to be encrypted?", "n", "y" );

if ( $enc eq "y" )
{
    print STDERR "Calling PGP -c, type password when prompted...\n";
    $doc = encrypt( $doc );
}

$n++;
print STDERR <<EOT;
$n. Do you want to sign the document?  Signing gives the advantage
   that no one else can replace your document.

EOT

$sign = question( "Sign document?", "y", "n" );
if ( $sign eq "y" )
{
    $n++;
    print STDERR <<EOT;
$n. Which key do you want to sign the document with?

EOT
    
    while ( 1 )
    {
	$key = question( "PGP key id to sign document:", "" );
	$keyf = extract_key( $key );
	if ( $keyf ne "" ) { last; }
    }

    print STDERR "Calling PGP -s, type passphrase for key when promped...\n";
    $doc = envelope( $doc, $key );
}
else
{
    $doc = envelope( $doc );
}

$n++;
print STDERR <<EOT;
$n. Which options do you want?  Only choice is exdirectory at the moment.
   exdirectory means your document will not appear in the eternity directory,
   all though anyone who knows the URL will be able to fetch it.

EOT

$options = question( "Options: ", "none", "exdirectory" );
if ( $options = "none" ) { $options = ""; }

if ( $options ne "exdirectory" )
{
    $n++;
    print STDERR <<EOT;
$n. When the server adds your url to the directory listing, it allows
   you one line (64 chars) to describe the URL.

EOT
    $desc = question( "Description: ", "" );
}

$n++;
print STDERR <<EOT;
$n. Which cache policy do you want?  on, off, or encrypt.  Off means
   your document will never be cached on the eternity server, on means
   the document will be cached if the server has cacheing turned on.
   encrypted means your document will be cached in encrypted form if
   the server supports this, or not cached at all if it doesn't.

EOT

while ( 1 )
{
    $cache = question( "Cache: ", "none", "on", "off", "encrypt" );
    if ( $cache eq "none" )
    { 
	$cache = ""; 
	last;
    }
    elsif ( $cache eq "on" || $cache eq "off" || $cache eq "encrypt" )
    {
	last;
    }
    print STDERR "invalid cache option\n";
}

$tmp = tmpnam();
open( OUT, ">$tmp" );
print OUT "Subject: Eternity $url_sha1\n";

if ( $f )
{
    $group = ( $g eq "" ) ? "alt.anonymous.messages" : $g;
    print OUT <<EOO;
Newsgroups: $group
Message-Id: <test>
From: Nobody <nobody\@test.net>
EOO
}

if ( $cache ) { print OUT "Cache: $cache\n"; }
if ( $option ) { print OUT "Options: $options\n"; }
if ( $desc ) { print OUT "Description: $desc\n"; }
print OUT "\n";

if ( $keyf ne "" )
{
    spool_out( OUT, $keyf );
}

spool_out( OUT, $doc );
close( OUT );

$n++;
print STDERR <<EOT;
$n. The submitted Eternity document will be encrypted with either password
   "eternity" or password sha1( 1$url ).  With password "eternity" this
   is just obfuscation, as the password is public knowledge.  With
   sha1( 1$url ) you need to know the url to decrypt.

EOT

$eternity = question( "Do you want \"eternity\" (y) or sha1 of url?", "y", "n" );

$password = $eternity ? "eternity" : $url_password;

$doc = encrypt( $tmp, $password );

`cp $doc $out 2> /dev/null`;

et_exit( 0 );

######################################################################
# crude query with default string

sub question
{
    my( $quest, $default, @choices ) = @_;
    my( $line, $choice_str );

    if ( $default ne "" )
    {
	$choice_str = join( '/', "\U$default", @choices );
	unshift( @choices, $default );
    }
    else
    {
	$choice_str = join( '/', @choices );
    }
    if ( $choice_str ne "" ) { $choice_str = "[$choice_str]"; }
    print STDERR "$quest $choice_str";

  TRY:
    while ( <STDIN> )
    {
	chomp( $line = $_ );
	if ( $line eq "" && $default ne "" )
	{
	    $ans = $default; 
	    last TRY;
	}
	if ( @choices == 0 )
	{
	    $ans = $line;
	    last TRY;
	}
	foreach $ch ( @choices )
	{
	    if ( $ch eq $line )
	    {
		$ans = $line;
		last TRY;
	    }
	}
	print STDERR "$quest $choice_str";
    }
    return $ans;
}

######################################################################
# parse config file

sub parse_conf_file
{
    my( $file ) = @_;
    my( $var, $val );
    if ( ! open( CFG, $file ) )
    {
	die( "can't open eternity configuration file $file\n" );
    }

    while ( <CFG> )
    {
	next if /^\s*#/;
	chop;
	s/\s*#.*$//;
	next if $_ eq "";
	($var,$val) = /^\s*(\w+)\s*=\s*(.+)\s*$/;
	$var = "\L$var";
	if ( defined( $config_var{ $var } ) ) # only allowed variables
	{
	    $val =~ s/^off$/0/;	    $val =~ s/^no$/0/;
	    $val =~ s/^on$/1/; 	    $val =~ s/^yes$/1/;
	    $val =~ s/^encrypt$/2/;
	    if ( $val =~ m/^\d$/ )
	    {
		$$var = $val;
	    }
	    else
	    {
		$val =~ s/secs$//;
		$val =~ s/mins$// && ($val *= 60);
		$val =~ s/hours$// && ($val *= 3600);
		$val =~ s/Kb$//;
		$val =~ s/kb$//;
		$val =~ s/Mb$// && ($val *= 1024);
		$val =~ s/mb$// && ($val *= 1024);
		$val =~ s/`//g;	           # no shell escapes!
                $val =~ s/^"//;
                $val =~ s/"$//;
                $$var = $val;
            }
        }
    }
    close( CFG );
}

######################################################################

sub wipe
{
    my( @files ) = @_;
    my( $file, $res );

    foreach $file ( @files )
    {
	$res = `$pgp_exe $pgp_flags -w $file`;
	unlink( $file );		# just in case!
    }
}

sub et_exit
{
    my( $code ) = @_;
    my( $tmpnam );

    wipe_tmpfiles();		# burn and delete all temp files

    exit( $code );
}

sub tmpnam
{
    my( $tmpnam );

    $ntmp++;
    $tmpnam = "$tmp_dir/${tmp_file}$ntmp.$$";
    LOG( $fil, "creating temporary $tmpnam" );
    return $tmpnam;
}

sub wipe_tmpfiles
{
    my( $i, $tmpnam );

    for ( $i = 1; $i <= $ntmp; $i++ )
    {
	$tmpnam = "$tmp_dir/${tmp_file}$i.$$";
	LOG( $fil, "wiping temporary $tmpnam" );
	wipe( $tmpnam );
    }
}

######################################################################
# encrypt document with passphrase, return encrypted filename, or ""

sub encrypt
{
    my( $file, $passphrase ) = @_;
    my( $status, $out );

    $out = tmpnam();
    if ( $passphrase ne "" )
    {
	DBG( "invoking: $pgp_exe $pgp_flags -ca -z\"$passphrase\" $file -o $out" );
	$status = `$pgp_exe $pgp_flags -ca -z"$passphrase" $file -o $out`;
    }
    else
    {
	DBG( "invoking: $pgp_exe 2> /dev/null -ca $file -o $out" );
	$status = `$pgp_exe 2> /dev/null -ca $file -o $out`;
    }
    DBG( $status );
    $status = ($? >> 8);
    return ( $status == 0 ) ? $out : "";
}

######################################################################
# sign document with key if key missing just armor

sub envelope
{
    my( $file, $key ) = @_;
    my( $status, $out );

    $out = tmpnam();
    if ( $key ne "" )
    {
	DBG( "invoking: $pgp_exe $pgp_flags -sa $file -o $out" );
	$status = `$pgp_exe $pgp_flags -sa $file -o $out`;
    }
    else
    {
	DBG( "invoking: $pgp_exe $pgp_flags -a $file -o $out" );
	$status = `$pgp_exe $pgp_flags -a $file -o $out`;
    }
    DBG( $status );
    $status = ($? >> 8);
    return ( $status == 0 ) ? $out : "";
}

sub extract_key
{
    my( $key ) = @_;
    my( $status, $out );
    
    $out = tmpnam();
    DBG( "invoking: $pgp_exe $pgp_flags -kxa $key -o $out" );
    $status = `$pgp_exe $pgp_flags -kxa $key -o $out`;
    DBG( $status );
    $status = ($? >> 8);
    return ( $status == 0 ) ? $out : "";
}

sub sha1
{
    my( $str ) = @_;
    my( $tmp );

    DBG( "invoking echo -n \"$str\" | $sha1_exe" );
    $tmp = `echo -n "$str" | $sha1_exe`;
    chop( $tmp );
    return $tmp;
}

sub sha1_file
{
    my( $file ) = @_;
    my( $tmp );

    $tmp = `$sha1_exe < $file`;
    chop( $tmp );
    return $tmp;
}

sub DBG
{
    my( @str ) = @_;

    if ( $d ) {	print STDERR join( " ", @str ); }
}
