#!/usr/bin/perl -w
#---------#---------#---------#---------#---------#---------#---------#---------
#
# title
#   e-mail a text file using sendmail
# author
#   idc@planetlarg.net 17 April 2001
# description
#   Accept the name a of a text file from the command line or
#   find the latest file in a directory (handy for mailing a log).
#   Copy the contents of the file into an e-mail and send it to a recipient.
#   The sender, receiver and title are specified within this script.
#   Some checks are in place eg. refuse to send huge files.
#   If unkown options are encountered, explain how to use this script.
# modifications
#
#---------#---------#---------#---------#---------#---------#---------#---------
# setup
   use 5.005;
   use strict;
   use vars qw ($DEBUG);
   $DEBUG         = 0; # set to 1 (true) to print extra information to STDERR.

# command line options
   # complain if any options were entered
   usage() if $#ARGV != -1;
   # complain if 1 option was not entered
   usage() if $#ARGV != 1;

#---------#---------#---------#---------#---------#---------#---------#---------
# main
    use Sys::Hostname;
    my $host             = hostname();  # the machine this script runs on

# stuff to tweak
    # Enter the destination addresses, each seperated by a space
    my $to         = 'idc@planetlarg.net';
    my $from       = 'idc@planetlarg.net';
    my $subject    = "file from $host";


# file to send
    # newest file in a directory
    #my $log_dir      = '/logs';
    #my $latest_file  = &latest_modified ($log_dir);
    #my $file         = "$log_dir/$latest_file";

    # file from the command line
    my $file      = $ARGV[0];

# check the file
    die "file is missing, empty or not text: $file "
            unless ( -T "$file");
    my $max        = 1024 * 1024;  # 1 Meg size limit
    die "file is too fat: $file "
            if &file_too_fat ("$file", $max);

# suck in the file contents
    my $body       = &file2scalar ("$file");

# Send the message.
    &send_mail(
               $from,
               $to,
               $subject,
               $body
   );

#---------#---------#---------#---------#---------#---------#---------#---------
# subroutines


# subroutine
#   latest_modified
# description
#  find the, er, latest file in a directory and return its name.
#  Latest means the one that was most recently modified.
#  Every file is examined.
#  readdir returns ONLY the file basename, not the path.
#
sub latest_modified {

   my $dir = shift;           # directory to examine
   opendir (DIR, $dir) or die "can't open directory $dir: $!";

   my $latest_mtime  = 0;     # most recent last modified time
   my $latest_file   = '';    # name of file with $latest_mtime
   my $file          = '';    # name of current file being examined
   while (defined ($file   = readdir (DIR))) {
      next if $file =~ m/^\.\.?$/; # skip . (this) and .. (parent) entries
      my (
                  $dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
                  $size, $atime, $mtime, $ctime, $blksize, $blocks
      ) = stat "$dir/$file";
      if ($latest_mtime < $mtime) {
         $latest_mtime  = $mtime;
         $latest_file   = $file;
      }
      print STDERR "   \$file    \t$file  \n" if $DEBUG;
      print STDERR "   \$mtime   \t$mtime \n" if $DEBUG;
   }
   return $latest_file;
}

# subroutine
#   file2scalar
# description
#   read the contents of a file and put it into a scalar.
#   This is the slurp-whole-file method, so make sure the scalar won't
#   take up too much memory.
#   I haven't bothered to create a shared lock.
#
sub file2scalar {

   my $file = shift;
   open (IN, "<$file") or die "can't open file $file: $!";
   undef $/;  # don't set the end-or-record seperator for slurp-whole-file mode
   my $contents = <IN>;
   close IN            or die "can't close file $file: $!";
   print STDERR "   \$file   \t$file \n" if $DEBUG;
   print STDERR "   \$contents \n\n$contents \n\n" if $DEBUG;

   return $contents;
}

# subroutine
#   file_too_fat
# description
#   file size check. how big is it?
#   A huge file causes an error.
#   return true if too big, false otherwise.
#
sub file_too_fat {

   my $file = shift;
   my $max  = shift;
   my (
               $dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
               $size, $atime, $mtime, $ctime, $blksize, $blocks
   ) = stat "$file";
   print STDERR "   \$size   \t$size  \n" if $DEBUG;
   print STDERR "   \$max    \t$max   \n" if $DEBUG;
   return 1 if $size > $max;
   return 0;
}


# subroutine
#   send_mail
# description:
#   use sendmail to send a message. Die on error
#   Fake the sender's address on the envelope
#
sub send_mail {
   my ($from, $to, $subject, $body) = @_;
   my $verbose = ''; # get sendmail to print extra info if debug is set
   $verbose = '-v' if $DEBUG;

   print STDERR "   \$to       \t$to      \n" if $DEBUG;
   print STDERR "   \$from     \t$from    \n" if $DEBUG;
   print STDERR "   \$subject  \t$subject \n" if $DEBUG;

   open ( MAIL, "| /usr/lib/sendmail $verbose -t -f $from -i") or die "can't open sendmail: $!";
   print MAIL<<END_MESSAGE;
To: $to
From: $from
Subject: $subject

$body
END_MESSAGE
   close MAIL or die "can't close sendmail: $!";
}



# subroutine
#   usage
# description
#   describe how this script should be used.
#   This is one way of producing a set of instructions, using simple indentation.
#   For more complex document, use mark-up. See the perldoc section below.
#
sub usage {

   use Text::Wrap;
   $Text::Wrap::columns = 72;
   my $pre1             = "   ";
   my $pre2             = "   ";
   my $name             = "$0 - mail a text file";
   my $synopsis         = "$0  -f /DIRECTORY/FILE";
   my $description      = "
This script sends a text file to a recipient.
File size is checked before it is sent.
Edit the script to change the sender, receiver or title.
";
   my $examples         = "
Send a missing files report.
$pre1$0  -f /directory/report.txt
";

   print
               "NAME \n",
               wrap ($pre1, $pre2, $name), "\n",
               "SYNOPSIS \n",
               wrap ($pre1, $pre2, $synopsis), "\n",
               "DESCRIPTION \n",
               fill ($pre1, $pre2, $description), "\n",
               "EXAMPLES \n",
               wrap ($pre1, $pre2, $examples), "\n",
   ;
   exit 1;
}
#---------#---------#---------#---------#---------#---------#---------#---------


__END__

documentation

The "__END__" string is a token. It is the logical end of program text.
You can put anything you want after this token, such as an
instruction manual page; it will be ignored by the compiler.


