/*****************************************************************************/
/*
                               ProxyMaint.c

See PROXYCACHE.C for commentary.  This module implements both the ROUTINE and
REACTIVE purge maintenance for the proxy file cache.  See PROXYCACHE.C module
for information on cache management strategy.

Routine proxy maintenance can be initiated from the command line.

  /PROXY=PURGE=BACKGROUND[=ProxyCachePurgeList]
  /PROXY=PURGE=REACTIVE[=ProxyCachePurgeList]
  /PROXY=PURGE=ROUTINE[=ProxyCachePurgeList]
  /PROXY=CACHE=STATISTICS


VERSION HISTORY
---------------
28-APR-2018  MGD  refactor Admin..() AST delivery
18-JAN-2010  JPP  bugfix; ProxyMaintInit() use v10orPrev10() for scan spec
19-AUG-2007  MGD  use PercentOf() to avoid divide-by-zero exceptions
12-APR-2005  MGD  ProxyMaintSupervisor() return if caching not enabled
04-SEP-2004  MGD  PROXYCACHE.C v9.0.0 cache file header date/time usage 
30-APR-2004  MGD  use QIO to erase cache file (save a few cycles)
13-JAN-2004  MGD  DECC 6.2 objected to '$DESCRIPTOR(name,ptr->string)'
11-JUN-2003  MGD  bugfix; ProxyMaintDeviceStats() volume count (set) handling
11-MAY-2003  MGD  proxy unknown request fields
29-APR-2003  MGD  add proxy cache device error count statistics
02-APR-2003  MGD  maintain 'ProxyXForwardedFor',
                  modify for 'ProxyForwarded'
20-FEB-2003  MGD  ProxytMaintSupervisor() used instead of timers,
                  background purge (set with '[ProxyCacheRoutineHourOfDay] 24')
03-JUN-2002  MGD  bugfix; ensure sys$search() RMS channel is released
15-MAY-2002  MGD  proxy gateway statistics
11-APR-2002  MGD  make a reactive purge initially more agressive,
                  bugfix; switch return not break with next reactive scan
04-APR-2002  MGD  add command-line and menu STOP to cache scans,
                  update admin and monitor status string with scan progress,
                  bugfix; command-line cache maintenance reporting
02-FEB-2002  MGD  rework POSTed query due to request body processing changes
22-SEP-2001  MGD  InstanceLock/UnLock() to control access to cache
04-AUG-2001  MGD  support module WATCHing
07-MAY-2001  MGD  monitor global section accounting changes
05-APR-2001  MGD  add boolean to prevent ProxyMaintCacheLock() lock status
                  block overwriting if multiple scans intiated from Admin Menu
20-DEC-2000  MGD  routine proxy maintainence optionally disabled/external
13-SEP-2000  MGD  ProxyMaintReport() call refined to optionally provide
                  host name cache entries
08-JUL-2000  MGD  add VMS locking around cache scan (for clusters)
04-MAR-2000  MGD  use FaolToBuffer(), et.al.
03-JAN-2000  MGD  no changes required for ODS-5 compliance ... it's not
                  (see note in PROXYCACHE.C)
30-DEC-1999  MGD  change $GETDVI() to $GETDVIW() (potential bugfix)
04-DEC-1999  MGD  rework startup cache device and report details
22-OCT-1999  MGD  inaccessable cache device non-fatal during startup
20-JUN-1999  MGD  allow for cache devices >9GB in space calculations,
                  some refinement to statistics report
19-AUG-1998  MGD  initial development (recommenced DEC 1998)
*/
/*****************************************************************************/

#ifdef WASD_VMS_V7
#undef _VMS__V6__SOURCE
#define _VMS__V6__SOURCE
#undef __VMS_VER
#define __VMS_VER 70000000
#undef __CRTL_VER
#define __CRTL_VER 70000000
#endif

/* standard C header files */
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

/* VMS related header files */
#include <atrdef.h>
#include <descrip.h>
#include <dvidef.h>
#include <fibdef.h>
#include <iodef.h>
#include <lkidef.h>
#include <lckdef.h>
#include <prvdef.h>
#include <rmsdef.h>
#include <ssdef.h>
#include <stsdef.h>

/* application-related header files */
#include "wasd.h"

#define WASD_MODULE "PROXYMAINT"

/******************/
/* global storage */
/******************/

char ErrorProxyMaintTooManyDevices [] = "Volume set has too many members.";

BOOL  ProxyMaintChangeToReactive,
      ProxyMaintStopScan;

int  ProxyMaintAllocBlocks,
     ProxyMaintBackgroundFileCount,
     ProxyMaintBackgroundInterval,
     ProxyMaintBackgroundPurgeCount,
     ProxyMaintDeletedAllocBlocks,
     ProxyMaintDeletedCount,
     ProxyMaintDeletedUsedBlocks,
     ProxyMaintFileAllocBlocks,
     ProxyMaintFileCount,
     ProxyMaintFileUsedBlocks,
     ProxyMaintPurgeAtHour,
     ProxyMaintPurgeHoursIndex,
     ProxyMaintReactivePurgeCount,
     ProxyMaintResultFileNameLength,
     ProxyMaintRoutinePurgeCount,
     ProxyMaintRoutineHoursIndex,
     ProxyMaintScanFileSpecLength,
     ProxyMaintScanType,
     ProxyMaintStatScanCount,
     ProxyMaintTargetPercent;
     ProxyMaintUsedBlocks;

unsigned long  ProxyMaintScanStartTime64 [2];

char  *ProxyMaintScanFileSpec;

char  ProxyMaintEraseFileName [256],
      ProxyMaintExpandedFileName [256],
      ProxyMaintStatusStringBckGrnd [128] = "<i>none</i>",
      ProxyMaintStatusStringReactive [196] = "<i>none</i>",
      ProxyMaintStatusStringRoutine [128] = "<i>none</i>",
      ProxyMaintStatusStringStatScan [128] = "<i>none</i>",
      ProxyMaintResultFileName [256];

struct FAB  ProxyMaintSearchFab;
struct NAM  ProxyMaintSearchNam;

PROXY_CACHE_FILE_QIO  ProxyMaintCacheFileQio;

/********************/
/* external storage */
/********************/

extern BOOL  ProxyCacheEnabled,
             ProxyCacheFreeSpaceAvailable,
             ProxyReportLog,
             ProxyReportCacheLog,
             ProxyServingEnabled,
             ProxyUnknownRequestFields;

extern int  EfnWait,
            EfnNoWait,
            HttpdTickSecond,
            OpcomMessages,
            ProxyCacheDeviceCheckMinutes,
            ProxyCacheDeviceMaxPercent,
            ProxyCacheDevicePurgePercent,
            ProxyCacheDeviceTargetPercent,
            ProxyCacheFileKBytesMax,
            ProxyCacheNegativeSeconds,
            ProxyCacheNoReloadSeconds,
            ProxyCachePurgeList[],
            ProxyCachePurgeListCount,
            ProxyCacheRoutineHourOfDay,
            ProxyConnectPersistMax,
            ProxyConnectPersistSeconds,
            ProxyConnectTimeoutSeconds,
            ProxyForwardedBy,
            ProxyHostLookupRetryCount,
            ProxyNetConnectCount,
            ProxyVerifyRecordMax,
            ProxyXForwardedFor;

extern int  ProxyCacheReloadList[],
            ToLowerCase[],
            ToUpperCase[];

extern char  CliProxyMaint[],
             ErrorSanityCheck[],
             ServerHostPort[];

extern char  *ProxyCacheReloadListPtr,
             *ProxyCachePurgeListPtr;

extern unsigned short HttpdNumTime[];

extern ACCOUNTING_STRUCT  *AccountingPtr;
extern CONFIG_STRUCT  Config;
extern MSG_STRUCT  Msgs;
extern PROXY_ACCOUNTING_STRUCT  *ProxyAccountingPtr;
extern PROXYVERIFY_GBLSEC  *ProxyVerifyGblSecPtr;
extern WATCH_STRUCT  Watch;

/****************************************************************************/
/*
Initialize maintainence values.
*/

ProxyMaintInit ()

{
   int  status;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY, "ProxyMaintInit()");

   ProxyMaintScanFileSpec = v10orPrev10(PROXY_CACHE_SPEC,-1);
   ProxyMaintScanFileSpecLength = strlen(ProxyMaintScanFileSpec);

   ProxyCacheFreeSpaceAvailable =
      ProxyMaintDeviceFreeSpace (ProxyCacheDeviceMaxPercent);
   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
   ProxyAccountingPtr->FreeSpaceAvailable = ProxyCacheFreeSpaceAvailable;
   if (!ProxyAccountingPtr->StatusString[0] ||
       strstr (ProxyAccountingPtr->StatusString, "in-progress"))
      strcpy (ProxyAccountingPtr->StatusString, "(none)");
   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   /* if space available schedule a purge, else if exceeded begin a purge */
   if (ProxyCacheFreeSpaceAvailable)
      ProxyMaintPrintDeviceStats (true);
   else
      ProxyMaintScanBegin (PROXY_MAINT_SCAN_REACTIVE);

   /* disable if not sensible */
   if (ProxyCacheRoutineHourOfDay < 0)
      ProxyCacheRoutineHourOfDay = -1;
   if (ProxyCacheRoutineHourOfDay > 24)
      ProxyCacheRoutineHourOfDay = -1;

   if (ProxyCacheRoutineHourOfDay == 24)
      FaoToStdout ("%HTTPD-I-PROXYMAINT, background purge active\n");
   else
   if (ProxyCacheRoutineHourOfDay < 0)
      FaoToStdout ("%HTTPD-I-PROXYMAINT, routine purge disabled\n");
}

/****************************************************************************/
/*
Initiate some proxy maintainance from the command line.
*/

ProxyMaintCli ()

{
   BOOL  ScanBackground,
         ScanRoutine,
         ScanReactive,
         ScanStatistics;
   int  ScanType;
   char  *cptr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY, "ProxyMaintCli()");

   ScanRoutine = ScanReactive = ScanStatistics = false;
   if ((ScanBackground = strsame (CliProxyMaint, "PURGE=BACKGROUND", 16)) ||
       (ScanRoutine = strsame (CliProxyMaint, "PURGE=ROUTINE", 13)) ||
       (ScanReactive = strsame (CliProxyMaint, "PURGE=REACTIVE", 14)) ||
       (ScanStatistics = strsame (CliProxyMaint, "CACHE=STATISTICS", 16)))
   {
      /* read the server configuration file (for OPCOM & proxy settings) */
      ConfigLoad (&Config);
      /* now configure up the proxy settings (disabled is OK here) */
      ProxyCacheInit (false);
      /* we'll assume it's always enabled */
      ProxyCacheEnabled = true;
      /* for CLI maintainance we always want messages on SYS$OUTPUT  */
      ProxyReportLog = true;

      if (ScanBackground)
      {
         /* override the configuration purge list if CLI one provided */
         if (CliProxyMaint[16] == '=')
            ProxyCachePurgeListPtr = CliProxyMaint + 16;
         ScanType = PROXY_MAINT_SCAN_BCKGRND;
      }
      else
      if (ScanRoutine)
      {
         /* override the configuration purge list if CLI one provided */
         if (CliProxyMaint[13] == '=')
            ProxyCachePurgeListPtr = CliProxyMaint + 14;
         ScanType = PROXY_MAINT_SCAN_ROUTINE;
      }
      else
      if (ScanReactive)
      {
         /* override the configuration purge list if CLI one provided */
         if (CliProxyMaint[14] == '=')
         {
            cptr = ProxyCachePurgeListPtr = CliProxyMaint + 15;
            while (*cptr && *cptr != '%') cptr++;
            if (*cptr && cptr > ProxyCachePurgeListPtr)
            {
               ProxyCacheDeviceTargetPercent = atoi(ProxyCachePurgeListPtr);
               if (ProxyCacheDeviceTargetPercent <= 0 ||
                   ProxyCacheDeviceTargetPercent >= 100)
               {
                  FaoToStdout ("%HTTPD-I-PROXYMAINT, device percentage problem\n");
                  exit (STS$K_ERROR | STS$M_INHIB_MSG);
               }
               while (*cptr && !isdigit(*cptr)) cptr++;
               ProxyCachePurgeListPtr = cptr;
            }
         }
         ScanType = PROXY_MAINT_SCAN_REACTIVE;
      }
      else
      if (ScanStatistics)
      {
         ProxyCachePurgeListPtr = NULL;
         ScanType = PROXY_MAINT_SCAN_STATISTICS;
      }

      if (ProxyCachePurgeListPtr)
      {
         ProxyCacheInitPurgeList ();
         if (ProxyCachePurgeListPtr[0] == '*')
         {
            FaoToStdout ("%HTTPD-I-PROXYMAINT, purge list problem\n");
            exit (STS$K_ERROR | STS$M_INHIB_MSG);
         }
      }

      /* if the scan starts OK then hibernate waiting for it to finish */
      if (VMSok (ProxyMaintScanBegin (ScanType))) sys$hiber ();

      exit (SS$_NORMAL);
   }

   fprintf (stdout, "%%HTTPD-E-IVKEYW, unrecognized keyword\n \\%s\\\n",
            CliProxyMaint);
   exit (STS$K_ERROR | STS$M_INHIB_MSG);
}

/****************************************************************************/
/*
Print cache device statistics to stdout.
*/

ProxyMaintPrintDeviceStats (BOOL PrintToLog)

{
   int  status,
        ErrorCount,
        FreeBlocks,
        FreeMBytes,
        FreePercent,
        TotalMBytes,
        TotalBlocks,
        UsedBlocks,
        UsedMBytes,
        UsedPercent;
   char  *DevNamePtr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY,
                 "ProxyMaintPrintDeviceStats() !UL", PrintToLog);

   status = ProxyMaintDeviceStats (&DevNamePtr, &TotalBlocks,
                                   &UsedBlocks, &FreeBlocks, &ErrorCount);
   if (VMSok (status))
   {
      TotalMBytes = PROXY_MAINT_DEVICE_MBYTES(TotalBlocks);
      UsedMBytes = PROXY_MAINT_DEVICE_MBYTES(UsedBlocks);
      FreeMBytes = PROXY_MAINT_DEVICE_MBYTES(FreeBlocks);
      UsedPercent = PercentOf(UsedBlocks,TotalBlocks);
      FreePercent = PercentOf(FreeBlocks,TotalBlocks);
   }
   else
      TotalBlocks = TotalMBytes = UsedBlocks = UsedMBytes =
         UsedPercent = FreeBlocks = FreeMBytes = FreePercent = 0;
      
   if (PrintToLog || ProxyReportLog)
      FaoToStdout (
"%HTTPD-I-PROXYMAINT, cache device !AZ, !UL error!%s, !&S\n\
!UL blocks (!ULMB), !UL used (!ULMB !UL%), !UL free (!ULMB !UL%)\n",
         DevNamePtr, ErrorCount, status,
         TotalBlocks, TotalMBytes,
         UsedBlocks, UsedMBytes, UsedPercent,
         FreeBlocks, FreeMBytes, FreePercent);

   if (OpcomMessages & OPCOM_PROXY_MAINT)
      FaoToOpcom (
"%HTTPD-I-PROXYMAINT, cache device !AZ, !&S\r\n\
!UL blocks (!ULMB), !UL used (!ULMB !UL%), !UL free (!ULMB !UL%)",
         DevNamePtr, status,
         TotalBlocks, TotalMBytes,
         UsedBlocks, UsedMBytes, UsedPercent,
         FreeBlocks, FreeMBytes, FreePercent);
}

/****************************************************************************/
/*
Include cache device statistics in WATCH report.
*/

ProxyMaintWatchDeviceStats ()

{
   int  status,
        ErrorCount,
        FreeBlocks,
        FreeMBytes,
        FreePercent,
        TotalMBytes,
        TotalBlocks,
        UsedBlocks,
        UsedMBytes,
        UsedPercent;
   char  *DevNamePtr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY,
                 "ProxyMaintWatchDeviceStats()");

   status = ProxyMaintDeviceStats (&DevNamePtr, &TotalBlocks,
                                   &UsedBlocks, &FreeBlocks, &ErrorCount);
   if (VMSok (status))
   {
      TotalMBytes = PROXY_MAINT_DEVICE_MBYTES(TotalBlocks);
      UsedMBytes = PROXY_MAINT_DEVICE_MBYTES(UsedBlocks);
      FreeMBytes = PROXY_MAINT_DEVICE_MBYTES(FreeBlocks);
      UsedPercent = PercentOf(UsedBlocks,TotalBlocks);
      FreePercent = PercentOf(FreeBlocks,TotalBlocks);
   }
   else
      TotalBlocks = TotalMBytes = UsedBlocks = UsedMBytes =
         UsedPercent = FreeBlocks = FreeMBytes = FreePercent = 0;

#if WATCH_CAT

   WatchDataFormatted (
"!AZ !UL error!%s !&S \
!UL blocks (!ULMB), !UL used (!ULMB !UL%), !UL free (!ULMB !UL%)\n", 
      DevNamePtr, ErrorCount, status,
      TotalBlocks, TotalMBytes,
      UsedBlocks, UsedMBytes, UsedPercent,
      FreeBlocks, FreeMBytes, FreePercent);

#endif /* WATCH_CAT */
}

/****************************************************************************/
/*
Return information about file system space on the proxy cache device.  Will
function correctly with volume sets of up to eight members.  Returns a VMS
status code that should be checked for success.
*/

int ProxyMaintDeviceStats
(
char **DevNamePtrPtr,
int *TotalBlocksPtr,
int *UsedBlocksPtr,
int *FreeBlocksPtr,
int *ErrorCountPtr
)
{
#define PROXY_MAINT_CACHE_DEVICE_MAX 8

   static int  DeviceCount,
               ErrCnt,
               FreeBlocks,
               MaxBlock,
               VolCount;
   static unsigned short  Length;
   static short  DevChannel [PROXY_MAINT_CACHE_DEVICE_MAX];
   static char  CacheDevName [65],
                DevName [65];
   static $DESCRIPTOR (DevNameDsc, "");
   static struct {
      short BufferLength;
      short ItemCode;
      void  *BufferPtr;
      void  *LengthPtr;
   }
   DevNamItemList [] = 
   {
      { sizeof(CacheDevName), DVI$_DEVNAM, &CacheDevName, &Length },
      { sizeof(VolCount), DVI$_VOLCOUNT, &VolCount, 0 },
      { 0, 0, 0, 0 }
   },
   NextDevNamItemList [] = 
   {
      { sizeof(DevName), DVI$_NEXTDEVNAM, &DevName, &Length },
      { 0, 0, 0, 0 }
   },
   BlocksItemList [] = 
   {
      { sizeof(MaxBlock), DVI$_MAXBLOCK, &MaxBlock, 0 },
      { sizeof(FreeBlocks), DVI$_FREEBLOCKS, &FreeBlocks, 0 },
      { sizeof(ErrCnt), DVI$_ERRCNT, &ErrCnt, 0 },
      { 0, 0, 0, 0 }
   };

   int  idx,
        status,
        ErrorCount,
        TotalBlocks,
        TotalFreeBlocks,
        TotalUsedBlocks;
   IO_SB  IOsb;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY, "ProxyMaintDeviceStats()");

   if (DevNamePtrPtr) *DevNamePtrPtr = DevName;
   if (TotalBlocksPtr) *TotalBlocksPtr = 0;
   if (UsedBlocksPtr) *UsedBlocksPtr = 0;
   if (FreeBlocksPtr) *FreeBlocksPtr = 0;
   if (ErrorCountPtr) *ErrorCountPtr = 0;

   if (!DeviceCount)
   {
      /**************/
      /* initialize */
      /**************/

      DevNameDsc.dsc$a_pointer = v10orPrev10(PROXY_CACHE_ROOT,-1);
      DevNameDsc.dsc$w_length = strlen(DevNameDsc.dsc$a_pointer);

      if (DevNamePtrPtr) *DevNamePtrPtr = DevNameDsc.dsc$a_pointer;

      /* assign a channel to the cache device (or primary if a volume set) */
      if (VMSnok (status =
          sys$assign (&DevNameDsc, &DevChannel[DeviceCount], 0, 0)))
         return (status);

      DeviceCount++;

      status = sys$getdviw (EfnWait, DevChannel[0], 0,
                            &DevNamItemList, &IOsb, 0, 0, 0);
      if (VMSok (status)) status = IOsb.Status;
      if (VMSnok (status)) return (status);

      CacheDevName[Length] = '\0';
      if (CacheDevName[0] == '_')
         memmove (CacheDevName, CacheDevName+1, Length);

      /* loop assigning a channel to all devices in volume set (if it is!) */
      while (--VolCount)
      {
         if (DeviceCount >= PROXY_MAINT_CACHE_DEVICE_MAX)
            ErrorExitVmsStatus (0, ErrorProxyMaintTooManyDevices, FI_LI);

         status = sys$getdviw (EfnWait, DevChannel[0], 0,
                               &NextDevNamItemList, &IOsb, 0, 0, 0);
         if (VMSok (status)) status = IOsb.Status;
         if (VMSnok (status)) return (status);

         DevName[Length] = '\0';

         if (!Length) break;

         DevNameDsc.dsc$w_length = Length;
         DevNameDsc.dsc$a_pointer = DevName;

         if (VMSnok (status =
             sys$assign (&DevNameDsc, &DevChannel[DeviceCount++], 0, 0)))
            return (status);
      }
   }

   /***********/
   /* process */
   /***********/

   ErrorCount = TotalBlocks = TotalFreeBlocks = 0;

   for (idx = 0; idx < DeviceCount; idx++)
   {
      status = sys$getdviw (EfnWait, DevChannel[idx], 0,
                            &BlocksItemList, &IOsb, 0, 0, 0);
      if (VMSok (status)) status = IOsb.Status;
      if (VMSnok (status)) return (status);

      TotalBlocks += MaxBlock;
      TotalFreeBlocks += FreeBlocks;
      ErrorCount += ErrCnt;
   }

   TotalUsedBlocks = TotalBlocks - TotalFreeBlocks;

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY,
                 "!UL !UL !UL !ULMB !ULMB !ULMB !UL% !UL%",
                  TotalBlocks, TotalFreeBlocks, TotalUsedBlocks,
                  TotalBlocks >> 11, TotalFreeBlocks >> 11,
                  TotalUsedBlocks >> 11,
                  TotalFreeBlocks*100/TotalBlocks, 
                  TotalUsedBlocks*100/TotalBlocks);

   if (DevNamePtrPtr) *DevNamePtrPtr = CacheDevName;
   if (TotalBlocksPtr) *TotalBlocksPtr = TotalBlocks;
   if (UsedBlocksPtr) *UsedBlocksPtr = TotalUsedBlocks;
   if (FreeBlocksPtr) *FreeBlocksPtr = TotalFreeBlocks;
   if (ErrorCountPtr) *ErrorCountPtr = ErrorCount;

   return (SS$_NORMAL);
}

/****************************************************************************/
/*
If the percentage of used space on the cache device is greater than or equal to
the specified percentage then return true, otherwise return false.
*/

BOOL ProxyMaintDeviceFreeSpace (int LimitPercent)

{
   int  status,
        UsedBlocks,
        UsedPercent,
        TotalBlocks;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY,
                 "ProxyMaintDeviceFreeSpace() !UL", LimitPercent);

   if (VMSok (status =
       ProxyMaintDeviceStats (NULL, &TotalBlocks, &UsedBlocks, NULL, NULL)))
   {
      UsedPercent = PercentOf(UsedBlocks,TotalBlocks);

      if (UsedPercent > LimitPercent)
         return (false);
      else
         return (true);
   }
   else
   {
      FaoToStdout (
"%HTTPD-W-PROXYMAINT, !20%D, cache device problem - PROXY CACHING DISABLED\n\
-!&M\n",
         0, status);

      if (OpcomMessages & OPCOM_PROXY_MAINT)
         FaoToOpcom (
"%HTTPD-W-PROXYMAINT, cache device problem - PROXY CACHING DISABLED\r\n\
-!&M",
         status);

      /* fudge ... indicate sufficient space, suppresses any purging */
      return (true);
   }
}

/*****************************************************************************/
/*
Called by HttpdTick() every minute (or so).   This routine schedules the
approriate proxy cache purge management activities.
*/

ProxyMaintSupervisor ()

{
   static int  SupervisorSecond,
               RoutineYear,
               RoutineMonth,
               RoutineDay;

   int  status;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY, "ProxyMaintSupervisor()");

   if (ProxyServingEnabled) ProxyTunnelLogicalName (NULL);

   if (!ProxyServingEnabled || !ProxyCacheEnabled) return;

   if (SupervisorSecond < HttpdTickSecond)
   {
      /******************/
      /* reactive purge */
      /******************/

      ProxyCacheFreeSpaceAvailable =
         ProxyMaintDeviceFreeSpace (ProxyCacheDeviceMaxPercent);
      InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
      ProxyAccountingPtr->FreeSpaceAvailable = ProxyCacheFreeSpaceAvailable;
      InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

      if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
      {
         if (ProxyCacheFreeSpaceAvailable)
            WatchThis (WATCHALL, WATCH_PROXY_CACHE,
                       "MAINT sufficient free SPACE !UL% max",
                       ProxyCacheDeviceMaxPercent);
         else
            WatchThis (WATCHALL, WATCH_PROXY_CACHE,
                       "MAINT INSUFFICIENT free SPACE !UL% max",
                       ProxyCacheDeviceMaxPercent);
         ProxyMaintWatchDeviceStats ();
      }

      /* if insufficient free space */
      if (!ProxyCacheFreeSpaceAvailable)
      {
         if (ProxyMaintScanType == PROXY_MAINT_SCAN_NONE)
         {
            /* can purge now */
            ProxyMaintScanBegin (PROXY_MAINT_SCAN_REACTIVE);
         }
         else
         if (ProxyMaintScanType == PROXY_MAINT_SCAN_BCKGRND)
         {
            /* after background has stopped */
            ProxyMaintChangeToReactive = true;
         }
      }
   }

   if (ProxyCacheRoutineHourOfDay >= 0 &&
       ProxyCacheRoutineHourOfDay <= 23)
   {
      /*****************/
      /* routine purge */
      /*****************/

      /* only if during the first five minutes of that hour */
      if (ProxyCacheRoutineHourOfDay == HttpdNumTime[3] &&
          (RoutineDay != HttpdNumTime[2] ||
           RoutineMonth != HttpdNumTime[1] ||
           RoutineYear != HttpdNumTime[0]) &&
          HttpdNumTime[4] <= 5)
      {
         RoutineYear = HttpdNumTime[0];
         RoutineMonth = HttpdNumTime[1];
         RoutineDay = HttpdNumTime[2];

         /* if no other scan is underway then start the routine one */
         if (ProxyMaintScanType == PROXY_MAINT_SCAN_NONE)
            ProxyMaintScanBegin (PROXY_MAINT_SCAN_ROUTINE);
      }
   }
   else
   if (ProxyCacheRoutineHourOfDay == 24)
   {
      /********************/
      /* background purge */
      /********************/

      /* check for background purge at same time and rate as reactive  */
      if (SupervisorSecond < HttpdTickSecond)
      {
         /* if another scan is not already underway start one */
         if (ProxyMaintScanType == PROXY_MAINT_SCAN_NONE)
            ProxyMaintScanBegin (PROXY_MAINT_SCAN_BCKGRND);
      }
   }

   if (SupervisorSecond < HttpdTickSecond)
      SupervisorSecond = HttpdTickSecond +
                         ProxyCacheDeviceCheckMinutes * 60;
}

/*****************************************************************************/
/*
Queue a wait for the apprpriate milliseconds before continuing on to the next
file in the cache. There are 86400 seconds in a 24 hour period.  At one per
second we can therefore scan 86400 files in that day (more or less).  At two
per second 172,800 per day.  At four per second 345,600 files per day.  And so
on.  Returns false if ProxyMaintScanSearch() should continue with the file
scan, true if it should wait.  When the timer expires the AST calls
ProxyMaintScanSearch() to continue with the scan.
*/

BOOL ProxyMaintBackgroundTimer ()

{
   static BOOL  FlipFlop;
   static int  mSec,
               PrevFileCount = -1;
   static unsigned long  DelayDelta [2];

   static struct RateTableStruct
   {
      int  min,
           max,
           mSec;
   }
   RateTable [] = 
   {
      {       0,    4320,  250 },  /* the default rate (4 per second) */
      {    4321,   33200, 2000 },  /* 1 every 2 seconds */
      {   33201,   86400, 1000 },  /* 1 per second */
      {   86401,  172800,  500 },  /* 2 per second */
      {  172801,  259200,  333 },  /* 3 per second */
      {  259201,  345600,  250 },  /* 4 per second */
      {  345601,  432000,  200 },  /* 5 per second */
      {  432001,  518400,  167 },  /* 6 per second */
      {  518401,  604800,  143 },  /* 7 per second */
      {  604801,  691200,  125 },  /* 8 per second */
      {  691201,  777600,  111 },  /* 9 per second */
      {  777601,  864000,  100 },  /* 10 per second */
      {  864001, 1728000,   50 },  /* 20 per second */
      { 1728001, 3456000,   25 },  /* 40, highly unlikely :^) */
      {       0,       0,    0 }   /* sentinal */
   };

   int  idx, status;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY,
                 "ProxyMaintBackgroundTimer() !&B !UL", FlipFlop, mSec);

   /* only every alternate call delays */
   if (FlipFlop = !FlipFlop) return (false);

   if (PrevFileCount != ProxyMaintBackgroundFileCount)
   {
      PrevFileCount = ProxyMaintBackgroundFileCount;

      /* scan through the rate table looking for a matching number of files */
      for (idx = 0; RateTable[idx].mSec; idx++)
         if (ProxyMaintBackgroundFileCount >= RateTable[idx].min &&
             ProxyMaintBackgroundFileCount < RateTable[idx].max)
            break;
      mSec = ProxyMaintBackgroundInterval = RateTable[idx].mSec;

      if (WATCH_MODULE(WATCH_MOD_PROXY))
         WatchThis (WATCHALL, WATCH_MOD_PROXY,
                    "!UL >= !UL < !UL !SL mS",
                    RateTable[idx].min, ProxyMaintBackgroundFileCount,
                    RateTable[idx].max, mSec);

      /* this is one millisecond delta */
      DelayDelta[0] = -10000;
      DelayDelta[1] = -1;
      /* multiply by the number of milliseconds between each file */
      if (VMSnok (status = lib$mult_delta_time (&mSec, &DelayDelta)))
         ErrorExitVmsStatus (status, NULL, FI_LI);
   }

   /* if matching rate not found (exceeds maximum) go like the clappers */
   if (!mSec) return (false);

   /* queue up a timer event delaying the next file check */
   if (VMSnok (status =
       sys$setimr (0, &DelayDelta, &ProxyMaintScanSearch, 0, 0)))
      ErrorExitVmsStatus (status, NULL, FI_LI);

   return (true);
}

/****************************************************************************/
/*
Initialize ready to begin a routine or purge pass.
*/

int ProxyMaintScanBegin (int ScanType)

{
   static BOOL  NotEnabledMessage;

   int  status;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY,
                 "ProxyMaintScanBegin() !UL", ScanType);

   if (!ProxyCacheEnabled)
   {
      if (NotEnabledMessage) return (STS$K_ERROR);
      NotEnabledMessage = true;
      if (ProxyReportLog)
         FaoToStdout ("%HTTPD-W-PROXYMAINT, !20%D, cache not enabled\n", 0);
      return (STS$K_ERROR);
   }
   NotEnabledMessage = false;

   if (ScanType == PROXY_MAINT_SCAN_REACTIVE &&
       ProxyMaintScanType == PROXY_MAINT_SCAN_BCKGRND)
   {
      /****************************/
      /* after background stopped */
      /****************************/

      ProxyMaintChangeToReactive = true;
      return (SS$_NORMAL);
   }

   if (ScanType == PROXY_MAINT_SCAN_STOP)
   {
      /************/
      /* whooaaa! */
      /************/

      ProxyMaintStopScan = true;
      return (SS$_NORMAL);
   }

   /* attempt to obtain the cache scan lock */
   status = InstanceLockNoWait (INSTANCE_CLUSTER_PROXYMAINT);
   if (status == SS$_NOTQUEUED)
   {
      if (ProxyMaintScanType == PROXY_MAINT_SCAN_BCKGRND)
      {
         /* background scans don't report such startup failures */
         return (STS$K_ERROR);
      }
      if (ProxyReportLog)
         FaoToStdout ("%HTTPD-W-PROXYMAINT, !20%D, cache already locked\n",
                      &ProxyMaintScanStartTime64);
      if (OpcomMessages & OPCOM_PROXY_MAINT)
         FaoToOpcom ("%HTTPD-W-PROXYMAINT, cache already locked");
      return (STS$K_ERROR);
   }
   if (VMSnok (status))
      ErrorExitVmsStatus (status, "InstanceLock()", FI_LI);

   /* only useful after the scan has started ;^) */
   ProxyMaintStopScan = false;

   sys$gettim (&ProxyMaintScanStartTime64);

   ProxyMaintScanType = ScanType;
   ProxyMaintTargetPercent = ProxyCacheDeviceTargetPercent;

   ProxyMaintPurgeHoursIndex = 0;
   /* if reactive purge we start a little more aggressively */
   if (ProxyMaintScanType == PROXY_MAINT_SCAN_REACTIVE)
      if (ProxyMaintPurgeHoursIndex < ProxyCachePurgeListCount)
         ProxyMaintPurgeHoursIndex++;
   ProxyMaintPurgeAtHour = ProxyCachePurgeList[ProxyMaintPurgeHoursIndex];

   ProxyMaintDeletedCount =
      ProxyMaintDeletedAllocBlocks =
      ProxyMaintDeletedUsedBlocks =
      ProxyMaintAllocBlocks =
      ProxyMaintUsedBlocks = 0;

   switch (ProxyMaintScanType)
   {
      case PROXY_MAINT_SCAN_STATISTICS :

         /****************************/
         /* getting cache statistics */
         /****************************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
         {
             WatchThis (WATCHALL, WATCH_PROXY_CACHE,
                        "MAINT begin STATISTICS SCAN");
             ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            FaoToStdout ("%HTTPD-I-PROXYMAINT, !20%D, begin statistics scan\n",
                         &ProxyMaintScanStartTime64);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            FaoToOpcom ("%HTTPD-I-PROXYMAINT, begin statistics scan");

         ProxyMaintPrintDeviceStats (false);
         ProxyMaintStatScanCount++;
         ProxyMaintScanNow ();
         return (SS$_NORMAL);

      case PROXY_MAINT_SCAN_REACTIVE :

         /********************************/
         /* purging to create free space */
         /********************************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
         {
             WatchThis (WATCHALL, WATCH_PROXY_CACHE,
"MAINT begin REACTIVE PURGE (>!UL hours, !UL% max, !UL% target)",
                ProxyMaintPurgeAtHour,  ProxyCacheDeviceMaxPercent,
                ProxyMaintTargetPercent);
             ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            FaoToStdout (
"%HTTPD-I-PROXYMAINT, !20%D, begin reactive purge\n\
(>!UL hours, !UL% max, !UL% target)\n",
               &ProxyMaintScanStartTime64,
               ProxyMaintPurgeAtHour,  ProxyCacheDeviceMaxPercent,
               ProxyMaintTargetPercent);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            FaoToOpcom (
"%HTTPD-I-PROXYMAINT, begin reactive purge\r\n\
(>!UL hours, !UL% max, !UL% target)",
                ProxyMaintPurgeAtHour,  ProxyCacheDeviceMaxPercent,
                ProxyMaintTargetPercent);

         ProxyMaintPrintDeviceStats (false);
         ProxyMaintReactivePurgeCount++;
         ProxyMaintScanNow ();
         return (SS$_NORMAL);

      case PROXY_MAINT_SCAN_ROUTINE :

         /*******************/
         /* routine cleanup */
         /*******************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
         {
             WatchThis (WATCHALL, WATCH_PROXY_CACHE,
                "MAINT begin ROUTINE PURGE (>!UL hours)",
                ProxyMaintPurgeAtHour);
             ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            FaoToStdout (
"%HTTPD-I-PROXYMAINT, !20%D, begin routine purge (>!UL hours)\n",
               &ProxyMaintScanStartTime64, ProxyMaintPurgeAtHour);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            FaoToOpcom (
"%HTTPD-I-PROXYMAINT, begin routine purge (>!UL hours)",
               ProxyMaintPurgeAtHour);

         ProxyMaintPrintDeviceStats (false);
         ProxyMaintRoutinePurgeCount++;
         ProxyMaintScanNow ();
         return (SS$_NORMAL);

      case PROXY_MAINT_SCAN_BCKGRND :

         /**********************/
         /* background cleanup */
         /**********************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
         {
             WatchThis (WATCHALL, WATCH_PROXY_CACHE,
                "MAINT begin BACKGROUND PURGE (>!UL hours)",
                ProxyMaintPurgeAtHour);
             ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            FaoToStdout (
"%HTTPD-I-PROXYMAINT, !20%D, begin background purge (>!UL hours)\n",
               &ProxyMaintScanStartTime64, ProxyMaintPurgeAtHour);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            FaoToOpcom (
"%HTTPD-I-PROXYMAINT, begin background purge (>!UL hours)",
               ProxyMaintPurgeAtHour);

         InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
         ProxyMaintBackgroundFileCount = ProxyAccountingPtr->PrevFileCount;
         InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

         ProxyMaintPrintDeviceStats (false);
         ProxyMaintBackgroundPurgeCount++;
         ProxyMaintScanNow ();
         return (SS$_NORMAL);
   }

   ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

/****************************************************************************/
/*
End of a single pass through the entire cache directory tree.  A PURGE will be
made up of multiple passes, each using the next access hours found in the
'ProxyCachePurgeList' array.  A ROUTINE purge will only make the one pass
though the tree using only the hours in the zero element.
*/

ProxyMaintScanEnd ()

{
   int  status,
        FreeBlocks,
        FreePercent,
        TotalBlocks;
   unsigned long  CurrentTime64 [2],
                  DeltaTime [2];
   char  *cptr;
   PROXY_CACHE_FILE_QIO  *cfqptr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY, "ProxyMaintScanEnd()");

   /* first ensure release of search channel by doing a syntax-only parse */
   ProxyMaintSearchFab.fab$l_fna = "a:[b]c.d;";
   ProxyMaintSearchFab.fab$b_fns = 9;
   ProxyMaintSearchFab.fab$b_dns = 0;
   ProxyMaintSearchFab.fab$l_fop &= ~FAB$M_ASY;

   ProxyMaintSearchNam.nam$l_esa = ProxyMaintExpandedFileName;
   ProxyMaintSearchNam.nam$b_ess = sizeof(ProxyMaintExpandedFileName)-1;
   ProxyMaintSearchNam.nam$l_rlf = 0;
   ProxyMaintSearchNam.nam$b_nop = NAM$M_SYNCHK;

   status = sys$parse (&ProxyMaintSearchFab, 0, 0);

   cfqptr = &ProxyMaintCacheFileQio;
   if (cfqptr->AcpChannel)
   {
      sys$dassgn (cfqptr->AcpChannel);
      cfqptr->AcpChannel = 0;
   }

   if (ProxyMaintStopScan)
   {
      cptr = "&nbsp; <b>(manually STOPed)</b>";

      if (ProxyReportLog)
         FaoToStdout ("%HTTPD-I-PROXYMAINT, !20%D, cache scan manually STOPed\n", 0);

      if (OpcomMessages & OPCOM_PROXY_MAINT)
         FaoToOpcom ("%HTTPD-I-PROXYMAINT, cache scan manually STOPed");

      ProxyCacheFreeSpaceAvailable =
         ProxyMaintDeviceFreeSpace (ProxyCacheDeviceMaxPercent);
   }
   else
   {
      cptr = "";
      ProxyCacheFreeSpaceAvailable =
         ProxyMaintDeviceFreeSpace (ProxyMaintTargetPercent);
   }

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
   ProxyAccountingPtr->FreeSpaceAvailable = ProxyCacheFreeSpaceAvailable;
   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   sys$gettim (&CurrentTime64);
   lib$sub_times (&CurrentTime64, &ProxyMaintScanStartTime64, &DeltaTime);

   switch (ProxyMaintScanType)
   {
      case PROXY_MAINT_SCAN_STATISTICS :

         /*******************/
         /* cach statistics */
         /*******************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
         {
            WatchThis (WATCHALL, WATCH_PROXY_CACHE,
               "MAINT end STATISTICS SCAN !UL files (!UL/!UL blocks)",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks);
            ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            FaoToStdout (
"%HTTPD-I-PROXYMAINT, !20%D, end statistics scan\n\
!UL files (!UL/!UL)\n",
               0, ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            FaoToOpcom (
"%HTTPD-I-PROXYMAINT, end statistics scan\r\n\
!UL files (!UL/!UL)",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks);
         ProxyMaintPrintDeviceStats (false);

         FaoToBuffer (ProxyMaintStatusStringStatScan,
                      sizeof(ProxyMaintStatusStringStatScan), NULL,
                      "!UL, !20%D (!%T)<br>!UL files (!UL/!UL)!AZ",
                      ProxyMaintStatScanCount,
                      &ProxyMaintScanStartTime64, &DeltaTime,
                      ProxyMaintFileCount, ProxyMaintUsedBlocks,
                      ProxyMaintAllocBlocks, cptr);
         {
            /* don't use WASD functions to write into the locked areas */
            $DESCRIPTOR (FaoDsc, "!AZ, !UL files (!UL/!UL)\0");
            $DESCRIPTOR (StringDsc, ""); 

            StringDsc.dsc$w_length =
               sizeof(ProxyAccountingPtr->StatusString)-1;
            StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
            InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
            sys$fao (&FaoDsc, NULL, &StringDsc,
                     DigitDayTime(0), ProxyMaintFileCount,
                     ProxyMaintUsedBlocks, ProxyMaintAllocBlocks);
            InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
         }

         break;

      case PROXY_MAINT_SCAN_REACTIVE :

         /********************************/
         /* purging to create free space */
         /********************************/

         /* if reached desired percent, or one complete purge, or no files */
         if (ProxyCacheFreeSpaceAvailable ||
             ProxyMaintPurgeHoursIndex >= ProxyCachePurgeListCount ||
             !ProxyMaintFileCount ||
             ProxyMaintStopScan)
         {
            /*****************/
            /* cease purging */
            /*****************/

            if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
            {
               WatchThis (WATCHALL, WATCH_PROXY_CACHE,
"MAINT end REACTIVE PURGE \
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
            }

            if (ProxyReportLog)
               FaoToStdout (
"%HTTPD-I-PROXYMAINT, !20%D, end reactive purge\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)\n",
                  0, ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks,
                  ProxyMaintDeletedAllocBlocks);

            if (OpcomMessages & OPCOM_PROXY_MAINT)
               FaoToOpcom (
"%HTTPD-I-PROXYMAINT, end reactive purge\r\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)",
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks,
                  ProxyMaintDeletedAllocBlocks);
            ProxyMaintPrintDeviceStats (false);

            FaoToBuffer (ProxyMaintStatusStringReactive,
                         sizeof(ProxyMaintStatusStringReactive), NULL, 
"!UL, !20%D (!%T)<br>!UL files (!UL/!UL), !UL deleted (!UL/!UL)!AZ",
                         ProxyMaintReactivePurgeCount,
                         &ProxyMaintScanStartTime64, &DeltaTime,
                         ProxyMaintFileCount, ProxyMaintUsedBlocks,
                         ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                         ProxyMaintDeletedUsedBlocks,
                         ProxyMaintDeletedAllocBlocks, cptr);
            {
               /* don't use WASD functions to write into the locked areas */
               $DESCRIPTOR (FaoDsc,
"!AZ, !UL files (!UL/!UL), !UL deleted (!UL/!UL)\0");
               $DESCRIPTOR (StringDsc, ""); 

               StringDsc.dsc$w_length =
                  sizeof(ProxyAccountingPtr->StatusString)-1;
               StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
               InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
               sys$fao (&FaoDsc, NULL, &StringDsc,
                        DigitDayTime(0), ProxyMaintFileCount,
                        ProxyMaintUsedBlocks, ProxyMaintAllocBlocks,
                        ProxyMaintDeletedCount, ProxyMaintDeletedUsedBlocks,
                        ProxyMaintDeletedAllocBlocks);
               InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
            }
            break;
         }

         /**********************/
         /* next pass of purge */
         /**********************/

         ProxyMaintPurgeAtHour =
            ProxyCachePurgeList[++ProxyMaintPurgeHoursIndex];

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
         {
            WatchThis (WATCHALL, WATCH_PROXY_CACHE,
"MAINT end pass REACTIVE PURGE \
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);

            WatchThis (WATCHALL, WATCH_PROXY_CACHE,
               "MAINT next REACTIVE PURGE >!UL hours !UL% max !UL% target",
               ProxyMaintPurgeAtHour, ProxyCacheDeviceMaxPercent,
               ProxyMaintTargetPercent);

            ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            FaoToStdout (
"%HTTPD-I-PROXYMAINT, !20%D, end reactive pass\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)\n\
next pass >!UL hours !UL% max !UL% target\n",
               0, ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks,
               ProxyMaintPurgeAtHour,  ProxyCacheDeviceMaxPercent,
               ProxyMaintTargetPercent);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            FaoToOpcom (
"%HTTPD-I-PROXYMAINT, end reactive pass\r\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)\r\n\
next pass >!UL hours !UL% max !UL% target",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks,
               ProxyMaintPurgeAtHour,  ProxyCacheDeviceMaxPercent,
               ProxyMaintTargetPercent);

         ProxyMaintPrintDeviceStats (false);
         {
            /* don't use WASD functions to write into the locked areas */
            $DESCRIPTOR (FaoDsc,
"!AZ, REACTIVE purge, >!UL hours, !UL% max, !UL% target\0");
            $DESCRIPTOR (StringDsc, ""); 

            StringDsc.dsc$w_length =
               sizeof(ProxyAccountingPtr->StatusString)-1;
            StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
            InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
            sys$fao (&FaoDsc, NULL, &StringDsc,
                     DigitDayTime(0), ProxyMaintPurgeAtHour,
                     ProxyCacheDeviceMaxPercent, ProxyMaintTargetPercent);
            InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
         }

         /* reset these, we want to accumulate the deleted totals */
         ProxyMaintAllocBlocks = ProxyMaintUsedBlocks = 0;

         ProxyMaintScanNow ();

         /* we're doing another scan, so we don't break! */
         return;

      case PROXY_MAINT_SCAN_ROUTINE :

         /*******************/
         /* routine cleanup */
         /*******************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
         {
            WatchThis (WATCHALL, WATCH_PROXY_CACHE,
"MAINT end ROUTINE PURGE \
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
            ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            FaoToStdout (
"%HTTPD-I-PROXYMAINT, !20%D, end routine purge\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)\n",
               0, ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            FaoToOpcom (
"%HTTPD-I-PROXYMAINT, end routine purge\r\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks);

         ProxyMaintPrintDeviceStats (false);

         FaoToBuffer (ProxyMaintStatusStringRoutine,
                      sizeof(ProxyMaintStatusStringRoutine), NULL, 
"!UL, !20%D (!%T)<br>!UL files (!UL/!UL), !UL deleted (!UL/!UL)!AZ",
                      ProxyMaintRoutinePurgeCount,
                      &ProxyMaintScanStartTime64, &DeltaTime,
                      ProxyMaintFileCount, ProxyMaintUsedBlocks,
                      ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                      ProxyMaintDeletedUsedBlocks,
                      ProxyMaintDeletedAllocBlocks, cptr);
         {
            /* don't use WASD functions to write into the locked areas */
            $DESCRIPTOR (FaoDsc,
"!AZ, !UL files (!UL/!UL), !UL deleted (!UL/!UL)\0");
            $DESCRIPTOR (StringDsc, ""); 

            StringDsc.dsc$w_length =
               sizeof(ProxyAccountingPtr->StatusString)-1;
            StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
            InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
            sys$fao (&FaoDsc, NULL, &StringDsc,
                      DigitDayTime(0), ProxyMaintFileCount,
                      ProxyMaintUsedBlocks, ProxyMaintAllocBlocks,
                      ProxyMaintDeletedCount, ProxyMaintDeletedUsedBlocks,
                      ProxyMaintDeletedAllocBlocks);
            InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
         }

         break;

      case PROXY_MAINT_SCAN_BCKGRND :

         /**********************/
         /* background cleanup */
         /**********************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
         {
            WatchThis (WATCHALL, WATCH_PROXY_CACHE,
"MAINT end BACKGROUND PURGE \
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
            ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            FaoToStdout (
"%HTTPD-I-PROXYMAINT, !20%D, end background purge\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)\n",
               0, ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            FaoToOpcom (
"%HTTPD-I-PROXYMAINT, end background purge\r\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks);

         ProxyMaintPrintDeviceStats (false);

         if (!ProxyMaintChangeToReactive && !ProxyMaintStopScan)
            FaoToBuffer (ProxyMaintStatusStringBckGrnd,
                         sizeof(ProxyMaintStatusStringBckGrnd), NULL, 
"!UL, !20%D (!%T)<br>!UL files (!UL/!UL), !UL deleted (!UL/!UL)!AZ",
                         ProxyMaintBackgroundPurgeCount,
                         &ProxyMaintScanStartTime64, &DeltaTime,
                         ProxyMaintFileCount, ProxyMaintUsedBlocks,
                         ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                         ProxyMaintDeletedUsedBlocks,
                         ProxyMaintDeletedAllocBlocks, cptr);
         {
            /* don't use WASD functions to write into the locked areas */
            $DESCRIPTOR (FaoDsc,
"!AZ, !UL files (!UL/!UL), !UL deleted (!UL/!UL)\0");
            $DESCRIPTOR (StringDsc, ""); 

            StringDsc.dsc$w_length =
               sizeof(ProxyAccountingPtr->StatusString)-1;
            StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
            InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
            sys$fao (&FaoDsc, NULL, &StringDsc,
                      DigitDayTime(0), ProxyMaintFileCount,
                      ProxyMaintUsedBlocks, ProxyMaintAllocBlocks,
                      ProxyMaintDeletedCount, ProxyMaintDeletedUsedBlocks,
                      ProxyMaintDeletedAllocBlocks);
            InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
         }

         break;

      default :
         ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
   }

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
   ProxyAccountingPtr->PrevFileCount = ProxyMaintFileCount;
   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   /* release the cache lock */
   if (VMSnok (status = InstanceUnLock (INSTANCE_CLUSTER_PROXYMAINT)))
      ErrorExitVmsStatus (status, "InstanceUnLock()", FI_LI);

   if (ProxyMaintScanType == PROXY_MAINT_SCAN_BCKGRND)
   {
      if (ProxyMaintChangeToReactive)
      {
         ProxyMaintChangeToReactive = false;
         ProxyMaintScanType = PROXY_MAINT_SCAN_NONE;
         ProxyMaintScanBegin (PROXY_MAINT_SCAN_REACTIVE);
         return;
      }
   }

   ProxyMaintStopScan = false;
   ProxyMaintScanType = PROXY_MAINT_SCAN_NONE;

   /* if from the command-line wake the main thread */
   if (CliProxyMaint[0]) sys$wake (0, 0);
}

/****************************************************************************/
/*
Initialize the RMS structures ready for the file search.
*/

ProxyMaintScanNow ()

{
   int  status;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY,
                 "ProxyMaintScanNow() !&Z", ProxyMaintScanFileSpec);

   ProxyMaintScanFileSpec = v10orPrev10(PROXY_CACHE_SPEC,-1);
   ProxyMaintSearchFab = cc$rms_fab;
   ProxyMaintSearchFab.fab$l_fna = ProxyMaintScanFileSpec;
   ProxyMaintSearchFab.fab$b_fns = ProxyMaintScanFileSpecLength;
   ProxyMaintSearchFab.fab$l_nam = &ProxyMaintSearchNam;

   /* initialize the NAM block */
   ProxyMaintSearchNam = cc$rms_nam;
   ProxyMaintSearchNam.nam$l_esa = ProxyMaintExpandedFileName;
   ProxyMaintSearchNam.nam$b_ess = sizeof(ProxyMaintExpandedFileName)-1;
   ProxyMaintSearchNam.nam$l_rsa = ProxyMaintResultFileName;
   ProxyMaintSearchNam.nam$b_rss = sizeof(ProxyMaintResultFileName)-1;

   status = sys$parse (&ProxyMaintSearchFab, 0, 0);
   if (VMSnok (status))
   {
      if (WATCH_MODULE(WATCH_MOD_PROXY))
         WatchThis (WATCHALL, WATCH_MOD_PROXY,
                    "sys$parse() !&S", status);
      ProxyMaintScanEnd ();
      return;
   }

   if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
      WatchThis (WATCHALL, WATCH_PROXY_CACHE,
         "SCAN !AZ !AZ", ProxyMaintScanFileSpec, ProxyMaintExpandedFileName);

   ProxyMaintFileCount = 0;

   /* now we've done the parse do the searching asynchronously */
   ProxyMaintSearchFab.fab$l_fop = FAB$M_ASY;

   ProxyMaintScanSearch ();
}

/****************************************************************************/
/*
Simply queue another asynchronous RMS search.
*/

ProxyMaintScanSearch ()

{
   int  status;
   unsigned long  CurrentTime64 [2],
                  DeltaTime [2];

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY, "ProxyMaintScanSearch()");

   if (ProxyMaintStopScan ||
       ProxyMaintChangeToReactive)
   {
      ProxyMaintScanEnd ();
      return;
   }

   switch (ProxyMaintScanType)
   {
      case PROXY_MAINT_SCAN_BCKGRND :

         /* if we should wait before continuing with the next file */
         if (ProxyMaintBackgroundTimer ()) return;

         /* just continue  */
         break;

      case PROXY_MAINT_SCAN_ROUTINE :
      case PROXY_MAINT_SCAN_STATISTICS :

         /* just continue through the complete scan */
         break;

      case PROXY_MAINT_SCAN_REACTIVE :

         /* only recheck free space after every ten files deleted */
         if (ProxyMaintFileCount &&
             (!ProxyMaintDeletedCount || ProxyMaintDeletedCount % 10))
            break;

         if (ProxyMaintDeviceFreeSpace (ProxyMaintTargetPercent))
         {
            /* reached our desired limit, cease purging */
            ProxyMaintScanEnd ();
            return;
         }
         break;

      default :
         ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
   }

   if (ProxyMaintFileCount % PROXY_MAINT_SCAN_MONITOR_FILES == 0)
   {
      sys$gettim (&CurrentTime64);
      lib$sub_times (&CurrentTime64, &ProxyMaintScanStartTime64, &DeltaTime);

      switch (ProxyMaintScanType)
      {
         case PROXY_MAINT_SCAN_STATISTICS :

            if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
            {
               WatchThis (WATCHALL, WATCH_PROXY_CACHE,
                  "MAINT STATISTICS SCAN !UL files (!UL/!UL blocks)",
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks);
               ProxyMaintWatchDeviceStats ();
            }

            FaoToBuffer (ProxyMaintStatusStringStatScan,
                         sizeof(ProxyMaintStatusStringStatScan), NULL,
"!UL, !20%D (!%T)<br>!UL files (!UL/!UL)",
                         ProxyMaintStatScanCount,
                         &ProxyMaintScanStartTime64, &DeltaTime,
                         ProxyMaintFileCount, ProxyMaintUsedBlocks,
                         ProxyMaintAllocBlocks);
            {
               /* don't use WASD functions to write into the locked areas */
               $DESCRIPTOR (FaoDsc,
"!AZ, STATISTICS, !UL (!UL/!UL)\0");
               $DESCRIPTOR (StringDsc, ""); 

               StringDsc.dsc$w_length =
                  sizeof(ProxyAccountingPtr->StatusString)-1;
               StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
               InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
               sys$fao (&FaoDsc, NULL, &StringDsc,
                        DigitDayTime(&ProxyMaintScanStartTime64),
                        ProxyMaintFileCount, ProxyMaintUsedBlocks,
                        ProxyMaintAllocBlocks);
               InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
            }
            break;

         case PROXY_MAINT_SCAN_REACTIVE :

            if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
            {
               WatchThis (WATCHALL, WATCH_PROXY_CACHE,
"MAINT REACTIVE !UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
               ProxyMaintWatchDeviceStats ();
            }

            FaoToBuffer (ProxyMaintStatusStringReactive,
                         sizeof(ProxyMaintStatusStringReactive), NULL,
"!UL, !20%D (!%T), >!UL hours, !UL% max, !UL% target<br>\
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                         ProxyMaintReactivePurgeCount,
                         &ProxyMaintScanStartTime64, &DeltaTime,
                         ProxyMaintPurgeAtHour, ProxyCacheDeviceMaxPercent,
                         ProxyMaintTargetPercent,
                         ProxyMaintFileCount, ProxyMaintUsedBlocks,
                         ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                         ProxyMaintDeletedUsedBlocks,
                         ProxyMaintDeletedAllocBlocks);
            {
               /* don't use WASD functions to write into the locked areas */
               $DESCRIPTOR (FaoDsc,
                            "!AZ, REACTIVE, !UL (!UL/!UL) - !UL (!UL/!UL)\0");
               $DESCRIPTOR (StringDsc, ""); 

               StringDsc.dsc$w_length =
                  sizeof(ProxyAccountingPtr->StatusString)-1;
               StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
               InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
               sys$fao (&FaoDsc, NULL, &StringDsc,
                        DigitDayTime(&ProxyMaintScanStartTime64),
                        ProxyMaintFileCount, ProxyMaintUsedBlocks,
                        ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                        ProxyMaintDeletedUsedBlocks,
                        ProxyMaintDeletedAllocBlocks);
               InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
            }
            break;

         case PROXY_MAINT_SCAN_ROUTINE :

            if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
            {
               WatchThis (WATCHALL, WATCH_PROXY_CACHE,
"MAINT ROUTINE !UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
               ProxyMaintWatchDeviceStats ();
            }

            FaoToBuffer (ProxyMaintStatusStringRoutine,
                         sizeof(ProxyMaintStatusStringRoutine), NULL,
"!UL, !20%D (!%T)<br>\
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                         ProxyMaintRoutinePurgeCount,
                         &ProxyMaintScanStartTime64, &DeltaTime,
                         ProxyMaintFileCount, ProxyMaintUsedBlocks,
                         ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                         ProxyMaintDeletedUsedBlocks,
                         ProxyMaintDeletedAllocBlocks);
            {
               /* don't use WASD functions to write into the locked areas */
               $DESCRIPTOR (FaoDsc,
"!AZ, ROUTINE, !UL (!UL/!UL) - !UL (!UL/!UL)\0");
               $DESCRIPTOR (StringDsc, ""); 

               StringDsc.dsc$w_length =
                  sizeof(ProxyAccountingPtr->StatusString)-1;
               StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
               InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
               sys$fao (&FaoDsc, NULL, &StringDsc,
                        DigitDayTime(&ProxyMaintScanStartTime64),
                        ProxyMaintFileCount, ProxyMaintUsedBlocks,
                        ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                        ProxyMaintDeletedUsedBlocks,
                        ProxyMaintDeletedAllocBlocks);
               InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
            }
            break;

         case PROXY_MAINT_SCAN_BCKGRND :

            if (WATCH_CATEGORY(WATCH_PROXY_CACHE))
            {
               WatchThis (WATCHALL, WATCH_PROXY_CACHE,
"MAINT BACKGROUND !UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
               ProxyMaintWatchDeviceStats ();
            }

            FaoToBuffer (ProxyMaintStatusStringBckGrnd,
                         sizeof(ProxyMaintStatusStringBckGrnd), NULL,
"!UL, !20%D (!%T)<br>\
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks) !UL mSec",
                         ProxyMaintBackgroundPurgeCount,
                         &ProxyMaintScanStartTime64, &DeltaTime,
                         ProxyMaintFileCount, ProxyMaintUsedBlocks,
                         ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                         ProxyMaintDeletedUsedBlocks,
                         ProxyMaintDeletedAllocBlocks,
                         ProxyMaintBackgroundInterval);
            {
               /* don't use WASD functions to write into the locked areas */
               $DESCRIPTOR (FaoDsc,
"!AZ, BACKGROUND, !UL (!UL/!UL) - !UL (!UL/!UL)\0");
               $DESCRIPTOR (StringDsc, ""); 

               StringDsc.dsc$w_length =
                  sizeof(ProxyAccountingPtr->StatusString)-1;
               StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
               InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
               sys$fao (&FaoDsc, NULL, &StringDsc,
                        DigitDayTime(&ProxyMaintScanStartTime64),
                        ProxyMaintFileCount, ProxyMaintUsedBlocks,
                        ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                        ProxyMaintDeletedUsedBlocks,
                        ProxyMaintDeletedAllocBlocks);
               InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
            }
            break;

         default :
            ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
      }
   }

   status = sys$search (&ProxyMaintSearchFab, &ProxyMaintScanSearchAst,
                                              &ProxyMaintScanSearchAst);
}

/****************************************************************************/
/*
AST from sys$search().  Check status and report any error.  Otherwise generate
and queue an asynchronous ACP file information QIO.
*/

ProxyMaintScanSearchAst (struct FAB *FabPtr)

{
   static $DESCRIPTOR (DeviceDsc, "");

   int  status;
   ATRDEF  *atptr;
   PROXY_CACHE_FILE_QIO  *cfqptr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY,
                 "ProxyMaintScanSearchAst() !&F !&X !&X",
                 &ProxyMaintScanSearchAst,
                 FabPtr->fab$l_sts, FabPtr->fab$l_stv);

   cfqptr = &ProxyMaintCacheFileQio;

   if (VMSnok (status = FabPtr->fab$l_sts))
   {
      /****************/
      /* search error */
      /****************/

      if (status == RMS$_NMF || (!ProxyMaintFileCount && status == RMS$_FNF))
      {
         /* end of search */
         ProxyMaintScanEnd ();
         return;
      }
      if (status == RMS$_FNF)
      {
         /* file probably deleted during search, just continue searching */
         ProxyMaintScanSearch ();
         return;
      }

      FaoToStdout (
"%HTTPD-W-PROXYMAINT, !20%D, cache maintenance problem\n\
-!&M\n",
         0, status);

      if (OpcomMessages & OPCOM_PROXY_MAINT)
         FaoToOpcom (
"%HTTPD-W-PROXYMAINT, cache maintenance problem\r\n\
-!&M",
            status);

      ProxyMaintScanEnd ();
      return;
   }

   ProxyMaintFileCount++;

   ProxyMaintResultFileNameLength = ProxyMaintSearchNam.nam$b_rsl;
   ProxyMaintResultFileName[ProxyMaintResultFileNameLength] = '\0';

   /************/
   /* file ACP */
   /************/

   if (!cfqptr->AcpChannel)
   {
      /* clean the structure out */
      memset (cfqptr, sizeof(ProxyMaintCacheFileQio), 0);

      /* assign a channel to the disk device containing the file */
      DeviceDsc.dsc$a_pointer = ProxyMaintSearchNam.nam$l_dev;
      DeviceDsc.dsc$w_length = ProxyMaintSearchNam.nam$b_dev;

      status = sys$assign (&DeviceDsc, &cfqptr->AcpChannel, 0, 0, 0);
      if (VMSnok (status))
         ErrorExitVmsStatus (status, "sys$assign()", FI_LI);

      /* set up the File Information Block for the ACP interface */
      cfqptr->FibDsc.dsc$w_length = sizeof(struct fibdef);
      cfqptr->FibDsc.dsc$a_pointer = &cfqptr->Fib;

      atptr = &cfqptr->FileAtr;
      atptr->atr$w_size = sizeof(cfqptr->BdtTime64);
      atptr->atr$w_type = ATR$C_BAKDATE;
      atptr->atr$l_addr = &cfqptr->BdtTime64;
      atptr++;
      atptr->atr$w_size = sizeof(cfqptr->CdtTime64);
      atptr->atr$w_type = ATR$C_CREDATE;
      atptr->atr$l_addr = &cfqptr->CdtTime64;
      atptr++;
      atptr->atr$w_size = sizeof(cfqptr->EdtTime64);
      atptr->atr$w_type = ATR$C_EXPDATE;
      atptr->atr$l_addr = &cfqptr->EdtTime64;
      atptr++;
      atptr->atr$w_size = sizeof(cfqptr->RdtTime64);
      atptr->atr$w_type = ATR$C_REVDATE;
      atptr->atr$l_addr = &cfqptr->RdtTime64;
      atptr++;
      atptr->atr$w_size = sizeof(cfqptr->AscDates);
      atptr->atr$w_type = ATR$C_ASCDATES;
      atptr->atr$l_addr = &cfqptr->AscDates;
      atptr++;
      atptr->atr$w_size = sizeof(cfqptr->RecAttr);
      atptr->atr$w_type = ATR$C_RECATTR;
      atptr->atr$l_addr = &cfqptr->RecAttr;
      atptr++;
      atptr->atr$w_size = atptr->atr$w_type = atptr->atr$l_addr = 0;
   }

   memcpy (&cfqptr->Fib.fib$w_did, &ProxyMaintSearchNam.nam$w_did, 6);

   cfqptr->FileNameDsc.dsc$a_pointer = ProxyMaintSearchNam.nam$l_name;
   cfqptr->FileNameDsc.dsc$w_length = ProxyMaintSearchNam.nam$b_name +
                                      ProxyMaintSearchNam.nam$b_type +
                                      ProxyMaintSearchNam.nam$b_ver;

   status = sys$qio (EfnNoWait, cfqptr->AcpChannel, IO$_ACCESS,
                     &cfqptr->IOsb, &ProxyMaintAcpInfoAst, 0, 
                     &cfqptr->FibDsc, &cfqptr->FileNameDsc, 0, 0,
                     &cfqptr->FileAtr, 0);
}

/****************************************************************************/
/*
AST from ACP QIO. Calculate the relevant file information and determine if it
should be purged (deleted).  If not then just queue another search.  If it
should be then initialize the deletion RMS structures ready for sys$erase().
*/

ProxyMaintAcpInfoAst ()

{
   int  status,
        AccessHours,
        AccessSeconds;
   unsigned long  AllocatedVbn,
                  EndOfFileVbn;
   unsigned long  CurrentTime6464 [2],
                  ResultTime64 [2];
   PROXY_CACHE_FILE_QIO  *cfqptr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY,
                 "ProxyMaintAcpInfoAst() !&F !&X",
                 &ProxyMaintAcpInfoAst, ProxyMaintCacheFileQio.IOsb.Status);

   cfqptr = &ProxyMaintCacheFileQio;

   if (VMSnok (status = cfqptr->IOsb.Status))
   {
      /*************/
      /* ACP error */
      /*************/

      FaoToStdout ("%HTTPD-W-PROXYMAINT, \
file ACP error (!AZ !UL)\n-!&M\n \\!AZ\\\n",
         FI_LI, status, ProxyMaintResultFileName);

      /* file probably deleted during search, just continue searching */
      ProxyMaintScanSearch ();
      return;
   }

   /**************/
   /* ACP QIO OK */
   /**************/

   AllocatedVbn = ((cfqptr->RecAttr.fat$l_hiblk & 0xffff) << 16) |
                  ((cfqptr->RecAttr.fat$l_hiblk & 0xffff0000) >> 16);

   EndOfFileVbn = ((cfqptr->RecAttr.fat$l_efblk & 0xffff) << 16) |
                  ((cfqptr->RecAttr.fat$l_efblk & 0xffff0000) >> 16);

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY,
                 "AllocatedVbn:!UL EndOfFileVbn:!UL FirstFreeByte:!UL",
                 AllocatedVbn, EndOfFileVbn, cfqptr->RecAttr.fat$w_ffbyte);

   ProxyMaintAllocBlocks += ProxyMaintFileAllocBlocks = AllocatedVbn;
   if (EndOfFileVbn >= 1 && cfqptr->RecAttr.fat$w_ffbyte)
      ProxyMaintUsedBlocks += ProxyMaintFileUsedBlocks = EndOfFileVbn;
   else
      ProxyMaintFileUsedBlocks = 0;

   switch (ProxyMaintScanType)
   {
      case PROXY_MAINT_SCAN_STATISTICS :

         /* got the stats, just search for next */
         ProxyMaintScanSearch ();
         return;

      case PROXY_MAINT_SCAN_BCKGRND :
      case PROXY_MAINT_SCAN_REACTIVE :
      case PROXY_MAINT_SCAN_ROUTINE :

         sys$gettim (&CurrentTime6464);

         /* if a v6.0 cache file, deal gracefully with the situation */
         if (QUAD_ZERO(cfqptr->BdtTime64))
         {
            /* check how long ago the file was last accessed */
            AccessSeconds = ProxyCacheDeltaSeconds (&CurrentTime6464,
                                                    &cfqptr->EdtTime64);
            AccessHours = AccessSeconds / 3600;
            /* break to drop through and delete the file */
            if (AccessHours >= ProxyMaintPurgeAtHour) break;
            /* search for next */
            ProxyMaintScanSearch ();
            return;
         }

         if (QUAD_NOT_ZERO(cfqptr->EdtTime64))
         {
            /* status will be LIB$_NEGTIM if expired */
            status = lib$sub_times (&cfqptr->EdtTime64,
                                    &CurrentTime6464,
                                    &ResultTime64);
            /* break to drop through and delete the file */
            if (VMSnok (status)) break;
         }

         /* check how long ago the file was last accessed */
         AccessSeconds = ProxyCacheDeltaSeconds (&CurrentTime6464,
                                                 &cfqptr->RdtTime64);
         AccessHours = AccessSeconds / 3600;
         /* break to drop through and delete the file */
         if (AccessHours >= ProxyMaintPurgeAtHour) break;

         /* search for next */
         ProxyMaintScanSearch ();
         return;

      default :
         ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
   }

   /***************/
   /* delete file */
   /***************/

   status = sys$qio (EfnNoWait, cfqptr->AcpChannel, IO$_DELETE | IO$M_DELETE,
                     &cfqptr->IOsb, &ProxyMaintDeleteAst, 0, 
                     &cfqptr->FibDsc, &cfqptr->FileNameDsc, 0, 0, 0, 0);
   if (VMSnok (status))
   {
      /* let the AST routine handle it! */
      cfqptr->IOsb.Status = status;
      SysDclAst (&ProxyMaintDeleteAst, 0);
   }
}

/****************************************************************************/
/*
AST from QIO IO$_DELETE.
*/

ProxyMaintDeleteAst ()

{
   int  status;
   PROXY_CACHE_FILE_QIO  *cfqptr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (WATCHALL, WATCH_MOD_PROXY,
                 "ProxyMaintDeleteAst() !&F !&X", &ProxyMaintDeleteAst);

   cfqptr = &ProxyMaintCacheFileQio;

   if (VMSok (status = cfqptr->IOsb.Status))
   {
      ProxyMaintDeletedCount++;
      ProxyMaintDeletedAllocBlocks += ProxyMaintFileAllocBlocks;
      ProxyMaintDeletedUsedBlocks += ProxyMaintFileUsedBlocks;
   }
   else
   {
      FaoToStdout ("%HTTPD-W-PROXYMAINT, \
file erase error (!AZ !UL)\n-!&M\n \\!AZ\\\n",
         FI_LI, status, ProxyMaintEraseFileName);
   }

   /* search next */
   ProxyMaintScanSearch ();
}

/*****************************************************************************/
/*
Return a report and control menu related proxy serving.
*/ 

ProxyMaintReport (REQUEST_STRUCT *rqptr)

{
   static char CacheDeviceFao [] =
"<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">Cache!AZ</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr><th>Device:</th><td>!AZ!&@\n\
<br>!UL &nbsp;error!%s\n\
<br>!UL &nbsp;blocks&nbsp; (!ULMB)\n\
<br>!UL &nbsp;used&nbsp; (!ULMB !UL%)\n\
<br>!UL &nbsp;free&nbsp; (!ULMB !UL%)\n\
</td></tr>\n\
<tr><th>Free Space:</th><td>!AZ</td></tr>\n\
<tr><th>In-Progress:</th><td>!AZ</td></tr>\n\
<tr><th>Statistics Scan:</th><td>!AZ</td></tr>\n\
<tr><th>!AZ:</th><td>!AZ</td></tr>\n\
<tr><th>Reactive Purge:</th><td>!AZ</td></tr>\n\
<tr><th>!AZ:</th><td>!AZ</td></tr>\n\
<tr><td colspan=\"2\">\n\
<table class=\"lftlft\">\n\
<tr><td>\n\
<form method=\"GET\" action=\"!AZ\">\n\
<input type=\"submit\" value=\" Statistics Scan \">\n\
</form>\n\
</td><td>\n\
<form method=\"GET\" action=\"!AZ\">\n\
<input type=\"submit\" value=\" Background Purge \">\n\
</form>\n\
</td><td>\n\
<form method=\"GET\" action=\"!AZ\">\n\
<input type=\"submit\" value=\" Reactive Purge \">\n\
</form>\n\
</td><td>\n\
<form method=\"GET\" action=\"!AZ\">\n\
<input type=\"submit\" value=\" Routine Purge \">\n\
</form>\n\
</td><td>\n\
<form method=\"GET\" action=\"!AZ\">\n\
<input type=\"submit\" value=\" Stop \">\n\
</form>\n\
</td></tr>\n\
</table>\n\
</td></tr>\n\
</table>\n\
</td></tr>\n\
</table>\n\
!AZ\
</body>\n\
</html>\n";

   int  status,
        ErrorCount,
        FreeBlocks,
        FreeMBytes,
        FreePercent,
        TotalMBytes,
        TotalBlocks,
        UsedBlocks,
        UsedMBytes,
        UsedPercent;
   unsigned short  Length;
   unsigned long  FaoVector [64];
   unsigned long  *vecptr;
   char  *DevNamePtr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_PROXY))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_PROXY, "ProxyMaintReport()");

   AdminPageTitle (rqptr, "Proxy Report");

   status = ProxyMaintStatisticsReport (rqptr, true);
   if (VMSnok (status))
   {
      rqptr->rqResponse.ErrorTextPtr = "ProxyMaintStatisticsReport()";
      ErrorVmsStatus (rqptr, status, FI_LI);
      AdminEnd (rqptr);
      return;
   }

   status = ProxyMaintFunctionReport (rqptr);
   if (VMSnok (status))
   {
      rqptr->rqResponse.ErrorTextPtr = "ProxyMaintFunctionReport()";
      ErrorVmsStatus (rqptr, status, FI_LI);
      AdminEnd (rqptr);
      return;
   }

   status = ProxyMaintDeviceStats (&DevNamePtr, &TotalBlocks,
                                   &UsedBlocks, &FreeBlocks, &ErrorCount);
   if (VMSok (status))
   {
      TotalMBytes = PROXY_MAINT_DEVICE_MBYTES(TotalBlocks);
      UsedMBytes = PROXY_MAINT_DEVICE_MBYTES(UsedBlocks);
      FreeMBytes = PROXY_MAINT_DEVICE_MBYTES(FreeBlocks);
      UsedPercent = PercentOf(UsedBlocks,TotalBlocks);
      FreePercent = PercentOf(FreeBlocks,TotalBlocks);
   }
   else
      TotalBlocks = TotalMBytes = UsedBlocks = UsedMBytes =
         UsedPercent = FreeBlocks = FreeMBytes = FreePercent = 0;

   vecptr = FaoVector;

   if (ProxyServingEnabled && ProxyCacheEnabled)
      *vecptr++ = "";
   else
      *vecptr++ = "<span style=\"color:#ff0000\"> ... DISABLED</span>";

   *vecptr++ = DevNamePtr;
   if (VMSok (status))
      *vecptr++ = "";
   else
   {
      *vecptr++ = " %!&M";
      *vecptr++ = status;
   }
   *vecptr++ = ErrorCount;
   *vecptr++ = TotalBlocks;
   *vecptr++ = TotalMBytes;
   *vecptr++ = UsedBlocks;
   *vecptr++ = UsedMBytes;
   *vecptr++ = UsedPercent;
   *vecptr++ = FreeBlocks;
   *vecptr++ = FreeMBytes;
   *vecptr++ = FreePercent;

   if (ProxyCacheEnabled)
   {
      if (ProxyCacheFreeSpaceAvailable)
         *vecptr++ = "AVAILABLE";
      else
         *vecptr++ = "<span style=\"color:#ff0000\">NOT AVAILABLE</span>";
   }
   else
      *vecptr++ = "<i>n/a</i>";

   switch (ProxyMaintScanType)
   {
      case PROXY_MAINT_SCAN_STATISTICS :
         *vecptr++ = "<span style=\"color:#ff0000\">STATISTICS SCAN</span>";
         break;
      case PROXY_MAINT_SCAN_REACTIVE :
         *vecptr++ = "<span style=\"color:#ff0000\">REACTIVE PURGE</span>";
         break;
      case PROXY_MAINT_SCAN_ROUTINE :
         *vecptr++ = "<span style=\"color:#ff0000\">ROUTINE PURGE</span>";
         break;
      case PROXY_MAINT_SCAN_BCKGRND :
         *vecptr++ = "<span style=\"color:#ff0000\">BACKGROUND PURGE</span>";
         break;
      default :
         if (ProxyCacheEnabled)
            *vecptr++ = "<i>none</i>";
         else
            *vecptr++ = "<i>n/a</i>";
   }

   *vecptr++ = ProxyMaintStatusStringStatScan;
   if (ProxyCacheRoutineHourOfDay == 24)
      *vecptr++ = "<u>Background Purge</u>";
   else
      *vecptr++ = "Background Purge";
   *vecptr++ = ProxyMaintStatusStringBckGrnd;
   *vecptr++ = ProxyMaintStatusStringReactive;
   if (ProxyCacheRoutineHourOfDay >= 0 &&
       ProxyCacheRoutineHourOfDay <= 23)
      *vecptr++ = "<u>Routine Purge</u>";
   else
      *vecptr++ = "Routine Purge";
   *vecptr++ = ProxyMaintStatusStringRoutine;

   *vecptr++ = ADMIN_CONTROL_PROXY_STATISTICS;
   *vecptr++ = ADMIN_CONTROL_PROXY_PURGE_BCKGRND;
   *vecptr++ = ADMIN_CONTROL_PROXY_PURGE_REACTIVE;
   *vecptr++ = ADMIN_CONTROL_PROXY_PURGE_ROUTINE;
   *vecptr++ = ADMIN_CONTROL_PROXY_STOP_SCAN;

   *vecptr++ = AdminRefresh();

   status = FaolToNet (rqptr, CacheDeviceFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc);

   AdminEnd (rqptr);
}

/*****************************************************************************/
/*
Return a report on proxy serving.
*/ 

ProxyMaintStatisticsReport
(
REQUEST_STRUCT *rqptr,
BOOL StatisticsReport
)
{
   static char  ResponseFao [] =
"<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">!AZ!AZ</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr>\n\
\
<td>\n\
<table cellpadding=\"0\" cellspacing=\"5\" border=\"0\">\n\
<tr><th class=\"sbttl\">Cache</th>\
<tr><th>Read:</th>\
<td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>304:</th>\
<td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>Write:</th>\
<td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>Cannot&nbsp;&nbsp;/Request:</th>\
<td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>/Response:</th>\
<td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>/Bytes:</th><td>!&,@SQ</td><td>(!UL%)</td></tr>\n\
<tr><th>Rx:</th><td>!&,@SQ</td>\
<td rowspan=\"2\" valign=\"middle\" \
style=\"vertical-align:center;\">(!UL%)</td></tr>\n\
<tr><th>Tx:</th><td>!&,@SQ</td></tr>\n\
<tr><th>1nn:</th><td>!&L</td></tr>\n\
<tr><th>2nn:</th><td>!&L</td></tr>\n\
<tr><th>3nn:</th><td>!&L</td></tr>\n\
<tr><th>4nn:</th><td>!&L</td></tr>\n\
<tr><th>5nn:</th><td>!&L</td></tr>\n\
<tr><th><i>none</i>:</th><td>!&L</td></tr>\n\
<tr><td>&nbsp;</td></tr>\n\
\
<tr><th class=\"sbttl\">Method</th>\
<tr><th>CONNECT:</th><td>!&L</td></tr>\n\
<tr><th>DELETE:</th><td>!&L</td></tr>\n\
<tr><th>GET:</th><td>!&L</td></tr>\n\
<tr><th>HEAD:</th><td>!&L</td></tr>\n\
<tr><th>OPTIONS:</th><td>!&L</td></tr>\n\
<tr><th>POST:</th><td>!&L</td></tr>\n\
<tr><th>PUT:</th><td>!&L</td></tr>\n\
<tr><th>TRACE:</th><td>!&L</td></tr>\n\
<tr><th>WebDAV:</th><td>!&L</td></tr>\n\
<tr><th>Extension:</th><td>!&L</td></tr>\n\
<tr><th>(SSH):</th><td>!&L</td></tr>\n\
</table>\n\
</td>\
\
<td>&nbsp;</td>\n\
\
<td>\n\
<table cellpadding=\"0\" cellspacing=\"5\" border=\"0\">\n\
<tr><th class=\"sbttl\">Network</th></tr>\n\
<tr><th>Requested:</th>\
<td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>IPv4:</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>IPv6:</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>Persistent&nbsp;&nbsp;/Current</th>\
<td>!&L</td>!&@</tr>\n\
<tr><th>/Peak</th><td>!&L</td></tr>\n\
<tr><th>/Count</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>Rx:</th><td>!&,@SQ</td>\
<td rowspan=\"2\" valign=\"middle\" \
style=\"vertical-align:center;\">(!UL%)</td></tr>\n\
<tr><th>Tx:</th><td>!&,@SQ</td></tr>\n\
<tr><th>1nn:</th><td>!&L</td></tr>\n\
<tr><th>2nn:</th><td>!&L</td></tr>\n\
<tr><th>3nn:</th><td>!&L</td></tr>\n\
<tr><th>4nn:</th><td>!&L</td></tr>\n\
<tr><th>5nn:</th><td>!&L</td></tr>\n\
<tr><th><i>none</i>:</th><td>!&L</td></tr>\n\
\
<tr><td>&nbsp;</td></tr>\n\
\
<tr><th class=\"sbttl\">Host&nbsp;Resolution</th>\
<tr><th>Literal:</th><td>!&L</td></tr>\n\
<tr><th>DNS:</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>Cache:</th><td>!&L</td></tr>\n\
<tr><th>Error:</th><td>!&L</td></tr>\n\
\
<tr><td>&nbsp;</td></tr>\n\
\
<tr><th class=\"sbttl\">Proxy&nbsp;Total</th>\
<tr><th>Requests:</th><td>!&L</td></tr>\n\
<tr><th>Bytes:</th><td colspan=\"2\" align=\"left\">!&,@SQ</td></tr>\n\
</table>\n\
</td>\
\
</tr>\n\
</table>\n\
</td></tr>\n\
</table>\n";

   int  status,
        ConnectCountNetwork,
        PercentBytesCache,
        PercentBytesNetwork,
        PercentBytesCannotCache,
        PercentCountCache304,
        PercentCountCacheRead,
        PercentCountCacheWrite,
        PercentCountRequestCannotCache,
        PercentCountResponseCannotCache,
        PercentCountIpv4,
        PercentCountIpv6,
        PercentCountNetwork,
        PercentCountPersist,
        PercentDnsName,
        TotalCount;
   unsigned short  Length;
   unsigned long  FaoVector [64],
                  BytesCache [2],
                  BytesCannotCache [2],
                  BytesRaw [2],
                  BytesRawRx [2],
                  BytesRawTx [2],
                  BytesCacheRx [2],
                  BytesCacheTx [2],
                  BytesTotal [2];
   unsigned long  *vecptr;
   float  FloatBytesCache,
          FloatBytesCannotCache,
          FloatBytesNetwork,
          FloatBytesTotal,
          FloatCountCache304,
          FloatCountCacheRead,
          FloatCountCacheWrite,
          FloatCountIpv4,
          FloatCountIpv6,
          FloatCountRequestCannotCache,
          FloatCountResponseCannotCache,
          FloatCountNetwork,
          FloatCountTotal,
          FloatNetworkCount,
          FloatPercent;
   char  Buffer [2048];
   $DESCRIPTOR (BufferDsc, Buffer);

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_PROXY))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_PROXY, "ProxyMaintStatisticsReport()");

   if (!ProxyServingEnabled && !StatisticsReport) return;

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);

   /*********/
   /* bytes */
   /*********/

   PLUS_QUAD_QUAD (ProxyAccountingPtr->BytesCacheRx,
                   ProxyAccountingPtr->BytesCacheTx,
                   BytesCache);

   PLUS_QUAD_QUAD (ProxyAccountingPtr->BytesCannotCacheRx,
                   ProxyAccountingPtr->BytesCannotCacheTx,
                   BytesCannotCache);

   PLUS_QUAD_QUAD (ProxyAccountingPtr->BytesRawRx,
                   ProxyAccountingPtr->BytesRawTx,
                   BytesRaw);

   PLUS_QUAD_QUAD (BytesCache, BytesRaw, BytesTotal);

   FloatBytesCache = (float)BytesCache[0] +
                     (float)BytesCache[1] * 4294967296.0;

   /********************/
   /* bytes percentage */
   /********************/

   FloatBytesCannotCache = (float)BytesCannotCache[0] +
                           (float)BytesCannotCache[1] * 4294967296.0;

   FloatBytesNetwork = (float)BytesRaw[0] +
                       (float)BytesRaw[1] * 4294967296.0;

   FloatBytesTotal = (float)BytesTotal[0] +
                     (float)BytesTotal[1] * 4294967296.0;

   if (FloatBytesTotal > 0.0)
   {
      PercentBytesCache = (int)(FloatPercent =
                          FloatBytesCache * 100.0 / FloatBytesTotal);
      if (FloatPercent - (float)PercentBytesCache >= 0.5) PercentBytesCache++;

      PercentBytesCannotCache =
         (int)(FloatPercent = FloatBytesCannotCache * 100.0 / FloatBytesTotal);
      if (FloatPercent - (float)PercentBytesCannotCache >= 0.5)
         PercentBytesCannotCache++;

      PercentBytesNetwork =
         (int)(FloatPercent = FloatBytesNetwork * 100.0 / FloatBytesTotal);
      if (FloatPercent - (float)PercentBytesNetwork >= 0.5)
         PercentBytesNetwork++;
   }
   else
      PercentBytesCache = PercentBytesCannotCache = PercentBytesNetwork = 0;

   /*********************/
   /* counts percentage */
   /*********************/

   FloatCountRequestCannotCache = ProxyAccountingPtr->RequestCannotCacheCount;
   FloatCountResponseCannotCache = ProxyAccountingPtr->ResponseCannotCacheCount;
   ConnectCountNetwork = ProxyAccountingPtr->ConnectIpv4Count +
                         ProxyAccountingPtr->ConnectIpv6Count;
   FloatCountIpv4 = (float)ProxyAccountingPtr->ConnectIpv4Count;
   FloatCountIpv6 = (float)ProxyAccountingPtr->ConnectIpv6Count;
   FloatCountNetwork = FloatCountIpv4 + FloatCountIpv6;
   FloatCountCache304 = ProxyAccountingPtr->CacheRead304Count;
   FloatCountCacheRead = ProxyAccountingPtr->CacheReadCount;
   FloatCountCacheWrite = ProxyAccountingPtr->CacheWriteCount;
   FloatCountTotal = FloatCountNetwork + FloatCountCacheRead;

   if (FloatCountTotal > 0.0)
   {
      PercentCountNetwork = 
         (int)(FloatPercent = FloatCountNetwork * 100.0 / FloatCountTotal);
      if (FloatPercent - (float)PercentCountNetwork >= 0.5)
         PercentCountNetwork++;

      PercentCountIpv4 = 
         (int)(FloatPercent = FloatCountIpv4 * 100.0 / FloatCountTotal);
      if (FloatPercent - (float)PercentCountIpv4 >= 0.5)
         PercentCountIpv4++;

      PercentCountIpv6 = 
         (int)(FloatPercent = FloatCountIpv6 * 100.0 / FloatCountTotal);
      if (FloatPercent - (float)PercentCountIpv6 >= 0.5)
         PercentCountIpv6++;

      PercentCountCacheRead = (int)(FloatPercent =
                          FloatCountCacheRead * 100.0 / FloatCountTotal);
      if (FloatPercent - (float)PercentCountCacheRead >= 0.5)
         PercentCountCacheRead++;

      PercentCountCache304 = (int)(FloatPercent =
                          FloatCountCache304 * 100.0 / FloatCountTotal);
      if (FloatPercent - (float)PercentCountCache304 >= 0.5)
         PercentCountCache304++;

      PercentCountRequestCannotCache =
         (int)(FloatPercent =
               FloatCountRequestCannotCache * 100.0 / FloatCountTotal);
      if (FloatPercent - (float)PercentCountRequestCannotCache >= 0.5)
         PercentCountRequestCannotCache++;
   }
   else
      PercentCountCache304 = PercentCountCacheRead = 
         PercentCountRequestCannotCache = PercentCountNetwork =
         PercentCountIpv4 = PercentCountIpv6 = 0;

   if (FloatCountNetwork > 0.0)
   {
      PercentCountCacheWrite = 
         (int)(FloatPercent = FloatCountCacheWrite * 100.0 / FloatCountNetwork);
      if (FloatPercent - (float)PercentCountCacheWrite >= 0.5)
         PercentCountCacheWrite++;

      PercentCountResponseCannotCache =
         (int)(FloatPercent =
               FloatCountResponseCannotCache * 100.0 / FloatCountNetwork);
      if (FloatPercent - (float)PercentCountResponseCannotCache >= 0.5)
         PercentCountResponseCannotCache++;
   }
   else
      PercentCountCacheWrite = PercentCountResponseCannotCache = 0;

   if (FloatCountNetwork > 0.0)
      PercentCountPersist =
         (int)(((float)ProxyAccountingPtr->ConnectPersistCount * 100) /
               FloatCountNetwork);
   else
      PercentCountPersist = 0;

   /* get these values because of the locking requirements */
   PUT_QUAD_QUAD (ProxyAccountingPtr->BytesRawRx, BytesRawRx);
   PUT_QUAD_QUAD (ProxyAccountingPtr->BytesRawTx, BytesRawTx);
   PUT_QUAD_QUAD (ProxyAccountingPtr->BytesCacheRx, BytesCacheRx);
   PUT_QUAD_QUAD (ProxyAccountingPtr->BytesCacheTx, BytesCacheTx);

   TotalCount = AccountingPtr->LookupDnsNameCount +
                AccountingPtr->LookupCacheNameCount;
   if (TotalCount)
      PercentDnsName = AccountingPtr->LookupDnsNameCount * 100 / TotalCount;
   else
      PercentDnsName = 0;

   /***********/
   /* display */
   /***********/

   vecptr = FaoVector;

   *vecptr++ = StatisticsReport ? "Statistics" : "Proxy";

   if (ProxyServingEnabled)
      *vecptr++ = "";
   else
      *vecptr++ = "<span style=\"color:#ff0000\"> ... DISABLED</span>";

   *vecptr++ = ProxyAccountingPtr->CacheReadCount;
   *vecptr++ = PercentCountCacheRead;
   *vecptr++ = ProxyAccountingPtr->CacheRead304Count;
   *vecptr++ = PercentCountCache304;
   *vecptr++ = ProxyAccountingPtr->CacheWriteCount;
   *vecptr++ = PercentCountCacheWrite;

   *vecptr++ = ProxyAccountingPtr->RequestCannotCacheCount;
   *vecptr++ = PercentCountRequestCannotCache;
   *vecptr++ = ProxyAccountingPtr->ResponseCannotCacheCount;
   *vecptr++ = PercentCountResponseCannotCache;
   *vecptr++ = &BytesCannotCache;
   *vecptr++ = PercentBytesCannotCache;

   *vecptr++ = &BytesCacheRx;
   *vecptr++ = PercentBytesCache;
   *vecptr++ = &BytesCacheTx;

   *vecptr++ = ProxyAccountingPtr->CacheStatusCodeCount[1];
   *vecptr++ = ProxyAccountingPtr->CacheStatusCodeCount[2];
   *vecptr++ = ProxyAccountingPtr->CacheStatusCodeCount[3];
   *vecptr++ = ProxyAccountingPtr->CacheStatusCodeCount[4];
   *vecptr++ = ProxyAccountingPtr->CacheStatusCodeCount[5];
   *vecptr++ = ProxyAccountingPtr->CacheStatusCodeCount[0];

   *vecptr++ = ProxyAccountingPtr->MethodConnectCount;
   *vecptr++ = ProxyAccountingPtr->MethodDeleteCount;
   *vecptr++ = ProxyAccountingPtr->MethodGetCount;
   *vecptr++ = ProxyAccountingPtr->MethodHeadCount;
   *vecptr++ = ProxyAccountingPtr->MethodOptionsCount;
   *vecptr++ = ProxyAccountingPtr->MethodPostCount;
   *vecptr++ = ProxyAccountingPtr->MethodPutCount;
   *vecptr++ = ProxyAccountingPtr->MethodTraceCount;
   *vecptr++ = ProxyAccountingPtr->MethodWebDavCopyCount +
               ProxyAccountingPtr->MethodWebDavLockCount +
               ProxyAccountingPtr->MethodWebDavMkColCount +
               ProxyAccountingPtr->MethodWebDavMoveCount +
               ProxyAccountingPtr->MethodWebDavPropFindCount +
               ProxyAccountingPtr->MethodWebDavPropPatchCount +
               ProxyAccountingPtr->MethodWebDavUnLockCount;
   *vecptr++ = ProxyAccountingPtr->MethodExtensionCount;
   *vecptr++ = ProxyAccountingPtr->MethodSshCount;

   *vecptr++ = ConnectCountNetwork;
   *vecptr++ = PercentCountNetwork;
   *vecptr++ = ProxyAccountingPtr->ConnectIpv4Count;
   *vecptr++ = PercentCountIpv4;
   *vecptr++ = ProxyAccountingPtr->ConnectIpv6Count;
   *vecptr++ = PercentCountIpv6;

   *vecptr++ = ProxyNetConnectCount;
   if (StatisticsReport)
   {
      *vecptr++ = "<td colspan=\"2\">[<a href=\"!AZ\">Report</a>]</td>";
      *vecptr++ = ADMIN_REPORT_PROXY_PERSISTENT;
   }
   else
      *vecptr++ = "";
   *vecptr++ = ProxyAccountingPtr->ConnectPersistPeak;
   *vecptr++ = ProxyAccountingPtr->ConnectPersistCount;
   *vecptr++ = PercentCountPersist;

   *vecptr++ = &BytesRawRx;
   *vecptr++ = PercentBytesNetwork;
   *vecptr++ = &BytesRawTx;

   *vecptr++ = ProxyAccountingPtr->NetStatusCodeCount[1];
   *vecptr++ = ProxyAccountingPtr->NetStatusCodeCount[2];
   *vecptr++ = ProxyAccountingPtr->NetStatusCodeCount[3];
   *vecptr++ = ProxyAccountingPtr->NetStatusCodeCount[4];
   *vecptr++ = ProxyAccountingPtr->NetStatusCodeCount[5];
   *vecptr++ = ProxyAccountingPtr->NetStatusCodeCount[0];

   *vecptr++ = AccountingPtr->LookupLiteralCount;
   *vecptr++ = AccountingPtr->LookupDnsNameCount;
   *vecptr++ = PercentDnsName;
   *vecptr++ = AccountingPtr->LookupCacheNameCount;
   *vecptr++ = AccountingPtr->LookupDnsNameErrorCount;

   *vecptr++ = AccountingPtr->DoProxyCount;
   *vecptr++ = &BytesRaw;

   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   status = FaolToNet (rqptr, ResponseFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   return (status);
}

/*****************************************************************************/
/*
Return a report on proxy special functionality.
*/ 

ProxyMaintFunctionReport (REQUEST_STRUCT *rqptr)

{
   static char  ResponseFao [] =
"<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">Function!AZ</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr>\n\
\
<td>\n\
<table class=\"rghtlft\">\n\
<tr><th class=\"sbttl tacnt\" colspan=\"4\">Gateway</th></tr>\n\
<tr><th class=\"tacnt\">Out&nbsp;\\&nbsp;In:</th>\
<th class=\"talft\">HTTP</th>\
<th class=\"talft\">HTTPS</th>\
</tr>\n\
<tr>\
<th>HTTP</th>\
<td>!&L</td>\
<td>!&L</td>\
</tr>\n\
<tr>\
<th>HTTPS</th>\
<td>!&L</td>\
<td>!&L</td>\
</tr>\n\
<tr>\
<th>FTP</th>\
<td>!&L</td>\
<td>!&L</td>\
</tr>\n\
\
<tr><th class=\"tacnt\">Out&nbsp;\\&nbsp;In:</th>\
<th class=\"talft\">IPv4</th>\
<th class=\"talft\">IPv6</th>\
</tr>\n\
<tr>\
<th>IPv4</th>\
<td>!&L</td>\
<td>!&L</td>\
</tr>\n\
<tr>\
<th>IPv6</th>\
<td>!&L</td>\
<td>!&L</td>\
</tr>\n\
</table>\n\
</td>\
\
<td>&nbsp;&nbsp;</td>\n\
\
<td>\n\
<table class=\"rghtlft\">\n\
<tr><th class=\"sbttl tacnt\" colspan=\"7\">Tunnel</th></tr>\n\
<tr><th class=\"tacnt\">Out&nbsp;\\&nbsp;In:</th>\
<th class=\"talft\">CONNECT</th>\
<th class=\"talft\">Firewall</th>\
<th class=\"talft\">Raw</th>\
</tr>\n\
<tr>\
<th>CONNECT</th>\
<td>!&L</td>\
<td>!&L</td>\
<td>!&L</td>\
</tr>\n\
<tr>\
<th>HTTP</th>\
<td>!&L</td>\
<td>!&L</td>\
<td>!&L</td>\
</tr>\n\
<tr>\
<th>HTTPS</th>\
<td>!&L</td>\
<td>!&L</td>\
<td>!&L</td>\
</tr>\n\
<tr>\
<th>Raw</th>\
<td>!&L</td>\
<td>!&L</td>\
<td>!&L</td>\
</tr>\n\
<tr><td></td></tr>\n\
<tr><th>Current:</th><td>!&L</td></tr>\n\
</table>\n\
</td>\
\
</tr>\n\
<tr><td></td></tr>\n\
<tr>\n\
\
<td>\n\
<table class=\"rghtrght\">\n\
<tr><th class=\"sbttl tacnt\" colspan=\"6\">FTP</th></tr>\n\
<tr><th>Total:</th><td>!&L</td>\n\
<th>DELE:</th><td>!&L</td>\n\
<th>DOS:</th><td>!&L</td></tr>\n\
<tr><th>Login&nbsp;Fail:</th><td>!&L</td>\n\
<th>LIST:</th><td>!&L</td>\n\
<th>Unix:</th><td>!&L</td></tr>\n\
<tr><th></th><td></td>\n\
<th>RETR:</th><td>!&L</td>\n\
<th>VMS:</th><td>!&L</td></tr>\n\
<tr><th></th><td></td>\n\
<th>STOR:</th><td>!&L</td>\n\
<th>?:</th><td>!&L</td></tr>\n\
</table>\n\
</td>\
\
<td>&nbsp;&nbsp;</td>\n\
\
<td>\n\
<table class=\"rghtrght\">\n\
<tr><th class=\"sbttl tacnt\" colspan=\"4\">Verify</th></tr>\n\
<tr><th>Max:</th><td>!&L</td>\n\
<th>Full:</th><td>!&L</td></tr>\n\
<tr><th>Current:</th><td>!&L</td>\n\
<th>200:</th><td>!&L</td></tr>\n\
<tr><th>Set:</th><td>!&L</td>\n\
<th>403:</th><td>!&L</td></tr>\n\
<tr><th>Find:</th><td>!&L</td>\n\
<th>404:</th><td>!&L</td></tr>\n\
</table>\n\
</td>\n\
\
</tr>\n\
</table>\n\
</td></tr>\n\
</table>\n";

   int  in, out, status;
   unsigned short  Length;
   unsigned long  FaoVector [64];
   unsigned long  *vecptr;
   char  Buffer [2048];
   $DESCRIPTOR (BufferDsc, Buffer);

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_PROXY))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_PROXY,
                 "ProxyMaintFunctionReport()");

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);

   vecptr = FaoVector;

   if (ProxyServingEnabled)
      *vecptr++ = "";
   else
      *vecptr++ = "<span style=\"color:#ff0000\"> ... DISABLED</span>";

   /* [http=0,https=1,ftp=2][http=0,https=1] */
   *vecptr++ = ProxyAccountingPtr->GatewaySchemeCount[0][0];
   *vecptr++ = ProxyAccountingPtr->GatewaySchemeCount[1][0];
   *vecptr++ = ProxyAccountingPtr->GatewaySchemeCount[0][1];
   *vecptr++ = ProxyAccountingPtr->GatewaySchemeCount[1][1];
   *vecptr++ = ProxyAccountingPtr->GatewaySchemeCount[0][2];
   *vecptr++ = ProxyAccountingPtr->GatewaySchemeCount[1][2];

   /* [ipv4=0,ipv6=1][ipv4=0,ipv6=1] */
   *vecptr++ = ProxyAccountingPtr->GatewayIpvCount[0][0];
   *vecptr++ = ProxyAccountingPtr->GatewayIpvCount[1][0];
   *vecptr++ = ProxyAccountingPtr->GatewayIpvCount[0][1];
   *vecptr++ = ProxyAccountingPtr->GatewayIpvCount[1][1];

   for (out = 0; out < TUNNEL_COUNT_MAX; out++)
   {
      if (out+1 == PROXY_TUNNEL_FIREWALL) continue;
      for (in = 0; in < TUNNEL_COUNT_MAX; in++)
      {
         if (in+1 == PROXY_TUNNEL_HTTP || in+1 == PROXY_TUNNEL_HTTPS) continue;
         *vecptr++ = ProxyAccountingPtr->TunnelCount[in][out];
      }
   }

   *vecptr++ = ProxyAccountingPtr->TunnelCurrent;

   *vecptr++ = ProxyAccountingPtr->FtpCount;
   *vecptr++ = ProxyAccountingPtr->FtpDeleCount;
   *vecptr++ = ProxyAccountingPtr->FtpDosCount;
   *vecptr++ = ProxyAccountingPtr->FtpLoginFailCount;
   *vecptr++ = ProxyAccountingPtr->FtpListCount;
   *vecptr++ = ProxyAccountingPtr->FtpUnixCount;
   *vecptr++ = ProxyAccountingPtr->FtpRetrCount;
   *vecptr++ = ProxyAccountingPtr->FtpVmsCount;
   *vecptr++ = ProxyAccountingPtr->FtpStorCount;
   *vecptr++ = ProxyAccountingPtr->FtpUnknownCount;

   *vecptr++ = ProxyVerifyRecordMax;
   *vecptr++ = ProxyAccountingPtr->VerifyFullCount;
   *vecptr++ = ProxyAccountingPtr->VerifyCurrentCount;
   *vecptr++ = ProxyAccountingPtr->Verify200Count;
   *vecptr++ = ProxyAccountingPtr->VerifySetRecordCount;
   *vecptr++ = ProxyAccountingPtr->Verify403Count;
   *vecptr++ = ProxyAccountingPtr->VerifyFindRecordCount;
   *vecptr++ = ProxyAccountingPtr->Verify404Count;

   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   status = FaolToNet (rqptr, ResponseFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   return (status);
}

/*****************************************************************************/
/*
Return a ontrol menu for dynamically adjusting proxy serving.
*/ 

ProxyMaintAdjust (REQUEST_STRUCT *rqptr)

{
   static char  ResponseFao [] =
"!AZ\
<html>\n\
<head>\n\
!AZ\
!AZ\
<title>WASD !AZ ... Proxy Report/Maintenance</title>\n\
</head>\n\
!AZ\n\
<div class=\"wasd\">\n\
<h2><nobr>WASD !AZ</nobr></h2>\n\
<h3>Proxy Report/Maintenance</h3>\n\
!20&W\n\
\
<form method=\"POST\" action=\"!AZ\">\n\
\
<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">Adjust Executing Server</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr><th>Serving:</th><td>\n\
<input type=\"radio\" name=\"ProxyServingEnabled\" value=\"enabled\"!AZ>enabled\n\
<input type=\"radio\" name=\"ProxyServingEnabled\" value=\"disabled\"!AZ>disabled\n\
</td></tr>\n\
<tr><th>Cache:</th><td>\n\
<input type=\"radio\" name=\"ProxyCacheEnabled\" value=\"enabled\"!AZ>enabled\n\
<input type=\"radio\" name=\"ProxyCacheEnabled\" value=\"disabled\"!AZ>disabled\n\
</td></tr>\n\
<tr><th>Unknown Request Fields:</th><td>\n\
<input type=\"radio\" name=\"ProxyUnknownRequestFields\" value=\"enabled\"!AZ>enabled\n\
<input type=\"radio\" name=\"ProxyUnknownRequestFields\" value=\"disabled\"!AZ>disabled\n\
</td></tr>\n\
<tr><th>Add &quot;Forwarded: by&quot;:</th><td>\n\
<input type=\"text\" size=\"10\" name=\"ProxyForwarded\" value=\"!AZ\">\n\
</td></tr>\n\
<tr><th>Add &quot;X-Forwarded-For:&quot;:</th><td>\n\
<input type=\"text\" size=\"10\" name=\"ProxyXForwardedFor\" value=\"!AZ\">\n\
</td></tr>\n\
<tr><th>Report To Log:</th><td>\n\
<input type=\"radio\" name=\"ProxyReportLog\" value=\"enabled\"!AZ>enabled\n\
<input type=\"radio\" name=\"ProxyReportLog\" value=\"disabled\"!AZ>disabled\n\
</td></tr>\n\
<tr><th>Report Cache To Log:</th><td>\n\
<input type=\"radio\" name=\"ProxyReportCacheLog\" value=\"enabled\"!AZ>enabled\n\
<input type=\"radio\" name=\"ProxyReportCacheLog\" value=\"disabled\"!AZ>disabled\n\
</td></tr>\n\
<tr><th>Host Name Lookup Retry:</th><td>\n\
<input type=\"text\" size=\"3\" name=\"ProxyHostLookupRetryCount\" value=\"!UL\">\n\
attempts</td></tr>\n\
<tr><th>Connect Timeout Seconds:</th><td>\n\
<input type=\"text\" size=\"7\" name=\"ProxyConnectTimeoutSeconds\" value=\"!AZ\">\n\
hh:mm:ss</td></tr>\n\
<tr><th>Connect Persist Max:</th><td>\n\
<input type=\"text\" size=\"3\" name=\"ProxyConnectPersistMax\" value=\"!UL\">\n\
connections</td></tr>\n\
<tr><th>Connect Persist Seconds:</th><td>\n\
<input type=\"text\" size=\"7\" name=\"ProxyConnectPersistSeconds\" value=\"!AZ\">\n\
hh:mm:ss</td></tr>\n\
<tr><th>Maximum Load Size:</th><td>\n\
<input type=\"text\" size=\"4\" name=\"ProxyCacheFileKBytesMax\" value=\"!UL\">\n\
kBytes</td></tr>\n\
<tr><th>Negative:</th><td>\n\
<input type=\"text\" size=\"7\" name=\"ProxyCacheNegativeSeconds\" value=\"!AZ\">\n\
hh:mm:ss</td></tr>\n\
<tr><th>No Reload:</th><td>\n\
<input type=\"text\" size=\"7\" name=\"ProxyCacheNoReloadSeconds\" value=\"!AZ\">\n\
hh:mm:ss</td></tr>\n\
<tr><th>Reload List:</th><td>\n\
<input type=\"text\" size=\"30\" name=\"ProxyCacheReloadList\" value=\"!AZ\">\n\
hours</td></tr>\n\
<tr><th>Routine Purge:</th><td>\n\
<input type=\"text\" size=\"3\" name=\"ProxyCacheRoutineHourOfDay\" value=!&@>\n\
hour-of-day (00-23)</td></tr>\n\
<tr><th>Purge List:</th><td>\n\
<input type=\"text\" size=\"30\" name=\"ProxyCachePurgeList\" value=\"!AZ\">\n\
hours</td></tr>\n\
<tr><th>Device Usage Check:</th><td>\n\
<input type=\"text\" size=\"2\" name=\"ProxyCacheDeviceCheckMinutes\" value=\"!UL\">\n\
minutes</td></tr>\n\
<tr><th>Device Usage Max:</th><td>\n\
<input type=\"text\" size=\"3\" name=\"ProxyCacheDeviceMaxPercent\" value=\"!UL\">\n\
percent</td></tr>\n\
<tr><th>Device Reduce By:</th><td>\n\
<input type=\"text\" size=\"3\" name=\"ProxyCacheDevicePurgePercent\" value=\"!UL\">\n\
percent</td></tr>\n\
<tr><td colspan=\"3\"><br>\n\
<input type=\"submit\" name=\"makechanges\" value=\" Make Changes \">\n\
<input type=reset value=\" Reset \">\n\
</td></tr>\n\
</table>\n\
</td></tr>\n\
</table>\n\
\
</form>\n\
\
</body>\n\
</html>\n";

#define REPBOOL(b)\
if (b)\
{\
   *vecptr++ = RadioButtonChecked;\
   *vecptr++ = RadioButtonUnchecked;\
}\
else\
{\
   *vecptr++ = RadioButtonUnchecked;\
   *vecptr++ = RadioButtonChecked;\
}

   static char  RadioButtonChecked [] = " CHECKED",
                RadioButtonUnchecked [] = "";

   int  status;
   unsigned long  FaoVector [64];
   unsigned long  *vecptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_PROXY))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_PROXY,
                 "ProxyMaintAdjustMenu()");

   vecptr = FaoVector;

   *vecptr++ = WASD_DOCTYPE;
   *vecptr++ = HtmlMetaInfo (rqptr, NULL);
   *vecptr++ = AdminWasdCss ();
   *vecptr++ = ServerHostPort;
   *vecptr++ = ADMIN_BODY_TAG;
   *vecptr++ = ServerHostPort;
   *vecptr++ = &rqptr->rqTime.BeginTime64;

   *vecptr++ = ADMIN_CONTROL_PROXY_ADJUST_NOW;

   REPBOOL (ProxyServingEnabled)
   REPBOOL (ProxyCacheEnabled)
   REPBOOL (ProxyUnknownRequestFields)
   if (ProxyForwardedBy == PROXY_FORWARDED_BY)
      *vecptr++ = "BY";
   else
   if (ProxyForwardedBy == PROXY_FORWARDED_FOR)
      *vecptr++ = "FOR";
   else
      *vecptr++ = "disabled";
   if (ProxyXForwardedFor == PROXY_XFORWARDEDFOR_ENABLED)
      *vecptr++ = "ENABLED";
   else
   if (ProxyXForwardedFor == PROXY_XFORWARDEDFOR_ADDRESS)
      *vecptr++ = "ADDRESS";
   else
   if (ProxyXForwardedFor == PROXY_XFORWARDEDFOR_UNKNOWN)
      *vecptr++ = "UNKNOWN";
   else
      *vecptr++ = "disabled";
   REPBOOL (ProxyReportLog)
   REPBOOL (ProxyReportCacheLog)
   *vecptr++ = ProxyHostLookupRetryCount;
   *vecptr++ = MetaConShowSeconds (rqptr, ProxyConnectTimeoutSeconds);
   *vecptr++ = ProxyConnectPersistMax;
   *vecptr++ = MetaConShowSeconds (rqptr, ProxyConnectPersistSeconds);
   *vecptr++ = ProxyCacheFileKBytesMax;
   *vecptr++ = MetaConShowSeconds (rqptr, ProxyCacheNegativeSeconds);
   *vecptr++ = MetaConShowSeconds (rqptr, ProxyCacheNoReloadSeconds);
   *vecptr++ = ProxyCacheReloadListPtr;
   if (ProxyCacheRoutineHourOfDay >= 0)
   {
      *vecptr++ = "!UL";
      *vecptr++ = ProxyCacheRoutineHourOfDay;
   }
   else
      *vecptr++ = "";
   *vecptr++ = ProxyCachePurgeListPtr;
   *vecptr++ = ProxyCacheDeviceCheckMinutes;
   *vecptr++ = ProxyCacheDeviceMaxPercent;
   *vecptr++ = ProxyCacheDevicePurgePercent;

   status = FaolToNet (rqptr, ResponseFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc);

   AdminEnd (rqptr);
}

/*****************************************************************************/
/*
*/ 

ProxyMaintControl (REQUEST_STRUCT *rqptr)

{
   BOOL  MakeChanges;
   int  status;
   unsigned short  Length;
   char  *cptr, *qptr, *sptr, *zptr;
   char  FieldName [128],
         FieldValue [256],
         ProxyCachePurgeList [64],
         ProxyCacheReloadList [64];

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_PROXY))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_PROXY, "ProxyMaintControl()");

   MakeChanges = false;

   /**********************/
   /* parse content body */
   /**********************/

   if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
   {
      if (!rqptr->rqBody.DataPtr)
      {
         /* read all the request body (special case) then AST back */
         BodyReadBegin (rqptr, &ProxyMaintControl, &BodyProcessReadAll);
         return;
      }
      qptr = rqptr->rqBody.DataPtr;
   }
   else
      qptr = rqptr->rqHeader.QueryStringPtr;

   while (*qptr)
   {
      status = StringParseQuery (&qptr, FieldName, sizeof(FieldName),
                                        FieldValue, sizeof(FieldValue));
      if (VMSnok (status))
      {
         /* error occured */
         if (status == SS$_IVCHAR) rqptr->rqResponse.HttpStatus = 400;
         rqptr->rqResponse.ErrorTextPtr = "parsing query string";
         ErrorVmsStatus (rqptr, status, FI_LI);
         AdminEnd (rqptr);
         return;
      }

      /************************/
      /* action button fields */
      /************************/

      if (strsame (FieldName, "hostname", -1))
      {
         TcpIpHostCacheSupervisor ((unsigned int)-1);
         ReportSuccess (rqptr, "Server !AZ host name cache purged.",
                        ServerHostPort);
      }
      else
      if (strsame (FieldName, "makechanges", -1))
         MakeChanges = true;
      else
      if (strsame (FieldName, "ProxyForwarded", -1))
      {
         if (TOUP(FieldValue[0]) == 'B')
            ProxyForwardedBy = PROXY_FORWARDED_BY;
         else
         if (TOUP(FieldValue[0]) == 'F')
            ProxyForwardedBy = PROXY_FORWARDED_FOR;
         else
            ProxyForwardedBy = PROXY_FORWARDED_DISABLED;
      }
      else
      if (strsame (FieldName, "ProxyXForwardedFor", -1))
      {
          if (TOUP(FieldValue[0]) == 'A')
            ProxyXForwardedFor = PROXY_XFORWARDEDFOR_ADDRESS;
          else
          if (TOUP(FieldValue[0]) == 'E')
            ProxyXForwardedFor = PROXY_XFORWARDEDFOR_ENABLED;
          else
          if (TOUP(FieldValue[0]) == 'U')
            ProxyXForwardedFor = PROXY_XFORWARDEDFOR_UNKNOWN;
          else
            ProxyXForwardedFor = PROXY_XFORWARDEDFOR_DISABLED;
      }
      else
      if (strsame (FieldName, "ProxyServingEnabled", -1))
      {
         InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
         if (TOUP(FieldValue[0]) == 'E')
            ProxyServingEnabled = ProxyAccountingPtr->ServingEnabled = true;
         else
         if (TOUP(FieldValue[0]) == 'D')
            ProxyServingEnabled = ProxyAccountingPtr->ServingEnabled = false;
         InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
      }
      else
      if (strsame (FieldName, "ProxyCacheEnabled", -1))
      {
         InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
         if (TOUP(FieldValue[0]) == 'E')
            ProxyCacheEnabled = ProxyAccountingPtr->CacheEnabled = true;
         else
         if (TOUP(FieldValue[0]) == 'D')
            ProxyCacheEnabled = ProxyAccountingPtr->CacheEnabled = false;
         InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
      }
      else
      if (strsame (FieldName, "ProxyHostLookupRetryCount", -1))
      {
         ProxyHostLookupRetryCount = atoi(FieldValue);
         if (ProxyHostLookupRetryCount < 0 || ProxyHostLookupRetryCount > 100)
            ProxyHostLookupRetryCount = 0;
      }
      else
      if (strsame (FieldName, "ProxyCacheFileKBytesMax", -1))
         ProxyCacheFileKBytesMax = atoi(FieldValue);
      else
      if (strsame (FieldName, "ProxyCacheReloadList", -1))
      {
         zptr = (sptr = ProxyCacheReloadList) + sizeof(ProxyCacheReloadList);
         for (cptr = FieldValue; *cptr && sptr < zptr; *sptr++ = *cptr++);
         if (sptr >= zptr)
         {
            ErrorGeneralOverflow (rqptr, FI_LI);
            return;
         }
         *sptr = '\0';
         ProxyCacheReloadListPtr = ProxyCacheReloadList;
      }
      else
      if (strsame (FieldName, "ProxyCacheRoutineHourOfDay", -1))
      {
         if (isdigit(FieldValue[0]))
            ProxyCacheRoutineHourOfDay = atoi(FieldValue);
         else
            ProxyCacheRoutineHourOfDay = -1;
      }
      else
      if (strsame (FieldName, "ProxyCacheDeviceCheckMinutes", -1))
         ProxyCacheDeviceCheckMinutes = atoi(FieldValue);
      else
      if (strsame (FieldName, "ProxyCacheDeviceMaxPercent", -1))
         ProxyCacheDeviceMaxPercent = atoi(FieldValue);
      else
      if (strsame (FieldName, "ProxyCacheDevicePurgePercent", -1))
         ProxyCacheDevicePurgePercent = atoi(FieldValue);
      else
      if (strsame (FieldName, "ProxyCacheNegativeSeconds", -1))
         ProxyCacheNegativeSeconds = MetaConSetSeconds (NULL, FieldValue, 1);
      else
      if (strsame (FieldName, "ProxyCacheNoReloadSeconds", -1))
         ProxyCacheNoReloadSeconds = MetaConSetSeconds (NULL, FieldValue, 1);
      else
      if (strsame (FieldName, "ProxyCachePurgeList", -1))
      {
         zptr = (sptr = ProxyCachePurgeList) + sizeof(ProxyCachePurgeList);
         for (cptr = FieldValue; *cptr && sptr < zptr; *sptr++ = *cptr++);
         if (sptr >= zptr)
         {
            ErrorGeneralOverflow (rqptr, FI_LI);
            return;
         }
         *sptr = '\0';
         ProxyCachePurgeListPtr = ProxyCachePurgeList;
      }
      else
      if (strsame (FieldName, "ProxyConnectPersistMax", -1))
         ProxyConnectPersistMax = atoi(FieldValue);
      else
      if (strsame (FieldName, "ProxyConnectPersistSeconds", -1))
         ProxyConnectPersistSeconds = MetaConSetSeconds (NULL, FieldValue, 1);
      else
      if (strsame (FieldName, "ProxyConnectTimeoutSeconds", -1))
         ProxyConnectTimeoutSeconds = MetaConSetSeconds (NULL, FieldValue, 1);
      else
      if (strsame (FieldName, "ProxyReportLog", -1))
      {
          if (TOUP(FieldValue[0]) == 'E')
            ProxyReportLog = true;
         else
         if (TOUP(FieldValue[0]) == 'D')
            ProxyReportLog = false;
      }
      else
      if (strsame (FieldName, "ProxyReportCacheLog", -1))
      {
          if (TOUP(FieldValue[0]) == 'E')
            ProxyReportCacheLog = true;
         else
         if (TOUP(FieldValue[0]) == 'D')
            ProxyReportCacheLog = false;
      }
      else
      if (strsame (FieldName, "ProxyUnknownRequestFields", -1))
      {
         if (TOUP(FieldValue[0]) == 'E')
            ProxyUnknownRequestFields = true;
         else
         if (TOUP(FieldValue[0]) == 'D')
            ProxyUnknownRequestFields = false;
      }
      else
      {
         /***********/
         /* unknown */
         /***********/

         ErrorGeneral (rqptr, "Unknown query field.", FI_LI);
         return;
      }
   }

   if (MakeChanges)
   {
      ProxyCacheInitValues ();
      ProxyMaintInit ();
      ProxyNetInit ();
      ReportSuccess (rqptr, "Server !AZ new proxy values loaded.",
                     ServerHostPort);
   }

   AdminEnd (rqptr);
}

/****************************************************************************/
