#!/usr/bin/perl
#: send myself mail at some future time
#======================================================================
#To send to my Pobox address:
#  set REMINDER_TO='sburke@pobox.com'; remind...
#======================================================================

my($Version, $PVersion) = version_from( 
  q[Time-stamp: "2008-10-17 02:09:10 AKDT sburke@cpan.org"]);
use strict;use warnings;use constant DEBUG =>0;my($Self,$Mailer);Look_at_env();
sub Usage { print
	     ########## USAGE MESSAGE FOLLOWS ##########
qq~$Self -- send mail to myself at some point in the future
$Version
Usage:
  $Self -s "Get the job done" now + 1 day
   = sending mail -s "Get the job done", via `at now + 1 day`.

Example:
 $Self -s "Those taters" now + 1 day

 ('$Self' replies:)
   Enter message text for sending at "now + 1 minute", then press control-D
 (user enters:)
   I like potatoes
   hooboy, I sure do
   [then hitting control-D]
 (response from machine:)
   job 506 at Mon Mar 17 13:07:00 2008

Options:
 remind -h or --help                      = show this message and quit
 remind -v                                = show version number and quit
 remind -s "hoo hah" [...at options...]   = sets Subject line to send with
 remind -q [...at options...]             = Don't print "Enter Message..."
 remind [...at options...] -- Hoo hah     = use "Hoo hah" as the message text
					        (and subject, unless you use -s)
Notes:
* $Self takes from STDIN.  That can mean what you type, or output
from some other program (which runs now, not later, remember!).

* If you provide no subject (via -s "Hoo hah"), then
$Self tries to get it from the first line of the message.

* This program is a wrapper around `$Mailer' and `at'.

* "[...at options...]", above, are whatever you'd feed to at(1) on
    the command line.  Examples:
      4pm
      4pm + 3 days
      now + 7 hours
      2am tomorrow
      2012-06-17
      4am 2012-06-17

~; exit; }
	      ########## END OF USAGE MESSAGE ##########
#======================================================================
my $Author = "sburke\x40cpan.org";
my $A = "\n  Aborting";
$|++;
Main();
exit;

my(#Globals:
     $Message, $Subject, $Mail_From, $Mail_To, $Username,
     @At_options, $Temp_file, $Temp_file_name, $AT, $Quiet,
);
#======================================================================

sub Look_at_env {
  my $home = $ENV{HOME} || '/';
  chdir($home) || die "Can't chdir to $home !?!?$A";

  $ENV{PWD} = $home;
  $Username     = $ENV{USER} || die "I can't figure out what user I am?!$A";

  $Mail_From    = $ENV{REMINDER_FROM}   || $Username;
  $Mail_To      = $ENV{REMINDER_TO}     || $Username;
  $Mailer = $ENV{REMINDER_MAILER} || $ENV{MAILER} || 'rmail';
  prune_env();

  $Self         = My_own_program_name();

  return;
}

#======================================================================

sub Main {
  args_processing();
  if( $> == 0 and !$Quiet) {
    print "I'm running as root.  Keep that in mind!\n";
  }
  get_message();
  make_temp_file();
  do_at();

  DEBUG and print "Done.\n";
}

#======================================================================

END { unlink $Temp_file_name if $Temp_file_name; }

use File::Temp ();
sub make_temp_file {
  $Temp_file = File::Temp->new(
    TEMPLATE => 'atmailme_XXXXX',
    DIR => '/tmp',
    SUFFIX => '.sh'
  );
  $Temp_file_name = $Temp_file->filename;

  $Message = # Yes, two From lines, first without a colon. That's just the way.
"From $Username
From: Self <$Mail_From>
To: Self <$Mail_To>
Subject: $Subject

" . $Message;

  DEBUG and print "Sending to $Temp_file_name: <<\n$Message\n>>\n";

  #print $Temp_file  #"printenv\n";
  #print $Temp_file  "echo -e '",
  #	esc_for_echo($Message),
  #	"' | rmail $Username\n",
  #;
  #$Temp_file->flush;
  #print $Temp_file "\cd";

  my $eoq = join '', 'EOQ', map int(rand(10)), 1 .. 20;

  print $Temp_file  "$Mailer $Username <<\"$eoq\"\n",
  	$Message,
  	"\n$eoq\n",
        "\n#\n# generated by '$Self' at ", scalar localtime,
        "\n#\n",
   ;
  close($Temp_file);

  if(DEBUG > 5) {
    open my $T, "<", $Temp_file_name or die "Guh on $Temp_file_name";
    print "Temp_file contains <", <$T>, ">\n";
    close($T);
  }

  return;
}

#======================================================================

sub do_at {
  open($AT,

    "at -q m -f $Temp_file_name @At_options 2>&1 |"

   ) #     ^-- set the queue to 'm', for _m_essage
    or die "Couldn't open a channel to 'at'\n";
  my @out = grep !m/\Awarning:\s*commands/, <$AT>;

  DEBUG and print "And at says:\n";
  print @out;

  return;
}

#======================================================================

sub Version {
  print "$Self  $Version  $Author\n";
  exit;
}

#======================================================================

use Getopt::Std;
sub args_processing {
  DEBUG and print "ARGV10: <@ARGV>\n";
  @ARGV or Usage();

  my %opts;
  getopt('vhqs:', \%opts) or Usage();
  Version() if $opts{'v'};
  Usage()   if $opts{'h'};
  $Subject   = $opts{'s'};
  $Quiet= 1 if $opts{'q'};

  DEBUG and print "ARGV20: <@ARGV>\n";
  at_options_and_maybe_message();
  DEBUG and print "ARGV30: <@ARGV>\nAts: <@At_options>\n";
   
  @At_options or die "But when to send the message?";

  DEBUG and print "Options @At_options\n",
        "Message ",  $Message || "(nil)", "\n",
	"Subject ",  $Subject || "(nil)", "\n",
  ;

  my @baddies = grep
     m/[^\-\/\+,\:\w]/,
      # ^-- seems to allow all characters in all tokens
      # allowed by at(1) = /usr/share/doc/at/timespec
     @At_options
  ;
  if(@baddies) {
    die join '',
      (@baddies == 1) ?
         "This doesn't look like a valid at(1) token"
       : "These doesn't look like valid at(1) tokens",
      ": @baddies$A";
  }

  return;
}

#======================================================================

sub at_options_and_maybe_message {
  # Move @ARGV items either to @At_options, or,
  #  after the first '--', to message-body text:
  while(@ARGV) {
    if($ARGV[0] eq '--') {
      shift @ARGV;
      @ARGV or die "But after `--', what text to send?$A";
      $Message = join ' ', splice @ARGV;
      die "Command-line specified message has no text!$A"
       unless $Message =~ m/\S/;
      last;
    } else {
      push @At_options, shift @ARGV;
    }
  }
  return;
}

#======================================================================
sub has_controlling_terminal { 0 == system('tty','-s') }
#======================================================================

sub get_message {
  DEBUG and print 'We ', has_controlling_terminal() ?
    "have" : "don't have", " a controlling terminal.\n";

  $Quiet = 1 unless has_controlling_terminal();

  unless($Quiet or ($Message and $Message =~ m/\S/)) {
    print "Enter message text for sending at \"@At_options\", then press control-D\n";
  }
  $Message = join '', <STDIN>
    unless defined $Message;
       # ^-- unless we set in on the subject line

  unless(defined($Message) and $Message =~ m/\S/) {
    die "No message, nothing queued\n";
  }

  unless(defined $Subject) {
    if($Message =~ m<\A \s*  ( [^\n]{1,78} )>x ) {
      $Subject = $1;
      DEBUG and print "Setting subject to \"$Subject\"\n";
    }
  }

  return;
}

#======================================================================

sub My_own_program_name {
  use File::Basename ();
  my $self = File::Basename::basename(my $orig = $0);
  $self = $0 if $self eq '-e' or $self =~ m{\b perl \w* \z}xi;
  return $self;
}

#======================================================================

sub esc_for_echo {
    my($it) = $_[0];
    $it =~ s/([^ \<\>\!\.\:a-zA-Z0-9])/sprintf('\x%02X',ord($1))/eg;
    return $it;
}

#======================================================================

sub version_from {
  my $v = $_[0];
  $v =~ s<\bTime-stamp:\s*\"(.*?)\"\z><$1>;
  return $v unless wantarray;

  my $pv = $v; # padded version
  $pv = "[Version $v]";
  my $to = $ENV{COLUMNS} || 80;
  $to--;
  if( length($pv) < $to ) {
    $v = (" " x ($to - length($pv))) . $pv;
  }

  return($pv, $v);
}

#======================================================================

sub prune_env {
  %ENV = ( map { (defined $ENV{$_}) ? ($_=>$ENV{$_}) : () }
    qw< USER LOGNAME HOME LANG LANGUAGE PATH PWD SHELL TERM TZ TZDIR
        HOSTALIASES TMPDIR PRINTER LPDEST HOST USERNAME
      >                                                           #"#
   # That's mostly just stuff I copied from `man 5 environ'
   # Is anything else really necessary just to send a piece of mail?
   #"
  );
  return;
}

#======================================================================

__END__
