From: Steven Foster
Subject: Script to automate your local veronica menu (maltshop-0.2).
Apparently-To: [email protected]
Status: OR

#!/usr/local/bin/perl
#
# Copyright (C) Steven Foster  1994
#
# You are welcome to modify this software in any way you desire, for
# your use or for use AT YOUR SITE.  However, you MAY NOT redistribute
# any modified forms of this program.  To maintain consistency, please
# forward your changes, improvements and suggestions to me
# ( [email protected] ).
#
# DISCLAIMER
# No warranty is expressed or implied that this software will be suitable
# for any particular purpose, or that it will work at all. This source
# code is supplied "as-is", and you should ascertain that it is suitable
# for your purpose before running it.  Neither I nor any other person or
# institution assumes any responsibility or liability for any undesirable
# consequences of the use of this software.
#
# ----------------   What is this program?   -------------------------
#
#  This script will build a veronica-access menu on your local gopher server.
#  This does not mean you will run a veronica server of your own; rather, you
#  will have a locally-held menu of veronica servers.  The menu will be
#  updated automatically, to show only those veronica servers currently
#  operating.
#
#  Reasons to have a local veronica menu:
#
#       1.  Your users will have veronica access in spite of downtime at
#           Nevada or Minnesota.
#
#       2.  The veronica access menus at Nevada and Minnesota
#           are getting too busy.  Everyone's response time will be
#           better if gopher sites have locally-held links to the veronica
#           servers.
#
#       3.  You have the most up-to-date menu.  You can configure this
#           program to check all veronica servers as often as you like.
#           If a veronica server is down, or too slow, it will not appear
#           on your menu.
#
#  What it does:
#
#       This script runs as a cron job.  It polls a list of known, public
#       veronica servers, determining which are reachable.  It then builds a
#       link-file in the data heirarchy of your gopher server, at a location
#       you specify.  You should specify a folder called something like:
#       "Search GopherSpace using veronica".
#       We recommend that you set the crontab to run the script every
#       twenty minutes or so.
#
#       Periodically, the script will get a new, updated list of public
#       veronica servers from an authoritative site.  You can set this
#       update frequency to your liking.  The program is shipped with a
#       setting for six-day intervals.  New veronica servers do not come
#       online all that often, so this is probably often enough.
#       You can set the update interval ( in days ) by editing a variable
#       at the top of the program file.
#
#       This program needs no maintenance.  After you have edited the
#       configuration variables, it will run by itself.
#
#  INSTALLATION:
#
#       The script has been tested with perl-4.036 on Ultrix, Solaris,
#       and NeXT-OS.
#
#       Install this script in a convenient location, for instance as
#       /usr/local/bin/mkvermenu.  Edit the crontab file to run the
#       script at about 20 minute intervals.  A sample crontab entry is:
#
#       18,38,58 * * * * root /usr/local/bin/mkvermenu
#
#       There is some variation is crontab formats for various systems.
#       Check the format to be sure.
#
#       Edit the configuration options at the beginning of the program.
#       You need to specify a file name where the script can keep its
#       list of known servers, and a directory and filename for the
#       file of gopher links that constitute the veronica menu. The latter
#       directory and file must be in your gopher server's data heirarchy,
#       at the location where you want the veronica menu to appear.
#       Both these files must be writable by the PID that executes the
#       script.  If you run as root, no problem.
#       Also, specify a writable tmp directory for the script to use.
#
#       The remaining configuration variables are simple.
#       A maximum of eight server entries is enough, since each server
#       needs two lines on the menu, one for item searches and one for
#       directory-only searches.
#       Leave the six-day update period as is, unless you have a
#       good reason to change it.
#       There is an option that allows you to turn off the presentation
#       of the search-gopher-directories items on the menu, should you
#       want to do that.
#
# --------------------------------------------------------------------
# ---   Set the following paths and options for your system  ---------
#
# Full pathname for the local list of known veronica servers.
# If not present, this file will be created when program is installed.
# NOTE:  This program must have write permission to this file.
$veronicaList = '/usr/local/etc/veronica.sites';

# Directory (in your gopher menu) where you want to put the
# list of veronica servers.
# NOTE:  This program must have write permission to this directory.
$VeronicaDir   = '/usr/local/gopher-data/veronica';

# Name for the file of links that will be built in $VeronicaDir.
# NOTE:  This program must have write permission to this file.
$LinkFile = '.Links-veronica';

# readable/writable tmp directory for use by this script.
$TmpDir = '/tmp';

# Maximum number of servers you want to appear on your menu:
$MaxServers = 8;

#  Interval in days between auto-updating the veronica site list
#  This MUST be at least one.
$UpdateInterval = 6;

#  Include "Search gopher directories" searches on the links menu.
#  Set to 0 to omit directory-type searches from your menu.
$Include_gopher_dir_srch = 1;

#  Include menu items for two veronica documents on the Nevada menu.
#  Set to 0 to skip them....
$Include_docs = 1;

# Set this to see program's progress
$Debug = 0;

# -------------  End of configuration options  ---------------------------
# -------------- Don't edit the stuff below.   ---------------------------

$master_veronica_list = "veronica.scs.unr.edu  70 0/veronica/.veronica.sites\n";
$master_veronica_list2 = "gopher.tc.umn.edu  70 0/Other Gopher and Information Servers/Veronica/.veronica.sites\n";

&Initialize;
if ( ! ( $#verList >= 0 ) ) {  exit; }
if ($Debug){ print "BEFORE connect:\n"; }
if ($Debug){ print "   servers are: \n\r @verList \n"; }
#  Try the veronica servers
&Connect;

#  make the file of links
&Write;

#  move file of links into place on gopher menu
$cmd = "cp $TmpDir/verlinks.tmp  $VeronicaDir/$LinkFile";
system($cmd);
unlink ("$TmpDir/verlinks.tmp");
$rm_cache_cmd = "rm $VeronicaDir/.cac*";
system ("$rm_cache_cmd");
exit;

#==========================================================================
sub Initialize
{
   $SIG{'INT'} = 'IGNORE';
   $SIG{'ALRM'} = 'Alarm';
   require 'sys/socket.ph';
   $Date = `date`;
   chop ($Date);

   ($Name, $Aliases, $Proto) = getprotobyname('tcp');

   push ( @Listhosts, $master_veronica_list );
   push ( @Listhosts, $master_veronica_list2 );
   $sockaddr = 'S n a4 x8';

# read list of known veronica servers
   open (veronicas, "< $veronicaList");
   while (<veronicas>)
   {
     if ( /^\#/ )  {  next;  }
     if ( /^\+/ ) {  s/^\+\s*//; push ( @Listhosts, $_ ); next;  }
     if ( /^\!/ ) {  s/^\!\s*//; push ( @verList,  $_);  }
   }
   close (veronicas);

# open temp file for new links list.
   open (Link, "> $TmpDir/verlinks.tmp");
   select (Link); $| = 1; select (STDOUT);
   print Link "# This file automatically created by crontab on\n";
   print Link "# $Date\n";

#  Is it time to get a new master list of veronica sites?

#  If no file of links, this is probably first startup, so get new list
       if ( ! (-e "$VeronicaDir/$LinkFile" ))
       { &UpDateHostList; return;  }

#  If no veronica sites file, get new list
       if ( ! (-e "$veronicaList" ))
       { &UpDateHostList;  return;    }

#  If no servers were found in the veronica sites file, get new list
       if ( ! ( $#verList >= 0 ) )
       { &UpDateHostList;  return;    }

#  If sites file is too old, get new list
       if ( $UpdateInterval < 1 ) { $UpdateInterval = 6; }
       if ( ( -M $veronicaList) >= $UpdateInterval )
       { &UpDateHostList;  return;}

}  # end initialize
# ----------------------------------------------------------------------

sub UpDateHostList
{
foreach $host ( @Listhosts )
{
       ( $Lhost, $Lport, $Lpath ) = split (  /\s+/, $host, 3);
       if ($Debug) { print "list host:  $Lhost  $Lport  $Lpath"; }
       chop ( $Lpath );
       $! = '';
       if ( &OpenServer ( $Lhost, $Lport ) )
       {
       if ( !$! )
       {
               print SERVER "$Lpath\r\n";
               @NewList = <SERVER>;
               close (SERVER);
#  return must be at least two lines long
               if (  !( $#NewList >= 1) ) { next; }
#   line format must be ok
               $BADFORMAT = 0;
               foreach   (@NewList)
               {
               s/\r//;
               if ( /^\#/ )  {  next;  }
               if ( /^\./ )  {  next;  }
               if ( /^\s*$/ )  {  next;  }
               if ( /^\+/ ) {  s/^\+\s*//; push ( @NewListhosts, $_ ); next;  }
               if ( /^\!/ ) {  s/^\!\s*//; push ( @NewverList,  $_); next;  }
               $BADFORMAT = 1;
               }

               if ($BADFORMAT)  { next; }

               # Merge old and new lists of site-file-servers
               foreach $a ( @NewListhosts )
               {
                 ( $NLhost, $NLport, $NLpath ) = split (  /\s+/, $a, 3);
                 chop ($NLpath);
                 $HL{"$NLhost\t$NLport\t$NLpath"} = $a;
               }
               foreach $a ( @Listhosts )
               {
                 ( $NLhost, $NLport, $NLpath ) = split (  /\s+/, $a, 3);
                 chop ($NLpath);
                 $HL{"$NLhost\t$NLport\t$NLpath"} = $a;
               }
if ($Debug) { print "writing new veronica sites list\n"; }
               open ( TMPLIST, ">$TmpDir/newhostlist.tmp" );
               print TMPLIST "# File $veronicaList created on: \n";
               print TMPLIST "#     $Date\n";
               print TMPLIST "# Using veronica site data from:\n";
               print TMPLIST "#     $Lhost  $Lport  $Lpath\n";
               print TMPLIST "# Do not change the format of lines in this file;\n";
               print TMPLIST "# the special characters are required by the program\n";
               print TMPLIST "#\n";
               # write list of site-file-servers
               foreach $uniq ( keys ( %HL) )
               { print TMPLIST "+$HL{$uniq}";  }
               # write list of veronica servers
               foreach ( @NewverList  )
               { print TMPLIST "!$_";  }
               close (TMPLIST);
               #  working list of veronicas is now new list
               undef @verList;
               @verList = @NewverList;
               #  here copy tempfile to real sites file.
               $cmd = "cp $TmpDir/newhostlist.tmp $veronicaList";
               system($cmd);
               unlink ("$TmpDir/newhostlist.tmp");
               last;
       }
       }
       close ( SERVER );
}
}

sub OpenServer
{
   local ( $server, $port ) = @_;
   $sockaddr = 'S n a4 x8';
   (($name, $aliases, $type, $len, $saddr) = gethostbyname($server))
     || do {warn "Bad gethostbyname!\n";  return (0); };
   $sin = pack($sockaddr, &AF_INET, $port, $saddr);
   socket(SERVER, &AF_INET, &SOCK_STREAM, $Proto)
     || do {warn "Bad socket call!\n";  return (0);};
   connect ( SERVER, $sin)
     || do {warn "Bad connect call!\n";  return (0);};
   select (SERVER); $| = 1; select (STDOUT); $| = 1;
   return (1);
}


sub Connect
{
   $Slimit = ( $#verList + 1 ) - $MaxServers;
   if ($Slimit < 0) { $Slimit = 0; }
   while  ( $#verList >= $Slimit )
   {
       alarm ('40');
       $Line =  shift(@verList);
       chop $Line;
       if($Debug){ print "Now call $Line \n"; }
       next unless ($Line);

          ($Host,$Port,$Description,$Version) = split (/\s*\!\s*/, $Line, 4);
          ($Name, $Aliases, $Type, $Length, $RHostAddr) =
               gethostbyname($Host);

          $RHostSocketAddr = pack ($sockaddr, &AF_INET, $Port, $RHostAddr);

          socket (S, &AF_INET, &SOCK_STREAM, $Proto)
                   || warn "Can't create socket: $!\n";
          #bind (S, $HostSocketAddr)
          #        || warn "Can't bind socket: $!\n";

          $! = '';
          connect (S, $RHostSocketAddr);
          if (!$!)
          {
              select (S); $| = 1; select (STDOUT);
              print S "-m?\r\n";
              $Reply = <S>;
              if ($Reply =~ /^.*\t.*\t.*\t\d+/)
              {
                   push (@veronicas, "$Host $Port $Description");
              }
           }
           else
           {
               print Link "# Can't connect to $Description: $!\n";
           }
   }
   alarm(0);
}

sub Write
{
   $Numb = 0;
   if ( $Include_docs )
   {
       $Numb++;
       print Link "Type=0\n";
       print Link "Name=veronica FAQ (from Nevada)\n";
       print Link "Path=0/veronica/veronica-faq\n";
       print Link "Host=veronica.scs.unr.edu\n";
       print Link "Port=70\n";
       print Link "Numb=$Numb\n";
       print Link "#\n";
       $Numb++;
       print Link "Type=0\n";
       print Link "Name=How to compose veronica queries (from Nevada)\n";
       print Link "Path=0/veronica/how-to-query-veronica\n";
       print Link "Host=veronica.scs.unr.edu\n";
       print Link "Port=70\n";
       print Link "Numb=$Numb\n";
       print Link "#\n";
   }
   foreach (@veronicas)
   {
       next unless ($_);
       $Numb++;
       ($Host, $Port, $Description) = split (/\s+/, $_, 3);
       print Link "Type=7\n";
       print Link "Name=veronica server at $Description\n";
       print Link "Path=\n";
       print Link "Host=$Host\n";
       print Link "Port=$Port\n";
       print Link "Numb=$Numb\n";
       print Link "#\n";
   }
   if ( $Include_gopher_dir_srch )
   {
   foreach (@veronicas)
   {
       next unless ($_);
       $Numb++;
       ($Host, $Port, $Description) = split (/\s+/, $_, 3);
       print Link "Type=7\n";
       print Link "Name=Search Gopher Directory Titles at $Description\n";
       print Link "Path=-t1  \n";
       print Link "Host=$Host\n";
       print Link "Port=$Port\n";
       print Link "Numb=$Numb\n";
       print Link "#\n";
   }
   }
   close (Link);
}

sub Alarm
{
   alarm (0);
   print Link "# Alarm $Description\n";
   &Connect;
}