/*****************************************************************************/
/*
                                Service.c

This module provides a complementary function to CONFIG.C, which basically
configures per-HTTPD process runtime characterstics, whereas SERVICE.C allows
per-service (essentially virtual services) characteristics to be specified. 
While there is some overlap between the two they do perform different tasks.

Prior to WASD v7.1 service configuration was performed by CONFIG.C as part of
general configuration.  This had it's limitations and this module goes some way
in providing a more flexible and understandable configuration environment. 
This module configuration file, HTTP$SERVICE, does not have to be used and if
is not present the HTTP$CONFIG configuration file [Service] directive will
continue to provide backward-compatible service configuration.  However, new
features will only be incorporated via this module, and HTTP$SERVICE is
available it's service configuration overrides anything still present in
HTTPD$CONFIG.

All service directives must be delimitted with '[' and ']'.  The (virtual)
service they apply to are specified with the usual '[[' and ']]' directive.

  [[http://the.host.name:80]]
  [ServiceIPaddress] 130.185.250.1 
  [ServiceProxy]  disabled
  [ServiceNoLog]  disabled
  [ServiceNoTrack]  disabled
  [ServiceBodyTag]  
  [ServiceErrorReportPath]  
    
Both IPv4 and IPv6 addresses may be used to specify a service name and or
service address.  This can be in normal or compressed form.  If part of a
service name hyphens should be substituted for colons so that the port can be
differentiated from the service address.

  [[http://130.185.250.1:80]]
  [[http://--FFFF-130.185.250.1:80]]
  [[http://--FFFF-1FA-B983:80]]

In the case of a service specification a 'generic' host name can be specified
using "*".  This generic host name is replaced with the IP host name of
whichever system starts the server.  This is a simple way for providing a basic
or common service on all members of a cluster for instance.

  [[http://*:80]]
  [ServiceProxy]  disabled
  [ServiceNoLog]  disabled
  [ServiceNoTrack]  disabled

This configuration file only really needs to be used for more complex service
configurations.  Basic HTTP and HTTPS services need only be specified using the
the HTTPD$CONFIG [Service] directive.

The [IncludeFile] directive takes a VMS file name as a parameter.  It then
attempts to read and insert any directives contained in that file into the
current point in the server configuration.  Files may be nested one deep (i.e.
a main configuration file allowed to include other files, but the other file
not allowed in include yet more).


PRECEDENCE OF SERVICE SPECIFICATIONS
------------------------------------
1. /SERVICE= command line qualifier
2. HTTPD$SERVER configuration file (if logical defined and file exists)
3. HTTPD$CONFIG [Service] directive


HTTPD$SERVICE DIRECTIVES
------------------------
  o  IncludeFile                      <filename>
  o  ServiceAdmin                     DISABLED | ENABLED
  o  ServiceBind                      <string>
  o  ServiceBodyTag                   <string>
  o  ServiceClientSSL                 DISABLED | ENABLED
  o  ServiceClientSSLcipherList       <string>
  o  ServiceClientSSLverifyCA         DISABLED | ENABLED
  o  ServiceClientSSLCaFile           <string>
  o  ServiceClientSSLversion          <string>
  o  ServiceErrorReportPath           <string>
  o  ServiceNoLog                     DISABLED | ENABLED
  o  ServiceNoTrack                   DISABLED | ENABLED
  o  ServiceProxy                     DISABLED | ENABLED
  o  ServiceProxyAuth                 NONE | PROXY | LOCAL
  o  ServiceProxyCache                DISABLED | ENABLED
  o  ServiceProxyChain                <string>
  o  ServiceProxyBind                 <string>
  o  ServiceProxyTrack                DISABLED | ENABLED
  o  ServiceProxySSL                  DISABLED | ENABLED
  o  ServiceSSLcert                   <string>
  o  ServiceSSLcipherList             <string>
  o  ServiceSSLverifyPeer             DISABLED | ENABLED
  o  ServiceSSLverifyPeerCAfile       <string>
  o  ServiceSSLversion                <string>
  o  ServiceSSLkey                    <string>


COMMAND-LINE PARAMETERS
-----------------------
The following syntax and values are also available for both the HTTPD$CONFIG
[service] configuration parameter and /SERVICE= qualifier.  Also see Sesola.C
for SSL parameter processing.

  http:                      system host name, HTTP service
  https:                     system host name, SSL service
  http:port                  system host name, supplied port, HTTP service
  https:port                 system host name, supplied SSL service
  http://host.domain         specified host name, pot 80, HTTP service
  https://host.domain        specified host name port 443, SSL service
  http://host.domain:port    specified host name and port, HTTP service
  https://host.domain:port   specified host name and port, SSL service
  ;admin                     service is for per-instance administration
  ;bind=                     supplied IP address for service
  ;ip=                       supplied IP address for service
  ;cafile=                   CA file for verifying X.509 certificates
  ;cert=                     SSL service's non-default server certificate
  ;chain=                    service chains to up-stream proxy service
  ;cipher=                   SSL service's specific, non-default cipher list
  ;connect                   service provides SSL connect proxy access
  ;key=                      SSL service's non-default server RSA private key
  ;lauth                     local authorization required
  ;backlog=                  listen queue length
  ;pauth                     proxy authorization required
  ;pbind                     supplied IP address for proxy socket
  ;pclientssl                client SSL supported
  ;proxy                     service provide proxy access
  ;[no]track                 enable/disable session tracking on this service
                             (by default is tracking is configuration enabled
                              proxy services have it off, non-proxy on)
  ;verify                    always verify SSL connection using X.509 cert

For example:

  [service]
  http://host.name:8080;proxy;connect
  https://host.name:443;cert=HT_ROOT:[LOCAL]SITE.PEM


VERSION HISTORY
---------------
06-AUG-2004  MGD  bugfix; ServiceFindVirtual() port string comparison
10-APR-2004  MGD  significant modifications to support IPv6
28-JAN-2004  MGD  service access log report (via LoggingReport())
15-AUG-2003  MGD  where CDATA constraints make using &#10; entity impossible
                  use a field name of hidden$lf and &#94; substituted for it,
                  bugfix; ServiceConfigReviseNow() form element names must be
                  unique (technically correct and enforced by modern browsers)
08-JUN-2003  MGD  bugfix; ServiceConfigFromString() (jpp@esme.fr)
28-JAN-2003  MGD  relax ServiceParse() so that [[the.host.name]] is accepted
                  (i.e. without both scheme and port)
30-SEP-2002  MGD  bugfix; ServiceConfigFromString()
21-SEP-2002  MGD  bugfix; make HTTPD$SERVICE not found non-fatal
15-MAY-2002  MGD  change [ServiceProxyHttpSSL] to [ServiceClientSSL]
23-FEB-2002  MGD  change [ServiceSSLclientVerify..] to [ServiceSSLverifyPeer..]
                  to avoid confusion with unrelated [ServiceClientSSL..]
06-JAN-2002  MGD  [ServiceProxyHttpSSL..] HTTP to SSL (HTTPS) gateway
14-OCT-2001  MGD  [ServiceProxyBind], [ServiceIPaddress] to [ServiceBind]
29-SEP-2001  MGD  instance support (including admin service)
15-SEP-2001  MGD  meta-config
04-AUG-2001  MGD  support module WATCHing
04-JUL-2001  MGD  additional information in service report
27-JUN-2001  MGD  bugfix; parsing of [ServiceProxyChain]
28-FEB-2001  MGD  OdsLoadTextFile(), OdsParseTextFile(), [IncludeFile]
16-FEB-2001  MGD  add service-based error report status code handling
10-DEC-2000  MGD  SSL client certificate verification
21-NOV-2000  MGD  allow for generic service (no host name specified)
17-JUN-2000  MGD  initial
*/
/*****************************************************************************/

#ifdef WASD_VMS_V6
#undef _VMS_V6_SOURCE
#define _VMS_V6_SOURCE
#undef __VMS_VER
#define __VMS_VER 60000000
#undef __CRTL_VER
#define __CRTL_VER 60000000
#endif

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

/* VMS related header files */
#include <ssdef.h>
#include <stsdef.h>

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

#define WASD_MODULE "SERVICE"

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

BOOL  ServiceAdminSpecified,
      ServiceConfigFileDefined;
      ServiceLoadFromConfigFile;
 
int  ServiceCount;

SERVICE_META  ServiceMeta;
SERVICE_META  *ServiceMetaPtr;

SERVICE_STRUCT  *ServiceListHead,
                *ServiceListTail;

char  ErrorServiceList [] = "service list confusing";

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

#ifdef DBUG
extern BOOL Debug;
#else
#define Debug 0 
#endif

extern BOOL  HttpdServerStartup,
             LoggingPerService,
             ProtocolHttpsAvailable,
             ProtocolHttpsConfigured;

extern int  ServerPort;

extern char  CliServices[],
             ErrorSanityCheck[],
             ServerHostName[],
             ServerHostPort[],
             SoftwareID[],
             Utility[];

extern CONFIG_STRUCT  Config;
extern IPADDRESS  TcpIpEmptyAddress;
extern META_CONFIG  *MetaGlobalServicePtr;
extern MSG_STRUCT  Msgs;
extern WATCH_STRUCT  Watch;

/*****************************************************************************/
/*
*/ 
 
int ServiceConfigLoad (META_CONFIG **MetaConPtrPtr)

{
   int  status;

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

   if (WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE,
                 "ServiceConfigLoad() !AZ", CONFIG_SERVICE_FILE_NAME);

   if (!ServiceConfigFileDefined && getenv (CONFIG_SERVICE_FILE_NAME))
      ServiceConfigFileDefined = ServiceLoadFromConfigFile = true;

   if (CliServices[0] || !ServiceLoadFromConfigFile)
   {
      /* set services using /SERVICE=, or no service configuration file */
      ServiceLoadFromConfigFile = false;
      status = ServiceConfigFromString (MetaConPtrPtr);
   }
   else
   {
      ServiceLoadFromConfigFile = true;
      status = MetaConLoad (MetaConPtrPtr, CONFIG_SERVICE_FILE_NAME,
                            &ServiceConfigLoadCallback, true, false);
   }

   if (*MetaConPtrPtr == MetaGlobalServicePtr)
   {
      /* server startup */
      MetaConStartupReport (MetaGlobalServicePtr, "SERVICE");
      if (VMSnok (status))
      {
         /* the HTTPD$SERVICE file does not *have* to exist! */
         if (status != RMS$_FNF) exit (status);
         status = SS$_NORMAL;
      }

      if (!ServiceCount)
         ErrorExitVmsStatus (0, "No service configured!", FI_LI);
   }

   return (status);
}

/*****************************************************************************/
/*
Called by MetaConUnload() to free resources allocated during service
configuration.
*/ 
 
ServiceConfigUnload (META_CONFIG *mcptr)

{
   int  status;
   SERVICE_META  *smptr;
   SERVICE_STRUCT *svptr;

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

   if (WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE, "ServiceConfigUnload()");

   smptr = mcptr->ServiceMetaPtr;
   if (smptr && smptr != ServiceMetaPtr)
   {
      for (svptr = smptr->ListHead; svptr; svptr = svptr->NextPtr)
      {
         if (svptr->SSLserverPtr) VmFree (svptr->SSLserverPtr, FI_LI);
         if (svptr->SSLclientPtr) VmFree (svptr->SSLclientPtr, FI_LI);
         VmFree (svptr, FI_LI);
      }
      VmFree (smptr, FI_LI);
      mcptr->ServiceMetaPtr = NULL;
   }
}

/*****************************************************************************/
/*
For each non-meta-config directive line read by MetaConLoad() this function is
called to parse the line text's contents and to configure the private data
structure associated with each rule.
*/

BOOL ServiceConfigLoadCallback (META_CONFIG *mcptr)

{
   static char  ProblemUnknownKeyword [] = "Unknown keyword",
                ProblemCannotConfig [] = "Cannot configure service",
                ProblemDirective [] = "Unknown directive",
                ProblemInvalidService [] = "Invalid service specification",
                ProblemOverflow [] = "Storage overflow",
                ProblemNoSSL [] = "SSL not available",
                ProblemUsage [] = "Cannot use during service configuration";

   int  status,
        retval,
        LineLength;
   char  *cptr, *sptr, *zptr;
   char  StringBuffer [256],
         StringScratch [256];
   SERVICE_META  *smptr;
   METACON_LINE  *mclptr;
   SERVICE_STRUCT  *svptr;
   SESOLA_CONTEXT  *scptr, *ssptr;

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

   if (WATCH_MODULE(WATCH_MOD_SERVICE))
   {
      WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE,
                 "ServiceConfigLoadCallback() !&F !&X",
                 &ServiceConfigLoadCallback, mcptr);
      if (WATCH_MODULE(WATCH_MOD__DETAIL))
      {
         mclptr = mcptr->ParsePtr;
         WatchDataFormatted ("!&X !UL !UL !UL !UL !&X !&Z !&Z\n",
            mclptr, mclptr->Size, mclptr->Token, mclptr->Number,
            mclptr->Length, mclptr->LineDataPtr, mclptr->TextPtr,
            mclptr->InlineTextPtr);
      }
   }

   /* get a pointer to the current "line" */
   mclptr = mcptr->ParsePtr;

   /* if this is during server startup then set the global service pointer */
   if (HttpdServerStartup)
      smptr = mcptr->ServiceMetaPtr = ServiceMetaPtr = &ServiceMeta;
   else
   /* if a report then conjure one up ex nihlo */
   if (!mcptr->ServiceMetaPtr)
      smptr = mcptr->ServiceMetaPtr = VmGet (sizeof(SERVICE_META));
   else
      /* not the first time through */
      smptr = mcptr->ServiceMetaPtr;

   if (mclptr->Token == METACON_TOKEN_PRE)
   {
      /******************/
      /* pre-initialize */
      /******************/

      return (true);
   }

   if (mclptr->Token == METACON_TOKEN_POST)
   {
      /****************/
      /* post-process */
      /****************/

      /* if last loaded service add this to the list */
      if (smptr->ServiceLooksValid)
      {
         status = ServiceConfigAdd (mcptr, &smptr->ConfigService);
         if (VMSnok (status))
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemCannotConfig);
      }

      if (HttpdServerStartup)
      {
         ServiceListHead = smptr->ListHead;
         ServiceListTail = smptr->ListTail;
         ServiceCount = smptr->ServiceCount;
      }

      return (true);
   }

   /* if it's not a service or text then mapping is not interested in it! */
   if (mclptr->Token != METACON_TOKEN_SERVICE &&
       mclptr->Token != METACON_TOKEN_TEXT)
   {
      MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemUsage);
      return (true);
   }

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

   /* buffer the text associated with the current "line" */
   zptr = (sptr = StringBuffer) + sizeof(StringBuffer);
   cptr = mclptr->TextPtr;
   while (*cptr && sptr < zptr) *sptr++ = *cptr++;
   if (sptr >= zptr)
   {
      MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemOverflow);
      return (false);
   }
   *sptr = '\0';
   cptr = StringBuffer;

   /* this service config structure will be used as working storage */
   svptr = &smptr->ConfigService;

   if (mclptr->Token == METACON_TOKEN_SERVICE)
   {
      /***************************/
      /* next (or first) service */
      /***************************/

      /* if previous valid service add this to the list */
      if (smptr->ServiceLooksValid)
      {
         status = ServiceConfigAdd (mcptr, &smptr->ConfigService);
         if (VMSnok (status))
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemCannotConfig);
      }

      /* initialize service structure */
      memset (svptr, 0, sizeof(SERVICE_STRUCT));
      smptr->ServiceLooksValid = false;

      /*******************/
      /* process service */
      /*******************/

      /* avoid loading the "next new service" entry ;^) */
      if (strchr (cptr, '?')) return (true);

      retval = ServiceParse (cptr,
                             &svptr->RequestScheme,
                             &svptr->ServerHostName,
                             sizeof(svptr->ServerHostName),
                             &svptr->ServerPort,
                             &svptr->GenericService);
      if (retval == -1)
      {
         MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemInvalidService);
         return (true);
      }

      if (svptr->RequestScheme == SCHEME_HTTPS)
      {
         if (!ProtocolHttpsAvailable)
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemNoSSL);
            return (true);
         }
      }

      smptr->ServiceLooksValid = true;
      return (true);
   }

   /**********************/
   /* service directives */
   /**********************/

   if (*cptr == '[')
   {
      cptr++;
      for (sptr = cptr; *sptr && *sptr != ']'; sptr++);
      if (*sptr != ']')
      {
         MetaConReport (mcptr, METACON_REPORT_ERROR, "missing \']\'?");
         return (true);
      }
      *sptr++ = '\0';
      while (*sptr && ISLWS(*sptr)) sptr++;
   }

   /****************************************************/
   /* ignore directives unless server specification ok */
   /****************************************************/

   if (!smptr->ServiceLooksValid) return (true);

   ssptr = svptr->SSLserverPtr;
   scptr = svptr->SSLclientPtr;

   if (strsame (cptr, "ServiceAdmin", -1))
      svptr->AdminService = MetaConSetBoolean (mcptr, sptr);
   else
   if (strsame (cptr, "ServiceBind", -1) ||
       /* and for backward compatibility */
       strsame (cptr, "ServiceIPaddress", -1))
   {
      MetaConSetString (mcptr, sptr, svptr->BindIpAddressString,
                        sizeof(svptr->BindIpAddressString));
      /* the convention for IPv6 literal hexadecimal is  upper-case */
      for (cptr = svptr->BindIpAddressString; *cptr; cptr++)
         *cptr = toupper(*cptr);
   }
   else
   if (strsame (cptr, "ServiceBodyTag", -1))
      MetaConSetString (mcptr, sptr, svptr->BodyTag, sizeof(svptr->BodyTag));
   else
   if (strsame (cptr, "ServiceClientSSLcipherList", -1))
   {
      if (!scptr)
         svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT));
      MetaConSetString (mcptr, sptr, scptr->CipherList,
                        sizeof(scptr->CipherList));
   }
   else
   if (strsame (cptr, "ServiceClientSSLverifyCA", -1))
   {
      if (!scptr)
         svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT));
      scptr->VerifyCA = MetaConSetBoolean (mcptr, sptr);
   }
   else
   if (strsame (cptr, "ServiceClientSSLCaFile", -1))
   {
      if (!scptr)
         svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT));
      MetaConSetString (mcptr, sptr, scptr->CaFile, sizeof(scptr->CaFile));
   }
   else
   if (strsame (cptr, "ServiceClientSSLversion", -1))
   {
      if (!scptr)
         svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT));
      MetaConSetString (mcptr, sptr, scptr->VersionString,
                        sizeof(scptr->VersionString));
   }
   else
   if (strsame (cptr, "ServiceClientSSL", -1))
   {
      if (!scptr)
         svptr->SSLclientPtr = scptr = VmGet (sizeof(SESOLA_CONTEXT));
      svptr->SSLclientEnabled = MetaConSetBoolean (mcptr, sptr);
   }
   else
   if (strsame (cptr, "ServiceErrorReportPath", -1))
      MetaConSetString (mcptr, sptr, svptr->ErrorReportPath,
                        sizeof(svptr->ErrorReportPath));
   else
   if (strsame (cptr, "ServiceNoLog", -1))
      svptr->NoLog = MetaConSetBoolean (mcptr, sptr);
   else
   if (strsame (cptr, "ServiceNoTrack", -1))
      svptr->SetNoTrack = MetaConSetBoolean (mcptr, sptr);
   else
   if (strsame (cptr, "ServiceProxy", -1))
      svptr->ProxyService = MetaConSetBoolean (mcptr, sptr);
   else
   if (strsame (cptr, "ServiceProxyAuth", -1))
   {
      MetaConSetString (mcptr, sptr, StringScratch, sizeof(StringScratch));
      if (!StringScratch[0] || strsame (StringScratch, "none", -1))
         svptr->ProxyAuthRequired = svptr->LocalAuthRequired = false;
      else
      if (strsame (StringScratch, "PROXY", -1))
         svptr->ProxyAuthRequired = true;
      else
      if (strsame (StringScratch, "LOCAL", -1))
         svptr->LocalAuthRequired = true;
      else
      {
         MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemUnknownKeyword);
         smptr->ServiceLooksValid = false;
         return (true);
      }
   }
   else
   if (strsame (cptr, "ServiceProxyBind", -1))
   {
      MetaConSetString (mcptr, sptr, svptr->ProxyBindIpAddressString,
                        sizeof(svptr->ProxyBindIpAddressString));
      /* the convention for IPv6 literal hexadecimal is  upper-case */
      for (cptr = svptr->ProxyBindIpAddressString; *cptr; cptr++)
         *cptr = toupper(*cptr);
   }
   else
   if (strsame (cptr, "ServiceProxyCache", -1))
      svptr->ProxyFileCacheEnabled = MetaConSetBoolean (mcptr, sptr);
   else
   if (strsame (cptr, "ServiceProxyChain", -1))
      MetaConSetString (mcptr, sptr, svptr->ProxyChainHostPort,
                        sizeof(svptr->ProxyChainHostPort));
   else
   if (strsame (cptr, "ServiceProxySSL", -1))
      svptr->ConnectService = MetaConSetBoolean (mcptr, sptr);
   else
   if (strsame (cptr, "ServiceProxyTrack", -1))
      svptr->SetProxyTrack = MetaConSetBoolean (mcptr, sptr);
   else
   if (strsame (cptr, "ServiceSSLcert", -1))
   {
      if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT));
      MetaConSetString (mcptr, sptr, ssptr->CertFile, sizeof(ssptr->CertFile));
   }
   else
   if (strsame (cptr, "ServiceSSLkey", -1))
   {
      if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT));
      MetaConSetString (mcptr, sptr, ssptr->KeyFile, sizeof(ssptr->KeyFile));
   }
   else
   if (strsame (cptr, "ServiceSSLverifyPeer", -1) ||
       /* backward compatibility */
       strsame (cptr, "ServiceSSLclientVerify", -1) ||
       strsame (cptr, "ServiceSSLclientVerifyRequired", -1))
   {
      if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT));
      ssptr->VerifyPeer = MetaConSetBoolean (mcptr, sptr);
   }
   else
   if (strsame (cptr, "ServiceSSLverifyPeerCAfile", -1) ||
       /* backward compatibility */
       strsame (cptr, "ServiceSSLclientVerifyCaFile", -1))
   {
      if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT));
      MetaConSetString (mcptr, sptr, ssptr->CaFile, sizeof(ssptr->CaFile));
   }
   else
   if (strsame (cptr, "ServiceSSLcipherList", -1))
   {
      if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT));
      MetaConSetString (mcptr, sptr, ssptr->CipherList,
                        sizeof(ssptr->CipherList));
   }
   else
   if (strsame (cptr, "ServiceSSLversion", -1))
   {
      if (!ssptr) svptr->SSLserverPtr = ssptr = VmGet (sizeof(SESOLA_CONTEXT));
      MetaConSetString (mcptr, sptr, ssptr->VersionString,
                        sizeof(ssptr->VersionString));
   }
   else
      MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemDirective);

   return (true);
}

/*****************************************************************************/
/*
Add the service to the loaded list.
*/

int ServiceConfigAdd
(
META_CONFIG *mcptr,
SERVICE_STRUCT *svptr
)
{
   static BOOL  SetOfficialServer;

   int  status;
   char  *cptr;
   SERVICE_META  *smptr;
   SERVICE_STRUCT *nsvptr;

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

   if (WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE, "ServiceConfigAdd()");

   /* get a pointer to the meta-config data */
   smptr = mcptr->ServiceMetaPtr;

   status = NetHostNameLookup (mcptr,
                               svptr->ServerHostName,
                               svptr->ServerPort,
                               &svptr->ServerHostName,
                               &svptr->ServerHostPort,
                               &svptr->ServerIpAddressString,
                               &svptr->ServerIpAddress, NULL);
   if (VMSnok (status))
   {
      if (WATCH_MODULE(WATCH_MOD_SERVICE))
         WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE, "!&S !-!&M", status);
      return (status);
   }

   if (svptr->BindIpAddressString[0])
   {
      status = TcpIpStringToAddress (svptr->BindIpAddressString,
                                     &svptr->BindIpAddress);
      if (VMSnok (status))
         MetaConReport (mcptr, METACON_REPORT_ERROR,
                        "Problem with !AZ bind address !AZ",
                        svptr->ServerHostName, svptr->BindIpAddressString);
   }

   /************************/
   /* check/set parameters */
   /************************/

   if (svptr->AdminService)
   {
      if (ServiceAdminSpecified)
      {
         MetaConReport (mcptr, METACON_REPORT_ERROR,
                        "Redundant administration service");
         return (STS$K_ERROR);
      }
      ServiceAdminSpecified = true;
   }

   svptr->ServerHostPortLength = strlen(svptr->ServerHostPort);
   svptr->ServerHostNameLength = strlen(svptr->ServerHostName);
   sprintf (svptr->ServerPortString, "%d", svptr->ServerPort); 

   if (svptr->RequestScheme == SCHEME_HTTPS)
      svptr->RequestSchemeNamePtr = "https:";
   else
      svptr->RequestSchemeNamePtr = "http:";

   if (!svptr->ListenBacklog)
      svptr->ListenBacklog = Config.cfServer.ListenBacklog;
   if (!svptr->ListenBacklog)
      svptr->ListenBacklog = DEFAULT_LISTEN_BACKLOG;

   /*
      By default proxy services have tracking disabled, others enabled.
      Explicitly enabling tracking for a service overrides this for both.
      Explicitly disabling tracking for a service overrides this for both.
   */
   if (Config.cfTrack.Enabled)
   {
      if (svptr->RequestScheme == SCHEME_HTTP &&
          !svptr->ProxyService &&
          !svptr->ConnectService)
         svptr->TrackEnabled = true;
      if (svptr->ProxyService && svptr->SetProxyTrack)
         svptr->TrackEnabled = true;
      if (svptr->SetNoTrack) svptr->TrackEnabled = false;
   }

   if (svptr->ErrorReportPath[0])
      svptr->ServiceErrorReportPath = true;
   else
      strzcpy (svptr->ErrorReportPath,
               Config.cfReport.ErrorReportPath,
               sizeof(svptr->ErrorReportPath));

   /*****************************/
   /* proxy service information */
   /*****************************/

   if (svptr->ProxyService || svptr->ConnectService)
   {
      if (svptr->ProxyChainHostPort[0])
      {
         status = NetHostNameLookup (mcptr,
                                     svptr->ProxyChainHostPort, 0,
                                     &svptr->ProxyChainHostName,
                                     &svptr->ProxyChainHostPort,
                                     &svptr->ProxyChainIpAddressString,
                                     &svptr->ProxyChainIpAddress, NULL);
         if (VMSnok (status)) return (status);

         svptr->ProxyChainHostNameLength = strlen(svptr->ProxyChainHostName);
         if (!svptr->ProxyChainPort)
            svptr->ProxyChainPort = DEFAULT_HTTP_PROXY_PORT;
         sprintf (svptr->ProxyChainPortString, "%d", svptr->ProxyChainPort);
      }
   }

   if (svptr->ProxyBindIpAddressString[0])
   {
      status = TcpIpStringToAddress (svptr->ProxyBindIpAddressString,
                                     &svptr->ProxyBindIpAddress);
      if (VMSnok (status))
         MetaConReport (mcptr, METACON_REPORT_ERROR,
                        "Problem with !AZ proxy bind address !AZ",
                        svptr->ServerHostName, svptr->ProxyBindIpAddressString);
   }

   /***************************************/
   /* allocate memory add and new service */
   /***************************************/

   /* copy the filled-out service structure into an in-list structure */
   nsvptr = VmGet (sizeof (SERVICE_STRUCT));
   memcpy (nsvptr, svptr, sizeof(SERVICE_STRUCT));

   if (svptr->SSLserverPtr)
   {
      /* SESOLA server structure */
      nsvptr->SSLserverPtr = VmGet (sizeof(SESOLA_CONTEXT));
      memcpy (nsvptr->SSLserverPtr,
              svptr->SSLserverPtr,
              sizeof(SESOLA_CONTEXT));
   }

   if (svptr->SSLclientPtr)
   {
      /* SESOLA client structure */
      nsvptr->SSLclientPtr = VmGet (sizeof(SESOLA_CONTEXT));
      memcpy (nsvptr->SSLclientPtr,
              svptr->SSLclientPtr,
              sizeof(SESOLA_CONTEXT));
   }

   if (!smptr->ListHead)
      smptr->ListHead = smptr->ListTail = nsvptr;
   else
   {
      smptr->ListTail->NextPtr = nsvptr;
      smptr->ListTail = nsvptr;
   }
   nsvptr->NextPtr = NULL;

   smptr->ServiceCount++;

   /*********/
   /* other */
   /*********/

   /* process the error report path and any associated status codes */
   for (cptr = nsvptr->ErrorReportPath; *cptr && !ISLWS(*cptr); cptr++);
   nsvptr->ErrorReportPathLength = cptr - nsvptr->ErrorReportPath;
   if (*cptr) *cptr++ = '\0';
   while (*cptr && ISLWS(*cptr)) cptr++;
   nsvptr->ErrorReportPathCodesPtr = cptr;
   nsvptr->ErrorReportPathCodesCount = 0;
   while (*cptr &&
          nsvptr->ErrorReportPathCodesCount <
          SERVICE_ERROR_REPORT_PATH_CODES_MAX)
   {
      while (*cptr && ISLWS(*cptr)) cptr++;
      if (isdigit(*cptr)) 
         nsvptr->ErrorReportPathCodes[nsvptr->ErrorReportPathCodesCount++] =
            atoi(cptr);
      while (*cptr && !ISLWS(*cptr)) cptr++;
   }
   if (nsvptr->ErrorReportPathCodesCount >=
       SERVICE_ERROR_REPORT_PATH_CODES_MAX)
      MetaConReport (mcptr, METACON_REPORT_ERROR,
                     "Error report path codes problem");

   /* process name, etc., generated from the primary port */
   if (!SetOfficialServer)
   {
      /* the host and port for the "official" server */
      SetOfficialServer = ServerPort = nsvptr->ServerPort;
      sprintf (ServerHostPort, "%s:%d", ServerHostName, ServerPort);
   }

   if (nsvptr->RequestScheme == SCHEME_HTTPS)
      if (ProtocolHttpsAvailable)
         ProtocolHttpsConfigured = true;

   return (SS$_NORMAL);
}

/*****************************************************************************/
/*
Parse a HTTP scheme, host name, and integer port number from a string. 
Defaults apply to missing components.  Some basic checking is performed on the
components.  Return the number of characters scanned from the string, or -1 to
indicate a fatal error.
*/ 

int ServiceParse
(
char *SourceString,
int *SchemeValuePtr,
char *HostNamePtr,
int SizeOfHostName,
int *PortNumberPtr,
BOOL *GenericServicePtr
)
{
   BOOL  IsHost;
   char  *cptr, *sptr, *tptr, *zptr;

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

   if (WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE,
                 "ServiceParse() !&Z", SourceString);

   *SchemeValuePtr = *PortNumberPtr = 0;
   *HostNamePtr = '\0';
   *GenericServicePtr = false;

   cptr = SourceString;

   if (strsame (cptr, "http:", 5))
   {
      *SchemeValuePtr = SCHEME_HTTP;
      cptr += 5;
   }
   else
   if (strsame (cptr, "https:", 6))
   {
      *SchemeValuePtr = SCHEME_HTTPS;
      cptr += 6;
   }
   else
   {
      /* if it looks like an incorrect scheme specification */
      for (tptr = cptr; *tptr && *tptr != '.' && *tptr != ':'; tptr++);
      if (tptr[0] == ':' && !isdigit(tptr[1]) && tptr[1] != '*') return (-1);
      /* otherwise it defaults to */
      *SchemeValuePtr = SCHEME_HTTP;
   }

   if (*cptr == '/') cptr++;
   if (*cptr == '/') cptr++;

   if (isdigit(*cptr))
   {
      /* check if it's a dotted-decimal address */
      for (tptr = cptr; *tptr && isdigit(*tptr); tptr++);
      if (*tptr == '.')
      {
         /* dotted decimal address */
         zptr = (sptr = HostNamePtr) + SizeOfHostName;
         while (*cptr && (*cptr == '.' || isdigit(*cptr)) && sptr < zptr)
            *sptr++ = *cptr++;
         if (sptr >= zptr) return (-1);
         *sptr = '\0';
      }
   }
   else
   if (*cptr == '*')
   {
      /* generic service */
      while (*cptr && *cptr == '*') cptr++;
   }
   else
   {
      /* alphanumeric host name, or numeric address */
      zptr = (sptr = HostNamePtr) + SizeOfHostName;
      while (*cptr && (*cptr == '*' || *cptr == '.' || isalnum(*cptr) ||
                       *cptr == '-' || *cptr == '_') && sptr < zptr)
         *sptr++ = *cptr++;
      if (sptr >= zptr) return (-1);
      *sptr = '\0';
   }
   if (!HostNamePtr[0])
   {
      /* none or generic specified, default to primary server name */
      *GenericServicePtr = true;
      zptr = (sptr = HostNamePtr) + SizeOfHostName;
      for (tptr = ServerHostName; *tptr && sptr < zptr; *sptr++ = *tptr++);
      if (sptr >= zptr) return (-1);
      *sptr = '\0';
   }

   if (isdigit(*cptr) || (cptr[0] == ':' && isdigit(cptr[1])))
   {
      /* IP port */
      if (*cptr == ':') cptr++;
      *PortNumberPtr = atol(cptr);
      while (*cptr && isdigit(*cptr)) cptr++;
      if (*PortNumberPtr <= 0 || *PortNumberPtr > 65535) return (-1);
   }
   else
   if (*SchemeValuePtr == SCHEME_HTTPS)
      *PortNumberPtr = DEFAULT_HTTPS_PORT;
   else
      *PortNumberPtr = DEFAULT_HTTP_PORT;

   return (cptr - SourceString);
}

/*****************************************************************************/
/*
Parse the 'Service's parameter to determine which host(s)/port(s) services need
to be provided for.  This is a comma or newline separated list with no
white-space.  The service information may comprise of optional components:
"[scheme://][host][:port][;bind=address][;proxy][;chain=server][;cert=file]".
*/ 

int ServiceConfigFromString (META_CONFIG **MetaConPtrPtr)

{
   BOOL  ConnectService,
         LocalAuthRequired,
         ProxyAuthRequired,
         ProxyService,
         ProxyServiceFileCache,
         SesolaServiceUsed,
         TrackEnabled,
         TrackDisabled;
   int  status,
        DummyRequestScheme,
        Length,
        ListenBacklog;
   char  *cptr, *sptr, *zptr;
   META_CONFIG  *mcptr;
   SERVICE_META  *smptr;
   SERVICE_STRUCT  *svptr;
   SESOLA_CONTEXT  SesolaServerService;
   SERVICE_STRUCT  ConfigService;
   char  StringScratch [256];

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

   if (WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE,
                 "ServiceConfigFromString() !&Z !&Z !AZ:!UL",
                 CliServices, Config.cfServer.ServicePtr,
                 ServerHostName, ServerPort);

   status = MetaConLoad (MetaConPtrPtr, NULL, NULL, false, false);
   if (VMSnok (status)) return (status);

   mcptr = *MetaConPtrPtr;
   smptr = mcptr->ServiceMetaPtr = ServiceMetaPtr = &ServiceMeta;

   if (CliServices[0])
      cptr = CliServices;
   else
   if (!Config.cfServer.ServicePtr)
   {
      if (ProtocolHttpsAvailable)
         sprintf (cptr = CliServices, "http://%s:80,https://%s:443",
                  ServerHostName, ServerHostName);
      else
         sprintf (cptr = CliServices, "http://%s:80", ServerHostName);
   }
   else
      cptr = Config.cfServer.ServicePtr;

   /******************************/
   /* parse comma-separated list */
   /******************************/

   while (*cptr)
   {
      while (*cptr && (isspace(*cptr) || *cptr == ',')) cptr++;
      if (!*cptr) break;

      memset (&ConfigService, 0, sizeof(ConfigService));
      memset (&SesolaServerService, 0, sizeof(SesolaServerService));
      SesolaServiceUsed = false;

      /*************************/
      /* service specification */
      /*************************/

      Length = ServiceParse (cptr,
                             &ConfigService.RequestScheme,
                             &ConfigService.ServerHostName,
                             sizeof(ConfigService.ServerHostName),
                             &ConfigService.ServerPort,
                             &ConfigService.GenericService);
      if (Length < 0) ErrorExitVmsStatus (0, ErrorServiceList, FI_LI);
      cptr += Length;

      if (!ProtocolHttpsAvailable &&
          ConfigService.RequestScheme == SCHEME_HTTPS)
      {
         WriteFaoStdout (
"%!AZ-W-SERVICE, SSL not available, service ignored\n \\!AZ//!AZ:!UL\\\n",
            Utility, ConfigService.RequestSchemeNamePtr,
                     ConfigService.ServerHostPort);
         continue;
      }

      if (ConfigService.RequestScheme == SCHEME_HTTPS)
      {
         ConfigService.RequestSchemeNamePtr = "https:";
         SesolaServiceUsed = true;
      }
      else
         ConfigService.RequestSchemeNamePtr = "http:";

      /****************************/
      /* other service parameters */
      /****************************/

      while (*cptr == ';')
      {
         if (strsame (cptr, ";admin", 6))
            ConfigService.AdminService = true;
         else
         if (strsame (cptr, ";backlog=", 9))
         {
            cptr += 9;
            ConfigService.ListenBacklog = atoi(cptr);
         }
         else
         if (strsame (cptr, ";bind=", 6) ||
             /* for backward compatibility */
             strsame (cptr, ";ip=", 4))
         {
            while (*cptr && *cptr != '=') cptr++;
            if (*cptr) cptr++;
            zptr = (sptr = ConfigService.BindIpAddressString) +
                    sizeof(ConfigService.BindIpAddressString);
            while (*cptr && !isspace(*cptr) && *cptr != ',' &&
                   *cptr != ';' && sptr < zptr) *sptr++ = *cptr++;
            if (sptr >= zptr)
               ErrorExitVmsStatus (0, ErrorServiceList, FI_LI);
            *sptr = '\0';
         }
         else
         if (strsame (cptr, ";cafile=", 8))
         {
            cptr += 8;
            zptr = (sptr = SesolaServerService.CaFile) +
                   sizeof(SesolaServerService.CaFile);
            while (*cptr && !isspace(*cptr) && *cptr != ',' &&
                   *cptr != ';' && sptr < zptr) *sptr++ = toupper(*cptr++);
            if (sptr >= zptr)
               ErrorExitVmsStatus (0, ErrorServiceList, FI_LI);
            *sptr = '\0';
            SesolaServiceUsed = true;
         }
         else
         if (strsame (cptr, ";cache", 6))
            ConfigService.ProxyFileCacheEnabled = true;
         else
         if (strsame (cptr, ";cert=", 6))
         {
            cptr += 6;
            zptr = (sptr = SesolaServerService.CertFile) +
                   sizeof(SesolaServerService.CertFile);
            while (*cptr && !isspace(*cptr) && *cptr != ',' &&
                   *cptr != ';' && sptr < zptr) *sptr++ = toupper(*cptr++);
            if (sptr >= zptr)
               ErrorExitVmsStatus (0, ErrorServiceList, FI_LI);
            *sptr = '\0';
            SesolaServiceUsed = true;
         }
         else
         if (strsame (cptr, ";chain=", 7))
         {
            cptr += 7;
            zptr = (sptr = ConfigService.ProxyChainHostPort) +
                   sizeof(ConfigService.ProxyChainHostPort);
            while (*cptr && !isspace(*cptr) && *cptr != ',' &&
                   *cptr != ';' && sptr < zptr) *sptr++ = *cptr++;
            if (sptr >= zptr)
               ErrorExitVmsStatus (0, ErrorServiceList, FI_LI);
            *sptr = '\0';
         }
         else
         if (strsame (cptr, ";connect", 8))
            ConfigService.ConnectService = true;
         else
         if (strsame (cptr, ";cipher=", 8))
         {
            cptr += 8;
            zptr = (sptr = SesolaServerService.CipherList) +
                   sizeof(SesolaServerService.CipherList);
            while (*cptr && !isspace(*cptr) && *cptr != ',' &&
                   *cptr != ';' && sptr < zptr) *sptr++ = *cptr++;
            if (sptr >= zptr)
               ErrorExitVmsStatus (0, ErrorServiceList, FI_LI);
            *sptr = '\0';
            SesolaServiceUsed = true;
         }
         else
         if (strsame (cptr, ";key=", 5))
         {
            cptr += 5;
            zptr = (sptr = SesolaServerService.KeyFile) +
                   sizeof(SesolaServerService.KeyFile);
            while (*cptr && !isspace(*cptr) && *cptr != ',' &&
                   *cptr != ';' && sptr < zptr) *sptr++ = toupper(*cptr++);
            if (sptr >= zptr)
               ErrorExitVmsStatus (0, ErrorServiceList, FI_LI);
            *sptr = '\0';
            SesolaServiceUsed = true;
         }
         else
         if (strsame (cptr, ";lauth", 6))
            ConfigService.LocalAuthRequired = true;
         else
         if (strsame (cptr, ";pauth", 6))
            ConfigService.ProxyAuthRequired = true;
         else
         if (strsame (cptr, ";proxy", 6))
            ConfigService.ProxyService = true;
         else
         if (strsame (cptr, ";pbind=", 7))
         {
            cptr += 7;
            zptr = (sptr = ConfigService.ProxyBindIpAddressString) +
                    sizeof(ConfigService.ProxyBindIpAddressString);
            while (*cptr && !isspace(*cptr) && *cptr != ',' &&
                   *cptr != ';' && sptr < zptr) *sptr++ = *cptr++;
            if (sptr >= zptr)
               ErrorExitVmsStatus (0, ErrorServiceList, FI_LI);
            *sptr = '\0';
         }
         else
         if (strsame (cptr, ";pclientssl", 11))
         {
            if (!ConfigService.SSLclientPtr)
               ConfigService.SSLclientPtr = VmGet (sizeof(SESOLA_CONTEXT));
            ConfigService.SSLclientEnabled = true;
         }
         else
         if (strsame (cptr, ";track", 6))
            ConfigService.SetProxyTrack = true;
         else
         if (strsame (cptr, ";notrack", 8))
            ConfigService.SetNoTrack = true;
         else
         if (strsame (cptr, ";verify", 7))
            SesolaServiceUsed =  SesolaServerService.VerifyPeer = true;
         else
         {
            if (*cptr == ';') cptr++;
            sptr = cptr;
            while (*sptr && *sptr != ';' && *sptr != ',') sptr++;
            WriteFaoStdout (
               "%!AZ-W-SERVICE, unknown parameter, ignored\n \\!#AZ\\\n",
               Utility, sptr-cptr, cptr);
            cptr = sptr;
         }

         if (*cptr == ';') cptr++;
         while (*cptr && !isspace(*cptr) && *cptr != ',' && *cptr != ';')
            cptr++;
      }

      /***************/
      /* add service */
      /***************/

      if (SesolaServiceUsed) ConfigService.SSLserverPtr = &SesolaServerService;

      status = ServiceConfigAdd (mcptr, &ConfigService);
      if (VMSnok (status))
      {
         WriteFaoStdout (
            "%!AZ-W-SERVICE, cannot configure service\n \\!AZ//!AZ\\\n",
            Utility, ConfigService.RequestSchemeNamePtr,
                     ConfigService.ServerHostPort);
         continue;
      }

      if (*cptr && *cptr != ',' && !isspace(*cptr))
         ErrorExitVmsStatus (0, ErrorServiceList, FI_LI);
   }

   if (HttpdServerStartup)
   {
      ServiceListHead = smptr->ListHead;
      ServiceListTail = smptr->ListTail;
      ServiceCount = smptr->ServiceCount;
   }

   return (SS$_NORMAL);
}

/*****************************************************************************/
/*
Search the list of virtual services for one matching.  If either host or port
component is omitted or specified as a wildcard then match any.
*/ 

BOOL ServiceIsConfigured (char *HostPort)

{
   char  *cptr, *sptr;
   SERVICE_STRUCT  *svptr;

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

   if (WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE,
                 "ServiceIsConfigured() !&Z", HostPort);

   for (svptr = ServiceListHead; svptr; svptr = svptr->NextPtr)
   {
      if (WATCH_MODULE(WATCH_MOD_SERVICE))
         WatchDataFormatted ("!&Z\n", svptr->ServerHostPort);

      cptr = HostPort;
      sptr = svptr->ServerHostPort;
      /* compare the host name portion */
      if (*cptr == ':') while (*sptr && *sptr != ':') sptr++;
      while (*cptr && *cptr != ':' && *sptr && *sptr != ':')
      {
         if (*cptr == '*')
         {
            while (*cptr == '*') cptr++;
            while (*sptr && tolower(*sptr) != tolower(*cptr)) sptr++;
            if (!*cptr || *cptr == ':' || !*sptr || *sptr == ':') break;
         }
         if (tolower(*cptr) != tolower(*sptr)) break;
         cptr++;
         sptr++;
      }
      /* continue if the host name portion did not match */
      if ((*cptr && *cptr != ':') || *sptr != ':') continue;
      /* return true if the parameter did not contain a specific port */
      if (!*cptr)
      {
         if (WATCH_MODULE(WATCH_MOD_SERVICE))
            WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE, "YES");
         return (true);
      }
      cptr++;
      sptr++;
      /* compare the port portion */
      while (*cptr && *sptr)
      {
         if (*cptr == '*')
         {
            while (*cptr == '*') cptr++;
            while (*sptr && *sptr != *cptr) sptr++;
            if (!*cptr || !*sptr) break;
         }
         if (*cptr != *sptr) break;
         cptr++;
         sptr++;
      }
      /* return true if the ports matched */
      if (!*cptr && !*sptr)
      {
         if (WATCH_MODULE(WATCH_MOD_SERVICE))
            WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE, "YES");
         return (true);
      }
   }

   if (WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE, "NO");
   return (false);
}

/*****************************************************************************/
/*
Find a service' host name and port that matches the request's "Host:" request
header line.  Point the request's service pointer at the virtual service's
structure is found.  Return true if a matching service was found, false if not.
*/ 

BOOL ServiceFindVirtual (REQUEST_STRUCT *rqptr)

{
   int  HostNameLength,
        HostPortLength;
   char  *sptr1, *sptr2;
   SERVICE_STRUCT  *svptr;

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SERVICE,
                 "ServiceFindVirtual() !&Z", rqptr->rqHeader.HostPtr);

   if (!rqptr->rqHeader.HostPtr || !rqptr->rqHeader.HostPtr[0])
   {
      /* just use whatever service the request connected to */
      if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_CONNECT))
         WatchThis (rqptr, FI_LI, WATCH_CONNECT,
                    "ACTUAL !AZ", rqptr->ServicePtr->ServerHostPort); 
      return (true);
   }

   for (sptr1 = rqptr->rqHeader.HostPtr; *sptr1 && *sptr1 != ':'; sptr1++);
   HostNameLength = sptr1 - rqptr->rqHeader.HostPtr;
   while (*sptr1) sptr1++;
   HostPortLength = sptr1 - rqptr->rqHeader.HostPtr;

   for (svptr = ServiceListHead; svptr; svptr = svptr->NextPtr)
   {
      if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_SERVICE))
         WatchThis (rqptr, FI_LI, WATCH_MOD_SERVICE,
                    "!AZ", svptr->ServerHostPort); 

      if (HostNameLength != svptr->ServerHostNameLength) continue;
      if (HostNameLength == HostPortLength)
      {
         /* no port specified, use default for request scheme */
         switch (rqptr->ServicePtr->RequestScheme)
         {
            case SCHEME_HTTP :
               if (svptr->ServerPort != DEFAULT_HTTP_PORT) continue;
               break;
            case SCHEME_HTTPS :
               if (svptr->ServerPort != DEFAULT_HTTPS_PORT) continue;
               break;
            default :
               continue;
         }
      }
      else
      if (HostPortLength != svptr->ServerHostPortLength)
         continue;

      sptr1 = rqptr->rqHeader.HostPtr;
      /* only if a port was included in the "Host:" compare that */
      if (HostNameLength == HostPortLength)
         sptr2 = svptr->ServerHostName;
      else
         sptr2 = svptr->ServerHostPort;
      while (*sptr1 && *sptr2)
      {
         if (tolower (*sptr1) == tolower (*sptr2))
         {
            sptr1++;
            sptr2++;
         }
         else
            break;
      }
      if (*sptr1 || *sptr2) continue;

      if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_CONNECT))
         WatchThis (rqptr, FI_LI, WATCH_CONNECT,
                    "VIRTUAL !AZ", svptr->ServerHostPort); 

      /* change the request's service pointer to it's virtual equivalent */
      rqptr->ServicePtr = svptr;

      return (true);
   }

   if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_CONNECT))
      WatchThis (rqptr, FI_LI, WATCH_CONNECT, "UNKNOWN service"); 

   return (false);
}

/*****************************************************************************/
/*
Output string configured services (called from ConfigReportNow()).
*/ 

int ServiceReportConfigFromString
(
REQUEST_STRUCT *rqptr,
CONFIG_STRUCT *cfptr
)
{
   static char  ServicesFao [] = 
"<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>Service</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n";

   static char  ServiceLoadFao [] =
"<TR><TH ALIGN=center>(see &quot;Services&quot; report)</TH></TR>\n";

   static char  ServiceNoneFao [] =
"<TR><TD><I>(none)</I></TD></TR>\n";

   static char  OneServiceFao [] =
"<TR><TH ALIGN=right>!UL.&nbsp;</TH>\
<TD><TT>!AZ//!AZ!AZ!&@!AZ!&@!&@!AZ!AZ!AZ!AZ</TT></TD>\
<TD>&nbsp;<TT>!AZ</TT></TD></TR>\n";

   static char  ServiceListFao [] =
"<TR><TH ALIGN=right>!UL.&nbsp;</TH><TD><TT>!AZ<TT></TD></TR>\n";

   static char  EndServicesFao [] =
"<TR><TD COLSPAN=2>\
<NOBR><B>Service Not Found URL:&nbsp;</B></NOBR>!AZ\
</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n";

   static char  NotNoneFao [] = "<I>(none)</I>\n";

   int  status,
        ServiceListCount;
   unsigned long  FaoVector [32];
   char  *cptr, *sptr;
   unsigned long  *vecptr;
   SERVICE_STRUCT  *svptr;

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

   if (WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (NULL, FI_LI, WATCH_MOD_SERVICE,
                 "ServiceReportConfigFromString()");

   status = NetWriteFaol (rqptr, ServicesFao, NULL);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

   if (ServiceLoadFromConfigFile)
   {
      status = NetWriteFaol (rqptr, ServiceLoadFao, NULL);
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
   }
   else
   if (!cfptr->cfServer.ServicePtr)
   {
      status = NetWriteFaol (rqptr, ServiceNoneFao, NULL);
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
   }
   else
   if (cfptr != &Config)
   {
      ServiceListCount = 1;
      for (cptr = cfptr->cfServer.ServicePtr; *cptr; cptr++)
      {
         sptr = cptr;
         vecptr = FaoVector;
         *vecptr++ = ServiceListCount++;
         *vecptr++ = sptr;
         while (*cptr && *cptr != ',') cptr++;
         if (*cptr)
         {
            *cptr = '\0';
            status = NetWriteFaol (rqptr, ServiceListFao, &FaoVector);
            if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
            *cptr = ',';
         }
         else
         {
            status = NetWriteFaol (rqptr, ServiceListFao, &FaoVector);
            if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
         }
      }
   }
   else
   {
      ServiceListCount = 1;
      for (svptr = ServiceListHead; svptr; svptr = svptr->NextPtr)
      {
         vecptr = FaoVector;

         *vecptr++ = ServiceListCount++;
         *vecptr++ = svptr->RequestSchemeNamePtr;
         *vecptr++ = svptr->ServerHostPort;

         if (svptr->ProxyFileCacheEnabled)
            *vecptr++ = ";cache";
         else
            *vecptr++ = "";
         if (svptr->ProxyChainHostPort[0])
         {
            *vecptr++ = ";chain=!AZ";
            *vecptr++ = svptr->ProxyChainHostPort;
         }
         else
            *vecptr++ = "";
         if (svptr->ConnectService)
            *vecptr++ = ";connect";
         else
            *vecptr++ = "";
         if (svptr->BindIpAddressString[0])
         {
            *vecptr++ = ";ip=!AZ";
            *vecptr++ = svptr->BindIpAddressString;
         }
         else
            *vecptr++ = "";
         if (svptr->ListenBacklog != DEFAULT_LISTEN_BACKLOG)
         {
            *vecptr++ = ";backlog=!UL";
            *vecptr++ = svptr->ListenBacklog;
         }
         else
            *vecptr++ = "";
         if (svptr->LocalAuthRequired)
            *vecptr++ = ";lauth";
         else
            *vecptr++ = "";
         if (svptr->ProxyAuthRequired)
            *vecptr++ = ";pauth";
         else
            *vecptr++ = "";
         if (svptr->ProxyService)
            *vecptr++ = ";proxy";
         else
            *vecptr++ = "";
         if (svptr->TrackEnabled)
            *vecptr++ = ";track";
         else
            *vecptr++ = "";

         *vecptr++ = svptr->ServerIpAddressString;

         status = NetWriteFaol (rqptr, OneServiceFao, &FaoVector);
         if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
      }
   }

   vecptr = FaoVector;
   if (cfptr->cfServer.ServiceNotFoundUrl[0])
      *vecptr++ = cfptr->cfServer.ServiceNotFoundUrl;
   else
      *vecptr++ = NotNoneFao;

   status = NetWriteFaol (rqptr, EndServicesFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

   return (SS$_NORMAL);
}

/*****************************************************************************/
/*
A server administration report on the server's configuration. This function
just wraps the reporting function, loading a temporary database if necessary
for reporting from the configuration file.
*/ 

ServiceReport
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction,
BOOL UseServerDatabase
)
{
   int  status;
   META_CONFIG  *mcptr;

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SERVICE,
                 "ServiceReport() !&F !&A !UL",
                 &ServiceReport, NextTaskFunction, UseServerDatabase);

   if (UseServerDatabase)
      ServiceReportNow (rqptr, MetaGlobalServicePtr);
   else
   {
      status = ServiceConfigLoad (&mcptr);
      if (VMSnok (status))
      {
         /* severe error reported */
         rqptr->rqResponse.HttpStatus = 403;
         ErrorGeneral (rqptr, mcptr->LoadReport.TextPtr, FI_LI);
      }
      else
         ServiceReportNow (rqptr, mcptr);
      MetaConUnload (&mcptr, NULL);
   }

   SysDclAst (NextTaskFunction, rqptr);
}

/*****************************************************************************/
/*
Return a report on the HTTPd service configuration ... now!  This function
blocks while executing.
*/ 

ServiceReportNow
(
REQUEST_STRUCT *rqptr,
META_CONFIG *mcptr
)
{
#define REPORT_ENDIS(b) \
{ if (b) *vecptr++ = "[enabled]"; else *vecptr++ = "[disabled]"; }

#define REPORT_STRING_DEFAULT(b) \
{ if (b && b[0]) *vecptr++ = b; else *vecptr++ = ReportDefault; }

#define REPORT_STRING_NONE(b) \
{ if (b && b[0]) *vecptr++ = b; else *vecptr++ = ReportNotNone; }

   static char  ReportServiceFao [] =
"<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>&nbsp;&nbsp;!UL&nbsp;&nbsp;-&nbsp;&nbsp;\
<A HREF=\"!AZ//!AZ\">!AZ//!AZ</A>&nbsp;&nbsp;</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0>\n\
<TR><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right>Service IP Address:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Bind IP Address:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Socket IP Address:</TH><TD>!&@</TD></TR>\n\
!&@\
<TR><TH ALIGN=right>Device:</TH><TD>!&@</TD></TR>\n";

   static char  ReportSslServiceFao [] =
"<TR><TD HEIGHT=3></TD></TR>\n\
<TR><TH ALIGN=right>SSL Version:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>SSL Certificate:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>SSL Private Key:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>SSL Verify Peer:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>SSL Verify Peer CA File:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>SSL Cipher List:</TH><TD>!AZ</TD></TR>\n";

   static char  ReportSslClientFao [] =
"<TR><TD HEIGHT=3></TD></TR>\n\
<TR><TH ALIGN=right>SSL/Client</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>SSL/Client Version:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>SSL/Client Verify CA:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>SSL/Client Verify CA File:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>SSL/Client Cipher List:</TH><TD>!AZ</TD></TR>\n";

   static char  ReportProxyFao [] =
"<TR><TD HEIGHT=3></TD></TR>\n\
<TR><TH ALIGN=right>Proxy:</TH><TD>!AZ</TD></TR>\n";

   static char  ReportProxyDetailsFao [] =
"<TR><TH ALIGN=right>Proxy SSL (CONNECT):</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Proxy Bind IP Address:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Proxy Cache:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Proxy Authorization:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Proxy Chain:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Proxy Track:</TH><TD>!AZ</TD></TR>\n";

   static char  ReportOtherFao [] =
"<TR><TD HEIGHT=3></TD></TR>\n\
<TR><TH ALIGN=right>Admin:</TH><TD>!&@</TD></TR>\n\
<TR><TH ALIGN=right>NoLog:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>NoTrack:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Body Tag:</TH><TD COLSPAN=2>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Report Path:</TH><TD COLSPAN=2>!&@</TD></TR>\n\
<TR><TD HEIGHT=3></TD></TR>\n\
<TR><TH ALIGN=right>Access Log:</TH><TD>!&@</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n";

   static char  EndPageFao [] =
"</TABLE>\n\
</BODY>\n\
</HTML>\n";

   static char  ReportDefault [] = "<I>(default)</I>",
                ReportNotAppl [] = "<I>(n/a)</I>",
                ReportNotNone [] = "<I>(none)</I>",
                ReportServiceNoneFao [] = "<I>(no services defined)</I>";

   int  idx, status,
        ServiceListCount;
   unsigned long  *vecptr;
   unsigned long  FaoVector [64];
   char  *cptr, *sptr, *zptr;
   SERVICE_META  *smptr;
   SERVICE_STRUCT  *svptr;
   SESOLA_CONTEXT  *scptr, *ssptr;

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SERVICE, "ServiceReportNow()");

   /* get a pointer to the meta-config data */
   smptr = mcptr->ServiceMetaPtr;

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   RESPONSE_HEADER_200_HTML (rqptr);
   AdminPageTitle (rqptr, "Service Configuration");
   AdminMetaConReport (rqptr, mcptr, MetaGlobalServicePtr);
   if (ServiceConfigFileDefined)
      AdminMetaConSource (rqptr, mcptr, MetaGlobalServicePtr,
                          mcptr->IncludeFile);

   /************/
   /* services */
   /************/

   if (!smptr->ListHead)
   {
      status = NetWriteFaol (rqptr, ReportServiceNoneFao, NULL);
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
   }
   else
   {
      ServiceListCount = 0;
      for (svptr = smptr->ListHead; svptr; svptr = svptr->NextPtr)
      {
         ServiceListCount++;

         vecptr = FaoVector;

         *vecptr++ = ServiceListCount;
         *vecptr++ = svptr->RequestSchemeNamePtr;
         *vecptr++ = svptr->ServerHostPort;
         *vecptr++ = svptr->RequestSchemeNamePtr;
         *vecptr++ = svptr->ServerHostPort;

         *vecptr++ = svptr->ServerIpAddressString;
         REPORT_STRING_NONE (svptr->BindIpAddressString)

         if (svptr->ServerChannel)
         {
            *vecptr++ = "!&I!&? (INADDR_ANY)\r\r";
            *vecptr++ = &svptr->ServerIpAddress;
            *vecptr++ = IPADDRESS_IS_ANY (&svptr->ServerIpAddress);
         }
         else
         if (mcptr == MetaGlobalServicePtr)
         {
            if (svptr->BgDevName[0])
            {
               *vecptr++ = "<I>(!AZ)</I>";
               *vecptr++ = svptr->BgDevName;
            }
            else
               *vecptr++ = ReportNotNone;
         }
         else
            *vecptr++ = ReportNotAppl;

         if (mcptr == MetaGlobalServicePtr)
         {
            if (VMSnok (svptr->ServerBindStatus))
            {
               if (svptr->ServerBindStatus)
               {
                  *vecptr++ = 
"<TR><TH ALIGN=right>Bind:</TH><TD>\
<FONT COLOR=\"#ff0000\">!&m<FONT></TD></TR>\n";
                  *vecptr++ = svptr->ServerBindStatus;
               }
               else
                  *vecptr++ = 
"<TR><TH ALIGN=right>Bind:</TH><TD>reused existing socket</TD></TR>\n";
            }
            else
               *vecptr++ = "";
         }
         else
            *vecptr++ = "";

         if (svptr->ServerChannel)
         {
            *vecptr++ = "!AZ (!UL channel!%s)";
            *vecptr++ = svptr->BgDevName;
            *vecptr++ = NetGetRefCnt (svptr->ServerChannel);
         }
         else
         if (svptr->BgDevName[0])
         {
            *vecptr++ = "<I>(!AZ)</I>";
            *vecptr++ = svptr->BgDevName;
         }
         else
            *vecptr++ = ReportNotNone;

         status = NetWriteFaol (rqptr, ReportServiceFao, &FaoVector);
         if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

         if (ssptr = svptr->SSLserverPtr)
         {
            vecptr = FaoVector;

            REPORT_STRING_DEFAULT (ssptr->VersionStringPtr)
            REPORT_STRING_DEFAULT (ssptr->CertFilePtr)
            REPORT_STRING_DEFAULT (ssptr->KeyFilePtr)
            REPORT_ENDIS (ssptr->VerifyPeer)
            REPORT_STRING_DEFAULT (ssptr->CaFilePtr)
            REPORT_STRING_DEFAULT (ssptr->CipherListPtr)

            status = NetWriteFaol (rqptr, ReportSslServiceFao, &FaoVector);
            if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
         }

         if (scptr = svptr->SSLclientPtr)
         {
            vecptr = FaoVector;

            REPORT_ENDIS (svptr->SSLclientEnabled)
            REPORT_STRING_DEFAULT (scptr->VersionStringPtr)
            REPORT_ENDIS (scptr->VerifyCA)
            REPORT_STRING_DEFAULT (scptr->CaFilePtr)
            REPORT_STRING_DEFAULT (scptr->CipherListPtr)

            status = NetWriteFaol (rqptr, ReportSslClientFao, &FaoVector);
            if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
         }

         vecptr = FaoVector;

         REPORT_ENDIS (svptr->ProxyService)

         status = NetWriteFaol (rqptr, ReportProxyFao, &FaoVector);
         if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

         if (svptr->ProxyService)
         {
            vecptr = FaoVector;

            REPORT_ENDIS (svptr->ConnectService)
            REPORT_STRING_NONE (svptr->ProxyBindIpAddressString)
            REPORT_ENDIS (svptr->ProxyFileCacheEnabled)
            if (svptr->ProxyAuthRequired)
               *vecptr++ = "PROXY";
            else
            if (svptr->LocalAuthRequired)
               *vecptr++ = "LOCAL";
            else
               *vecptr++ = ReportNotNone;
            REPORT_STRING_NONE (svptr->ProxyChainHostPort)
            REPORT_ENDIS (svptr->ProxyTrack)

            status = NetWriteFaol (rqptr, ReportProxyDetailsFao, &FaoVector);
            if (VMSnok (status))
               ErrorNoticed (status, "NetWriteFaol()", FI_LI);
         }

         vecptr = FaoVector;

         if (svptr->AdminService)
            *vecptr++ = "<FONT COLOR=\"#ff0000\"><B>!AZ</B></FONT>";
         REPORT_ENDIS (svptr->AdminService)
         REPORT_ENDIS (svptr->NoLog)
         REPORT_ENDIS (svptr->SetNoTrack)
         REPORT_STRING_DEFAULT (svptr->BodyTag)
         if (svptr->ServiceErrorReportPath)
         {
            *vecptr++ = "!AZ !AZ";
            *vecptr++ = svptr->ErrorReportPath;
            *vecptr++ = svptr->ErrorReportPathCodesPtr;
         }
         else
            *vecptr++ = ReportDefault;

         if (svptr->LogFileNameLength)
         {
            *vecptr++ =
"<A HREF=\"!AZ?service=!AZ\">!AZ</A> <I>(!&?open\rclosed\r)</I>";
            *vecptr++ = ADMIN_REPORT_SERVICE_LOG;
            *vecptr++ = svptr->ServerHostPort;
            if (LoggingPerService)
            {
               *vecptr++ = svptr->LogFileName;
               *vecptr++ = svptr->LogFileOpen;
            }
            else
            {
               *vecptr++ = ServiceListHead->LogFileName;
               *vecptr++ = ServiceListHead->LogFileOpen;
            }
         }
         else
            *vecptr++ = "<I>(none)</I>";

         status = NetWriteFaol (rqptr, ReportOtherFao, &FaoVector);
         if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
      }
   }

   /**************/
   /* end report */
   /**************/

   status = NetWriteFao (rqptr, EndPageFao);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

#undef REPORT_ENDIS
#undef REPORT_STRING_DEFAULT
#undef REPORT_STRING_NONE
}

/*****************************************************************************/
/*
A server administration menu for service configuration. This function just
wraps the revision function, loading a temporary database if necessary for
reporting from the configuration file.
*/ 

ServiceConfigRevise
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction,
BOOL UseServerDatabase
)
{
   int  status;
   META_CONFIG  *mcptr;

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SERVICE,
                 "ServiceConfigRevise() !&F !&A !UL",
                 &ServiceConfigRevise, NextTaskFunction, UseServerDatabase);

   if (UseServerDatabase)
      ServiceConfigReviseNow (rqptr, MetaGlobalServicePtr);
   else
   {
      status = ServiceConfigLoad (&mcptr);
      if (VMSnok (status))
      {
         /* severe error reported */
         rqptr->rqResponse.HttpStatus = 403;
         ErrorGeneral (rqptr, mcptr->LoadReport.TextPtr, FI_LI);
      }
      else
         ServiceConfigReviseNow (rqptr, mcptr);
      MetaConUnload (&mcptr, NULL);
   }

   SysDclAst (NextTaskFunction, rqptr);
}

/*****************************************************************************/
/*
Return a report on the HTTPd service configuration ... now!
*/ 

ServiceConfigReviseNow
(
REQUEST_STRUCT *rqptr,
META_CONFIG *mcptr
)
{
   static char  ReviseServiceNoneFao [] = "<I>(no services defined)</I>";

   static char  ReviseServiceFao [] =
"!AZ\
<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>!&@!AZ!AZ!AZ</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0>\n\
<TR><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
\
<TR><TH ALIGN=right>Scheme:</TH><TD>\n\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[[\">\n\
<INPUT TYPE=radio NAME=ServiceScheme!UL VALUE=\"http:\"!&? CHECKED\r\r>http\n\
<INPUT TYPE=radio NAME=ServiceScheme!UL VALUE=\"https:\"!&? CHECKED\r\r>https\n\
</TD></TR>\n\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"//\">\n\
<TR><TH ALIGN=right>Name:</TH><TD>\
<INPUT TYPE=text SIZE=40 NAME=ServiceName!UL VALUE=\"!AZ\">\
</TD></TR>\n\
<INPUT TYPE=hidden name=hidden$lf VALUE=\":\">\n\
<TR><TH ALIGN=right>Port:</TH><TD>\
<INPUT TYPE=text SIZE=5 NAME=ServicePort!UL VALUE=\"!AZ\">\n\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"]]\">\n\
</TD></TR>\n\
\
<TR><TH ALIGN=right>Bind IP Address:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceBind]&#32;&#32;\">\n\
<INPUT TYPE=text SIZE=20 NAME=ServiceBind!UL VALUE=\"!AZ\">\n\
<I>(only if NI multi-homed)</I></TD></TR>\n";

   static char  ReviseSslServiceFao [] =
"<TR><TD HEIGHT=3></TD></TR>\n\
\
<TR><TH ALIGN=right>SSL Version:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceSSLversion]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceSSLversion!UL VALUE=\"\"!&? CHECKED\r\r>default\n\
<INPUT TYPE=radio NAME=ServiceSSLversion!UL VALUE=TLSV1!&? CHECKED\r\r>TLSV1\n\
<INPUT TYPE=radio NAME=ServiceSSLversion!UL VALUE=SSLV2/V3!&? CHECKED\r\r>SSLV2/V3\n\
<INPUT TYPE=radio NAME=ServiceSSLversion!UL VALUE=SSLV3!&? CHECKED\r\r>SSLV3\n\
<INPUT TYPE=radio NAME=ServiceSSLversion!UL VALUE=SSLV2!&? CHECKED\r\r>SSLV2\n\
</TD></TR>\n\
<TR><TH ALIGN=right>SSL Certificate:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceSSLcert]&#32;&#32;\">\n\
<INPUT TYPE=text SIZE=40 NAME=ServiceSSLcert!UL VALUE=\"!AZ\">\n\
</TD></TR>\n\
<TR><TH ALIGN=right>SSL Private Key:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceSSLkey]&#32;&#32;\">\n\
<INPUT TYPE=text SIZE=40 NAME=ServiceSSLkey!UL VALUE=\"!AZ\">\n\
</TD></TR>\n\
<TR><TH ALIGN=right>SSL Verify Peer:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceSSLVerifyPeer]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceSSLVerifyPeer!UL VALUE=enabled!&? CHECKED\r\r>enabled\n\
<INPUT TYPE=radio NAME=ServiceSSLVerifyPeer!UL VALUE=disabled!&? CHECKED\r\r>disabled\n\
</TD></TR>\n\
<TR><TH ALIGN=right>SSL Verify Peer CA File:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceClientSSLCaFile]&#32;&#32;\">\n\
<INPUT TYPE=text SIZE=40 NAME=ServiceSSLCaFile!UL VALUE=\"!AZ\">\n\
</TD></TR>\n\
<TR><TH ALIGN=right>SSL Cipher List:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceSSLcipherList]&#32;&#32;\">\n\
<INPUT TYPE=text SIZE=40 NAME=ServiceSSLcipherList!UL VALUE=\"!AZ\">\n\
</TD></TR>\n";

   static char  ReviseProxyFao [] =
"<TR><TD HEIGHT=3></TD></TR>\n\
\
<TR><TH ALIGN=right>Proxy:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceProxy]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceProxy!UL VALUE=enabled!&? CHECKED\r\r>enabled\n\
<INPUT TYPE=radio NAME=ServiceProxy!UL VALUE=disabled!&? CHECKED\r\r>disabled\n\
</TD></TR>\n";

   static char  ReviseProxyDetailsFao [] =
"<TR><TH ALIGN=right>Proxy SSL (CONNECT):</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceProxySSL]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceProxySSL!UL VALUE=enabled!&? CHECKED\r\r>enabled\n\
<INPUT TYPE=radio NAME=ServiceProxySSL!UL VALUE=disabled!&? CHECKED\r\r>disabled\n\
</TD></TR>\n\
\
<TR><TH ALIGN=right>Proxy Bind IP Address:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceProxyBind]&#32;&#32;\">\n\
<INPUT TYPE=text SIZE=20 NAME=ServiceProxyBind!UL VALUE=\"!AZ\">\n\
<I>(only if NI multi-homed)</I></TD></TR>\n\
\
<TR><TH ALIGN=right>Proxy Cache:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceProxyCache]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceProxyCache!UL VALUE=enabled!&? CHECKED\r\r>enabled\n\
<INPUT TYPE=radio NAME=ServiceProxyCache!UL VALUE=disabled!&? CHECKED\r\r>disabled\n\
</TD></TR>\n\
\
<TR><TH ALIGN=right>Proxy Authorization:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceProxyAuth]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceProxyAuth!UL VALUE=\"none\"!&? CHECKED\r\r>none\n\
<INPUT TYPE=radio NAME=ServiceProxyAuth!UL VALUE=\"PROXY\"!&? CHECKED\r\r>PROXY\n\
<INPUT TYPE=radio NAME=ServiceProxyAuth!UL VALUE=\"LOCAL\"!&? CHECKED\r\r>LOCAL\n\
</TD></TR>\n\
\
<TR><TH ALIGN=right>Proxy Chain:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceProxyChain]&#32;&#32;\">\n\
<INPUT TYPE=text SIZE=40 NAME=ServiceProxyChain!UL VALUE=\"!AZ\">\n\
</TD></TR>\n\
\
<TR><TH ALIGN=right>Proxy Track:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceProxyTrack]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceProxyTrack!UL VALUE=enabled!&? CHECKED\r\r>enabled\n\
<INPUT TYPE=radio NAME=ServiceProxyTrack!UL VALUE=disabled!&? CHECKED\r\r>disabled\n\
</TD></TR>\n";

   static char  ReviseSslClientFao [] =
"<TR><TD HEIGHT=3></TD></TR>\n\
\
<TR><TH ALIGN=right>SSL/Client:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceClientSSL]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceClientSSL!UL VALUE=enabled!&? CHECKED\r\r>enabled\n\
<INPUT TYPE=radio NAME=ServiceClientSSL!UL VALUE=disabled!&? CHECKED\r\r>disabled\n\
</TD></TR>\n";

   static char  ReviseSslClientDetailsFao [] =
"<TR><TH ALIGN=right>SSL/Client Version:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceClientSSLversion]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceClientSSLversion!UL VALUE=\"\"!&? CHECKED\r\r>default\n\
<INPUT TYPE=radio NAME=ServiceClientSSLversion!UL VALUE=TLSV1!&? CHECKED\r\r>TLSV1\n\
<INPUT TYPE=radio NAME=ServiceClientSSLversion!UL VALUE=SSLV2/V3!&? CHECKED\r\r>SSLV2/V3\n\
<INPUT TYPE=radio NAME=ServiceClientSSLversion!UL VALUE=SSLV3!&? CHECKED\r\r>SSLV3\n\
<INPUT TYPE=radio NAME=ServiceClientSSLversion!UL VALUE=SSLV2!&? CHECKED\r\r>SSLV2\n\
</TD></TR>\n\
<TR><TH ALIGN=right>SSL/Client Verify CA:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceClientSSLverifyCA]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceClientSSLverifyCA!UL VALUE=enabled!&? CHECKED\r\r>enabled\n\
<INPUT TYPE=radio NAME=ServiceClientSSLverifyCA!UL VALUE=disabled!&? CHECKED\r\r>disabled\n\
</TD></TR>\n\
<TR><TH ALIGN=right>SSL/Client Verify CA File:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceClientSSLCaFile]&#32;&#32;\">\n\
<INPUT TYPE=text SIZE=40 NAME=ServiceClientSSLCaFile!UL VALUE=\"!AZ\">\n\
</TD></TR>\n\
<TR><TH ALIGN=right>SSL/Client Cipher List:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceClientSSLcipherList]&#32;&#32;\">\n\
<INPUT TYPE=text SIZE=40 NAME=ServiceClientSSLcipherList!UL VALUE=\"!AZ\">\n\
</TD></TR>\n";

   static char  ReviseOtherFao [] =
"<TR><TD HEIGHT=3></TD></TR>\n\
\
<TR><TH ALIGN=right>Admin:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceAdmin]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceAdmin!UL VALUE=enabled!&? CHECKED\r\r>enabled\n\
<INPUT TYPE=radio NAME=ServiceAdmin!UL VALUE=disabled!&? CHECKED\r\r>disabled\n\
</TD></TR>\n\
\
<TR><TH ALIGN=right>NoLog:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceNoLog]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceNoLog!UL VALUE=enabled!&? CHECKED\r\r>enabled\n\
<INPUT TYPE=radio NAME=ServiceNoLog!UL VALUE=disabled!&? CHECKED\r\r>disabled\n\
</TD></TR>\n\
\
<TR><TH ALIGN=right>NoTrack:</TH><TD>\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceNoTrack]&#32;&#32;\">\n\
<INPUT TYPE=radio NAME=ServiceNoTrack!UL VALUE=enabled!&? CHECKED\r\r>enabled\n\
<INPUT TYPE=radio NAME=ServiceNoTrack!UL VALUE=disabled!&? CHECKED\r\r>disabled\n\
</TD></TR>\n\
\
<TR><TH ALIGN=right>Body Tag:</TH><TD COLSPAN=2>\n\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceBodyTag]&#32;&#32;\">\n\
<INPUT TYPE=text size=40 NAME=ServiceBodyTag!UL VALUE=\"!AZ\">\n\
</TD></TR>\n\
\
<TR><TH ALIGN=right>Error Report Path:</TH><TD COLSPAN=2>\n\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;[ServiceErrorReportPath]&#32;&#32;\">\n\
<INPUT TYPE=text size=40 NAME=ServiceErrorReportPath!UL VALUE=\"!&@\">\n\
</TD></TR>\n\
\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
<INPUT TYPE=hidden name=hidden$lf VALUE=\"&#94;\">\n";

   static char  NotNone [] = "<I>(none)</I>\n";

   int  idx, status,
        ServiceListCount;
   unsigned long  *vecptr;
   unsigned long  FaoVector [128];
   char  *cptr, *sptr, *zptr;
   SERVICE_META  *smptr;
   SERVICE_STRUCT  *svptr;
   SESOLA_CONTEXT  *scptr, *ssptr;

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_SERVICE))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SERVICE, "ServiceConfigReviseNow()");

   /* get a pointer to the meta-config data */
   smptr = mcptr->ServiceMetaPtr;

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   RESPONSE_HEADER_200_HTML (rqptr);
   AdminPageTitle (rqptr, "Service Configuration");
   AdminMetaConReport (rqptr, mcptr, MetaGlobalServicePtr);

   if (ServiceConfigFileDefined)
   {
      AdminMetaConSource (rqptr, mcptr, MetaGlobalServicePtr,
                          mcptr->IncludeFile);
      AdminMetaConBeginUpdateForm (rqptr);
   }
   else
   {
      status = NetWriteFao (rqptr, "<FORM METHOD=POST ACTION=\"\">");
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFao()", FI_LI);
   }

   /************/
   /* services */
   /************/

   if (!smptr->ListHead)
   {
      status = NetWriteFaol (rqptr, ReviseServiceNoneFao, NULL);
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
   }
   else
   {
      ServiceListCount = 0;
      for (svptr = smptr->ListHead; svptr; svptr = svptr->NextPtr)
      {
         ServiceListCount++;

         vecptr = FaoVector;

         /* place holder for the "next new service" comment (see below) */
         *vecptr++ = "";

         *vecptr++ = "!UL&nbsp;&nbsp;-&nbsp;&nbsp;";
         *vecptr++ = ServiceListCount;

         *vecptr++ = svptr->RequestSchemeNamePtr;
         *vecptr++ = "//";
         *vecptr++ = svptr->ServerHostPort;

         *vecptr++ = ServiceListCount;
         *vecptr++ = svptr->RequestScheme == SCHEME_HTTP;
         *vecptr++ = ServiceListCount;
         *vecptr++ = svptr->RequestScheme == SCHEME_HTTPS;
         *vecptr++ = ServiceListCount;
         if (svptr->GenericService)
            *vecptr++ = "*";
         else
            *vecptr++ = svptr->ServerHostName;
         *vecptr++ = ServiceListCount;
         *vecptr++ = svptr->ServerPortString;
         *vecptr++ = ServiceListCount;
         *vecptr++ = svptr->BindIpAddressString;

         status = NetWriteFaol (rqptr, ReviseServiceFao, &FaoVector);
         if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

         if (ssptr = svptr->SSLserverPtr)
         {
            vecptr = FaoVector;

            *vecptr++ = ServiceListCount;
            *vecptr++ = !ssptr->VersionString[0];
            *vecptr++ = ServiceListCount;
            *vecptr++ = strsame (ssptr->VersionString, "TLSV1", -1);
            *vecptr++ = ServiceListCount;
            *vecptr++ = strsame (ssptr->VersionString, "SSLV2/C3", -1);
            *vecptr++ = ServiceListCount;
            *vecptr++ = strsame (ssptr->VersionString, "SSLV3", -1);
            *vecptr++ = ServiceListCount;
            *vecptr++ = strsame (ssptr->VersionString, "SSLV2", -1);
            *vecptr++ = ServiceListCount;
            *vecptr++ = ssptr->CertFile;
            *vecptr++ = ServiceListCount;
            *vecptr++ = ssptr->KeyFile;
            *vecptr++ = ServiceListCount;
            *vecptr++ = ssptr->VerifyPeer;
            *vecptr++ = ServiceListCount;
            *vecptr++ = !ssptr->VerifyPeer;
            *vecptr++ = ServiceListCount;
            *vecptr++ = ssptr->CaFile;
            *vecptr++ = ServiceListCount;
            *vecptr++ = ssptr->CipherList;

            status = NetWriteFaol (rqptr, ReviseSslServiceFao, &FaoVector);
            if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
         }

         vecptr = FaoVector;
         *vecptr++ = ServiceListCount;
         *vecptr++ = svptr->ProxyService;
         *vecptr++ = ServiceListCount;
         *vecptr++ = !svptr->ProxyService;
         status = NetWriteFaol (rqptr, ReviseProxyFao, &FaoVector);
         if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

         if (svptr->ProxyService)
         {
            vecptr = FaoVector;

            *vecptr++ = ServiceListCount;
            *vecptr++ = svptr->ConnectService;
            *vecptr++ = ServiceListCount;
            *vecptr++ = !svptr->ConnectService;
            *vecptr++ = ServiceListCount;
            *vecptr++ = svptr->ProxyBindIpAddressString;
            *vecptr++ = ServiceListCount;
            *vecptr++ = svptr->ProxyFileCacheEnabled;
            *vecptr++ = ServiceListCount;
            *vecptr++ = !svptr->ProxyFileCacheEnabled;
            *vecptr++ = ServiceListCount;
            *vecptr++ = !(svptr->ProxyAuthRequired ||
                          svptr->LocalAuthRequired);
            *vecptr++ = ServiceListCount;
            *vecptr++ = svptr->ProxyAuthRequired;
            *vecptr++ = ServiceListCount;
            *vecptr++ = svptr->LocalAuthRequired;
            *vecptr++ = ServiceListCount;
            *vecptr++ = svptr->ProxyChainHostPort;
            *vecptr++ = ServiceListCount;
            *vecptr++ = svptr->ProxyTrack;
            *vecptr++ = ServiceListCount;
            *vecptr++ = !svptr->ProxyTrack;

            status = NetWriteFaol (rqptr, ReviseProxyDetailsFao, &FaoVector);
            if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

            vecptr = FaoVector;
            *vecptr++ = ServiceListCount;
            *vecptr++ = svptr->SSLclientEnabled;
            *vecptr++ = ServiceListCount;
            *vecptr++ = !svptr->SSLclientEnabled;
            status = NetWriteFaol (rqptr, ReviseSslClientFao, &FaoVector);
            if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

            if (scptr = svptr->SSLclientPtr)
            {
               vecptr = FaoVector;

               *vecptr++ = ServiceListCount;
               *vecptr++ = !scptr->VersionString[0];
               *vecptr++ = ServiceListCount;
               *vecptr++ = strsame (scptr->VersionString, "TLSV1", -1);
               *vecptr++ = ServiceListCount;
               *vecptr++ = strsame (scptr->VersionString, "SSLV2/V3", -1);
               *vecptr++ = ServiceListCount;
               *vecptr++ = strsame (scptr->VersionString, "SSLV3", -1);
               *vecptr++ = ServiceListCount;
               *vecptr++ = strsame (scptr->VersionString, "SSLV2", -1);
               *vecptr++ = ServiceListCount;
               *vecptr++ = scptr->VerifyCA;
               *vecptr++ = ServiceListCount;
               *vecptr++ = !scptr->VerifyCA;
               *vecptr++ = ServiceListCount;
               *vecptr++ = scptr->CaFile;
               *vecptr++ = ServiceListCount;
               *vecptr++ = scptr->CipherList;

               status = NetWriteFaol (rqptr, ReviseSslClientDetailsFao,
                                      &FaoVector);
               if (VMSnok (status))
                  ErrorNoticed (status, "NetWriteFaol()", FI_LI);
            }
         }

         vecptr = FaoVector;

         *vecptr++ = ServiceListCount;
         *vecptr++ = svptr->AdminService;
         *vecptr++ = ServiceListCount;
         *vecptr++ = !svptr->AdminService;
         *vecptr++ = ServiceListCount;
         *vecptr++ = svptr->NoLog;
         *vecptr++ = ServiceListCount;
         *vecptr++ = !svptr->NoLog;
         *vecptr++ = ServiceListCount;
         *vecptr++ = svptr->SetNoTrack;
         *vecptr++ = ServiceListCount;
         *vecptr++ = !svptr->SetNoTrack;
         *vecptr++ = ServiceListCount;
         *vecptr++ = svptr->BodyTag;
         *vecptr++ = ServiceListCount;
         if (svptr->ServiceErrorReportPath)
         {
            *vecptr++ = "!AZ !AZ";
            *vecptr++ = svptr->ErrorReportPath;
            *vecptr++ = svptr->ErrorReportPathCodesPtr;
         }
         else
            *vecptr++ = "";

         status = NetWriteFaol (rqptr, ReviseOtherFao, &FaoVector);
         if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
      }
   }

   /***********************/
   /* "blank" new service */
   /***********************/

   ServiceListCount++;

   vecptr = FaoVector;
   *vecptr++ =
"<INPUT TYPE=hidden name=hidden$lf VALUE=\"\
&#32;&#94;# place-holder for the next new service\">\n";
   *vecptr++ = "";
   *vecptr++ = "- Next New Service -";
   *vecptr++ = "";
   *vecptr++ = "";
   *vecptr++ = ServiceListCount;
   *vecptr++ = true;
   *vecptr++ = ServiceListCount;
   *vecptr++ = false;
   *vecptr++ = ServiceListCount;
   *vecptr++ = "?.?.?.?";
   *vecptr++ = ServiceListCount;
   *vecptr++ = "80";
   *vecptr++ = ServiceListCount;
   *vecptr++ = "";
   status = NetWriteFaol (rqptr, ReviseServiceFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

   vecptr = FaoVector;
   *vecptr++ = ServiceListCount;
   *vecptr++ = false;
   *vecptr++ = ServiceListCount;
   *vecptr++ = true;
   status = NetWriteFaol (rqptr, ReviseProxyFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

   vecptr = FaoVector;
   *vecptr++ = ServiceListCount;
   *vecptr++ = false;
   *vecptr++ = ServiceListCount;
   *vecptr++ = true;
   *vecptr++ = ServiceListCount;
   *vecptr++ = false;
   *vecptr++ = ServiceListCount;
   *vecptr++ = true;
   *vecptr++ = ServiceListCount;
   *vecptr++ = false;
   *vecptr++ = ServiceListCount;
   *vecptr++ = true;
   *vecptr++ = ServiceListCount;
   *vecptr++ = "";
   *vecptr++ = ServiceListCount;
   *vecptr++ = "";
   status = NetWriteFaol (rqptr, ReviseOtherFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

   /**************/
   /* end report */
   /**************/

   if (ServiceConfigFileDefined)
      AdminMetaConEndUpdateForm (rqptr);
   else
      status = NetWriteFaol (rqptr, "</FORM>\n", NULL);
   status = NetWriteFaol (rqptr, "</BODY>\n</HTML>\n", NULL);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
}

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