#!perl
#
# Simple keyring usage example: check IMAP folder for newest emails.
# Non-sensitive data are taken from program args, but password is 
# got directly from the user and saved in keyring.
#
# Try running app a few times. Only on first run there should be
# password prompt.
#
# Example usage (for gmail):
#
# perl imap_query_sample.pl --machine imap.gmail.com --user joe2774 
#
# Set PASSWD_KEYRING_AUTO_DEBUG=1 environment variable to get details
# of what is picked and why.

{
    package RunMe;
    use Moose;
    with 'MooseX::Getopt';

    use Passwd::Keyring::Auto;      # get_keyring
    use Term::ReadKey;              # For secure password prompt
    use Net::IMAP::Simple::SSL;     # We access IMAP as illustration
    use Email::Simple;              # and get some email data.

    has 'machine' => (
        is=>'ro', isa=>'Str', required=>1,
        documentation=>'IMAP machine name, like imap.gmail.com');
    has 'port' =>  (is=>'ro', isa=>'Int', required=>1, default=> 993,
                   documentation=>'IMAP port (default 993)');
    has 'user' => (is=>'ro', isa=>'Str', required=>1,
                   documentation=>'Your username on IMAP server');
    has 'folder' => (is=>'ro', isa=>'Str', required=>1, default=>'INBOX',
                     documentation=>'The folder to check');
    has 'count' => (is=>'ro', isa=>'Int', required=>1, default=> 5,
                    documentation=>'Number of emails printed');

    my $GROUP = 'Passwd::Keyring tests and samples';
    my $APP = 'imap_query_sample';

    my $ATTEMPTS_COUNT = 3;

    sub run {
        my $self = shift;

        my $keyring = get_keyring(app=>$APP, group=>$GROUP);

        my $imap_addr = $self->machine . ':' . $self->port;
        my $imap = Net::IMAP::Simple::SSL->new($imap_addr)
          or die "Can't connect to $imap_addr: $Net::IMAP::Simple::errstr\n";

        my $realm = "IMAP:$imap_addr";
        my $user = $self->user;
        # Attempt to recover previously saved password
        my $password = $keyring->get_password($user, $realm);
        for (my $attempt_no = 1; 1; ++ $attempt_no) {
            unless($password) {
                print "Enter password for $user on $realm: ";
                ReadMode 'noecho';
                $password = ReadLine 0; chomp($password);
                ReadMode 'normal';
                print "\n";
            }
            if( $imap->login($user, $password) ) {
                # Saving the password for future
                $keyring->set_password($user, $password, $realm);
                last; # Password OK, continuing work
            } else {
                print "IMAP login failed (bad password?). Error: " . $imap->errstr, "\n";
                # Clearing the bad password in case it was taken from keyring
                $keyring->clear_password($self->user, $realm);
                $password = '';
                # Retrying unless we exhaused attempts
                if($attempt_no >= $ATTEMPTS_COUNT) {
                    die "$attempt_no login failures, good bye\n";
                }
            }
        }

        # Actual work, as illustration

        my $folder = $self->folder;
        my $count = $imap->select($folder);
        unless(defined($count)) {
            die "Unable to open folder $folder (maybe bad name?): $Net::IMAP::Simple::errstr\n";
        }
        print "$count messages in $folder. Newest:\n";
        for(my $idx = $count;
            $idx > 0 && $idx > $count - $self->count;
            -- $idx) {
            my $email = Email::Simple->new(join '', @{ $imap->top($idx) } );
            printf("[%03d] %s\n", $idx, $email->header('Subject'));
        }
    }
}

my $run_me = RunMe->new_with_options();
$run_me->run;
