#!/usr/bin/perl
#
# Usage: rstat.pl
#  -l[ncsa|ms|w3c] logfile1 [logfile2 ...] [-c] [-y year] [-m month] [-d day-range]
#  [-o output file] [-z] [-ftp ftp-server] [-cfg config-file] [-shift #]
#
# -c      default to current month instead of previous month.
#         $CURRENT configuration variable.
# -cfg    load different cfg file, defaults to rstat.cfg
# -d      day range to collect statistics for defaults to 1-31.
# -l      specifies the logfiles to gather statistics from (wildcards OK)
#         and optionally format of logfiles, can be repeated.
#         Default log file format is specifed with $DEFLOG variable in rstat.cfg.
# -m      and -y specifies month and year to gather statistics for. defaults to
#         month prior to current month.
# -o      output file name, default is $SERVICE-yyyy-mm.log or
#         $SERVICE-yyyy-mm_d1-d2.log if -d option is used.
# -z      compress output, .gz added to filename. $COMPRESS configuration variable
# -ftp    username:password@ftpserver/directory
#         ftp output file to specified server. Implies -z. $FTP configuration variable.
# -shift  "Columns" to skip with NCSA log-format. Primarily useful when the server name
#         appears at the start of the log line. Use -shift 1 for this case. $SHIFT config
#         variable.
#
# version 1.1a, 2003-06-12
#    - fixed bug with sprintf
#

use strict;

#####################################################################################

my( $version ) = '1.1a';

# This is where we store the data
my( %robots, %robotreason, %robotaccess, %downloads, %ip_count );

# vars
my( $i, $j, $s, $t, $u, @t, $debug, %logs, $fullmonth, $month, $nummonth,
    $report, $year, $foundwp, $limit, $robrep, %access, $log_lines, $gz,
    $compress, $currmonth, %tot, $doftp, $dayrange, $logstyle, $configfile,
    $ftp_user, $ftp_pass, $ftp_server, $ftp_path, $ncsashift, $cangz );

print "rstat.pl version $version\n arguments: " . join( " ", @ARGV ) . "\n";

# parse arguments

my( @args ) = @ARGV;

# parse arguments
$i = 0;
undef %logs;
$report = "";
$currmonth = $compress = 0;
unless ( @args ) { goto USAGE }
while ( @args ) {
  $s = lc( shift @args );
CHKARG:
  if ( $s eq '-c' ) {
    $currmonth = 1;
    }
  elsif ( $s eq '-cfg' ) {
    $configfile = shift @args;
    }
  elsif ( $s eq '-d' ) {
    $dayrange = shift @args;
    }
  elsif ( $s eq "-debug" ) {
    $debug = 1;
    }
  elsif ( $s eq '-ftp' ) {
    $doftp = shift @args;
    }
  elsif ( $s =~ /^-l/ ) {
    if ( $s eq "-lncsa" )   { $u = "NCSA" }
    elsif ( $s eq "-lms" )  { $u = "MS" }
    elsif ( $s eq "-lw3c" ) { $u = "W3C" }
    elsif ( $s eq "-lideas" ) { $u = "IDEAS" }
    elsif ( $s =~ /^-l\S/ ) { goto USAGE }
    else                    { $u = '-default-' }
    while ( @args ) {
      $t = shift @args;
      if ( $t =~ /^-/ ) {
        $s = lc( $t );
        goto CHKARG;
        }
      push @{$logs{$u}}, glob( $t );
      }
    }
  elsif ( $s eq "-m" ) {
    $nummonth = shift @args;
    unless ( $nummonth =~ /^\d{1,2}$/ ) {
      die "-m argument must be month number\n";
      }
    if ( $nummonth < 1 or $nummonth > 12 ) {
      die "-m month number must be between 1 and 12\n";
      }
    }
  elsif ( $s eq "-o" ) {
    $report = shift @args;
    }
  elsif ( $s eq '-shift' ) {
    $ncsashift = shift @args;
    unless ( $ncsashift =~ /^\d+$/ and $ncsashift >= 0 ) {
      die "-shift argument must be numeric and non-negative";
      }
    }
  elsif ( $s eq "-y" ) {
    $year = shift @args;
    unless ( $year =~ /^\d{4,4}$/ ) {
      die "-y argument must be four digit year\n";
      }
    }
  elsif ( $s eq "-z" ) {
    $compress = 1;
    }
  else { goto USAGE }
  }

# read configuration
$configfile ||= 'rstat.cfg';
eval { require $configfile };
die "Failed reading cfg $configfile, $@" if $@;

goto CHKCONF;

USAGE:
    die <<_USG_;
Usage: $0
  -l[ncsa|ms|w3c] logfile1 [logfile2 ...] [-c] [-y year] [-m month]
  [-d day-range] [-o output file] [-z] [-ftp ftp-server] [-cfg config-file]
  [-shift #]
_USG_

CHKCONF:

unless ( $currmonth ) {
  $currmonth = $CONF::CURRMONTH if $CONF::CURRMONTH;
  }

unless ( $nummonth and $year ) {

  $t = &LocalISODate;
  @t = split( /-/, $t );
  if ( $t[1] == 1 and !$currmonth ) {
    $t[1] = 12;
    $t[0]--;
    }
  else {
    $t[1]-- unless $currmonth;
    }
  if ( $nummonth ) { $t[1] = $nummonth }
  if ( $year ) { $t[0] = $year }

  $nummonth = $t[1];
  $year = $t[0];

  }
$nummonth = sprintf( "%02d", $nummonth );

if ( $dayrange ) {
  if ( $dayrange =~ /^(\d+)-(\d+)$/ ) {
    if ( $1 < 1 or $1 > $2 or $2 > 31 ) {
      print "-d option $dayrange is invalid\n";
      goto USAGE;
      }
    }
  else {
    print "-d option $dayrange is invalid\n";
    goto USAGE;
    }
  }

unless ( $compress ) {
  $compress = $CONF::COMPRESS if $CONF::COMPRESS;
  }

unless ( $doftp ) {
  $doftp = $CONF::FTP if $CONF::FTP;
  }
if ( $doftp ) {

  if ( $doftp =~ /\@/ ) {
    $ftp_user = $`;
    $ftp_server = $';
    }
  else {
    $ftp_server = $doftp;
    }

  if ( $ftp_user =~ /:/ ) {
    $ftp_user = $`;
    $ftp_pass = $';
    }

  if ( $ftp_server =~ m!/! ) {
    $ftp_server = $`;
    $ftp_path = "/$'";
    }

  $compress = 1 if $doftp;

  }

unless ( $ncsashift ) {
  $ncsashift ||= $CONF::SHIFT if $CONF::SHIFT;
  $ncsashift ||= 0;
  }

# check for Zlib
eval "use Compress::Zlib;";
if ( $@ ) {
  print "Compress::Zlib not found\n";
  print "Not compressing output\n" if $compress;
  $cangz = 0;
  $compress = 0;
}
else {
  $cangz = 1;
}

$i = 0;
# check for logfiles
foreach $s ( keys %logs ) {
#  @t = @{$logs{$s}};
  foreach $u ( @{$logs{$s}} ) {
    if ( -e $u and !(-d $u) ) {
      $i++;
      }
    else {
      print "No log-file $u\n";
      $u = '';
      }
    }
  }
die "\nNo log files specified (-l option)\n" unless $i;

#
# Gather data from log files
#
$log_lines = 0;

foreach $s ( keys %logs ) {

  @t = @{$logs{$s}};

  foreach $u ( @t ) {

    next unless $u;

    if ( $u =~ /\.gz$/ ) {
      if ( $cangz ) {
        $gz = gzopen( $u, "rb" ) or die "Can't init inflate of $u: " . $gz->gzerror();
      }
      else {
        die "Can not read compressed log file $u\n";
      }
    }
    else {
      open( INF, '<'.$u ) or die "Can't open log file $u";
      binmode( INF );           # so we don't die on binary scrap in the file
                                # EOF, for example
      $gz = 0;
    }


    if ( $s eq '-default-' ) {
      $logstyle = $CONF::DEFLOG;
      }
    else {
      $logstyle = $s;
      }

    print "\nScanning $u for download info for $year-$nummonth\nLog-style is $logstyle\n";

    $log_lines += &CollectStats( $logstyle, $dayrange, $nummonth, $year, $gz, $u, $ncsashift );

    if ( $gz ) {
      $gz->gzclose();
      }
    else {
      close( INF );
      }

    }

  }

print "\n$log_lines log lines processed\n";

#
# Check for additional robots
#
$tot{'a'} = $tot{'d'} = 0;
$i = keys %robots;
$foundwp = keys %downloads;
$limit = $foundwp*$CONF::ROBOT_FRACTION;
$robrep = "Looking for suspect robots (limit is $limit accesses for $foundwp papers)...";
print "$robrep";
foreach $s ( keys %downloads ) { # handle
  foreach $t ( keys %{$downloads{$s}} ) { # a_type
    $tot{$t} += keys %{$downloads{$s}{$t}};
    foreach $u ( keys %{$downloads{$s}{$t}} ) { # ip-number
      $access{'a'}{$u} ||= 0;
      $access{'d'}{$u} ||= 0;
      $access{$t}{$u} += $downloads{$s}{$t}{$u};
      if ( $access{'a'}{$u}+$access{'d'}{$u} > $limit ) {
        $robots{$u} = 1;
        $robotaccess{$u} = $access{'a'}{$u}+$access{'d'}{$u};
        $robotreason{$u} = "Too many accesses";
        }
      }
    }
  }
$i = keys( %robots ) - $i;
$u = "\n$i additional robots found. Total of " . keys( %robots ) . " robots.\n";
print "$u";
$robrep .= $u;

print "\n$foundwp papers with abstract access or download\n";
print "$tot{'a'} abstract accesses\n";
print "$tot{'d'} downloads\n";

# clean up some robot stuff
#print "\nRemoving robot accesses...";
#$s = 0;
#foreach $i ( keys %downloads ) { # handle
#  foreach $j ( keys %{$downloads{$i}} ) { # a_type
#    foreach $t ( keys %{$downloads{$i}{$j}} ) { # ip-number
#      if ( $robots{$t} ) {
#        delete $downloads{$i}{$j}{$t};
#        delete $downloads{$i}{$j} unless keys %{$downloads{$i}{$j}};
#        delete $downloads{$i} unless keys %{$downloads{$i}};
#        delete $access{'a'}{$t} if $access{'a'}{$t};
#        delete $access{'d'}{$t} if $access{'d'}{$t};
#        $s++;
#        }
#      }
#    }
#  }
#$u = " $s removed\n";
#print $u;
#$robrep .= $u;


#
# print summary stats
#
unless ( $report ) {
  ( $report = $CONF::SERVICE ) =~ s/:/_/g;
  if ( $dayrange ) {
    $report .= "-$year-$nummonth"."_$dayrange.log";
    }
  else {
    $report .= "-$year-$nummonth.log";
    }
  }
if ( $compress ) {
  $report .= '.gz';
  $gz = gzopen( $report, 'wb' ) or die "Could not open output log-file $report";
  }
else {
  open ( OUT, ">$report" ) or die "Could not open output log-file $report";
  }

print "Writing summary statistics to $report\n";

# robots...
$t = "robots: " . join( " ", keys %robots ) . "\n";
if ( $compress ) {
  $gz->gzwrite( $t ) or die "Error writing line to .gz file: " . $gz->gzerror();
  }
else {
  print OUT $t;
  }

foreach $i ( sort keys %downloads ) { # handle

  $t = "$year-$nummonth $CONF::SERVICE $i";
  foreach $j ( keys %{$downloads{$i}} ) { # a_type
    $t .= " $j: " . join( " ", keys %{$downloads{$i}{$j}} ); # ip-number
    }
  $t .= "\n";
  if ( $compress ) {
    $gz->gzwrite( $t ) or die "Error writing line to .gz file: " . $gz->gzerror();
    }
  else {
    print OUT $t;
    }

  }

if ( $compress ) {
  $gz->gzclose();
  }
else {
  close( OUT );
  }

if ( $doftp ) {

  use Net::FTP;

# my $ftp = Net::FTP->new( $ftp_server ) or die $@;
# FV, 2010-04-01, added Passive mode. See mail from Sune Karlsson, Mon, 01 Mar 2010 12:34:46 +010
  my $ftp = Net::FTP->new( $ftp_server, Passive=>1 ) or die $@;
  $ftp_user = 'anonymous' unless $ftp_user;
  $ftp->login( $ftp_user, $ftp_pass ) or die "Could not log in to $ftp_server";
  if ( $ftp_path ) {
    $ftp->cwd( $ftp_path ) or die "Could not change dir to $ftp_path for server $ftp_server";
    }
  $ftp->binary() or die $@;
  $ftp->put( $report ) or die "Could not store file $report in dir $ftp_path on server $ftp_server";
  $ftp->quit();

  print "Stored $report at ftp://$ftp_server/$ftp_path\n";

  }

# avoid spurios -w warnings
$CONF::SITE_PATH = $CONF::ROBOT_FRACTION = $CONF::SITE_REDIR_SCRIPT =
$CONF::STAT_IP_EXCLUDE = $CONF::DEFLOG = '';

sub is_robot {

my( $stem, $query ) = @_;

return $stem =~ "/robots.txt";

}

#------------------------------

sub CollectStats {
# Assumes INF open for reading

# arguments
my( $logstyle, $day_range, $monthnum, $year, $gz, $infile, $ncsashift ) = @_;

# local vars
my( $l_num, $monthfound ) = ( 0, 0 );
my( $line, @line, @lin1, $date, $ipnumber, $uri_stem, $method, $status, $match,
    $t, $month, $ext, $file, $series, $type, $handle, $a_type, $firstday, $lastday,
    $referer, $uri_query, $h_date, $h_time, %w3c_field, $cookie, $i, @line2,
    $gz_status, $isrobot );
my( $noecho ) = 1;
my( $w3c_header ) = 0;

if ( defined &CONF::IS_ROBOT ) {
  $isrobot = \&CONF::IS_ROBOT;
}
else {
  $isrobot = \&is_robot;
}
$infile =~ s!^.*[\\/]!!;

if ( $day_range ) {
  $day_range =~ /(\d+)-(\d+)/;
  $firstday = $1;
  $lastday = $2;
  }

if ( $logstyle eq "NCSA" ) {
  $month = substr( &Num2Month( $monthnum ), 0, 3 );
  }
elsif ( $logstyle eq "MS" or $logstyle eq "W3C" ) {
  # do nothing right now
  }
elsif ( $logstyle eq "IDEAS" ) {
  ( $t = $monthnum ) =~ s!^0!0*!;
  $match = ( $infile =~ m!^$year-$t-(\d+)! );
  if ( $day_range and $match ) {
    $match = ( $firstday <= $1 and $lastday >= $1 );
    }
  return 0 unless $match
  }

# read first line
if ( $gz ) {
  $gz_status = $gz->gzreadline( $line );
  unless ( $gz_status > 0 or $gz->gzerror() == Z_STREAM_END() ) {
    die "Could not read first line from gz-file: $gz_status, " . $gz->gzerror();
    }
  }
else {
  $line = <INF>;
  }

READ_LOOP: while ( $line ) {

  $line = &CONF::LOG_PREPROC( $line ) if ( defined &CONF::LOG_PREPROC );

  $l_num++;

  $referer = $cookie = "-";

  if ( $logstyle eq "MS" ) {
    # MS

    @line = split( ", ", $line );
    $ipnumber = $line[0];
    $date = $line[2];
    $status = $line[10];
    $method = $line[12];
    $uri_stem = $line[13];
    $uri_query = $line[$#line-1];

    # make sure that we have parsed a full line
    next READ_LOOP unless $#line > 13;

    $match = ( $date =~ /^$year-$monthnum-(\d+)/ );
    if ( $day_range and $match ) {
      $match = ( $firstday <= $1 and $lastday >= $1 );
      }

    }
  elsif ( $logstyle eq "NCSA" ) {
    # NCSA, uncertain if this is correct

    # get date
    $line =~ s/\[([^\]]+)\]/-/ or next READ_LOOP; # weird if it doesn't match.
    $date = $1;

    $match = ( $date =~ m!(\d+)/$month/$year!i );
    if ( $day_range and $match ) {
      $match = ( $firstday <= $1 and $lastday >= $1 );
      }

    # get request
    $line =~ s/"([^"]+)"/-/ or next READ_LOOP; # weird if it doesn't match "
    $uri_stem = $1;
    # strip off protocol
    $uri_stem =~ s!\s+HTTP[/\d\.]+\s*$!!i;
    # parse out method
    if ( $uri_stem =~ s!^\s*([a-z]+)\s+!!i ) {
      $method = $1;
      }
    else {
      $method = '-';
      }
    # parse out uri-stem and uri-query
    if ( $uri_stem =~ s/^([^?]+)\?(.+)$/$1/ ) {
      $uri_query = $2;
      }
    else {
      $uri_query = '-';
      }

    # assume rest is safe to split on blank
    @lin1 = split( / /, $line );
    $ipnumber = $lin1[$ncsashift];
    $status   = $lin1[5+$ncsashift];

    }
  elsif ( $logstyle eq "W3C" ) {
    # W3C extended log file format as per http://www.w3.org/TR/WD-logfile.html
    # with modifications to fit the log file generated by IIS 4

    if ( substr($line,0,1) eq "#" ) {
      # process header records

      if ( $line =~ /date:/i ) {
        # get date

        ( $t = $' ) =~ s/^\s+//;
        $t =~ s/\s+$//;
        @lin1 = split( /\s+/, $t );
        $h_date = $lin1[0];
        $h_time = $lin1[1];
        print "W3C date header: $h_date $h_time \n";

        }
      elsif ( $line =~ /fields:/i ) {
        # get field names and set up field key in hash

        print "W3C field header on line $l_num\n";

        ( $t = $' ) =~ s/^\s+//;
        $t =~ s/\s+$//;
        @lin1 = split( /\s+/, $t );
        for ( $i = 0; $i <= $#lin1; $i++ ) {
          $w3c_field{lc($lin1[$i])} = $i;
          }

        $w3c_header = @lin1;

        }

      next READ_LOOP;

      }
    else {
      # real log line

      die "No W3C Field: header in logfile" unless $w3c_header;

      @line = split( /\s+/, $line );
      next READ_LOOP if @line < $w3c_header; # not all fields present in line

      $ipnumber  = $w3c_field{'c-ip'}         ? $line[$w3c_field{'c-ip'}]         : "-";
      $date      = $w3c_field{'date'}         ? $line[$w3c_field{'date'}]         : $h_date;
      $status    = $w3c_field{'sc-status'}    ? $line[$w3c_field{'sc-status'}]    : "-";
      $method    = $w3c_field{'cs-method'}    ? $line[$w3c_field{'cs-method'}]    : "-";
      $uri_stem  = $w3c_field{'cs-uri-stem'}  ? $line[$w3c_field{'cs-uri-stem'}]  : "-";
      $uri_query = $w3c_field{'cs-uri-query'} ? $line[$w3c_field{'cs-uri-query'}] : "-";

      # fixup if we didn't get what we wanted
      if ( $ipnumber eq '-' ) {
        $ipnumber = $w3c_field{'c-dns'}      ? $line[$w3c_field{'c-dns'}]        : "-";
        }
      if ( $uri_stem eq '-' ) {
        $t        = $w3c_field{'cs-uri'}     ? $line[$w3c_field{'cs-uri'}]        : "-";
        next READ_LOOP if $t eq '-';
        if ( $t =~ /\?/ ) {
          $uri_stem = $`;
          $uri_query = $';
          }
        else {
          $uri_stem = $t;
          $uri_query = '-';
          }
        }

      $match = ( $date =~ /^$year-$monthnum-(\d+)/ );
      if ( $day_range and $match ) {
        $match = ( $firstday <= $1 and $lastday >= $1 );
        }

      }

    }
  elsif ( $logstyle eq 'IDEAS' ) {

    # split on blank and ', '
    @line = split( /,* /, $line );
    # deal with , without blank
    for ( $i=0; $i < @line; $i++ ) {
      if ( $line[$i] =~ /,/ ) {
        @line2 = split( /,/, $line[$i] );
        splice( @line, $i, 1, @line2 );
        }
      }

    do { print $line; next } if ( @line < 2 );
    next if ( $line[0] =~ /unknown/i );
    do { print "1: $line"; next } if ( $line[0] !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ );
    if ( @line == 3 ) {
      $ipnumber = $line[0];
      $uri_query = $line[1];
      }
    else {
      $ipnumber = shift @line;
      # ip-numbers passed on by proxys and caches? Eat them.
      while ( $line[0] =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|unknown)$/i ) { shift @line }
      do { print "2: $line"; next } if @line != 2;
      $uri_query = $line[0];
      }

    $uri_stem = 'paper';
    $method = 'GET';
    $status = '200';

    }
  else {

    die "Unknown type of log file: $logstyle";

    }

  $noecho || print "ip=$ipnumber met=$method req=$uri_stem q=$uri_query stat=$status dat=$date\n";

  # check that it is the right month
  if ( $match ) {

    print "Log for $year-$monthnum found on line $l_num\n" unless $monthfound;

    $monthfound = 1;

    # find robots
    if ( &$isrobot( $uri_stem, $uri_query ) ) {
      unless( $ipnumber =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ ) {
        $ipnumber = &DNS_Cache($ipnumber) || $ipnumber;
        # make sure we get something back
#        next READ_LOOP unless $ipnumber =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
#        keep track of robots even when DNS fails
        }
      $robots{$ipnumber}++;
      $robotreason{$ipnumber} = "robots.txt";
      $robotaccess{$ipnumber} ||= 0;
#      next READ_LOOP;
      }

    # count robots
    if ( $robots{$ipnumber} ) {
      $robotaccess{$ipnumber}++;
      }

    # only GET
    next READ_LOOP unless ( uc($method) eq "GET" );

    # only successes and redirects
    unless ( $status =~ /[0-9]+/ ) { print "\n$line\n" }
    next READ_LOOP unless ( $status =~ /^(?:200|206|301|302|304)$/ );

    # Find the type of URL we have and reject non wp-url's

    # URL-decode stem and query first
    $uri_stem =~ s/%([\da-fA-F]{2})/chr(hex($1))/eg;
    $uri_query =~ s/%([\da-fA-F]{2})/chr(hex($1))/eg;

    if ( $uri_stem =~ m!^$CONF::SITE_PATH!io ) {

      # html page or download from this server

      ( $handle, $a_type ) = &CONF::Path_2_handle( $uri_stem, $uri_query );

      next READ_LOOP unless $handle;

      }
    elsif ( $uri_stem =~ m!^$CONF::SITE_REDIR_SCRIPT$!io ) {
      # redirected download from other server

      ( $handle, $a_type ) = &CONF::Redir_2_handle( $uri_stem, $uri_query );

      next READ_LOOP unless $handle;

      }
    else {
      next READ_LOOP;
      }

    # Get ip-number, this is potentially expensive, so we do this last
    # and pointless given that we have IP numbers in the log
    unless( $ipnumber =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ ) {
      $ipnumber = &DNS_Cache($ipnumber) || $ipnumber;
      # make sure we get something back
#      next READ_LOOP unless $ipnumber =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
#      don't reject hosts where DNS fails
      }

    next READ_LOOP if $ipnumber =~ /^$CONF::STAT_IP_EXCLUDE$/;

    # get statistics
    $downloads{lc($handle)}{$a_type}{$ipnumber}++;
    $ip_count{$ipnumber}++;

    }

  elsif ( $monthfound ) {
    # we have passed the current month
    print "Log data for $date found at line $l_num, skipping rest of file\n";
    last READ_LOOP;
    }

  } # read logfile
continue {
  if ( $gz ) {
    $gz_status = $gz->gzreadline( $line );
    unless ( $gz_status > 0 ) {
      return ( $l_num ) if $gz->gzerror() == Z_STREAM_END();
      die "Could not read line $l_num from gz-file: $gz_status, " . $gz->gzerror();
      }
    }
  else {
    $line = <INF>;
    }
  }

return ( $l_num );

}

#---------------------------------------
sub DNS_Cache {

my( $ip ) = @_;

use vars qw(%WP_stat_DNS_Cache);

$WP_stat_DNS_Cache{$ip} || ( $WP_stat_DNS_Cache{$ip} = &DNS( $ip ) || $ip );

}

#-------------------------------------

sub DNS {

  # returns blank if lookup fails

  my ( $name ) = @_;

  my ( $ipnum );

  unless ( $name =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ ) {
    join( ".", unpack( 'C4', ( gethostbyname($name) || "" ) ) );
    }
  else { "" }

}

#---------------------------------------

sub Month2Num {

my( $month ) = @_;

if    ( $month =~ /^jan/i ) { "01" }
elsif ( $month =~ /^feb/i ) { "02" }
elsif ( $month =~ /^mar/i ) { "03" }
elsif ( $month =~ /^apr/i ) { "04" }
elsif ( $month =~ /^may/i ) { "05" }
elsif ( $month =~ /^jun/i ) { "06" }
elsif ( $month =~ /^jul/i ) { "07" }
elsif ( $month =~ /^aug/i ) { "08" }
elsif ( $month =~ /^sep/i ) { "09" }
elsif ( $month =~ /^oct/i ) { "10" }
elsif ( $month =~ /^nov/i ) { "11" }
elsif ( $month =~ /^dec/i ) { "12" }

}

#---------------------------------------

sub Num2Month {

my( $num ) = @_;

my( @months ) = ( "January", "February", "March", "April", "May", "June", "July",
                  "August", "September", "October", "November", "December" );

$months[$num-1];

}

#---------------------------------------

sub LocalISODate {

my( @date );

if ( @_ ) {
  @date = localtime $_[0];
  }
else {
  @date = localtime;
  }

return $date[5]+1900 . sprintf( "-%02d", $date[4]+1 ) .
       sprintf( "-%02d", $date[3] );

}
