/*****************************************************************************/
#ifdef COMMENTS_WITH_COMMENTS
/*
                                  config.c

Loads and processes the soyMAIL configuration file.  This file is located using
the SOYMAIL_CONFIG logical name.  If the configuration file does not exist or
cannot be accessed then soyMAIL just reports a fatal error.  Directives and
comments must begin in the first column.


CONFIGURATION DIRECTIVES
------------------------
[access-log]            file specification of access log

[attachment-MIME-types] space-separated list of MIME types (full or leading
                        partial) handled internally by the browser when
                        accessing attachments (e.g. PDF, Excel, etc.)

[charset-default]       if not otherwise specified use this
                          e.g. UTF-8 (default), ISO-8859-1, Windows-1252
                        overrides user option of same name

[charsets-rtl]          character sets right-to-left directionality

[compose-charsets]      comma-separated character sets available for
                        selection on the message composition page

[compose-contacts-after]  integer, number of contacts after which the
                          list expansion and keyword select is available

[compose-contacts-size]   integer, size in "em" of contacts select list

[compose-edit-cols]     each of these three configuration directives takes
                        a string of space or comma (or any non-digit in fact)
[compose-edit-rows]     separated numbers representing the steps in each of
                        three functions editing parameters, e.g.
[compose-wrap-at]       "72 80 96 132" or "72,80,96,132"

[compose-user-from]     ENABLE User-specified compose "From:" address field
                        DISPLAY keyword displays but disables field edit

[context-cache-expires]  minutes unsued before CGIplus context cache expires

[disk-quota-percent]    by default begins reporting low disk quota at 85%
                        change this to suit or set to above 100 to disable

[downtime]              disable the use of soyMAIL and give the specified
                        message to users as a simple announcement

[font-family-local]     additional font families presented in the selector
                        (intended for non-en language support)

[greeting]              "Welcome to soyMAIL - UNAUTHORISED USE IS PROHIBITED."

[help-about-site]       site-specific information presented on the Help
                        'About' page

[html-editor-load]      JavaScript to load and instantiate the HTML editor

[image-inline]          delay in mS when loading inline images
                        default is 500 but can be adjusted to suit site

[include-file]          include the specified file
                        (maximum include file depth is 2)

[login-acme-doi]        ACME domain of interpretation (authentication source)
                        defaults to "VMS"

[login-acme-no-restrict]   ignore any login source restriction (e.g.
                           /NONETWORK) provided authentication is OK

[login-agent]           external agent to perform login authentication
                        (see description in LOGIN.C module)

[login-alias]           alias to username mapping
                        (see description in LOGIN.C module)

[login-alias-smtp-from]    if ENABLEd use any login alias as the local
                           part of the generated 'self' (From:) address

[login-autocomplete]    ENABLE to allow login page username/password
                        form field autocompletion (default is DISABLE)

[login-change-page]     file-system location (VMS or C-RTL syntax)
                        of the password change page HTML content

[login-idle]            number of seconds between accesses before the session
                        is considered idle and forces the user to login again

[login-language]        language file used on login page

[login-no-pwdexp]       ignore expired passwords

[login-page]            file-system location (VMS or C-RTL syntax)
                        of the login page HTML content
                        
[login-secret]          string used as the key for RC4 encryption of
                        authentication credentials (minimum 24 characters)

[login-type]            integer representing desired login type
                        NETWORK=1 (default), LOCAL=3, DIALUP=4, REMOTE=5.

[login-verify]          number of seconds between verification of supplied
                        authentication credentials against the authenticator

[logout-realm]          used with HTTP server authorization
                        for VMS Apache this enables the logout functionality
                        this string must be *exactly* the same as the AuthName
                        attribute of the authorization against soyMAIL
                        (a realm of '-' disables the logout button)

[message-flags]         hexadecimal number to enable MAIL$M_NEWMSG (0x01),
                        MAIL$M_REPLIED (0x02) and MAIL$M_MARKED (0x80) flags
                        processing by soyMAIL (bit-wise OR)

[message-list-footer]   site-specific information (HTML text) presented in a
                        separate panel below the message listing

[page-title]            superior line in main menu panel and titles all pages
[page-title1]           (synonym)

[page-title2]           when set the text in 'page-title1' is forced left
                        and the 'page-title2' if forced right on the page

[print-header]          text header for printed mail messages

[print-footer]          text footer for printed mail messages

[private-access]        allows mapping between authenticated user
                        (CGI remote-user) and a VMS username

[private-request]       this request is marked as private access
                        (overrides the /~ sentinel for private access)

[public-access]         permits and maps request path strings to VMS Mail
                        e.g. /path/ /username/file/folder/

[public-no-access]      VMS file specification, where public access is
                        authorized (credentials OK) but the user does not have
                        access (to a particular resource) return the content

[public-footer]         site-specific information (HTML text) presented in a
                        separate panel below the message listing

[public-options-default]  allows the soyMAIL administrator to set public
                          access 'personal option' settings
                          (required to escape each leading '[' with a '\')

[public-request]        this request is marked as public access

[search-control]        'full'  (expensive and the default)
                        'header-only'  (very lightweight)
                        'keyword-only'  (no regular expressions allowed)
                        'none'  (very cheap :-)
                        'priority='  set priority of message text searches
                        'vms-select'  use VMS Mail select rather than search

[site-style-sheet]      if specified loads <url> style sheet (after the theme)

[SMTP-default-host]     if an email address is provided without a domain part
                        an '@' and this string is appended to create an valid
                        (looking) RFC822 address - this disables the ability
                        to send VMS mail!

[SMTP-from-host]        used when constructing the 'user@from.host.name'

[SMTP-server-host]      host name/address of SMTP server soyMAIL connects to
                        for Internet mail transport (defaults to localhost) 

[soyMAIL-at-title]      site description provided in title bar of browser
                        window (defaults to "soyMAIL @ the.server.name")

[update-last-login]     keywords INTERACTIVE ('I') or NON-INTERACTIVE ('N')
                        update the SYSUAF last login date/time with each
                        initial soyMAIL startup (indicated by an empty state)

[user-name-info]        ENABLE to show current logged-in VMS username in
                        main-menu status information, ALIAS to show login
                        alias (if [login-alias] in use and mapped)

[user-options-default]  allows the soyMAIL administrator to preset some
                        options (e.g. language) by providing options
                        configuration text aginst this directive
                        (required to escape each leading '[' with a '\')

[user-options-override]  allows the soyMAIL administrator to override user
                         selected options by providing options configuration
                         text against this directive
                         (required to escape each leading '[' with a '\')

[VMS-newmsg]            if enabled leaves the new mail count and new message
                        flag untouched (more like VMS command-line Mail)

[VMS-occluded]          ENABLE to hide the VMS-isms of soyMAIL
                        (e.g. the VMS options, [extract] button, etc.)


CONDITIONAL CONFIGURATION
-------------------------
[if-CGI-<name>]         matches the value of the specifed CGI variable
[if-not-CGI-<name>]     not matches the value of the specifed CGI variable
[if-private]            if private mail access
[if-public]             if public mail access
[if-END]                following directives are unconditionally processed
[end]                   stop processing the configuration at this point
                       
If a match then uses the configuration directives up to the complementary
[if-END].  Matching is either done by keyword or regular expression (see brief
description in SEARCH.C module).  Regular expression are introduced by a
leading caret symbol ('^') which of course is not included in the expression.

Keyword example ...

  [if-CGI-http_host]  the.host.name
  [if-CGI-server_name]  the.server.name
  [if-not-CGI-remote_user]  R_J_ECUREUIL

Equivalent regular expression example ...

  [if-CGI-http_host]  ^*the\.host\.name*
  [if-CGI-server_name]  ^*the\.server\.name*
  [if-not-CGI-remote_user]  ^*R_J_ECUREUIL*

Regular expression equivalents of these common wildcards

  asterisk ('*')           .*
  question mark ('?')      .
  percentage ('%')         .

A non-existant CGI variable is treated as an empty string.  Non-empty and empty
strings may be respectively tested for with

  [if-CGI-variable_name]  ^.*
  [if-not-CGI-variable_name]  ^.*

Conditionals may be nested (to any depth) but every conditional *must* be
closed with an [if-END].  For example:

  [if-private]
  [page-title] The Page Title
  [if-CGI-server_name] www.site1.com
  [message-list-footer] For more information on Site 1 see ...
  [if-end]
  [if-CGI-server_name] www.site2.com
  [message-list-footer] For more information on Site 2 see ...
  [if-end]
  [if-end]
  #
  [if-public]
  [page-title] The Public Page
  [message-list-footer] Just an public example!
  [public-access] /public-2005/ /WEBMASTER/MAIL/PUB2005/
  [if-end]


PRIVATE ACCESS
--------------
By default private access is recognised with a path (excluding the script
component) beginning "/~".  This can be overridden using the [private-request]
configuration directive.  The specifies that the request should be handled as
private regardless of the absence of the "/~".  When this directive is used the
"/~" sentinel cannot be used.  The directive requires no parameter and is
intended to be used within some conditional test that would indicate it is a
private request.

The following example shows a configuration test for the presence of a
REMOTE_USER variable indicating it is a request subject to server
authorization.

  [if-CGI-remote_user]
  [private-request]
  [end-if]

This effectively directs soyMAIL to consider any request subject to
authorization as a private access request.

This second example shows a test based on request URI and assumes that the
server has mapped the path /mail/ onto the soyMAIL executable.

  [if-CGI-script_name]  ^^/mail$
  [private-request]
  [end-if]

The first leading caret indicates it is a regex pattern.  The second a regex
'beginning of line' (the start of the request URI).  Then the literal URI used
to activate soyMAIL down to the regex 'end of line' dollar symbol.

The [private-request] directive must be specified before the use of any
directives that rely on recognition of private or public access (e.g.
[if-private], [if-public], [private-access], [public-access], etc) for the
obvious reason.

Each [private-access] mapping has the following format

  <remote-user>/<auth-realm>/<vms-username>

where the <remote-user> is the username used for authentication purposes,
<auth-realm> is a WASDism and allows the script to know against which realm the
authentication occured, and <vms-username> is the VMS username to be used for
Mail access.  Any or all can be a single asterisk wildcard.  The mapping
'*/*/*' would map all authenticated remote usernames directly as equivalent VMS
username.  The mapping 'MARK/*/DANIEL' would map remote user MARK to the VMS
username DANIEL.

A particular username can be disabled by using a hyphen in place of the VMS
username, hence the mapping 'SYSTEM/*/-' disables access to the SYSTEM
account's Mail.

The special-case mapping 'DANIEL/*/(POSTMASTER)' marks the remote username as a
'super-user' of soyMAIL, or Postmaster.  POSTMASTER mapping must be used with a
URL that begins "/~".


DEDICATED ACCESS
----------------
A Web service can be dedicated to soyMAIL access.  For this example it will be
the host webmail.domain.name and if no URI is specified it defaults to soyMAIL. 
The WASD configuration would be:

  # soyMAIL configuration
  [private-request]  enabled

  # HTTPD$MAP
  [[webmail.domain.name:443]]
  pass /soymail/-/* /wasd_root/src/soymail/*
  map /* /cgi-bin/soymail/~* map=once
  exec /cgi-bin/* /cgi-bin/*
  * "403 uh-uh!"

  [[webmail.domain.name]]
  redirect * https://webmail.domain.name/


PUBLIC ACCESS
-------------
Each rule value has the following format

  /path-to-mail/ /<user>/<file>/<folder>/

as in the following example

  /WASD-2006/  /daniel/mail/WASD_2006/

which would map the path '/WASD-2005/' to VMS username 'DANIEL', that accounts
MAIL.MAI mail file, and the folder (case-sensitive) 'WASD_2005'.

Alternatively, to allow mapping to any folder in a public path, with the
folder of the initial access specified

  /path-to-mail/ /<user>/<file>/*<initial-folder>/

as in the following example

  /WASD-2006/  /daniel/mail/*WASD_2006/

with optional restriction of no access to the MAIL and NEWMAIL folders

  /path-to-mail/ /<user>/<file>/*!<initial-folder>/

as in the following example

  /WASD-2006/  /daniel/mail/*!WASD_2006/


AUTHORIZED PUBLIC ACCESS
------------------------
(If that's not too oxymoronic!)  To require authorization for a public access
path add [] after the result path and before any public folder name.

  /cognoscenti/  /daniel/mail/cognos/  []  Public Folder Name

Now this is getting fairly esoteric ...

When using AUTOGENOUS AGENT AUTHENTICATION (see LOGIN.C) and access to multiple
paths needs to be controlled individually then (perhaps conditionally) add a
configuration directive to cause authorization to be done with each request
(yes, a bit more costly!)

  [login-verify]  -1

Using the

  [public-no-access]  vms:[file]specification.txt

configuration directive an advisory message should be supplied noting that
access has been denied.  The content of this file should be an HTML page.

If the usual '<user><file><folder>' of the [public-access] mapping is replaced
with a VMS file specification then that soyMAIL path delivers the HTML content
of that file to the user.  This path can use the [] authorization token and so
be both a login trigger and a list of accessable resources.

  [if-public]
  [login-verify]  -1
  [login-agent]  @CGI-BIN:[000000]PUBAUTH.COM
  [public-no-access]  DEVICE:[DIRECTORY]NO_ACCESS.HTML
  [public-access]
  /lists/  DEVICE:[DIRECTORY]LISTS.HTML []
  /test1/  /system/mail/*/  []  A Wildcard Public (no start folder)
  /test2/  /system/mail/*!test/  []  A Wildcard Public (no MAIL or NEWMAIL)
  /test3/  /system/mail/*testing/  []  A Wildcard Public (no restriction)
  /test4/  /system/mail/NEWMAIL/  []  A Static Public
  [if-end]


COPYRIGHT
---------
Copyright (C) 2005-2025 Mark G.Daniel
This program, comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it under the
conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version.


VERSION HISTORY
---------------
11-OCT-2023  MGD  add [image-inline]
09-JUN-2013  MGD  add [public-footer]
                  add [site-style-sheet]
06-MAR-2010  MGD  add [context-cache-expires]
19-APR-2009  MGD  add [public-no-access]
                  ConfigPublicAccess() [] authorized public token
13-NOV-2008  MGD  add [VMS-newmsg]
11-AUG-2008  MGD  add [include-file], [login-alias]
                    and [login-alias-SMTP-from]
15-JUL-2008  MGD  add [compose-edit-cols], [compose-edit-rows],
                    [compose-wrap-at], [contacts-compose-after],
                    [contacts-compose-size] and [user-name-info]
09-JUL-2008  MGD  add [attachment-MIME-types] (see example in MESSAGE.C)
22-JUN-2008  MGD  add [login-type] and [login-change-page]
14-JUN-2008  MGD  add [compose-user-from]
15-OCT-2007  MGD  add [login-agent]
03-OCT-2007  MGD  ConfigSetAccess() if [login-secret] then private access
14-AUG-2007  MGD  add [message-flags]
15-JUL-2007  MGD  add [login-acme-no-restrict]
02-APR-2007  MGD  add [access-log]
26-MAR-2007  MGD  unconditionally enable [logout] if [login-secret] set
07-OCT-2006  MGD  add [login-acme-doi], [login-autocomplete], [login-idle],
                  [login-language], [login-page], [login-secret] and
                  [login-verify]
21-JUN-2006  MGD  add [disk-quota-percent] and [vms-occluded]
17-APR-2006  MGD  add [update-last-login]
15-APR-2006  MGD  add [private-request] and [public-request]
                  change [if-CGI-..] and [if-not-CGI-..] so that an empty
                    parameter acts as a boolean test for a value or not
09-APR-2006  MGD  ConfigPublicAccess() modify wildcard folder to allow
                  initial folder specification and prohibit MAIL and NEWMAIL
02-APR-2006  MGD  ConfigPublicAccess() support wildcard folder
16-MAR-2006  MGD  add [logout-realm]
25-FEB-2006  MGD  add [greeting]
01-FEB-2005  MGD  initial
*/
#endif /* COMMENTS_WITH_COMMENTS */
/*****************************************************************************/

#ifdef SOYMAIL_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

#pragma nomember_alignment

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

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

/* application header file */
#include "soymail.h"
#include "config.h"

#define FI_LI "CONFIG", __LINE__

/* global storage */

#define CONFIG_INCLUDE_DEPTH 2
int  ConfigFileIndex;
int  ConfigTextLength [CONFIG_INCLUDE_DEPTH+1];
char  *ConfigTextPtr [CONFIG_INCLUDE_DEPTH+1];

CONFIG_DATA  SoyMailConfig;

/* external storage */

extern BOOL  Debug,
             WatchEnabled;

/*****************************************************************************/
/*
Load the configuration file into memory as a series of newline-delimitted
strings.  Then leaving that in memory, parse the configuration directives and
set the configuration data structure field.
*/

void ConfigLoad (REQUEST_DATA *rdptr)

{
   BOOL  WasConditional;
   int  status,
        ConditionalCount,
        ConditionalFalseAt;
   char  *cptr, *sptr,
         *NamePtr,
         *ValuePtr,
         *ErrorPtr;

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

   if (Debug) fprintf (stdout, "ConfigLoad()\n");

   memset (&SoyMailConfig, 0, sizeof(SoyMailConfig));

   ConfigFileIndex = 0;

   if (WatchEnabled)
   {
      cptr = TrnLnm("SOYMAIL_CONFIG", NULL, 0);
      WatchThis (FI_LI, "CONFIG FILE !AZ", cptr ? cptr : "logical not defined");
   }
   status = ReadFileIntoMemory ("SOYMAIL_CONFIG",
                                &ConfigTextPtr[ConfigFileIndex],
                                &ConfigTextLength[ConfigFileIndex]);
   if (VMSnok (status))
   {
      CgiLibResponseError (FI_LI, status, "SOYMAIL_CONFIG.");
      exit (SS$_NORMAL);
   }

   /* by default username/password autocomplete is disabled */
   SoyMailConfig.LoginAutoComplete = FALSE;

   /* full search is on by default, restrict as required */
   SoyMailConfig.SearchEnabled = TRUE;
   SoyMailConfig.SearchKeywordOnly = FALSE;
   SoyMailConfig.SearchHeaderOnly = FALSE;
   SoyMailConfig.SearchPriority = -1;

   /* by default soyMAIL's VMS-isms are obvious */
   SoyMailConfig.VmsOccluded = FALSE;

   /* by default we start reporting low disk quota at 85% used */
   SoyMailConfig.DiskQuotaPercent = 85;

   /* ten minutes by default */
   SoyMailConfig.ContextCacheSeconds = 10 * 60;

   /* obvious ones */
   SoyMailConfig.CharSetsRightToLeft = CHARSETS_COMMONLY_RTL;

   ConditionalCount = ConditionalFalseAt = 0;

   while (ConfigFileIndex ||
          *ConfigTextPtr[ConfigFileIndex])
   {
      cptr = ConfigTextParse (&ConfigTextPtr[ConfigFileIndex],
                              &NamePtr, &ValuePtr);
      if (!cptr)
      {
         if (ConfigFileIndex--) continue;
         break;
      }

      /****************/
      /* conditionals */
      /****************/

      ErrorPtr = NULL;
      WasConditional = FALSE;

      if (strsame (NamePtr, "if-CGI-", 7))
      {
         WasConditional = TRUE;
         ConditionalCount++;
         CGIVAR (cptr, NamePtr+7);
         if (!cptr) cptr = "";
         if (!ConditionalFalseAt)
         {
            if (!*ValuePtr) {
               if (!*cptr) ConditionalFalseAt = ConditionalCount;
            }
            else
            if (!SearchKeyword (cptr, ValuePtr, NULL))
               ConditionalFalseAt = ConditionalCount;
         }
      }
      else
      if (strsame (NamePtr, "if-not-CGI-", 11))
      {
         WasConditional = TRUE;
         ConditionalCount++;
         CGIVAR (cptr, NamePtr+11);
         if (!cptr) cptr = "";
         if (!ConditionalFalseAt)
         {
            if (!*ValuePtr) {
               if (*cptr) ConditionalFalseAt = ConditionalCount;
            }
            else
            if (SearchKeyword (cptr, ValuePtr, NULL))
               ConditionalFalseAt = ConditionalCount;
         }
      }
      else
      if (strsame (NamePtr, "if-private", -1))
      {
         WasConditional = TRUE;
         ConditionalCount++;
         if (!ConditionalFalseAt)
         {
            ConfigSetAccess (rdptr);
            if (!rdptr->PrivateAccess)
               ConditionalFalseAt = ConditionalCount;
         }
      }
      else
      if (strsame (NamePtr, "if-public", -1))
      {
         WasConditional = TRUE;
         ConditionalCount++;
         if (!ConditionalFalseAt)
         {
            ConfigSetAccess (rdptr);
            if (!rdptr->PublicAccess)
               ConditionalFalseAt = ConditionalCount;
         }
      }
      else
      if (strsame (NamePtr, "if-END", -1) ||
          strsame (NamePtr, "end-if", -1) ||
          strsame (NamePtr, "endif", -1))
      {
         WasConditional = TRUE;
         if (ConditionalFalseAt == ConditionalCount) ConditionalFalseAt = 0;
         if (ConditionalCount > 0)
            ConditionalCount--;
         else
            ErrorPtr = "conditonal underflow";
      }

      if (WatchEnabled)
      {
         if (WasConditional)
            if (!ConditionalFalseAt || ConditionalCount == ConditionalFalseAt)
               cptr = "TEST";
            else
               cptr = "SKIP";
         else
         if (ConditionalFalseAt)
            cptr = "SKIP";
         else
            cptr = "DO";

         if (!*ValuePtr)
            sptr = "";
         else
         if (strchr (ValuePtr, '\n'))
            sptr = "\n";
         else
            sptr = " ";

         WatchThis (FI_LI, "CONFIG !UL !AZ [!AZ]!AZ!AZ",
                    ConditionalCount, cptr, NamePtr,
                    sptr, ValuePtr);
      }

      if (ConditionalFalseAt || WasConditional)
      {
         if (ErrorPtr) ErrorExit (SS$_BUGCHECK, FI_LI);;
         continue;
      }

      if (strsame (NamePtr, "end", -1))
      {
         if (ConfigFileIndex--) continue;
         break;
      }

      /*****************/
      /* set directive */
      /*****************/

      if (strsame (NamePtr, "access-log", -1))
         SoyMailConfig.AccessLogFile = ValuePtr;
      else
      if (strsame (NamePtr, "attachment-MIME-types", -1))
         SoyMailConfig.AttachmentMimeTypes = ValuePtr;
      else
      if (strsame (NamePtr, "charset-default", -1))
         SoyMailConfig.CharSetDefault = ValuePtr;
      else
      if (strsame (NamePtr, "charsets-rtl", -1))
         SoyMailConfig.CharSetsRightToLeft = ValuePtr;
      else
      if (strsame (NamePtr, "compose-charsets", -1))
         SoyMailConfig.ComposeCharSets = ValuePtr;
      else
      if (strsame (NamePtr, "compose-contacts-after", -1))
         SoyMailConfig.ComposeContactsAfter = atoi(ValuePtr);
      else
      if (strsame (NamePtr, "compose-contacts-size", -1))
         SoyMailConfig.ComposeContactsSize = atoi(ValuePtr);
      else
      if (strsame (NamePtr, "compose-edit-cols", -1))
         SoyMailConfig.ComposeEditCols = ValuePtr;
      else
      if (strsame (NamePtr, "compose-edit-rows", -1))
         SoyMailConfig.ComposeEditRows = ValuePtr;
      else
      if (strsame (NamePtr, "compose-user-from", -1))
      {
         if (strsame (ValuePtr, "DISPLAY", -1))
            SoyMailConfig.ComposeUserFrom = -1;
         else
            SoyMailConfig.ComposeUserFrom = ConfigBoolean(ValuePtr);
      }
      else
      if (strsame (NamePtr, "compose-wrap-at", -1))
         SoyMailConfig.ComposeWrapAt = ValuePtr;
      else
      if (strsame (NamePtr, "context-cache-expires", -1))
         SoyMailConfig.ContextCacheSeconds = atoi(ValuePtr)*60;
      else
      if (strsame (NamePtr, "disk-quota-percent", -1))
         SoyMailConfig.DiskQuotaPercent = atoi(ValuePtr);
      else
      if (strsame (NamePtr, "downtime", -1))
         SoyMailConfig.DowntimeMessage = ValuePtr;
      else
      if (strsame (NamePtr, "include-file", -1))
      {
         if (ConfigFileIndex < CONFIG_INCLUDE_DEPTH)
         {
            ConfigFileIndex++;
            status = ReadFileIntoMemory (ValuePtr,
                                         &ConfigTextPtr[ConfigFileIndex],
                                         &ConfigTextLength[ConfigFileIndex]);
            if (VMSnok (status))
            {
               ConfigFileIndex--;
               if (WatchEnabled)
                  WatchThis (FI_LI, "%X!8XL !AZ", status, SysGetMsg(status));
            }
         }
         else
         if (WatchEnabled)
            WatchThis (FI_LI, "MAX include depth !UL", CONFIG_INCLUDE_DEPTH);
      }
      else
      if (strsame (NamePtr, "image-inline", -1))
         SoyMailConfig.ImageInline = strtol(ValuePtr, NULL, 10);
      else
      if (strsame (NamePtr, "message-flags", -1))
         SoyMailConfig.MessageFlags = strtol(ValuePtr, NULL, 16);
      else
      if (strsame (NamePtr, "message-list-footer", -1))
         SoyMailConfig.MessageListFooter = ValuePtr;
      else
      if (strsame (NamePtr, "font-family-local", -1))
         SoyMailConfig.FontFamilyLocal = ValuePtr;
      else
      if (strsame (NamePtr, "greeting", -1))
         SoyMailConfig.InitialGreeting = ValuePtr;
      else
      if (strsame (NamePtr, "help-about-site", -1))
         SoyMailConfig.HelpAboutSite = ValuePtr;
      else
      if (strsame (NamePtr, "html-editor-load", -1))
         SoyMailConfig.HtmlEditorLoad = ValuePtr;
      else
      if (strsame (NamePtr, "login-acme-no-restrict", -1))
         SoyMailConfig.LoginAcmeNoRestrict = ConfigBoolean(ValuePtr);
      else
      if (strsame (NamePtr, "login-acme-doi", -1))
         SoyMailConfig.LoginAcmeDoi = ValuePtr;
      else
      if (strsame (NamePtr, "login-agent", -1))
         SoyMailConfig.LoginAgent = ValuePtr;
      else
      if (strsame (NamePtr, "login-alias", -1))
         SoyMailConfig.LoginAlias = ValuePtr;
      else
      if (strsame (NamePtr, "login-alias-SMTP-from", -1))
         SoyMailConfig.LoginAliasSmtpFrom = ConfigBoolean(ValuePtr);
      else
      if (strsame (NamePtr, "login-autocomplete", -1))
         SoyMailConfig.LoginAutoComplete = ConfigBoolean(ValuePtr);
      else
      if (strsame (NamePtr, "login-change-page", -1))
         SoyMailConfig.LoginChangePage = ValuePtr;
      else
      if (strsame (NamePtr, "login-idle", -1))
         SoyMailConfig.LoginIdleSeconds = atoi(ValuePtr);
      else
      if (strsame (NamePtr, "login-language", -1))
         SoyMailConfig.LoginLanguage = ValuePtr;
      else
      if (strsame (NamePtr, "login-no-pwdexp", -1))
         SoyMailConfig.LoginNoPwdExp = ConfigBoolean(ValuePtr);
      else
      if (strsame (NamePtr, "login-page", -1))
         SoyMailConfig.LoginPage = ValuePtr;
      else
      if (strsame (NamePtr, "login-secret", -1))
         SoyMailConfig.LoginSecret = ValuePtr;
      else
      if (strsame (NamePtr, "login-type", -1))
         SoyMailConfig.LoginType = atoi(ValuePtr);
      else
      if (strsame (NamePtr, "login-verify", -1))
         SoyMailConfig.LoginVerifySeconds = atoi(ValuePtr);
      else
      if (strsame (NamePtr, "logout-realm", -1))
         SoyMailConfig.LogoutRealm = ValuePtr;
      else
      if (strsame (NamePtr, "VMS-newmsg", -1))
         SoyMailConfig.VmsNewMsg = ConfigBoolean(ValuePtr);
      else
      if (strsame (NamePtr, "page-title", -1) ||
          strsame (NamePtr, "page-title1", -1))
         SoyMailConfig.PageTitle1 = ValuePtr;
      else
      if (strsame (NamePtr, "page-title2", -1))
         SoyMailConfig.PageTitle2 = ValuePtr;
      else
      if (strsame (NamePtr, "print-footer", -1))
         SoyMailConfig.PrintFooter = ValuePtr;
      else
      if (strsame (NamePtr, "print-header", -1))
         SoyMailConfig.PrintHeader = ValuePtr;
      else
      if (strsame (NamePtr, "private-access", -1))
         SoyMailConfig.PrivateAccess = ValuePtr;
      else
      if (strsame (NamePtr, "private-request", -1))
      {
         SoyMailConfig.PrivateRequest = TRUE;
         SoyMailConfig.PublicRequest = FALSE;
      }
      else
      if (strsame (NamePtr, "public-access", -1))
         SoyMailConfig.PublicAccess = ValuePtr;
      else
      if (strsame (NamePtr, "public-footer", -1))
         SoyMailConfig.PublicFooter = ValuePtr;
      else
      if (strsame (NamePtr, "public-no-access", -1))
         SoyMailConfig.PublicNoAccessPage = ValuePtr;
      else
      if (strsame (NamePtr, "public-options-default", -1))
         SoyMailConfig.PublicOptionsDefault = ValuePtr;
      else
      if (strsame (NamePtr, "public-request", -1))
      {
         SoyMailConfig.PublicRequest = TRUE;
         SoyMailConfig.PrivateRequest = FALSE;
      }
      else
      if (strsame (NamePtr, "search-control", -1))
         SoyMailConfig.SearchControl = ValuePtr;
      else
      if (strsame (NamePtr, "site-style-sheet", -1))
         SoyMailConfig.SiteStyleSheet = ValuePtr;
      else
      if (strsame (NamePtr, "SMTP-default-host", -1))
         SoyMailConfig.SmtpDefaultHost = ValuePtr;
      else
      if (strsame (NamePtr, "SMTP-from-host", -1))
         SoyMailConfig.SmtpFromHost = ValuePtr;
      else
      if (strsame (NamePtr, "SMTP-server-host", -1))
         SoyMailConfig.SmtpServerHost = ValuePtr;
      else
      if (strsame (NamePtr, "soyMAIL-at-title", -1))
         SoyMailConfig.SoyMailAtTitle = ValuePtr;
      else
      if (strsame (NamePtr, "update-last-login", -1))
         SoyMailConfig.UpdateLastLogin = ValuePtr;
      else
      if (strsame (NamePtr, "user-name-info", -1))
      {
         if (strsame (ValuePtr, "ALIAS", -1))
            SoyMailConfig.UserNameInfo = -1;
         else
            SoyMailConfig.UserNameInfo = ConfigBoolean(ValuePtr);
      }
      else
      if (strsame (NamePtr, "user-options-default", -1))
         SoyMailConfig.UserOptionsDefault = ValuePtr;
      else
      if (strsame (NamePtr, "user-options-override", -1))
         SoyMailConfig.UserOptionsOverride = ValuePtr;
      else
      if (strsame (NamePtr, "vms-occluded", -1))
         SoyMailConfig.VmsOccluded = ConfigBoolean(ValuePtr);
      else
      if (WatchEnabled)
         WatchThis (FI_LI, "CONFIG ERROR [!AZ]", NamePtr);
   }

   /* in case there were no [if-private] and/or [if-public] */
   ConfigSetAccess (rdptr);

   if (SoyMailConfig.SearchControl)
   {
      for (cptr = SoyMailConfig.SearchControl; *cptr; cptr++)
         *cptr = tolower(*cptr);
      if (strstr (SoyMailConfig.SearchControl, "full"))
      {
         SoyMailConfig.SearchEnabled = TRUE;
         SoyMailConfig.SearchKeywordOnly = FALSE;
         SoyMailConfig.SearchHeaderOnly = FALSE;
      }
      if (strstr (SoyMailConfig.SearchControl, "keyword-only"))
         SoyMailConfig.SearchKeywordOnly = TRUE;
      if (strstr (SoyMailConfig.SearchControl, "header-only"))
         SoyMailConfig.SearchHeaderOnly = TRUE;
      if (strstr (SoyMailConfig.SearchControl, "vms-select"))
         SoyMailConfig.SelectNotSearch = TRUE;
      if (cptr = strstr (SoyMailConfig.SearchControl, "priority="))
      {
         cptr += 9;
         if (isdigit(*cptr)) SoyMailConfig.SearchPriority = atol(cptr);
      }
      if (strstr (SoyMailConfig.SearchControl, "none"))
      {
         SoyMailConfig.SearchEnabled =
            SoyMailConfig.SearchKeywordOnly =
            SoyMailConfig.SearchHeaderOnly =
            SoyMailConfig.SelectNotSearch = FALSE;
      }
   }

   if (SoyMailConfig.LoginSecret)
   {
      unsigned char  *ucptr;
      /* make it a little less like ASCII data by swapping the nybles */
      for (ucptr = (unsigned char*)SoyMailConfig.LoginSecret; *ucptr; ucptr++)
         *ucptr = ((*ucptr & 0xf0) >> 4) | ((*ucptr & 0x0f) << 4);
      /* check that it's a large enough secret */
      if (ucptr - (unsigned char*)SoyMailConfig.LoginSecret < 24)
      {
         if (WatchEnabled)
            WatchThis (FI_LI, "CONFIG ERROR [login-secret] less than 24 characters");
         ErrorExit (SS$_NOPRIV, FI_LI);
      }

      if (!SoyMailConfig.LoginLanguage) SoyMailConfig.LoginLanguage = "en";
   }

   if (SoyMailConfig.LogoutRealm)
   {
      /* realm has been set */
      if (!strcmp (SoyMailConfig.LogoutRealm, "-"))
      {
         /* setting disables logout */
         SoyMailConfig.LogoutDisabled = TRUE;
      }
   }
   else
   {
      /* realm has not been set */
      if (!CgiLibEnvironmentIsWasd() && !SoyMailConfig.LoginSecret)
      {
         /* Apache and OSU must have it set for logout to function */
         SoyMailConfig.LogoutDisabled = TRUE;
      }
   }

   if (!SoyMailConfig.ComposeContactsAfter)
      SoyMailConfig.ComposeContactsAfter = 50;

   if (!SoyMailConfig.ComposeContactsSize)
      SoyMailConfig.ComposeContactsSize = 20;

   if (WatchEnabled)
      if (ConditionalCount)
         WatchThis (FI_LI, "CONFIG WARNING conditionals not balanced");
}

/****************************************************************************/
/*
Return true or false depedning on what contained in the parameter.
*/ 

BOOL ConfigBoolean (char *cptr)

{
   /*********/
   /* begin */
   /*********/

   switch (*cptr)
   {
       case '1' : return (TRUE);
       case '0' : return (FALSE);
       case '+' : return (TRUE);
       case '-' : return (FALSE);
   }
   if (strsame (cptr,"TRUE",-1)) return (TRUE);
   if (strsame (cptr,"FALSE",-1)) return (FALSE);
   if (strsame (cptr,"ENABLE", 6)) return (TRUE);
   if (strsame (cptr,"DISABLE", 7)) return (FALSE);
   if (strsame (cptr,"ON",-1)) return (TRUE);
   if (strsame (cptr,"OFF",-1)) return (FALSE);
   if (strsame (cptr,"YES",-1)) return (TRUE);
   if (strsame (cptr,"NO",-1)) return (FALSE);

   if (WatchEnabled) WatchThis (FI_LI, "CONFIG BOOLEAN? [!AZ]", cptr);

   return (FALSE);
}

/*****************************************************************************/
/*
Parses the contents of a file read into memory, dividing it up into
null-terminated strings with names and values.  The general format for these
name-value pairs is '[name] value', with values allowed to comprise one or more
newline separated strings.

NOTE: This function is used to parse a number of different soyMAIL files that
all use this '[name] value' schema.
*/

char* ConfigTextParse
(
char** TextPtrPtr,
char** NamePtrPtr,
char** ValuePtrPtr
)
{
#undef ISLWS
#define ISLWS(ch) (ch == ' ' || ch == '\t')
#define ISNL(ch) (ch == '\n')

   char  *cptr, *sptr, *nwsptr,
         *NamePtr,
         *ValuePtr,
         *ErrorPtr;

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

   if (Debug) fprintf (stdout, "ConfigTextParse()\n");

   if (!TextPtrPtr) ErrorExit (SS$_BUGCHECK, FI_LI);
   if (!NamePtrPtr) ErrorExit (SS$_BUGCHECK, FI_LI);
   if (!ValuePtrPtr) ErrorExit (SS$_BUGCHECK, FI_LI);

   cptr = *TextPtrPtr;
   if (!*cptr) return (NULL);

   NamePtr = ValuePtr = NULL;
   *NamePtrPtr = NamePtr;
   *ValuePtrPtr = ValuePtr;

   while (*cptr)
   {
      /* ignore leading white-space */
      while (*cptr && (ISLWS(*cptr) || ISNL(*cptr))) cptr++;
      if (!*cptr) break;
      if (*cptr == '#')
      {
         while (*cptr && !ISNL(*cptr)) cptr++;
         continue;
      }
      sptr = cptr;
      while (*cptr && *cptr != ']' && !ISNL(*cptr))
      {
         if (*cptr == '\\' && *(cptr+1) && *(cptr+1) != '\n') cptr++;
         cptr++;
      }
      if (*sptr != '[' || *cptr != ']')
      {
         if (WatchEnabled)
            WatchThis (FI_LI, "CONFIG PARSE ERROR !#AZ", cptr-sptr, sptr);
         while (*cptr && !ISNL(*cptr)) cptr++;
         continue;
      }
      NamePtr = ++sptr;
      *cptr++ = '\0';
      /* scan over intervening white-space */
      while (*cptr)
      {
         while (*cptr && ISLWS(*cptr)) cptr++;
         if (*cptr && !ISNL(*cptr)) break;
         if (*(cptr+1) == '[' || *(cptr+1) == '#') break;
         cptr++;
      }
      /* also note first non-white-space */
      ValuePtr = nwsptr = sptr = cptr;
      /* scan to next directive */
      while (*cptr)
      {
         while (*cptr && !ISNL(*cptr))
         {
            if (*cptr == '\\' && *(cptr+1) && *(cptr+1) != '\n') cptr++;
            *sptr = *cptr;
            /* note most recent non-white-space */
            if (*cptr && !ISLWS(*sptr) && !ISNL(*sptr)) nwsptr = sptr;
            sptr++;
            cptr++;
         }
         if (!*cptr) break;
         /* must be a newline */
         if (*(cptr+1) == '[' || *(cptr+1) == '#') break;
         *sptr++ = *cptr++;
      }
      /* before possibly terminating over the top of any newline */
      while (ISNL(*cptr)) cptr++;
      if (sptr > ValuePtr)
      {
         /* terminate the value */
         *(nwsptr+1) = '\0';
      }
      else
         ValuePtr = "";
      break;
   }

   *NamePtrPtr = NamePtr;
   *ValuePtrPtr = ValuePtr;
   *TextPtrPtr = cptr;

   if (WatchEnabled && NamePtr && ValuePtr)
   {
      if (!*ValuePtr)
         sptr = "";
      else
      if (strchr (ValuePtr, '\n'))
         sptr = "\n";
      else
         sptr = " ";

      WatchThis (FI_LI, "PARSE [!AZ]!AZ!AZ", NamePtr, sptr, ValuePtr);
   }

   return (NamePtr);
}

/****************************************************************************/
/*
Write the supplied text to the supplied file ensuring that with multi-line text
any subsequent lines have the leading, configuration-reserved characters '['
and '#', escaped.  Even with empty text writes at least a newline character!
*/ 

int ConfigWriteEscaped
(
FILE *fp,
char *TextPtr
)
{
   int  lcnt, retval;
   char  *cptr, *eptr, *sptr;

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

   lcnt = retval = 0;
   cptr = sptr = TextPtr;
   for (;;)
   {
      while (*cptr && *cptr != '\n') cptr++;
      lcnt++;
      if (lcnt > 1 && (*sptr == '[' || *sptr == '#'))
         eptr = "\\";
      else
         eptr = "";
      retval += fprintf (fp, "%s%*.*s\n", eptr, cptr-sptr, cptr-sptr, sptr);
      if (*cptr) cptr++;
      if (!*cptr) break;
      sptr = cptr;
   }
   return (retval);
}

/****************************************************************************/
/*
Map the private access path into the respective VMS Mail username.
Returns TRUE is a mapping can be made, FALSE if not.
*/ 

BOOL ConfigPrivateAccess (REQUEST_DATA *rdptr)

{
   char  *cptr, *sptr, *zptr,
         *AuthRealmPtr,
         *MappingPtr;
   char  AuthRealmName [64],
         RemoteUserName [64],
         VmsUserName [64];

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

   if (WatchEnabled)
   {
      WatchThis (FI_LI, "PRIVATE access");
      fprintf (stdout, "%s\n", SoyMailConfig.PrivateAccess ?
                                  SoyMailConfig.PrivateAccess : "(null)");
   }

   CGIVARNULL (AuthRealmPtr, "AUTH_REALM");

   cptr = SoyMailConfig.PrivateAccess;
   if (!cptr) cptr = "";

   while (*cptr)
   {
      /* get mapped remote (authenticated) user name */
      while (*cptr && ISLWS(*cptr)) cptr++;
      MappingPtr = cptr;
      zptr = (sptr = RemoteUserName) + sizeof(RemoteUserName)-1;
      while (*cptr && *cptr != '/' && *cptr != '\n' &&
             !ISLWS(*cptr) && sptr < zptr)
      {
         if (*cptr == '\\' && *(cptr+1)) cptr++;
         *sptr++ = *cptr++;
      }
      if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI);
      *sptr = '\0';

      /* get mapped authentication realm name (WASDism) */
      while (*cptr && (ISLWS(*cptr) || *cptr == '/')) cptr++;
      zptr = (sptr = AuthRealmName) + sizeof(AuthRealmName)-1;
      while (*cptr && *cptr != '/' && *cptr != '\n' &&
             !ISLWS(*cptr) && sptr < zptr)
      {
         if (*cptr == '\\' && *(cptr+1)) cptr++;
         *sptr++ = *cptr++;
      }
      if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI);
      *sptr = '\0';

      /* get mapped VMS user name */
      while (*cptr && (ISLWS(*cptr) || *cptr == '/')) cptr++;
      zptr = (sptr = VmsUserName) + sizeof(VmsUserName)-1;
      while (*cptr && *cptr != '/' && *cptr != '\n' &&
             !ISLWS(*cptr) && sptr < zptr)
      {
         if (*cptr == '\\' && *(cptr+1)) cptr++;
         *sptr++ = *cptr++;
      }
      if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI);
      *sptr = '\0';

      if (WatchEnabled)
         WatchThis (FI_LI, "PRIVATE mapping !#AZ", cptr-MappingPtr, MappingPtr);

      /* go to end-of-line */
      while (*cptr && !ISNL(*cptr)) cptr++;
      while (*cptr && ISNL(*cptr)) cptr++;

      if (WatchEnabled)
         WatchThis (FI_LI, "PRIVATE remote !AZ !AZ",
                    RemoteUserName, rdptr->RemoteUser);

      if (RemoteUserName[0] != '*' &&
          !strsame (RemoteUserName, rdptr->RemoteUser, -1))
         continue;

      if (WatchEnabled && AuthRealmPtr)
         WatchThis (FI_LI, "PRIVATE realm !AZ !AZ", AuthRealmName, AuthRealmPtr);

      if (AuthRealmPtr &&
          AuthRealmName[0] != '*' &&
          !strsame (AuthRealmName, AuthRealmPtr, -1))
         continue;

      if (strsame (VmsUserName, "(POSTMASTER)", -1))
      {
         rdptr->PostMaster = TRUE;
         continue;
      }

      zptr = (sptr = rdptr->UserName) + sizeof(rdptr->UserName)-1;
      if (VmsUserName[0] == '*')
         cptr = rdptr->RemoteUser;
      else
         cptr = VmsUserName;
      while (*cptr && sptr < zptr) *sptr++ = *cptr++;
      if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI);
      *sptr = '\0';

      if (WatchEnabled)
         WatchThis (FI_LI, "PRIVATE username !AZ", rdptr->UserName);
      return (TRUE);
   }

   if (WatchEnabled) WatchThis (FI_LI, "PRIVATE failed to match");

   return (FALSE);
}

/****************************************************************************/
/*
Map the public access path into the respective VMS Mail username, mail file and
mail folder.  Returns TRUE is a path match is made, FALSE if not.
*/ 

BOOL ConfigPublicAccess (REQUEST_DATA *rdptr)

{
   char  *cptr, *sptr, *tptr, *zptr;
   char  PathMatchBuffer [256];

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

   if (WatchEnabled)
   {
      WatchThis (FI_LI, "PUBLIC access !AZ", rdptr->PublicAccessString);
      fprintf (stdout, "%s\n", SoyMailConfig.PublicAccess ?
                                SoyMailConfig.PublicAccess : "(null)");
   }

   cptr = SoyMailConfig.PublicAccess;
   if (!cptr) cptr = "";

   while (*cptr)
   {
      while (*cptr && ISLWS(*cptr)) cptr++;
      if (*cptr == '/') cptr++;
      zptr = (sptr = PathMatchBuffer) + sizeof(PathMatchBuffer)-1;
      while (*cptr && *cptr != '/' && !ISLWS(*cptr) && !ISNL(*cptr) &&
             sptr < zptr)
      {
         if (*cptr == '\\' && *(cptr+1)) cptr++;
         *sptr++ = *cptr++;
      }
      if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI);
      *sptr = '\0';

      if (!strsame (rdptr->PublicAccessString, PathMatchBuffer, -1))
      {
         /* no match, go to start of next rule */
         if (WatchEnabled) WatchThis (FI_LI, "PUBLIC no !AZ", PathMatchBuffer);
         while (*cptr && !ISNL(*cptr)) cptr++;
         while (ISNL(*cptr)) cptr++;
         continue;
      }

      if (*cptr == '/') cptr++;
      while (*cptr && isspace(*cptr)) cptr++;

      if (*cptr == '/')
      {
         /**********************/
         /* public access mail */
         /**********************/

         cptr++;
         zptr = (sptr = rdptr->UserName) + sizeof(rdptr->UserName)-1;
         while (*cptr && *cptr != '/' && !ISLWS(*cptr) && !ISNL(*cptr) &&
                sptr < zptr)
         {
            if (*cptr == '\\' && *(cptr+1)) cptr++;
            *sptr++ = *cptr++;
         }
         if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI);
         *sptr = '\0';
         rdptr->UserNameLength = strlen(rdptr->UserName);
         if (*cptr == '/') cptr++;
         while (*cptr && ISLWS(*cptr)) cptr++;

         zptr = (sptr = rdptr->MailFileName) + sizeof(rdptr->MailFileName)-1;
         while (*cptr && *cptr != '/' && !ISLWS(*cptr) && !ISNL(*cptr) &&
                sptr < zptr)
         {
            if (*cptr == '\\' && *(cptr+1)) cptr++;
            *sptr++ = *cptr++;
         }
         if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI);
         *sptr = '\0';
         rdptr->MailFileNameLength = strlen(rdptr->MailFileName);
         if (*cptr == '/') cptr++;
         while (*cptr && ISLWS(*cptr)) cptr++;

         if (*cptr == '*')
         {
            /* allowed public access to any folder */
            rdptr->PublicWildcard = TRUE;
            cptr++;
            if (*cptr == '!')
            {
               /* except the MAIL and NEWMAIL folders */
               rdptr->PublicNoNewMail = TRUE;
               cptr++;
            }
         }
         else
         {
            /* t'be sure t'be sure */
            rdptr->PublicWildcard = FALSE;
         }

         /* use the folder specified in the directive */
         zptr = (sptr = rdptr->FolderName) + sizeof(rdptr->FolderName)-1;
         while (*cptr && *cptr != '/' && !ISLWS(*cptr) && !ISNL(*cptr) &&
                sptr < zptr)
         {
            if (*cptr == '\\' && *(cptr+1)) cptr++;
            *sptr++ = *cptr++;
         }
         if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI);
         *sptr = '\0';
         rdptr->FolderNameLength = strlen(rdptr->FolderName);
         if (*cptr == '/') cptr++;
      }
      else
      {
         /**********************/
         /* public access page */
         /**********************/

         zptr = (sptr = rdptr->PublicAccessPage) +
                sizeof(rdptr->PublicAccessPage)-1;
         while (*cptr && !ISLWS(*cptr) && !ISNL(*cptr) && sptr < zptr)
            *sptr++ = *cptr++;
         if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI);
         *sptr = '\0';

         while (*cptr && ISLWS(*cptr)) cptr++;
         if (*(USHORTPTR)cptr == '[]') rdptr->PublicAuthorized = TRUE;

         /* end of public access page configuration */
         return (TRUE);
      }

      while (*cptr && ISLWS(*cptr)) cptr++;
      if (*(USHORTPTR)cptr == '[]')
      {
         rdptr->PublicAuthorized = TRUE;
         cptr += 2;
         while (*cptr && ISLWS(*cptr)) cptr++;
      }

      if (!ISNL(*cptr))
      {
         /* trailing public folder title */
         zptr = (sptr = rdptr->PublicAccessTitle) +
                sizeof(rdptr->PublicAccessTitle)-1;
         while (*cptr && !ISNL(*cptr) && sptr < zptr)
         {
            if (*cptr == '\\' && *(cptr+1)) cptr++;
            *sptr++ = *cptr++;
         }
         *sptr = '\0';
      }

      if (rdptr->UserNameLength &&
          rdptr->MailFileNameLength &&
          (rdptr->FolderNameLength || rdptr->PublicWildcard))
      {
         if (WatchEnabled)
            WatchThis (FI_LI, "PUBLIC as \"!AZ\" \"!AZ\" \"!AZ\" \"!AZ\" !AZ",
                       rdptr->UserName, rdptr->MailFileName,
                       rdptr->FolderName, rdptr->PublicAccessTitle,
                       rdptr->PublicAuthorized ? "AUTHORIZED" : "PUBLIC");
         return (TRUE);
      }
                                  
      /* something wrong, go to start of next rule */
      if (WatchEnabled) WatchThis (FI_LI, "PUBLIC error !AZ", PathMatchBuffer);
      while (*cptr && *cptr != '\n') cptr++;
      while (*cptr == '\n') cptr++;
   }

   rdptr->UserName[rdptr->UserNameLength = 0] = '\0';
   rdptr->MailFileName[rdptr->MailFileNameLength = 0] = '\0';
   rdptr->FolderName[rdptr->FolderNameLength = 0] = '\0';

   if (WatchEnabled) WatchThis (FI_LI, "PUBLIC failed to match");

   return (FALSE);
}

/****************************************************************************/
/*
Determine whether the access is private or public.
*/ 

void ConfigSetAccess (REQUEST_DATA *rdptr)

{
   /*********/
   /* begin */
   /*********/

   if (rdptr->PrivateAccess || rdptr->PublicAccess) return;

   if (SoyMailConfig.PrivateRequest)
      rdptr->PrivateAccess = TRUE;
   else
   if (SoyMailConfig.PublicRequest)
      rdptr->PublicAccess = TRUE;
   else
   if (*(USHORTPTR)rdptr->CgiPathInfoPtr == '/~')
      rdptr->PrivateAccess = TRUE;
   else
      rdptr->PublicAccess = TRUE;

   if (WatchEnabled)
      WatchThis (FI_LI, "!AZ access", rdptr->PrivateAccess ? "PRIVATE" : "PUBLIC");
}

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

