#!/usr/bin/perl
##############################################################################
## checkmail - Very simple pop3 mail biff, with GUI configuration.
##
## Copyright (C) 2002 Jess Thrysoee <jess (at)thrysoee.dk>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
##############################################################################

use Tk;
use strict;
use IO::Socket;
use Digest::MD5 qw(md5_hex);

chdir;
until(open(IN, ".checkmail")) {SetupGUI()}

my %input;
while (<IN>) {
   chomp;
   (my $mykey, my $myvalue) = split(/=/,$_);
   $input{$mykey} = $myvalue;
}
close(IN);

my $preset = 0;

if (defined($ARGV[0]) && $ARGV[0] eq "-setup") { 
   $preset = 1;
   SetupGUI()
}

my $apop = 1;
my $apop_ref = \$apop;
my $mailcount = 0;

##
## Here starts the main loop.
##
while( 1 ) {
   my @newmail;

   if ($$apop_ref == 1) {@newmail = POP3()};
   if ($$apop_ref == 0) {@newmail = POP3()};

   if ($mailcount < $newmail[0]){
      $mailcount = $newmail[0];
      system("play $input{'SOUND'} &> /dev/null &");
      GUI(@newmail);
   }
   sleep($input{"TIMEOUT"});
}

##
## Here we connect to the POP3 server and check for new mails.
##
## The relative complexity of this sub, is because my ISP insists on having
## a timestamp in their banner even though the don't support APOP.
##
sub POP3 {

   my $EOL = "\015\012";
   my @result = (0,0);

   my $pop = IO::Socket::INET->new(Proto => "tcp",
      PeerAddr => $input{"HOST"},
      PeerPort => "110");
   
   if ($pop) {
      my $answer = <$pop>;
      chomp $answer;

      if ($answer =~ /(<[\w\d\-\.]+\@[\w\d\-\.]+>)/ && $$apop_ref) {
         my $hash = md5_hex($1,$input{'PASS'});
         print $pop "APOP " . "$input{'USER'} " . $hash . $EOL;
         $answer = <$pop>;

         if ($answer =~ /^-ERR/) {
            $$apop_ref = 0;
            close $pop;
            return @result;
         }
      } else {           
         print $pop "USER " . $input{'USER'} . $EOL;
         $answer = <$pop>;
         print $pop "PASS " . $input{'PASS'} . $EOL;
         $answer = <$pop>;
      }     

      print $pop "STAT" . $EOL;
      $answer = <$pop>;
      chomp $answer;
      @result = split(/\s+/, $answer);
      shift @result;

      use integer;
      $result[1] = $result[1]/1024;

      close $pop; #it is intentional that I don't send a QUIT
                  #to the server. This is to prevent it to go
                  #into the UPDATE state and mark your mails 
                  #as read.   
   }
   return @result;
}          

##
## This is the main GUI that pops up when there is new mail.
##
sub GUI { 
   require Tk::LabFrame;

   my @GUIcount = @_;   

   my $main = Tk::MainWindow->new(-title => 'Mail!');

   $main->optionAdd("*font" => '-*-helvetica-medium-r-*-10-*',
      'userDefault');

   my $labframe1 = $main->LabFrame(-label => $input{HOST}, 
      -labelside => "acrosstop");
   $labframe1->pack(-expand => 1, -fill => 'both');
 
   $main->optionAdd("*font" => '-*-helvetica-bold-r-*-14-*',
      'userDefault');

   my $announceLabel = $labframe1->Label(-text => "You have Mail!");
   $announceLabel->pack(-expand => 1, -pady => 2, -padx => 20);

   $main->optionAdd("*font" => '-*-helvetica-medium-r-*-12-*', 
        'userDefault');

   my $amountLabel = $labframe1->Label(-text => "$GUIcount[0] message(s), "
      . "$GUIcount[1] kb."); 
   $amountLabel->pack(-expand => 1);

   my $getitLabel = $labframe1->Label(-text => "Shall we get it now?");
   $getitLabel->pack(-expand => 1, -pady => 2);

   my $frame = $main->Frame();
   $frame->pack(-expand => 1, -fill => 'both');

   my $button_no = $frame->Button(-text => 'no',
      -width => 5,
      -command => sub{buttonfunc($main,0)});
   $button_no->pack(-side => 'right', -expand => 1, -pady => 2);

   my $button_yes = $frame->Button(-text => 'yes',
      -width => 5,
      -command => sub{buttonfunc($main,1)});
   $button_yes->pack(-side => 'left', -expand => 1, -pady => 2);


   $button_no->bind("<Return>", sub {$button_no->invoke});
   $button_yes->bind("<Return>", sub {$button_yes->invoke});

   MainLoop(); }

##
## This is the functions that determin what the buttons do
## in the above GUI
##
sub buttonfunc {
   
   my (my $window, my $whichbutton) = @_;

   if($whichbutton) {
      system "$input{'CLIENT'} &> /dev/null  &";  
      $mailcount = 0;
   }
   $window->destroy();
}  

##
## This is the setup GUI, where you set your specifications.
##
sub SetupGUI {

   require Tk::LabFrame;

   my $mainSetup = Tk::MainWindow->new(-title => "Checkmail");

   $mainSetup->optionAdd("*font" => '-*-helvetica-medium-r-*-12-*',
      'userDefault');

   my $labframe1 = $mainSetup->LabFrame(-label => "Mailbox",
      -labelside => "acrosstop");
   $labframe1->pack(-expand => 1, -fill => 'both');     

   my $box1 = $labframe1->Frame()->pack(-expand => 1, -fill => 'x');
   my $hostLabel = $box1->Label(-text => "Hostname:");
   $hostLabel->pack(-expand => 1, -side => 'left', -anchor => 'e');

   my $entryHost = $box1->Entry(-background => 'white',
      -width => 15);
   $entryHost->pack(-expand => 1, -side => 'left', -anchor => 'w');

   my $box2 = $labframe1->Frame()->pack(-expand => 1, -fill => 'x');
   my $userLabel = $box2->Label(-text => "Username:");
   $userLabel->pack(-expand => 1, -side => 'left', -anchor => 'e');

   my $entryUser = $box2->Entry(-background => 'white',
      -width => 15);
   $entryUser->pack(-expand => 1, -side => 'left', -anchor => 'w');

   my $box3 = $labframe1->Frame()->pack(-expand => 1, -fill => 'x');
   my $passLabel = $box3->Label(-text => "Password:");
   $passLabel->pack(-expand => 1, -side => 'left', -anchor => 'e');

   my $entryPassword = $box3->Entry(-background => 'white',
      -show => '*',
      -width => 15);
   $entryPassword->pack(-expand => 1, -side => 'left', -anchor => 'w');

   my $box4 = $labframe1->Frame()->pack(-expand => 1, -fill => 'x');
   my $checkLabel = $box4->Label(-text => "Check for mail every");
   $checkLabel->pack(-expand => 1, -side => 'left', -anchor => 'e');

   my $var;
   my $opt = $box4->Optionmenu(-options => [qw(1 2 5 10 30 60)],
      -variable => \$var);
   $opt->pack(-expand => 1, -side => 'left', -anchor => 'w'); 

   my $minLabel = $box4->Label(-text => "min.");
   $minLabel->pack(-expand => 1, -side => 'left', -anchor => 'w');

   my $labframe2 = $mainSetup->LabFrame(-label => "Apps",
      -labelside => "acrosstop");
   $labframe2->pack(-expand => 1, -fill => 'both');     

   my $box5 = $labframe2->Frame()->pack(-expand => 1, -fill => 'x');
   my $clientLabel = $box5->Label(-text => "Mail client:");
   $clientLabel->pack(-expand => 1, -side => 'left', -anchor => 'e');

   my $entryClient = $box5->Entry(-background => 'white',
      -width => 15);
   $entryClient->pack(-expand => 1, -side => 'left', -anchor => 'w');

   my $box6 = $labframe2->Frame()->pack(-expand => 1, -fill => 'x');
   my $soundLabel = $box6->Label(-text => "Sound file:");
   $soundLabel->pack(-expand => 1, -side => 'left', -anchor => 'e');

   my $entrySound = $box6->Entry(-background => 'white',
      -width => 15);
   $entrySound->pack(-expand => 1, -side => 'left', -anchor => 'w');

   my @entrylist = ($entryHost, $entryUser, $entryPassword, $entrySound,
                   \$var, $entryClient, $mainSetup);

   my $frame = $mainSetup->Frame()->pack(-expand => 1, -fill => 'x');

   my $button1 = $frame->Button(-text => "OK",
      -command => sub{WriteToFile(@entrylist)});
   $button1->pack(-side => 'right', -padx => 3);

   my $button2 = $frame->Button(-text => "Cancel",
      -command => sub{exit()});
   $button2->pack(-side => 'right');

   $button1->bind("<Return>", sub {$button1->invoke});
   $button2->bind("<Return>", sub {$button2->invoke});

   if ($preset){
      $entryHost->insert(0, "$input{'HOST'}");
      $entryUser->insert(0, "$input{'USER'}");
      $entryPassword->insert(0, "$input{'PASS'}");
      $entrySound->insert(0, "$input{'SOUND'}");
      $entryClient->insert(0, "$input{'CLIENT'}");
      my $var2 = $input{'TIMEOUT'} /60;
      $opt->configure(-textvariable => \$var2); 
   }  

   MainLoop();
}

##
## Here we write your specs. to a file ~/.checkmail
##
sub WriteToFile {

   my @entries = @_;
   my %value;
      
   $value{"HOST"} = $entries[0]->get;
   $value{"USER"} = $entries[1]->get;
   $value{"PASS"} = $entries[2]->get;
   $value{"SOUND"} = $entries[3]->get;
   my $deref = $entries[4];
   $value{"TIMEOUT"} = (60 * $$deref);
   $value{"CLIENT"} = $entries[5]->get;

   $entries[6]->destroy();

   chdir;
   open (CONF_FILE, "> .checkmail") || die "cannot open .checkmail: $!";
   chmod(0600, ".checkmail");
   my $key;
   foreach $key (keys (%value)) {
      print CONF_FILE "$key=$value{$key}\n";
   }
   close(CONF_FILE); 
}

##
## EOF
##
