#!/usr/bin/perl

use strict;
use warnings;
use Getopt::Long;
use Net::POP3;
use Pod::Usage;
use Term::ReadLine;
use Term::ANSIColor qw(:constants);
$Term::ANSIColor::AUTORESET = 1;
use Carp;

use vars qw(
   $VERSION
   $opt_verbose
   $opt_host
   $opt_username
   $opt_password
   $opt_port
   $opt_noprompt
   $opt_separator
   $opt_output
   $opt_stdin
   $opt_lines
   $opt_timeout
   $opt_help
   $opt_version
   $processed_count
   $pop3
);
$VERSION = sprintf '%d.%02d', q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/;

$opt_port    = 110;
$opt_timeout = 120;
$opt_separator = "none";
GetOptions(
   'v|verbose'     => \$opt_verbose,
   'h|host=s'      => \$opt_host,
   'u|username=s'  => \$opt_username,
   'p|password=s'  => \$opt_password,
   'noprompt'      => \$opt_noprompt,
   'timeout=i'     => \$opt_timeout,
   'port=s'        => \$opt_port,
   's|separator=s' => \$opt_separator,
   'l|lines=i'     => \$opt_lines,
   'o|output=s'    => \$opt_output,
   'stdin'         => \$opt_stdin,
   'help'          => \$opt_help,
   'version'       => \$opt_version,
) or pod2usage(2);
pod2usage(1) if $opt_help;
if ($opt_version) {
   print <<VERSION;
pop3retr, version $VERSION
Written by Michael Nachbaur <mike\@nachbaur.com>.

Copyright (c) 2002-2003 Michael A Nachbaur. All rights reserved. This program
is free software; you can redistribute it and/or modify it under the same
terms as Perl itself.
VERSION
   exit(0);
}

unless ($opt_noprompt) {
   my $term = new Term::ReadLine::Gnu 'pop3list';
   my $attribs = $term->Attribs;
   $term->ReadHistory;
   Term::ReadLine::Gnu->Features->{ornaments} = 0;
   Term::ReadLine::Gnu->Features->{autohistory} = 0;
   my $OUT = $term->OUT || *STDOUT;
   unless ($opt_host) {
       $opt_host = $term->readline("Hostname: ");
   }
   unless ($opt_username) {
       $opt_username = $term->readline("Username: ");
   }
   unless ($opt_password) {
       $attribs->{redisplay_function} = $attribs->{shadow_redisplay};
       $opt_password = $term->readline("Password: ");
       $term->remove_history($term->where_history);
   }
   $term->WriteHistory;
}

if ($opt_separator !~ /(none|short|long|fancy|null)/) {
   die "You specified an invalid separator: \"$opt_separator\".\n";
}

if ($opt_output and !-w $opt_output) {
   die "The output location \"$opt_output\" does not exist or is not writable.\n";
}

$opt_lines = undef if (defined($opt_lines) and $opt_lines < 1);

$processed_count = 0;
my $message_count = undef;
($pop3, $message_count) = pop_connect(
   username => $opt_username,
   password => $opt_password,
   host     => $opt_host,
   port     => $opt_port,
   timeout  => $opt_timeout,
) or carp "Could not get connect to the mail server.\n";

if ($#ARGV < 0 or $opt_stdin) {
   if (test_interactive()) {
       print STDERR "Enter the message IDs you want to retrieve, followed by \na carriage return.  Press CTRL-D to finish.\n";
       print STDERR "$message_count messages in this mailbox.\n";
   }
   while (my $line = <STDIN>) {
       chomp $line;
       if (test_interactive() and $line =~ /\s*all\s*/i) {
           print STDERR "The \"all\" keyword is not available in STDIN mode.\n";
           next;
       }
       processMessages($line);
   }
} else {
   if ($#ARGV == 0 and lc($ARGV[0]) eq 'all') {
       my $list = $pop3->list;
       foreach my $id (keys %{$list}) {
           retrieveMessage($id);
       }
   } else {
       foreach my $id (@ARGV) {
           processMessages($id);
       }
   }
}

print STDERR GREEN "Disconnecting from POP3 server.\n" if ($opt_verbose);
$pop3->quit();

sub processMessages {
   my ($id) = @_;

   if ($id =~ /^\s*(\d+)\s*$/) {
       my $message = $1;
       retrieveMessage($message);
   } elsif ($id =~ /^\s*(\d+)-(\d+)\s*$/ and $1 < $2) {
       foreach my $message ($1 .. $2) {
           retrieveMessage($message);
       }
   }
}

sub retrieveMessage {
   my ($id) = @_;
   $processed_count++;
   print STDERR RED "Processed $processed_count messages.\n" if $opt_verbose;
   printSeparator($id);
   if ($opt_output and -d $opt_output) {
       open OUTFILE, ">$opt_output/$id";
   } elsif ($opt_output and -f $opt_output or -c $opt_output) {
       open OUTFILE, ">>$opt_output";
   } else {
       open OUTFILE, ">&STDOUT";
   }
   if ($opt_lines) {
       print STDERR YELLOW "Retrieving $opt_lines lines from #$id\n" if $opt_verbose;
       print OUTFILE join("", @{$pop3->top($id, $opt_lines)}) or print STDERR RED "Cannot retrieve message #$id\n";
   } else {
       print STDERR YELLOW "Retrieving #$id\n" if $opt_verbose;
       print OUTFILE join("", @{$pop3->get($id)});
   }
   close OUTFILE;
}

sub printSeparator {
   my ($id) = shift @_;
   return if ($opt_separator eq "none");
   print STDERR YELLOW "Printing separator\n" if $opt_verbose;
   if ($opt_separator eq "short") {
       print STDOUT "$id\n";
   } elsif ($opt_separator eq "long") {
       print STDOUT "-" x 4 . "[ " . $id . " ]" . "-" x (67 - length($id)) . "\n";
   } elsif ($opt_separator eq "fancy") {
       my $top_line = "+" . "-" x 73 . "+" . "\n";
       print STDOUT $top_line;
       my @messages = ("$processed_count", "Message $id");
       foreach (@messages) {
           print STDOUT "| " . $_ . " " x (71 - length($_)) . " |\n";
       }
       print STDOUT $top_line;
   } elsif ($opt_separator eq "null") {
       print STDOUT "\0";
   }
}

sub pop_connect {
   my (%params) = @_;
   my $res = undef;
   my $num_messages = undef;

   print STDERR GREEN "Opening POP3 connectionn to $params{host}.\n" if ($opt_verbose);
   my $pop = Net::POP3->new($params{host}, Timeout => $params{timeout});

   print STDERR GREEN "Sending USER command.\n" if ($opt_verbose);
   $res = $pop->user( $params{username} );
   unless ($res) {
       print STDERR RED "Username rejected\n";
       return 0;
   }

   print STDERR GREEN "Sending PASS command.\n" if ($opt_verbose);
   $res = $pop->pass( $params{password} );
   unless ($res) {
       print STDERR RED "Password rejected\n";
       return 0;
   } else {
       ($num_messages) = $res =~ /^(\d+)/;
   }
   print STDERR CYAN "This mailbox contains $num_messages messages.\n" if ($opt_verbose);

   return ($pop, $num_messages);
}

sub test_interactive {
   return -t STDIN && -t STDOUT;
}

1;
__END__
# Below is stub documentation for your module. You better edit it!

=head1 NAME

pop3retr - Retrieves the specified messages from a POP3 account

=head1 OSNAMES

Any Unix-like only

=head1 SCRIPT CATEGORIES

UNIX/System_administration
Mail

=head1 PREREQUISITES

This script requires the C<Getopt::Long>, C<Pod::Usage> and C<Carp> packages, which
should be on your system anyway.  Additionally, C<Term::ReadLine> and C<Term::ANSIColor>
are used to print pretty verbose messages, so you can differentiate between local message
and server-side mail.  Finally, the magic behind this package is thanks to C<Net::POP3>,
which is required.

=head1 SYNOPSIS

 pop3retr [OPTIONS] <message numbers>

 Options:
   -v, --verbose    be verbose about what's happening
   -h, --host       hostname of POP server
   -u, --username   pop3 username
   -p, --password   password
   -s, --separator  message separator: none|short|long|fancy|null (default: none)
   -l, --lines      optional number of lines to retrieve
       --noprompt   do not prompt for information
       --stdin      force the use of STDIN only
       --port       override the TCP port (default: 110)
       --timeout    response timeout in secs (default: 120)
       --help       this help screen
       --version    version  information

=head1 DESCRIPTION

pop3retr is a simple script that connects to a POP3 server and
retrieves the indicated messages, by message number.  It accepts
message numbers on the command line, or through STDIN.

This functions very simillarly to L<pop3dele>, so refer to that
manpage for more details.

=head1 README

pop3retr is a simple script that connects to a POP3 server and
retrieves the indicated messages, by message number.  It accepts
message numbers on the command line, or through STDIN.

This functions very simillarly to L<pop3dele>, so refer to that
manpage for more details.

=head1 SEPARATORS

There are four types of separator possible.  In the event that
multiple messages are returned, this separator will be used.

=over 4

=item none (default)

all the messages will be run together with no separation (essentially
an exact copy of the mail file on the mail server).

=item short

this outputs just the message number.

=item long

this outputs a short separator, consisting of 75 dashes and the message number.

=item fancy

this returns a fancy separator, a block of ascii-art, also indicating
the message number.

=item null

this will return a null byte (\0), for use in processing by another
script.

=back

=head1 AUTHOR

Michael A Nachbaur, E<lt>[email protected]<gt>

=head1 COPYRIGHT

Copyright (c) 2002-2003 Michael A Nachbaur. All rights reserved. This program is
free software; you can redistribute it and/or modify it under the same
terms as Perl itself.

=head1 SEE ALSO

L<pop3list>, L<pop3dele>.

=head1 REVISION

$Id: pop3retr,v 1.2 2003/09/10 17:44:54 nachbaur Exp $

=cut