#!/usr/bin/perl

#default header filter
@headpattern=("From:","To:","Subject:","CC:");
#no. of body-lines to show
$bodylines=10;

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

####################################################################
# (c) 2001-2005 by Christoph Baumann <cgb@debian.org>              #
# The GNU Genral Public license applies to this program            #
# Thanks:                                                          #
#   * Jorrit "J" Fahlke for patching the config file parser       #
#   * Takeshi Hamasaki for the headpattern options patch           #
#   * Sebastian Henschel for the "*" patch                         #
#   * Wulf Forrester-Barker for the mailbox command and the abbrev.#
####################################################################


sub get_keylist
{
    my ($arg, @keylist, @hurz, $low, $high, $i);
    $arg = shift;

    if ($arg =~ /-/)
    {
        @hurz=split(/-/,$arg);
        if ($hurz[0] > $hurz[1])
        {
            $low = $hurz[1];
            $high = $hurz[0];
        }
        else
        {
            $low = $hurz[0];
            $high = $hurz[1];
        }
        for ($i=$low; $i<=$high; $i++)
        {
            push(@keylist,$i);
        }
    }
    else
    {
        push(@keylist,$arg);
    }
    return @keylist;
}



print "pop3browser 0.4.1 -- (c) 2001-2005 by Christoph Baumann\n";
print "There is absolutely no warranty for this program!\n";
print "The GNU General Public License applies to this program.\n";
print "(see http://www.gnu.org)\n";
print "Enter 'help' for a command summary\n";





#read config data
if (-e "$ENV{\"HOME\"}/.pop3browserrc")
{
    ($mode)=(stat("$ENV{\"HOME\"}/.pop3browserrc"))[2];
    if ($mode != 0100600)
    {
	print "The configuration file was readable for other users than the file owner.\n";
	print "Correcting it...\n";
	#this file should only be read by the owner
	chmod(0600,"$ENV{\"HOME\"}/.pop3browserrc") 
	    or die "FATAL: could't chmod the configfile. Do you own it?";
    }
    open(CONFIG,"$ENV{\"HOME\"}/.pop3browserrc")
	or die "FATAL: could't open the configfile. Do you own it?";
    $i=0;
    
    while (<CONFIG>)
    {
	chomp();
	s/^\s+//;
	next if /^\#/;
	next if /^$/;
	
	# allow to set $bodylines in ~/.pop3browserrc
	#   example:
	#   @bodylines = 15
	#
	if ( $_ =~ /^\@bodylines/ )
	{
	    ($dummy, $bodylines) = split(/\s*=\s*/, $_, 2);

	    next;
	}
	
	# allow to set @headpattern in ~/.pop3browserrc
	#   example:
	#   @headpattern = "From:","To:","Subject:","Date:"
	#
	if ( $_ =~ /^\@headpattern/ )
	{
	    ($dummy, $hp) = split(/\s*=\s*/, $_, 2);
	    @headpattern = split( /\,/ , $hp);
	    for ( @headpattern )
	    {
		s/\"//g;
	    }
	    
	    next;
	}
	
	($host[$i]{"host"},$host[$i]{"uid"},$host[$i]{"passwd"},$host[$i]{"protocol"})=split(/\s+/, $_, 4);

	$i+=1;	
    }
    $nhosts=$i;
    close(CONFIG);
    if ($nhosts==0)
    {
	print "No host definitions found in configuration file!\n";
	exit 0;
    }
}
else
{
    print "No config file found!\n";
    print "pop3browser needs the file .pop3browserrc in your home directory to work.\n";
    print ".pop3browserrc contains the definitions for your pop3 accounts.\n";
    print "One definition per line and in the following form:\n";
    print "hostname userid password\n";
    print "The entries in one line may be separated by spaces or tabs,\n";
    print "lines starting with \# are ignored.\n";
    exit 0;
}


use Net::POP3;

#init some vars
$connected=0;
$number=0;
$nopatterns=@headpattern;
$cmd=" ";

while (!($cmd =~ /^q/) && !($cmd =~ /exit/)) 
{
    print "pop3browser>";
    $cmd=<STDIN>;
    $cmd=~s/^\s+//;
    
    if (($cmd =~ /login/) || ($cmd =~ /^o/))
    {
	if ($connected > 0)
	{
	    $folder->quit();
	    print "Closing current connection...\n";
	    for ($i=1;$i<=$number;$i+=1)
	    {
		$tags[$i]="";
	    }
	    $number=0;
	    $connected=0;
	}
	chop($cmd);
	if ($cmd =~ /^o\d/) {
	    $hostno = substr($cmd,1);
	} else {
	    @hurz=split(/\s+/,$cmd);
	    $hostno=$hurz[1];
	}
	$folder = Net::POP3->new($host[$hostno]{"host"});
	if (!$folder)
	{
	    print "No connection to host\n";
	}
	else
	{
	    if ($host[$hostno]{"protocol"} =~ /^apop/)
	    {
		$number=$folder->apop($host[$hostno]{"uid"},$host[$hostno]{"passwd"});
	    }
	    else
	    {
		$number=$folder->login($host[$hostno]{"uid"},$host[$hostno]{"passwd"});
	    }
	    
	    if ($number eq "0E0")
	    {
		$number=0;
	    }
	    
	    if ($number eq "")
	    {
		print "Authentication failed\n";
	    }
	    else
	    {
		print $number," mails on ",$host[$hostno]{"host"}," for ",$host[$hostno]{"uid"},"\n";
		$connected=1;
		$list=$folder->list();
	    }
	}
    }
    
    if ($cmd =~ /apop/)
    {
	if ($connected > 0)
	{
	    $folder->quit();
	    print "Closing current connection...\n";
	    @tags=();
	    $number=0;
	    $connected=0;
	}
	chop($cmd);
	@hurz=split(/\s+/,$cmd);
	$hostno=$hurz[1];
	$folder = Net::POP3->new($host[$hostno]{"host"});
	if (!$folder)
	{
	    print "No connection to host\n";
	}
	else
	{		  
	    $number=$folder->apop($host[$hostno]{"uid"},$host[$hostno]{"passwd"});

	    if ($number eq "0E0")
	    {
		$number=0;
	    }
	    
	    if ($number eq "")
	    {
		print "Authentication failed\n";
	    }
	    else
	    {
		print $number," mails on ",$host[$hostno]{"host"}," for ",$host[$hostno]{"uid"},"\n";
		$connected=1;
		$list=$folder->list();
	    }
	}
    }
    
    if ($cmd =~ /^c/)
    {
	if ($connected > 0)
	{
	    for ($i=1;$i<=$number;$i+=1)
	    {
		 if ($tags[$i] =~ /d/)
		 {
		     print "deleting mail number $i!\n";
		     $folder->delete($i);
		 }
		 $tags[$i]="";
	    }
	    $folder->quit();
	    $number=0;
	    $connected=0;
	    print "Connection closed\n";
	}
	else
	{
	    print "No open connection...\n";
	}
    }

    if (($cmd =~ /^q/)||($cmd =~ /exit/))
    {
	if ($connected > 0)
	{
	    for ($i=1;$i<=$number;$i+=1)
	    {
		 if ($tags[$i] =~ /d/)
		 {
		     print "deleting mail number $i!\n";
		     $folder->delete($i);
		 }
		 $tags[$i]="";
	    }
	    $folder->quit();
	    $number=0;
	    $connected=0;
	    print "Connection closed\n";
	}
    }
    
    if ($cmd =~ /hosts/)
    {
	for ($i=0;$i<$nhosts;$i+=1)
	{
	    print $i,"\t",$host[$i]{"uid"},"@",$host[$i]{"host"},"\n";
	}
    }
    
    
    if (($cmd =~ /list/)&&($connected > 0)) 
    {
	for ($key=1;$key<=$number;$key+=1) 
	{
	    print "msg no: $key \t size: $list->{$key} \t tags: $tags[$key]\n";
	}  
    }

    if (($cmd =~ /^m/)&&($connected > 0)) 
    {
	# WFB: attempt to list message number, tag, size, sender and subject
	print "No  Tag Size Sender                          Subject\n";
	for ($key=1;$key<=$number;$key+=1) 
	{
	    printf "%3d %1s %6d ",$key,$tags[$key],$list->{$key};
	    $header=$folder->top($key,$bodylines);
	    $lines=@$header;
	    $body=0;
	    $from="sender unknown";
	    $subject="(no subject)";
	    for ($i=0;$i<$lines;$i+=1)
	    {
		#body starts with an empty line
		if ($header->[$i] =~ /^\n/)
		{
		    $body=1;
		}
		
		# show only From and Subject
		if ($body != 1)
		{
		    if ($header->[$i] =~ /^From:/)
		    {
			$from = substr($header->[$i],6);
		    }
		    if ($header->[$i] =~ /^Subject:/)
		    {
			$subject = substr($header->[$i],9);
		    }
		} 
	    }
	    $from =~ s/^\s*//;
	    chomp($from = substr($from,0,30));
	    $subject =~ s/^\s*//;
	    chomp($subject = substr($subject,0,30));
	    printf "%-30s  %-30s",$from,$subject;
	    print "\n";
	}  
    }
    
    if (($cmd =~ /^k/)&&($connected > 0))
    {
	chop($cmd);
	@hurz=split(/\s+/,$cmd);
	$match=$hurz[1];
	#fix '*' to do the intended
	if ($match =~ /^\*/) {$match =~ s/^\*/\.\*/;}
	for ($key=1;$key<=$number;$key+=1) 
	{
	    print "Processing mail no. $key\n";
	    $header=$folder->top($key,0);
	    $lines=@$header;
	    for ($i=0;$i<$lines;$i+=1)
	    {
		if ($header->[$i] =~ /$match/)
		{
		    if ($tags[$key]!~ /d/)
		    {
			$tags[$key]="$tags[$key]d";
			print "number: $key matched!\n";
			#only mark for deletion and do it on close or quit
			#$folder->delete($key);
		    }
		    #match found, so stop looping
		    last;
		}
	    }
	}  
    }
    
    if (($cmd =~ /^u/)&&($connected > 0))
    {
	chop($cmd);
	@hurz=split(/\s+/,$cmd);
	$match=$hurz[1];
	#fix '*' to do the intended
	if ($match =~ /^\*/) {$match =~ s/^\*/\.\*/;}
	for ($key=1;$key<=$number;$key+=1) 
	{
	    print "Processing mail no. $key\n";
	    $header=$folder->top($key,0);
	    $lines=@$header;
	    for ($i=0;$i<$lines;$i+=1)
	    {
		if ($header->[$i] =~ /$match/)
		{
		    if ($tags[$key] =~ /d/)
		    {
			print "undeleting mail no. $key\n";
 			$tags[$key] =~ s/d//;
		    }
		    #match found, so stop looping
		    last;
		}
	    }
	}  
    }
    
    if (($cmd =~ /^s/)&&($connected > 0))
    {
	chop($cmd);
	if ($cmd =~ /^s\d/) 
	{
	    $key = substr($cmd,1);
	} else {
	    @hurz=split(/\s+/,$cmd);
	    $key=$hurz[1];
	}
	if (($key <= $number)&&($key > 0))
	{
	    $header=$folder->top($key,$bodylines);
	    $lines=@$header;
	    $body=0;
	    if ($tags[$key]!~ /s/)
	    {
		$tags[$key]="$tags[$key]s";
	    }
	    for ($i=0;$i<$lines;$i+=1)
	    {
		#body starts with an empty line
		if ($header->[$i] =~ /^\n/)
		{
		    $body=1;
		}
		
		#show only lines matching @headpatterns
		if ($body != 1)
		{
		    for ($j=0;$j<$nopatterns;$j+=1)
		    {
			if ($header->[$i] =~ /^$headpattern[$j]/)
			{
			    print $header->[$i]; 
			}
		    }
		} 
		else
		{
		    print $header->[$i];
		}
	    }
	}
	else
	{
	    print "illegal message number!\n";
	}
    }
    
    if (($cmd =~ /^d/)&&($connected > 0))
    {
    	chop($cmd);
	@hurz=split(/\s+/,$cmd);
        shift(@hurz);
	foreach $arg (@hurz)
	{
            @keylist = get_keylist($arg);
            foreach $key (@keylist)
            {
		if (($key <= $number)&&($key > 0))
		{
		    print "marking message no. $key for deletion\n";
		    #only mark for deletion and do it on close or quit
		    #$folder->delete($key);
		    if ($tags[$key]!~ /d/)
		    {
			$tags[$key]="$tags[$key]d";
		    }
                }
		else
		{
		    print "illegal message number: $key!\n";
		}
            }
        }
    }
    
    if (($cmd =~ /^undel/)&&($connected > 0))
    {
        chop($cmd);
        @hurz=split(/\s+/,$cmd);
        shift(@hurz);
        foreach $arg (@hurz)
        {
            @keylist = get_keylist($arg);
            foreach $key (@keylist)
            {
                if (($key <= $number)&&($key > 0))
                {
                    if ($tags[$key] =~ /d/)
                    {
                        print "undeleting mail no. $key\n";
                        $tags[$key] =~ s/d//;
                    }
                }
                else
                {
                    print "illegal message number: $key!\n";
                }
            }
        }
    }
    
    
    if ($cmd =~ /help/)
    {
	print "Supported commands:\n";
	print "login n       : log into host no. n, use 'hosts' to get a list of available hosts\n";
	print "o(pen) n      : same as login\n";
	print "apop n        : same as login but uses APOP\n"; 
	print "c(lose)       : close current connection and delete marked mails\n";
	print "hosts         : list available hosts\n";
	print "list          : list sizes and message numbers of the mails on the account\n";
	print "m(ailbox)     : list message number, size, sender and subject for each mail\n";
	print "s(how) n      : show header and some body lines of mail number n\n";
	print "d(elete) n    : delete mail numbers n ('1 2 3' or '1-3' or '3-1' or '1 2-3 5-4')\n";
	print "undel(ete) n  : remove delete tag from mail numbers n (s 'del')\n";
	print "k(ill) expr   : delete mails matching expr in header\n";
	print "u(nkill) expr : remove delete tag from mails matching expr in header\n";
	print "q(uit)        : quit program and delete marked mails\n";
	print "exit          : same as quit\n";
	print "help          : print this help\n";
    }
}

#close still open connections
if ($connected > 0)
{
    $folder->quit();
    print "Connection closed\n";
}
