home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.muug.mb.ca
/
2014.06.ftp.muug.mb.ca.tar
/
ftp.muug.mb.ca
/
pub
/
src
/
gopher
/
gopher1.01
/
gopherd
/
gopherd.c.orig
< prev
next >
Wrap
Text File
|
1992-07-05
|
36KB
|
1,588 lines
/* Originally derived from an
* Example of server using TCP protocol
* pp 284-5 Stevens UNIX Network Programming
*/
#include "gopherd.h"
void LOGGopher();
static int uid = -2; /* use root by default */
#ifdef LOADRESTRICT
#ifndef MAXLOAD
#define MAXLOAD 10.0
#endif
double atof();
double maxload = MAXLOAD;
double sysload = 0.0;
#include <nlist.h>
#include <kvm.h>
int nproc;
long avenrun[3];
kvm_t * kd;
char prog[1024];
struct nlist nl[] = {
{"_avenrun" }, /* SunOS 4.1.1, your milage may vary... */
#define X_AVENRUN 0
{""},
};
#endif /*LOADRESTRICT */
/*
* This routine finds out the hostname of the server machine.
* It uses a couple of methods to find the fully qualified
* Domain name
*/
char *
GetDNSname(backupdomain)
char *backupdomain;
{
static char DNSname[MAXHOSTNAMELEN];
struct hostent *hp;
/* Work out our fully-qualified name, for later use */
if (gethostname(DNSname, MAXHOSTNAMELEN) != 0) {
fprintf(stderr, "Cannot determine the name of this host\n");
exit(-1);
}
/* Now, use gethostbyname to (hopefully) do a nameserver lookup */
hp = gethostbyname( DNSname);
/*
** If we got something, and the name is longer than hostname, then
** assume that it must the the fully-qualified hostname
*/
if ( hp!=NULL && strlen(hp->h_name) > strlen(DNSname) )
strncpy( DNSname, hp->h_name, MAXHOSTNAMELEN );
else
strcat(DNSname, backupdomain);
return(DNSname);
}
/*
* Tries to figure out what the currently connected port is.
*
* If it's a socket then it will return the port of the socket,
* if it isn't a socket then it returns -1.
*/
int GetPort(fd)
int fd;
{
struct sockaddr_in serv_addr;
int length = sizeof(serv_addr);
/** Try to figure out the port we're running on. **/
if (getsockname(fd, (struct sockaddr *) &serv_addr,&length) == 0)
return(ntohs(serv_addr.sin_port));
else
return(-1);
}
void
main(argc, argv)
int argc;
char *argv[];
{
int sockfd;
int newsockfd;
int clilen;
int childpid;
int i;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
/*** for getopt processing ***/
int c;
extern char *optarg;
extern int optind;
int errflag =0;
pname = argv[0];
strcpy(Data_Dir, DATA_DIRECTORY);
err_init(); /* openlog() early - before we chroot() of course */
/*** Check argv[0], see if we're running as gopherls, etc. ***/
RunServer = RunLS = RunIndex = FALSE;
if (strstr(argv[0], "gopherls") != NULL) {
RunLS = TRUE;
} else if (strstr(argv[0], "gindexd") != NULL) {
RunIndex = TRUE;
dochroot = FALSE;
} else
RunServer = TRUE; /** Run the server by default **/
while ((c = getopt(argc, argv, "mCDIcL:l:s:u:U:")) != -1)
switch (c) {
case 'D':
DEBUG = TRUE;
break;
case 'I':
RunFromInetd = TRUE;
break;
case 'C':
Caching = FALSE;
break;
case 'm':
if (RunIndex)
MacIndex = TRUE;
break;
case 'c':
dochroot = FALSE;
if (!RunFromInetd) {
printf("Not using chroot() - be careful\n");
if ( getuid() == 0 || geteuid() == 0 )
printf("You should run without root perms\n");
}
break;
case 'L': /** Load average at which to restrict usage **/
#ifdef LOADRESTRICT
maxload = atof(optarg);
#endif
break;
case 'l': /** logfile name **/
if (*optarg == '/')
strcpy(LOGFile, optarg);
else {
getwd(LOGFile);
strcat(LOGFile, "/");
strcat(LOGFile, optarg);
}
break;
case 's': /** security file name **/
if (*optarg == '/')
strcpy(SecurityFile, optarg);
else {
getwd(SecurityFile);
strcat(SecurityFile, "/");
strcat(SecurityFile, optarg);
}
break;
case 'u':
{
struct passwd *pw = getpwnam( optarg );
if ( !pw ) {
fprintf(stderr,
"Could not find user '%s' in passwd file\n",
optarg);
errflag++;
} else {
uid = pw->pw_uid;
if (!RunFromInetd) {
printf("Running as user '%s' (%d)\n",
optarg, uid);
}
}
}
break;
case 'U': /* set uid to use */
uid = atoi( optarg );
if (!RunFromInetd) {
printf("Running using uid %d\n", uid);
}
break;
case '?':
case 'h':
errflag++;
break;
}
if (errflag) {
fprintf(stderr, "Usage: %s [-CDIc] [-u userid] [-U uid] [-s securityfile] [-l logfile] <datadirectory> <port>\n", argv[0]);
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, " -U specifies the UID for use with -c\n");
fprintf(stderr, " -s specifies the name of a security file\n");
fprintf(stderr, " -l specifies the name of a logfile\n");
exit(-1);
}
if (uid == -2)
uid = getuid(); /** Run as current user... **/
if ( uid == 0 && !RunFromInetd )
printf("Hope you know what you're doing ...\n");
if (optind < argc) {
strcpy(Data_Dir, argv[optind]);
optind++;
} else if (RunLS)
strcpy(Data_Dir, "/");
if (optind < argc) {
GopherPort = atoi(argv[optind]);
optind++;
}
if (RunLS) {
Zehostname ="";
Caching = FALSE;
fflush(stdout);
uchdir(Data_Dir);
listdir(0, "/");
exit(0);
}
if (!RunFromInetd) {
printf("Data directory is %s\n", Data_Dir);
printf("Port is %d\n", GopherPort);
}
if (*LOGFile != '\0' && !RunFromInetd)
printf("Logging to File %s\n", LOGFile);
if (*SecurityFile != '\0' && !RunFromInetd)
printf("Using Security file %s\n", SecurityFile);
/*
* Would like to setuid() here, but have to wait until after the
* bind() in case we're going to be running on a privileged port.
*/
if (uchdir(Data_Dir)) {
if (!RunIndex) {
fprintf(stderr, "Cannot change to data directory!! %s \n",Data_Dir);
exit(-1);
}
}
if (dochroot && getuid() != 0) {
fprintf(stderr, "Gopherd uses the privileged call chroot(). Please become root.\n");
exit(-1);
}
/** Open up the SecurityFile
**Warning!** we have to do this over because the daemon_start
function closes all fd's
This part just checks to see if the file exists...
**/
if (*SecurityFile != '\0') {
SECFileHandle = ufopen(SecurityFile, "r");
if (SECFileHandle == NULL) {
printf("Can't open the security file: %s\n", SecurityFile);
exit(-1);
}
fclose(SECFileHandle);
}
#ifdef LOADRESTRICT
if(RunFromInetd) {
if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, prog)) == NULL)
exit(-1);
if (kvm_nlist(kd, nl) != 0)
exit(-1);
if(nl[X_AVENRUN].n_type == 0)
exit(-1);
if(kvm_read(kd,nl[X_AVENRUN].n_value,avenrun,sizeof avenrun)
!= sizeof avenrun)
exit(-1);
if((sysload = (((double) avenrun[2]) / FSCALE)) > maxload) {
printf("- System Load exceeded (%4.2f > %4.2f). ",sysload,maxload);
printf("Please Retry Request later.\r\n.\r\n");
exit(1);
}
}
#endif /*LOADRESTRICT*/
if (DEBUG == FALSE && RunFromInetd==FALSE)
daemon_start(0);
/*** Hmmm, does this look familiar? :-) ***/
if (*SecurityFile != '\0') {
SECFileHandle = ufopen(SecurityFile, "r");
if (SECFileHandle == NULL) {
printf("Can't open the security file: %s\n", SecurityFile);
exit(-1);
}
}
err_init(); /* does this look familiar too?? :-) */
/** Ask the system what host we're running on **/
Zehostname = GetDNSname(DOMAIN_NAME);
if (RunFromInetd) {
/** Ask the system which port we're running on **/
int newport=0;
if ((newport =GetPort(0)) !=0)
GopherPort=newport;
/*** Do the stuff for inetd ***/
while(do_command(0)!=0); /* process the request */
exit(0);
}
/** Open a TCP socket (an internet stream socket **/
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_dump("server: can't open stream socket");
/** Bind our local address so that the client can send to us **/
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(GopherPort);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) <0)
err_dump("server: can't bind local address");
/* have to setuid() here, in case we're using a privileged port */
if ( setuid(uid) != 0 )
err_sys("Cannot setuid(%d): ",uid);
listen(sockfd, 5);
for ( ; ; ) {
/*
* Wait for a connection from a client process.
* This is an example of a concurrent server.
*/
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr,
&clilen);
/*** weird.. with this thing here our gethostaddrs work.
without it, it fails..... why?
***/
if (newsockfd < 0)
err_dump("server: accept error");
if ( (childpid = fork()) < 0)
err_dump("server: fork error");
else if (childpid == 0) { /* Child process */
close(sockfd); /* close original socket */
while(do_command(newsockfd)!=0); /* process the request */
exit(0);
}
/** clean up any zombie children **/
sig_child();
close(newsockfd); /* parent process */
}
}
/*
*
* Code stolen from nntp.....
*
* inet_netnames -- return the network, subnet, and host names of
* our peer process for the Internet domain.
*
* Parameters: "sock" is our socket, which we don't need.
* "sin" is a pointer to the result of
* a getpeername() call.
* "host_name"
* is filled in by this routine with the
* corresponding ASCII names of our peer.
* Returns: Nothing.
* Side effects: None.
*/
inet_netnames(sock, sin, host_name)
int sock;
struct sockaddr_in *sin;
char *host_name;
{
u_long net_addr;
struct hostent *hp;
struct netent *np;
net_addr = inet_netof(sin->sin_addr); /* net_addr in host order */
np = getnetbyaddr(net_addr, AF_INET);
hp = gethostbyaddr((char *) &sin->sin_addr.s_addr,
sizeof (sin->sin_addr.s_addr), AF_INET);
if (hp != NULL)
(void) strcpy(host_name, hp->h_name);
else
(void) strcpy(host_name, inet_ntoa(sin->sin_addr));
}
/*
* This finds the current peer and the time and jams it into the
* logfile (if any) and adds the message at the end
*/
void
LOGGopher(sockfd, message)
int sockfd;
char *message;
{
struct sockaddr sa;
int length;
static char host_name[256];
time_t Now;
char *cp;
/* cp + ' ' + host_name + ' : ' + MAXLINE + '\n' + '\0' */
char buf[286+MAXLINE];
struct flock lock;
host_name[0] = '\0';
if (LOGFileDesc != -1) {
if (sockfd > -1) {
length = sizeof (sa);
getpeername(sockfd, &sa, &length);
inet_netnames(sockfd, &sa, host_name);
}
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0L;
lock.l_len = 0L;
fcntl(LOGFileDesc, F_SETLKW, &lock);
time(&Now); /* Include this in the lock to make sure */
cp = ctime(&Now); /* log entries are chronological */
ZapCRLF(cp);
/* someone else may have written to the file since we opened it */
lseek(LOGFileDesc, 0L, SEEK_END);
sprintf(buf, "%s %d %s : %s\n", cp, getpid(), host_name, message);
write(LOGFileDesc, buf, strlen(buf));
/* unlock the file */
lock.l_type = F_UNLCK;
fcntl(LOGFileDesc, F_SETLKW, &lock);
if (DEBUG)
printf("%s %d %s : %s\n", cp, getpid(), host_name, message);
}
}
process_mailfile(sockfd, Mailfname)
int sockfd;
char *Mailfname;
{
FILE *Mailfile;
char Zeline[MAXLINE];
char outputline[MAXLINE];
char Title[MAXLINE];
long Startbyte=0, Endbyte=0, Bytecount=0;
boolean flagged = 0;
Mailfile = rfopen(Mailfname, "r");
if (Mailfile == NULL) {
Abortoutput(sockfd, "Cannot access file");
return;
}
while (fgets(Zeline, MAXLINE, Mailfile) != NULL) {
if (strncmp(Zeline, "Subject: ", 9)==0 && (!flagged)) {
flagged =1;
strcpy(Title, Zeline + 9);
ZapCRLF(Title);
if (DEBUG)
fprintf(stderr, "Found title %s", Title);
}
if (is_mail_from_line(Zeline)==0) {
Endbyte = Bytecount;
flagged =0;
if (Endbyte != 0) {
sprintf(outputline, "0%s\tR%d-%d-%s\t%s\t%d\r\n",
Title, Startbyte, Bytecount, Mailfname,
Zehostname, GopherPort);
if (writestring(sockfd, outputline) < 0)
LOGGopher(sockfd, "Client went away"), exit(-1);
Startbyte=Bytecount;
*Title = '\0';
}
}
Bytecount += strlen(Zeline);
}
if (*Title != '\0') {
sprintf(outputline, "0%s\tR%d-%d-%s\t%s\t%d\r\n",
Title, Startbyte, Bytecount, Mailfname,
Zehostname, GopherPort);
if (writestring(sockfd, outputline)<0)
LOGGopher(sockfd, "Client went away"),exit(-1);
}
if (writestring(sockfd, ".\r\n")<0)
LOGGopher(sockfd, "Client went away"),exit(-1);
}
boolean
Can_Read(sockfd)
int sockfd;
{
struct sockaddr sa;
int length;
char host_name[256];
char *cp;
boolean MatchReturn;
char inputline[MAXLINE];
if (*SecurityFile == '\0')
return(TRUE);
length = sizeof (sa);
getpeername(sockfd, &sa, &length);
inet_netnames(sockfd, &sa, host_name);
fseek(SECFileHandle, 0, 0); /** Go to beginning of Security File **/
while (fgets(inputline, MAXLINE, SECFileHandle) != NULL) {
ZapCRLF(inputline);
MatchReturn = TRUE;
cp = inputline;
if (*cp == '!') {
MatchReturn = FALSE;
cp++;
}
if (isdigit(*cp)) {
/** Internet address x.x.x.x ..***/
/** Check for a match from the beginning **/
if (strstr(host_name,cp) == host_name)
return(MatchReturn);
}
else if (*cp == '*') {
return(MatchReturn);
}
else if (*cp != '#') {
/*** Not a comment, must be a host name **/
if ((strstr(host_name, cp) + strlen(cp)) == (host_name + strlen(host_name))) {
return(MatchReturn);
}
}
}
/*** Hmmm, didn't find a match... Let em have it***/
return(TRUE);
}
int
do_command(sockfd)
int sockfd;
{
char inputline[MAXLINE];
int length; /* Length of the command line */
char logline[MAXLINE];
char *selstr;
/*** Reopen the security file ***/
if (*SecurityFile != '\0') {
SECFileHandle = ufopen(SecurityFile, "r");
if (SECFileHandle == NULL) {
LOGGopher(sockfd, "Security File dissappeared!");
exit(-1);
}
}
/*** Reopen the log file ***/
if (LOGFile[0] != '\0') {
LOGFileDesc = uopen(LOGFile, O_WRONLY | O_APPEND |O_CREAT, 0755);
if (LOGFileDesc == -1) {
printf("Can't open the logfile: %s\n", LOGFile);
exit(-1);
}
}
/** Change our root directory **/
if ( dochroot ) {
if (chroot(Data_Dir)) {
Abortoutput(sockfd, "Data_Dir dissappeared!");
exit(-1);
}
uchdir("/"); /* needed after chroot */
}
if (setuid(uid)) {
LOGGopher(sockfd, "Can't set UID!");
Abortoutput(sockfd, "Can't set UID!");
exit(-1);
}
length = readline(sockfd, inputline, MAXLINE); /** Get the line **/
if (length <= 0) {
close(sockfd);
err_quit("getcommand: readline error");
}
ZapCRLF(inputline);
/*
* Decide if we're an HTML server or not...
*/
if (strncmp(inputline, "GET /", 5) == 0) {
char *htmlcpin;
char *htmlcpout;
UsingHTML = TRUE;
selstr = inputline+5;
/** Convert the hex things back to text... ***/
Fromhexstr(selstr, selstr);
} else
selstr = inputline;
if (RunIndex) {
/*** Run like the old gindexd thing. ***/
char tempstr[512];
uchdir("/");
strcpy(tempstr, Data_Dir);
strcat(tempstr, "\t");
if (*selstr == '\t')
strcat(tempstr, selstr+1);
else
strcat(tempstr, selstr);
strcpy(Data_Dir, "/");
if (DEBUG)
writestring(sockfd, tempstr);
Do_IndexTrans(sockfd, tempstr);
return(0);
}
/*** With the funky new capability system we can just check the
first letter, end decide what the object refers to. ***/
switch (*selstr) {
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. ***/
listdir(sockfd, "/");
LOGGopher(sockfd, "Root Connection");
break;
case 'h':
/*** A raw html file ***/
/*** Turn off html'ing and just dump the file ***/
UsingHTML = FALSE;
case '0':
/*** It's a generic file capability ***/
printfile(sockfd, selstr+1, 0, -1);
/*** Log it ***/
strcpy(logline, "retrieved file ");
strcat(logline, selstr+1);
LOGGopher(sockfd, logline);
break;
case '1':
/*** It's a directory capability ***/
listdir(sockfd, selstr+1);
/** Log it **/
strcpy(logline, "retrieved directory ");
strcat(logline, selstr+1);
LOGGopher(sockfd, logline);
break;
case '7':
/*** It's an index capability ***/
Do_IndexTrans(sockfd, selstr+1);
break;
case '9':
/*** It's a binary thingie... ***/
/*** Okay, it's not a sound, but what the heck.... ***/
echosound(sockfd, selstr+1);
/* Log it */
strcpy(logline, "retrieved binary ");
strcat(logline, selstr+1);
LOGGopher(sockfd, logline);
break;
case 's':
/*** It's a sound capability ***/
echosound(sockfd, selstr+1);
/* Log it */
strcpy(logline, "retrieved sound ");
strcat(logline, selstr+1);
LOGGopher(sockfd, logline);
break;
case 'm':
/*** This is an internal identifier ***/
/*** The m paired with an Objtype of 1 makes a mail spool file
into a directory.
***/
process_mailfile(sockfd, selstr + 1);
/** Log it **/
strcpy(logline, "retrieved maildir ");
strcat(logline, selstr+1);
LOGGopher(sockfd, logline);
break;
case 'R':
/*** 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) {
Abortoutput(sockfd, "Range specifier error");
break;
}
*cp = '\0';
startbyte = atoi(selstr+1);
oldcp = cp+1;
cp = strchr(oldcp, '-');
if (cp == NULL) {
Abortoutput(sockfd, "Range specifier error");
exit(-1);
}
*cp = '\0';
endbyte = atoi(oldcp);
oldcp = cp + 1;
if (DEBUG)
fprintf(stderr, "Start: %d, End: %d File: %s\n", startbyte, endbyte, oldcp);
writestring(sockfd, "This section is from the document '");
writestring(sockfd, oldcp);
writestring(sockfd, "'.\r\n\r\n");
printfile(sockfd, oldcp, startbyte, endbyte);
/*** Log it ***/
sprintf(logline, "retrieved range %d - %d of file %s", startbyte, endbyte, oldcp);
LOGGopher(sockfd, logline);
break;
}
case 'f':
if (strncmp(selstr, "ftp:",4)==0){
SendFtpQuery(selstr+4);
TranslateResults(sockfd);
break;
}
sprintf(logline, "retrieved %s", selstr);
LOGGopher(sockfd, logline);
break;
case 'e':
if (strncmp(selstr, "exec:", 5)==0) {
/* args are between colons */
char *args, *command;
command = strrchr(selstr + 5, ':');
if ((command == NULL) || (command == selstr + 5))
break;
args = selstr+5;
*command = '\0';
command++;
EXECargs = selstr+5;
printfile(sockfd, command, 0, -1);
}
case 'w':
{
if (strncmp(selstr, "waissrc:", 8) == 0) {
SearchRemoteWAIS(sockfd, selstr+8);
break;
}
else if (strncmp(selstr, "waisdocid:", 10) == 0) {
Fetchdocid(sockfd, selstr+10);
break;
}
}
default:
/*** Hmmm, must be an old link... Let's see if it exists ***/
switch (isadir(selstr)) {
case -1:
/* no such file */
sprintf(inputline, "'%s' does not exist", selstr);
Abortoutput(sockfd, "File does not exist, or can't stat()");
break;
case 0:
/* it's a file */
printfile(sockfd, selstr, 0, -1);
/* Log it... */
strcpy(logline, "retrieved file ");
strcat(logline, selstr);
LOGGopher(sockfd, logline);
break;
case 1:
/* it's a directory */
listdir(sockfd, selstr);
/* Log it */
strcpy(logline, "retrieved directory ");
strcat(logline, inputline);
LOGGopher(sockfd, logline);
break;
}
}
return(0);
}
/*
* Cache timeout value.
* If cache is less than secs seconds old, it's ok.
* Otherwise, compare time of cache to dir and all files in dir and dir/.cap.
* If cache is newest, it's ok, otherwise it must be rebuilt.
*
* Not really great for big directories, but better in general for smaller
* directories..
*/
boolean
Cachetimedout(cache, secs, dir)
char *cache;
int secs;
char *dir;
{
STATSTR buf;
int result;
time_t now;
int cachetime;
char cappath[512];
DIR *ZeDir;
struct dirent *dp;
result = rstat(cache, &buf);
if (result != 0)
return(-1);
time(&now);
if (DEBUG)
printf("Cache now: %d, cache file: %d", now,buf.st_mtime);
if ( now < (buf.st_mtime + secs))
return(FALSE);
cachetime = buf.st_mtime;
/** Check the directory, see if it's been modified... **/
result = rstat(dir, &buf);
if (result == 0 && cachetime < buf.st_mtime)
return(TRUE);
if ((ZeDir = ropendir(dir)) == NULL)
return(TRUE);
for (dp = readdir(ZeDir); dp != NULL; dp = readdir(ZeDir)) {
result = rstat(dp->d_name, &buf);
if (result == 0 && cachetime < buf.st_mtime) {
closedir(ZeDir);
return(TRUE);
}
}
closedir(ZeDir);
sprintf(cappath, "%s/.cap", dir);
if ((ZeDir = ropendir(cappath)) == NULL)
return(FALSE);
for (dp = readdir(ZeDir); dp != NULL; dp = readdir(ZeDir)) {
result = rstat(dp->d_name, &buf);
if (result == 0 && cachetime < buf.st_mtime) {
closedir(ZeDir);
return(TRUE);
}
}
closedir(ZeDir);
return(FALSE);
}
/*
* Returns true (1) for a directory
* false (0) for a file
* -1 for anything else
*/
boolean
isadir(path)
char *path;
{
STATSTR buf;
int result;
result = rstat(path, &buf);
if (result != 0)
return(-1);
if (S_ISDIR(buf.st_mode)) {
if (! access(path, F_OK))
return(1);
else
return(-1);
}
else if (S_ISREG(buf.st_mode))
return(0);
else
return(-1);
}
/*
* This function tries to find out what type of file a pathname is.
* It then fills in the VAR type variables ObjType & ServerPath with
* corresponding info.
*/
Getfiletypes(newpath, filename, ObjType, ServerPath)
char *newpath;
char *filename;
char **ObjType;
char **ServerPath;
{
boolean dirresult;
int Zefilefd;
static char Zebuf[64];
char *cp;
static char Selstr[512];
if (ServerPath != NULL) /* Don't overwrite existing path if any */
*ServerPath = Selstr;
dirresult = isadir(filename);
if (dirresult == -1) { /** Symlink or Special **/
*ObjType = "3";
return;
}
if (dirresult == 1) {
*ObjType = "1";
*Selstr = '1';
strcpy(Selstr +1, newpath);
return;
}
else { /** Some kind of data file.... */
/** Macintosh HQX files **/
if ((strcmp(filename+strlen(filename)-4, ".hqx")) == 0) {
*ObjType = "4";
*Selstr = '0';
strcpy(Selstr + 1, newpath);
return;
}
/** SGML files end in .html **/
if ((strcmp(filename+strlen(filename)-5, ".html")) ==0) {
*ObjType = "h";
strcpy(Selstr, "GET /h");
strcat(Selstr, newpath);
return;
}
#ifdef WAISSEARCH
if ((strcmp(filename+strlen(filename)-4, ".src")) == 0) {
*ObjType = "7";
strcpy(Selstr, "waissrc:");
strcat(Selstr, newpath);
return;
}
#endif
else if ((strcmp((char*)filename+strlen(filename)-6,".tar.Z"))== 0){
*ObjType = "9";
*Selstr = '9';
strcpy(Selstr + 1, newpath);
return;
}
/*** Test and see if the thing exists... ***/
if ((Zefilefd = ropen(filename, O_RDONLY)) < 0) {
*ObjType = "3";
return;
}
bzero(Zebuf, 64);
read(Zefilefd, Zebuf, 64);
close(Zefilefd);
/*** Check the first few bytes for sound data ***/
cp = Zebuf;
if (*cp++ == '.' && *cp++ == 's' && *cp++ == 'n' && *cp++ == 'd') {
*ObjType = "s";
*Selstr = 's';
strcpy(Selstr+1, newpath);
return;
}
/*** Check and see if it's mailbox data ***/
if (is_mail_from_line(Zebuf)==0) {
*ObjType = "1";
*Selstr = 'm';
strcpy(Selstr+1, newpath);
return;
}
/*** Check for uuencoding data ***/
cp = Zebuf;
if (*cp++ == 'b' && *cp++ == 'e' && *cp++ == 'g' &&
*cp++ == 'i' && *cp++ == 'n') {
*ObjType = "6";
*Selstr = '6';
strcpy(Selstr+1, newpath);
return;
}
/*** The default is a generic text file ***/
*ObjType = "0";
*Selstr = '0';
strcpy(Selstr + 1, newpath);
return;
}
/*** we shouldn't be able to get here..... ***/
/*** but if you do, you won't have set *ObjType, so you'll
be in trouble ***/
*ObjType = "3";
return;
}
/*
** 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)
int sockfd;
char *pathname;
{
DIR *ZeDir;
FILE *DotFile;
int i;
char *cp, ch;
static char ZeObjType[3];
char sidename[256];
char filename[256];
static char newpath[512];
FILE *SideFile;
static GopherStruct *Gopherp = NULL;
char *Typep, *Pathp, *cachefile;
struct dirent *dp;
/*** Make our gopherstruct ****/
if (Gopherp == NULL)
Gopherp = GSnew();
if (rchdir(pathname)<0) {
Abortoutput(sockfd, "- Cannot access that directory");
return;
}
if (UsingHTML)
cachefile = ".cache.html";
else
cachefile = ".cache";
if (Caching && Cachetimedout(cachefile, CACHE_TIME, ".")==FALSE) {
/** Cache is still active, spit out the cache file
and get outta here..... **/
printfile(sockfd, cachefile, 0, -1);
return;
}
/** If we didn't cache then we have to use a sorting directory... **/
SortDir = GDnew(64);
/* open "." since we just moved there - makes it work when not
chroot()ing and using relative paths */
if ((ZeDir = ropendir(".")) == NULL) {
Abortoutput(sockfd, "Cannot get that directory");
return;
}
for (dp = readdir(ZeDir); dp != NULL; dp = readdir(ZeDir)) {
strcpy(newpath, pathname);
if (newpath[strlen(newpath)-1] != '/')
strcat(newpath, "/");
strcat(newpath, dp->d_name);
strcpy(sidename, "./.cap/");
strcpy(filename, dp->d_name);
strcat(sidename, dp->d_name);
/** Only chew list out files that don't start with a dot **/
/** or aren't named dev, usr, bin, etc, or core. **/
if (filename[0] == '.' &&
isadir(filename)==0 &&
strncmp(filename, ".cache", 6) !=0) {
/*** This is a link file, let's process it ***/
int linkfd;
linkfd = uopen(filename, O_RDONLY);
if (linkfd >0) {
GDfromLink(SortDir, linkfd, Zehostname, GopherPort);
close(linkfd);
}
}
if ((filename[0] != '.')
&& strcmp(filename, "bin") != 0 &&
strcmp(filename, "dev") != 0 &&
strcmp(filename, "usr") != 0 &&
strcmp(filename, "core")!= 0 &&
strcmp(filename, "etc") != 0) {
/** Check to see if there's a set-aside file with more info ***/
/** But first initialize the Gopherstruct **/
GSinit(Gopherp);
GSsetHost(Gopherp, Zehostname);
GSsetPort(Gopherp, GopherPort);
Typep = Pathp = NULL;
Getfiletypes(newpath, filename, &Typep, &Pathp);
if (*Typep =='3')
continue;
GSsetType(Gopherp, Typep[0]);
GSsetPath(Gopherp, Pathp);
if (GSgetTitle(Gopherp) == NULL) {
/*** Check to see if we have a compressed file ***/
if (strcmp(filename + strlen(filename) -2, ".Z") ==0 &&
strcmp(filename + strlen(filename) -6, ".tar.Z") != 0)
filename[strlen(filename) - 2] = '\0';
GSsetTitle(Gopherp, filename);
}
else
GSsetTitle(Gopherp, filename);
if ((SideFile = rfopen(sidename, "r"))!=0) {
if (DEBUG == TRUE)
printf("Side file name: %s\n", sidename);
Process_Side(SideFile, Gopherp);
}
/*** Add the entry to the directory ***/
GDaddGS(SortDir, Gopherp);
}
}
GDsort(SortDir);
if (UsingHTML) {
int aboutfd;
aboutfd = uopen(".about.html", O_RDONLY);
if (aboutfd > 0) {
while (readline(aboutfd, newpath, 512))
writestring(sockfd, newpath);
close(aboutfd);
}
GDtoNetHTML(SortDir, sockfd);
}
else
GDtoNet(SortDir, sockfd);
writestring(sockfd, ".\r\n");
/*
* Write out the cache... *After* we send out the data to the net.
*/
if (Caching) {
int cachefd;
cachefd = uopen(cachefile, O_WRONLY|O_CREAT|O_TRUNC, 0755);
if (cachefd != 0) {
if (DEBUG) {
printf("Caching directory...\n");
}
if (UsingHTML) {
int aboutfd;
aboutfd = uopen(".about.html", O_RDONLY);
if (aboutfd > 0) {
while (readline(aboutfd, newpath, 512))
writestring(cachefd, newpath);
close(aboutfd);
}
GDtoNetHTML(SortDir, cachefd);
}
else
GDtoNet(SortDir, cachefd);
close(cachefd);
}
}
closedir(ZeDir);
}
/*
* 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.
*/
Process_Side(sidefile, Gopherp)
FILE *sidefile;
GopherObj *Gopherp;
{
char inputline[MAXLINE];
static char ItemType[3];
static char ItemName[255];
static char Path[255];
static char Host[64];
static char Port[10];
char *cp;
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! */
/*** 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... ***/
*(GSgetPath(Gopherp)) = '7';
}
}
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);
}
}
fclose(sidefile);
}
/*
** 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
**
**
*/
void
printfile(sockfd, pathname, startbyte, endbyte)
int sockfd;
char *pathname;
int startbyte, endbyte;
{
FILE *ZeFile;
char inputline[512];
int i;
char Zcatcommand[256];
char *cp;
/*
* This chdir is unnecssary, since we have not gone anywhere since
* the chroot.
*/
/* rchdir("/"); */
/*** Check and see if the peer has permissions to read files ***/
if (UsingHTML) {
writestring(sockfd, "<XMP>\r\n");
}
if (Can_Read(sockfd) == FALSE) {
if (writestring(sockfd, BUMMERSTR) <0)
LOGGopher(sockfd, "Client went away"), exit(-1);
writestring(sockfd, "\r\nBummer.....\r\n.\r\n");
close(sockfd);
return;
}
if ( (ZeFile = rfopen(pathname, "r")) == NULL) {
/*
* The specified file does not exist
*/
char notexistline[256];
sprintf(notexistline, "'%s' does not exist!!", pathname);
Abortoutput(sockfd, notexistline);
return;
}
if (startbyte != 0)
fseek(ZeFile, startbyte, 0);
{
FILE *pp;
if (pp = specialfile(ZeFile, pathname)) {
fclose(ZeFile);
ZeFile = pp;
}
}
while (fgets(inputline, MAXLINE, ZeFile) != NULL) {
ZapCRLF(inputline);
if (writestring(sockfd, inputline) <0)
LOGGopher(sockfd, "Client went away"), exit(-1);
if (writestring(sockfd, "\r\n"))
LOGGopher(sockfd, "Client went away"), exit(-1);
if (endbyte >0) {
if (ftell(ZeFile) >= endbyte)
break;
}
}
Specialclose(ZeFile);
if (UsingHTML) {
writestring(sockfd, "</XMP>\r\n");
}
if (writestring(sockfd, ".\r\n")<0)
LOGGopher(sockfd, "Client went away"), exit(-1);
}
#define BUFSIZE 1400 /* A pretty good value for ethernet */
void
echosound(sockfd, filename)
int sockfd;
char *filename;
{
FILE *sndfile;
unsigned char in[BUFSIZE];
register int j;
int gotbytes;
if (strcmp(filename, "-") == 0) {
/*** Do some live digitization!! **/
sndfile = popen("record -", "r");
}
else
sndfile = rfopen(filename, "r");
while(1) {
gotbytes = fread(in, 1, BUFSIZE, sndfile);
if (gotbytes == 0)
break; /*** end of file or error... ***/
j = writen(sockfd, in, gotbytes);
if (j == 0)
break; /*** yep another error condition ***/
}
}