/******************************************************************** * 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 #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 #endif #ifdef VMS_SERVER #include "serverutil.h" #include #include #include #include #include #include #ifdef UCX #include #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 #include #include #include 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 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 ] \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-- **/ { 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\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, "", 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, "\r\n\r\n"); writestring(sockfd, "\r\n"); if (GDgetTitle(gd) != NULL) { writestring(sockfd, ""); writestring(sockfd, GDgetTitle(gd)); writestring(sockfd, ""); } writestring(sockfd, "\r\n\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, "

"); writestring(sockfd, GDCgetSite(Config)); writestring(sockfd, "

\r\n

"); writestring(sockfd, GDCgetOrg(Config)); writestring(sockfd, "

\r\n"); } } GDtoNet(gd, sockfd, GSFORM_HTML, Gticket, &GSicontoNet); writestring(sockfd, "\r\n\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 #include 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, "
\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..  [mea@gopher.funet.fi] */

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
.