/********************************************************************
* wilkinson
* 3.142VMS-1
* 1996/01/24 11:00
* gopher_root1:[gopher.g2.vms2_13.gopherd]gopherd.c,v
* Exp
*
* Paul Lindner, University of Minnesota CIS.
*
* Copyright 1991, 1992 by the Regents of the University of Minnesota
* see the file "Copyright" in the distribution for conditions of use.
*********************************************************************
* MODULE: gopherd.c
* Routines to implement the gopher server.
*********************************************************************
* Revision History:
* gopherd.c,v
* Revision 3.142VMS-2 1996/01/24 11:00 wilkinson
* Made OPCOM message console selection configurable
*
* Revision 3.142VMS-1 1995/11/24 14:00 wilkinson
* Added local host filter to suppress extensive postprocessing on resources
* on other servers.
* Override argv[0] from command line with GOPHERD_IMG_NAME logical value, if
* any.
* Fixed "Client went away" errors in printfile() to not call gopherd_exit().
* Make use of new CMDisErrored() macro after CMDfromNet() and act appropriate
* for OpenVMS detached server in the case of an error.
*
* Revision 3.142VMS 1995/09/20 15:30 wilkinson
* Consolodate VMS/Unix source code for server as well as client
*
* Revision 3.142 1995/02/25 20:49:03 lindner
* More timeouts..
*
* Revision 3.141 1995/02/25 06:42:55 lindner
* Add write timeouts
*
* Revision 3.140 1995/02/17 18:32:35 lindner
* Fix for mindex
*
* Revision 3.139 1995/02/16 22:34:01 lindner
* Fix for no defined Maxsessions, A/UX signal handling
*
* Revision 3.138 1995/02/16 22:32:38 lindner
* HTML icon support
*
* Revision 3.137 1995/02/13 19:07:57 lindner
* Add MaxConnection limit for total server
*
* Revision 3.136 1995/02/11 06:22:25 lindner
* Mods to use new statistics and concurrent session tracking
*
* Revision 3.135 1995/02/07 08:37:36 lindner
* Rewrite of mailfile/multifile parsing
*
* Revision 3.134 1995/02/07 07:12:33 lindner
* oops!
*
* Revision 3.133 1995/02/07 07:04:05 lindner
* performance fixes
*
* Revision 3.132 1995/02/06 22:27:55 lindner
* Use dynamic space for Data_Dir, remove RunLS
*
* Revision 3.131 1995/02/06 21:26:15 lindner
* Misc performance fixes
*
* Revision 3.130 1995/02/02 17:13:53 lindner
* Fix memory leaks
*
* Revision 3.129 1995/02/01 21:41:26 lindner
* Fix for loco symbolic links
*
* Revision 3.128 1995/01/26 18:50:21 lindner
* More efficient, more understandable, less buggy directory parser..
*
* Revision 3.127 1995/01/04 17:37:27 lindner
* Make shell scripts line buffered...
*
* Revision 3.126 1994/12/20 17:20:15 lindner
* Rewritten directory parser
*
* Revision 3.125 1994/12/15 17:47:49 lindner
* Add script running capability
*
* Revision 3.124 1994/12/12 21:46:25 lindner
* bonehead
*
* Revision 3.123 1994/12/12 21:03:50 lindner
* Start towards CGI compliance
*
* Revision 3.122 1994/12/12 17:41:52 lindner
* Hack around AIX
*
* Revision 3.121 1994/12/12 16:58:01 lindner
* Add new AUTHresult code, AUTHRES_SYSERR
*
* Revision 3.120 1994/12/11 18:42:19 lindner
* Fix for authenticated ask blocks
*
* Revision 3.119 1994/12/10 08:24:22 lindner
* Bracket out the moddate setting code
*
* Revision 3.118 1994/12/10 06:13:22 lindner
* Add error message for writing ask data
*
* Revision 3.117 1994/12/02 00:38:37 lindner
* Fix for odd GUSER malloc
*
* Revision 3.116 1994/11/30 23:01:09 lindner
* Allow username to be passed to the environment
*
* Revision 3.115 1994/11/30 22:26:33 lindner
* Error fix
*
* Revision 3.114 1994/11/30 22:25:47 lindner
* Error fix
*
* Revision 3.113 1994/11/30 21:53:25 lindner
* Fix for one last Gticket problem
*
* Revision 3.112 1994/11/30 21:26:57 lindner
* Fix for one last Gticket problem
*
* Revision 3.111 1994/11/08 19:48:50 lindner
* Fix bug in mailfile processing
*
* Revision 3.110 1994/10/24 22:17:08 lindner
* Add PDF type
*
* Revision 3.109 1994/10/21 01:45:53 lindner
* refix linger problem
*
* Revision 3.108 1994/10/19 17:21:56 lindner
* Fix for dreaded Malformed command dialog
*
* Revision 3.105 1994/10/13 05:17:48 lindner
* Compiler complaint fixes
*
* Revision 3.104 1994/10/10 18:38:55 lindner
* change in linger behavior
*
* Revision 3.103 1994/09/29 19:59:31 lindner
* Force people to update their gopherd.conf files
*
* Revision 3.102 1994/08/18 22:28:28 lindner
* Abstract for top level and malloc casts
*
* Revision 3.101 1994/08/01 21:56:01 lindner
* Add proto
*
* Revision 3.100 1994/07/31 05:08:52 lindner
* Add pid header file...
*
* Revision 3.99 1994/07/22 16:36:45 lindner
* Fix bug in item_info for top level
*
* Revision 3.98 1994/07/22 15:02:32 lindner
* Fix bugs..
*
* Revision 3.97 1994/07/21 22:07:21 lindner
* Remove ifdef for NeXT
*
* Revision 3.96 1994/07/21 17:23:55 lindner
* FTP gopher+ gw, and file separator code
*
* Revision 3.95 1994/07/19 20:25:42 lindner
* Sizes for gopher directories from .cache files
*
* Revision 3.94 1994/06/29 05:42:43 lindner
* Add authentication capabilities
*
* Revision 3.93 1994/06/03 06:25:37 lindner
* Another fix for Hgopher method of alt views
*
* Revision 3.92 1994/05/18 04:00:40 lindner
* Add port# to waiting for connection message
*
* Revision 3.91 1994/05/14 04:19:33 lindner
* Fix for text files with really long lines
*
* Revision 3.90 1994/05/02 07:41:12 lindner
* Mods to use setlocale()
*
* Revision 3.89 1994/04/25 20:49:07 lindner
* Fix for debug code
*
* Revision 3.88 1994/04/21 21:24:21 lindner
* FIOclose fix
*
* Revision 3.87 1994/04/21 21:15:12 lindner
* Add looking up address item
*
* Revision 3.86 1994/04/19 14:30:17 lindner
* Fix for gopher+ shell scripts
*
* Revision 3.85 1994/04/13 04:17:57 lindner
* Fix for abnormal exits
*
* Revision 3.84 1994/04/08 21:09:21 lindner
* fix for shutdown calls and compiler goofiness
*
* Revision 3.83 1994/04/07 17:28:39 lindner
* putenv stuff
*
* Revision 3.82 1994/04/01 05:03:35 lindner
* Move putenv() later
*
* Revision 3.81 1994/03/31 22:47:36 lindner
* Fix for date and time for multiple view items and Type 1 ask forms
*
* Revision 3.80 1994/03/31 21:25:37 lindner
* Shutdown inetd sockets, simplify some code
*
* Revision 3.79 1994/03/30 21:36:35 lindner
* Fix for binary ask data from Don Gilbert
*
* Revision 3.78 1994/03/17 21:18:08 lindner
* Massive reworking of access limits
*
* Revision 3.77 1994/03/17 04:30:11 lindner
* VMS fixes gopherd.h
*
* Revision 3.76 1994/03/15 17:59:05 lindner
* Some code moved to Sockets.c, some reorg. Fixes for SCO compiler
*
* Revision 3.75 1994/03/08 17:11:16 lindner
* One more fix for listdir
*
* Revision 3.74 1994/03/08 16:45:30 lindner
* Fix things broken by fixing recursive dirs
*
* Revision 3.73 1994/03/08 15:55:41 lindner
* gcc -Wall fixes
*
* Revision 3.72 1994/03/08 15:02:04 lindner
* Fix for unset variable i in main() Causes crash on NextStep486
*
* Revision 3.71 1994/03/08 06:15:57 lindner
* Fix for recursive directory traversal
*
* Revision 3.70 1994/03/04 23:26:08 lindner
* Fix for changes in strstring.h
*
* Revision 3.69 1994/02/20 21:41:31 lindner
* Allow use of -u if the uid specified matches the current uid
*
* Revision 3.68 1994/02/20 16:53:17 lindner
* Add buffered text writes to server, shutdown inbound part of socket after reading
*
* Revision 3.67 1994/01/25 05:25:25 lindner
* Many HTML and URL related fixes
*
* Revision 3.66 1994/01/06 05:42:41 lindner
* Fix for bad clients that don't bind right
*
* Revision 3.65 1993/12/30 04:15:20 lindner
* removed extra fclose in Side_File, fixes certain Linux distributions
*
* Revision 3.64 1993/12/27 16:34:50 lindner
* Now Add dots to the end of the DNS name
*
* Revision 3.63 1993/12/09 20:47:47 lindner
* Remove error destroying data, add boot up message with pid to log file
*
* Revision 3.62 1993/11/03 03:35:25 lindner
* Add headlines to the top level HTML page
*
* Revision 3.61 1993/11/02 06:08:11 lindner
* Strip extensions off of files with multiple views
*
* Revision 3.60 1993/11/02 05:58:12 lindner
* WAIS index speedups, mondo HTML mods
*
* Revision 3.59 1993/10/28 22:08:17 lindner
* memory leak fixes, fix for -u problem
*
* Revision 3.58 1993/10/20 03:22:59 lindner
* none
*
* Revision 3.57 1993/10/20 03:19:31 lindner
* Better error messages..
*
* Revision 3.56 1993/10/11 04:40:52 lindner
* Changes to allow logging via daemon.info syslogd facility
*
* Revision 3.55 1993/10/04 06:47:16 lindner
* Eliminate bogus warning messages
* Remove gindexd crap
* Mods to allow for new command structure for ASKfile
* Auxconf support..
*
* Revision 3.54 1993/09/30 16:57:02 lindner
* Fix for WAIS and $ requests
*
* Revision 3.53 1993/09/22 04:27:35 lindner
* Add ignore options to sidefile processing
*
* Revision 3.52 1993/09/22 00:29:40 lindner
* Speedups for gopher0 and big directories
*
* Revision 3.51 1993/09/21 07:00:03 lindner
* Fix for sites that don't do _POSIX_SAVED_IDS
*
* Revision 3.50 1993/09/21 04:16:45 lindner
* Move cache settings into gopherd.conf
*
* Revision 3.49 1993/09/21 02:35:07 lindner
* Server now adds extensions in a case insensitive manner
*
* Revision 3.48 1993/09/20 16:56:12 lindner
* Mods for moved code
*
* Revision 3.47 1993/09/18 03:27:19 lindner
* slight mod for ftp gateway
*
* Revision 3.46 1993/09/11 05:06:22 lindner
* Mod for ignoring files, and more efficient binary transfers
*
* Revision 3.45 1993/09/11 04:40:41 lindner
* Don't fork for localhost mindex databases
*
* Revision 3.44 1993/08/24 20:58:58 lindner
* fixed typo in add_title code
*
* Revision 3.43 1993/08/23 20:10:39 lindner
* Additional colon, gopher+ error fix
*
* Revision 3.42 1993/08/23 19:38:23 lindner
* Yet another fix for mindexd troubles..
*
* Revision 3.41 1993/08/23 18:46:11 lindner
* Crude addition of a veronica top-level block
*
* Revision 3.40 1993/08/23 02:34:30 lindner
* Optional date and time
*
* Revision 3.39 1993/08/20 18:03:00 lindner
* Mods to allow gopherd.conf files control ftp gateway access
*
* Revision 3.38 1993/08/19 20:52:25 lindner
* Mitra comments
*
* Revision 3.37 1993/08/19 20:25:50 lindner
* Mitra's Debug patch
*
* Revision 3.36 1993/08/12 06:27:35 lindner
* Get rid of errant message when using inetd
*
* Revision 3.35 1993/08/11 22:47:44 lindner
* Fix for gopher0 clients on gopher+ server
*
* Revision 3.34 1993/08/11 21:34:05 lindner
* Remove extensions from titles for files with multiple views.
* Move CMDfromNet() to *after* the chroot() and setuid()
*
* Revision 3.33 1993/08/11 14:39:24 lindner
* Fix for send_binary bug
*
* Revision 3.32 1993/08/11 02:27:40 lindner
* Fix for wais gateway and Unix client
*
* Revision 3.31 1993/08/10 20:26:57 lindner
* Fixed bogus reading of .cache+ files
*
* Revision 3.30 1993/08/06 14:42:49 lindner
* fix for mindex
*
* Revision 3.29 1993/08/06 14:30:40 lindner
* Fixes for better security logging
*
* Revision 3.28 1993/08/05 20:47:16 lindner
* Log execution of programs
*
* Revision 3.27 1993/08/02 17:59:26 lindner
* Fix for Debug syntax error when using DL
*
* Revision 3.26 1993/07/29 20:49:25 lindner
* removed dead vars, test for non-existant binary files, Dump_Core thing..
*
* Revision 3.25 1993/07/27 20:16:03 lindner
* Fixed bug logging directory transactions
*
* Revision 3.24 1993/07/27 06:13:40 lindner
* Bug fixes for redeffed .cache stuff
*
* Revision 3.23 1993/07/27 05:27:46 lindner
* Mondo Debug overhaul from Mitra
*
* Revision 3.22 1993/07/27 01:52:59 lindner
* More comments, don't let server die if fork error..
*
* Revision 3.21 1993/07/26 20:33:02 lindner
* Fix from guyton, cachefd can be zero
*
* Revision 3.20 1993/07/26 17:24:02 lindner
* Faster .cache output
*
* Revision 3.19 1993/07/26 15:32:26 lindner
* mods for application/gopher-menu and faster .cache sending
*
* Revision 3.18 1993/07/23 03:24:22 lindner
* fix for ppoen and NeXTs with overwritten envp
* mucho mods for looking up filenames,
* enhanced item info for waissrc: and others.
* update for Text/plain & other MIME types.
*
* Revision 3.17 1993/07/20 23:53:51 lindner
* LOGGOpher changes, Argv mucking, and Version Number enhancements
*
* Revision 3.16 1993/07/13 03:57:29 lindner
* Fix for gopherls, improved mailfile handling, iteminfo on 3b2
*
* Revision 3.15 1993/07/10 04:22:36 lindner
* fix for gopherls
*
* Revision 3.14 1993/07/08 17:54:40 lindner
* fix for .src files that already end with .src
*
* Revision 3.13 1993/07/07 19:33:00 lindner
* Sockets.c update, fix for compressed binarys, exec: fixes
*
* Revision 3.12 1993/06/11 16:59:57 lindner
* gzip support, less lookups, etc.
*
* Revision 3.11 1993/04/15 22:20:08 lindner
* CAPFILES mods
*
* Revision 3.10 1993/04/15 04:48:07 lindner
* Debug code from Mitra
*
* Revision 3.9 1993/04/10 06:06:11 lindner
* Admit1 Fixes for combined public/authed server
*
* Revision 3.8 1993/04/07 05:54:39 lindner
* Fixed dreaded .mindex addition problem
*
* Revision 3.7 1993/03/26 19:47:50 lindner
* Hacks to support wais gateway and gplus indexing
*
* Revision 3.6 1993/03/25 21:36:30 lindner
* Mods for directory/recursive etal
*
* Revision 3.5 1993/03/24 22:08:59 lindner
* Removed unused variable
*
* Revision 3.4 1993/03/24 20:23:17 lindner
* Lots of bug fixes, compressed file support, linger fixes, etc.
*
* Revision 3.3 1993/03/01 02:22:40 lindner
* Mucho additions for admit1 stuff..
*
* Revision 3.2 1993/02/19 21:21:11 lindner
* Fixed problems with signals, problems with gethostbyaddr() and
* inconsisent behavior that depended on the order of files in a directory.
*
* Revision 3.1.1.1 1993/02/11 18:02:55 lindner
* Gopher+1.2beta release
*
*********************************************************************/
/* Originally derived from an
* Example of server using TCP protocol
* pp 284-5 Stevens UNIX Network Programming
*/
#include <stdio.h>
#if defined(VMS_SERVER) && !defined(GOPHERD_C)
#define GOPHERD_C 1
#endif
#include "gopherd.h"
#include "command.h"
#include "patchlevel.h"
#include "Malloc.h"
#include "Debug.h"
#include "fileio.h"
#ifdef _AUX_SOURCE
# include <compat.h>
#endif
#ifdef VMS_SERVER
#include "serverutil.h"
#include <stsdef.h>
#include <stdarg.h>
#include <descrip.h>
#include <prvdef.h>
#include <syidef.h>
#include <jpidef.h>
#ifdef UCX
#include <ucx$inetdef.h>
#endif
#endif
#undef stat /** Stupid openers thing..**/
GopherDirObj *GDfromSelstr();
GopherDirObj *GDfromUFS();
void item_info();
/******* Global variables *********/
static char* Gdefusername = NULL;
#if defined(VMS_SERVER) && defined(__VAXC)
typedef unsigned int uid_t;
typedef unsigned short gid_t;
#endif
static uid_t Guid;
static gid_t Ggid;
/**********************************/
extern char *getdesc();
extern double maxload;
int Process_Side();
char *GDESencrypt();
#include "STAarray.h"
#include "STRstring.h"
#include "Sockets.h"
#ifdef VMS_SERVER
#include <ssdef.h>
#include <opcdef.h>
#include <descrip.h>
#include <errno.h>
char *setable_argv[2];
extern int vaxc$errno_stv;
extern double sysload;
extern char log_alq[10];
extern char log_deq[10];
GopherStruct *VMS$VFLAGS(int sockfd, char *filename, int access, char Gtype);
void GDaddDateNsize(GopherDirObj *gd);
void GDpostprocVMS(GopherDirObj *gd, GDCobj *gdc, int port, boolean isGplus);
char VMS$EXASearch(ExtArray *cfgext, char *filename);
int gopher_traceback();
union prvdef prvadr;
unsigned long int ON = -1;
static
struct
{
int socket;
char *tx;
} traceback = { -1, NULL};
static unsigned long condition;
static int tracing_back = 0;
static
struct _exit_block
{
unsigned long must_be_zero;
unsigned long routine$;
unsigned long number_of_args;
unsigned long exit_condition$;
} gopher_traceback_blk =
{0, (unsigned long) &gopher_traceback, 1, (unsigned long) &condition};
#endif
/* This function is called on a read timeout from the network */
#include <setjmp.h>
jmp_buf env;
SIGRETTYPE read_timeout(sig)
int sig;
{
longjmp(env,1);
}
#ifndef VMS_SERVER
void
gopherd_usage(progname)
char *progname;
{
fprintf(stderr, "Usage: %s [-mCDIc] [-u username] [-s securityfile] [-l logfile] [ -L loadavg ] <datadirectory> <port>\n", progname);
fprintf(stderr, " -C turns caching off\n");
fprintf(stderr, " -D enables copious debugging info\n");
fprintf(stderr, " -I enable \"inetd\" mode\n");
fprintf(stderr, " -c disable chroot(), use secure open routines instead\n");
fprintf(stderr, " -u specifies the username for use with -c\n");
fprintf(stderr, " -o override the default options file '%s'\n", CONF_FILE);
fprintf(stderr, " -l specifies the name of a logfile\n");
fprintf(stderr, " -L specifies maximum load to run at\n");
}
#endif
void
gopherd_exit(val)
int val;
{
#ifdef VMS_SERVER
if (RunFromInetd || DEBUG)
ServerSetArgv(""); /* Don't leave this around */
#endif
exit(val);
}
/*
* This is for when we dump core.. Make an attempt to clean up..
*/
SIGRETTYPE
sigabnormalexit()
{
gopherd_exit(-1);
}
#ifndef VMS_SERVER
void
main(argc, argv, envp)
#else
main(argc, argv, envp)
#endif
int argc;
char *argv[];
char *envp[];
{
int childpid;
int sockfd, newsockfd;
int clilen;
struct sockaddr_in cli_addr;
boolean OptionsRead = FALSE;
int i=0;
char tmpstr[256];
/*** for getopt processing ***/
int c;
#ifndef VMS_SERVER
extern char *optarg;
extern int optind;
int errflag =0;
#else
char *cp;
char restart[80];
char rstrt[80];
int finish = 0;
char nodename[16];
struct itmlst {
unsigned short int length;
unsigned short int code;
char *bufadr;
int *retlen;
} syi$itmlst[2] = { { 15, SYI$_NODENAME, NULL, NULL},
{ 0, 0, 0, 0 } },
jpi$itmlst[2] = { { 255, JPI$_IMAGNAME, NULL, NULL },
{ 0, 0, 0, 0 } };
VAXC$ESTABLISH (gopher_traceback);
SYS$DCLEXH (&gopher_traceback_blk);
if (getenv("GOPHERD_INETD_DEBUG")) {
DEBUG = TRUE;
}
#endif
#ifndef VMS_SERVER
Gtxtlocale(LC_ALL, "");
Argv = argv;
#if !(defined(NeXT) || defined(_AIX))
/* NeXTs don't like their envp to be overwritten... */
for (i=0; envp[i] != NULL; i++)
;
#endif
if (i > 0)
LastArgv = envp[i - 1] + strlen(envp[i - 1]);
else
LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
pname = argv[0];
#else
Gtxtlocale("sys$disk:_", getenv("LC_MESSAGES"));
Argv = setable_argv;
Argv[0] = (char *)malloc(256);
Argv[1] = NULL;
LastArgv = Argv[0] + 256;
jpi$itmlst[0].bufadr = pname = (char *)malloc(256);
if (SS$_NORMAL!=SYS$GETJPIW(0,0,0,jpi$itmlst,0,0,0))
strcpy(pname, argv[0]);
#endif
err_init(); /* openlog() early - before we chroot() of course */
RunServer = TRUE; /** Run the server by default **/
Config = GDCnew(); /** Set up the general configuration **/
#ifndef VMS_SERVER
while ((c = getopt(argc, argv, "CDIcL:l:o:u:")) != -1)
switch (c) {
case 'D':
DEBUG = TRUE;
break;
case 'I':
RunFromInetd = TRUE;
break;
case 'C':
GDCsetCaching(Config, FALSE);
break;
case 'c':
dochroot = FALSE;
break;
case 'L': /** Load average at which to restrict usage **/
maxload = atof(optarg);
break;
case 'l': /** logfile name **/
if (*optarg == '/' || strcasecmp(optarg, "syslog") == 0)
GDCsetLogfile(Config, optarg);
else {
getwd(tmpstr);
strcat(tmpstr, "/");
strcat(tmpstr, optarg);
GDCsetLogfile(Config, tmpstr);
}
break;
case 'o': /** option file **/
if (*optarg == '/')
GDCfromFile(Config, optarg);
else {
getwd(tmpstr);
strcat(tmpstr, "/");
strcat(tmpstr, optarg);
GDCfromFile(Config, tmpstr);
}
OptionsRead = TRUE;
break;
case 'u':
{
struct passwd *pw = getpwnam( optarg );
if ( !pw ) {
fprintf(stderr,
"Could not find user '%s' in passwd file\n",
optarg);
errflag++;
} else {
if (!(pw->pw_uid == getuid() || getuid() == 0))
printf("Need to be root to use -u\n"), gopherd_exit(-1);
Gdefusername = strdup(optarg);
Guid = pw->pw_uid;
Ggid = pw->pw_gid;
}
}
break;
case '?':
case 'h':
errflag++;
break;
}
if (errflag) {
gopherd_usage(argv[0]);
gopherd_exit(-1);
}
if (optind < argc) {
Data_Dir = strdup(argv[optind]);
optind++;
Debug("main: Setting data to Data Directory is %s\n",Data_Dir);
}
if (Data_Dir == NULL)
Data_Dir = strdup(DATA_DIRECTORY);
Debug("main: Data Directory is %s\n",Data_Dir);
if (optind < argc) {
GopherPort = atoi(argv[optind]);
optind++;
Debug("main: Setting port to %d\n",GopherPort);
}
Debug("main: Port is %d\n",GopherPort);
#else
#if defined(MULTINET) || defined(WOLLONGONG)
/*
* The fopen() in GDCfromFile() opens and closes SYS$INPUT
*/
{
int status;
unsigned short chan;
$DESCRIPTOR(desc, "SYS$INPUT");
/*
* See if we're running under Inetd/MULTINET_SERVER
* and if so, get a channel to the socket.
*/
if (strstr(getenv("SYS$INPUT"), "_INET")) {
status = sys$assign(&desc, &chan, 0, 0, 0);
if (!(status & 1))
gopherd_exit(status);
sockfd = (int) chan;
RunFromInetd = TRUE;
}
}
#endif /* MultiNet or Wollongong */
#if defined(UCX) && defined(UCX$C_AUXS)
/*
* This socket call will try to get the client connection from
* the AUX Server. If the socket call fails, assume we are
* running as a detached process. Otherwise, we are running
* from the AUX server.
*/
if ((sockfd = socket (UCX$C_AUXS, 0, 0)) != -1)
RunFromInetd = TRUE;
#endif /* UCX v2+ */
if (RunFromInetd) {
/*
* Track down the configuration file. Everything this server process
* needs to know is now passed through that file. -- F.Macrides
*/
int newport = 0;
char *ConfigFile = (char *) malloc(sizeof(char)*256);
/*
* In multiserver environments, the port number is appended
* to a system logical for each server's configuration file.
*/
if ((newport = SOCKgetPort(sockfd)) < 0)
gopherd_exit(SS$_NOIOCHAN);
sprintf(ConfigFile, "GOPHER_CONFIG%d", newport);
if (getenv(ConfigFile) != NULL) {
GDCfromFile(Config, getenv(ConfigFile));
} else {
/*
* Try the system logical without a numeric suffix.
*/
strcpy(ConfigFile, "GOPHER_CONFIG");
if (getenv(ConfigFile) != NULL) {
GDCfromFile(Config, getenv(ConfigFile));
} else {
/*
* Must be an explicit filespec in CONF.H
*/
GDCfromFile(Config, CONF_FILE);
}
}
free(ConfigFile);
/*
* Make sure the configuration file didn't set these wrong.
*/
GDCsetInetdActive(Config, TRUE);
GDCsetPort(Config, newport);
OptionsRead = TRUE;
/*
* Turn off all privs except TMPMBX and NETMBX
*/
VMS$DisableAllPrivs();
}
/*** VMS command line options are cast to lowercase in C programs, unless
each argument was double-quoted. Furthermore, many of the options
are pretty irrelevant for debugging purposes, and passing command
line options to a VMS detatched process isn't particularly easy.
So command line options will be used in VMS only for debugging,
when not running under Inetd/MULTINET_SERVER, and they will be
restricted to the configuration file and port number. They'll
all be positional:
$ gopher_debug [[[debug] port] config]
***/
if (argc>1 && !RunFromInetd) {
if (argc>3)
DEBUG = TRUE;
GDCfromFile(Config, argv[--argc]);
OptionsRead = TRUE;
if (argc>1 && !RunFromInetd)
GDCsetPort(Config, atoi(argv[--argc]));
}
dochroot = FALSE;
#endif
/** Read the options in, if not overridden **/
if (OptionsRead == FALSE)
GDCfromFile(Config, CONF_FILE);
/** Check to make sure the options specified are workable... ***/
GDCintegrityCheck(Config);
#ifndef VMS_SERVER
/*** Make sure we do a tzset before doing a chroot() ***/
tzset();
#else
/** Were the FTPPort, EXECPort, SRCHPort or OVERPort were unspecified?**/
if (GDCgetFTPPort(Config) == -1)
GDCsetFTPPort(Config,GDCgetPort(Config));
if (GDCgetEXECPort(Config) == -1)
GDCsetEXECPort(Config,GDCgetPort(Config));
if (GDCgetSRCHPort(Config) == -1)
GDCsetSRCHPort(Config,GDCgetPort(Config));
if (GDCgetOVERPort(Config) == -1)
GDCsetOVERPort(Config,GDCgetPort(Config));
/** Get the lookaside file format **/
if (cp = GDCgetLookAside(Config))
if (*cp != '\0') {
char lookaside[256];
strcpy(lookaside, GDCgetLookAside(Config));
strcat(lookaside, ".DIR");
EXAprocessLine(Config->Extensions, EXT_IGNORE, lookaside, NULL);
}
/** Get the restart logical if any **/
if (cp = GDCgetRestart(Config))
if (cp && *cp != '\0')
strcpy(restart, GDCgetRestart(Config));
else
restart[0] = '\0';
/** Set the AbortOutput() message file info **/
SetAbrtMsg(TooBusy,"2busy");
SetAbrtMsg(BadHostData,"badhostdata");
SetAbrtMsg(IOErr,"data_gone");
SetAbrtMsg(NothingThere,"nothing");
SetAbrtMsg(RangeErr,"range");
SetAbrtMsg(BaddirMsg,"baddir");
SetAbrtMsg(BummerMsg,"bummer");
SetAbrtMsg(IndexErr,"index");
SetAbrtMsg(NoSuchFile,"no_such_file");
SetAbrtMsg(SyntaxErr,"syntax");
SetAbrtMsg(BumClient,"client");
/** Set up the configured language, if any **/
i = 0;
if (cp=GDCgetSupportDir(Config))
i += strlen(cp);
if (cp=GDCgetHiddenPrefix(Config))
i += strlen(cp);
LastArgv = (char *)malloc(i+1);
if (cp = GDCgetSupportDir(Config))
strcpy(LastArgv, cp);
if (cp = GDCgetHiddenPrefix(Config))
strcat(LastArgv, cp);
Gtxtlocale(LastArgv, getenv("LC_MESSAGES"));
free(LastArgv);
LastArgv = Argv[0] + 256;
/** If LogTag specified, replace host & pid tokens if any **/
syi$itmlst[0].bufadr = nodename;
if (SS$_NORMAL!=SYS$GETSYIW(0,0,0,syi$itmlst,0,0,0))
strcpy(nodename,"unknown");
if (GDCgetLogTag(Config)) {
char LogTag[60] = {"["};
char *tk;
char *cp = GDCgetHostname(Config);
char *rest;
char *all;
int i=1;
all = rest = (char *)malloc(2+strlen(GDCgetLogTag(Config)));
strcpy(rest, GDCgetLogTag(Config));
if (DEBUG) {
strcat(LogTag,"Dbg");
i=strlen(LogTag);
}
while (strlen(rest)) {
tk = rest;
while (strlen(rest) && *rest!=' ')
rest++;
if (strlen(rest))
*rest++ = '\0';
else
if (!strlen(tk))
break;
if (strcasecmp(tk,"pid")==0)
LogTag[i+=sprintf(LogTag+i,"%s%s%x",
strlen(LogTag)>1?"/":"",
RunFromInetd?"i":"",getpid())] = '\0';
else
if (strcasecmp(tk,"node")==0)
LogTag[i+=sprintf(LogTag+i,"%s%s",
strlen(LogTag)>1?"/":"",nodename)] = '\0';
else
if (strcasecmp(tk,"host")==0)
LogTag[i+=sprintf(LogTag+i,"%s%s",
strlen(LogTag)>1?"/":"", cp?cp:"?")] = '\0';
else
LogTag[i+=sprintf(LogTag+i,"%s%s",
strlen(LogTag)>1?"/":"", tk)] = '\0';
}
if (strlen(LogTag)>1) {
strcat(LogTag,"]");
GDCsetLogTag(Config, LogTag);
}
free(all);
}
/** If an error file was specified, set it up **/
if (cp=GDCgetErrorfile(Config))
if (*cp != '\0') {
char *t = (char *)malloc(256);
char *s = (char *)malloc(256);
if (RunFromInetd && t) {
boolean matches=FALSE;
int status;
/* use ";" to trigger exec mode logical */
sprintf(t,"LNM$PROCESS_TABLE;SYS$ERROR=%s",cp);
bzero((char *) &prvadr, sizeof(prvadr));
prvadr.prv$v_sysprv = 1;
if (SS$_NORMAL != (vaxc$errno =
SYS$SETPRV (ON, &prvadr, 0, 0))) {
LOGGopher(-1,"Can't insure SYSPRV for SYS$ERROR define, %s",
STRerror(vaxc$errno));
}
if (status=putenv(t))
strcpy(s,STRerror());
VMS$DisableAllPrivs();
strcpy(t,getenv("SYS$ERROR"));
if (strcasecmp(cp,t)==0)
matches=TRUE;
else {
LOGGopher(-1,"(env)%s%s(cfg)%s %s",
t, matches?"==":"!=", cp, status?s:"");
Debug("(env)%s", t);
Debug(matches?"==(cfg)%s\n":"!=%s(cfg)\n", cp);
if(status)
Debug("%s\n",s);
}
free(t);
free(s);
}
freopen(cp,"a",stderr);
}
#endif
if (!RunFromInetd) {
#ifndef VMS_SERVER
char *cp;
printf("Internet Gopher Server %s.%s patch %d\n", GOPHER_MAJOR_VERSION, GOPHER_MINOR_VERSION, PATCHLEVEL);
#else
printf("VMSGopher-L Gopher Server version %s-%s\n", GOPHERD_VERSION,
PATCH_LEVEL);
#endif
printf("Copyright 1991,92,93 the Regents of the University of Minnesota\n");
printf("See the file 'Copyright' for conditions of use\n");
printf("Data directory is %s\n", Data_Dir);
printf("Port is %d\n", GopherPort);
#ifndef VMS_SERVER
/*** Check for weird inconsistencies, security warnings etal ***/
if (getuid() == 0 && Gdefusername == NULL)
printf("Warning! You should run the server with the -u option!\n");
else if (Gdefusername != NULL)
printf("Running as user '%s'\n", Gdefusername);
if (dochroot == FALSE)
printf("Not using chroot() - be careful\n");
#else
/* if a GOPHERD_IMG_NAME logical exists, override the name */
if (cp=getenv("GOPHERD_IMG_NAME"))
strcpy(pname,cp);
/* If there's a ][ in the image name, we can compress it out */
if (cp=strstr(pname,"][")) {
/* If we have the facility at some point to search for all
logical names in LNM$SYSTEM which wildcard-match the device
specification in GDCgetDatadir(Config), we should do so, and
then if any of them have an equivalence which matches the
string *pname up to and including the first "]", we should
replace that string up to and including the first "]" with
the logical name which matched that string. Thus, if our
executable is anywhere within the legitimate data area for
the server, we have a string referencing the executable file
which is *servable* by the server. Note that the user-written
system service SYS$LOOKUP() written by Ferry Bolh�r is a
good candidate for this functionality. Otherwise one would
need to do a spawn out to DCL doing a SHOW LOG/TAB=LNM$SYSTEM
of GOPHER_ROOT* or whatever the device name in GDCgetDatadir()
is, and interpret the resultant equivalence strings. */
}
while(cp=strstr(pname,"]["))
strcpy(cp,cp+2);
printf("Image is %s\n", pname);
printf("PID is %x\n", getpid());
#endif
cp = GDCgetLogfile(Config);
if (cp && *cp != '\0')
printf("Logging to File %s\n", GDCgetLogfile(Config));
#ifdef VMS_SERVER
{
if (cp=GDCgetLogTag(Config))
printf("Log Tag is %s\n", cp);
LOGGopher(-1, "================================");
LOGGopher(-1, "Starting %x %s", getpid(), pname);
LOGGopher(-1, "Hostalias %s on node %s", GDCgetHostname(Config),
nodename);
}
#endif
}
#ifdef VMS_SERVER
else
{
/* Be nice to put some kind of log entry, at least, for INETD connections ??? */
}
#endif
if (uchdir(Data_Dir)) {
#ifndef VMS_SERVER
fprintf(stderr, "Cannot change to data directory!! %s \n",Data_Dir);
gopherd_exit(-1);
#else
LOGGopher(-99,"fatal: cannot change to data directory %s!!, %s",
GDCgetDatadir(Config),STRerror(errno));
#endif
}
#ifndef VMS_SERVER
if (dochroot && getuid() != 0) {
fprintf(stderr, "Gopherd uses the privileged call chroot(). Please become root.\n");
gopherd_exit(-1);
}
#endif
fflush(stderr);
fflush(stdout);
#ifdef _AUX_SOURCE
(void) set42sig();
#endif
#ifndef VMS_SERVER
if (DEBUG == FALSE && RunFromInetd==FALSE)
daemon_start(TRUE);
/** We ignore SIGUSR2, so the PID routines can "ping" us **/
(void) signal(SIGUSR2, SIG_IGN);
(void) signal(SIGINT, sigabnormalexit);
(void) signal(SIGSEGV, sigabnormalexit);
#endif
/*** Hmmm, does this look familiar? :-) ***/
err_init();
/** Ask the system what host we're running on **/
#ifndef VMS_SERVER
Zehostname = SOCKgetDNSname(DOMAIN_NAME, GDCgetHostname(Config));
#else
GDCsetHostname(Config, SOCKgetDNSname(DOMAIN_NAME,GDCgetHostname(Config)));
#endif
Debug("I think your hostname is %s\n", Zehostname);
if (RunFromInetd) {
/** Ask the system which port we're running on **/
int newport=0;
#ifndef VMS_SERVER
if ((newport =SOCKgetPort(0)) !=0)
GopherPort=newport;
/*** Do the stuff for inetd ***/
while(do_command(fileno(stdout))!=0); /* process the request */
#else
if ((newport = SOCKgetPort(0)) !=0)
if (newport != -1)
GDCsetPort(Config, newport);
ServerStarted = time(NULL);
ServerSetArgv("waiting for connection @ %d", GopherPort);
/*** Do the stuff for inetd ***/
while(do_command(sockfd)!=0); /* process the request */
shutdown(sockfd, 2);
closenet(sockfd);
#endif
shutdown(fileno(stdout), 2);
shutdown(fileno(stdin), 2);
shutdown(fileno(stderr), 2);
fclose(stdout);
fclose(stdin);
fclose(stderr);
#ifdef VMS_SERVER
traceback.socket = -1;
traceback.tx = NULL;
SYS$CANEXH (&gopher_traceback_blk);
#endif
gopherd_exit(0);
} else {
#ifndef VMS_SERVER
LOGGopher(-1, "Starting gopher server (pid %d)", getpid());
#endif
}
/** Set our cmd string **/
ServerStarted = time(NULL);
ServerSetArgv("waiting for connection @ %d", GopherPort);
/** Open a TCP socket (an internet stream socket **/
sockfd = SOCKbind_to_port(GopherPort);
#if defined(VMS_SERVER) && defined(MULTINET)
SOCKkeepalive(sockfd, TRUE);
#endif
listen(sockfd, 5);
for ( ; ; ) {
/*
* Wait for a connection from a client process.
* This is an example of a concurrent server.
*/
#ifdef VMS_SERVER
/** Test the RESTART logical **/
if (strlen(restart)) {
char *r = getenv(restart);
if (r)
if (strlen(strcpy(rstrt,r)))
finish = 1;
}
#endif
clilen = sizeof(cli_addr);
while (1) {
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd >= 0)
break;
else if (errno != EINTR) /** Restart accept if we hit a
SIGCHLD signal..**/
LOGGopher(newsockfd, "Client went away");
}
SOCKlinger(sockfd, FALSE);
SOCKlinger(newsockfd, FALSE);
ActiveSessions ++;
if ((GDCgetMaxconns(Config) > 0 ) && ActiveSessions > GDCgetMaxconns(Config)) {
writestring(newsockfd, "0Too Many Users at this time, try again later\txx\txx\t70\r\n");
ActiveSessions--;
close(newsockfd);
} else {
#ifndef VMS_SERVER
if ( (childpid = fork()) < 0) {
/** Problems forking.. **/
writestring(newsockfd, "3Problems forking!\tmoo\terror.host\t0\r\n.\r\n");
}
else if (childpid == 0) { /* Child process */
close(sockfd); /* close original socket */
(void)do_command(newsockfd);/* process the request */
gopherd_exit(0);
}
/** Parent **/
/** clean up any zombie children **/
sig_child();
close(newsockfd); /* parent process */
Connections++;
#else
while(do_command(newsockfd)!=0);{} /* process the request */
traceback.socket = -1;
traceback.tx = NULL;
closenet(newsockfd);
Connections++;
ActiveSessions--; /* We don't have child processes like that */
#endif
}
#ifdef VMS_SERVER
if (finish) {
ServerSetArgv("");
LOGGopher(-1,"Server %x Stopped (%s = '%s')",getpid(),
restart, rstrt);
if (!RunFromInetd)
printf("Server Shutdown, %s = '%s'", restart, rstrt);
SYS$CANEXH (&gopher_traceback_blk);
break;
}
#endif
}
}
/*
* Given a specific view, add the filename extension that
* we stripped off for the multiple views stuff
*/
char *
AddExtension(cmd, view)
CMDobj *cmd;
char *view;
{
char *filename = CMDgetFile(cmd);
char *newselstr;
int flen=0, newlen=0;
char *newfile;
if (filename == NULL)
return(CMDgetSelstr(cmd));
newfile = EXAfindFile(Config->Extensions, filename, view);
if (newfile == NULL)
return(CMDgetSelstr(cmd));
flen = strlen(filename);
newlen = strlen(newfile);
if (newlen > flen) {
/*** Add the found extensions... ***/
newselstr = (char *)malloc(MAXPATHLEN);
strcpy(newselstr, CMDgetSelstr(cmd));
strcat(newselstr, newfile+flen);
Debug("New long file is %s", newselstr);
return(newselstr);
}
return(CMDgetSelstr(cmd));
}
int EXECflag;
void
SetScriptEnvironment(cmd, view)
CMDobj *cmd;
char *view;
{
char tmpstr[256];
#ifdef VMS_SERVER
#define SetEnvironmentVariable(a,b) VMS$SetEnv("LNM$JOB",a,b)
#endif
SetEnvironmentVariable("GPLUS_CMD", CMDgetCommand(cmd));
SetEnvironmentVariable("CONTENT_TYPE", view);
SetEnvironmentVariable("GUSER", CMDgetUser(cmd));
SetEnvironmentVariable("GTICKET", CMDgetTicket(cmd));
#ifndef VMS_SERVER
sprintf(tmpstr, "UofMNgopherd/%s.%spl%d", GOPHER_MAJOR_VERSION,
GOPHER_MINOR_VERSION, PATCHLEVEL);
#else
sprintf(tmpstr, "UofMNgopherd/%s.%sVMS-%d", GOPHER_MAJOR_VERSION,
GOPHER_MINOR_VERSION, PATCHLEVEL);
#endif
SetEnvironmentVariable("SERVER_SOFTWARE", tmpstr);
SetEnvironmentVariable("SERVER_NAME", Zehostname);
sprintf(tmpstr, "%d", GopherPort);
SetEnvironmentVariable("SERVER_PORT", tmpstr);
SetEnvironmentVariable("SERVER_PROTOCOL", "gopher/1.0");
SetEnvironmentVariable("GATEWAY_INTERFACE", "CGI/1.0");
SetEnvironmentVariable("REQUEST_METHOD", "GET");
SetEnvironmentVariable("PATH_INFO", CMDgetSelstr(cmd));
SetEnvironmentVariable("PATH_TRANSLATED", CMDgetSelstr(cmd));
SetEnvironmentVariable("SCRIPT_NAME", CMDgetFile(cmd));
SetEnvironmentVariable("QUERY_STRING", CMDgetSearch(cmd));
SetEnvironmentVariable("REMOTE_HOST", CurrentPeerName);
SetEnvironmentVariable("REMOTE_ADDR", CurrentPeerIP);
SetEnvironmentVariable("REMOTE_USER", CMDgetUser(cmd));
}
int
do_command(sockfd)
int sockfd;
{
char logline[MAXLINE];
char *view = NULL;
char *Selstr = NULL;
#ifndef VMS_SERVER
CMDobj *cmd;
#endif
char *filter = NULL;
char *cp;
int result;
cmd = CMDnew();
/*** Reopen the log file ***/
cp = GDCgetLogfile(Config);
if (cp != NULL && *cp != '\0') {
if (strcasecmp(GDCgetLogfile(Config), "syslog")==0) {
#ifndef VMS_SERVER
LOGFileDesc = -2; /** log file is syslog **/
#else
*((int *)(&LOGFileDesc)) = -2; /* DEC compilers are picky */
#endif
} else if ((int)LOGFileDesc < 0) {
#ifndef VMS_SERVER
LOGFileDesc = uopen(GDCgetLogfile(Config),
O_WRONLY | O_APPEND |O_CREAT, 0644);
#else
LOGFileDesc = fopen_VMSopt(GDCgetLogfile(Config), "a");
#endif
}
if ((int)LOGFileDesc == -1) {
#ifndef VMS_SERVER
printf("Can't open the logfile: %s\n", GDCgetLogfile(Config));
gopherd_exit(-1);
#else
VMS$fprintf(stderr, "Can't open the logfile: %s\n",
GDCgetLogfile(Config));
return(0);
#endif
}
}
#ifndef VMS_SERVER
if(LoadTooHigh()) {
Abortoutput(sockfd, "System is too busy right now. Please try again later.");
gopherd_exit(-1);
}
#endif
(void) signal(SIGALRM,read_timeout);
#ifndef VMS_SERVER
(void) alarm(READTIMEOUT);
#else
(void) alarm(GDCgetReadTimeout(Config));
#endif
if (setjmp(env)) {
LOGGopher(sockfd,"readline: Timed out!");
#ifndef VMS_SERVER
gopherd_exit(-1);
#else
/** Disable the alarm signal **/
(void) alarm(0);
(void) signal(SIGALRM,SIG_IGN);
return(0);
#endif
}
ServerSetArgv("Looking up address");
/*** Find out who's knockin' ***/
SOCKnetnames(sockfd, CurrentPeerName, CurrentPeerIP);
ServerSetArgv("input from %s", CurrentPeerName);
CMDfromNet(cmd, sockfd);
#ifdef VMS_SERVER
if (CMDisErrored(cmd)) {
/** Disable the alarm signal **/
(void) alarm(0);
(void) signal(SIGALRM,SIG_IGN);
return(0);
}
#endif
if (CMDgetUser(cmd) != NULL)
CurrentUser = strdup(CMDgetUser(cmd));
GDCevalDir(Config, CMDgetFile(cmd));
IsGplus = CMDisGplus(cmd); /* need for error output.. */
#ifdef VMS_SERVER
/** Don't service the client's request if the Load is too high **/
if (LoadTooHigh()) {
char *sel = CMDgetSelstr(cmd);
boolean refuse = TRUE;
if (sel) /* Users w/ 2busy msg should be allowed to see it */
if (strncasecmp(TooBusy,sel,strlen(TooBusy))==0)
refuse = FALSE;
if (refuse) {
LOGGopher(sockfd, "System Load (%g) Too High", sysload);
SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
Abortoutput(sockfd,
"System is too busy right now. Please try again later.");
return(0);
}
}
#endif
#ifndef VMS_SERVER /* Hey, look, it's 1st priority after the other G+ features, OK? */
#ifndef NO_AUTHENTICATION
if (CMDgetTicket(cmd) != NULL) {
/** The user has already authenticated, extract from ticket **/
char *tix = CMDgetTicket(cmd);
char *user = CMDgetUser(cmd);
char *cleartext;
GDCsetCaching(Config, FALSE);
/** decode ticket here... ***/
cleartext = (char*) GDESdecrypt(CMDgetUser(cmd), CurrentPeerIP,
GDCgetPW(Config), tix);
if (strcmp(cleartext, CMDgetUser(cmd)) == 0) {
/*CMDremoveAsk(cmd);*/
; /** Do the trans... **/
Setuid_username(user);
Gticket = (char*) malloc(sizeof(char*) *
(strlen(CMDgetUser(cmd)) +
strlen(tix) + 4));
sprintf(Gticket, "*%s %s ", CMDgetUser(cmd), tix);
} else {
GplusError(sockfd, 1, "Your ticket is invalid", NULL);
}
} else if (strncmp(CMDgetSelstr(cmd), "validate ", 9) == 0) {
/** Take the information from the ASK block and validate the user,
then, generate tickets **/
char *selstr, *auth;
GDCsetCaching(Config, FALSE);
/** Only look at the ask block if it's there... **/
if (*CMDgetCommand(cmd) != '!') {
selstr = CMDgetSelstr(cmd) + 9;
/** Doctor up the item so it looks real.. **/
CMDsetSelstr(cmd, selstr);
auth = GDCauthType(Config, CMDgetFile(cmd));
switch (GDCvalidate(Config, auth, CMDgetAskline(cmd, 0),
CMDgetAskline(cmd, 1), CurrentPeerName,
CurrentPeerIP)) {
char *ask1, *ask0, *crypted;
case AUTHRES_OK:
Gticket = (char*) malloc(sizeof(char*) *
(strlen(CMDgetAskline(cmd,0)) +
strlen(CMDgetAskline(cmd,1))+4));
ask0 = CMDgetAskline(cmd, 0);
ask1 = CMDgetAskline(cmd, 1);
crypted = GDESencrypt(ask0, CurrentPeerIP,
GDCgetPW(Config), ask0);
sprintf(Gticket, "*%s %s ", ask0, crypted);
CMDremoveAsk(cmd);
; /** Do the trans... **/
break;
case AUTHRES_BADPW:
if (strlen(CMDgetAskline(cmd, 1)) == 0)
GplusError(sockfd, 1, "Please enter a valid password", NULL);
else
GplusError(sockfd, 1, "Your password is incorrect, try again", NULL);
break;
case AUTHRES_SYSERR:
GplusError(sockfd, 1, "Authentication system failue, please contact administrator", NULL);
break;
default:
GplusError(sockfd, 1, "Your username is incorrect, try again", NULL);
break;
}
}
} else {
/** Check to see if this item needs authentication, and send
a redirect error message **/
char *auth = GDCauthType(Config, CMDgetFile(cmd));
if (auth != NULL) {
/** Need to redirect **/
Abortoutput(sockfd, "Sorry, no access to this item");
}
}
#endif
#endif
#ifdef VMS_SERVER
/** Disable the alarm signal **/
(void) alarm(0);
(void) signal(SIGALRM,SIG_IGN);
#endif
/** At this point there won't be any more data coming in, so shutdown
the incoming data for the socket
**/
shutdown(sockfd, 0);
/** Change our root directory **/
#ifndef VMS_SERVER
if ( dochroot ) {
if (chroot(Data_Dir))
Abortoutput(sockfd, "Data_Dir dissappeared!"), gopherd_exit(-1);
uchdir("/"); /* needed after chroot */
}
if (getuid() == 0) {
setgid(Ggid);
setuid(Guid);
}
#else
if (uchdir(Data_Dir)) {
SetAbrtFile(IOErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
Abortoutput(sockfd, "Data_Dir disappeared!");
LOGGopher(-99,"fatal: cannot change to data directory %s!!, %s",
GDCgetDatadir(Config),STRerror(errno));
}
#endif
if (CMDisAskitem(cmd)) {
FILE *retrfile;
/** Write the stuff out to a file, here, as gopherd user
so we can remove file later on.. **/
ASKfile = tempnam(NULL, "gdata");
Debug("Ask data is in %s\n", ASKfile);
retrfile = ufopen(ASKfile, "w",0777);
if (retrfile != NULL) {
int i;
for (i=0; i < CMDnumAsklines(cmd); i++) {
fputs(CMDgetAskline(cmd, i), retrfile);
putc('\n', retrfile);
}
fclose(retrfile);
} else {
#ifdef VMS_SERVER
SetAbrtFile(IOErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
Abortoutput(sockfd, "Cannot write ask data");
}
}
#ifdef VMS_SERVER
#ifdef FOTEOS
if (strncmp(inputline, "GET ", 4) == 0) {
UsingHTML = TRUE;
NoDOT = TRUE;
/** Check for Unix root directory designator **/
if (*(inputline+4) == '/')
selstr = inputline+5;
else
selstr = inputline+4;
/** Convert the hex things back to text... ***/
Fromhexstr(selstr, selstr);
/** Check for http flag from a gopher server's tuple **/
if (strncmp(selstr, "GET ", 4) == 0) {
if (*(selstr+4) == '/')
selstr = selstr+5;
else
selstr = selstr+4;
}
/** Trim off HTTP2 trailers, if present **/
if ((cp=strstr(selstr, " HTTP/1.0")) != NULL) {
*cp = '\0';
/** Clear the Accept:, User-Agent: and From: **/
/** fields from the receive buffer **/
HTTP2line = (char *) malloc(sizeof(char)*MAXLINE);
do {
length = readline(sockfd, HTTP2line, MAXLINE);
ZapCRLF(HTTP2line);
} while (length > 0 && strlen(HTTP2line) != 0);
free(HTTP2line);
}
}
else
selstr = inputline;
#endif
#endif
/** Extract the view if it exists **/
if (CMDgetCommand(cmd) != NULL) {
char *command = CMDgetCommand(cmd);
int cmdlen = strlen(command);
if ((cmdlen > 1) && (*command == '+'))
view = command+1;
else if (*command == '!') {
item_info(cmd, sockfd);
if (*(command+1) != '\0')
filter = command + 1;
return(0);
}
else if (*command == '$') {
if (*(command+1) != '\0')
filter = command + 1;
view = "application/gopher+-menu";
CMDsetView(cmd, view);
*command = '+';
}
else if (*command == '+') {
;
}
else
#ifndef VMS_SERVER
Abortoutput(sockfd, "Malformed command"); /*** Error ***/
#else
{
SetAbrtFile(SyntaxErr, NULL /* AbortGS ??? */, KeepAbrtGS,
NULL);
Abortoutput(sockfd, "Malformed command");
}
#endif
}
if (strncmp(CMDgetSelstr(cmd), "waisdocid:",10)==0)
view = "Text/plain";
/*** Root level null selector string.. ***/
if (!view && strlen(CMDgetSelstr(cmd)) == 0)
view = "application/gopher-menu";
if (!view && *CMDgetSelstr(cmd) == 'h') {
/** It might be a directory..., or an HTML file on disk **/
struct stat statbuf;
if (!rstat(CMDgetFile(cmd), &statbuf)) {
if (S_ISDIR(statbuf.st_mode)) {
/*** It's a directory capability ***/
*CMDgetSelstr(cmd) = '1';
}
}
view = "text/html";
}
/*** Try to speed things up for gopher0 requests, avoid reading
big directories.. ***/
if (!view && CMDisGplus(cmd) == FALSE) {
struct stat statbuf;
char *cp = CMDgetSelstr(cmd);
;
if (*cp == '0' ||
*cp == 'R') {
if (!rstat(CMDgetFile(cmd), &statbuf))
view = "text/plain";
}
else if (*cp == '1') {
if (!rstat(CMDgetFile(cmd), &statbuf))
view = "application/gopher-menu";
}
}
#ifndef VMS_SERVER
/*** Try to find a view if not supplied ***/
if (view == NULL) {
GopherDirObj *gd;
int num,i;
/** Get gopher directory containing item in question **/
gd = GDfromSelstr(cmd, sockfd);
if (gd != NULL) {
num = GDSearch(gd, CMDgetSelstr(cmd));
if (num >=0) {
GopherObj *gs;
gs= GDgetEntry(gd, num);
if (GSgplusInited(gs) == FALSE) {
view = "";
}
/** If only one view, take it **/
else if (GSgetNumViews(gs) == 1)
view = VIgetViewnLang(GSgetView(gs, 0),(char*)malloc(128));
else {
/*** Hmmm, let's choose one.. ***/
for (i=0; i<GSgetNumViews(gs); i++) {
char *tmpview;
tmpview = VIgetType(GSgetView(gs,i));
if (GSgetType(gs) == '0') {
if (strcasecmp(tmpview, "Text/plain")==0) {
view = VIgetViewnLang(GSgetView(gs, i),(char*)malloc(128));
break;
}
}
if (GSgetType(gs) == 'I') {
if (strcmp(tmpview, "image/gif")==0) {
view = VIgetViewnLang(GSgetView(gs, i),(char*)malloc(128));
break;
}
}
}
if (view == NULL)
/** Give up, take the first view... **/
view = VIgetViewnLang(GSgetView(gs,0), (char*)malloc(128));
}
/** We should have a view by now **/
}
GDdestroy(gd);
} else {
;/* Can't get a Gopher directory listing... */;
}
}
#else
/*
OpenVMS: We need a faster way to determine potential alternate views on a selection
than loading up the entire directory, ackk!!! Maybe a direct-access cache
file, or scan the ACLs or something?
*/
#endif
CMDsetView(cmd, view);
/*
* Set the environment variables for the scripts
* shell script writers everywhere..
*/
SetScriptEnvironment(cmd, view);
/** Decide whether to add extensions of not .. **/
if (view != NULL )
Selstr = AddExtension(cmd, view);
else
Selstr = CMDgetSelstr(cmd);
ServerSetArgv("%s to %s", Selstr, CurrentPeerName);
EXECflag = 0;
/* Ask items are shell scripts, not directories.
Selector strings with '1/' should never be shell scripts */
if (CMDisAskitem(cmd) && *Selstr == '1')
*Selstr = '0';
/*** With the funky new capability system we can just check the
first letter(s), end decide what the object refers to. ***/
Debug("Attempting to get Selstr %s\n", Selstr);
switch (*Selstr) {
#ifdef VMS_SERVER
case '/':
if (strlen(Selstr) != 1) /* Some Gopher Filter or Robot? */
goto old_link;
Null_Capability:
#endif
case '\0':
case '\t':
/*** The null capability, so it's not a file, probably wants
to talk to a directory server ***/
/*** we'll just do a "list" of the root directory, with no user
capability. ***/
#ifndef VMS_SERVER
listdir(sockfd, "/", CMDisGplus(cmd), view, filter);
#else
listdir(sockfd, Data_Dir, CMDisGplus(cmd), view, filter);
#endif
LOGGopher(sockfd, "Root Connection");
break;
case A_HTML:
case A_FILE:
case A_UNIXBIN:
case A_SOUND:
case A_IMAGE:
case A_GIF:
#ifdef VMS_SERVER
case A_MOVIE:
case A_UUENCODE:
#endif
/*** It's some kind of file ***/
/*** Is it binary?? ***/
if (view == NULL) {
Debugmsg("View is null\n");
if (*Selstr != A_FILE)
send_binary(sockfd, Selstr+1, CMDisGplus(cmd));
else
printfile(sockfd, Selstr+1, 0, -1, CMDisGplus(cmd));
} else {
Debug("Testing view %s\n",view);
if (GSisText(NULL, view))
printfile(sockfd, Selstr+1, 0, -1, CMDisGplus(cmd));
else
send_binary(sockfd, Selstr+1, CMDisGplus(cmd));
}
/*** Log it ***/
LOGGopher(sockfd, "retrieved file %s", Selstr+1 );
break;
case A_DIRECTORY:
/*** It's a directory capability ***/
#ifdef VMS_SERVER
if (strlen(Selstr+1)==0)
goto Null_Capability;
#endif
listdir(sockfd, Selstr+1, CMDisGplus(cmd), view, filter);
/** Log it **/
LOGGopher(sockfd, "retrieved directory %s", Selstr+1);
break;
case A_INDEX:
/*** It's an index capability ***/
#ifdef VMS_SERVER
if (!GDCgetSRCHPort(Config))
goto NoSRCHAccess;
#endif
result = GDCCanSearch(Config, CurrentPeerName, CurrentPeerIP,
ActiveSessions);
if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
NoSRCHAccess:
SetAbrtFileIf(BummerMsg, NULL /* AbortGS ??? */, KeepAbrtGS,
NULL);
#endif
Abortoutput(sockfd, GDCgetBummerMsg(Config));
LOGGopher(sockfd, "Denied access for %s", Selstr+1);
break;
} else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
Abortoutput(sockfd, "Sorry, too busy now...");
break;
}
Do_IndexTrans(sockfd, Selstr+1, cmd, TRUE);
break;
#ifndef VMS_SERVER
case 'm':
if (strncmp(Selstr, "mindex:", 7)==0) {
/*** First test for multiple indexes ***/
result = GDCCanSearch(Config, CurrentPeerName, CurrentPeerIP,
ActiveSessions);
if (result == SITE_NOACCESS) {
Abortoutput(sockfd, GDCgetBummerMsg(Config));
LOGGopher(sockfd, "Denied access for %s", Selstr+1);
break;
} else if (result == SITE_TOOBUSY) {
Abortoutput(sockfd, "Sorry, too busy now...");
break;
}
do_mindexd(sockfd, Selstr+7, CMDgetSearch(cmd), CMDisGplus(cmd),
view);
break;
}
/*** This is an internal identifier ***/
/*** The m paired with an Objtype of 1 makes a mail spool file
into a directory.
***/
result = GDCCanBrowse(Config, CurrentPeerName, CurrentPeerIP,
ActiveSessions);
if (result == SITE_NOACCESS) {
Abortoutput(sockfd, GDCgetBummerMsg(Config));
LOGGopher(sockfd,"Denied access for %s", Selstr+1);
break;
} else if (result == SITE_TOOBUSY) {
Abortoutput(sockfd, "Sorry, too busy right now..");
break;
}
process_mailfile(sockfd, Selstr + 1);
writestring(sockfd, ".\r\n");
/** Log it **/
LOGGopher(sockfd, "retrieved maildir %s", Selstr+1 );
break;
#endif
#ifndef VMS_SERVER
case 'R':
#else
case A_RANGE:
#endif
/*** This is an internal identifier ***/
/*** The R defines a range ****/
/*** The format is R<startbyte>-<endbyte>-<filename> **/
{
int startbyte, endbyte;
char *cp, *oldcp;
cp = strchr(Selstr+1, '-');
if (cp == NULL) {
#ifdef VMS_SERVER
SetAbrtFile(RangeErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
Abortoutput(sockfd, "Range specifier error");
break;
}
*cp = '\0';
startbyte = atoi(Selstr+1);
oldcp = cp+1;
cp = strchr(oldcp, '-');
if (cp == NULL) {
#ifdef VMS_SERVER
SetAbrtFile(RangeErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
Abortoutput(sockfd, "Range specifier error");
#ifndef VMS_SERVER
gopherd_exit(-1);
#else
break;
#endif
}
*cp = '\0';
endbyte = atoi(oldcp);
oldcp = cp + 1;
Debug("Start: %d, ", startbyte);
Debug("End: %d, ", endbyte);
Debug("File: %s\n", oldcp);
printfile(sockfd, oldcp, startbyte, endbyte, CMDisGplus(cmd));
/*** Log it ***/
LOGGopher(sockfd, "retrieved range %d - %d of file %s", startbyte, endbyte, oldcp);
break;
}
case 'f':
#ifdef VMS_SERVER
if (!GDCgetFTPPort(Config))
goto NoFTPAccess;
#endif
result = GDCCanFTP(Config,CurrentPeerName,CurrentPeerIP,ActiveSessions);
if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
NoFTPAccess:
SetAbrtFileIf(BummerMsg, NULL /* AbortGS ??? */, KeepAbrtGS,
NULL);
#endif
Abortoutput(sockfd, GDCgetBummerMsg(Config));
LOGGopher(sockfd, "Denied access for %s", Selstr);
break;
} else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
Abortoutput(sockfd, "Sorry, too busy now...");
break;
}
if (strncmp(Selstr, "ftp:",4)==0){
LOGGopher(sockfd, "retrieved %s", Selstr);
GopherFTPgw(sockfd, Selstr+4, cmd);
break;
}
break;
case A_EXEC:
#ifndef VMS_SERVER
result = GDCCanBrowse(Config, CurrentPeerName, CurrentPeerIP,
ActiveSessions);
#else
if (!GDCgetEXECPort(Config))
goto NoEXECAccess;
result = GDCCanEXEC(Config, CurrentPeerName, CurrentPeerIP,
ActiveSessions);
#endif
if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
NoEXECAccess:
SetAbrtFileIf(BummerMsg, NULL /* AbortGS ??? */, KeepAbrtGS,
NULL);
#endif
Abortoutput(sockfd, GDCgetBummerMsg(Config));
LOGGopher(sockfd,"Denied access for %s", Selstr+1);
break;
} else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
Abortoutput(sockfd, "Sorry, too busy right now..");
break;
}
if (strncmp(Selstr, "exec:", 5)==0) {
/* args are between colons */
char *args, *command;
command = strrchr(Selstr + 5, ':');
if (command == NULL)
break;
#ifdef VMS_SERVER
{
char *c2;
/*
command is pointing to the last colon; it could be part of
an OpenVMS device specification, or it could be the other
arg boundary (if the device was defaulted), so we search
backward for a third colon, between the first one and here.
*/
*command = '\0';
c2 = strrchr(Selstr + 5, ':');
*command = ':';
/*
If we did find another colon, so if it's followed by a
validated path, that's the colon which terminates the
arguments, so point command there (otherwise, leave
command pointing to the last colon, because the device
was defaulted, and that *is* an arg boundary).
*/
if (c2 != NULL && VMS$Validate_Filespec(c2+1))
command = c2;
}
#endif
if (*(Selstr+4) == ':' && *(Selstr+5) == ':')
args = NULL;
else
args = Selstr+5;
*command = '\0';
command++;
EXECargs = args;
EXECflag = 1;
printfile(sockfd, command, 0, -1, CMDisGplus(cmd));
LOGGopher(sockfd, "Executed %s %s", command, (args == NULL)
? " " : args);
}
break;
#ifndef VMS_SERVER
case 'w':
#else
case A_WAIS:
#endif
{
if (strncmp(Selstr, "waissrc:", 8) == 0) {
char waisfname[512]; /*** Ick this is gross ***/
result = GDCCanSearch(Config, CurrentPeerName, CurrentPeerIP,
ActiveSessions);
if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
SetAbrtFile(BummerMsg, NULL /* AbortGS ??? */, KeepAbrtGS,
NULL);
#endif
Abortoutput(sockfd, GDCgetBummerMsg(Config));
LOGGopher(sockfd, "Denied access for %s", Selstr+1);
break;
} else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS,
NULL);
#endif
Abortoutput(sockfd, "Sorry, too busy now...");
break;
}
strcpy(waisfname, Selstr+8);
if ((int)strlen(waisfname) <= 4 ||
strncmp(&waisfname[strlen(waisfname)-4],".src",4) )
strcat(waisfname, ".src");
SearchRemoteWAIS(sockfd, waisfname, cmd, view);
break;
}
else if (strncmp(Selstr, "waisdocid:", 10) == 0) {
result = GDCCanSearch(Config, CurrentPeerName, CurrentPeerIP,
ActiveSessions);
if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
SetAbrtFile(BummerMsg, NULL /* AbortGS ??? */, KeepAbrtGS,
NULL);
#endif
Abortoutput(sockfd, GDCgetBummerMsg(Config));
LOGGopher(sockfd, "Denied access for %s", Selstr+1);
break;
} else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
SetAbrtFile(TooBusy, NULL /* AbortGS ??? */, KeepAbrtGS,
NULL);
#endif
Abortoutput(sockfd, "Sorry, too busy now...");
break;
}
Fetchdocid(sockfd, cmd);
break;
}
}
default:
/*** Hmmm, must be an old link... Let's see if it exists ***/
#ifdef VMS_SERVER
old_link:
#endif
switch (isadir(Selstr)) {
case -1:
/* no such file */
#ifdef VMS
no_such_file:
if (vaxc$errno || vaxc$errno_stv)
LOGGopher(sockfd, "'%s' does not exist (%s%s%s)", Selstr,
STRerror(errno),
vaxc$errno_stv?"/":"",
STRerror_stv());
else
#endif
sprintf(logline, "'%s' does not exist", Selstr);
#ifdef VMS_SERVER
SetAbrtFile(NoSuchFile, NULL /* AbortGS ??? */, KeepAbrtGS,
NULL);
#endif
Abortoutput(sockfd, logline);
break;
case 0:
/* it's a file */
printfile(sockfd, Selstr, 0, -1, CMDisGplus(cmd));
/* Log it... */
LOGGopher(sockfd, "retrieved file %s", Selstr);
break;
case 1:
/* it's a directory */
listdir(sockfd, Selstr, CMDisGplus(cmd), view, filter);
/* Log it */
LOGGopher(sockfd, "retrieved directory %s", Selstr);
break;
}
}
/** Free data ***/
CMDdestroy(cmd);
return(0);
}
/*
* This function tries to find out what type of file a pathname is.
*/
void
Getfiletypes(newpath, filename, gs)
char *newpath;
char *filename;
GopherObj *gs;
{
int Zefilefd;
char *cp;
char Zebuf[100];
char Selstr[512];
switch (isadir(filename)) {
case -1:
GSsetType(gs,A_ERROR);
return;
case 1:
GSsetType(gs,A_DIRECTORY);
*Selstr = A_DIRECTORY;
strcpy(Selstr +1, newpath);
#ifdef VMS_SERVER
if (cp=strrchr(Selstr +1,'.'))
*cp = '\0';
if (cp=strrchr(Selstr +1,']'))
*cp = '.';
strcat(Selstr +1,"]");
#endif
GSsetPath(gs, Selstr);
GSsetTTL(gs, GDCgetCachetime(Config));
return;
default:
/*** The default is a generic text file ***/
GSsetType(gs, A_FILE);
*Selstr = A_FILE;
strcpy(Selstr + 1, newpath);
/*** Test and see if the thing exists... and is readable ***/
if ((Zefilefd = ropen(filename, O_RDONLY)) < 0) {
GSsetType(gs, A_ERROR);
return;
}
if (read(Zefilefd, Zebuf, sizeof(Zebuf)) <0) {
GSsetType(gs, A_ERROR);
return;
}
close(Zefilefd);
/*** Check the first few bytes for sound data ***/
cp = Zebuf;
if (strncmp(cp, ".snd", 4)==0) {
GSsetType(gs, A_SOUND);
*Selstr = A_SOUND;
strcpy(Selstr+1, newpath);
}
#ifndef VMS_SERVER
/*** Check and see if it's mailbox data ***/
else if (is_multipartfile(Zebuf) != SPLIT_UNKNOWN) {
GSsetType(gs, A_DIRECTORY);
*Selstr = 'm';
strcpy(Selstr+1, newpath);
GSsetGplus(gs, FALSE); /** Not yet.. **/
}
#endif
/*** Check for uuencoding data ***/
else if (strncmp(cp,"begin",6) == 0) {
#ifndef VMS_SERVER
GSsetType(gs, '6');
*Selstr = '6';
#else
GSsetType(gs, A_UUENCODE);
*Selstr = A_UUENCODE;
#endif
strcpy(Selstr+1, newpath);
}
/*** Check for GIF magic code ***/
else if (strncmp(cp, "GIF", 3) == 0) {
GSsetType(gs, A_IMAGE);
*Selstr = A_UNIXBIN;
strcpy(Selstr + 1, newpath);
}
#ifdef VMS_SERVER
{
char Gtype;
if (Gtype = VMS$EXASearch(Config->Extensions, filename)) {
GSsetType(gs, Gtype);
strcpy(Selstr, newpath);
}
}
#endif
GSsetPath(gs, Selstr);
}
}
/*
* Add a default view if none exists..
*/
void
AddDefaultView(gs, size, dirname)
GopherObj *gs;
int size;
char *dirname;
{
char *lang = GDCgetLang(Config);
STATSTR statbuf;
switch (GSgetType(gs)) {
case A_FILE:
GSaddView(gs, "Text/plain", lang, size);
break;
case A_DIRECTORY:
if (GDCgetCaching(Config) && dirname != NULL) {
strcat(dirname, "/.cache");
if (ustat(dirname, &statbuf) == 0)
size = statbuf.st_size;
GSaddView(gs, "application/gopher-menu", lang, size);
strcat(dirname, "+");
if (ustat(dirname, &statbuf) == 0)
size = statbuf.st_size;
GSaddView(gs, "application/gopher+-menu", lang, size);
GSaddView(gs, "text/html", lang, size);
} else {
GSaddView(gs, "application/gopher-menu", lang, size);
GSaddView(gs, "application/gopher+-menu", lang, size);
GSaddView(gs, "text/html", lang, size);
}
break;
case A_MACHEX:
GSaddView(gs, "application/mac-binhex40", lang, size);
break;
case A_PCBIN:
GSaddView(gs, "application/octet-stream", lang, size);
break;
case A_CSO:
GSaddView(gs, "application/qi", lang, 0);
break;
case A_INDEX:
GSaddView(gs, "application/gopher-menu", lang, size);
GSaddView(gs, "application/gopher+-menu", lang, size);
break;
case A_TELNET:
break;
case A_SOUND:
GSaddView(gs, "audio/basic", lang, size);
break;
case A_UNIXBIN:
GSaddView(gs, "application/octet-stream", lang, size);
break;
case A_GIF:
GSaddView(gs, "image/gif", lang, size);
break;
case A_HTML:
GSaddView(gs, "text/html", lang, size);
break;
case A_TN3270:
GSaddView(gs, "application/tn3270", lang, 0);
break;
case A_MIME:
GSaddView(gs, "multipart/mixed", lang, size);
break;
case A_IMAGE:
GSaddView(gs, "image", lang, size);
break;
case A_PDF:
GSaddView(gs, "application/pdf", lang, size);
break;
}
}
void
GSaddDateNsize(gs, statbuf)
GopherObj *gs;
struct stat statbuf;
{
#ifndef VMS_SERVER
#ifdef ADD_DATE_AND_TIME
int fd, i;
char longname[256];
char *cdate, *ti, *fp, *stitle;
switch (GSgetType(gs)) {
case '1': /*** It's a directory ***/
case '7': /*** It's an index ***/
case 'f': /*** ftp link ***/
case 'e': /*** exec link ***/
case 'h': /*** www link ***/
case 'w': /*** wais link ***/
case 'm':
break;
default:
{
stitle = GSgetTitle(gs);
if (strstr( stitle, "kb]") == 0) {
/* Correct for multiple view items */
cdate= ctime( &statbuf.st_mtime); /* last mod time */
cdate[ 7]= 0; cdate[10]= 0; cdate[24]= 0;
sprintf( longname, "%s [%s%s%s, %ukb]", stitle,
cdate+8,cdate+4,cdate+22, (statbuf.st_size+1023) / 1024);
GSsetTitle(gs,longname);
}
}
break;
}
#endif /* ADD_DATE_AND_TIME */
;
#else
/*
* Adds the "Date+Size" patch
* (actually, calls VMS$FormatTokens() to edits symbolic tokens in the
* Title to reflect information about the file specified in the Path).
* Nops if there are no symbolic tokens in the Title, if the Title or
* Path are NULL, or if the type code for the path is inappropriate for
* the operation specified. Nops also if statistical information about
* the file specified can't be secured.
*/
char *fp;
time_t tstamp;
if ((fp = GSgetTitle(gs))==NULL)
return;
if (strchr(fp,'%')==NULL)
return;
if ((fp = GSgetPath(gs))==NULL)
return;
if (GSisCreateDate(gs))
tstamp = statbuf.st_ctime;
else
tstamp = statbuf.st_mtime?statbuf.st_mtime:statbuf.st_ctime;
GSsetTitle(gs,VMS$FormatTokens(GSgetTitle(gs), fp+1, &statbuf.st_size,
&tstamp, GSgetPort(gs),GSgetHost(gs), GSgetType(gs),
GSgetAdmin(gs),cmd));
#endif
}
/*
* Add a DL description if it's there ...
*/
void
GStitlefromDL(gs, filename)
GopherObj *gs;
char *filename;
{
#ifdef DL
char dlpath[2]; /*** for DL**/
char *dlout;
/* Process a "dl" description if there is one! */
dlpath[0] = '.';
dlpath[1] = '\0';
dlout = getdesc(NULL,dlpath,filename,0);
Debug("dl: %s", dlpath);
Debug(" %s", filename);
Debug(" %s\n", dlout);
if (dlout != NULL) {
GSsetTitle(gs, dlout);
}
#endif
;
}
#ifndef VMS_SERVER
void
GSfromCapfile(gs, filename)
GopherObj *gs;
char *filename;
{
#ifdef CAPFILES
char capfile[MAXPATHLEN];
FILE *SideFile;
strcpy(capfile,".cap/");
strcat(capfile, filename);
if ((SideFile = rfopen(capfile, "r"))!=0) {
Debug("cap file name: %s\n", capfile);
(void) Process_Side(SideFile, gs);
fclose (SideFile);
}
#endif
;
}
/*
* Run scripts that generate blocks...
*/
GSrunScripts(gs, filename)
GopherObj *gs;
char *filename;
{
FileIO *fio;
char tmpstr[256];
char *bname, *sname;
int i, blocks;
blocks = GDCnumBlkScripts(Config);
for (i=0; i < blocks; i++) {
bname = GDCgetBlkName(Config, i);
sname = GDCgetBlkScript(Config, i);
if (GSfindBlock(gs, bname) == NULL) {
*tmpstr = '\0';
if (!dochroot)
#ifndef VMS_SERVER
strcpy(tmpstr, Data_Dir);
#else
strcpy(tmpstr, (char *)Data_Dir);
#endif
strcat(tmpstr, sname);
strcat(tmpstr, " ");
strcat(tmpstr, filename);
fio = FIOopenCmdline(tmpstr, "r");
if (fio == NULL)
return;
while (FIOreadlinezap(fio, tmpstr,256)) {
GSsetBlock(gs, bname, tmpstr, TRUE);
}
FIOclose(fio);
}
}
}
#endif
void
GSsetDefaults(gs)
GopherObj *gs;
{
GSinit(gs);
GSsetHost(gs, Zehostname);
GSsetPort(gs, GopherPort);
GSsetGplus(gs, TRUE);
GSsetType(gs, '\0');
}
/*
* Load up a gopher directory from the file system given a directory
*/
GopherDirObj *
GDfromUFS(pathname, sockfd, isGplus)
char *pathname;
int sockfd;
boolean isGplus;
{
DIR *ZeDir;
char filename[256];
static char newpath[512];
static GopherObj *Gopherstow = NULL;
static Extobj *extstow = NULL;
Extobj *ext;
GopherObj *gs;
#ifndef VMS_SERVER
struct dirent *dp;
#else
char *dp;
GDCobj *Local = NULL;
#endif
GopherDirObj *gd;
struct stat statbuf;
boolean AddItem = TRUE;
static char Pathp[512];
StrArray *Linkfiles;
int i;
Debug("GDfromUFS:%s\r\n",pathname);
Debug("GDfromUFS:Config=%d\r\n",Config);
/*** Initialize static memory... ****/
if (Gopherstow == NULL) {
Gopherstow = GSnew();
}
if (isGplus && GSgplusInited(Gopherstow) == FALSE)
GSplusnew(Gopherstow);
if (extstow == NULL)
ext = extstow = EXnew();
else
ext = extstow;
gs = Gopherstow;
if (rchdir(pathname)<0) {
return(NULL);
}
gd = GDnew(32);
#ifndef VMS_SERVER
if (GDCgetCaching(Config) &&
Cachetimedout(".cache+", GDCgetCachetime(Config), ".")==FALSE) {
int cachefd;
if ((cachefd = ropen(".cache+", O_RDONLY)) >=0) {
GDplusfromNet(gd, cachefd, NULL);
close(cachefd);
return(gd);
}
}
#endif
/* open "." since we just moved there - makes it work when not
chroot()ing and using relative paths */
if ((ZeDir = uopendir(".")) == NULL) {
char tmpstr[256];
sprintf(tmpstr, "Cannot access directory '%s'", pathname);
#ifdef VMS_SERVER
SetAbrtFile(BaddirMsg, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
Abortoutput(sockfd, tmpstr);
GDdestroy(gd); /* Note Unix had this memory leak */
return(NULL);
}
#ifndef VMS_SERVER
Linkfiles = STAnew(10); if (Linkfiles==NULL) return(NULL);
#endif
for (dp = readdir(ZeDir); dp != NULL; dp = readdir(ZeDir)) {
strcpy(newpath, pathname);
#ifndef VMS_SERVER
strcpy(filename, dp->d_name);
#else
strcpy(filename, dp);
#endif
#ifndef VMS_SERVER
if (newpath[strlen(newpath)-1] != '/')
strcat(newpath, "/");
strcat(newpath, dp->d_name);
#else
strcat(newpath, dp);
#endif
gs = Gopherstow;
GSinit(gs);
#ifndef VMS_SERVER
/*********************************************************/
/*** Ignored file ***/
if (strcmp(filename, ".")==0 || strcmp(filename, "..")==0 ||
strncmp(filename, ".cache", 6) ==0 ||
strcmp(filename, ".about.html") == 0 ||
GDCignore(Config,filename))
continue;
/*********************************************************/
/*** This is a link file, process it after other files ***/
else if (filename[0] == '.' && isadir(filename)==0) {
String *temp;
temp = STRnew();
STRset(temp, filename);
STApush(Linkfiles, temp);
STRdestroy(temp);
continue;
}
/*********************************************************/
/*** directory starting with a period ***/
else if (filename[0] == '.') {
continue;
}
#else
/************************************************************/
/*** This is a link file, process it *before* other files ***/
if (GDCisLink(Local?Local:Config,filename)
&& isadir(filename)==0
&& strncasecmp(filename, ".cache", 6) !=0) {
FileIO *fio;
if (fio = FIOopenUFS(filename, O_RDONLY, 0)) {
if (!Local)
Local = GDCcpy(Config);
GDCfromLink(Local, fio, '=');
GDfromLink(gd, fio, Zehostname, GopherPort, pathname,
CurrentPeerName, sockfd, ACC_BROWSE);
FIOclose(fio);
switch(GDCcanAccess(Local, CurrentPeerName, CurrentPeerIP,
ActiveSessions, ACC_BROWSE)) {
case SITE_NOACCESS:
case SITE_TOOBUSY:
GDCdestroy(Local);
GDdestroy(gd);
return(NULL);
case SITE_OK:
case SITE_UNDEF:
default: ;
}
}
continue;
}
/*********************************************************/
/*** Ignored file ***/
else if (GDCisHidden(Local?Local:Config,filename)
|| GDCgetIgnoreAll(Local?Local:Config)
|| GDCignore(Local?Local:Config, filename)) {
/* Ignore this file */
continue;
}
/* Possibly apply Access Restrictions here */
#endif
/*********************************************************/
/*** Gopher+ block file ***/
#ifndef VMS_SERVER
else if (GDCBlockExtension(Config, filename, ext))
#else
else if (GDCBlockExtension(Local?Local:Config, filename, ext))
#endif
{
char Selstr[512];
char *tmpstr = Selstr;
int num;
*Selstr = '0'; strcpy(Selstr+1, newpath);
/** Strip off the extension from the path **/
tmpstr[strlen(tmpstr) - strlen(EXgetExt(ext))]='\0';
num = GDSearch(gd, tmpstr);
if (num != -1) {
gs = GDgetEntry(gd, num);
AddItem = FALSE;
} else {
GSsetDefaults(gs);
GSsetPath(gs, tmpstr);
}
if (strcasecmp(EXgetBlockname(ext), "ASK") == 0)
GSsetAsk(gs, TRUE);
GSaddBlock(gs, EXgetBlockname(ext), fixfile(newpath));
}
/*********************************************************/
/*** Some kind of data file ***/
else {
ustat(filename, &statbuf);
/* Strip any Decoder extensions from newpath before processing
them, filename needs to remain as is for type checking*/
#ifndef VMS_SERVER
if (EXAcasedSearch(Config->Extensions, ext, filename,
#else
if (EXAcasedSearch(Local?Local->Extensions:
Config->Extensions, ext, filename,
#endif
EXT_DECODER)) {
newpath[strlen(newpath) - strlen(EXgetExt(ext))] = '\0';
filename[strlen(filename) - strlen(EXgetExt(ext))] = '\0';
}
/*** Add views to item... ***/
#ifndef VMS_SERVER
if (GDCViewExtension(Config, filename, &ext))
#else
if (GDCViewExtension(Local?Local:Config, filename, &ext))
#endif
{
char *Prefix;
int num;
Prefix = EXgetPrefix(ext);
strcpy(Pathp, Prefix);
strcpy(Pathp+strlen(Prefix), newpath);
/*** Strip extension off of pathname***/
Pathp[strlen(Prefix)+strlen(newpath)-strlen(EXgetExt(ext))]= '\0';
/*** Strip extension off of title***/
filename[strlen(filename)-strlen(EXgetExt(ext))]= '\0';
/*** search for existing entry to add view to **/
num = GDSearch(gd, Pathp);
if (num != -1) {
gs = GDgetEntry(gd, num);
AddItem = FALSE;
} else {
GSsetDefaults(gs);
}
#ifndef VMS_SERVER
GSsetTitle(gs, filename);
#else
/* leave title blank, fix it in GDpostprocVMS() */
#endif
GSsetPath(gs, Pathp);
GSsetType(gs, EXgetObjtype(ext));
/** Oh say can we hack, by the dawns early day :-) **/
if (strcasecmp(EXgetExt(ext), ".mindex")==0) {
GSsetGplus(gs, FALSE);
}
if (isGplus) {
char *lang;
lang = EXgetVLang(ext);
if (lang == NULL || strcmp(lang, "")==0)
#ifndef VMS_SERVER
lang = GDCgetLang(Config);
#else
lang = GDCgetLang(Local?Local:Config);
#endif
GSaddView(gs, EXgetView(ext), lang, statbuf.st_size);
}
}
/** Mystery file without known extension ***/
else {
char Selstr[512];
int num;
strcpy(Selstr+1, newpath);
*Selstr = '0';
num = GDSearch(gd, Selstr);
if (num != -1) {
gs = GDgetEntry(gd, num);
AddItem = FALSE;
} else {
GSsetDefaults(gs);
}
Getfiletypes(newpath ,filename, gs);
if (GSgetType(gs) == '3')
continue;
#ifndef VMS_SERVER
GSsetTitle(gs, filename);
#else
/* leave title blank, fix it in GDpostprocVMS() */
#endif
if (strncmp(GSgetPath(gs), "validate ", 9) == 0) {
/** Ugly hack for now... **/
#if defined(VMS_SERVER) && defined(__VAXC)
char **askb;
int i;
char *cp = GSgetPath(gs);
askb = GDCauthAsk(Local?Local:Config, cp+10);
#else
char **askb = GDCauthAsk(Config, GSgetPath(gs)+10);
int i;
#endif
GSsetAsk(gs, TRUE);
for (i=0; askb[i] != NULL; i++) {
GSsetBlock(gs, "ASK", askb[i], TRUE);
}
}
if (GSisGplus(gs) && isGplus)
AddDefaultView(gs, statbuf.st_size, NULL);
}
#ifndef VMS_SERVER
/** Check DL database for a good name**/
GStitlefromDL(gs, filename);
if (GSgetTitle(gs) == NULL)
GSsetTitle(gs, filename);
GSaddDateNsize(gs, statbuf);
GSfromCapfile(gs, dp->d_name);
#else
/* leave title blank, fix it in GDpostprocVMS() */
#endif
if (GSgetType(gs) == '3' || GSgetType(gs) == '-')
continue;
}
/*** Here we go, we have either a filled in block or data item ***/
if (isGplus && AddItem) {
char tmpstr[256];
char timeval[16];
struct tm *tmthing;
char *cp;
if (GSgplusInited(gs) == FALSE)
GSplusnew(gs);
/*** Add admin, abstract entries, etal ***/
if (!GSgetAdmin(gs)) {
sprintf(tmpstr, "%s <%s>",
#ifndef VMS_SERVER
GDCgetAdmin(Config), GDCgetAdminEmail(Config));
#else
GDCgetAdmin(Local?Local:Config),
GDCgetAdminEmail(Local?Local:Config));
#endif
GSsetAdmin(gs, tmpstr);
}
if (GSgetModDate(gs) == NULL) {
/** Set mod date entry **/
tmthing = localtime(&(statbuf.st_mtime));
strftime(timeval,sizeof(timeval), "%Y%m%d%H%M%S", tmthing);
sprintf(tmpstr,"%s<%s>", asctime(tmthing),timeval);
cp = strchr(tmpstr, '\n');
if (cp != NULL)
*cp = ' ';
GSsetModDate(gs, tmpstr);
}
}
/*** Add the entry to the directory ***/
if (AddItem) {
GDaddGS(gd, gs);
} else
AddItem = TRUE;
}
#ifndef VMS_SERVER
/*** We don't have links yet, so we can process the BlockScripts ***/
if (isGplus) {
char *cp;
for (i=0; i < GDgetNumitems(gd); i++) {
gs = GDgetEntry(gd, i);
cp = strrchr(GSgetPath(gs), '/');
if (cp != NULL)
GSrunScripts(gs, cp+1);
}
}
/** Process .Link files **/
for (i=0 ; i<STAgetTop(Linkfiles); i++) {
FileIO *fio;
fio = FIOopenUFS(STAgetText(Linkfiles,i), O_RDONLY, 0);
if (fio != NULL) {
GDfromLink(gd, fio, Zehostname, GopherPort, pathname, CurrentPeerName);
FIOclose(fio);
}
}
closedir(ZeDir);
GDsort(gd);
STAdestroy(Linkfiles);
#else
if (GDgetNumitems(gd)) {
GDpostprocVMS(gd,Local?Local:Config,GopherPort,isGplus);
GDaddDateNsize(gd);
if (GDCgetSortDir(Local?Local:Config))
GDsort(gd);
}
if (Local)
GDCdestroy(Local);
Local = NULL;
#endif
return(gd);
}
/* Misleading title - its actually loading a GD from the parent of cmd*/
GopherDirObj *
GDfromSelstr(cmd,sockfd)
CMDobj *cmd;
int sockfd;
{
char *it = NULL;
char *cp;
GopherDirObj *gd;
char directory[512];
#ifdef VMS_SERVER
GopherObj *gs;
#endif
it = CMDgetFile(cmd);
if (it == NULL)
return(NULL);
else
strcpy(directory, it);
#ifndef VMS_SERVER
cp = strrchr(directory, '/');
if (cp != NULL)
*(cp+1) = '\0';
#else
if (cp = strrchr(directory, ']')) { /* device:[...]filename */
*(cp+1) = '\0'; /* discard "]filename" */
if (strcmp(directory,it)==0) {
/* Was just directory to start, so back up 1 directory.... */
if (cp = strrchr(directory,'.'))
strcpy(cp,"]");
else {
if (cp=strrchr(directory,'['))
strcpy(cp,"[000000]");
else
return(NULL); /* Bad syntax... */
}
}
}
else { /* device:filename */
if (cp = strrchr(directory,':'))
strcpy(cp+1,"[000000]"); /* Just device: */
else /* No [...] *or* device */
strcpy(directory,GDCgetDatadir(Config));
}
if (gs=VMS$VFLAGS(sockfd, directory, ACC_BROWSE, A_DIRECTORY))
GSdestroy(gs);
else
goto no_access;
#endif
if (rchdir(directory)<0) {
char tmpstr[512];
if (isadir(directory)==0)
return(NULL);
#ifdef VMS_SERVER
no_access:
SetAbrtFile(BaddirMsg, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
sprintf(tmpstr, "- Cannot access directory '%s'", directory);
Abortoutput(sockfd, tmpstr);
return(NULL);
}
gd = GDfromUFS(directory, sockfd, TRUE); /** Returns NULL if error **/
return(gd);
}
/*
* Send item information to client
*/
void
item_info(cmd, sockfd)
CMDobj *cmd;
int sockfd;
{
GopherDirObj *gd = NULL;
GopherObj *gs = NULL;
int num;
struct stat statbuf;
char tmpstr[256];
char *cp;
cp = CMDgetSelstr(cmd);
num = strlen(cp);
/* If the selstr starts with ftp: hand it off to the gateway code */
if (strncmp(cp, "ftp:", 4) == 0) {
GopherFTPgw(sockfd, cp+4, cmd);
return;
}
/* If the selstr ends in '/', strip it off. */
if ( num != 0 && *(cp + num - 1) == '/' )
*(cp + num - 1) = '\0';
/** For now, strip off first character and find the directory above **/
Debug("Item info for %s\n", cp);
if ((*cp == '/') || (*cp == '\0') || (*(cp+1) == '\0')) {
gs = GSnew();
GSsetDefaults(gs);
GSsetPath(gs, "");
GSsetTitle(gs, GDCgetSite(Config));
GSsetType(gs, '1');
GSplusnew(gs);
GSsendHeader(sockfd, -1);
writestring(sockfd, "+INFO ");
GStoNet(gs,sockfd, GSFORM_G0, Gticket);
sprintf(tmpstr, "+ADMIN:\r\n Admin: %s <%s>\r\n", GDCgetAdmin(Config),
GDCgetAdminEmail(Config));
writestring(sockfd, tmpstr);
if (GSgetModDate(gs) == NULL) {
if (rstat("/", &statbuf) == 0) {
char timeval[16];
struct tm *tmthing;
tmthing = localtime(&(statbuf.st_mtime));
strftime(timeval,sizeof(timeval), "%Y%m%d%H%M%S", tmthing);
sprintf(tmpstr," Mod-Date: %s<%s>\r\n",
asctime(tmthing),timeval);
cp = strchr(tmpstr, '\n');
if (cp != NULL)
*cp = ' ';
writestring(sockfd, tmpstr);
}
} else {
sprintf(tmpstr, " Mod-Date: %s\r\n", GSgetModDate(gs));
writestring(sockfd, tmpstr);
}
if (GSgetTTL(gs) > -1) {
sprintf(tmpstr, " TTL: %d\r\n", GSgetTTL(gs));
} else {
sprintf(tmpstr, " TTL: %d\r\n", GDCgetCachetime(Config));
}
writestring(sockfd, tmpstr);
sprintf(tmpstr, " Site: %s\r\n", GDCgetSite(Config));
writestring(sockfd, tmpstr);
sprintf(tmpstr, " Org: %s\r\n", GDCgetOrg(Config));
writestring(sockfd, tmpstr);
sprintf(tmpstr, " Loc: %s\r\n", GDCgetLoc(Config));
writestring(sockfd, tmpstr);
sprintf(tmpstr, " Geog: %s\r\n", GDCgetGeog(Config));
writestring(sockfd, tmpstr);
#ifndef VMS_SERVER
sprintf(tmpstr, " Version: U of Minnesota Unix %s.%s pl%d\r\n",
#else
sprintf(tmpstr,
" Version: U of Minnesota / VMSGopher-L OpenVMS %s.%s-%d\r\n",
#endif
GOPHER_MAJOR_VERSION, GOPHER_MINOR_VERSION, PATCHLEVEL);
writestring(sockfd, tmpstr);
if (Connections != 0) {
time_t howlong = time(NULL) - ServerStarted + 1;
int connperhour = (Connections * 3600) / howlong;
char *started = ctime(&ServerStarted);
*(started+24) = '\0';
sprintf(tmpstr, "+STATISTICS:\r\n Total Connections: %d\r\n Server Started: %s\r\n Connections per Hour: %d\r\n Concurrent Sessions: %d\r\n",
Connections, started, connperhour, ActiveSessions);
writestring(sockfd, tmpstr);
}
if (GDCgetAbstract(Config) != NULL) {
char abline[256];
char *cp, *nl;
writestring(sockfd, "+ABSTRACT:\r\n");
nl = cp = GDCgetAbstract(Config);
while (nl) {
nl = strchr(cp, '\n');
writestring (sockfd, " ");
if (nl == NULL)
writestring(sockfd, cp);
else {
strncpy(abline, cp, (nl-cp));
abline[nl-cp] = '\0';
writestring(sockfd, abline);
cp = nl+1;
}
writestring(sockfd, "\r\n");
}
}
writestring(sockfd, "+VERONICA:\r\n treewalk:");
if (GDCgetShouldIndex(Config) == TRUE)
writestring(sockfd, " yes");
else
writestring(sockfd, " no");
writestring(sockfd, "\r\n+VIEWS:\r\n");
#ifndef VMS_SERVER
if (GDCgetCaching(Config)) {
if (rstat("/.cache", &statbuf) == 0) {
sprintf(tmpstr, " application/gopher-menu %s: <%dk>\r\n",
GDCgetLang(Config), (statbuf.st_size + 512)/1024);
writestring(sockfd, tmpstr);
} else {
writestring(sockfd, " application/gopher-menu: <0k>\r\n");
}
if (rstat("/.cache+", &statbuf) == 0) {
sprintf(tmpstr, " application/gopher+-menu %s: <%dk>\r\n",
GDCgetLang(Config), (statbuf.st_size + 512)/1024);
writestring(sockfd, tmpstr);
} else {
writestring(sockfd, " application/gopher+-menu: <0k>\r\n");
}
} else {
writestring(sockfd, " application/gopher-menu: <0k>\r\n");
writestring(sockfd, " application/gopher+-menu: <0k>\r\n");
}
#else
writestring(sockfd, " application/gopher-menu: <0k>\r\n");
writestring(sockfd, " application/gopher+-menu: <0k>\r\n");
#endif
writestring(sockfd, " text/html: <0k>\r\n");
#ifndef VMS_SERVER
writestring(sockfd, " Directory/recursive: <0k>\r\n");
writestring(sockfd, " Directory+/recursive: <0k>\r\n");
#endif
GSdestroy(gs);
}
#ifndef NO_AUTHENTICATION
else if (strncmp(CMDgetSelstr(cmd), "validate ",9) == 0) {
gd = GDfromSelstr(cmd,sockfd);
num = GDSearch(gd, CMDgetSelstr(cmd)+9);
uchdir("/");
if (num < 0 || gd == NULL) {
GplusError(sockfd, 1, "Cannot find item information for that item", NULL);
return;
}
else {
/* Find the authenticator associated with the directory/item **/
char *selstr = CMDgetSelstr(cmd);
char **askb = GDCauthAsk(Config, CMDgetFile(cmd));
int i;
gs = GDgetEntry(gd, num);
GSsetPath(gs, selstr);
Debug("Askblock is %s\n", askb[0]);
GSsetAsk(gs, TRUE);
for (i=0; askb[i] != NULL; i++) {
GSsetBlock(gs, "ASK", askb[i], TRUE);
}
DebugGSplusPrint(gs,"got here...");
GSsendHeader(sockfd, -1);
GSplustoNet(gs, sockfd, NULL, "");
}
}
#endif /* NO_AUTHENTICATION */
else {
gd = GDfromSelstr(cmd,sockfd);
num = GDSearch(gd, CMDgetSelstr(cmd));
uchdir("/");
if (num < 0 || gd == NULL) {
GplusError(sockfd, 1, "Cannot find item information for that item", NULL);
return;
}
else {
gs = GDgetEntry(gd, num);
GSsendHeader(sockfd, -1);
GSplustoNet(gs, sockfd, NULL, Gticket);
}
}
if (gd)
GDdestroy(gd);
writestring(sockfd, ".\r\n");
}
/*
* Output HTML for cutesy icons
*/
GSicontoNet(gs, sockfd)
GopherObj *gs;
int sockfd;
{
char iconfile[64];
int fd;
if (gs==NULL)
return;
sprintf(iconfile, "/lib/htmlicon.%c", GSgetType(gs));
if ((fd = ropen(iconfile, O_RDONLY)) >= 0) {
char imgline[128];
sprintf(imgline, "<IMG ALIGN=absbottom BORDER=0 SRC=gopher://%s:%d/I9%s>",
Zehostname, GopherPort, iconfile);
writestring(sockfd, imgline);
close(fd);
}
}
/*
** This function lists out what is in a particular directory.
** it also outputs the contents of link files.
**
** It also checks for the existance of a .cache file if caching is
** turned on...
**
** Ack is this ugly.
*/
void
listdir(sockfd, pathname, isgplus, view, filter)
int sockfd;
char *pathname;
boolean isgplus;
char *view;
char *filter;
{
GopherDirObj *gd = NULL;
boolean attrlist = FALSE;
boolean HTMLit = FALSE;
boolean Recurse = FALSE;
char *filtereddata[16], **filtered=filtereddata;
int i=0;
#ifndef VMS_SERVER
StrArray *RecurseDirs = NULL;
String *stapath;
#endif
int result;
#ifdef VMS_SERVER
GopherStruct *Vgs = NULL;
char *Vpath;
char *cp;
char *c2;
Vpath = (char *)malloc((strlen(pathname)*2)+strlen("[000000.dir]")+2);
*Vpath = A_DIRECTORY;
strcpy (Vpath+1, pathname);
if (cp = strrchr(Vpath, ']')) {
if (strlen(cp)>1)
goto bad_dir;
*cp = '\0';
if (cp = strrchr(Vpath, '.')) { /* we had [path.path] */
*cp = ']';
strcat(Vpath, ".dir"); /* change to [path]path.dir */
}
else
if (cp = strrchr(Vpath, '[')) { /* we had [path] */
c2=strcpy(Vpath+strlen(Vpath)+5, cp+1);
strcpy(cp,"[000000]");
strcat(Vpath, c2);
strcat(Vpath, ".dir"); /* chng to [000000]path.dir */
}
}
else {
bad_dir:
LOGGopher(sockfd, "Invalid Directory Spec %s", pathname);
SetAbrtFile(SyntaxErr, NULL, KeepAbrtGS, "Invalid Filespec");
Abortoutput(sockfd, GDCgetBummerMsg(Config));
free(Vpath);
return;
}
Vgs = VMS$VFLAGS(sockfd, Vpath+1, ACC_BROWSE, A_DIRECTORY);
if (Vgs) {
strcpy(Vpath+1, pathname);
GSsetPath(Vgs, Vpath);
free(Vpath);
}
else {
free(Vpath);
goto no_access;
}
#endif
result = GDCCanBrowse(Config, CurrentPeerName, CurrentPeerIP,
ActiveSessions);
if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
no_access:
SetAbrtFile(BummerMsg, Vgs, DstrAbrtGS, AbortString);
#endif
Abortoutput(sockfd, GDCgetBummerMsg(Config));
return;
} else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
SetAbrtFile(TooBusy, Vgs, DstrAbrtGS, NULL);
#endif
Abortoutput(sockfd, "Sorry, too busy right now..");
return;
}
#ifndef VMS_SERVER
if (uchdir("/"))
perror("SOL dude");
#endif
if (filter != NULL) {
while (*filter != '\0') {
if (*filter=='+') {
*filter = '\0';
filtered[i] = filter+1;
filter++; i++;
}
filter++;
}
filtered[i] = NULL;
} else
filtered = NULL;
if (view != NULL) {
if (strncmp(view, "application/gopher+-menu",24) == 0)
attrlist = TRUE;
else if (strncmp(view, "text/html", 9) == 0) {
attrlist = TRUE;
HTMLit = TRUE;
}
#ifndef VMS_SERVER
else if (strncmp(view, "Directory+/recursive", 20)==0)
Recurse = TRUE;
else if (strncmp(view, "Directory/recursive", 19)==0)
Recurse = TRUE;
#endif
}
#ifndef VMS_SERVER
if (Recurse)
RecurseDirs = STAnew(32);
#endif
if (rchdir(pathname)<0) {
char tmpstr[512];
#ifdef UMNDES
if (errno == EACCES)
PleaseAuthenticate(sockfd);
#endif
sprintf(tmpstr, "- Cannot access directory '%s'", pathname);
#ifdef VMS_SERVER
SetAbrtFile(BaddirMsg, Vgs, DstrAbrtGS, NULL);
#endif
Abortoutput(sockfd, tmpstr);
return;
}
if (isgplus)
GSsendHeader(sockfd, -1);
do {
Debug("Sending %s\n", pathname);
#ifndef VMS_SERVER
if (Recurse) {
rchdir("/");
rchdir(pathname);
}
#endif
#ifndef VMS_SERVER
if (!attrlist && GDCgetCaching(Config) && !HTMLit &&
(Cachetimedout(".cache", GDCgetCachetime(Config), ".") ==FALSE)) {
/*** Old style cache ***/
send_binary(sockfd, ".cache", FALSE);
} else if (GDCgetCaching(Config) && !HTMLit &&
Cachetimedout(".cache+", GDCgetCachetime(Config)
, ".")==FALSE) {
/*** Gopher+ cache. ***/
if (strncmp(view, "application/gopher+-menu",24)==0)
send_binary(sockfd, ".cache+", FALSE);
else if (strncmp(view, "application/gopher-menu",23)==0)
send_binary(sockfd, ".cache", FALSE);
}
else
#endif
{
/** If we didn't cache then we have to
load up the directory **/
gd = GDfromUFS(pathname, sockfd, attrlist);
if (gd == NULL) {
/** Should generate an error message here **/
break;
}
#ifndef VMS_SERVER
rchdir("/");
#else
rchdir(pathname);
#endif
if (HTMLit) {
writestring(sockfd, "<HTML>\r\n<HEAD>\r\n");
writestring(sockfd, "<LINK rev=made href=\"mailto:");
writestring(sockfd, GDCgetAdminEmail(Config));
writestring(sockfd, "\">\r\n");
if (GDgetTitle(gd) != NULL) {
writestring(sockfd, "<TITLE>");
writestring(sockfd, GDgetTitle(gd));
writestring(sockfd, "</TITLE>");
}
writestring(sockfd, "</HEAD>\r\n<BODY>\r\n");
{
STATSTR statbuf;
char html_path[1024];
char *cp;
strcpy (html_path, pathname);
cp = html_path + strlen(html_path) - 1;
if (*cp != '/') {
*(++cp) = '/';
*(++cp) = '\0';
}
strcat (html_path, ".about.html");
if (rstat(html_path, &statbuf) == 0)
send_binary(sockfd, html_path, FALSE);
else if (strcmp(pathname, "/") == 0) {
writestring(sockfd, "<H1>");
writestring(sockfd, GDCgetSite(Config));
writestring(sockfd, "</H1>\r\n<H2>");
writestring(sockfd, GDCgetOrg(Config));
writestring(sockfd, "</H2>\r\n");
}
}
GDtoNet(gd, sockfd, GSFORM_HTML, Gticket, &GSicontoNet);
writestring(sockfd, "</BODY>\r\n</HTML>\r\n");
}
else if (attrlist)
GDplustoNet(gd, sockfd,filtered, Gticket);
else
GDtoNet(gd, sockfd, GSFORM_G0, Gticket, NULL);
}
#ifndef VMS_SERVER
/*
* Write out the cache... After we send out the data to the net.
*/
if (GDCgetCaching(Config) && gd != NULL) {
int cachefd;
char cachefile[MAXPATHLEN];
strcpy(cachefile, pathname);
strcat(cachefile, "/.cache");
cachefd = ropen(cachefile, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (cachefd >= 0) {
Debug("Caching directory... into %s\n",cachefile);
GDtoNet(gd, cachefd, GSFORM_G0, NULL, NULL);
close(cachefd);
}
if (attrlist) {
strcat(cachefile, "+");
cachefd = ropen(cachefile, O_WRONLY|O_CREAT|O_TRUNC,0644);
if (cachefd >= 0) {
GDplustoNet(gd, cachefd,filtered, "");
close(cachefd);
}
}
}
#endif
#ifndef VMS_SERVER
if (Recurse) {
GopherObj *gs;
String *pushstring = STRnew();
/** Push entries on the stack **/
for (i=0; i< GDgetNumitems(gd); i++) {
STATSTR stbuf;
char *cp;
gs = GDgetEntry(gd, i);
if ((GSgetType(gs) == A_DIRECTORY) &&
(strcmp(GSgetHost(gs), Zehostname) == 0)) {
STRset(pushstring, GSgetPath(gs));
rchdir("/");
cp = STRget(pushstring);
if (rstat(cp+1, &stbuf) == 0)
STApush(RecurseDirs, pushstring);
}
}
do {
stapath = STApop(RecurseDirs);
if (stapath == NULL) {
Recurse = FALSE; /** Done **/
break;
}
pathname = STRget(stapath);
#ifndef VMS_SERVER
if (*pathname == 'm')
process_mailfile(sockfd, pathname+1);
#endif
} while (*pathname == 'm');
pathname++;
if (gd != NULL) {
GDdestroy(gd);
gd = NULL;
}
STRdestroy(pushstring);
}
#endif
} while (Recurse);
writestring(sockfd, ".\r\n");
GDdestroy(gd);
}
/*
* This processes a file containing any subset of
* Type, Name, Path, Port or Host, and returns pointers to the
* overriding data that it finds.
*
* The caller may choose to initialise the pointers - so we don't
* touch them unless we find an over-ride.
*/
#ifndef VMS_SERVER
int
Process_Side(sidefile, Gopherp)
#else
int
Process_Side(sidefile, Gopherp, host, port, filename)
char *host;
int port;
char *filename;
#endif
FILE *sidefile;
GopherObj *Gopherp;
{
char inputline[MAXLINE];
char *cp;
int retval = TRUE;
inputline[0] = '\0';
for (;;) {
for (;;) {
cp = fgets(inputline, 1024, sidefile);
if (inputline[0] != '#' || cp == NULL)
break;
}
/*** Test for EOF ***/
if (cp==NULL)
break;
ZapCRLF(inputline); /* should zap tabs as well! */
#ifndef VMS_SERVER
/*** Test for the various field values. **/
if (strncmp(inputline, "Type=", 5)==0) {
GSsetType(Gopherp, inputline[5]);
if (inputline[5] == '7') {
/*** Might as well set the path too... ***/
cp = GSgetPath(Gopherp);
*cp = '7';
} else if (inputline[5] == '9') {
/*** Might as well set the path too... ***/
cp = GSgetPath(Gopherp);
*cp = '9';
} else if (inputline[5] == '-')
retval = FALSE;
}
else if (strncmp(inputline, "Name=", 5)==0) {
GSsetTitle(Gopherp, inputline+5);
}
else if (strncmp(inputline, "Host=", 5)==0) {
GSsetHost(Gopherp, inputline+5);
}
else if (strncmp(inputline, "Port=", 5)==0) {
GSsetPort(Gopherp, atoi(inputline+5));
}
else if (strncmp(inputline, "Path=", 5)==0) {
GSsetPath(Gopherp, inputline+5);
}
else if (strncmp(inputline, "Numb=", 5)==0) {
GSsetNum(Gopherp, atoi(inputline+5));
}
else if (strncmp(inputline, "Name=", 5)==0) {
GSsetTitle(Gopherp, inputline+5);
}
#else
VMS$Continuation(inputline,sidefile,1024,'-');
Process_Sideline(inputline, Gopherp, host, port, filename);
if (GSgetType(Gopherp) == '-')
retval = FALSE;
#endif
}
#ifdef VMS_SERVER
if (GSgetPort(Gopherp)==0) {
char *cp;
if (GSgetPath(Gopherp))
switch(*(cp=GSgetPath(Gopherp)))
{
case A_INDEX: GSsetPort(Gopherp, GDCgetSRCHPort(Config)); break;
case A_FTP: GSsetPort(Gopherp, GDCgetFTPPort(Config)); break;
case A_EXEC: GSsetPort(Gopherp, GDCgetEXECPort(Config)); break;
default: GSsetPort(Gopherp,port);
}
else
GSsetPort(Gopherp,port);
}
GSsetLookaside(Gopherp,TRUE);
#endif
return(retval);
}
#ifdef VMS_SERVER
/*
* This produces a lookaside filename given a path and a filename.
*
* For VMS, the application ACE's (if any) are tested to locate any
* GOPHER_ACE entries; if present, these are processed, and NULL is
* returned to indicate no lookaside file is to be processed, even if
* one exists.
*/
FILE *
Build_Lookaside(char *path, char *file, GopherStruct *gs)
{
FILE *SideFile;
char sidename[256];
char *cp;
if (!path)
return(NULL);
if (GSgetLookaside(gs))
return(NULL);
if (ACL_Lookaside(path, gs, GDCgetHostname(Config),
GDCgetPort(Config))) {
return(NULL);
}
if (file==NULL) {
file = path;
path = "[]";
}
strcpy(sidename, path);
if (cp=strchr(sidename,']'))
*cp = '\0';
if (sidename[strlen(sidename)-1]!='[')
strcat(sidename, ".");
strcat(sidename, GDCgetLookAside(Config));
strcat(sidename, "]");
strcat(sidename, file);
if ((SideFile = fopen_VMSopt(sidename, "r"))!=0)
Debug("Side file name: %s\n", sidename);
return(SideFile);
}
/*
* This tests for Application ACE entries in the file's Access Control List
* which match the GOPHER_ACE value. If any, store them into the GopherStruct
* supplied and return TRUE; otherwise return FALSE.
*/
#include <atrdef.h>
#include <acedef.h>
int
ACL_Lookaside(char *file, GopherStruct *gs, char *host, int port)
{
int ACL_present=FALSE; /* Status */
char *acl_ptr; /* Pointer to acl string */
char *gopher$; /* Pointer to GOPHER character data */
int g; /* Length of GOPHER character data */
struct acedef
*ace$; /* Pointer to actual ACE */
int status; /* system status */
int acl_length; /* ACL return length */
char acl_buffer[ATR$S_READACL]; /* ACL work buffer */
struct dsc$descriptor_s
acl_entry = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
struct FAB
acl_fab;
struct XABPRO
acl_xab;
if (!file)
return(ACL_present);
acl_fab = cc$rms_fab;
acl_xab = cc$rms_xabpro;
acl_fab.fab$l_fna = file;
acl_fab.fab$b_fns = strlen(acl_fab.fab$l_fna);
if (((status = SYS$OPEN(&acl_fab, 0, 0)) &1) != 1)
return(ACL_present);
/*
Connect the XAB to the FAB block and perform an initial $DISPLAY.
*/
acl_fab.fab$l_xab = (char *) &acl_xab;
acl_xab.xab$l_aclbuf = acl_buffer;
acl_xab.xab$w_aclsiz = ATR$S_READACL;
acl_xab.xab$l_aclctx = 0;
if (!(1 & sys$display(&acl_fab, 0, 0)))
goto close_fab;
/*
While we actually have an ACL and the ACL lookup was correct, we
run through the ACL list within the ACL buffer and format each ACE.
Since the buffer is relatively small, keep '$DISPLAY'ing the
file until there is no more ACL entries to find. The first $DISPLAY
was done when the file was $OPENed, the next $DISPLAY reads XAB$L_ACLCXT
to get subsequent ACL entries for the buffer.
*/
while ((acl_xab.xab$w_acllen != 0)) {
acl_ptr = acl_buffer; /* Beginning of the ACLs */
while((*acl_ptr != 0) && ((acl_ptr - acl_buffer) < ATR$S_READACL))
{ /* The first byte is the size of the ACL */
ace$ = (struct acedef *)acl_ptr;
if ((ace$->ace$b_type==ACE$C_INFO) &&
(strncmp((char *)&(ace$->ace$l_access),
GOPHER_ACE,strlen(GOPHER_ACE))==0)) {
g = ace$->ace$b_size - 4 - strlen(GOPHER_ACE);
gopher$ = (char *)malloc(sizeof(char)*g+1);
memcpy(gopher$,
((char *)&(ace$->ace$l_access))+strlen(GOPHER_ACE) ,g);
*(gopher$+g) = '\0';
Process_Sideline(gopher$, gs, host, port, "(ACL)");
free(gopher$);
ACL_present = TRUE;
}
acl_ptr = acl_ptr + *acl_ptr; /* Get the next ACL entry */
}
bzero(acl_buffer, ATR$S_READACL);
status = sys$display(&acl_fab, 0, 0);/* Get the next ACL block */
if (!(status&1))
goto close_fab;
if (!(acl_xab.xab$l_aclsts&1))
break;
}
close_fab:
sys$close(&acl_fab,0,0);
if (ACL_present && (GSgetPort(gs)==0)) {
char *cp;
if (GSgetPath(gs))
switch(*(cp=GSgetPath(gs)))
{
case A_INDEX: GSsetPort(gs, GDCgetSRCHPort(Config)); break;
case A_FTP: GSsetPort(gs, GDCgetFTPPort(Config)); break;
case A_EXEC: GSsetPort(gs, GDCgetEXECPort(Config)); break;
default: GSsetPort(gs,port);
}
else
GSsetPort(gs,port);
}
GSsetLookaside(gs,TRUE);
return(ACL_present);
}
/*
* This processes a line containing any of Type=, Name=, Path=, Port=,
* Host=, Numb=, Hidden or Access=, and overrides the supplied GopherStruct
* entries.
*/
Process_Sideline(char *inputline, GopherStruct *gs, char *host, int port,
char *filename)
{
Debug("%s:", filename);
Debug("%s\n", inputline);
/*** Test for the various field values. **/
if (strncasecmp(inputline, GS_TYPE, strlen(GS_TYPE))==0) {
char *cp;
GSsetType(gs, inputline[strlen(GS_TYPE)]);
if (inputline[strlen(GS_TYPE)] == A_INDEX) {
/*** Might as well set the path too... ***/
cp = GSgetPath(gs);
*cp = A_INDEX;
} else if (inputline[strlen(GS_TYPE)] == A_UNIXBIN) {
/*** Might as well set the path too... ***/
cp = GSgetPath(gs);
*cp = A_UNIXBIN;
}
}
else
if (strncasecmp(inputline, GS_NAME, strlen(GS_NAME))==0) {
GSsetTitle(gs, inputline+strlen(GS_NAME));
}
else
if (strncasecmp(inputline, GS_HOST, strlen(GS_HOST))==0) {
if ((inputline[strlen(GS_HOST)] == '+'
|| inputline[strlen(GS_HOST)] == '*')
&& inputline[strlen(GS_HOST)+1] == '\0')
GSsetHost(gs, host);
else
GSsetHost(gs, inputline+strlen(GS_HOST));
}
else
if (strncasecmp(inputline, GS_PORT, strlen(GS_PORT))==0) {
if ((inputline[strlen(GS_PORT)] == '+'
|| inputline[strlen(GS_PORT)] == '*')
&& inputline[strlen(GS_PORT)+1] == '\0')
GSsetPort(gs, 0);
else
GSsetPort(gs, atoi(inputline+strlen(GS_PORT)));
}
else
if (strncasecmp(inputline, GS_PATH, strlen(GS_PATH))==0) {
GSsetPath(gs, inputline+strlen(GS_PATH));
}
else
if (strncasecmp(inputline, GS_NUMB, strlen(GS_NUMB))==0) {
GSsetNum(gs, atoi(inputline+strlen(GS_NUMB)));
}
else if (strncmp(inputline, GS_ABSTRACT, strlen(GS_ABSTRACT))==0) {
GSsetAbstract(gs, inputline+strlen(GS_ABSTRACT));
}
else if (strncmp(inputline, GS_ADMIN, strlen(GS_ADMIN))==0) {
GSsetAdmin(gs, inputline+strlen(GS_ADMIN));
}
else
if (strncasecmp(inputline, GS_HDDN, strlen(GS_HDDN))==0) {
GSsetNum(gs, -99);
}
else
if (strncasecmp(inputline, GS_ACCS, strlen(GS_ACCS)) == 0) {
if (GSgetAccess(gs) == NULL)
GSsetAccess(gs, SiteArrayNew());
GSsetAccessSite(gs, inputline+strlen(GS_ACCS));
GSsetDefAcc(gs, SiteDefAccess(gs->Access));
if (GSgetDefAcc(gs) == ACC_UNKNOWN)
GSsetDefAcc(gs, ACC_FULL);
}
else
if (strncasecmp(inputline, GS_HEAD, strlen(GS_HEAD))==0) {
GSsetHeader(gs, inputline+strlen(GS_HEAD));
}
else
if (strncasecmp(inputline, GS_FOOT, strlen(GS_FOOT))==0) {
GSsetFooter(gs, inputline+strlen(GS_FOOT));
}
else
if (strncasecmp(inputline, GS_RHEAD, strlen(GS_RHEAD))==0) {
GSsetRHeader(gs, inputline+strlen(GS_RHEAD));
}
else
if (strncasecmp(inputline, GS_RFOOT, strlen(GS_RFOOT))==0) {
GSsetRFooter(gs, inputline+strlen(GS_RFOOT));
}
}
#endif
/* dgg++ test if file is ASK script */
boolean
IsAskfile( pathname)
char *pathname;
{
struct stat statbuf;
static char tmpfile[512];
strcpy(tmpfile, pathname);
strcat(tmpfile, ".ask"); /* hack -- should do fancy lookup in Config */
if (!rstat( tmpfile, &statbuf))
return TRUE;
else
return FALSE;
}
int
check_file(sockfd, filename, isGplus)
int sockfd;
char *filename;
boolean isGplus;
{
;
#ifdef VMS_SERVER
return(1);
#endif
}
/*
** This function opens the specified file, starts a zcat if needed,
** and barfs the file across the socket.
**
** It now also checks and sees if access is allowed
**
** This also used the global variable ASKfile
**
*/
void
printfile(sockfd, pathname, startbyte, endbyte, Gplus)
int sockfd;
char *pathname;
int startbyte, endbyte;
boolean Gplus;
{
FILE *ZeFile;
#ifndef VMS_SERVER
char inputline[MAXPATHLEN+4];
#else
char inputline[MAXLINE+4];
#endif
FILE *pp;
static char *writebuf = NULL;
static char *bufptr = NULL;
int len;
int result;
#ifndef VMS_SERVER
boolean dontbuffer = FALSE;
#else
boolean dontbuffer = TRUE;
boolean tried_support = FALSE;
GopherStruct *gs = NULL;
#define FromDoc "This is a section of the document "
if (gs = VMS$VFLAGS(sockfd, pathname, ACC_READ, *(pathname-1))) {
if (GSgetTitle(gs)==NULL)
GSsetTitle(gs,pathname);
if (GSgetHeader(gs)==NULL)
if (GDCgetDHead(Config)!=NULL)
GSsetHeader(gs, GDCgetDHead(Config));
if (GSgetFooter(gs)==NULL)
if (GDCgetDFoot(Config)!=NULL)
GSsetFooter(gs, GDCgetDFoot(Config));
GSaddDateNsize(gs);
if ((GSgetRHeader(gs)==NULL) && ((startbyte!=0) || (endbyte!=-1))) {
inputline[0] = '\0';
sprintf(inputline,"%s%s '%s'.\r\n\r\n", FromDoc,
(strlen(GSgetTitle(gs))>78-strlen(FromDoc))?"\r\n ":"",
GSgetTitle(gs));
GSsetRHeader(gs,inputline);
}
}
else {
startbyte = 0;
endbyte = -1;
goto no_access;
}
#endif
/*** Check and see if the peer has permissions to read files ***/
result = GDCCanRead(Config, CurrentPeerName, CurrentPeerIP, ActiveSessions);
if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
no_access:
SetAbrtFileIf(BummerMsg, gs, DstrAbrtGS, AbortString);
#endif
Abortoutput(sockfd, GDCgetBummerMsg(Config));
LOGGopher(sockfd,"Denied access for %s", pathname);
return;
} else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
SetAbrtFile(TooBusy, gs, DstrAbrtGS, NULL);
#endif
Abortoutput(sockfd, "Sorry, too busy right now. Try again in a few minutes");
return;
}
Debugmsg("starting printfile");
/* dgg++ patch to block ASK calls from bad clients */
if (IsAskfile(pathname)) {
if (!Gplus || !ASKfile) {
char errmsg[256];
if (!ASKfile)
sprintf(errmsg," Missing input data to ASK form\t\t\t\r\n");
else
sprintf(errmsg," This ASK form needs a gopher+ client\t\t\t\r\n");
#ifdef VMS_SERVER
SetAbrtFile(BumClient, gs, DstrAbrtGS, NULL);
#endif
Abortoutput(sockfd, errmsg);
return;
}
}
#ifdef VMS_SERVER
if (UsingHTML && strcmp(pathname, ".cache.html") != 0) {
writestring(sockfd, "<PRE>\r\n");
}
Try_Support:
#endif
if ( (ZeFile = rfopen(pathname, "r")) == NULL) {
/*
* The specified file does not exist
*/
char notexistline[256];
sprintf(notexistline, "'%s' does not exist!!", pathname);
#ifdef VMS_SERVER
if (!tried_support) {
/* if (a device or directory prefixes) break; */
/* if (isn't a .SCRIPT,.SHELL or .ASK) break */
tried_support = TRUE;
if (!uchdir(GDCgetSupportDir(Config)))
goto Try_Support;
}
SetAbrtFile(NoSuchFile, gs, DstrAbrtGS, NULL);
#endif
Abortoutput(sockfd, notexistline);
return;
}
if (writebuf == NULL) {
writebuf = (char *) malloc(4096 * sizeof(char));
bufptr = writebuf;
}
if (startbyte != 0)
fseek(ZeFile, startbyte, 0);
if ((pp = specialfile(sockfd, ZeFile, pathname))!=NULL) {
fclose(ZeFile);
ZeFile = pp;
dontbuffer = TRUE;
}
if (Gplus)
GSsendHeader(sockfd, -1);
#ifdef VMS_SERVER
if (gs) {
print_aux(sockfd, gs, GS_HEAD);
if (startbyte != 0)
print_aux(sockfd, gs, GS_RHEAD);
}
#endif
#ifndef VMS_SERVER
while (fgets(inputline, MAXPATHLEN, ZeFile) != NULL)
#else
(void) signal(SIGALRM,read_timeout); /* use for write timeouts too */
if (setjmp(env)) {
LOGGopher(sockfd,"writestring: Timed out!");
goto ByeBye;
}
while (fgets(inputline, MAXLINE, ZeFile) != NULL)
#endif
{
ZapCRLF(inputline);
/** Period on a line by itself, double it.. **/
if (*inputline == '.' && inputline[1] == '\0' && !EXECflag) {
inputline[1] = '.';
inputline[2] = '\0';
}
if ((int)strlen(inputline) < 512)
strcat(inputline, "\r\n");
len = strlen(inputline);
if (dontbuffer) {
alarm(WRITETIMEOUT);
writestring(sockfd, inputline);
alarm(0);
} else {
if (((bufptr-writebuf) + len+1) > 4096) {
/** Write out the buffer if it's too big to fit.. **/
(void) alarm(WRITETIMEOUT);
if (writestring(sockfd, writebuf))
#ifndef VMS_SERVER
LOGGopher(sockfd, "Client went away"), gopherd_exit(-1);
#else
{
LOGGopher(sockfd, "Client went away");
ByeBye:
(void)alarm(0);
(void) signal(SIGALRM,SIG_IGN);
Specialclose(ZeFile);
if (gs)
GSdestroy(gs);
return;
}
#endif
(void) alarm(0);
bufptr=writebuf;
}
strcpy(bufptr, inputline);
bufptr += len;
*bufptr = '\0';
}
if (endbyte >0) {
if (ftell(ZeFile) >= endbyte)
break;
}
}
Specialclose(ZeFile);
#ifdef VMS_SERVER
if (gs) {
if (endbyte > 0)
print_aux(sockfd, gs, GS_RFOOT);
print_aux(sockfd, gs, GS_FOOT);
GSdestroy(gs);
}
#endif
if (bufptr != writebuf) {
alarm(WRITETIMEOUT);
writestring(sockfd, writebuf);
alarm(0);
}
if (writestring(sockfd, ".\r\n")<0)
#ifndef VMS_SERVER
LOGGopher(sockfd, "Client went away"), gopherd_exit(-1);
#else
LOGGopher(sockfd, "Client went away");
#endif
}
#define BUFSIZE 8192 /* No, user process can't affect Kernel-IP
decission on what the actual output MTU is.
System call overhead may well be too great
to care for this. If the protocol had been
on top of the UDP, things would be
different.. [
[email protected]] */
void
send_binary(sockfd, filename, isGplus)
int sockfd;
char *filename;
boolean isGplus;
{
FILE *sndfile,*pp;
unsigned char in[BUFSIZE];
register int j;
int gotbytes, size;
struct stat buf;
int result;
#ifdef VMS_SERVER
GopherStruct *Vgs = NULL;
if (!(Vgs = VMS$VFLAGS(sockfd, filename, ACC_READ, *(filename-1))))
goto no_access;
#endif
Debug("Sending %s, binary way\n", filename);
/** Don't check decoder/extension if .cache **/
if (strncmp(filename, ".cache",6)!=0) {
result = GDCCanRead(Config, CurrentPeerName, CurrentPeerIP,
ActiveSessions);
if (result == SITE_NOACCESS) {
#ifdef VMS_SERVER
no_access:
SetAbrtFileIf(BummerMsg, Vgs, DstrAbrtGS, AbortString);
#endif
Abortoutput(sockfd, GDCgetBummerMsg(Config));
return;
} else if (result == SITE_TOOBUSY) {
#ifdef VMS_SERVER
SetAbrtFile(TooBusy, Vgs, DstrAbrtGS, NULL);
#endif
Abortoutput(sockfd, "Too busy right now");
return;
}
#ifndef VMS_SERVER
if (strcmp(filename, "-") == 0) {
/*** Do some live digitization!! **/
sndfile = popen("record -", "r");
}
else
#endif
sndfile = rfopen(filename, "r");
if (!sndfile) {
/*
* The specified file does not exist
*/
char notexistline[256];
sprintf(notexistline, "'%s' does not exist!!", filename);
#ifdef VMS_SERVER
SetAbrtFile(NoSuchFile, Vgs, DstrAbrtGS, NULL);
#endif
Abortoutput(sockfd, notexistline);
return;
}
if ((pp = specialfile(sockfd, sndfile, filename)) != NULL) {
fclose(sndfile);
sndfile = pp;
}
} else
sndfile = rfopen(filename, "r");
if ((isGplus) && strcmp(filename, "-") == 0)
GSsendHeader(sockfd, -2);
else if (isGplus) {
rstat(filename, &buf);
size = buf.st_size;
GSsendHeader(sockfd, size);
}
while(1) {
gotbytes = fread(in, 1, BUFSIZE, sndfile);
if (gotbytes == 0)
break; /*** end of file or error... ***/
(void) alarm(WRITETIMEOUT); /** In case client is hung.. **/
j = writen(sockfd, in, gotbytes);
(void) alarm(0);
Debug("Wrote %d binary bytes\n",j);
if (j <= 0)
break; /*** yep another error condition ***/
}
Specialclose(sndfile);
#ifdef VMS_SERVER
/** Disable the alarm signal **/
(void) alarm(0);
(void) signal(SIGALRM,SIG_IGN);
if (Vgs)
GSdestroy(Vgs);
#endif
}
#ifdef VMS_SERVER
/*
* VMS$VFLAGS -- Validate Filename, LookAside, and GopherStruct...
*
* Given a filename and a peer address:
* 1) Validate that filespec as syntactically valid.
* 2) Don't allow arbitrary filenames to be selected.
* 3) Construct a GopherStruct for the file being selected
* and verify that the peer has access as specified.
* 4) If access is permitted, pass back the GopherStruct;
* if it's denied for any reason, pass back NULL after
* saving a pointer to the GopherStruct in AbrtGS and
* indicating to Abortoutput() that it can destroy that
* GopherStruct when it's done.
*/
GopherStruct *
VMS$VFLAGS(int sockfd, char *filename, int access, char Gtype)
{
GopherStruct *gs = NULL;
char *cp;
FILE *SideFile;
AccessResult result;
gs = GSnew();
GSsetType(gs, Gtype);
cp = (char *)malloc(2+strlen(filename));
*cp = Gtype;
strcpy(cp+1,filename);
GSsetPath(gs,cp);
free(cp);
if (filename[0]=='/') {
/*** Convert to VMS pathspecs ***/
if (cp=VMS$WWW_to_VMS(filename, Gtype)) {
filename = (char *)malloc(2+strlen(cp));
*filename = Gtype;
strcpy(filename+1,cp);
GSsetPath(gs, filename);
free(filename);
filename = GSgetPath(gs);
filename++;
}
}
/* Don't accept anything but valid VMS file specification */
if (!VMS$Validate_Filespec(filename)) {
LOGGopher(sockfd, "Invalid Filespec for %s", filename);
SetAbrtFile(SyntaxErr, gs, DstrAbrtGS, "Invalid Filespec");
return(NULL);
}
/* Don't allow arbitrary files to be accessed */
if (strchr(filename,':'))
if (strncasecmp(filename, GDCgetDatadir(Config),
strcspn(GDCgetDatadir(Config),":"))) {
LOGGopher(sockfd, "Denied access for %s", filename);
SetAbrtFile(BummerMsg, gs, DstrAbrtGS, NULL);
return(NULL);
}
/* Check and see if the peer has permissions to access the resource */
cp = strchr(GSgetPath(gs),']');
if (SideFile = Build_Lookaside(cp?filename:"[]", cp?cp+1:filename, gs)) {
Process_Side(SideFile,gs,GDCgetHostname(Config),GDCgetPort(Config),
filename);
fclose(SideFile);
}
result = GDCcanAccess(Config, CurrentPeerName, CurrentPeerIP,
ActiveSessions, access);
if (result==SITE_OK || result==SITE_UNDEF)
result = GScanAccess(sockfd, gs, access);
switch(result)
{
case SITE_OK:
case SITE_UNDEF:
return(gs);
case SITE_NOACCESS:
LOGGopher(sockfd, "Denied access for %s", filename);
SetAbrtFile(BummerMsg, gs, DstrAbrtGS, NULL);
return(NULL);
case SITE_TOOBUSY:
LOGGopher(sockfd, "Too much from %s, Denied %s",
CurrentPeerName, filename);
SetAbrtFile(TooBusy, gs, DstrAbrtGS, "Sorry, too busy now...");
return(NULL);
}
}
/* A condition handler to catch problems, display who and where, then
* resignal so we get a traceback dump on sys$output
*/
int
gopher_traceback(unsigned long int signal[], int mech[])
{
static char fmt[256];
static char buf[512];
static unsigned short i = 0;
static $DESCRIPTOR(dsc$fmt,fmt);
static $DESCRIPTOR(dsc$bad,"signal[0..!UL] =!-!#(9XL)");
static $DESCRIPTOR(dsc$big,"signal[0..!UL] =!20(9XL)!/!_...max displayed");
static $DESCRIPTOR(dsc$buf,buf);
static unsigned long s[10];
if ((++tracing_back) > 1) {
LOGGopher(traceback.socket, "ACK! Looping (%d) in gopher_traceback",
tracing_back);
if (tracing_back > 2)
return(SS$_RESIGNAL);
}
if (traceback.tx==NULL)
traceback.tx = "";
LOGGopher(traceback.socket, "Dying! %s", traceback.tx);
if (Argv)
if (Argv[0])
LOGGopher(traceback.socket, "Death @ %s", Argv[0]);
buf[0] = '\0';
if (signal[0])
{
dsc$fmt.dsc$w_length = sizeof(fmt);
dsc$buf.dsc$w_length = sizeof(buf);
i = 0;
if (SS$_NORMAL == sys$getmsg(signal[1], &dsc$fmt, &dsc$fmt, 15, 0))
sys$faol(&dsc$fmt,&i,&dsc$buf,&signal[2]);
else {
if (signal[0]<=20)
sys$faol(&dsc$bad, &i, &dsc$buf, &signal[0]);
else
sys$faol(&dsc$big, &i, &dsc$buf, &signal[0]);
}
buf[i] = '\0';
LOGGopher(traceback.socket, "%s\n", buf);
}
if (traceback.socket > -1)
VMS$fprintf(stderr,"%s : %s\n", CurrentPeerName, traceback.tx);
else
VMS$fprintf(stderr,"<> : %s\n", traceback.tx);
if (Argv)
if (strlen(Argv[0]))
VMS$fprintf(stderr, "%s\n", Argv[0]);
if (strlen(buf))
VMS$fprintf(stderr,"%s\n", buf);
LOGGopher(traceback.socket, "Traceback likely on %s", getenv("SYS$ERROR"));
VMS$fprintf(stderr, "Traceback likely on %s", getenv("SYS$ERROR"));
return(SS$_RESIGNAL);
}
/*
* Write out auxilliaries (Header, Range header, Range footer, Footer)
* to Type=0 documents
*/
int
print_aux(int sockfd, GopherStruct *gs, char *auxilliary)
{
FILE *AuxFile;
char inputline[512] = "";
if (gs == NULL)
return(TRUE);
if (GSgetType(gs) != A_FILE)
return(TRUE);
if (strcmp(auxilliary,GS_HEAD)==0)
auxilliary = GSgetHeader(gs);
else
if (strcmp(auxilliary, GS_FOOT)==0)
auxilliary = GSgetFooter(gs);
else
if (strcmp(auxilliary, GS_RHEAD)==0)
auxilliary = GSgetRHeader(gs);
else
if (strcmp(auxilliary, GS_RFOOT)==0)
auxilliary = GSgetRFooter(gs);
else
return(TRUE);
if (auxilliary==NULL)
return(TRUE);
if (strlen(auxilliary)==0)
return(TRUE);
/* Apply Auxilliary Information */
if (isadir(auxilliary)==0) {
/* Auxilliary is a file; append entire file */
if (AuxFile = fopen_VMSopt(auxilliary,"r")) {
while (fgets(inputline, MAXLINE, AuxFile) != NULL) {
ZapCRLF(inputline);
/** Period on a line by itself, double it.. **/
if (*inputline == '.' && inputline[1] == '\0' && !EXECflag) {
inputline[1] = '.';
inputline[2] = '\0';
}
if (writestring(sockfd, inputline)==-1)
return(TRUE);
}
fclose(AuxFile);
}
return(TRUE);
}
/* Auxilliary is a text line; append it */
/** Period on a line by itself, double it.. **/
if (auxilliary[0]=='.' && auxilliary[1]=='\0' && !EXECflag) {
auxilliary[1] = '.';
auxilliary[2] = '\0';
}
writestring(sockfd, auxilliary);
return(TRUE);
}
/*
* Adds the "Date+Size" patch
*/
void
GDaddDateNsize(GopherDirObj *gd)
{
int i;
GopherObj *ge;
char *filename;
STATSTR statbuf;
for (i=0; i< GDgetNumitems(gd); i++) {
ge = GDgetEntry(gd, i);
filename = GSgetPath(ge);
ustat(filename+1, &statbuf);
GSaddDateNsize(ge, statbuf);
}
}
void
GDpostprocVMS(GopherDirObj *gd, GDCobj *gdc, int port, boolean isGplus)
{
int i;
GopherObj *ge;
FILE *SideFile;
char file_wo_path[256];
char admin[256];
char *cp;
char *local;
String *filename=STRnew();
STATSTR statbuf;
for (i=0; i< GDgetNumitems(gd); i++)
{
memset((char *)&statbuf, 0, sizeof(STATSTR));
ge = GDgetEntry(gd, i);
cp = GSgetPath(ge);
if (strlen(cp+1)) {
str_tolower(cp+1);
STRset(filename,cp+1);
}
if (A_INFO == GSgetType(ge))
continue;
if (local=GSgetHost(ge))
if (strcmp(GDCgetHostname(gdc),local)!=0)
local = NULL; /* Either not ours or nobody's */
/*
* Get the file statistics for this file if it's a local resource...
*/
fixup_stats:
if (local) {
switch(GSgetType(ge)) {
case A_DIRECTORY: /*** It's a directory ***/
case A_INDEX: /*** It's an index ***/
case A_FTP: /*** ftp link ***/
case A_EXEC: /*** exec link ***/
case A_HTML: /*** www link ***/
case A_WAIS: /*** wais or whois link ***/
break;
default:
if (cp=STRget(filename)) {
if (ustat(cp, &statbuf) != 0)
statbuf.st_size = 0;
}
}
}
/*
* If no Lookaside information has been applied yet, do so... (local only)
*/
fixup_lookaside:
if (local && (!GSgetLookaside(ge))) {
if (SideFile = Build_Lookaside(STRget(filename), NULL, ge)) {
Process_Side(SideFile, ge, local, GDCgetPort(Config),
STRget(filename));
fclose(SideFile);
}
}
/*
* If no title has been applied yet, insert a default name if appropriate.
* For directories, strip off the '.DIR' that might be there.
*/
fixup_title:
if (cp=STRget(filename)) {
strcpy(file_wo_path, cp);
if (GSgetTitle(ge) == NULL)
switch(GSgetType(ge)) {
case A_DIRECTORY: /*** It's a directory ***/
if (cp=strchr(file_wo_path, ']'))
*cp = '\0';
if (cp=strchr(file_wo_path, '['))
strcpy(file_wo_path, cp+1);
if (cp=strrchr(file_wo_path, '.'))
strcpy(file_wo_path, cp+1);
GSsetTitle(ge, file_wo_path);
break;
case A_INDEX: /*** It's an index ***/
case A_FTP: /*** ftp link ***/
case A_EXEC: /*** exec link ***/
case A_HTML: /*** www link ***/
case A_WAIS: /*** wais or whois link ***/
break;
default:
if ((GDCgetDName(Config)))
GSsetTitle(ge, GDCgetDName(Config));
else {
if (cp=strchr(file_wo_path,']'))
strcpy(file_wo_path,cp+1);
if (cp=strchr(file_wo_path,';'))
*cp='\0';
GSsetTitle(ge, file_wo_path);
}
}
}
/*
* If the port is still undetermined, apply a default port; if the
* item is oversize, override the port with our oversize port
*/
fixup_port:
if (GSgetPort(ge)==0) {
char *cp;
if (cp=GSgetPath(ge))
switch(*cp)
{
case A_INDEX: GSsetPort(ge, GDCgetSRCHPort(gdc)); break;
case A_FTP: GSsetPort(ge, GDCgetFTPPort(gdc)); break;
case A_EXEC: GSsetPort(ge, GDCgetEXECPort(gdc)); break;
case A_INFO: break;
default: GSsetPort(ge, port);
}
else
GSsetPort(ge,port);
}
if (local && (GDCgetOVERSize(gdc)!=-1))
if (statbuf.st_size > GDCgetOVERSize(gdc))
GSsetPort(ge, GDCgetOVERPort(gdc));
/*
* If it's *OUR* resource, and we're defaulting to Gopher+, then
* make sure we say it's Gopher+. Don't touch Gopher+ ask
* items or items not served by this host, or if we've been
* told (isGplus==FALSE) this isn't a Gopher+ operation.
* Note also that we don't impose the G+ flag on items we might not
* be able to supply results in G+ format for....
*/
fixup_isGplus:
if (isGplus || GDCgetIsGplus(gdc))
if (!(GSisGplus(ge) || GSisAsk(ge)))
if (local)
switch(*(cp=GSgetPath(ge))) {
/* Really special case -- Port 13 is time/date and
the server there doesn't speak Gopher+ */
case A_FILE: if (GSgetPort(ge)==13) break;
case A_DIRECTORY:
case A_MACHEX:
case A_PCBIN:
case A_UNIXBIN:
case A_SOUND:
case A_GIF:
case A_HTML:
case A_IMAGE:
case A_MOVIE:
case A_UUENCODE: GSsetGplus(ge, GDCgetIsGplus(gdc));
break;
/* The servers which honor these services don't
necessarily speak Gopher+, so don't say they
do by any default... */
case A_WAIS:
case A_MAILSPOOL:
case A_FTP:
case A_RANGE:
case A_EOI:
case A_APP:
case A_PDF:
case A_TN3270:
case A_CSO:
case A_ERROR:
case A_TELNET:
case A_EXEC:
case A_INDEX:
case A_CALENDAR:
case A_MIME:
case A_INFO:
default: break;
}
/*
* If we haven't already applied an administrator, do so.
*/
fixup_admin:
if (local && (!GSgetAdmin(ge))) {
sprintf(admin,"%s <%s>", GDCgetAdmin(gdc), GDCgetAdminEmail(gdc));
GSsetAdmin(ge, admin);
}
/*
* If we haven't already applied a MODDATE, and we have a date, do so.
*/
fixup_moddate:
if (local && GSgetModDate(ge) == NULL && statbuf.st_mtime) {
char timeval[16];
char tmpstr[60];
struct tm *tmthing;
tmthing = localtime(&(statbuf.st_mtime));
strftime(timeval,sizeof(timeval), "%Y%m%d%H%M%S", tmthing);
sprintf(tmpstr,"%s<%s>",asctime(tmthing),timeval);
GSsetModDate(ge, tmpstr);
}
/*
* If it's a Gopher+ item but we don't have a default view yet, apply one.
*/
fixup_view:
if (local && GSisGplus(ge) && (GSgetNumViews(ge)==0))
AddDefaultView(ge, statbuf.st_size, NULL);
}
STRdestroy(filename);
}
/*
* This processes a single filename, adding it to the sort directory
*/
AddFiletoDir(int sockfd, GopherStruct *gs, GopherDirObj *gd,
char *filename, char *newpath, String *DName)
{
FILE *SideFile;
char VMS_dir_title[256];
char Typep;
char *Pathp;
Typep = '\0';
Pathp = NULL;
Getfiletypes(newpath, filename, &Typep, &Pathp);
if (Typep == A_ERROR)
return;
GSsetType(gs, Typep);
GSsetPath(gs, Pathp);
/*
* Verify the receiver has access to this resource
*/
switch(GSgetType(gs)) {
case A_WAIS: if (strncmp(GSgetPath(gs),"aisdocid:",9)==0)
if (!GScanAccess(sockfd, gs, ACC_BROWSE))
return;
/* Note: Wais *searches* fall thru */
case A_INDEX: if (!GScanAccess(sockfd, gs, ACC_SEARCH))
return;
break;
case A_EXEC:
case A_FILE:
case A_SOUND:
case A_MACHEX:
case A_PCBIN:
case A_UUENCODE:
case A_RANGE:
case A_UNIXBIN:
case A_GIF:
case A_HTML:
case A_IMAGE: if (!GScanAccess(sockfd, gs, ACC_READ))
return;
if (GSgetType(gs) != A_EXEC)
break;
/* Note: non-EXEC's fall thru */
case A_MAILSPOOL:
case A_FTP:
case A_DIRECTORY: if (!GScanAccess(sockfd, gs, ACC_BROWSE))
return;
}
/*** Add the entry to the directory ***/
GDaddGS(gd, gs);
GSinit(gs);
}
/* Search the extensions list for this file, and if found return the
type code; otherwise return zero
*/
char
VMS$EXASearch(ExtArray *extarr, char *fileext)
{
int i, extlen, exlen;
Extobj *temp;
Debugmsg("EXAsearch:\r\n");
extlen = strlen(fileext);
/*** Linear search. Ick. ***/
for (i=0; i< EXAgetNumEntries(extarr); i++) {
temp = EXAgetEntry(extarr,i);
exlen = strlen(EXgetExt(temp));
if (exlen <= extlen && strcasecmp(fileext+extlen-exlen,
EXgetExt(temp)) == 0) {
return(EXgetObjtype(temp));
}
}
return(0);
}
/*
VMS$fprintf() provides a facility by which we can grab any fprintf() calls,
and if stderr has been specified while we're known to be an INETD process,
we can force the output to the operator's console.
use the *real* fprintf() function herein.
*/
#ifdef __STDC__
int VMS$fprintf(FILE *fp, const char *fmt, ...)
#else /* !__STDC__ */
int VMS$fprintf(fp, fmt, va_alist)
FILE *fp;
char *fmt;
va_dcl
#endif /* __STDC__ */
{
va_list args;
static
struct
{
unsigned char
type;
unsigned target:24;
long rqst_id;
char text[512];
} msg = {OPC$_RQ_RQST, 0, 0, ""};
int i;
int channel=0;
char message[512];
char *cp;
char *c2;
char LogTag[60];
char LogBuf[100];
static
$DESCRIPTOR(dsc$msg, NULL);
#ifdef __STDC__
va_start(args, fmt);
#else
va_start(args);
#endif
if (msg.target==0)
msg.target = GDCgetOPCOM(Config);
if (c2=GDCgetLogTag(Config)) {
c2 = strcpy(LogTag, c2);
if (cp=strchr(c2,'#')) {
if (strncasecmp(cp-2,"Cn",2)==0) {
cp -= 2;
*cp = '\0';
strcpy(LogBuf,LogTag);
cp += 3;
i = strlen(LogBuf);
LogBuf[i+=sprintf(LogBuf+i,"#%d",Connections)] = '\0';
strcat(LogBuf, cp);
c2 = LogBuf;
}
}
}
(void) vsprintf(message, fmt, args);
va_end(args);
strcpy(msg.text,"GopherD");
if (strlen(c2)) {
strcat(msg.text, " ");
strcat(msg.text, c2);
}
strcat(msg.text,":\n");
i = 255 - strlen(msg.text);
i = (i>strlen(message))?i:strlen(message);
strncat(msg.text, message, i);
msg.text[256] = '\0'; /* Truncate to maximum $SNDOPR size of 255 */
if ((fp == stderr) && RunFromInetd) {
fprintf(fp, "%s", message); /* Write to stderr first... */
dsc$msg.dsc$a_pointer = (char *)&msg;
dsc$msg.dsc$w_length = sizeof(msg) - sizeof(msg.text)
+ strlen(msg.text);
if ((i = SYS$SNDOPR(&dsc$msg, channel)) != SS$_NORMAL) {
vaxc$errno = i;
return(-1);
}
else
return(strlen(msg.text));
}
else
return(fprintf(fp, "%s", msg.text));
}
#endif