/********************************************************************
* wilkinson
* 3.142VMS-1
* 1996/01/24 11:00
* gopher_root1:[gopher.g2.vms2_13.gopherd]gopherd.c,v
* Exp
*
* Paul Lindner, University of Minnesota CIS.
*
* Copyright 1991, 1992 by the Regents of the University of Minnesota
* see the file "Copyright" in the distribution for conditions of use.
*********************************************************************
* MODULE: gopherd.c
* Routines to implement the gopher server.
*********************************************************************
* Revision History:
* gopherd.c,v
* Revision 3.142VMS-2 1996/01/24 11:00  wilkinson
* Made OPCOM message console selection configurable
*
* Revision 3.142VMS-1 1995/11/24 14:00  wilkinson
* Added local host filter to suppress extensive postprocessing on resources
*      on other servers.
* Override argv[0] from command line with GOPHERD_IMG_NAME logical value, if
*      any.
* Fixed "Client went away" errors in printfile() to not call gopherd_exit().
* Make use of new CMDisErrored() macro after CMDfromNet() and act appropriate
*      for OpenVMS detached server in the case of an error.
*
* Revision 3.142VMS 1995/09/20  15:30   wilkinson
* Consolodate VMS/Unix source code for server as well as client
*
* Revision 3.142  1995/02/25  20:49:03  lindner
* More timeouts..
*
* Revision 3.141  1995/02/25  06:42:55  lindner
* Add write timeouts
*
* Revision 3.140  1995/02/17  18:32:35  lindner
* Fix for mindex
*
* Revision 3.139  1995/02/16  22:34:01  lindner
* Fix for no defined Maxsessions, A/UX signal handling
*
* Revision 3.138  1995/02/16  22:32:38  lindner
* HTML icon support
*
* Revision 3.137  1995/02/13  19:07:57  lindner
* Add MaxConnection limit for total server
*
* Revision 3.136  1995/02/11  06:22:25  lindner
* Mods to use new statistics and concurrent session tracking
*
* Revision 3.135  1995/02/07  08:37:36  lindner
* Rewrite of mailfile/multifile parsing
*
* Revision 3.134  1995/02/07  07:12:33  lindner
* oops!
*
* Revision 3.133  1995/02/07  07:04:05  lindner
* performance fixes
*
* Revision 3.132  1995/02/06  22:27:55  lindner
* Use dynamic space for Data_Dir, remove RunLS
*
* Revision 3.131  1995/02/06  21:26:15  lindner
* Misc performance fixes
*
* Revision 3.130  1995/02/02  17:13:53  lindner
* Fix memory leaks
*
* Revision 3.129  1995/02/01  21:41:26  lindner
* Fix for loco symbolic links
*
* Revision 3.128  1995/01/26  18:50:21  lindner
* More efficient, more understandable, less buggy directory parser..
*
* Revision 3.127  1995/01/04  17:37:27  lindner
* Make shell scripts line buffered...
*
* Revision 3.126  1994/12/20  17:20:15  lindner
* Rewritten directory parser
*
* Revision 3.125  1994/12/15  17:47:49  lindner
* Add script running capability
*
* Revision 3.124  1994/12/12  21:46:25  lindner
* bonehead
*
* Revision 3.123  1994/12/12  21:03:50  lindner
* Start towards CGI compliance
*
* Revision 3.122  1994/12/12  17:41:52  lindner
* Hack around AIX
*
* Revision 3.121  1994/12/12  16:58:01  lindner
* Add new AUTHresult code, AUTHRES_SYSERR
*
* Revision 3.120  1994/12/11  18:42:19  lindner
* Fix for authenticated ask blocks
*
* Revision 3.119  1994/12/10  08:24:22  lindner
* Bracket out the moddate setting code
*
* Revision 3.118  1994/12/10  06:13:22  lindner
* Add error message for writing ask data
*
* Revision 3.117  1994/12/02  00:38:37  lindner
* Fix for odd GUSER malloc
*
* Revision 3.116  1994/11/30  23:01:09  lindner
* Allow username to be passed to the environment
*
* Revision 3.115  1994/11/30  22:26:33  lindner
* Error fix
*
* Revision 3.114  1994/11/30  22:25:47  lindner
* Error fix
*
* Revision 3.113  1994/11/30  21:53:25  lindner
* Fix for one last Gticket problem
*
* Revision 3.112  1994/11/30  21:26:57  lindner
* Fix for one last Gticket problem
*
* Revision 3.111  1994/11/08  19:48:50  lindner
* Fix bug in mailfile processing
*
* Revision 3.110  1994/10/24  22:17:08  lindner
* Add PDF type
*
* Revision 3.109  1994/10/21  01:45:53  lindner
* refix linger problem
*
* Revision 3.108  1994/10/19  17:21:56  lindner
* Fix for dreaded Malformed command dialog
*
* Revision 3.105  1994/10/13  05:17:48  lindner
* Compiler complaint fixes
*
* Revision 3.104  1994/10/10  18:38:55  lindner
* change in linger behavior
*
* Revision 3.103  1994/09/29  19:59:31  lindner
* Force people to update their gopherd.conf files
*
* Revision 3.102  1994/08/18  22:28:28  lindner
* Abstract for top level and malloc casts
*
* Revision 3.101  1994/08/01  21:56:01  lindner
* Add proto
*
* Revision 3.100  1994/07/31  05:08:52  lindner
* Add pid header file...
*
* Revision 3.99  1994/07/22  16:36:45  lindner
* Fix bug in item_info for top level
*
* Revision 3.98  1994/07/22  15:02:32  lindner
* Fix bugs..
*
* Revision 3.97  1994/07/21  22:07:21  lindner
* Remove ifdef for NeXT
*
* Revision 3.96  1994/07/21  17:23:55  lindner
* FTP gopher+ gw, and file separator code
*
* Revision 3.95  1994/07/19  20:25:42  lindner
* Sizes for gopher directories from .cache files
*
* Revision 3.94  1994/06/29  05:42:43  lindner
* Add authentication capabilities
*
* Revision 3.93  1994/06/03  06:25:37  lindner
* Another fix for Hgopher method of alt views
*
* Revision 3.92  1994/05/18  04:00:40  lindner
* Add port# to waiting for connection message
*
* Revision 3.91  1994/05/14  04:19:33  lindner
* Fix for text files with really long lines
*
* Revision 3.90  1994/05/02  07:41:12  lindner
* Mods to use setlocale()
*
* Revision 3.89  1994/04/25  20:49:07  lindner
* Fix for debug code
*
* Revision 3.88  1994/04/21  21:24:21  lindner
* FIOclose fix
*
* Revision 3.87  1994/04/21  21:15:12  lindner
* Add looking up address item
*
* Revision 3.86  1994/04/19  14:30:17  lindner
* Fix for gopher+ shell scripts
*
* Revision 3.85  1994/04/13  04:17:57  lindner
* Fix for abnormal exits
*
* Revision 3.84  1994/04/08  21:09:21  lindner
* fix for shutdown calls and compiler goofiness
*
* Revision 3.83  1994/04/07  17:28:39  lindner
* putenv stuff
*
* Revision 3.82  1994/04/01  05:03:35  lindner
* Move putenv() later
*
* Revision 3.81  1994/03/31  22:47:36  lindner
* Fix for date and time for multiple view items and Type 1 ask forms
*
* Revision 3.80  1994/03/31  21:25:37  lindner
* Shutdown inetd sockets, simplify some code
*
* Revision 3.79  1994/03/30  21:36:35  lindner
* Fix for binary ask data from Don Gilbert
*
* Revision 3.78  1994/03/17  21:18:08  lindner
* Massive reworking of access limits
*
* Revision 3.77  1994/03/17  04:30:11  lindner
* VMS fixes gopherd.h
*
* Revision 3.76  1994/03/15  17:59:05  lindner
* Some code moved to Sockets.c, some reorg. Fixes for SCO compiler
*
* Revision 3.75  1994/03/08  17:11:16  lindner
* One more fix for listdir
*
* Revision 3.74  1994/03/08  16:45:30  lindner
* Fix things broken by fixing recursive dirs
*
* Revision 3.73  1994/03/08  15:55:41  lindner
* gcc -Wall fixes
*
* Revision 3.72  1994/03/08  15:02:04  lindner
* Fix for unset variable i in main() Causes crash on NextStep486
*
* Revision 3.71  1994/03/08  06:15:57  lindner
* Fix for recursive directory traversal
*
* Revision 3.70  1994/03/04  23:26:08  lindner
* Fix for changes in strstring.h
*
* Revision 3.69  1994/02/20  21:41:31  lindner
* Allow use of -u if the uid specified matches the current uid
*
* Revision 3.68  1994/02/20  16:53:17  lindner
* Add buffered text writes to server, shutdown inbound part of socket after reading
*
* Revision 3.67  1994/01/25  05:25:25  lindner
* Many HTML and URL related fixes
*
* Revision 3.66  1994/01/06  05:42:41  lindner
* Fix for bad clients that don't bind right
*
* Revision 3.65  1993/12/30  04:15:20  lindner
* removed extra fclose in Side_File, fixes certain Linux distributions
*
* Revision 3.64  1993/12/27  16:34:50  lindner
* Now Add dots to the end of the DNS name
*
* Revision 3.63  1993/12/09  20:47:47  lindner
* Remove error destroying data, add boot up message with pid to log file
*
* Revision 3.62  1993/11/03  03:35:25  lindner
* Add headlines to the top level HTML page
*
* Revision 3.61  1993/11/02  06:08:11  lindner
* Strip extensions off of files with multiple views
*
* Revision 3.60  1993/11/02  05:58:12  lindner
* WAIS index speedups, mondo HTML mods
*
* Revision 3.59  1993/10/28  22:08:17  lindner
* memory leak fixes, fix for -u problem
*
* Revision 3.58  1993/10/20  03:22:59  lindner
* none
*
* Revision 3.57  1993/10/20  03:19:31  lindner
* Better error messages..
*
* Revision 3.56  1993/10/11  04:40:52  lindner
* Changes to allow logging via daemon.info syslogd facility
*
* Revision 3.55  1993/10/04  06:47:16  lindner
* Eliminate bogus warning messages
* Remove gindexd crap
* Mods to allow for new command structure for ASKfile
* Auxconf support..
*
* Revision 3.54  1993/09/30  16:57:02  lindner
* Fix for WAIS and $ requests
*
* Revision 3.53  1993/09/22  04:27:35  lindner
* Add ignore options to sidefile processing
*
* Revision 3.52  1993/09/22  00:29:40  lindner
* Speedups for gopher0 and big directories
*
* Revision 3.51  1993/09/21  07:00:03  lindner
* Fix for sites that don't do _POSIX_SAVED_IDS
*
* Revision 3.50  1993/09/21  04:16:45  lindner
* Move cache settings into gopherd.conf
*
* Revision 3.49  1993/09/21  02:35:07  lindner
* Server now adds extensions in a case insensitive manner
*
* Revision 3.48  1993/09/20  16:56:12  lindner
* Mods for moved code
*
* Revision 3.47  1993/09/18  03:27:19  lindner
* slight mod for ftp gateway
*
* Revision 3.46  1993/09/11  05:06:22  lindner
* Mod for ignoring files, and more efficient binary transfers
*
* Revision 3.45  1993/09/11  04:40:41  lindner
* Don't fork for localhost mindex databases
*
* Revision 3.44  1993/08/24  20:58:58  lindner
* fixed typo in add_title code
*
* Revision 3.43  1993/08/23  20:10:39  lindner
* Additional colon, gopher+ error fix
*
* Revision 3.42  1993/08/23  19:38:23  lindner
* Yet another fix for mindexd troubles..
*
* Revision 3.41  1993/08/23  18:46:11  lindner
* Crude addition of a veronica top-level block
*
* Revision 3.40  1993/08/23  02:34:30  lindner
* Optional date and time
*
* Revision 3.39  1993/08/20  18:03:00  lindner
* Mods to allow gopherd.conf files control ftp gateway access
*
* Revision 3.38  1993/08/19  20:52:25  lindner
* Mitra comments
*
* Revision 3.37  1993/08/19  20:25:50  lindner
* Mitra's Debug patch
*
* Revision 3.36  1993/08/12  06:27:35  lindner
* Get rid of errant message when using inetd
*
* Revision 3.35  1993/08/11  22:47:44  lindner
* Fix for gopher0 clients on gopher+ server
*
* Revision 3.34  1993/08/11  21:34:05  lindner
* Remove extensions from titles for files with multiple views.
* Move CMDfromNet() to *after* the chroot() and setuid()
*
* Revision 3.33  1993/08/11  14:39:24  lindner
* Fix for send_binary bug
*
* Revision 3.32  1993/08/11  02:27:40  lindner
* Fix for wais gateway and Unix client
*
* Revision 3.31  1993/08/10  20:26:57  lindner
* Fixed bogus reading of .cache+ files
*
* Revision 3.30  1993/08/06  14:42:49  lindner
* fix for mindex
*
* Revision 3.29  1993/08/06  14:30:40  lindner
* Fixes for better security logging
*
* Revision 3.28  1993/08/05  20:47:16  lindner
* Log execution of programs
*
* Revision 3.27  1993/08/02  17:59:26  lindner
* Fix for Debug syntax error when using DL
*
* Revision 3.26  1993/07/29  20:49:25  lindner
* removed dead vars, test for non-existant binary files, Dump_Core thing..
*
* Revision 3.25  1993/07/27  20:16:03  lindner
* Fixed bug logging directory transactions
*
* Revision 3.24  1993/07/27  06:13:40  lindner
* Bug fixes for redeffed .cache stuff
*
* Revision 3.23  1993/07/27  05:27:46  lindner
* Mondo Debug overhaul from Mitra
*
* Revision 3.22  1993/07/27  01:52:59  lindner
* More comments, don't let server die if fork error..
*
* Revision 3.21  1993/07/26  20:33:02  lindner
* Fix from guyton, cachefd can be zero
*
* Revision 3.20  1993/07/26  17:24:02  lindner
* Faster .cache output
*
* Revision 3.19  1993/07/26  15:32:26  lindner
* mods for application/gopher-menu and faster .cache sending
*
* Revision 3.18  1993/07/23  03:24:22  lindner
* fix for ppoen and NeXTs with overwritten envp
* mucho mods for looking up filenames,
* enhanced item info for waissrc: and others.
* update for Text/plain & other MIME types.
*
* Revision 3.17  1993/07/20  23:53:51  lindner
* LOGGOpher changes, Argv mucking, and Version Number enhancements
*
* Revision 3.16  1993/07/13  03:57:29  lindner
* Fix for gopherls, improved mailfile handling, iteminfo on 3b2
*
* Revision 3.15  1993/07/10  04:22:36  lindner
* fix for gopherls
*
* Revision 3.14  1993/07/08  17:54:40  lindner
* fix for .src files that already end with .src
*
* Revision 3.13  1993/07/07  19:33:00  lindner
* Sockets.c update, fix for compressed binarys, exec: fixes
*
* Revision 3.12  1993/06/11  16:59:57  lindner
* gzip support, less lookups, etc.
*
* Revision 3.11  1993/04/15  22:20:08  lindner
* CAPFILES mods
*
* Revision 3.10  1993/04/15  04:48:07  lindner
* Debug code from Mitra
*
* Revision 3.9  1993/04/10  06:06:11  lindner
* Admit1 Fixes for combined public/authed server
*
* Revision 3.8  1993/04/07  05:54:39  lindner
* Fixed dreaded .mindex addition problem
*
* Revision 3.7  1993/03/26  19:47:50  lindner
* Hacks to support wais gateway and gplus indexing
*
* Revision 3.6  1993/03/25  21:36:30  lindner
* Mods for directory/recursive etal
*
* Revision 3.5  1993/03/24  22:08:59  lindner
* Removed unused variable
*
* Revision 3.4  1993/03/24  20:23:17  lindner
* Lots of bug fixes, compressed file support, linger fixes, etc.
*
* Revision 3.3  1993/03/01  02:22:40  lindner
* Mucho additions for admit1 stuff..
*
* Revision 3.2  1993/02/19  21:21:11  lindner
* Fixed problems with signals, problems with gethostbyaddr() and
* inconsisent behavior that depended on the order of files in a directory.
*
* Revision 3.1.1.1  1993/02/11  18:02:55  lindner
* Gopher+1.2beta release
*
*********************************************************************/


/* Originally derived from an
* Example of server using TCP protocol
* pp 284-5 Stevens UNIX Network Programming
*/

#include <stdio.h>
#if defined(VMS_SERVER) && !defined(GOPHERD_C)
#define GOPHERD_C 1
#endif
#include "gopherd.h"
#include "command.h"
#include "patchlevel.h"
#include "Malloc.h"
#include "Debug.h"
#include "fileio.h"

#ifdef _AUX_SOURCE
#  include <compat.h>
#endif

#ifdef VMS_SERVER
#include "serverutil.h"
#include <stsdef.h>
#include <stdarg.h>
#include <descrip.h>
#include <prvdef.h>
#include <syidef.h>
#include <jpidef.h>
#ifdef UCX
#include <ucx$inetdef.h>
#endif
#endif

#undef stat /** Stupid openers thing..**/

GopherDirObj *GDfromSelstr();
GopherDirObj *GDfromUFS();
void         item_info();

/******* Global variables *********/

static  char*   Gdefusername = NULL;
#if defined(VMS_SERVER) && defined(__VAXC)
 typedef unsigned int uid_t;
 typedef unsigned short gid_t;
#endif
static  uid_t   Guid;
static  gid_t   Ggid;
/**********************************/

extern char *getdesc();
extern double maxload;
int Process_Side();
char *GDESencrypt();

#include "STAarray.h"
#include "STRstring.h"
#include "Sockets.h"


#ifdef VMS_SERVER
#include <ssdef.h>
#include <opcdef.h>
#include <descrip.h>
#include <errno.h>
char *setable_argv[2];
extern int vaxc$errno_stv;
extern double sysload;
extern char log_alq[10];
extern char log_deq[10];

GopherStruct *VMS$VFLAGS(int sockfd, char *filename, int access, char Gtype);
void GDaddDateNsize(GopherDirObj *gd);
void GDpostprocVMS(GopherDirObj *gd, GDCobj *gdc, int port, boolean isGplus);
char VMS$EXASearch(ExtArray *cfgext, char *filename);
int  gopher_traceback();

union prvdef prvadr;
unsigned long int ON = -1;

static
   struct
       {
           int socket;
           char *tx;
        }  traceback = { -1, NULL};
static unsigned long condition;
static int      tracing_back = 0;

static
   struct _exit_block
       {
           unsigned long must_be_zero;
           unsigned long routine$;
           unsigned long number_of_args;
           unsigned long exit_condition$;
       }   gopher_traceback_blk =
      {0, (unsigned long) &gopher_traceback, 1, (unsigned long) &condition};
#endif

/* This function is called on a read timeout from the network */

#include <setjmp.h>
jmp_buf env;

SIGRETTYPE read_timeout(sig)
 int sig;
{
    longjmp(env,1);
}

#ifndef VMS_SERVER
void
gopherd_usage(progname)
 char *progname;
{
    fprintf(stderr, "Usage: %s [-mCDIc] [-u username] [-s securityfile] [-l logfile] [ -L loadavg ] <datadirectory> <port>\n", progname);
    fprintf(stderr, "   -C  turns caching off\n");
    fprintf(stderr, "   -D  enables copious debugging info\n");
    fprintf(stderr, "   -I  enable \"inetd\" mode\n");
    fprintf(stderr, "   -c  disable chroot(), use secure open routines instead\n");
    fprintf(stderr, "   -u  specifies the username for use with -c\n");
    fprintf(stderr, "   -o  override the default options file '%s'\n", CONF_FILE);
    fprintf(stderr, "   -l  specifies the name of a logfile\n");
    fprintf(stderr, "   -L  specifies maximum load to run at\n");
}
#endif

void
gopherd_exit(val)
 int val;
{
#ifdef VMS_SERVER
    if (RunFromInetd || DEBUG)
        ServerSetArgv("");             /* Don't leave this around */
#endif
    exit(val);
}

/*
* This is for when we dump core..  Make an attempt to clean up..
*/

SIGRETTYPE
sigabnormalexit()
{
    gopherd_exit(-1);
}

#ifndef VMS_SERVER
void
main(argc, argv, envp)
#else
main(argc, argv, envp)
#endif
 int   argc;
 char  *argv[];
 char  *envp[];
{
    int                childpid;
    int                sockfd, newsockfd;
    int                clilen;
    struct sockaddr_in cli_addr;
    boolean            OptionsRead = FALSE;
    int                i=0;
    char               tmpstr[256];

    /*** for getopt processing ***/
    int c;
#ifndef VMS_SERVER
    extern char *optarg;
    extern int optind;
    int errflag =0;
#else
    char *cp;
    char restart[80];
    char rstrt[80];
    int  finish = 0;
    char nodename[16];
    struct itmlst {
         unsigned short int length;
         unsigned short int code;
         char *bufadr;
         int *retlen;
    }    syi$itmlst[2] = {  { 15, SYI$_NODENAME, NULL, NULL},
                           { 0, 0, 0, 0 } },
         jpi$itmlst[2] = {  { 255, JPI$_IMAGNAME, NULL, NULL },
                           { 0, 0, 0, 0 } };

    VAXC$ESTABLISH (gopher_traceback);
    SYS$DCLEXH (&gopher_traceback_blk);
    if (getenv("GOPHERD_INETD_DEBUG")) {
       DEBUG = TRUE;
    }
#endif


#ifndef VMS_SERVER
    Gtxtlocale(LC_ALL, "");

    Argv = argv;

#if !(defined(NeXT) || defined(_AIX))
    /* NeXTs don't like their envp to be overwritten... */

    for (i=0; envp[i] != NULL; i++)
         ;
#endif

    if (i > 0)
         LastArgv = envp[i - 1] + strlen(envp[i - 1]);
    else
         LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);

    pname = argv[0];
#else
    Gtxtlocale("sys$disk:_", getenv("LC_MESSAGES"));
    Argv = setable_argv;

    Argv[0] = (char *)malloc(256);
    Argv[1] = NULL;
    LastArgv = Argv[0] + 256;

    jpi$itmlst[0].bufadr = pname = (char *)malloc(256);
    if (SS$_NORMAL!=SYS$GETJPIW(0,0,0,jpi$itmlst,0,0,0))
       strcpy(pname, argv[0]);
#endif

    err_init();        /* openlog() early - before we chroot() of course */

    RunServer = TRUE;  /** Run the server by default **/

    Config = GDCnew();  /** Set up the general configuration **/

#ifndef VMS_SERVER
    while ((c = getopt(argc, argv, "CDIcL:l:o:u:")) != -1)
         switch (c) {
         case 'D':
              DEBUG = TRUE;
              break;

         case 'I':
              RunFromInetd = TRUE;
              break;

         case 'C':
              GDCsetCaching(Config, FALSE);
              break;

         case 'c':
              dochroot = FALSE;
              break;

         case 'L':  /** Load average at which to restrict usage **/
              maxload = atof(optarg);
              break;

         case 'l':  /** logfile name **/
              if (*optarg == '/' || strcasecmp(optarg, "syslog") == 0)
                   GDCsetLogfile(Config, optarg);
              else {
                   getwd(tmpstr);
                   strcat(tmpstr, "/");
                   strcat(tmpstr, optarg);

                   GDCsetLogfile(Config, tmpstr);
              }
              break;

         case 'o': /** option file **/
              if (*optarg == '/')
                   GDCfromFile(Config, optarg);
              else {
                   getwd(tmpstr);
                   strcat(tmpstr, "/");
                   strcat(tmpstr, optarg);
                   GDCfromFile(Config, tmpstr);
              }
              OptionsRead = TRUE;
              break;
         case 'u':
              {
                   struct passwd *pw = getpwnam( optarg );
                   if ( !pw ) {
                        fprintf(stderr,
                             "Could not find user '%s' in passwd file\n",
                             optarg);
                        errflag++;
                   } else {
                        if (!(pw->pw_uid == getuid() || getuid() == 0))
                             printf("Need to be root to use -u\n"), gopherd_exit(-1);

                        Gdefusername = strdup(optarg);

                        Guid = pw->pw_uid;
                        Ggid = pw->pw_gid;

                   }
              }
              break;

         case '?':
         case 'h':
              errflag++;
              break;
         }

    if (errflag) {
         gopherd_usage(argv[0]);
         gopherd_exit(-1);
    }

    if (optind < argc) {
         Data_Dir = strdup(argv[optind]);
         optind++;
         Debug("main: Setting data to Data Directory is %s\n",Data_Dir);
    }
    if (Data_Dir == NULL)
         Data_Dir = strdup(DATA_DIRECTORY);

    Debug("main: Data Directory is %s\n",Data_Dir);

    if (optind < argc) {
         GopherPort = atoi(argv[optind]);
         optind++;
         Debug("main: Setting port to %d\n",GopherPort);
    }
    Debug("main: Port is %d\n",GopherPort);
#else


#if defined(MULTINET) || defined(WOLLONGONG)
   /*
    *  The fopen() in GDCfromFile() opens and closes SYS$INPUT
    */
    {
         int status;
         unsigned short chan;
         $DESCRIPTOR(desc, "SYS$INPUT");
        /*
         *  See if we're running under Inetd/MULTINET_SERVER
         *  and if so, get a channel to the socket.
         */
         if (strstr(getenv("SYS$INPUT"), "_INET")) {
              status = sys$assign(&desc, &chan, 0, 0, 0);
              if (!(status & 1))
                   gopherd_exit(status);
              sockfd = (int) chan;
              RunFromInetd = TRUE;
         }
    }
#endif /* MultiNet or Wollongong */

#if defined(UCX) && defined(UCX$C_AUXS)
        /*
         *  This socket call will try to get the client connection from
         *  the AUX Server.  If the socket call fails, assume we are
         *  running as a detached process.  Otherwise, we are running
         *  from the AUX server.
         */
         if ((sockfd = socket (UCX$C_AUXS, 0, 0)) != -1)
              RunFromInetd = TRUE;
#endif /* UCX v2+ */

    if (RunFromInetd) {
   /*
    *  Track down the configuration file.  Everything this server process
    *  needs to know is now passed through that file. -- F.Macrides
    */
         int newport = 0;
         char *ConfigFile = (char *) malloc(sizeof(char)*256);

        /*
         *  In multiserver environments, the port number is appended
         *  to a system logical for each server's configuration file.
         */
         if ((newport = SOCKgetPort(sockfd)) < 0)
              gopherd_exit(SS$_NOIOCHAN);
         sprintf(ConfigFile, "GOPHER_CONFIG%d", newport);
         if (getenv(ConfigFile) != NULL) {
              GDCfromFile(Config, getenv(ConfigFile));
         } else {
             /*
              *  Try the system logical without a numeric suffix.
              */
              strcpy(ConfigFile, "GOPHER_CONFIG");
              if (getenv(ConfigFile) != NULL) {
                   GDCfromFile(Config, getenv(ConfigFile));
              } else {
                  /*
                   *  Must be an explicit filespec in CONF.H
                   */
                   GDCfromFile(Config, CONF_FILE);
              }
         }
         free(ConfigFile);

        /*
         *  Make sure the configuration file didn't set these wrong.
         */
         GDCsetInetdActive(Config, TRUE);
         GDCsetPort(Config, newport);

         OptionsRead = TRUE;

        /*
         *  Turn off all privs except TMPMBX and NETMBX
         */
         VMS$DisableAllPrivs();
    }

/***    VMS command line options are cast to lowercase in C programs, unless
           each argument was double-quoted.  Furthermore, many of the options
           are pretty irrelevant for debugging purposes, and passing command
           line options to a VMS detatched process isn't particularly easy.

           So command line options will be used in VMS only for debugging,
           when not running under Inetd/MULTINET_SERVER, and they will be
           restricted to the configuration file and port number.  They'll
           all be positional:

           $ gopher_debug [[[debug] port] config]
***/
    if (argc>1 && !RunFromInetd) {
       if (argc>3)
           DEBUG = TRUE;
       GDCfromFile(Config, argv[--argc]);
       OptionsRead = TRUE;
       if (argc>1 && !RunFromInetd)
           GDCsetPort(Config, atoi(argv[--argc]));
    }
    dochroot = FALSE;
#endif

    /** Read the options in, if not overridden **/
    if (OptionsRead == FALSE)
         GDCfromFile(Config, CONF_FILE);

    /** Check to make sure the options specified are workable... ***/
    GDCintegrityCheck(Config);

#ifndef VMS_SERVER
    /*** Make sure we do a tzset before doing a chroot() ***/
    tzset();
#else
    /** Were the FTPPort, EXECPort, SRCHPort or OVERPort were unspecified?**/
    if (GDCgetFTPPort(Config) == -1)
       GDCsetFTPPort(Config,GDCgetPort(Config));
    if (GDCgetEXECPort(Config) == -1)
       GDCsetEXECPort(Config,GDCgetPort(Config));
    if (GDCgetSRCHPort(Config) == -1)
       GDCsetSRCHPort(Config,GDCgetPort(Config));
    if (GDCgetOVERPort(Config) == -1)
       GDCsetOVERPort(Config,GDCgetPort(Config));

    /** Get the lookaside file format                              **/
    if (cp = GDCgetLookAside(Config))
        if (*cp != '\0') {
           char lookaside[256];
           strcpy(lookaside, GDCgetLookAside(Config));
           strcat(lookaside, ".DIR");
           EXAprocessLine(Config->Extensions, EXT_IGNORE, lookaside, NULL);
        }

    /** Get the restart logical if any                             **/
    if (cp = GDCgetRestart(Config))
        if (cp && *cp != '\0')
           strcpy(restart, GDCgetRestart(Config));
        else
           restart[0] = '\0';

    /** Set the AbortOutput() message file info                    **/
    SetAbrtMsg(TooBusy,"2busy");
    SetAbrtMsg(BadHostData,"badhostdata");
    SetAbrtMsg(IOErr,"data_gone");
    SetAbrtMsg(NothingThere,"nothing");
    SetAbrtMsg(RangeErr,"range");
    SetAbrtMsg(BaddirMsg,"baddir");
    SetAbrtMsg(BummerMsg,"bummer");
    SetAbrtMsg(IndexErr,"index");
    SetAbrtMsg(NoSuchFile,"no_such_file");
    SetAbrtMsg(SyntaxErr,"syntax");
    SetAbrtMsg(BumClient,"client");

    /** Set up the configured language, if any                     **/
    i = 0;
    if (cp=GDCgetSupportDir(Config))
       i += strlen(cp);
    if (cp=GDCgetHiddenPrefix(Config))
       i += strlen(cp);
    LastArgv = (char *)malloc(i+1);
    if (cp = GDCgetSupportDir(Config))
       strcpy(LastArgv, cp);
    if (cp = GDCgetHiddenPrefix(Config))
       strcat(LastArgv, cp);
    Gtxtlocale(LastArgv, getenv("LC_MESSAGES"));
    free(LastArgv);
    LastArgv = Argv[0] + 256;

    /** If LogTag specified, replace host & pid tokens if any      **/
    syi$itmlst[0].bufadr = nodename;
    if (SS$_NORMAL!=SYS$GETSYIW(0,0,0,syi$itmlst,0,0,0))
        strcpy(nodename,"unknown");

    if (GDCgetLogTag(Config)) {
         char LogTag[60] = {"["};
         char *tk;
         char *cp = GDCgetHostname(Config);
         char *rest;
         char *all;
         int  i=1;

         all = rest = (char *)malloc(2+strlen(GDCgetLogTag(Config)));
         strcpy(rest, GDCgetLogTag(Config));

         if (DEBUG) {
           strcat(LogTag,"Dbg");
           i=strlen(LogTag);
         }
         while (strlen(rest)) {
             tk = rest;
             while (strlen(rest) && *rest!=' ')
               rest++;
             if (strlen(rest))
               *rest++ = '\0';
             else
               if (!strlen(tk))
                   break;
             if (strcasecmp(tk,"pid")==0)
               LogTag[i+=sprintf(LogTag+i,"%s%s%x",
                               strlen(LogTag)>1?"/":"",
                                   RunFromInetd?"i":"",getpid())] = '\0';
             else
             if (strcasecmp(tk,"node")==0)
               LogTag[i+=sprintf(LogTag+i,"%s%s",
                               strlen(LogTag)>1?"/":"",nodename)] = '\0';
             else
                 if (strcasecmp(tk,"host")==0)
                   LogTag[i+=sprintf(LogTag+i,"%s%s",
                               strlen(LogTag)>1?"/":"", cp?cp:"?")] = '\0';
             else
                LogTag[i+=sprintf(LogTag+i,"%s%s",
                               strlen(LogTag)>1?"/":"", tk)] = '\0';
         }
         if (strlen(LogTag)>1) {
             strcat(LogTag,"]");
             GDCsetLogTag(Config, LogTag);
         }
         free(all);
     }

    /** If an error file was specified, set it up                  **/
    if (cp=GDCgetErrorfile(Config))
       if (*cp != '\0') {
           char *t = (char *)malloc(256);
           char *s = (char *)malloc(256);

           if (RunFromInetd && t) {
               boolean matches=FALSE;
               int     status;
                                   /* use ";" to trigger exec mode logical */
               sprintf(t,"LNM$PROCESS_TABLE;SYS$ERROR=%s",cp);
               bzero((char *) &prvadr, sizeof(prvadr));
               prvadr.prv$v_sysprv = 1;
               if (SS$_NORMAL != (vaxc$errno =
                                       SYS$SETPRV (ON, &prvadr, 0, 0))) {
                 LOGGopher(-1,"Can't insure  SYSPRV for SYS$ERROR define, %s",
                                               STRerror(vaxc$errno));
               }
               if (status=putenv(t))
                   strcpy(s,STRerror());
               VMS$DisableAllPrivs();
               strcpy(t,getenv("SYS$ERROR"));
               if (strcasecmp(cp,t)==0)
                   matches=TRUE;
               else {
                   LOGGopher(-1,"(env)%s%s(cfg)%s %s",
                           t, matches?"==":"!=", cp, status?s:"");
                   Debug("(env)%s", t);
                   Debug(matches?"==(cfg)%s\n":"!=%s(cfg)\n", cp);
                   if(status)
                       Debug("%s\n",s);
               }
               free(t);
               free(s);
           }
           freopen(cp,"a",stderr);
       }
#endif


    if (!RunFromInetd) {
#ifndef VMS_SERVER
         char *cp;
         printf("Internet Gopher Server %s.%s patch %d\n", GOPHER_MAJOR_VERSION, GOPHER_MINOR_VERSION, PATCHLEVEL);
#else
         printf("VMSGopher-L Gopher Server version %s-%s\n", GOPHERD_VERSION,
                                   PATCH_LEVEL);
#endif
         printf("Copyright 1991,92,93 the Regents of the University of Minnesota\n");
         printf("See the file 'Copyright' for conditions of use\n");
         printf("Data directory is %s\n", Data_Dir);
         printf("Port is %d\n", GopherPort);

#ifndef VMS_SERVER
         /*** Check for weird inconsistencies, security warnings etal ***/
         if (getuid() == 0 && Gdefusername == NULL)
              printf("Warning! You should run the server with the -u option!\n");
         else if (Gdefusername != NULL)
              printf("Running as user '%s'\n", Gdefusername);

         if (dochroot == FALSE)
              printf("Not using chroot() - be careful\n");
#else
         /*  if a GOPHERD_IMG_NAME logical exists, override the name       */
         if (cp=getenv("GOPHERD_IMG_NAME"))
               strcpy(pname,cp);
         /*    If there's a ][ in the image name, we can compress it out   */
         if (cp=strstr(pname,"][")) {
           /*  If we have the facility at some point to search for all
               logical names in LNM$SYSTEM which wildcard-match the device
               specification in GDCgetDatadir(Config), we should do so, and
               then if any of them have an equivalence which matches the
               string *pname up to and including the first "]", we should
               replace that string up to and including the first "]" with
               the logical name which matched that string.  Thus, if our
               executable is anywhere within the legitimate data area for
               the server, we have a string referencing the executable file
               which is *servable* by the server.  Note that the user-written
               system service SYS$LOOKUP() written by Ferry Bolh�r is a
               good candidate for this functionality.  Otherwise one would
               need to do a spawn out to DCL doing a SHOW LOG/TAB=LNM$SYSTEM
               of GOPHER_ROOT* or whatever the device name in GDCgetDatadir()
               is, and interpret the resultant equivalence strings. */
         }
         while(cp=strstr(pname,"]["))
           strcpy(cp,cp+2);
         printf("Image is %s\n", pname);
         printf("PID is %x\n", getpid());
#endif

         cp = GDCgetLogfile(Config);

         if (cp && *cp != '\0')
              printf("Logging to File %s\n", GDCgetLogfile(Config));
#ifdef VMS_SERVER
         {
             if (cp=GDCgetLogTag(Config))
               printf("Log Tag is %s\n", cp);

             LOGGopher(-1, "================================");
             LOGGopher(-1, "Starting %x %s", getpid(), pname);
             LOGGopher(-1, "Hostalias %s on node %s", GDCgetHostname(Config),
                                                               nodename);
         }
#endif
    }
#ifdef VMS_SERVER
    else
    {
       /*  Be nice to put some kind of log entry, at least, for INETD connections ??? */
    }
#endif


    if (uchdir(Data_Dir)) {
#ifndef VMS_SERVER
         fprintf(stderr, "Cannot change to data directory!! %s \n",Data_Dir);
         gopherd_exit(-1);
#else
         LOGGopher(-99,"fatal: cannot change to data directory %s!!, %s",
                                   GDCgetDatadir(Config),STRerror(errno));
#endif
    }

#ifndef VMS_SERVER
    if (dochroot && getuid() != 0) {
         fprintf(stderr, "Gopherd uses the privileged call chroot().  Please become root.\n");
         gopherd_exit(-1);
    }
#endif

    fflush(stderr);
    fflush(stdout);

#ifdef _AUX_SOURCE
    (void) set42sig();
#endif

#ifndef VMS_SERVER
    if (DEBUG == FALSE && RunFromInetd==FALSE)
         daemon_start(TRUE);

    /** We ignore SIGUSR2, so the PID routines can "ping" us **/
    (void) signal(SIGUSR2, SIG_IGN);
    (void) signal(SIGINT, sigabnormalexit);
    (void) signal(SIGSEGV, sigabnormalexit);
#endif

    /*** Hmmm, does this look familiar? :-) ***/
    err_init();

    /** Ask the system what host we're running on **/
#ifndef VMS_SERVER
    Zehostname = SOCKgetDNSname(DOMAIN_NAME, GDCgetHostname(Config));
#else
    GDCsetHostname(Config, SOCKgetDNSname(DOMAIN_NAME,GDCgetHostname(Config)));
#endif
    Debug("I think your hostname is %s\n", Zehostname);

    if (RunFromInetd) {
         /** Ask the system which port we're running on **/
         int newport=0;
#ifndef VMS_SERVER
         if ((newport =SOCKgetPort(0)) !=0)
              GopherPort=newport;
         /*** Do the stuff for inetd ***/
         while(do_command(fileno(stdout))!=0); /* process the request */
#else
         if ((newport = SOCKgetPort(0)) !=0)
           if (newport != -1)
              GDCsetPort(Config, newport);
         ServerStarted = time(NULL);
         ServerSetArgv("waiting for connection @ %d", GopherPort);
         /*** Do the stuff for inetd ***/
         while(do_command(sockfd)!=0);         /* process the request */
         shutdown(sockfd, 2);
         closenet(sockfd);
#endif


         shutdown(fileno(stdout), 2);
         shutdown(fileno(stdin), 2);
         shutdown(fileno(stderr), 2);

         fclose(stdout);
         fclose(stdin);
         fclose(stderr);

#ifdef VMS_SERVER
         traceback.socket = -1;
         traceback.tx     = NULL;
         SYS$CANEXH (&gopher_traceback_blk);
#endif
         gopherd_exit(0);
    } else {
#ifndef VMS_SERVER
         LOGGopher(-1, "Starting gopher server (pid %d)", getpid());
#endif
    }

    /** Set our cmd string **/

    ServerStarted = time(NULL);
    ServerSetArgv("waiting for connection @ %d", GopherPort);

    /** Open a TCP socket (an internet stream socket **/
    sockfd = SOCKbind_to_port(GopherPort);
#if defined(VMS_SERVER) && defined(MULTINET)
    SOCKkeepalive(sockfd, TRUE);
#endif

    listen(sockfd, 5);

    for ( ; ; ) {
         /*
          * Wait for a connection from a client process.
          * This is an example of a concurrent server.
          */
#ifdef VMS_SERVER
    /** Test the RESTART logical  **/
         if (strlen(restart)) {
             char *r = getenv(restart);
             if (r)
               if (strlen(strcpy(rstrt,r)))
                   finish = 1;
         }
#endif

         clilen = sizeof(cli_addr);
         while (1) {
              newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr,
                                 &clilen);

              if (newsockfd >= 0)
                   break;
              else if (errno != EINTR)  /** Restart accept if we hit a
                                            SIGCHLD signal..**/
                   LOGGopher(newsockfd, "Client went away");
         }


         SOCKlinger(sockfd, FALSE);
         SOCKlinger(newsockfd, FALSE);

         ActiveSessions ++;
         if ((GDCgetMaxconns(Config) > 0 ) && ActiveSessions > GDCgetMaxconns(Config)) {
              writestring(newsockfd, "0Too Many Users at this time, try again later\txx\txx\t70\r\n");
              ActiveSessions--;
              close(newsockfd);

         } else {
#ifndef VMS_SERVER

              if ( (childpid = fork()) < 0) {
                   /** Problems forking..  **/
                   writestring(newsockfd, "3Problems forking!\tmoo\terror.host\t0\r\n.\r\n");
              }

              else if (childpid == 0) {        /* Child process */
                   close(sockfd);              /* close original socket */

                   (void)do_command(newsockfd);/* process the request */
                   gopherd_exit(0);
              }
              /** Parent **/
              /** clean up any zombie children **/
              sig_child();

              close(newsockfd);                /* parent process */
              Connections++;
#else

         while(do_command(newsockfd)!=0);{}    /* process the request */
         traceback.socket = -1;
         traceback.tx     = NULL;
         closenet(newsockfd);
         Connections++;
         ActiveSessions--;     /*  We don't have child processes like that */
#endif
         }
#ifdef VMS_SERVER
         if (finish) {
           ServerSetArgv("");
           LOGGopher(-1,"Server %x Stopped (%s = '%s')",getpid(),
                                   restart, rstrt);
           if (!RunFromInetd)
               printf("Server Shutdown, %s = '%s'", restart, rstrt);
           SYS$CANEXH (&gopher_traceback_blk);
           break;
         }
#endif
    }
}





/*
* Given a specific view, add the filename extension that
* we stripped off for the multiple views stuff
*/

char *
AddExtension(cmd, view)
 CMDobj *cmd;
 char *view;
{
    char *filename = CMDgetFile(cmd);
    char *newselstr;
    int  flen=0, newlen=0;
    char *newfile;

    if (filename == NULL)
         return(CMDgetSelstr(cmd));

    newfile = EXAfindFile(Config->Extensions, filename, view);
    if (newfile == NULL)
                return(CMDgetSelstr(cmd));

    flen = strlen(filename);
    newlen = strlen(newfile);

    if (newlen > flen) {
         /*** Add the found extensions... ***/
         newselstr = (char *)malloc(MAXPATHLEN);

         strcpy(newselstr, CMDgetSelstr(cmd));
         strcat(newselstr, newfile+flen);
         Debug("New long file is %s", newselstr);
         return(newselstr);
    }
    return(CMDgetSelstr(cmd));
}


int EXECflag;


void
SetScriptEnvironment(cmd, view)
 CMDobj *cmd;
 char *view;
{
    char tmpstr[256];
#ifdef VMS_SERVER
#define SetEnvironmentVariable(a,b)  VMS$SetEnv("LNM$JOB",a,b)
#endif

    SetEnvironmentVariable("GPLUS_CMD", CMDgetCommand(cmd));
    SetEnvironmentVariable("CONTENT_TYPE", view);
    SetEnvironmentVariable("GUSER", CMDgetUser(cmd));
    SetEnvironmentVariable("GTICKET", CMDgetTicket(cmd));

#ifndef VMS_SERVER
    sprintf(tmpstr, "UofMNgopherd/%s.%spl%d", GOPHER_MAJOR_VERSION,
            GOPHER_MINOR_VERSION, PATCHLEVEL);
#else
    sprintf(tmpstr, "UofMNgopherd/%s.%sVMS-%d", GOPHER_MAJOR_VERSION,
            GOPHER_MINOR_VERSION, PATCHLEVEL);
#endif

    SetEnvironmentVariable("SERVER_SOFTWARE", tmpstr);

    SetEnvironmentVariable("SERVER_NAME", Zehostname);

    sprintf(tmpstr, "%d", GopherPort);
    SetEnvironmentVariable("SERVER_PORT", tmpstr);
    SetEnvironmentVariable("SERVER_PROTOCOL", "gopher/1.0");
    SetEnvironmentVariable("GATEWAY_INTERFACE", "CGI/1.0");

    SetEnvironmentVariable("REQUEST_METHOD", "GET");
    SetEnvironmentVariable("PATH_INFO", CMDgetSelstr(cmd));
    SetEnvironmentVariable("PATH_TRANSLATED", CMDgetSelstr(cmd));

    SetEnvironmentVariable("SCRIPT_NAME", CMDgetFile(cmd));
    SetEnvironmentVariable("QUERY_STRING", CMDgetSearch(cmd));

    SetEnvironmentVariable("REMOTE_HOST", CurrentPeerName);
    SetEnvironmentVariable("REMOTE_ADDR", CurrentPeerIP);

    SetEnvironmentVariable("REMOTE_USER", CMDgetUser(cmd));

}


int
do_command(sockfd)
 int sockfd;
{
    char    logline[MAXLINE];
    char    *view     = NULL;
    char    *Selstr   = NULL;
#ifndef VMS_SERVER
    CMDobj  *cmd;
#endif
    char    *filter   = NULL;
    char    *cp;
    int     result;

    cmd = CMDnew();

    /*** Reopen the log file ***/
    cp = GDCgetLogfile(Config);

    if (cp != NULL && *cp != '\0') {
         if (strcasecmp(GDCgetLogfile(Config), "syslog")==0) {
#ifndef VMS_SERVER
             LOGFileDesc = -2;  /** log file is syslog **/
#else
             *((int *)(&LOGFileDesc)) = -2;    /* DEC compilers are picky */
#endif
         } else if ((int)LOGFileDesc < 0) {
#ifndef VMS_SERVER
              LOGFileDesc = uopen(GDCgetLogfile(Config),
                                  O_WRONLY | O_APPEND |O_CREAT, 0644);
#else
              LOGFileDesc = fopen_VMSopt(GDCgetLogfile(Config), "a");
#endif
         }
         if ((int)LOGFileDesc == -1) {
#ifndef VMS_SERVER
              printf("Can't open the logfile: %s\n", GDCgetLogfile(Config));
              gopherd_exit(-1);
#else
              VMS$fprintf(stderr, "Can't open the logfile: %s\n",
                                       GDCgetLogfile(Config));
              return(0);
#endif
         }
    }

#ifndef VMS_SERVER
    if(LoadTooHigh()) {
         Abortoutput(sockfd, "System is too busy right now. Please try again later.");
         gopherd_exit(-1);
    }
#endif


    (void) signal(SIGALRM,read_timeout);
#ifndef VMS_SERVER
    (void) alarm(READTIMEOUT);
#else
    (void) alarm(GDCgetReadTimeout(Config));
#endif

    if (setjmp(env)) {
         LOGGopher(sockfd,"readline: Timed out!");
#ifndef VMS_SERVER
         gopherd_exit(-1);
#else
        /** Disable the alarm signal **/
        (void) alarm(0);
        (void) signal(SIGALRM,SIG_IGN);
        return(0);
#endif
    }

    ServerSetArgv("Looking up address");

    /*** Find out who's knockin' ***/
    SOCKnetnames(sockfd, CurrentPeerName, CurrentPeerIP);

    ServerSetArgv("input from %s", CurrentPeerName);

    CMDfromNet(cmd, sockfd);
#ifdef VMS_SERVER
    if (CMDisErrored(cmd)) {
       /** Disable the alarm signal **/
       (void) alarm(0);
       (void) signal(SIGALRM,SIG_IGN);
       return(0);
     }
#endif

    if (CMDgetUser(cmd) != NULL)
         CurrentUser = strdup(CMDgetUser(cmd));

    GDCevalDir(Config, CMDgetFile(cmd));
    IsGplus = CMDisGplus(cmd);  /* need for error output.. */

#ifdef VMS_SERVER
    /** Don't service the client's request if the Load is too high **/
    if (LoadTooHigh()) {
       char *sel = CMDgetSelstr(cmd);
       boolean refuse = TRUE;

       if (sel) /* Users w/ 2busy msg should be allowed to see it */
           if (strncasecmp(TooBusy,sel,strlen(TooBusy))==0)
               refuse = FALSE;
       if (refuse) {
           LOGGopher(sockfd, "System Load (%g) Too High", sysload);
           SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
           Abortoutput(sockfd,
                   "System is too busy right now. Please try again later.");
           return(0);
       }
    }
#endif

#ifndef VMS_SERVER /* Hey, look, it's 1st priority after the other G+ features, OK? */
#ifndef NO_AUTHENTICATION

    if (CMDgetTicket(cmd) != NULL) {
         /** The user has already authenticated, extract from ticket **/
         char   *tix = CMDgetTicket(cmd);
         char   *user = CMDgetUser(cmd);
         char   *cleartext;

         GDCsetCaching(Config, FALSE);

         /** decode ticket here... ***/
         cleartext = (char*) GDESdecrypt(CMDgetUser(cmd), CurrentPeerIP,
                                 GDCgetPW(Config), tix);

         if (strcmp(cleartext, CMDgetUser(cmd)) == 0) {
              /*CMDremoveAsk(cmd);*/
              ; /** Do the trans... **/
              Setuid_username(user);

              Gticket = (char*) malloc(sizeof(char*) *
                                       (strlen(CMDgetUser(cmd)) +
                                        strlen(tix) + 4));
              sprintf(Gticket, "*%s %s ", CMDgetUser(cmd), tix);

         } else {
              GplusError(sockfd, 1, "Your ticket is invalid", NULL);
         }

    } else if (strncmp(CMDgetSelstr(cmd), "validate ", 9) == 0) {
         /** Take the information from the ASK block and validate the user,
             then, generate tickets **/
         char *selstr, *auth;

         GDCsetCaching(Config, FALSE);

         /** Only look at the ask block if it's there... **/
         if (*CMDgetCommand(cmd) != '!') {

              selstr = CMDgetSelstr(cmd) + 9;

              /** Doctor up the item so it looks real.. **/
              CMDsetSelstr(cmd, selstr);

              auth = GDCauthType(Config, CMDgetFile(cmd));

              switch (GDCvalidate(Config, auth, CMDgetAskline(cmd, 0),
                                  CMDgetAskline(cmd, 1), CurrentPeerName,
                                  CurrentPeerIP)) {
                   char *ask1, *ask0, *crypted;

              case AUTHRES_OK:

                   Gticket = (char*) malloc(sizeof(char*) *
                                            (strlen(CMDgetAskline(cmd,0)) +
                                             strlen(CMDgetAskline(cmd,1))+4));
                   ask0 = CMDgetAskline(cmd, 0);
                   ask1 = CMDgetAskline(cmd, 1);
                   crypted = GDESencrypt(ask0, CurrentPeerIP,
                                         GDCgetPW(Config), ask0);

                   sprintf(Gticket, "*%s %s ", ask0, crypted);


                   CMDremoveAsk(cmd);
                   ; /** Do the trans... **/
                   break;
              case AUTHRES_BADPW:
                   if (strlen(CMDgetAskline(cmd, 1)) == 0)
                        GplusError(sockfd, 1, "Please enter a valid password", NULL);
                   else
                        GplusError(sockfd, 1, "Your password is incorrect, try again", NULL);
                   break;
              case AUTHRES_SYSERR:
                   GplusError(sockfd, 1, "Authentication system failue, please contact administrator", NULL);
                   break;

              default:
                   GplusError(sockfd, 1, "Your username is incorrect, try again", NULL);
                   break;
              }
         }

    } else {
         /** Check to see if this item needs authentication, and send
             a redirect error message **/
         char *auth = GDCauthType(Config, CMDgetFile(cmd));
         if (auth != NULL) {
              /** Need to redirect **/
              Abortoutput(sockfd, "Sorry, no access to this item");
         }

    }
#endif
#endif

#ifdef VMS_SERVER
    /** Disable the alarm signal **/
    (void) alarm(0);
    (void) signal(SIGALRM,SIG_IGN);
#endif

    /** At this point there won't be any more data coming in, so shutdown
        the incoming data for the socket
     **/
    shutdown(sockfd, 0);

    /** Change our root directory **/
#ifndef VMS_SERVER
    if ( dochroot ) {
         if (chroot(Data_Dir))
              Abortoutput(sockfd, "Data_Dir dissappeared!"), gopherd_exit(-1);

         uchdir("/");  /* needed after chroot */
    }

    if (getuid() == 0) {
         setgid(Ggid);
         setuid(Guid);
    }
#else
    if (uchdir(Data_Dir)) {
         SetAbrtFile(IOErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
         Abortoutput(sockfd, "Data_Dir disappeared!");
         LOGGopher(-99,"fatal: cannot change to data directory %s!!, %s",
                                   GDCgetDatadir(Config),STRerror(errno));
    }
#endif


    if (CMDisAskitem(cmd)) {
         FILE *retrfile;

         /** Write the stuff out to a file, here, as gopherd user
             so we can remove file later on.. **/
         ASKfile = tempnam(NULL, "gdata");
         Debug("Ask data is in %s\n", ASKfile);
         retrfile = ufopen(ASKfile, "w",0777);
         if (retrfile != NULL) {
              int i;

              for (i=0; i < CMDnumAsklines(cmd); i++) {
                   fputs(CMDgetAskline(cmd, i), retrfile);
                   putc('\n', retrfile);
              }
              fclose(retrfile);
         } else {
#ifdef VMS_SERVER
              SetAbrtFile(IOErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
              Abortoutput(sockfd, "Cannot write ask data");
         }
    }
#ifdef VMS_SERVER
#ifdef FOTEOS
    if (strncmp(inputline, "GET ", 4) == 0) {

         UsingHTML = TRUE;
         NoDOT = TRUE;

         /** Check for Unix root directory designator **/
         if (*(inputline+4) == '/')
              selstr = inputline+5;
         else
              selstr = inputline+4;

         /** Convert the hex things back to text... ***/
         Fromhexstr(selstr, selstr);

         /** Check for http flag from a gopher server's tuple **/
         if (strncmp(selstr, "GET ", 4) == 0) {
              if (*(selstr+4) == '/')
                   selstr = selstr+5;
              else
                   selstr = selstr+4;
         }

         /** Trim off HTTP2 trailers, if present **/
         if ((cp=strstr(selstr, " HTTP/1.0")) != NULL) {
              *cp = '\0';

              /** Clear the Accept:, User-Agent: and From: **/
              /** fields from the receive buffer           **/
              HTTP2line = (char *) malloc(sizeof(char)*MAXLINE);
              do {
                   length = readline(sockfd, HTTP2line, MAXLINE);
                   ZapCRLF(HTTP2line);
              } while (length > 0 && strlen(HTTP2line) != 0);
              free(HTTP2line);
         }
    }
    else
         selstr = inputline;
#endif
#endif

    /** Extract the view if it exists **/
    if (CMDgetCommand(cmd) != NULL) {
         char *command = CMDgetCommand(cmd);
         int  cmdlen = strlen(command);

         if ((cmdlen > 1) && (*command == '+'))
              view = command+1;
         else if (*command == '!') {
              item_info(cmd, sockfd);
              if (*(command+1) != '\0')
                   filter = command + 1;
              return(0);
         }
         else if (*command == '$') {
              if (*(command+1) != '\0')
                   filter = command + 1;
              view = "application/gopher+-menu";
              CMDsetView(cmd, view);
              *command = '+';
         }
         else if (*command == '+') {
              ;
         }
         else
#ifndef VMS_SERVER
              Abortoutput(sockfd, "Malformed command"); /*** Error ***/
#else
           {
               SetAbrtFile(SyntaxErr, NULL /* AbortGS ??? */, KeepAbrtGS,
                                                                   NULL);
               Abortoutput(sockfd, "Malformed command");
           }
#endif
    }

    if (strncmp(CMDgetSelstr(cmd), "waisdocid:",10)==0)
         view = "Text/plain";

    /*** Root level null selector string.. ***/
    if (!view && strlen(CMDgetSelstr(cmd)) == 0)
         view = "application/gopher-menu";


    if (!view && *CMDgetSelstr(cmd) == 'h') {
         /** It might be a directory..., or an HTML file on disk **/
         struct stat    statbuf;

         if (!rstat(CMDgetFile(cmd), &statbuf)) {
              if (S_ISDIR(statbuf.st_mode)) {
                   /*** It's a directory capability ***/
                   *CMDgetSelstr(cmd) = '1';
              }
         }
         view = "text/html";
    }

    /*** Try to speed things up for gopher0 requests, avoid reading
         big directories.. ***/

    if (!view && CMDisGplus(cmd) == FALSE) {
         struct stat  statbuf;
         char         *cp = CMDgetSelstr(cmd);
         ;
         if (*cp == '0' ||
             *cp == 'R') {
              if (!rstat(CMDgetFile(cmd), &statbuf))
                   view = "text/plain";
         }
         else if (*cp == '1') {
              if (!rstat(CMDgetFile(cmd), &statbuf))
                   view = "application/gopher-menu";
         }
    }


#ifndef VMS_SERVER
    /*** Try to find a view if not supplied ***/
    if (view == NULL) {
         GopherDirObj *gd;
         int num,i;

         /** Get gopher directory containing item in question **/
         gd = GDfromSelstr(cmd, sockfd);

         if (gd != NULL) {
              num = GDSearch(gd, CMDgetSelstr(cmd));
              if (num >=0) {
                   GopherObj *gs;

                   gs= GDgetEntry(gd, num);

                   if (GSgplusInited(gs) == FALSE) {
                        view = "";
                   }

                   /**  If only one view, take it **/
                   else if (GSgetNumViews(gs) == 1)
                        view = VIgetViewnLang(GSgetView(gs, 0),(char*)malloc(128));
                   else {
                        /*** Hmmm, let's choose one.. ***/
                        for (i=0; i<GSgetNumViews(gs); i++) {
                             char *tmpview;

                             tmpview = VIgetType(GSgetView(gs,i));
                             if (GSgetType(gs) == '0') {
                                  if (strcasecmp(tmpview, "Text/plain")==0) {
                                       view = VIgetViewnLang(GSgetView(gs, i),(char*)malloc(128));
                                       break;
                                  }
                             }
                             if (GSgetType(gs) == 'I') {
                                  if (strcmp(tmpview, "image/gif")==0) {
                                       view = VIgetViewnLang(GSgetView(gs, i),(char*)malloc(128));
                                       break;
                                  }
                             }

                        }
                        if (view == NULL)
                             /** Give up, take the first view... **/
                             view = VIgetViewnLang(GSgetView(gs,0), (char*)malloc(128));
                   }
                   /** We should have a view by now **/
              }
              GDdestroy(gd);
         } else {
              ;/* Can't get a Gopher directory listing... */;
         }
    }
#else
   /*
       OpenVMS:  We need a faster way to determine potential alternate views on a selection
                   than loading up the entire directory, ackk!!!  Maybe a direct-access cache
                   file, or scan the ACLs or something?
   */
#endif

    CMDsetView(cmd, view);

    /*
     * Set the environment variables for the scripts
     * shell script writers everywhere..
     */

    SetScriptEnvironment(cmd, view);


    /** Decide whether to add extensions of not .. **/
    if (view != NULL )
         Selstr = AddExtension(cmd, view);
    else
         Selstr = CMDgetSelstr(cmd);



    ServerSetArgv("%s to %s", Selstr, CurrentPeerName);

    EXECflag = 0;

    /*  Ask items are shell scripts, not directories.
        Selector strings with '1/' should never be shell scripts */

    if (CMDisAskitem(cmd) && *Selstr == '1')
         *Selstr = '0';

    /*** With the funky new capability system we can just check the
         first letter(s), end decide what the object refers to. ***/


    Debug("Attempting to get Selstr %s\n", Selstr);

    switch (*Selstr) {
#ifdef VMS_SERVER
    case '/':
         if (strlen(Selstr) != 1)          /*  Some Gopher Filter or Robot?    */
              goto old_link;

   Null_Capability:
#endif


    case '\0':
    case '\t':

         /*** The null capability, so it's not a file, probably wants
              to talk to a directory server ***/

         /*** we'll just do a "list" of the root directory, with no user
              capability.  ***/


#ifndef VMS_SERVER
         listdir(sockfd, "/", CMDisGplus(cmd), view, filter);
#else
         listdir(sockfd, Data_Dir, CMDisGplus(cmd), view, filter);
#endif
         LOGGopher(sockfd, "Root Connection");
         break;

    case A_HTML:
    case A_FILE:
    case A_UNIXBIN:
    case A_SOUND:
    case A_IMAGE:
    case A_GIF:
#ifdef VMS_SERVER
    case A_MOVIE:
    case A_UUENCODE:
#endif
         /*** It's some kind of file ***/

         /*** Is it binary??  ***/

         if (view == NULL)  {
              Debugmsg("View is null\n");
              if (*Selstr != A_FILE)
                   send_binary(sockfd, Selstr+1, CMDisGplus(cmd));
              else
                   printfile(sockfd, Selstr+1, 0, -1, CMDisGplus(cmd));
         } else {
              Debug("Testing view %s\n",view);
              if (GSisText(NULL, view))
                   printfile(sockfd, Selstr+1, 0, -1, CMDisGplus(cmd));
              else
                   send_binary(sockfd, Selstr+1, CMDisGplus(cmd));
         }
         /*** Log it ***/
         LOGGopher(sockfd, "retrieved file %s", Selstr+1 );
         break;


    case A_DIRECTORY:
         /*** It's a directory capability ***/
#ifdef VMS_SERVER
         if (strlen(Selstr+1)==0)
           goto Null_Capability;
#endif
         listdir(sockfd, Selstr+1, CMDisGplus(cmd), view, filter);

         /** Log it **/
         LOGGopher(sockfd, "retrieved directory %s", Selstr+1);
         break;

    case A_INDEX:
         /*** It's an index capability ***/
#ifdef VMS_SERVER
         if (!GDCgetSRCHPort(Config))
           goto NoSRCHAccess;
#endif
         result = GDCCanSearch(Config, CurrentPeerName, CurrentPeerIP,
                               ActiveSessions);

         if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
         NoSRCHAccess:
              SetAbrtFileIf(BummerMsg, NULL /* AbortGS ??? */, KeepAbrtGS,
                                                                       NULL);
#endif
              Abortoutput(sockfd, GDCgetBummerMsg(Config));
              LOGGopher(sockfd, "Denied access for %s", Selstr+1);
              break;
         } else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
              SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
              Abortoutput(sockfd, "Sorry, too busy now...");
              break;
         }

         Do_IndexTrans(sockfd, Selstr+1, cmd, TRUE);

         break;

#ifndef VMS_SERVER
    case 'm':
         if (strncmp(Selstr, "mindex:", 7)==0) {
              /*** First test for multiple indexes ***/
              result = GDCCanSearch(Config, CurrentPeerName, CurrentPeerIP,
                               ActiveSessions);

              if (result == SITE_NOACCESS) {
                   Abortoutput(sockfd, GDCgetBummerMsg(Config));
                   LOGGopher(sockfd, "Denied access for %s", Selstr+1);
                   break;
              } else if (result == SITE_TOOBUSY) {
                   Abortoutput(sockfd, "Sorry, too busy now...");
                   break;
              }

              do_mindexd(sockfd, Selstr+7, CMDgetSearch(cmd), CMDisGplus(cmd),
                         view);
              break;
         }


         /*** This is an internal identifier ***/
         /*** The m paired with an Objtype of 1 makes a mail spool file
           into a directory.
           ***/

         result = GDCCanBrowse(Config, CurrentPeerName, CurrentPeerIP,
                               ActiveSessions);

         if (result == SITE_NOACCESS) {
              Abortoutput(sockfd,  GDCgetBummerMsg(Config));
              LOGGopher(sockfd,"Denied access for %s", Selstr+1);
              break;
         } else if (result == SITE_TOOBUSY) {
              Abortoutput(sockfd, "Sorry, too busy right now..");
              break;
         }

         process_mailfile(sockfd, Selstr + 1);
         writestring(sockfd, ".\r\n");

         /** Log it **/
         LOGGopher(sockfd, "retrieved maildir %s", Selstr+1 );

         break;
#endif

#ifndef VMS_SERVER
    case 'R':
#else
    case A_RANGE:
#endif
         /*** This is an internal identifier ***/
         /*** The R defines a range  ****/
         /*** The format is R<startbyte>-<endbyte>-<filename> **/
    {
         int startbyte, endbyte;
         char *cp, *oldcp;

         cp = strchr(Selstr+1, '-');

         if (cp == NULL) {
#ifdef VMS_SERVER
              SetAbrtFile(RangeErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
              Abortoutput(sockfd, "Range specifier error");
              break;
         }

         *cp = '\0';
         startbyte = atoi(Selstr+1);
         oldcp = cp+1;

         cp = strchr(oldcp, '-');

         if (cp == NULL) {
#ifdef VMS_SERVER
              SetAbrtFile(RangeErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
              Abortoutput(sockfd, "Range specifier error");
#ifndef VMS_SERVER
              gopherd_exit(-1);
#else
              break;
#endif
         }

         *cp = '\0';
         endbyte = atoi(oldcp);
         oldcp = cp + 1;

         Debug("Start: %d, ", startbyte);
         Debug("End: %d, ", endbyte);
         Debug("File: %s\n", oldcp);

         printfile(sockfd, oldcp, startbyte, endbyte, CMDisGplus(cmd));

         /*** Log it ***/
         LOGGopher(sockfd, "retrieved range %d - %d of file %s", startbyte, endbyte, oldcp);
         break;
    }

    case 'f':
#ifdef VMS_SERVER
         if (!GDCgetFTPPort(Config))
           goto NoFTPAccess;
#endif
         result = GDCCanFTP(Config,CurrentPeerName,CurrentPeerIP,ActiveSessions);
         if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
         NoFTPAccess:
              SetAbrtFileIf(BummerMsg, NULL /* AbortGS ??? */, KeepAbrtGS,
                                                                       NULL);
#endif
              Abortoutput(sockfd,  GDCgetBummerMsg(Config));
              LOGGopher(sockfd, "Denied access for %s", Selstr);
              break;
         } else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
              SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
              Abortoutput(sockfd, "Sorry, too busy now...");
              break;
         }

         if (strncmp(Selstr, "ftp:",4)==0){

              LOGGopher(sockfd, "retrieved %s", Selstr);

              GopherFTPgw(sockfd, Selstr+4, cmd);
              break;
         }
         break;


    case A_EXEC:
#ifndef VMS_SERVER
         result = GDCCanBrowse(Config, CurrentPeerName, CurrentPeerIP,
                               ActiveSessions);
#else
         if (!GDCgetEXECPort(Config))
           goto NoEXECAccess;
         result = GDCCanEXEC(Config, CurrentPeerName, CurrentPeerIP,
                               ActiveSessions);
#endif
         if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
         NoEXECAccess:
              SetAbrtFileIf(BummerMsg, NULL /* AbortGS ??? */, KeepAbrtGS,
                                                                       NULL);
#endif
              Abortoutput(sockfd,  GDCgetBummerMsg(Config));
              LOGGopher(sockfd,"Denied access for %s", Selstr+1);
              break;
         } else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
              SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
              Abortoutput(sockfd, "Sorry, too busy right now..");
              break;
         }

         if (strncmp(Selstr, "exec:", 5)==0) {
              /* args are between colons */
              char *args, *command;

              command = strrchr(Selstr + 5, ':');
              if (command == NULL)
                   break;

#ifdef VMS_SERVER
              {
                   char *c2;
               /*
                   command is pointing to the last colon; it could be part of
                   an OpenVMS device specification, or it could be the other
                   arg boundary (if the device was defaulted), so we search
                   backward for a third colon, between the first one and here.
               */
                   *command = '\0';
                   c2 = strrchr(Selstr + 5, ':');
                   *command = ':';
               /*
                   If we did find another colon, so if it's followed by a
                   validated path, that's the colon which terminates the
                   arguments, so point command there (otherwise, leave
                   command pointing to the last colon, because the device
                   was defaulted, and that *is* an arg boundary).
               */
                   if (c2 != NULL && VMS$Validate_Filespec(c2+1))
                        command = c2;
              }
#endif

              if (*(Selstr+4) == ':' && *(Selstr+5) == ':')
                   args = NULL;
              else
                   args = Selstr+5;

              *command = '\0';
              command++;

              EXECargs = args;
              EXECflag = 1;

              printfile(sockfd, command, 0, -1, CMDisGplus(cmd));
              LOGGopher(sockfd, "Executed %s %s", command,  (args == NULL)
                        ? " " : args);
         }
         break;

#ifndef VMS_SERVER
    case 'w':
#else
    case A_WAIS:
#endif
    {
         if (strncmp(Selstr, "waissrc:", 8) == 0) {
              char waisfname[512];  /*** Ick this is gross ***/

              result = GDCCanSearch(Config, CurrentPeerName, CurrentPeerIP,
                               ActiveSessions);

              if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
                   SetAbrtFile(BummerMsg, NULL /* AbortGS ??? */, KeepAbrtGS,
                                                                       NULL);
#endif
                   Abortoutput(sockfd, GDCgetBummerMsg(Config));
                   LOGGopher(sockfd, "Denied access for %s", Selstr+1);
                   break;
              } else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
                   SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS,
                                                                       NULL);
#endif
                   Abortoutput(sockfd, "Sorry, too busy now...");
                   break;
              }

              strcpy(waisfname, Selstr+8);
              if ((int)strlen(waisfname) <= 4 ||
                  strncmp(&waisfname[strlen(waisfname)-4],".src",4) )
                   strcat(waisfname, ".src");
              SearchRemoteWAIS(sockfd, waisfname, cmd, view);
              break;
         }
         else if (strncmp(Selstr, "waisdocid:", 10) == 0) {

              result = GDCCanSearch(Config, CurrentPeerName, CurrentPeerIP,
                               ActiveSessions);

              if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
                   SetAbrtFile(BummerMsg, NULL /* AbortGS ??? */, KeepAbrtGS,
                                                                       NULL);
#endif
                   Abortoutput(sockfd, GDCgetBummerMsg(Config));
                   LOGGopher(sockfd, "Denied access for %s", Selstr+1);
                   break;
              } else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
                   SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS,
                                                                       NULL);
#endif
                   Abortoutput(sockfd, "Sorry, too busy now...");
                   break;
              }

              Fetchdocid(sockfd, cmd);
              break;
         }
    }


    default:
         /*** Hmmm, must be an old link... Let's see if it exists ***/
#ifdef VMS_SERVER
    old_link:
#endif

         switch (isadir(Selstr)) {
         case -1:
              /* no such file */
#ifdef VMS
         no_such_file:
              if (vaxc$errno || vaxc$errno_stv)
                   LOGGopher(sockfd, "'%s' does not exist (%s%s%s)", Selstr,
                                   STRerror(errno),
                                   vaxc$errno_stv?"/":"",
                                       STRerror_stv());
               else
#endif
              sprintf(logline, "'%s' does not exist", Selstr);
#ifdef VMS_SERVER
              SetAbrtFile(NoSuchFile, NULL /* AbortGS ??? */, KeepAbrtGS,
                                                                   NULL);
#endif
              Abortoutput(sockfd, logline);
              break;

         case 0:
              /* it's a file */
              printfile(sockfd, Selstr, 0, -1, CMDisGplus(cmd));

              /* Log it... */
              LOGGopher(sockfd, "retrieved file %s", Selstr);

              break;

         case 1:
              /* it's a directory */
              listdir(sockfd, Selstr, CMDisGplus(cmd), view, filter);

              /* Log it */
              LOGGopher(sockfd, "retrieved directory %s", Selstr);

              break;
         }
    }

    /** Free data ***/
    CMDdestroy(cmd);

    return(0);
}


/*
* This function tries to find out what type of file a pathname is.
*/

void
Getfiletypes(newpath, filename, gs)
 char *newpath;
 char *filename;
 GopherObj *gs;
{
    int Zefilefd;
    char *cp;
    char Zebuf[100];
    char Selstr[512];


    switch (isadir(filename)) {
    case -1:
         GSsetType(gs,A_ERROR);
         return;

    case 1:
         GSsetType(gs,A_DIRECTORY);
         *Selstr = A_DIRECTORY;
         strcpy(Selstr +1, newpath);
#ifdef VMS_SERVER
         if (cp=strrchr(Selstr +1,'.'))
           *cp = '\0';
         if (cp=strrchr(Selstr +1,']'))
           *cp = '.';
         strcat(Selstr +1,"]");
#endif
         GSsetPath(gs, Selstr);
         GSsetTTL(gs, GDCgetCachetime(Config));
         return;

    default:
         /*** The default is a generic text file ***/
         GSsetType(gs, A_FILE);

         *Selstr = A_FILE;
         strcpy(Selstr + 1, newpath);

         /*** Test and see if the thing exists... and is readable ***/

         if ((Zefilefd = ropen(filename, O_RDONLY)) < 0) {
              GSsetType(gs, A_ERROR);
              return;
         }

         if (read(Zefilefd, Zebuf, sizeof(Zebuf)) <0) {
              GSsetType(gs, A_ERROR);
              return;
         }
         close(Zefilefd);

         /*** Check the first few bytes for sound data ***/

         cp = Zebuf;

         if (strncmp(cp, ".snd", 4)==0) {
              GSsetType(gs, A_SOUND);
              *Selstr = A_SOUND;
              strcpy(Selstr+1, newpath);
         }

#ifndef VMS_SERVER
         /*** Check and see if it's mailbox data ***/

         else if (is_multipartfile(Zebuf) != SPLIT_UNKNOWN) {
              GSsetType(gs, A_DIRECTORY);
              *Selstr = 'm';
              strcpy(Selstr+1, newpath);
              GSsetGplus(gs, FALSE);  /** Not yet.. **/
         }
#endif

         /*** Check for uuencoding data ***/

         else if (strncmp(cp,"begin",6) == 0)  {
#ifndef VMS_SERVER
              GSsetType(gs, '6');
              *Selstr = '6';
#else
              GSsetType(gs, A_UUENCODE);
              *Selstr = A_UUENCODE;
#endif
              strcpy(Selstr+1, newpath);
         }

         /*** Check for GIF magic code ***/

         else if (strncmp(cp, "GIF", 3) == 0) {
              GSsetType(gs, A_IMAGE);
              *Selstr = A_UNIXBIN;
              strcpy(Selstr + 1, newpath);
         }
#ifdef VMS_SERVER
        {
            char Gtype;

            if (Gtype = VMS$EXASearch(Config->Extensions, filename)) {
                GSsetType(gs, Gtype);
                strcpy(Selstr, newpath);
            }
         }
#endif

         GSsetPath(gs, Selstr);

    }
}

/*
* Add a default view if none exists..
*/

void
AddDefaultView(gs, size, dirname)
 GopherObj *gs;
 int size;
 char *dirname;
{
    char *lang = GDCgetLang(Config);
    STATSTR statbuf;

    switch (GSgetType(gs)) {
    case A_FILE:
         GSaddView(gs, "Text/plain", lang, size);
         break;
    case A_DIRECTORY:
         if (GDCgetCaching(Config) && dirname != NULL) {
              strcat(dirname, "/.cache");
              if (ustat(dirname, &statbuf) == 0)
                   size = statbuf.st_size;

              GSaddView(gs, "application/gopher-menu", lang, size);

              strcat(dirname, "+");
              if (ustat(dirname, &statbuf) == 0)
                   size = statbuf.st_size;

              GSaddView(gs, "application/gopher+-menu", lang, size);
              GSaddView(gs, "text/html", lang, size);
         } else {
              GSaddView(gs, "application/gopher-menu", lang, size);
              GSaddView(gs, "application/gopher+-menu", lang, size);
              GSaddView(gs, "text/html", lang, size);
         }

         break;
    case A_MACHEX:
         GSaddView(gs, "application/mac-binhex40", lang, size);
         break;
    case A_PCBIN:
         GSaddView(gs, "application/octet-stream", lang, size);
         break;
    case A_CSO:
         GSaddView(gs, "application/qi", lang, 0);
         break;
    case A_INDEX:
         GSaddView(gs, "application/gopher-menu", lang, size);
         GSaddView(gs, "application/gopher+-menu", lang, size);
         break;
    case A_TELNET:
         break;
    case A_SOUND:
         GSaddView(gs, "audio/basic", lang, size);
         break;
    case A_UNIXBIN:
         GSaddView(gs, "application/octet-stream", lang, size);
         break;
    case A_GIF:
         GSaddView(gs, "image/gif", lang, size);
         break;
    case A_HTML:
         GSaddView(gs, "text/html", lang, size);
         break;
    case A_TN3270:
         GSaddView(gs, "application/tn3270", lang, 0);
         break;
    case A_MIME:
         GSaddView(gs, "multipart/mixed", lang, size);
         break;
    case A_IMAGE:
         GSaddView(gs, "image", lang, size);
         break;
    case A_PDF:
         GSaddView(gs, "application/pdf", lang, size);
         break;
    }
}



void
GSaddDateNsize(gs, statbuf)
 GopherObj *gs;
 struct stat statbuf;
{
#ifndef VMS_SERVER
#ifdef ADD_DATE_AND_TIME
    int           fd, i;
    char         longname[256];
    char         *cdate, *ti, *fp, *stitle;

    switch (GSgetType(gs)) {
    case '1': /*** It's a directory ***/
    case '7': /*** It's an index ***/
    case 'f': /*** ftp link ***/
    case 'e': /*** exec link ***/
    case 'h': /*** www link ***/
    case 'w': /*** wais link ***/
    case 'm':
         break;
    default:
    {
         stitle = GSgetTitle(gs);
         if (strstr( stitle, "kb]") == 0) {
              /* Correct for multiple view items */

              cdate= ctime( &statbuf.st_mtime); /* last mod time */
              cdate[ 7]= 0; cdate[10]= 0; cdate[24]= 0;
              sprintf( longname, "%s  [%s%s%s, %ukb]", stitle,
                     cdate+8,cdate+4,cdate+22, (statbuf.st_size+1023) / 1024);
              GSsetTitle(gs,longname);
         }
    }
         break;
    }
#endif /* ADD_DATE_AND_TIME */
    ;
#else
/*
* Adds the "Date+Size" patch
*      (actually, calls VMS$FormatTokens() to edits symbolic tokens in the
*      Title to reflect information about the file specified in the Path).
*      Nops if there are no symbolic tokens in the Title, if the Title or
*      Path are NULL, or if the type code for the path is inappropriate for
*      the operation specified.  Nops also if statistical information about
*      the file specified can't be secured.
*/
   char        *fp;
   time_t      tstamp;

   if ((fp = GSgetTitle(gs))==NULL)
       return;
   if (strchr(fp,'%')==NULL)
       return;
   if ((fp = GSgetPath(gs))==NULL)
       return;
   if (GSisCreateDate(gs))
       tstamp = statbuf.st_ctime;
   else
       tstamp = statbuf.st_mtime?statbuf.st_mtime:statbuf.st_ctime;

   GSsetTitle(gs,VMS$FormatTokens(GSgetTitle(gs), fp+1, &statbuf.st_size,
                           &tstamp, GSgetPort(gs),GSgetHost(gs), GSgetType(gs),
                                       GSgetAdmin(gs),cmd));
#endif
}



/*
* Add a DL description if it's there ...
*/

void
GStitlefromDL(gs, filename)
 GopherObj *gs;
 char *filename;
{
#ifdef DL
    char               dlpath[2];    /*** for DL**/
    char               *dlout;

    /* Process a "dl" description if there is one! */

    dlpath[0] = '.';
    dlpath[1] = '\0';
    dlout = getdesc(NULL,dlpath,filename,0);

    Debug("dl: %s", dlpath);
    Debug(" %s", filename);
    Debug(" %s\n", dlout);

    if (dlout != NULL) {
         GSsetTitle(gs, dlout);
    }
#endif
    ;
}

#ifndef VMS_SERVER
void
GSfromCapfile(gs, filename)
 GopherObj *gs;
 char *filename;
{
#ifdef CAPFILES

    char capfile[MAXPATHLEN];
    FILE *SideFile;

    strcpy(capfile,".cap/");
    strcat(capfile, filename);

    if ((SideFile = rfopen(capfile, "r"))!=0) {
         Debug("cap file name: %s\n", capfile);
         (void) Process_Side(SideFile, gs);
         fclose (SideFile);
    }
#endif
    ;
}

/*
* Run scripts that generate blocks...
*/

GSrunScripts(gs, filename)
 GopherObj *gs;
 char      *filename;
{
    FileIO *fio;
    char tmpstr[256];
    char *bname, *sname;
    int i, blocks;

    blocks = GDCnumBlkScripts(Config);

    for (i=0; i < blocks; i++) {
         bname = GDCgetBlkName(Config, i);
         sname = GDCgetBlkScript(Config, i);

         if (GSfindBlock(gs, bname) == NULL) {
              *tmpstr = '\0';
              if (!dochroot)
#ifndef VMS_SERVER
                   strcpy(tmpstr, Data_Dir);
#else
                   strcpy(tmpstr, (char *)Data_Dir);
#endif

              strcat(tmpstr, sname);
              strcat(tmpstr, " ");
              strcat(tmpstr, filename);

              fio = FIOopenCmdline(tmpstr, "r");
              if (fio == NULL)
                   return;

              while (FIOreadlinezap(fio, tmpstr,256)) {
                   GSsetBlock(gs, bname, tmpstr, TRUE);
              }

              FIOclose(fio);
         }
    }
}
#endif


void
GSsetDefaults(gs)
 GopherObj *gs;
{
    GSinit(gs);
    GSsetHost(gs, Zehostname);
    GSsetPort(gs, GopherPort);
    GSsetGplus(gs, TRUE);
    GSsetType(gs, '\0');
}

/*
* Load up a gopher directory from the file system given a directory
*/

GopherDirObj *
GDfromUFS(pathname, sockfd, isGplus)
 char *pathname;
 int sockfd;
 boolean isGplus;
{
    DIR                *ZeDir;
    char               filename[256];
    static char        newpath[512];
    static GopherObj   *Gopherstow = NULL;
    static Extobj      *extstow = NULL;
    Extobj             *ext;
    GopherObj          *gs;
#ifndef VMS_SERVER
    struct dirent      *dp;
#else
    char               *dp;
    GDCobj             *Local = NULL;
#endif
    GopherDirObj       *gd;
    struct stat        statbuf;
    boolean            AddItem = TRUE;
    static char        Pathp[512];
    StrArray           *Linkfiles;
    int                i;


    Debug("GDfromUFS:%s\r\n",pathname);
    Debug("GDfromUFS:Config=%d\r\n",Config);

    /*** Initialize static memory... ****/
    if (Gopherstow == NULL) {
         Gopherstow = GSnew();
    }

    if (isGplus && GSgplusInited(Gopherstow) == FALSE)
         GSplusnew(Gopherstow);

    if (extstow == NULL)
         ext = extstow = EXnew();
    else
         ext = extstow;


    gs = Gopherstow;

    if (rchdir(pathname)<0) {
         return(NULL);
    }

    gd = GDnew(32);

#ifndef VMS_SERVER
    if (GDCgetCaching(Config) &&
        Cachetimedout(".cache+", GDCgetCachetime(Config), ".")==FALSE) {
         int cachefd;

         if ((cachefd = ropen(".cache+", O_RDONLY)) >=0) {
              GDplusfromNet(gd, cachefd, NULL);
              close(cachefd);
              return(gd);
         }
    }
#endif

    /* open "." since we just moved there - makes it work when not
       chroot()ing and using relative paths */
    if ((ZeDir = uopendir(".")) == NULL) {
         char tmpstr[256];

         sprintf(tmpstr, "Cannot access directory '%s'", pathname);
#ifdef VMS_SERVER
         SetAbrtFile(BaddirMsg, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
         Abortoutput(sockfd, tmpstr);
         GDdestroy(gd);                /* Note Unix had this memory leak */
         return(NULL);
    }

#ifndef VMS_SERVER
    Linkfiles = STAnew(10); if (Linkfiles==NULL) return(NULL);
#endif

    for (dp = readdir(ZeDir); dp != NULL; dp = readdir(ZeDir)) {

         strcpy(newpath, pathname);
#ifndef VMS_SERVER
         strcpy(filename, dp->d_name);
#else
         strcpy(filename, dp);
#endif

#ifndef VMS_SERVER
         if (newpath[strlen(newpath)-1] != '/')
              strcat(newpath, "/");
         strcat(newpath, dp->d_name);
#else
         strcat(newpath, dp);
#endif

         gs = Gopherstow;
         GSinit(gs);

#ifndef VMS_SERVER
         /*********************************************************/
         /*** Ignored file ***/
         if (strcmp(filename, ".")==0 || strcmp(filename, "..")==0 ||
             strncmp(filename, ".cache", 6) ==0 ||
             strcmp(filename, ".about.html") == 0 ||
             GDCignore(Config,filename))
              continue;

         /*********************************************************/
         /*** This is a link file, process it after other files ***/
         else if (filename[0] == '.' && isadir(filename)==0) {
              String *temp;

              temp = STRnew();
              STRset(temp, filename);

              STApush(Linkfiles, temp);
              STRdestroy(temp);
              continue;
         }

         /*********************************************************/
         /*** directory starting with a period ***/
         else if (filename[0] == '.') {
              continue;
         }
#else

         /************************************************************/
         /*** This is a link file, process it *before* other files ***/
         if (GDCisLink(Local?Local:Config,filename)
               && isadir(filename)==0
                   && strncasecmp(filename, ".cache", 6) !=0) {
           FileIO *fio;

           if (fio = FIOopenUFS(filename, O_RDONLY, 0)) {
               if (!Local)
                   Local = GDCcpy(Config);
               GDCfromLink(Local, fio, '=');
               GDfromLink(gd, fio, Zehostname, GopherPort, pathname,
                               CurrentPeerName, sockfd, ACC_BROWSE);
               FIOclose(fio);
               switch(GDCcanAccess(Local, CurrentPeerName, CurrentPeerIP,
                                               ActiveSessions, ACC_BROWSE)) {
               case SITE_NOACCESS:
               case SITE_TOOBUSY:
                       GDCdestroy(Local);
                       GDdestroy(gd);
                       return(NULL);
               case SITE_OK:
               case SITE_UNDEF:
               default:    ;
               }
           }
           continue;
         }
         /*********************************************************/
         /*** Ignored file ***/
         else    if (GDCisHidden(Local?Local:Config,filename)
                           || GDCgetIgnoreAll(Local?Local:Config)
                               || GDCignore(Local?Local:Config, filename)) {
           /* Ignore this file */
           continue;
         }
         /*    Possibly apply Access Restrictions here                 */
#endif

         /*********************************************************/
         /*** Gopher+ block file  ***/

#ifndef VMS_SERVER
         else if (GDCBlockExtension(Config, filename, ext))
#else
         else if (GDCBlockExtension(Local?Local:Config, filename, ext))
#endif
         {
              char Selstr[512];
              char *tmpstr = Selstr;
              int num;

              *Selstr = '0'; strcpy(Selstr+1, newpath);

              /** Strip off the extension from the path **/
              tmpstr[strlen(tmpstr) - strlen(EXgetExt(ext))]='\0';

              num = GDSearch(gd, tmpstr);
              if (num != -1) {
                   gs = GDgetEntry(gd, num);
                   AddItem = FALSE;
              } else {
                   GSsetDefaults(gs);
                   GSsetPath(gs, tmpstr);
              }

              if (strcasecmp(EXgetBlockname(ext), "ASK") == 0)
                   GSsetAsk(gs, TRUE);

              GSaddBlock(gs, EXgetBlockname(ext), fixfile(newpath));

         }

         /*********************************************************/
         /*** Some kind of data file                            ***/
         else {
              ustat(filename, &statbuf);

              /* Strip any Decoder extensions from newpath before processing
                 them, filename needs to remain as is for type checking*/

#ifndef VMS_SERVER
              if (EXAcasedSearch(Config->Extensions, ext, filename,
#else
              if (EXAcasedSearch(Local?Local->Extensions:
                                       Config->Extensions, ext, filename,
#endif
                                 EXT_DECODER)) {

                   newpath[strlen(newpath) - strlen(EXgetExt(ext))] = '\0';
                   filename[strlen(filename) - strlen(EXgetExt(ext))] = '\0';
              }

              /*** Add views to item... ***/

#ifndef VMS_SERVER
              if (GDCViewExtension(Config, filename, &ext))
#else
              if (GDCViewExtension(Local?Local:Config, filename, &ext))
#endif
              {
                   char *Prefix;
                   int  num;

                   Prefix = EXgetPrefix(ext);

                   strcpy(Pathp, Prefix);
                   strcpy(Pathp+strlen(Prefix), newpath);

                   /*** Strip extension off of pathname***/
                   Pathp[strlen(Prefix)+strlen(newpath)-strlen(EXgetExt(ext))]= '\0';

                   /*** Strip extension off of title***/
                   filename[strlen(filename)-strlen(EXgetExt(ext))]= '\0';

                   /*** search for existing entry to add view to **/
                   num = GDSearch(gd, Pathp);
                   if (num != -1) {
                        gs = GDgetEntry(gd, num);
                        AddItem = FALSE;
                   } else {
                        GSsetDefaults(gs);
                   }

#ifndef VMS_SERVER
                   GSsetTitle(gs, filename);
#else
           /*  leave title blank, fix it in GDpostprocVMS()    */
#endif
                   GSsetPath(gs, Pathp);
                   GSsetType(gs, EXgetObjtype(ext));


                   /** Oh say can we hack, by the dawns early day :-) **/
                   if (strcasecmp(EXgetExt(ext), ".mindex")==0) {
                        GSsetGplus(gs, FALSE);
                   }

                   if (isGplus) {
                        char *lang;

                        lang = EXgetVLang(ext);
                        if (lang == NULL || strcmp(lang, "")==0)
#ifndef VMS_SERVER
                             lang = GDCgetLang(Config);
#else
                             lang = GDCgetLang(Local?Local:Config);
#endif
                        GSaddView(gs, EXgetView(ext), lang, statbuf.st_size);
                   }
              }
              /** Mystery file without known extension ***/
              else {
                   char Selstr[512];
                   int num;

                   strcpy(Selstr+1, newpath);
                   *Selstr = '0';

                   num = GDSearch(gd, Selstr);
                   if (num != -1) {
                        gs = GDgetEntry(gd, num);
                        AddItem = FALSE;
                   } else {
                        GSsetDefaults(gs);
                   }

                   Getfiletypes(newpath ,filename, gs);
                   if (GSgetType(gs) == '3')
                        continue;

#ifndef VMS_SERVER
                   GSsetTitle(gs, filename);
#else
           /*  leave title blank, fix it in GDpostprocVMS()    */
#endif

                   if (strncmp(GSgetPath(gs), "validate ", 9) == 0) {
                        /** Ugly hack for now...  **/
#if defined(VMS_SERVER) && defined(__VAXC)
                        char **askb;
                        int i;
                        char *cp = GSgetPath(gs);

                        askb = GDCauthAsk(Local?Local:Config, cp+10);
#else
                        char **askb = GDCauthAsk(Config, GSgetPath(gs)+10);
                        int i;
#endif

                        GSsetAsk(gs, TRUE);

                        for (i=0; askb[i] != NULL; i++) {
                             GSsetBlock(gs, "ASK", askb[i], TRUE);
                        }
                   }

                   if (GSisGplus(gs) && isGplus)
                        AddDefaultView(gs, statbuf.st_size, NULL);
              }

#ifndef VMS_SERVER
              /** Check DL database for a good name**/
              GStitlefromDL(gs, filename);

              if (GSgetTitle(gs) == NULL)
                   GSsetTitle(gs, filename);

              GSaddDateNsize(gs, statbuf);

              GSfromCapfile(gs, dp->d_name);
#else
           /*  leave title blank, fix it in GDpostprocVMS()    */
#endif
              if (GSgetType(gs) == '3' || GSgetType(gs) == '-')
                   continue;
         }

         /*** Here we go, we have either a filled in block or data item ***/

         if (isGplus && AddItem) {
              char tmpstr[256];
              char timeval[16];
              struct tm *tmthing;
              char *cp;

              if (GSgplusInited(gs) == FALSE)
                   GSplusnew(gs);

              /*** Add admin, abstract entries, etal ***/
              if (!GSgetAdmin(gs)) {
                   sprintf(tmpstr, "%s <%s>",
#ifndef VMS_SERVER
                           GDCgetAdmin(Config), GDCgetAdminEmail(Config));
#else
                           GDCgetAdmin(Local?Local:Config),
                                   GDCgetAdminEmail(Local?Local:Config));
#endif
                   GSsetAdmin(gs, tmpstr);
              }

              if (GSgetModDate(gs) == NULL) {
                   /** Set mod date entry **/
                   tmthing = localtime(&(statbuf.st_mtime));
                   strftime(timeval,sizeof(timeval), "%Y%m%d%H%M%S", tmthing);
                   sprintf(tmpstr,"%s<%s>", asctime(tmthing),timeval);
                   cp = strchr(tmpstr, '\n');
                   if (cp != NULL)
                        *cp = ' ';
                   GSsetModDate(gs, tmpstr);
              }

         }


         /*** Add the entry to the directory ***/
         if (AddItem) {
              GDaddGS(gd, gs);
         } else
              AddItem = TRUE;

    }

#ifndef VMS_SERVER
    /*** We don't have links yet, so we can process the BlockScripts ***/
    if (isGplus) {
         char *cp;

         for (i=0; i < GDgetNumitems(gd); i++) {
              gs = GDgetEntry(gd, i);
              cp = strrchr(GSgetPath(gs), '/');
              if (cp != NULL)
                   GSrunScripts(gs, cp+1);
         }
    }

    /** Process .Link files **/

    for (i=0 ; i<STAgetTop(Linkfiles); i++) {
         FileIO *fio;

         fio = FIOopenUFS(STAgetText(Linkfiles,i), O_RDONLY, 0);

         if (fio != NULL) {
              GDfromLink(gd, fio, Zehostname, GopherPort, pathname, CurrentPeerName);
              FIOclose(fio);
         }
    }

    closedir(ZeDir);

    GDsort(gd);
    STAdestroy(Linkfiles);
#else
    if (GDgetNumitems(gd)) {
       GDpostprocVMS(gd,Local?Local:Config,GopherPort,isGplus);
       GDaddDateNsize(gd);
       if (GDCgetSortDir(Local?Local:Config))
           GDsort(gd);
    }
    if (Local)
       GDCdestroy(Local);
    Local = NULL;
#endif

    return(gd);

}



/* Misleading title - its actually loading a GD from the parent of cmd*/

GopherDirObj *
GDfromSelstr(cmd,sockfd)
 CMDobj *cmd;
 int sockfd;
{
    char *it = NULL;
    char *cp;
    GopherDirObj *gd;
    char directory[512];
#ifdef VMS_SERVER
    GopherObj *gs;
#endif

    it = CMDgetFile(cmd);

    if (it == NULL)
         return(NULL);
    else
         strcpy(directory, it);

#ifndef VMS_SERVER
    cp = strrchr(directory, '/');
    if (cp != NULL)
         *(cp+1) = '\0';
#else
    if (cp = strrchr(directory, ']')) {    /* device:[...]filename         */
       *(cp+1) = '\0';                     /*      discard "]filename"     */
       if (strcmp(directory,it)==0) {
               /* Was just directory to start, so back up 1 directory....  */
           if (cp = strrchr(directory,'.'))
               strcpy(cp,"]");
           else {
               if (cp=strrchr(directory,'['))
                   strcpy(cp,"[000000]");
               else
                   return(NULL);           /* Bad syntax...    */
           }
       }
    }
    else {                                 /*  device:filename             */
       if (cp = strrchr(directory,':'))
           strcpy(cp+1,"[000000]");        /* Just device:                 */
       else                                /*  No [...] *or* device        */
           strcpy(directory,GDCgetDatadir(Config));
    }
    if (gs=VMS$VFLAGS(sockfd, directory, ACC_BROWSE, A_DIRECTORY))
       GSdestroy(gs);
    else
       goto no_access;
#endif

    if (rchdir(directory)<0) {
         char tmpstr[512];
         if (isadir(directory)==0)
                 return(NULL);
#ifdef VMS_SERVER
     no_access:
         SetAbrtFile(BaddirMsg, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
         sprintf(tmpstr, "- Cannot access directory '%s'", directory);
         Abortoutput(sockfd, tmpstr);
         return(NULL);
    }

    gd = GDfromUFS(directory, sockfd, TRUE); /** Returns NULL if error **/

    return(gd);
}



/*
* Send item information to client
*/
void
item_info(cmd, sockfd)
 CMDobj *cmd;
 int sockfd;
{
    GopherDirObj *gd = NULL;
    GopherObj *gs = NULL;
    int num;
    struct stat statbuf;
    char tmpstr[256];
    char *cp;

    cp = CMDgetSelstr(cmd);
    num = strlen(cp);


    /* If the selstr starts with ftp: hand it off to the gateway code */
    if (strncmp(cp, "ftp:", 4) == 0) {
         GopherFTPgw(sockfd, cp+4, cmd);
         return;
    }

    /* If the selstr ends in '/', strip it off. */
    if ( num != 0 && *(cp + num - 1) == '/' )
         *(cp + num - 1) = '\0';

    /** For now, strip off first character and find the directory above **/

    Debug("Item info for %s\n", cp);

    if ((*cp == '/') || (*cp == '\0') || (*(cp+1) == '\0')) {
         gs = GSnew();
         GSsetDefaults(gs);
         GSsetPath(gs, "");
         GSsetTitle(gs, GDCgetSite(Config));
         GSsetType(gs, '1');
         GSplusnew(gs);

         GSsendHeader(sockfd, -1);
         writestring(sockfd, "+INFO ");
         GStoNet(gs,sockfd, GSFORM_G0, Gticket);

         sprintf(tmpstr, "+ADMIN:\r\n Admin: %s <%s>\r\n", GDCgetAdmin(Config),
                 GDCgetAdminEmail(Config));
         writestring(sockfd, tmpstr);


         if (GSgetModDate(gs) == NULL) {
              if (rstat("/", &statbuf) == 0) {
                   char timeval[16];
                   struct tm *tmthing;

                   tmthing = localtime(&(statbuf.st_mtime));
                   strftime(timeval,sizeof(timeval), "%Y%m%d%H%M%S", tmthing);
                   sprintf(tmpstr," Mod-Date: %s<%s>\r\n",
                           asctime(tmthing),timeval);
                   cp = strchr(tmpstr, '\n');
                   if (cp != NULL)
                        *cp = ' ';
                   writestring(sockfd, tmpstr);
              }
         } else {
              sprintf(tmpstr, " Mod-Date: %s\r\n", GSgetModDate(gs));
              writestring(sockfd, tmpstr);
         }

         if (GSgetTTL(gs) > -1) {
              sprintf(tmpstr, " TTL: %d\r\n", GSgetTTL(gs));
         } else {
              sprintf(tmpstr, " TTL: %d\r\n", GDCgetCachetime(Config));
         }
         writestring(sockfd, tmpstr);

         sprintf(tmpstr, " Site: %s\r\n", GDCgetSite(Config));
         writestring(sockfd, tmpstr);
         sprintf(tmpstr, " Org: %s\r\n", GDCgetOrg(Config));
         writestring(sockfd, tmpstr);
         sprintf(tmpstr, " Loc: %s\r\n", GDCgetLoc(Config));
         writestring(sockfd, tmpstr);
         sprintf(tmpstr, " Geog: %s\r\n", GDCgetGeog(Config));
         writestring(sockfd, tmpstr);
#ifndef VMS_SERVER
         sprintf(tmpstr, " Version: U of Minnesota Unix %s.%s pl%d\r\n",
#else
         sprintf(tmpstr,
               " Version: U of Minnesota / VMSGopher-L OpenVMS %s.%s-%d\r\n",
#endif
                 GOPHER_MAJOR_VERSION, GOPHER_MINOR_VERSION, PATCHLEVEL);
         writestring(sockfd, tmpstr);

         if (Connections != 0) {
              time_t howlong  = time(NULL) - ServerStarted + 1;
              int connperhour = (Connections * 3600) / howlong;
              char *started = ctime(&ServerStarted);

              *(started+24) = '\0';
              sprintf(tmpstr, "+STATISTICS:\r\n Total Connections: %d\r\n Server Started: %s\r\n Connections per Hour: %d\r\n Concurrent Sessions: %d\r\n",
                      Connections, started, connperhour, ActiveSessions);
              writestring(sockfd, tmpstr);

         }


         if (GDCgetAbstract(Config) != NULL) {
              char abline[256];
              char *cp, *nl;

              writestring(sockfd, "+ABSTRACT:\r\n");
              nl = cp = GDCgetAbstract(Config);
              while (nl) {
                   nl = strchr(cp, '\n');
                   writestring (sockfd, " ");
                   if (nl == NULL)
                        writestring(sockfd, cp);
                   else {
                        strncpy(abline, cp, (nl-cp));
                        abline[nl-cp] = '\0';
                        writestring(sockfd, abline);
                        cp = nl+1;
                   }
                   writestring(sockfd, "\r\n");
              }

         }

         writestring(sockfd, "+VERONICA:\r\n treewalk:");
         if (GDCgetShouldIndex(Config) == TRUE)
              writestring(sockfd, " yes");
         else
              writestring(sockfd, " no");

         writestring(sockfd, "\r\n+VIEWS:\r\n");
#ifndef VMS_SERVER
         if (GDCgetCaching(Config)) {
              if (rstat("/.cache", &statbuf) == 0) {
                   sprintf(tmpstr, " application/gopher-menu %s: <%dk>\r\n",
                           GDCgetLang(Config), (statbuf.st_size + 512)/1024);
                   writestring(sockfd, tmpstr);
              } else {
                   writestring(sockfd, " application/gopher-menu: <0k>\r\n");
              }

              if (rstat("/.cache+", &statbuf) == 0) {
                   sprintf(tmpstr, " application/gopher+-menu %s: <%dk>\r\n",
                           GDCgetLang(Config), (statbuf.st_size + 512)/1024);
                   writestring(sockfd, tmpstr);
              } else {
                   writestring(sockfd, " application/gopher+-menu: <0k>\r\n");
              }

         } else {
              writestring(sockfd, " application/gopher-menu: <0k>\r\n");
              writestring(sockfd, " application/gopher+-menu: <0k>\r\n");
         }
#else
         writestring(sockfd, " application/gopher-menu: <0k>\r\n");
         writestring(sockfd, " application/gopher+-menu: <0k>\r\n");
#endif

         writestring(sockfd, " text/html: <0k>\r\n");
#ifndef VMS_SERVER
         writestring(sockfd, " Directory/recursive: <0k>\r\n");
         writestring(sockfd, " Directory+/recursive: <0k>\r\n");
#endif

         GSdestroy(gs);
    }
#ifndef NO_AUTHENTICATION
    else if (strncmp(CMDgetSelstr(cmd), "validate ",9) == 0) {

         gd = GDfromSelstr(cmd,sockfd);

         num = GDSearch(gd, CMDgetSelstr(cmd)+9);

         uchdir("/");

         if (num < 0 || gd == NULL) {
              GplusError(sockfd, 1, "Cannot find item information for that item", NULL);
              return;
         }
         else {
              /* Find the authenticator associated with the directory/item **/
              char *selstr = CMDgetSelstr(cmd);
              char **askb = GDCauthAsk(Config, CMDgetFile(cmd));
              int i;

              gs = GDgetEntry(gd, num);
              GSsetPath(gs, selstr);
              Debug("Askblock is %s\n", askb[0]);
              GSsetAsk(gs, TRUE);

              for (i=0; askb[i] != NULL; i++) {
                   GSsetBlock(gs, "ASK", askb[i], TRUE);
              }

              DebugGSplusPrint(gs,"got here...");


              GSsendHeader(sockfd, -1);
              GSplustoNet(gs, sockfd, NULL, "");
         }

    }
#endif /* NO_AUTHENTICATION */

    else {

         gd = GDfromSelstr(cmd,sockfd);

         num = GDSearch(gd, CMDgetSelstr(cmd));
         uchdir("/");

         if (num < 0 || gd == NULL) {
              GplusError(sockfd, 1, "Cannot find item information for that item", NULL);
              return;
         }
         else {
              gs = GDgetEntry(gd, num);
              GSsendHeader(sockfd, -1);
              GSplustoNet(gs, sockfd, NULL, Gticket);
         }

    }
    if (gd)
         GDdestroy(gd);

    writestring(sockfd, ".\r\n");
}

/*
* Output HTML for cutesy icons
*/

GSicontoNet(gs, sockfd)
 GopherObj *gs;
 int       sockfd;
{
    char iconfile[64];
    int fd;

    if (gs==NULL)
         return;

    sprintf(iconfile, "/lib/htmlicon.%c", GSgetType(gs));


    if ((fd = ropen(iconfile, O_RDONLY)) >= 0) {
         char imgline[128];
         sprintf(imgline, "<IMG ALIGN=absbottom BORDER=0 SRC=gopher://%s:%d/I9%s>",
                 Zehostname, GopherPort, iconfile);
         writestring(sockfd, imgline);
         close(fd);
    }
}
/*
** This function lists out what is in a particular directory.
** it also outputs the contents of link files.
**
** It also checks for the existance of a .cache file if caching is
** turned on...
**
** Ack is this ugly.
*/

void
listdir(sockfd, pathname, isgplus, view, filter)
 int sockfd;
 char *pathname;
 boolean isgplus;
 char *view;
 char *filter;
{
    GopherDirObj *gd = NULL;
    boolean      attrlist = FALSE;
    boolean      HTMLit   = FALSE;
    boolean      Recurse  = FALSE;
    char         *filtereddata[16], **filtered=filtereddata;
    int          i=0;
#ifndef VMS_SERVER
    StrArray     *RecurseDirs = NULL;
    String       *stapath;
#endif
    int          result;
#ifdef VMS_SERVER
    GopherStruct   *Vgs = NULL;
    char         *Vpath;
    char         *cp;
    char         *c2;

    Vpath = (char *)malloc((strlen(pathname)*2)+strlen("[000000.dir]")+2);
    *Vpath = A_DIRECTORY;
    strcpy (Vpath+1, pathname);
    if (cp = strrchr(Vpath, ']')) {
       if (strlen(cp)>1)
           goto bad_dir;
       *cp = '\0';
       if (cp = strrchr(Vpath, '.')) {         /* we had [path.path]       */
           *cp = ']';
           strcat(Vpath, ".dir");              /* change to [path]path.dir */
       }
       else
           if (cp = strrchr(Vpath, '[')) {     /* we had [path]            */
               c2=strcpy(Vpath+strlen(Vpath)+5, cp+1);
               strcpy(cp,"[000000]");
               strcat(Vpath, c2);
               strcat(Vpath, ".dir");          /* chng to [000000]path.dir */
           }
    }
    else {
  bad_dir:
       LOGGopher(sockfd, "Invalid Directory Spec %s", pathname);
       SetAbrtFile(SyntaxErr, NULL, KeepAbrtGS, "Invalid Filespec");
       Abortoutput(sockfd, GDCgetBummerMsg(Config));
       free(Vpath);
       return;
    }
    Vgs = VMS$VFLAGS(sockfd, Vpath+1, ACC_BROWSE, A_DIRECTORY);
    if (Vgs) {
       strcpy(Vpath+1, pathname);
       GSsetPath(Vgs, Vpath);
       free(Vpath);
    }
    else {
       free(Vpath);
       goto no_access;
    }
#endif

    result = GDCCanBrowse(Config, CurrentPeerName, CurrentPeerIP,
                          ActiveSessions);

    if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
    no_access:
         SetAbrtFile(BummerMsg, Vgs, DstrAbrtGS, AbortString);
#endif
         Abortoutput(sockfd,  GDCgetBummerMsg(Config));
         return;
    } else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
         SetAbrtFile(TooBusy, Vgs, DstrAbrtGS, NULL);
#endif
         Abortoutput(sockfd, "Sorry, too busy right now..");
         return;
    }

#ifndef VMS_SERVER
    if (uchdir("/"))
         perror("SOL dude");
#endif

    if (filter != NULL) {
         while (*filter != '\0') {
              if (*filter=='+') {
                   *filter = '\0';
                   filtered[i] = filter+1;
                   filter++; i++;
              }
              filter++;
         }
         filtered[i] = NULL;
    } else
         filtered = NULL;

    if (view != NULL) {
         if (strncmp(view, "application/gopher+-menu",24) == 0)
              attrlist = TRUE;
         else if (strncmp(view, "text/html", 9) == 0) {
              attrlist = TRUE;
              HTMLit   = TRUE;
         }
#ifndef VMS_SERVER
         else if (strncmp(view, "Directory+/recursive", 20)==0)
              Recurse = TRUE;
         else if (strncmp(view, "Directory/recursive", 19)==0)
              Recurse = TRUE;
#endif
    }
#ifndef VMS_SERVER
    if (Recurse)
         RecurseDirs = STAnew(32);
#endif

    if (rchdir(pathname)<0) {
         char tmpstr[512];

#ifdef UMNDES
         if (errno == EACCES)
              PleaseAuthenticate(sockfd);
#endif

         sprintf(tmpstr, "- Cannot access directory '%s'", pathname);
#ifdef VMS_SERVER
         SetAbrtFile(BaddirMsg, Vgs, DstrAbrtGS, NULL);
#endif
         Abortoutput(sockfd, tmpstr);
         return;
    }

    if (isgplus)
         GSsendHeader(sockfd, -1);

    do {
         Debug("Sending %s\n", pathname);

#ifndef VMS_SERVER
         if (Recurse) {
              rchdir("/");
              rchdir(pathname);
         }
#endif

#ifndef VMS_SERVER
         if (!attrlist && GDCgetCaching(Config) && !HTMLit &&
            (Cachetimedout(".cache", GDCgetCachetime(Config), ".") ==FALSE)) {
              /*** Old style cache  ***/
              send_binary(sockfd, ".cache", FALSE);

         } else if (GDCgetCaching(Config) && !HTMLit &&
                    Cachetimedout(".cache+", GDCgetCachetime(Config)
                                  , ".")==FALSE) {
              /*** Gopher+ cache. ***/

              if (strncmp(view, "application/gopher+-menu",24)==0)
                   send_binary(sockfd, ".cache+", FALSE);
              else if (strncmp(view, "application/gopher-menu",23)==0)
                   send_binary(sockfd, ".cache", FALSE);

         }
         else
#endif
         {
              /** If we didn't cache then we have to
                load up the directory **/
              gd = GDfromUFS(pathname, sockfd, attrlist);

              if (gd == NULL) {
                   /** Should generate an error message here **/
                   break;
              }

#ifndef VMS_SERVER
              rchdir("/");
#else
              rchdir(pathname);
#endif

              if (HTMLit) {
                   writestring(sockfd, "<HTML>\r\n<HEAD>\r\n");
                   writestring(sockfd, "<LINK rev=made href=\"mailto:");
                   writestring(sockfd, GDCgetAdminEmail(Config));
                   writestring(sockfd, "\">\r\n");

                   if (GDgetTitle(gd) != NULL) {
                        writestring(sockfd, "<TITLE>");
                        writestring(sockfd, GDgetTitle(gd));
                        writestring(sockfd, "</TITLE>");
                   }
                   writestring(sockfd, "</HEAD>\r\n<BODY>\r\n");

              {
                   STATSTR   statbuf;
                   char      html_path[1024];
                   char      *cp;

                   strcpy    (html_path, pathname);
                   cp = html_path + strlen(html_path) - 1;
                   if (*cp != '/') {
                        *(++cp) = '/';
                        *(++cp) = '\0';
                   }
                   strcat    (html_path, ".about.html");

                   if (rstat(html_path, &statbuf) == 0)
                        send_binary(sockfd, html_path, FALSE);
                   else if (strcmp(pathname, "/") == 0) {
                        writestring(sockfd, "<H1>");
                        writestring(sockfd, GDCgetSite(Config));
                        writestring(sockfd, "</H1>\r\n<H2>");
                        writestring(sockfd, GDCgetOrg(Config));
                        writestring(sockfd, "</H2>\r\n");
                   }
              }
                   GDtoNet(gd, sockfd, GSFORM_HTML, Gticket, &GSicontoNet);
                   writestring(sockfd, "</BODY>\r\n</HTML>\r\n");
              }
              else if (attrlist)
                   GDplustoNet(gd, sockfd,filtered, Gticket);
              else
                   GDtoNet(gd, sockfd, GSFORM_G0, Gticket, NULL);

         }
#ifndef VMS_SERVER
         /*
          * Write out the cache... After we send out the data to the net.
          */
         if (GDCgetCaching(Config) && gd != NULL) {
              int cachefd;
              char cachefile[MAXPATHLEN];

              strcpy(cachefile, pathname);
              strcat(cachefile, "/.cache");

              cachefd = ropen(cachefile, O_WRONLY|O_CREAT|O_TRUNC, 0644);

              if (cachefd >= 0) {
                   Debug("Caching directory... into %s\n",cachefile);
                   GDtoNet(gd, cachefd, GSFORM_G0, NULL, NULL);
                   close(cachefd);
              }

              if (attrlist) {
                   strcat(cachefile, "+");

                   cachefd = ropen(cachefile, O_WRONLY|O_CREAT|O_TRUNC,0644);

                   if (cachefd >= 0) {
                        GDplustoNet(gd, cachefd,filtered, "");
                        close(cachefd);
                   }
              }
         }
#endif

#ifndef VMS_SERVER
         if (Recurse) {
              GopherObj *gs;
              String    *pushstring = STRnew();

              /** Push entries on the stack **/
              for (i=0; i< GDgetNumitems(gd); i++) {
                   STATSTR stbuf;
                   char    *cp;

                   gs = GDgetEntry(gd, i);

                   if ((GSgetType(gs) == A_DIRECTORY) &&
                       (strcmp(GSgetHost(gs), Zehostname) == 0)) {
                        STRset(pushstring,  GSgetPath(gs));

                        rchdir("/");

                        cp = STRget(pushstring);
                        if (rstat(cp+1, &stbuf) == 0)
                             STApush(RecurseDirs, pushstring);
                   }
              }

              do {
                   stapath = STApop(RecurseDirs);
                   if (stapath == NULL) {
                        Recurse = FALSE;  /** Done **/
                        break;
                   }
                   pathname = STRget(stapath);

#ifndef VMS_SERVER
                   if (*pathname == 'm')
                        process_mailfile(sockfd, pathname+1);
#endif
              } while (*pathname == 'm');

              pathname++;

              if (gd != NULL) {
                   GDdestroy(gd);
                   gd = NULL;
              }
              STRdestroy(pushstring);
         }
#endif
    } while (Recurse);

    writestring(sockfd, ".\r\n");
    GDdestroy(gd);
}


/*
* This processes a file containing any subset of
* Type, Name, Path, Port or Host, and returns pointers to the
* overriding data that it finds.
*
* The caller may choose to initialise the pointers - so we don't
* touch them unless we find an over-ride.
*/

#ifndef VMS_SERVER
int
Process_Side(sidefile, Gopherp)
#else
int
Process_Side(sidefile, Gopherp, host, port, filename)
 char *host;
 int port;
 char *filename;
#endif
 FILE *sidefile;
 GopherObj *Gopherp;
{
    char inputline[MAXLINE];
    char *cp;
    int  retval = TRUE;

    inputline[0] = '\0';

    for (;;) {
         for (;;) {
              cp = fgets(inputline, 1024, sidefile);
              if (inputline[0] != '#' || cp == NULL)
                   break;
         }

         /*** Test for EOF ***/
         if (cp==NULL)
              break;

         ZapCRLF(inputline);  /* should zap tabs as well! */
#ifndef VMS_SERVER
         /*** Test for the various field values. **/

         if (strncmp(inputline, "Type=", 5)==0) {
              GSsetType(Gopherp, inputline[5]);
              if (inputline[5] == '7') {
                   /*** Might as well set the path too... ***/
                   cp = GSgetPath(Gopherp);
                   *cp = '7';
              } else if (inputline[5] == '9') {
                   /*** Might as well set the path too... ***/
                   cp = GSgetPath(Gopherp);
                   *cp = '9';
              } else if (inputline[5] == '-')
                   retval = FALSE;
         }

         else if (strncmp(inputline, "Name=", 5)==0) {
              GSsetTitle(Gopherp, inputline+5);
         }

         else if (strncmp(inputline, "Host=", 5)==0) {
              GSsetHost(Gopherp, inputline+5);
         }

         else if (strncmp(inputline, "Port=", 5)==0) {
              GSsetPort(Gopherp, atoi(inputline+5));
         }

         else if (strncmp(inputline, "Path=", 5)==0) {
              GSsetPath(Gopherp, inputline+5);
         }

         else if (strncmp(inputline, "Numb=", 5)==0) {
              GSsetNum(Gopherp, atoi(inputline+5));
         }

         else if (strncmp(inputline, "Name=", 5)==0) {
              GSsetTitle(Gopherp, inputline+5);
         }

#else
         VMS$Continuation(inputline,sidefile,1024,'-');
         Process_Sideline(inputline, Gopherp, host, port, filename);
         if (GSgetType(Gopherp) == '-')
           retval = FALSE;
#endif

    }
#ifdef VMS_SERVER
    if (GSgetPort(Gopherp)==0) {
       char *cp;

       if (GSgetPath(Gopherp))
           switch(*(cp=GSgetPath(Gopherp)))
           {
         case A_INDEX: GSsetPort(Gopherp, GDCgetSRCHPort(Config)); break;
         case A_FTP:   GSsetPort(Gopherp, GDCgetFTPPort(Config));  break;
         case A_EXEC:  GSsetPort(Gopherp, GDCgetEXECPort(Config)); break;
         default:      GSsetPort(Gopherp,port);
           }
       else
           GSsetPort(Gopherp,port);
    }
    GSsetLookaside(Gopherp,TRUE);
#endif

    return(retval);
}

#ifdef VMS_SERVER
/*
* This produces a lookaside filename given a path and a filename.
*
*  For VMS, the application ACE's (if any) are tested to locate any
*  GOPHER_ACE entries; if present, these are processed, and NULL is
*  returned to indicate no lookaside file is to be processed, even if
*  one exists.
*/
FILE *
Build_Lookaside(char *path, char *file, GopherStruct *gs)
{
   FILE    *SideFile;
   char    sidename[256];
   char    *cp;

   if (!path)
       return(NULL);
   if (GSgetLookaside(gs))
       return(NULL);
   if (ACL_Lookaside(path, gs, GDCgetHostname(Config),
                       GDCgetPort(Config))) {
       return(NULL);
   }
   if (file==NULL) {
       file = path;
       path = "[]";
   }
   strcpy(sidename, path);
   if (cp=strchr(sidename,']'))
       *cp = '\0';
   if (sidename[strlen(sidename)-1]!='[')
       strcat(sidename, ".");
   strcat(sidename, GDCgetLookAside(Config));
   strcat(sidename, "]");
   strcat(sidename, file);
   if ((SideFile = fopen_VMSopt(sidename, "r"))!=0)
       Debug("Side file name: %s\n", sidename);
   return(SideFile);
}
/*
*  This tests for Application ACE entries in the file's Access Control List
*  which match the GOPHER_ACE value.  If any, store them into the GopherStruct
*  supplied and return TRUE; otherwise return FALSE.
*/
#include <atrdef.h>
#include <acedef.h>
int
ACL_Lookaside(char *file, GopherStruct *gs, char *host, int port)
{
   int     ACL_present=FALSE;          /* Status                           */
   char    *acl_ptr;                   /* Pointer to acl string            */
   char    *gopher$;                   /* Pointer to GOPHER character data */
   int     g;                          /* Length of GOPHER character data  */
   struct  acedef
           *ace$;                      /* Pointer to actual ACE            */
   int     status;                     /* system status                    */
   int     acl_length;                 /* ACL return length                */
   char    acl_buffer[ATR$S_READACL];  /* ACL work buffer                  */
   struct dsc$descriptor_s
           acl_entry = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
   struct FAB
           acl_fab;
   struct XABPRO
           acl_xab;

   if (!file)
       return(ACL_present);
   acl_fab = cc$rms_fab;
   acl_xab = cc$rms_xabpro;
   acl_fab.fab$l_fna = file;
   acl_fab.fab$b_fns = strlen(acl_fab.fab$l_fna);
   if (((status = SYS$OPEN(&acl_fab, 0, 0)) &1) != 1)
       return(ACL_present);
       /*
           Connect the XAB to the FAB block and perform an initial $DISPLAY.
       */
   acl_fab.fab$l_xab = (char *) &acl_xab;
   acl_xab.xab$l_aclbuf = acl_buffer;
   acl_xab.xab$w_aclsiz = ATR$S_READACL;
   acl_xab.xab$l_aclctx = 0;
   if (!(1 & sys$display(&acl_fab, 0, 0)))
       goto close_fab;
   /*
       While we actually have an ACL and the ACL lookup was correct, we
       run through the ACL list within the ACL buffer and format each ACE.
       Since the buffer is relatively small, keep '$DISPLAY'ing the
       file until there is no more ACL entries to find.  The first $DISPLAY
       was done when the file was $OPENed, the next $DISPLAY reads XAB$L_ACLCXT
       to get subsequent ACL entries for the buffer.
   */
   while ((acl_xab.xab$w_acllen != 0)) {
       acl_ptr = acl_buffer;               /* Beginning of the ACLs */
       while((*acl_ptr != 0) && ((acl_ptr - acl_buffer) < ATR$S_READACL))
       {               /* The first byte is the size of the ACL */
           ace$ = (struct acedef *)acl_ptr;
           if ((ace$->ace$b_type==ACE$C_INFO) &&
               (strncmp((char *)&(ace$->ace$l_access),
                   GOPHER_ACE,strlen(GOPHER_ACE))==0)) {
               g = ace$->ace$b_size - 4 - strlen(GOPHER_ACE);
               gopher$ = (char *)malloc(sizeof(char)*g+1);
               memcpy(gopher$,
                       ((char *)&(ace$->ace$l_access))+strlen(GOPHER_ACE) ,g);
               *(gopher$+g) = '\0';
               Process_Sideline(gopher$, gs, host, port, "(ACL)");
               free(gopher$);
               ACL_present = TRUE;
           }
           acl_ptr = acl_ptr + *acl_ptr;   /* Get the next ACL entry */
       }
       bzero(acl_buffer, ATR$S_READACL);
       status = sys$display(&acl_fab, 0, 0);/* Get the next ACL block */
       if (!(status&1))
           goto close_fab;
       if (!(acl_xab.xab$l_aclsts&1))
           break;
       }
close_fab:
   sys$close(&acl_fab,0,0);
   if (ACL_present && (GSgetPort(gs)==0)) {
       char *cp;

       if (GSgetPath(gs))
           switch(*(cp=GSgetPath(gs)))
           {
         case A_INDEX: GSsetPort(gs, GDCgetSRCHPort(Config)); break;
         case A_FTP:   GSsetPort(gs, GDCgetFTPPort(Config));  break;
         case A_EXEC:  GSsetPort(gs, GDCgetEXECPort(Config)); break;
         default:      GSsetPort(gs,port);
           }
       else
           GSsetPort(gs,port);
   }
   GSsetLookaside(gs,TRUE);
   return(ACL_present);
}

/*
* This processes a line containing any of Type=, Name=, Path=, Port=,
* Host=, Numb=, Hidden or Access=, and overrides the supplied GopherStruct
* entries.
*/
Process_Sideline(char *inputline, GopherStruct *gs, char *host, int port,
                       char *filename)
{
   Debug("%s:", filename);
   Debug("%s\n", inputline);

   /*** Test for the various field values. **/

   if (strncasecmp(inputline, GS_TYPE, strlen(GS_TYPE))==0) {
       char *cp;

       GSsetType(gs, inputline[strlen(GS_TYPE)]);
              if (inputline[strlen(GS_TYPE)] == A_INDEX) {
                   /*** Might as well set the path too... ***/
                   cp = GSgetPath(gs);
                   *cp = A_INDEX;
              } else if (inputline[strlen(GS_TYPE)] == A_UNIXBIN) {
                   /*** Might as well set the path too... ***/
                   cp = GSgetPath(gs);
                   *cp = A_UNIXBIN;
              }
   }
   else
   if (strncasecmp(inputline, GS_NAME, strlen(GS_NAME))==0) {
       GSsetTitle(gs, inputline+strlen(GS_NAME));
   }
   else
   if (strncasecmp(inputline, GS_HOST, strlen(GS_HOST))==0) {
       if ((inputline[strlen(GS_HOST)] == '+'
                       || inputline[strlen(GS_HOST)] == '*')
                           && inputline[strlen(GS_HOST)+1] == '\0')
           GSsetHost(gs, host);
       else
           GSsetHost(gs, inputline+strlen(GS_HOST));
   }
   else
   if (strncasecmp(inputline, GS_PORT, strlen(GS_PORT))==0) {
       if ((inputline[strlen(GS_PORT)] == '+'
                       || inputline[strlen(GS_PORT)] == '*')
                           && inputline[strlen(GS_PORT)+1] == '\0')
           GSsetPort(gs, 0);
       else
           GSsetPort(gs, atoi(inputline+strlen(GS_PORT)));
   }
   else
   if (strncasecmp(inputline, GS_PATH, strlen(GS_PATH))==0) {
       GSsetPath(gs, inputline+strlen(GS_PATH));
   }
   else
   if (strncasecmp(inputline, GS_NUMB, strlen(GS_NUMB))==0) {
       GSsetNum(gs, atoi(inputline+strlen(GS_NUMB)));
   }
   else if (strncmp(inputline, GS_ABSTRACT, strlen(GS_ABSTRACT))==0) {
       GSsetAbstract(gs, inputline+strlen(GS_ABSTRACT));
   }
   else if (strncmp(inputline, GS_ADMIN, strlen(GS_ADMIN))==0) {
       GSsetAdmin(gs, inputline+strlen(GS_ADMIN));
   }
   else
   if (strncasecmp(inputline, GS_HDDN, strlen(GS_HDDN))==0) {
       GSsetNum(gs, -99);
   }
   else
   if (strncasecmp(inputline, GS_ACCS, strlen(GS_ACCS)) == 0) {
       if (GSgetAccess(gs) == NULL)
           GSsetAccess(gs, SiteArrayNew());
       GSsetAccessSite(gs, inputline+strlen(GS_ACCS));
       GSsetDefAcc(gs, SiteDefAccess(gs->Access));
       if (GSgetDefAcc(gs) == ACC_UNKNOWN)
           GSsetDefAcc(gs, ACC_FULL);
   }
   else
   if (strncasecmp(inputline, GS_HEAD, strlen(GS_HEAD))==0) {
       GSsetHeader(gs, inputline+strlen(GS_HEAD));
   }
   else
   if (strncasecmp(inputline, GS_FOOT, strlen(GS_FOOT))==0) {
       GSsetFooter(gs, inputline+strlen(GS_FOOT));
   }
   else
   if (strncasecmp(inputline, GS_RHEAD, strlen(GS_RHEAD))==0) {
       GSsetRHeader(gs, inputline+strlen(GS_RHEAD));
   }
   else
   if (strncasecmp(inputline, GS_RFOOT, strlen(GS_RFOOT))==0) {
       GSsetRFooter(gs, inputline+strlen(GS_RFOOT));
   }
}
#endif

/* dgg++   test if file is ASK script */
boolean
IsAskfile( pathname)
 char *pathname;
{
    struct stat statbuf;
    static char tmpfile[512];

    strcpy(tmpfile, pathname);
    strcat(tmpfile, ".ask"); /* hack -- should do fancy lookup in Config */

    if (!rstat( tmpfile, &statbuf))
         return TRUE;
    else
         return FALSE;
}

int
check_file(sockfd, filename, isGplus)
 int sockfd;
 char *filename;
 boolean isGplus;
{
    ;
#ifdef VMS_SERVER
   return(1);
#endif
}

/*
** This function opens the specified file, starts a zcat if needed,
** and barfs the file across the socket.
**
** It now also checks and sees if access is allowed
**
**  This also used the global variable ASKfile
**
*/

void
printfile(sockfd, pathname, startbyte, endbyte, Gplus)
 int     sockfd;
 char    *pathname;
 int     startbyte, endbyte;
 boolean Gplus;
{
    FILE        *ZeFile;
#ifndef VMS_SERVER
    char         inputline[MAXPATHLEN+4];
#else
    char         inputline[MAXLINE+4];
#endif
    FILE        *pp;
    static char *writebuf = NULL;
    static char *bufptr   = NULL;
    int          len;
    int          result;
#ifndef VMS_SERVER
    boolean      dontbuffer = FALSE;
#else
    boolean      dontbuffer = TRUE;
    boolean      tried_support = FALSE;
    GopherStruct *gs = NULL;
#define FromDoc "This is a section of the document "

    if (gs = VMS$VFLAGS(sockfd, pathname, ACC_READ, *(pathname-1))) {
        if (GSgetTitle(gs)==NULL)
           GSsetTitle(gs,pathname);
        if (GSgetHeader(gs)==NULL)
           if (GDCgetDHead(Config)!=NULL)
               GSsetHeader(gs, GDCgetDHead(Config));
        if (GSgetFooter(gs)==NULL)
           if (GDCgetDFoot(Config)!=NULL)
               GSsetFooter(gs, GDCgetDFoot(Config));
        GSaddDateNsize(gs);
        if ((GSgetRHeader(gs)==NULL) && ((startbyte!=0) || (endbyte!=-1))) {
           inputline[0] = '\0';
           sprintf(inputline,"%s%s '%s'.\r\n\r\n", FromDoc,
                       (strlen(GSgetTitle(gs))>78-strlen(FromDoc))?"\r\n ":"",
                                       GSgetTitle(gs));
           GSsetRHeader(gs,inputline);
        }
    }
    else {
       startbyte = 0;
       endbyte = -1;
       goto no_access;
    }

#endif

    /*** Check and see if the peer has permissions to read files ***/

    result = GDCCanRead(Config, CurrentPeerName, CurrentPeerIP, ActiveSessions);

    if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
    no_access:
         SetAbrtFileIf(BummerMsg, gs, DstrAbrtGS, AbortString);
#endif
         Abortoutput(sockfd, GDCgetBummerMsg(Config));
         LOGGopher(sockfd,"Denied access for %s", pathname);
         return;
    } else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
         SetAbrtFile(TooBusy, gs, DstrAbrtGS, NULL);
#endif
         Abortoutput(sockfd, "Sorry, too busy right now. Try again in a few minutes");
         return;
    }


    Debugmsg("starting printfile");

    /* dgg++  patch to block ASK calls from bad clients */
    if (IsAskfile(pathname)) {
         if (!Gplus || !ASKfile) {
              char errmsg[256];
              if (!ASKfile)
                   sprintf(errmsg," Missing input data to ASK form\t\t\t\r\n");
              else
                   sprintf(errmsg," This ASK form needs a gopher+ client\t\t\t\r\n");
#ifdef VMS_SERVER
              SetAbrtFile(BumClient, gs, DstrAbrtGS, NULL);
#endif
              Abortoutput(sockfd, errmsg);
              return;
         }
    }

#ifdef VMS_SERVER
    if (UsingHTML && strcmp(pathname, ".cache.html") != 0) {
         writestring(sockfd, "<PRE>\r\n");
    }

  Try_Support:
#endif

    if ( (ZeFile = rfopen(pathname, "r")) == NULL) {
         /*
          * The specified file does not exist
          */
         char notexistline[256];
         sprintf(notexistline, "'%s' does not exist!!", pathname);
#ifdef VMS_SERVER
         if (!tried_support) {
     /*    if (a device or directory prefixes)     break;      */
     /*    if (isn't a .SCRIPT,.SHELL or .ASK)     break       */
           tried_support = TRUE;
           if (!uchdir(GDCgetSupportDir(Config)))
               goto Try_Support;
         }
         SetAbrtFile(NoSuchFile, gs, DstrAbrtGS, NULL);
#endif
         Abortoutput(sockfd, notexistline);
         return;
    }


    if (writebuf == NULL) {
         writebuf = (char *) malloc(4096 * sizeof(char));
         bufptr   = writebuf;
    }

    if (startbyte != 0)
         fseek(ZeFile, startbyte, 0);

    if ((pp = specialfile(sockfd, ZeFile, pathname))!=NULL) {
         fclose(ZeFile);
         ZeFile = pp;
         dontbuffer = TRUE;
    }

    if (Gplus)
         GSsendHeader(sockfd, -1);
#ifdef VMS_SERVER
    if (gs) {
       print_aux(sockfd, gs, GS_HEAD);
       if (startbyte != 0)
           print_aux(sockfd, gs, GS_RHEAD);
    }
#endif

#ifndef VMS_SERVER
    while (fgets(inputline, MAXPATHLEN, ZeFile) != NULL)
#else
    (void) signal(SIGALRM,read_timeout);   /* use for write timeouts too */
    if (setjmp(env)) {
         LOGGopher(sockfd,"writestring: Timed out!");
         goto ByeBye;
    }
    while (fgets(inputline, MAXLINE, ZeFile) != NULL)
#endif
    {

         ZapCRLF(inputline);

         /** Period on a line by itself, double it.. **/
         if (*inputline == '.' && inputline[1] == '\0' && !EXECflag) {
              inputline[1] = '.';
              inputline[2] = '\0';
         }

         if ((int)strlen(inputline) < 512)
              strcat(inputline, "\r\n");

         len = strlen(inputline);

         if (dontbuffer) {
              alarm(WRITETIMEOUT);
              writestring(sockfd, inputline);
              alarm(0);
         } else {
              if (((bufptr-writebuf) + len+1) > 4096) {
                   /** Write out the buffer if it's too big to fit.. **/
                   (void) alarm(WRITETIMEOUT);
                   if (writestring(sockfd, writebuf))
#ifndef VMS_SERVER
                        LOGGopher(sockfd, "Client went away"), gopherd_exit(-1);
#else
                   {
                        LOGGopher(sockfd, "Client went away");
                   ByeBye:
                        (void)alarm(0);
                        (void) signal(SIGALRM,SIG_IGN);
                        Specialclose(ZeFile);
                        if (gs)
                           GSdestroy(gs);
                        return;
                   }
#endif
                   (void) alarm(0);
                   bufptr=writebuf;
              }


              strcpy(bufptr, inputline);
              bufptr += len;
              *bufptr = '\0';
         }
         if (endbyte >0) {
              if (ftell(ZeFile) >= endbyte)
                   break;
         }
    }

    Specialclose(ZeFile);

#ifdef VMS_SERVER
    if (gs) {
       if (endbyte > 0)
           print_aux(sockfd, gs, GS_RFOOT);
       print_aux(sockfd, gs, GS_FOOT);
       GSdestroy(gs);
    }
#endif

    if (bufptr != writebuf) {
         alarm(WRITETIMEOUT);
         writestring(sockfd, writebuf);
         alarm(0);
    }

    if (writestring(sockfd, ".\r\n")<0)
#ifndef VMS_SERVER
         LOGGopher(sockfd, "Client went away"), gopherd_exit(-1);
#else
         LOGGopher(sockfd, "Client went away");
#endif
}


#define BUFSIZE 8192  /* No, user process can't affect Kernel-IP
                        decission on what the actual output MTU is.
                        System call overhead may well be too great
                        to care for this.  If the protocol had been
                        on top of the UDP, things would be
                        different..  [[email protected]] */

void
send_binary(sockfd, filename, isGplus)
 int sockfd;
 char *filename;
 boolean isGplus;
{
    FILE           *sndfile,*pp;
    unsigned char  in[BUFSIZE];
    register int   j;
    int            gotbytes, size;
    struct stat    buf;
    int            result;
#ifdef VMS_SERVER
    GopherStruct   *Vgs = NULL;

    if (!(Vgs = VMS$VFLAGS(sockfd, filename, ACC_READ, *(filename-1))))
       goto no_access;
#endif

    Debug("Sending %s, binary way\n", filename);
    /** Don't check decoder/extension if .cache **/
    if (strncmp(filename, ".cache",6)!=0) {

         result = GDCCanRead(Config, CurrentPeerName, CurrentPeerIP,
                             ActiveSessions);
         if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
       no_access:
              SetAbrtFileIf(BummerMsg, Vgs, DstrAbrtGS, AbortString);
#endif
              Abortoutput(sockfd, GDCgetBummerMsg(Config));
              return;
         } else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
              SetAbrtFile(TooBusy, Vgs, DstrAbrtGS, NULL);
#endif
              Abortoutput(sockfd, "Too busy right now");
              return;
         }

#ifndef VMS_SERVER
         if (strcmp(filename, "-") == 0) {
              /*** Do some live digitization!! **/
              sndfile = popen("record -", "r");
         }
         else
#endif
              sndfile = rfopen(filename, "r");

         if (!sndfile) {
              /*
               * The specified file does not exist
               */
              char notexistline[256];
              sprintf(notexistline, "'%s' does not exist!!", filename);
#ifdef VMS_SERVER
              SetAbrtFile(NoSuchFile, Vgs, DstrAbrtGS, NULL);
#endif
              Abortoutput(sockfd, notexistline);

              return;
         }

         if ((pp = specialfile(sockfd, sndfile, filename)) != NULL) {
              fclose(sndfile);
              sndfile = pp;
         }
    } else
         sndfile = rfopen(filename, "r");


    if ((isGplus) && strcmp(filename, "-") == 0)
         GSsendHeader(sockfd, -2);
    else if (isGplus) {
         rstat(filename, &buf);
         size = buf.st_size;
         GSsendHeader(sockfd, size);
    }

    while(1) {
         gotbytes = fread(in, 1, BUFSIZE, sndfile);

         if (gotbytes == 0)
              break;       /*** end of file or error... ***/

         (void) alarm(WRITETIMEOUT);  /** In case client is hung.. **/
         j = writen(sockfd, in, gotbytes);
         (void) alarm(0);

         Debug("Wrote %d binary bytes\n",j);
         if (j <= 0)
              break;       /*** yep another error condition ***/

    }
    Specialclose(sndfile);
#ifdef VMS_SERVER
    /** Disable the alarm signal **/
    (void) alarm(0);
    (void) signal(SIGALRM,SIG_IGN);
    if (Vgs)
       GSdestroy(Vgs);
#endif
}

#ifdef VMS_SERVER
/*
*  VMS$VFLAGS -- Validate Filename, LookAside, and GopherStruct...
*
*  Given a filename and a peer address:
*      1) Validate that filespec as syntactically valid.
*      2) Don't allow arbitrary filenames to be selected.
*      3) Construct a GopherStruct for the file being selected
*          and verify that the peer has access as specified.
*      4) If access is permitted, pass back the GopherStruct;
*          if it's denied for any reason, pass back NULL after
*          saving a pointer to the GopherStruct in AbrtGS and
*          indicating to Abortoutput() that it can destroy that
*          GopherStruct when it's done.
*/

GopherStruct *
VMS$VFLAGS(int sockfd, char *filename, int access, char Gtype)
{
    GopherStruct *gs = NULL;
    char *cp;
    FILE *SideFile;
    AccessResult result;

    gs = GSnew();
    GSsetType(gs, Gtype);
    cp = (char *)malloc(2+strlen(filename));
    *cp = Gtype;
    strcpy(cp+1,filename);
    GSsetPath(gs,cp);
    free(cp);
    if (filename[0]=='/') {
         /*** Convert to VMS pathspecs ***/
         if (cp=VMS$WWW_to_VMS(filename, Gtype)) {
           filename = (char *)malloc(2+strlen(cp));
           *filename = Gtype;
           strcpy(filename+1,cp);
           GSsetPath(gs, filename);
           free(filename);
           filename = GSgetPath(gs);
           filename++;
         }
    }
    /* Don't accept anything but valid VMS file specification */
    if (!VMS$Validate_Filespec(filename)) {
       LOGGopher(sockfd, "Invalid Filespec for %s", filename);
       SetAbrtFile(SyntaxErr, gs, DstrAbrtGS, "Invalid Filespec");
       return(NULL);
    }

    /* Don't allow arbitrary files to be accessed */
    if (strchr(filename,':'))
       if (strncasecmp(filename, GDCgetDatadir(Config),
                           strcspn(GDCgetDatadir(Config),":"))) {
           LOGGopher(sockfd, "Denied access for %s", filename);
           SetAbrtFile(BummerMsg, gs, DstrAbrtGS, NULL);
           return(NULL);
       }

    /* Check and see if the peer has permissions to access the resource */
    cp = strchr(GSgetPath(gs),']');
    if (SideFile = Build_Lookaside(cp?filename:"[]", cp?cp+1:filename, gs)) {
       Process_Side(SideFile,gs,GDCgetHostname(Config),GDCgetPort(Config),
                                                               filename);
       fclose(SideFile);
    }
    result = GDCcanAccess(Config, CurrentPeerName, CurrentPeerIP,
                                                       ActiveSessions, access);
    if (result==SITE_OK || result==SITE_UNDEF)
         result = GScanAccess(sockfd, gs, access);
     switch(result)
     {
     case SITE_OK:
     case SITE_UNDEF:
         return(gs);
     case SITE_NOACCESS:
         LOGGopher(sockfd, "Denied access for %s", filename);
         SetAbrtFile(BummerMsg, gs, DstrAbrtGS, NULL);
         return(NULL);
     case SITE_TOOBUSY:
         LOGGopher(sockfd, "Too much from %s, Denied %s",
                                   CurrentPeerName, filename);
         SetAbrtFile(TooBusy, gs, DstrAbrtGS, "Sorry, too busy now...");
         return(NULL);
     }
}

/*      A condition handler to catch problems, display who and where, then
*      resignal so we get a traceback dump on sys$output
*/
int
gopher_traceback(unsigned long int signal[], int mech[])
{
   static char fmt[256];
   static char buf[512];
   static unsigned short i = 0;
   static $DESCRIPTOR(dsc$fmt,fmt);
   static $DESCRIPTOR(dsc$bad,"signal[0..!UL] =!-!#(9XL)");
   static $DESCRIPTOR(dsc$big,"signal[0..!UL] =!20(9XL)!/!_...max displayed");
   static $DESCRIPTOR(dsc$buf,buf);
   static unsigned long s[10];

   if ((++tracing_back) > 1) {
       LOGGopher(traceback.socket, "ACK! Looping (%d) in gopher_traceback",
                                               tracing_back);
       if (tracing_back > 2)
           return(SS$_RESIGNAL);
   }

   if (traceback.tx==NULL)
       traceback.tx = "";
   LOGGopher(traceback.socket, "Dying! %s", traceback.tx);
   if (Argv)
       if (Argv[0])
           LOGGopher(traceback.socket, "Death @ %s", Argv[0]);
   buf[0] = '\0';
   if (signal[0])
   {
       dsc$fmt.dsc$w_length = sizeof(fmt);
       dsc$buf.dsc$w_length = sizeof(buf);
       i = 0;
       if (SS$_NORMAL == sys$getmsg(signal[1], &dsc$fmt, &dsc$fmt, 15, 0))
           sys$faol(&dsc$fmt,&i,&dsc$buf,&signal[2]);
       else {
           if (signal[0]<=20)
               sys$faol(&dsc$bad, &i, &dsc$buf, &signal[0]);
           else
               sys$faol(&dsc$big, &i, &dsc$buf, &signal[0]);
       }
       buf[i] = '\0';
       LOGGopher(traceback.socket, "%s\n", buf);
   }
   if (traceback.socket > -1)
       VMS$fprintf(stderr,"%s : %s\n", CurrentPeerName, traceback.tx);
   else
       VMS$fprintf(stderr,"<> : %s\n", traceback.tx);
   if (Argv)
       if (strlen(Argv[0]))
           VMS$fprintf(stderr, "%s\n", Argv[0]);
   if (strlen(buf))
       VMS$fprintf(stderr,"%s\n", buf);
   LOGGopher(traceback.socket, "Traceback likely on %s", getenv("SYS$ERROR"));
   VMS$fprintf(stderr, "Traceback likely on %s", getenv("SYS$ERROR"));
   return(SS$_RESIGNAL);
}

/*
*  Write out auxilliaries (Header, Range header, Range footer, Footer)
*  to Type=0 documents
*/
int
print_aux(int sockfd, GopherStruct *gs, char *auxilliary)
{
   FILE    *AuxFile;
   char    inputline[512] = "";

   if (gs == NULL)
       return(TRUE);

   if (GSgetType(gs) != A_FILE)
       return(TRUE);
   if (strcmp(auxilliary,GS_HEAD)==0)
       auxilliary = GSgetHeader(gs);
   else
   if (strcmp(auxilliary, GS_FOOT)==0)
       auxilliary = GSgetFooter(gs);
   else
   if (strcmp(auxilliary, GS_RHEAD)==0)
       auxilliary = GSgetRHeader(gs);
   else
   if (strcmp(auxilliary, GS_RFOOT)==0)
       auxilliary = GSgetRFooter(gs);
   else
       return(TRUE);

   if (auxilliary==NULL)
       return(TRUE);

   if (strlen(auxilliary)==0)
       return(TRUE);

   /* Apply Auxilliary Information */

   if (isadir(auxilliary)==0) {
       /* Auxilliary is a file; append entire file */
       if (AuxFile = fopen_VMSopt(auxilliary,"r")) {
           while (fgets(inputline, MAXLINE, AuxFile) != NULL) {
               ZapCRLF(inputline);
               /** Period on a line by itself, double it.. **/
               if (*inputline == '.' && inputline[1] == '\0' && !EXECflag) {
                      inputline[1] = '.';
                      inputline[2] = '\0';
               }
               if (writestring(sockfd, inputline)==-1)
                   return(TRUE);
           }
           fclose(AuxFile);
       }
       return(TRUE);
   }
   /* Auxilliary is a text line; append it */
   /** Period on a line by itself, double it.. **/
   if (auxilliary[0]=='.' && auxilliary[1]=='\0' && !EXECflag) {
       auxilliary[1] = '.';
       auxilliary[2] = '\0';
   }
   writestring(sockfd, auxilliary);
   return(TRUE);
}



/*
* Adds the "Date+Size" patch
*/

void
GDaddDateNsize(GopherDirObj *gd)
{
   int         i;
   GopherObj   *ge;
   char        *filename;
   STATSTR     statbuf;

   for (i=0; i< GDgetNumitems(gd); i++) {
       ge = GDgetEntry(gd, i);
       filename = GSgetPath(ge);
       ustat(filename+1, &statbuf);
       GSaddDateNsize(ge, statbuf);
   }
}



void
GDpostprocVMS(GopherDirObj *gd, GDCobj *gdc, int port, boolean isGplus)
{
   int         i;
   GopherObj   *ge;
   FILE        *SideFile;
   char        file_wo_path[256];
   char        admin[256];
   char        *cp;
   char        *local;
   String      *filename=STRnew();
   STATSTR     statbuf;

   for (i=0; i< GDgetNumitems(gd); i++)
   {
       memset((char *)&statbuf, 0, sizeof(STATSTR));

       ge = GDgetEntry(gd, i);
       cp = GSgetPath(ge);
       if (strlen(cp+1)) {
           str_tolower(cp+1);
           STRset(filename,cp+1);
       }
       if (A_INFO == GSgetType(ge))
           continue;
       if (local=GSgetHost(ge))
           if (strcmp(GDCgetHostname(gdc),local)!=0)
               local = NULL;           /*  Either not ours or nobody's */
/*
*      Get the file statistics for this file if it's a local resource...
*/
   fixup_stats:
           if (local) {
               switch(GSgetType(ge)) {
           case A_DIRECTORY:       /*** It's a directory ***/
           case A_INDEX:           /*** It's an index ***/
           case A_FTP:             /*** ftp link ***/
           case A_EXEC:            /*** exec link ***/
           case A_HTML:            /*** www link ***/
           case A_WAIS:            /*** wais or whois link ***/
                       break;
           default:
                       if (cp=STRget(filename)) {
                           if (ustat(cp, &statbuf) != 0)
                               statbuf.st_size = 0;
                       }
               }
           }
/*
*      If no Lookaside information has been applied yet, do so... (local only)
*/
   fixup_lookaside:
       if (local && (!GSgetLookaside(ge))) {
           if (SideFile = Build_Lookaside(STRget(filename), NULL, ge)) {
               Process_Side(SideFile, ge, local, GDCgetPort(Config),
                                                           STRget(filename));
               fclose(SideFile);
           }
       }
/*
*      If no title has been applied yet, insert a default name if appropriate.
*          For directories, strip off the '.DIR' that might be there.
*/
   fixup_title:
       if (cp=STRget(filename)) {
           strcpy(file_wo_path, cp);
           if (GSgetTitle(ge) == NULL)
               switch(GSgetType(ge)) {
               case A_DIRECTORY:           /*** It's a directory ***/
                       if (cp=strchr(file_wo_path, ']'))
                           *cp = '\0';
                       if (cp=strchr(file_wo_path, '['))
                           strcpy(file_wo_path, cp+1);
                       if (cp=strrchr(file_wo_path, '.'))
                           strcpy(file_wo_path, cp+1);
                       GSsetTitle(ge, file_wo_path);
                       break;
               case A_INDEX:       /*** It's an index ***/
               case A_FTP:                 /*** ftp link ***/
               case A_EXEC:        /*** exec link ***/
               case A_HTML:        /*** www link ***/
               case A_WAIS:        /*** wais or whois link ***/
                       break;
               default:
                       if ((GDCgetDName(Config)))
                           GSsetTitle(ge, GDCgetDName(Config));
                       else {
                           if (cp=strchr(file_wo_path,']'))
                               strcpy(file_wo_path,cp+1);
                           if (cp=strchr(file_wo_path,';'))
                               *cp='\0';
                           GSsetTitle(ge, file_wo_path);
                       }
               }
       }
/*
*      If the port is still undetermined, apply a default port; if the
*          item is oversize, override the port with our oversize port
*/
   fixup_port:
       if (GSgetPort(ge)==0) {
           char *cp;
           if (cp=GSgetPath(ge))
               switch(*cp)
               {
               case A_INDEX:   GSsetPort(ge, GDCgetSRCHPort(gdc)); break;
               case A_FTP:     GSsetPort(ge, GDCgetFTPPort(gdc));  break;
               case A_EXEC:    GSsetPort(ge, GDCgetEXECPort(gdc)); break;
               case A_INFO:    break;
               default:        GSsetPort(ge, port);
               }
           else
               GSsetPort(ge,port);
        }
        if (local && (GDCgetOVERSize(gdc)!=-1))
            if (statbuf.st_size > GDCgetOVERSize(gdc))
               GSsetPort(ge, GDCgetOVERPort(gdc));

/*
*      If it's *OUR* resource, and we're defaulting to Gopher+, then
*          make sure we say it's Gopher+.  Don't touch Gopher+ ask
*          items or items not served by this host, or if we've been
*          told (isGplus==FALSE) this isn't a Gopher+ operation.
*      Note also that we don't impose the G+ flag on items we might not
*          be able to supply results in G+ format for....
*/
   fixup_isGplus:
        if (isGplus || GDCgetIsGplus(gdc))
           if (!(GSisGplus(ge) || GSisAsk(ge)))
               if (local)
                   switch(*(cp=GSgetPath(ge))) {
                           /* Really special case -- Port 13 is time/date and
                                   the server there doesn't speak Gopher+  */
               case A_FILE:        if (GSgetPort(ge)==13)  break;
               case A_DIRECTORY:
               case A_MACHEX:
               case A_PCBIN:
               case A_UNIXBIN:
               case A_SOUND:
               case A_GIF:
               case A_HTML:
               case A_IMAGE:
               case A_MOVIE:
               case A_UUENCODE:    GSsetGplus(ge, GDCgetIsGplus(gdc));
                                   break;
                           /*  The servers which honor these services don't
                               necessarily speak Gopher+, so don't say they
                               do by any default... */
               case A_WAIS:
               case A_MAILSPOOL:
               case A_FTP:
               case A_RANGE:
               case A_EOI:
               case A_APP:
               case A_PDF:
               case A_TN3270:
               case A_CSO:
               case A_ERROR:
               case A_TELNET:
               case A_EXEC:
               case A_INDEX:
               case A_CALENDAR:
               case A_MIME:
               case A_INFO:
               default:            break;
                   }
/*
*      If we haven't already applied an administrator, do so.
*/
   fixup_admin:
        if (local && (!GSgetAdmin(ge))) {
           sprintf(admin,"%s <%s>", GDCgetAdmin(gdc), GDCgetAdminEmail(gdc));
           GSsetAdmin(ge, admin);
        }
/*
*      If we haven't already applied a MODDATE, and we have a date, do so.
*/
   fixup_moddate:
       if (local && GSgetModDate(ge) == NULL && statbuf.st_mtime) {
           char timeval[16];
           char tmpstr[60];
           struct tm *tmthing;

           tmthing = localtime(&(statbuf.st_mtime));
           strftime(timeval,sizeof(timeval), "%Y%m%d%H%M%S", tmthing);
           sprintf(tmpstr,"%s<%s>",asctime(tmthing),timeval);
           GSsetModDate(ge, tmpstr);
       }
/*
*      If it's a Gopher+ item but we don't have a default view yet, apply one.
*/
   fixup_view:
        if (local && GSisGplus(ge) && (GSgetNumViews(ge)==0))
           AddDefaultView(ge, statbuf.st_size, NULL);
   }
   STRdestroy(filename);
}


/*
*  This processes a single filename, adding it to the sort directory
*/
AddFiletoDir(int sockfd, GopherStruct *gs, GopherDirObj *gd,
               char *filename, char *newpath, String *DName)
{

   FILE   *SideFile;
   char   VMS_dir_title[256];
   char   Typep;
   char   *Pathp;

   Typep = '\0';
   Pathp = NULL;


   Getfiletypes(newpath, filename, &Typep, &Pathp);

   if (Typep == A_ERROR)
       return;

   GSsetType(gs, Typep);
   GSsetPath(gs, Pathp);

/*
*      Verify the receiver has access to this resource
*/
   switch(GSgetType(gs)) {
   case A_WAIS:        if (strncmp(GSgetPath(gs),"aisdocid:",9)==0)
                           if (!GScanAccess(sockfd, gs, ACC_BROWSE))
                               return;
                                       /* Note: Wais *searches* fall thru */
   case A_INDEX:       if (!GScanAccess(sockfd, gs, ACC_SEARCH))
                           return;
                       break;
   case A_EXEC:
   case A_FILE:
   case A_SOUND:
   case A_MACHEX:
   case A_PCBIN:
   case A_UUENCODE:
   case A_RANGE:
   case A_UNIXBIN:
   case A_GIF:
   case A_HTML:
   case A_IMAGE:       if (!GScanAccess(sockfd, gs, ACC_READ))
                           return;
                       if (GSgetType(gs) != A_EXEC)
                           break;
                                   /* Note: non-EXEC's fall thru */
   case A_MAILSPOOL:
   case A_FTP:
   case A_DIRECTORY:   if (!GScanAccess(sockfd, gs, ACC_BROWSE))
                           return;
   }
/*** Add the entry to the directory ***/

   GDaddGS(gd, gs);
   GSinit(gs);
}

/*  Search the extensions list for this file, and if found return the
   type code; otherwise return zero
*/
char
VMS$EXASearch(ExtArray *extarr, char *fileext)
{
    int i, extlen, exlen;
    Extobj *temp;

    Debugmsg("EXAsearch:\r\n");
    extlen = strlen(fileext);

    /*** Linear search.  Ick. ***/

    for (i=0; i< EXAgetNumEntries(extarr); i++) {
         temp = EXAgetEntry(extarr,i);

          exlen = strlen(EXgetExt(temp));

          if (exlen <= extlen && strcasecmp(fileext+extlen-exlen,
                         EXgetExt(temp)) == 0) {

               return(EXgetObjtype(temp));
          }
    }
    return(0);
}

/*
   VMS$fprintf() provides a facility by which we can grab any fprintf() calls,
   and if stderr has been specified while we're known to be an INETD process,
   we can force the output to the operator's console.
   use the *real* fprintf() function herein.
*/


#ifdef __STDC__
int VMS$fprintf(FILE *fp, const char *fmt, ...)
#else /* !__STDC__ */
int VMS$fprintf(fp, fmt, va_alist)
 FILE *fp;
 char *fmt;
va_dcl
#endif /* __STDC__ */
{
   va_list         args;
static
   struct
   {
       unsigned char
                   type;
       unsigned    target:24;
       long        rqst_id;
       char        text[512];
   }   msg = {OPC$_RQ_RQST, 0, 0, ""};
   int             i;
   int             channel=0;
   char            message[512];
   char            *cp;
   char            *c2;
   char            LogTag[60];
   char            LogBuf[100];
 static
   $DESCRIPTOR(dsc$msg, NULL);

#ifdef __STDC__
    va_start(args, fmt);
#else
    va_start(args);
#endif
    if (msg.target==0)
       msg.target = GDCgetOPCOM(Config);

    if (c2=GDCgetLogTag(Config)) {
       c2 = strcpy(LogTag, c2);
       if (cp=strchr(c2,'#')) {
          if (strncasecmp(cp-2,"Cn",2)==0) {
             cp -= 2;
             *cp = '\0';
             strcpy(LogBuf,LogTag);
             cp += 3;
             i = strlen(LogBuf);
             LogBuf[i+=sprintf(LogBuf+i,"#%d",Connections)] = '\0';
             strcat(LogBuf, cp);
             c2 = LogBuf;
          }
       }
    }
    (void) vsprintf(message, fmt, args);
    va_end(args);
    strcpy(msg.text,"GopherD");
    if (strlen(c2)) {
        strcat(msg.text, " ");
        strcat(msg.text, c2);
    }
    strcat(msg.text,":\n");
    i = 255 - strlen(msg.text);
    i = (i>strlen(message))?i:strlen(message);
    strncat(msg.text, message, i);
    msg.text[256] = '\0';      /* Truncate to maximum $SNDOPR size of 255 */
    if ((fp == stderr) && RunFromInetd) {
       fprintf(fp, "%s", message);     /* Write to stderr first... */
       dsc$msg.dsc$a_pointer = (char *)&msg;
       dsc$msg.dsc$w_length = sizeof(msg) - sizeof(msg.text)
                                           + strlen(msg.text);
       if ((i = SYS$SNDOPR(&dsc$msg, channel)) != SS$_NORMAL) {
           vaxc$errno = i;
           return(-1);
       }
       else
           return(strlen(msg.text));
    }
    else
       return(fprintf(fp, "%s", msg.text));
}
#endif