#!/usr/local/bin/perl
# Written 8/96 C. Paul Ferroni (Allen-Bradley, Inc.)
#   Paul.Ferroni@ab.com
#
#
#####
# v1.0 (original version) 9/96 cpf
# Script to analyze contents of the ftp root directory, and
#   tell everyone who needs to know what files are here.
#
# The relationship between files and email addresses are found
#   in the ftpaccess file with lines starting: ##FTPCHECK
#
# This version uses "formail" (part of procmail package), but could
#   easily be modified to talk directly to any mailer.
# It also depends on the find command with the "-ls" switch.  This 
#   could be replaced with a "find2perl" generated routine for 
#   OS's missing this feature.  If anyone makes does this, I'd
#   like to see it.
#
#####
# v2.0  10/96 cpf
#
# Modified use of the <option> field to include the ability to
#  set reporting on/off depending on file age, and also to set
#  the ability to delete files on/off depending on file age.
#  Also added the ability to skip the entry entirely, regardless
#  of other options.  See syntax section below for details.
#
# Also made much more efficient (one less pass through data)
#  and modified the reporting to reflect the report/delete options.
#
#####
# v2.1 12/4/96 cpf
#
# Modified the "find" command to skip/ignore any files that start with 
#  a "." so that .message and other such things are skipped automatically.
#
#####
# v2.2  12/12/96 cpf
# Modified the "find" command again to use $FindArgs in the body of the
#  program.  $FindArgs is defined at the top in the "user defined" portion
#  to allow for the variations needed from site to site.  I've modified
#  the args at our site to "prune" at the top of all lofs file systems,
#  since I'm mounting bin, dev and etc in each chroot'ed ftp "home" using
#  a read-only lofs mount of /ftp/(bin|etc|dev).
# Also added $DeBugTo mail address, and modified the send-mail portion
#  to send ALL email to $DeBugTo (instead of real owner from ftpaccess)
#  if running in debug mode (-d).
#
#####
# v2.3 1/20/97 cpf
# Fixed bug determing proper file dates (for deleting, and reporting purposes)
#  based on the date string for < 1 year old files returned from the 
#  find command.  Revised logic that determines contents of $year.
# Fixed another bug that always got file times > 1 year wrong (similar
#  to the bug above, but at another point.
#
##################################################################
### SYNTAX
#
# Usage: ftpcheck [-d]
#
# Syntax of line in ftpaccess file:
##FTPCHECK    <options>    <address>    <directory>
#
# where:
# <option> is comma seperated list of:
#        SKP   : for SKIPPING this entry -- overrides other options.
#        RPT   : generate a report if ANY files are found.  No report
#                is sent to the user if this flag is not set.
#        OLDxx : define an "old" file as one that is > xx days old.
#                (defaults to 7).  Note that if OLD date is > DEL date, 
#                then it's reset to the DEL date.  Also note that
#                if RPT is not set, and OLD is set, a report is sent
#                only if old (or deleted) files are found.
#        DELxx : automatically deleting files that are > xx days old 
#                (defaults to 14). If report is generated, indicate
#                files that are deleted.
#
# <address> is email comma seperated list of address(es) of responsible 
#   person(s)
#
# <directory> is directory UNDER $FtpRoot this user is responsible for.
#
# examples:
##FTPCHECK    OLD10,DEL30  Paul.Ferroni@ab.com    /incoming
#   Send report if files > 10 days old. Also, delete (and report)
#    anything > 30 days old.
#
##FTPCHECK    RPT          foo@node.com,bar@other.node.com    /foobar
#   Send report daily, if any files exist.  Send to two email addresses.
#
##FTPCHECK    SKP,RPT     foo@node.com            /other
#   Skip this entry regardless of other options.
#
##FTPCHECK    RPT,OLD,DEL21  Paul.Ferroni@ab.com    /incoming
#   Send report every day (if any files), marking files > 7 (default) days 
#   old as "OLD" and deleting files > 21 days old (and reporting this).
#
##FTPCHECK    OLD21,DEL21  Paul.Ferroni@ab.com    /incoming
#   Delete files > 21 days old, and send report of this.  Don't send
#   a report otherwise.  Since OLD days == DEL days, this forces a
#   report to be sent if old files found, but all "old" files are also deleted.
##################################################################

require "/usr/local/lib/perl/getopts.pl";
require "/usr/local/lib/perl/timelocal.pl";

# site dependent stuff...
$server    = "ftp.cle.ab.com";
$subj      = "Subject: $server usage";
$from      = "ftp-admin\@cle.ab.com";
$FtpRoot   = "/ftp";
$ftpaccess = "/usr/local/etc/ftpaccess";
#$ftpaccess = "/tmp/ftpaccess";	# for debug purposes
$du        = "/usr/bin/du -sk";
$find      = "/usr/bin/find";
$FindArgs  = "! \"(\" -fstype lofs -prune \")\" -type f ! -name \".*\" -ls";
$formail   = "/usr/local/bin/formail";
$mailer    = "/usr/lib/sendmail -t";
$DeBugTo   = "Paul.Ferroni\@ab.com";
# ...end of site dependent stuff (hopefully).

%months    = (Jan,0,Feb,1,Mar,2,Apr,3,May,4,Jun,5,Jul,6,Aug,7,Sep,8,Oct,9,Nov,10,Dec,11);
%rmonths   = (0,Jan,1,Feb,2,Mar,3,Apr,4,May,5,Jun,6,Jul,7,Aug,8,Sep,9,Oct,10,Nov,11,Dec);
($ls,$lm,$lh,$lday,$thismonth,$thisyear,$lwday,$lyday,$li) = localtime(time);  # get current time info

&Getopts('d');
print "Running in debug mode...\n" if $opt_d;

open (ACC, $ftpaccess) || die "Cannot open ftpaccess file\n";

while (<ACC>) {
    chop;
    next unless /^##FTPCHECK/;
    print "Raw FTPCHECK line: \n$_\n" if $opt_d;

    $skp= $rpt= $del= $old = 0; # default flags
    $OldAge= $DelAge= '';       # unset age values (cleaner for debug output)
    $file_count = 0;            # initialize file counter
    %fileinfo = ();             # initialize the array (needed after first loop)

    ($junk, $optstring, $add, $directory) = split;
    $add = $DeBugTo if $opt_d;  #   Change To: address to DeBugTo if in debug mode.

    #############################################################################
    ### parse out the $optstring....
    #
    @opts = split(/,/,$optstring);
    foreach $option (@opts) {

            # if SKP, then set skp flag.
        $skp = 1 if $option =~ "SKP";

            # if RPT, then set rpt flag.
        $rpt = 1 if $option =~ "RPT";

            # if OLD, then set old flag, and get OldAge (if supplied), or use default OldAge of "7"
        ($old=1) && ((length($option)>3) ? (($OldAge=$option) =~ s/OLD//) : ($OldAge= 7)) if $option =~ "OLD";

            # if DEL, then set del flag, and get DelAge (if supplied), or use default DelAge of "14"
        ($del=1) && ((length($option)>3) ? (($DelAge=$option) =~ s/DEL//) : ($DelAge=14)) if $option =~ "DEL";
    }

    $OldAge = $DelAge if ($del && ($DelAge < $OldAge));  # sanity check -- old date has to be <= delete date.

    print "skp: $skp  rpt: $rpt  old: $old  del: $del   OldAge: $OldAge  DelAge: $DelAge\n\n" if $opt_d;

    next if $skp;  # Skip this entry if SKP is set


    #############################################################################
    ### Build array of all files in this tree...
    #
    # Note: we skip directories and only report files.  If you have a "bin" or "dev"
    # directory (for chroot'ed users), you may need to do something here to keep from
    # reporting the contents of these directories...
    #
    # Note that if running in debug mode (-d), no files are deleted.
    #
    $old_files = 0;                                   # initialize old file counter
    $rpttime = time -  ($OldAge * 24 * 60 * 60);      # $OldAge days ago
    $deltime = time -  ($DelAge * 24 * 60 * 60);      # $DelAge days ago
    open (FIND, "$find $FtpRoot$directory $FindArgs |") || die "Cannot run find on $directory\n";
    while (<FIND>) {
        chop;
        $file_count++;    
        @fields = split;
        ($file = @fields[10]) =~ s#^$FtpRoot##;
        $filesize = @fields[6];
        $month = @fields[7];
        $day = @fields[8];
        $year = @fields[9];

        # If $year field has a ":" in it, it's less than 1 year old.
        # We want to put a two digit year in it's place for timelocal().
        # Logic:  if file month is > current month, then use previous year, else use current year.
        #
        if ($year =~ /:/) {
            $year = $thisyear;
            $year-- if (@months{$month} > $thismonth);
        }
        $year =~ s/(^19|^20)// ;    # Good for next 104 years, if timelocal() gets fixed.

        $filetime = timelocal(0,0,0,$day,@months{$month},$year);
        $filestring = "         ";                 # Default string
        if ($old  && ($filetime < $rpttime)) {     # mark files as "old" if desired
            $old_files++ ;                         # increment (set) old file flag
            $filestring = "***OLD***";
        }
        if ($del && ($filetime < $deltime))     {  # delete file if desired 
            unlink("$FtpRoot$file") unless $opt_d;   # Delete file (except when in debug mode)
            $old_files++ ;                         # increment (set) old file flag
            $filestring = "*DELETED*";
                print "Would have deleted $file (filetime: $filetime  deltime: $deltime)\n" if $opt_d;
        }

        $fileinfo{$file} = join('|',$filetime,$filesize,$file,$filestring);
    }
    close (FIND);

    # if there are no files to be reported, then skip to top of loop and go on...
    next if ($file_count == 0);

    #############################################################################
    ### Get total size for directory..
    #
    open (DU, "$du $FtpRoot$directory |") || die "Cannot get du on $directory\n";
    read (DU, $buf, 80);
    ($total_size, $junk) = split(/\t/,$buf);
    close (DU);

    #############################################################################
    ### Now open up pipe to mailer, and pump in data listing files (and message)...
    #
    # Now report the results if rpt flag is set

    if ($rpt || ($old && $old_files)) {

        open(MAIL, "| $formail -a 'To: $add' -A 'From: $from' -A 'Subject: $subj ($directory)' | $mailer")
            || die "open: Can not open mail pipe\n";

        print MAIL "DEBUG INFO:\n" if $opt_d; 
        print MAIL "    Raw options field: $optstring\n" if $opt_d;
        print MAIL "    skp: $skp  rpt: $rpt  old: $old  del: $del   OldAge: $OldAge  DelAge: $DelAge\n\n" if $opt_d;

        print MAIL << "EOM1";

Dear $server user,

  Below is a report of your usage of space on the FTP server:
$server in directory: $directory.
EOM1

        if ($old) {
        print MAIL << "EOM2";

  Your account is set to have this report generated nightly
if any files are greater than $OldAge days old.  

  Files are listed with oldest first.  Files with the flag 
"***OLD***" are more than $OldAge days old.  
EOM2
        }


        if ($del) {
        print MAIL << "EOM3";

  Files are automatically deleted (and reported to you) if 
they are greater than $DelAge days old.  Any files marked with 
"*DELETED*" have been automatically deleted.
EOM3
        }

        print MAIL "\n\n\n";  # separate the text from the data...

        # Now that we've built fileinfo array, sort and output the info...


        foreach $entry (sort values(%fileinfo)) {
            ($t, $s, $n, $h) = split(/\|/,$entry);

            ($ls,$lm,$lh,$lday,$lmon,$lyr,$lwday,$lyday,$li) = localtime($t);

            printf (MAIL "%s %-40s  size:%10s  created: %3s %2s 19%s\n",$h,$n,$s,@rmonths{$lmon},$lday,$lyr);
            printf      ("%s %-40s  size:%10s  created: %3s %2s 19%s\n",$h,$n,$s,@rmonths{$lmon},$lday,$lyr) if $opt_d;
        }

        printf ("\nTotal space used by the %s directory: %8s Kbytes in %s files\n",
            $directory, $total_size, $file_count) if $opt_d;

        printf (MAIL "\nTotal space used by the %s directory: %8s Kbytes in %s files\n",
            $directory, $total_size, $file_count);
    
        close (MAIL);

    } 

}
close (ACC);

exit;
