mirror of
https://github.com/NishiOwO/ncsa-httpd.git
synced 2025-04-22 01:04:43 +00:00
NCSA HTTPd 0.5
This commit is contained in:
commit
951a80ef8d
134
INSTALL
Normal file
134
INSTALL
Normal file
@ -0,0 +1,134 @@
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
Unless you're installing this server standalone, you'll need to be able
|
||||
to edit /etc/services and /etc/inetd.conf.
|
||||
|
||||
|
||||
If you're on a Sun Sparc, a DECstation 3000/5000 or DECstation
|
||||
AXP, an IBM RS/6000, or an SGI, you can download a binary of the
|
||||
server from ftp.ncsa.uiuc.edu, /Mosaic/ncsa_httpd. The binaries have
|
||||
been compiled with the following options:
|
||||
|
||||
All error files are in /usr/local/etc/httpd
|
||||
The config file is /usr/local/etc/httpd/httpd.conf
|
||||
The default port number is 80.
|
||||
The maximum number of aliases is 20.
|
||||
The name of a directory's index file is index.html.
|
||||
The server does not truncate lines.
|
||||
Errors are not logged to syslog.
|
||||
HTTP accesses are logged to /usr/local/etc/httpd/access_log
|
||||
The annotation server support is turned off.
|
||||
|
||||
The executables named httpd do not have gopher support, while
|
||||
those named httpd.gopher do have it.
|
||||
|
||||
If you want to compile the source yourself, do that first. The
|
||||
httpd.h file should have fairly straightforward instructions for what
|
||||
you need to configure. The Makefile has various options for different
|
||||
platforms, once again it should be fairly straightforward.
|
||||
|
||||
If you have any problems, or things you would like to see changed so
|
||||
as to make them more understandable at this stage, please e-mail
|
||||
httpd@ncsa.uiuc.edu with suggestions.
|
||||
|
||||
|
||||
Once you have a binary
|
||||
......................
|
||||
|
||||
|
||||
1. Decide where you want your server to live. A good place is
|
||||
/usr/local/etc/httpd, because that's where the error and config files
|
||||
are expected to be. We recommend placing your server under the name
|
||||
/usr/local/etc/httpd/httpd, and that way all of your httpd stuff is in
|
||||
one place.
|
||||
|
||||
1.5. Decide if you want to run the server standalone, or from inetd. If
|
||||
you are not sure, running standalone is generally faster. If you are choosing
|
||||
an inetd based server, skip forward to step 2.
|
||||
|
||||
To use the server standalone, simply execute it with the -s and -p options. The
|
||||
-p followed by a number tells the server what port to listen to. In order to
|
||||
listen to ports with a number < 1024, you'll need to be root to run the server
|
||||
initially.
|
||||
|
||||
Example:
|
||||
httpd -s -p 8081
|
||||
|
||||
To start a standalone server listening to port 8081.
|
||||
|
||||
Skip to step 4.
|
||||
|
||||
2. Edit /etc/services, and place a line in there which tells inetd
|
||||
which port you want your http server to listen to. Make sure the
|
||||
define for ANN_PORT in httpd.h and this line agree. Example:
|
||||
|
||||
http 80/tcp
|
||||
|
||||
|
||||
3. Edit /etc/inetd.conf, and place a line in there which tells inetd
|
||||
to fire off a server each time someone connects to that port. Example:
|
||||
|
||||
http stream tcp nowait nobody /usr/local/etc/httpd/httpd httpd
|
||||
|
||||
assuming you decided to put the server binary in /usr/local/etc/httpd.
|
||||
It is an excellent idea to run httpd under user ID 'nobody' (as shown
|
||||
above), as there's not much damage 'nobody' can do.
|
||||
|
||||
|
||||
4. If you are running with access logging enabled, make sure
|
||||
/usr/local/etc/httpd/access_log (or the appropriate path/file) is
|
||||
writeable by the user ID being used to run httpd (usually, nobody).
|
||||
If this file is not writeable by that user ID, httpd will not work.
|
||||
|
||||
|
||||
5. Place the error message files where the server is expecting them.
|
||||
If you used a precompiled binary, create the directory
|
||||
/usr/local/etc/httpd/errors, and copy all of the html files from this
|
||||
distribution in there. If you didn't use the precompiled one, create
|
||||
whatever directory you decided to put them under. Be sure they are
|
||||
readable. You can feel free to edit the messages to specify problem
|
||||
report addresses or whatnot. If you are compiling the server from the
|
||||
source, you can put these wherever you like.
|
||||
|
||||
|
||||
6. Create a httpd.conf in /usr/local/etc/httpd/httpd.conf for your
|
||||
server. If you are compiling from the source, the #define CONFIG_FILE
|
||||
tells where the server will look for this file by default. The server
|
||||
can also be started with the -f flag and a filename to override this
|
||||
default.
|
||||
|
||||
The syntax for the httpd.conf file is very simple. Every line which
|
||||
begins with a # sign is a comment. Besides that, lines are of the
|
||||
format:
|
||||
|
||||
alias:realdir
|
||||
|
||||
Where alias is the name which should be seen by the clients, and
|
||||
realdir is the real server directory which should be mapped to that
|
||||
location. Examples:
|
||||
|
||||
/foo:/bletch
|
||||
|
||||
Makes it so that all client requests beginning with /foo are mapped to
|
||||
the real directory /bletch.
|
||||
|
||||
/:/port4/web-docs
|
||||
|
||||
This has the effect of mapping all client requests into the directory
|
||||
/port4/web-docs, which makes for easy centralization.
|
||||
|
||||
Aliases are cumulative; once you map something, all of your subsequent
|
||||
aliases will have the previous ones expanded in the alias name before
|
||||
they themselves are interpreted. For example:
|
||||
|
||||
/:/port4/web-docs
|
||||
/foo:/port4/web-docs/bletch
|
||||
|
||||
This would re-map all access to /port4/web-docs/foo to
|
||||
/port4/web-docs/bletch.
|
||||
|
||||
To deny access to a directory, map it to something non-existent like /deny.
|
||||
|
||||
|
||||
httpd@ncsa.uiuc.edu
|
39
Makefile
Normal file
39
Makefile
Normal file
@ -0,0 +1,39 @@
|
||||
# Makefile for NCSA's httpd.
|
||||
|
||||
# For normal machines with ANSI compilers
|
||||
CC= cc
|
||||
# For Suns or other non-ANSI platforms
|
||||
#CC= gcc
|
||||
|
||||
# For optimization
|
||||
#CFLAGS= -O2
|
||||
# For debugging information
|
||||
#CFLAGS= -g
|
||||
# If your system does not have strdup(), then do this
|
||||
# CFLAGS = -O2 -DNEED_STRDUP
|
||||
|
||||
# Place here any flags you may need upon linking, such as a flag to
|
||||
# prevent dynamic linking (if desired)
|
||||
LFLAGS=
|
||||
|
||||
# Place here any extra libraries you may need to link to. You
|
||||
# shouldn't have to.
|
||||
EXTRA_LIBS=
|
||||
|
||||
# You shouldn't have to edit anything else.
|
||||
|
||||
OBJS=http_config.o httpd.o http_request.o util.o http_dir.o http_gopher.o \
|
||||
ann_request.o ann_set.o
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $(DEFINES) $<
|
||||
|
||||
all: httpd
|
||||
|
||||
httpd: $(OBJS)
|
||||
$(CC) $(LFLAGS) -o httpd $(OBJS) $(EXTRA_LIBS)
|
||||
|
||||
$(OBJS): Makefile httpd.h
|
||||
|
||||
clean:
|
||||
rm -f httpd $(OBJS)
|
111
README
Normal file
111
README
Normal file
@ -0,0 +1,111 @@
|
||||
NCSA HTTP server
|
||||
================
|
||||
|
||||
Beta Release 0.5
|
||||
|
||||
Final release for Gopher and Group Annotation features. In future releases,
|
||||
Gopher support will not reappear, and Group Annotation features will reappear
|
||||
in a more protocol-supported fashion.
|
||||
|
||||
This code is in the public domain. Specifically, we give to the public
|
||||
domain all rights for future licensing of the source code, all resale
|
||||
rights, and all publishing rights.
|
||||
|
||||
We ask, but do not require, that the following message be included in
|
||||
all derived works:
|
||||
|
||||
Portions developed at the National Center for Supercomputing
|
||||
Applications at the University of Illinois at Urbana-Champaign.
|
||||
|
||||
THE UNIVERSITY OF ILLINOIS GIVES NO WARRANTY, EXPRESSED OR IMPLIED,
|
||||
FOR THE SOFTWARE AND/OR DOCUMENTATION PROVIDED, INCLUDING, WITHOUT
|
||||
LIMITATION, WARRANTY OF MERCHANTABILITY AND WARRANTY OF FITNESS FOR A
|
||||
PARTICULAR PURPOSE.
|
||||
|
||||
|
||||
Now that that's out of the way, we can move on to more interesting
|
||||
things.
|
||||
|
||||
OVERVIEW
|
||||
--------
|
||||
|
||||
You've found the Beta release of NCSA's HTTP server. This code is
|
||||
in testing and development, but is currently fully functional as a
|
||||
simple HTTP server.
|
||||
|
||||
|
||||
NEW IN RELEASE 0.5
|
||||
------------------
|
||||
|
||||
o Slight bug in gopher support fixed
|
||||
o Standalone support added
|
||||
|
||||
NEW IN RELEASE 0.4
|
||||
------------------
|
||||
|
||||
o Added annotation server support. See README.GROUP-ANNOTATIONS.
|
||||
o Added access logging capability.
|
||||
o Added HTTP/1.0 compatibility (3rd+ args of GET command ignored).
|
||||
o Changed handling of full hostnames.
|
||||
o Fixed bug with index.html not being recognized unless a trailing
|
||||
slash was appended to the directory name.
|
||||
o Fixed bug with extraneous null appended to end of file data
|
||||
stream.
|
||||
o Fixed bug handling directory URL's with trailing slashes.
|
||||
o Made a little more Ultrix-friendly.
|
||||
|
||||
NEW IN RELEASE 0.3
|
||||
------------------
|
||||
|
||||
o Security hole (should be the last) fixed
|
||||
o Nearly complete drop-in gopher support. See README.GOPHER for details
|
||||
o Not being able to find the config file does not send back a path
|
||||
|
||||
NEW IN RELEASE 0.2
|
||||
------------------
|
||||
|
||||
o Two security holes fixed
|
||||
o Double slashes in root index fixed
|
||||
o Not being able to find the error files no longer hangs the server
|
||||
|
||||
FEATURES
|
||||
--------
|
||||
|
||||
o Directory aliasing and denial. The server can be configured to
|
||||
map different directories to different places in the filesystem, and
|
||||
easily deny access to a particular directory or file by mapping it to
|
||||
something which does not exist. The interface is designed to be simple.
|
||||
|
||||
o Automatic directory index files and on-the-fly index generation.
|
||||
If the server finds that the client is requesting a directory, it
|
||||
first checks for a file, by default called "index.html" but which can
|
||||
be compiled to any name, and if it does not find such a file it
|
||||
generates an HTML index of the directory and sends it back to the
|
||||
client.
|
||||
|
||||
o Drop-in gopher support. This server is able to automatically
|
||||
interpret gopher directory structures and construct, on the fly, HTML
|
||||
indexes of them. Should be faster than gopherd in HTML mode.
|
||||
|
||||
o Group annotation server capabilities. See
|
||||
README.GROUP-ANNOTATIONS for more details.
|
||||
|
||||
o Access logging. See httpd.h for more details.
|
||||
|
||||
o Designed to be small and low-impact: the entire source is
|
||||
3,000 lines, with full gopher, annotation server, and logging support.
|
||||
|
||||
FUTURE PLANS
|
||||
------------
|
||||
|
||||
o HTTP2 (HTTP/1.0) support.
|
||||
|
||||
o On the fly uncompression.
|
||||
|
||||
That should be all you need. E-mail problems, suggestions for features
|
||||
or documentation, cash donations, food, etc. to httpd@ncsa.uiuc.edu.
|
||||
We encourage you to try and break the server, or compromise its
|
||||
security, and let us know how you did it so we can fix it.
|
||||
|
||||
Rob, Eric, Marc
|
||||
httpd@ncsa.uiuc.edu
|
66
README.GOPHER
Normal file
66
README.GOPHER
Normal file
@ -0,0 +1,66 @@
|
||||
This code is in the public domain. Specifically, we give to the public
|
||||
domain all rights for future licensing of the source code, all resale
|
||||
rights, and all publishing rights.
|
||||
|
||||
We ask, but do not require, that the following message be included in
|
||||
all derived works:
|
||||
|
||||
Portions developed at the National Center for Supercomputing
|
||||
Applications at the University of Illinois at Urbana-Champaign.
|
||||
|
||||
THE UNIVERSITY OF ILLINOIS GIVES NO WARRANTY, EXPRESSED OR IMPLIED,
|
||||
FOR THE SOFTWARE AND/OR DOCUMENTATION PROVIDED, INCLUDING, WITHOUT
|
||||
LIMITATION, WARRANTY OF MERCHANTABILITY AND WARRANTY OF FITNESS FOR A
|
||||
PARTICULAR PURPOSE.
|
||||
|
||||
|
||||
OVERVIEW OF GOPHER SUPPORT
|
||||
--------------------------
|
||||
|
||||
As of beta release 0.3, this server will automatically generate
|
||||
HTML listings of a gopher directory without going through gopherd.
|
||||
This means that . files which are links are processed, and .cap files
|
||||
are processed. Any request which begins with /gopher-data will be
|
||||
considered a gopher request if gopher is turned on. Therefore, mapping
|
||||
/gopher-data to the location of your server's data will allow you to
|
||||
access /gopher-data and get a gopher listing in return.
|
||||
|
||||
These features are new to 0.3, and I would appreciate any and all
|
||||
feedback on its usefulness, features and/or lack thereof, problems,
|
||||
and suggestions.
|
||||
|
||||
NOTE THAT THE GOPHER PROTOCOL IS NOT SUPPORTED in this server;
|
||||
only httpd is supported; thus, Gopher clients will not be able to talk
|
||||
to this server. The point of this feature is to allow existing Gopher
|
||||
data hierarchies to be painlessly transferred into a WWW environment.
|
||||
|
||||
KNOWN PROBLEMS
|
||||
--------------
|
||||
|
||||
o Shell scripts are not executed; i.e. a file of type 0 with #! at
|
||||
the beginning is returned plaintext instead of being executed.
|
||||
|
||||
o There is a slight ordering inconsistency between this server and
|
||||
the real gopherd, i.e. all numbered are placed first, in order, THEN
|
||||
the unnumbered files are placed in alphabetical order. The gopher
|
||||
server fills in the holes with alphabetized entries.
|
||||
|
||||
o Not all gopher features are currently supported, namely the ph
|
||||
interface.
|
||||
|
||||
o Files of type 's', 'G', 'I', and any files which are special and
|
||||
need to be treated in a special way are sent back to the client.
|
||||
Therefore, if the file does not have the proper extension the client
|
||||
may not recognize it. i.e. if a GIF does not have the .gif extension
|
||||
NCSA Mosaic probably won't recognize it.
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
See the file INSTALL for normal and gopher installations. Be sure
|
||||
you define GROK_GOPHER in httpd.h if you are compiling the server, or
|
||||
that you use the binary with gopher support if you are using a
|
||||
precompiled one.
|
||||
|
||||
|
||||
httpd@ncsa.uiuc.edu
|
49
README.GROUP-ANNOTATIONS
Normal file
49
README.GROUP-ANNOTATIONS
Normal file
@ -0,0 +1,49 @@
|
||||
This code is in the public domain. Specifically, we give to the public
|
||||
domain all rights for future licensing of the source code, all resale
|
||||
rights, and all publishing rights.
|
||||
|
||||
We ask, but do not require, that the following message be included in
|
||||
all derived works:
|
||||
|
||||
Portions developed at the National Center for Supercomputing
|
||||
Applications at the University of Illinois at Urbana-Champaign.
|
||||
|
||||
THE UNIVERSITY OF ILLINOIS GIVES NO WARRANTY, EXPRESSED OR IMPLIED,
|
||||
FOR THE SOFTWARE AND/OR DOCUMENTATION PROVIDED, INCLUDING, WITHOUT
|
||||
LIMITATION, WARRANTY OF MERCHANTABILITY AND WARRANTY OF FITNESS FOR A
|
||||
PARTICULAR PURPOSE.
|
||||
|
||||
|
||||
OVERVIEW OF GROUP ANNOTATION SERVER SUPPORT
|
||||
-------------------------------------------
|
||||
|
||||
As of beta release 0.4, this server can optionally be compiled to
|
||||
serve as a group annotation server as well as a normal HTTP server.
|
||||
|
||||
Users of NCSA Mosaic for X version 1.1 and later can set their
|
||||
annotationServer resource to the string "machine.name:port" (e.g.,
|
||||
"www.foo.com:80") to take advantage of a given group annotation
|
||||
server. Other WWW browsers may support use of group annotation
|
||||
servers in the future.
|
||||
|
||||
If you wish to set up httpd to act as a group annotation server,
|
||||
#define ANNOTATIONS in httpd.h and set ANN_DIR, ANN_PORT, and
|
||||
ANN_VIRTUAL_DIR to appropriate values. Make sure that the directory
|
||||
named by ANN_DIR (and virtually named by ANN_VIRTUAL_DIR) is writeable
|
||||
by the user ID you're running httpd under (normally 'nobody'), and
|
||||
make sure enough disk space is free in the partition holding that
|
||||
directory to handle anticipated group annotation activity.
|
||||
|
||||
THE GROUP ANNOTATION SUPPORT OR PROTOCOL MAY CHANGE AT ANY TIME. IT
|
||||
IS NOT AN OFFICIAL OR SUPPORTED PROTOCOL. Among other things, we have
|
||||
yet to address matters of scale, server-based logging, tracking, and
|
||||
notification activities, garbage collection/expiration, and
|
||||
authentication. The current server is merely an experimental proof of
|
||||
concept, and it works fine given its design restraints.
|
||||
|
||||
We would greatly appreciate being notified of any serious use of the
|
||||
group annotation capability in research, educational, academic, or
|
||||
commercial environments, as well as general comments and suggestions.
|
||||
|
||||
|
||||
httpd@ncsa.uiuc.edu
|
729
ann_request.c
Normal file
729
ann_request.c
Normal file
@ -0,0 +1,729 @@
|
||||
/*
|
||||
* ann_request.c: functions to get and process annotation requests
|
||||
*
|
||||
* Eric Bina
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "httpd.h"
|
||||
#include <time.h>
|
||||
|
||||
|
||||
#ifdef ANNOTATIONS
|
||||
|
||||
/*
|
||||
* All the recognized arguments that you can pass in an annotation
|
||||
* request.
|
||||
*/
|
||||
static char *ArgUrl, *ArgGroup, *ArgUser, *ArgTitle, *ArgDate, *ArgAudio;
|
||||
static int ArgLength;
|
||||
|
||||
|
||||
|
||||
#define HASH_TABLE_SIZE 1037
|
||||
|
||||
|
||||
/*
|
||||
* Hash a url to a number used to name a log file.
|
||||
*/
|
||||
static int HashUrl (char *url)
|
||||
{
|
||||
int len, i, val;
|
||||
|
||||
if (!url)
|
||||
return 0;
|
||||
len = strlen (url);
|
||||
if (!len)
|
||||
return 0;
|
||||
val = 0;
|
||||
for (i = 0; i < 10; i++)
|
||||
val += url[(i * val + 7) % len];
|
||||
|
||||
return val % HASH_TABLE_SIZE;
|
||||
}
|
||||
|
||||
|
||||
#define BUFF_SIZE 8192
|
||||
|
||||
/*
|
||||
* Function to read from the passed FILE * until the characer
|
||||
* cstop is reached, EOF, or LINEFEED. Return the length of
|
||||
* the value read (not counting the trailing '\0') in sz.
|
||||
* If escapes is 1, backslash escapes the character immediately after it.
|
||||
*/
|
||||
char *readto(FILE *in, char cstop, int *sz, int escapes)
|
||||
{
|
||||
char *buf;
|
||||
int i, size;
|
||||
|
||||
*sz = 0;
|
||||
buf = (char *)malloc(BUFF_SIZE * sizeof(char));
|
||||
if (buf == NULL)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
for (i=0; i<BUFF_SIZE; i++)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = fgetc(in);
|
||||
if ((escapes)&&(val == '\\'))
|
||||
{
|
||||
val = fgetc(in);
|
||||
}
|
||||
else if (val == cstop)
|
||||
{
|
||||
buf[i] = '\0';
|
||||
break;
|
||||
}
|
||||
else if (val == LINEFEED)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
buf[i] = (char)val;
|
||||
}
|
||||
size = i;
|
||||
while (i == BUFF_SIZE)
|
||||
{
|
||||
char *tptr;
|
||||
|
||||
tptr = buf;
|
||||
buf = (char *)malloc((size + BUFF_SIZE) * sizeof(char));
|
||||
if (buf == NULL)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
bcopy(tptr, buf, size);
|
||||
free(tptr);
|
||||
tptr = (char *)(buf + size);
|
||||
for (i=0; i<BUFF_SIZE; i++)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = fgetc(in);
|
||||
if ((escapes)&&(val == '\\'))
|
||||
{
|
||||
val = fgetc(in);
|
||||
}
|
||||
else if (val == cstop)
|
||||
{
|
||||
tptr[i] = '\0';
|
||||
break;
|
||||
}
|
||||
else if (val == LINEFEED)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
tptr[i] = (char)val;
|
||||
}
|
||||
size += i;
|
||||
}
|
||||
*sz = size;
|
||||
return(buf);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read the next value from the annotation request input stream.
|
||||
* Values are everything from the '=' to the ';', unless the first
|
||||
* character is a '"', in which case it is everything up to the
|
||||
* closing quote.
|
||||
*/
|
||||
char *read_val(FILE *in)
|
||||
{
|
||||
int val;
|
||||
int vsize;
|
||||
char *argval;
|
||||
char *tptr;
|
||||
|
||||
argval = NULL;
|
||||
val = fgetc(in);
|
||||
if (val == '\"')
|
||||
{
|
||||
int junk;
|
||||
|
||||
argval = readto(in, '\"', &vsize, 1);
|
||||
if (argval == NULL)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
tptr = readto(in, ';', &junk, 0);
|
||||
free(tptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *tptr;
|
||||
|
||||
tptr = readto(in, ';', &vsize, 0);
|
||||
if (tptr == NULL)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
argval = (char *)malloc((vsize + 2) * sizeof(char));
|
||||
if (argval == NULL)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
argval[0] = (char)val;
|
||||
strcpy(&argval[1], tptr);
|
||||
free(tptr);
|
||||
vsize++;
|
||||
}
|
||||
|
||||
return(argval);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process the annotation request input stream, and set the values of
|
||||
* all the known accepted parameters.
|
||||
*/
|
||||
void ReadArgs(FILE *in, FILE *out)
|
||||
{
|
||||
int ret, size;
|
||||
char *arg;
|
||||
time_t time_val;
|
||||
char *time_string;
|
||||
|
||||
ArgUrl = NULL;
|
||||
ArgGroup = NULL;
|
||||
ArgUser = NULL;
|
||||
ArgTitle = NULL;
|
||||
ArgDate = NULL;
|
||||
ArgAudio = NULL;
|
||||
ArgLength = -1;
|
||||
|
||||
arg = readto(in, '=', &size, 0);
|
||||
if (arg == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
while (arg[0] != '\0')
|
||||
{
|
||||
if (strncmp(arg, "url", 3) == 0)
|
||||
{
|
||||
ArgUrl = read_val(in);
|
||||
if (ArgUrl == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
else if (strncmp(arg, "group", 5) == 0)
|
||||
{
|
||||
ArgGroup = read_val(in);
|
||||
if (ArgGroup == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
else if (strncmp(arg, "user", 4) == 0)
|
||||
{
|
||||
ArgUser = read_val(in);
|
||||
if (ArgUser == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
else if (strncmp(arg, "title", 5) == 0)
|
||||
{
|
||||
ArgTitle = read_val(in);
|
||||
if (ArgTitle == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
else if (strncmp(arg, "date", 4) == 0)
|
||||
{
|
||||
ArgDate = read_val(in);
|
||||
if (ArgDate == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
else if (strncmp(arg, "audio", 5) == 0)
|
||||
{
|
||||
ArgAudio = read_val(in);
|
||||
if (ArgAudio == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
else if (strncmp(arg, "length", 6) == 0)
|
||||
{
|
||||
char *tptr;
|
||||
|
||||
tptr = read_val(in);
|
||||
if (tptr == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
ArgLength = atoi(tptr);
|
||||
free(tptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *unknown;
|
||||
|
||||
unknown = read_val(in);
|
||||
if (unknown == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
free(unknown);
|
||||
}
|
||||
|
||||
free(arg);
|
||||
arg = readto(in, '=', &size, 0);
|
||||
if (arg == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We allow the client to pass us the date, but we ignore
|
||||
* it and calculate our own.
|
||||
*/
|
||||
time_val = time(NULL);
|
||||
time_string = ctime(&time_val);
|
||||
time_string[strlen(time_string) - 1] = '\0';
|
||||
ArgDate = strdup(time_string);
|
||||
if (ArgDate == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We know we have received a SET_ANN request. Process it.
|
||||
*/
|
||||
void Set_request(FILE *in, FILE *out)
|
||||
{
|
||||
int indx;
|
||||
int ret, hash;
|
||||
LogData *lptr;
|
||||
char *data;
|
||||
|
||||
/*
|
||||
* Read the args, and make sure we have a URL to
|
||||
* set the annotation on.
|
||||
*/
|
||||
ReadArgs(in, out);
|
||||
if (ArgUrl == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
/*
|
||||
fprintf(out, "<PRE>\n");
|
||||
fprintf(out, "Got a Set-Url-Annotations Request\n");
|
||||
fprintf(out, "URL = %s\nGroup = %s\nUser = %s\nDate = %s\n",
|
||||
ArgUrl, ArgGroup, ArgUser, ArgDate);
|
||||
fprintf(out, "Length = %d\n", ArgLength);
|
||||
*/
|
||||
data = NULL;
|
||||
/*
|
||||
* Read the data for this new annotation.
|
||||
*/
|
||||
if (ArgLength > 0)
|
||||
{
|
||||
data = (char *)malloc(ArgLength * sizeof(char));
|
||||
if (data == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
ret = fread(data, sizeof(char), ArgLength, in);
|
||||
if (ret != ArgLength)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock the logfile prepratory to setting the new annotation.
|
||||
*/
|
||||
hash = HashUrl(ArgUrl);
|
||||
lptr = GetLogData(hash);
|
||||
if (lptr == NULL)
|
||||
{
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
indx = MaxIndex(lptr);
|
||||
|
||||
/*
|
||||
* If this is an audio annotation, make the real sound file it
|
||||
* points to, and then put in a standard text annotation.
|
||||
*/
|
||||
if (ArgAudio != NULL)
|
||||
{
|
||||
char *url;
|
||||
|
||||
/* Looks like ArgAudio is a string corresponding to
|
||||
the type of audio -- we should use this. @@@ */
|
||||
url = WriteAudioData(hash, (indx + 1),
|
||||
data, ArgLength, ArgAudio, 0);
|
||||
if (url == NULL)
|
||||
{
|
||||
UnlockIt();
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
if (data)
|
||||
{
|
||||
free((char *)data);
|
||||
}
|
||||
data = (char *)malloc(strlen("This is an audio annotation. <P>\n\nTo hear the annotation, go <A HREF=\"%s\">here</A>. <P>\n") +
|
||||
strlen(url) + 1);
|
||||
if (data == NULL)
|
||||
{
|
||||
UnlockIt();
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
sprintf(data, "This is an audio annotation. <P>\n\nTo hear the annotation, go <A HREF=\"%s\">here</A>. <P>\n", url);
|
||||
ArgLength = strlen(data) + 1;
|
||||
free(url);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the annotation, modify and write the log.
|
||||
* Unlock the lock, and free memory.
|
||||
*/
|
||||
ret = WriteAnnData(ArgUrl, hash, (indx + 1),
|
||||
ArgTitle, ArgUser, ArgDate, data, ArgLength, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
UnlockIt();
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
indx = ret;
|
||||
ret = AddLogData(lptr, indx, ArgUrl);
|
||||
if (ret != 1)
|
||||
{
|
||||
UnlockIt();
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
ret = WriteLogData(lptr);
|
||||
if (ret < 0)
|
||||
{
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
if ((ArgLength > 0)&&(data != NULL))
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
FreeLogData(lptr);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We know we have received a GET_ANN request. Process it.
|
||||
*/
|
||||
void Get_request(FILE *in, FILE *out)
|
||||
{
|
||||
int i;
|
||||
int ret, hash;
|
||||
LogData *lptr;
|
||||
int *alist, acnt;
|
||||
|
||||
/*
|
||||
* Read the args, and make sure we have a URL to
|
||||
* get the annotations on.
|
||||
*/
|
||||
ReadArgs(in, out);
|
||||
if (ArgUrl == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
/*
|
||||
fprintf(out, "<PLAINTEXT>\n");
|
||||
fprintf(out, "Got a Get-Url-Annotations Request\n");
|
||||
fprintf(out, "URL = %s\nGroup = %s\nUser = %s\nDate = %s\n",
|
||||
ArgUrl, ArgGroup, ArgUser, ArgDate);
|
||||
fprintf(out, "Length = %d\n", ArgLength);
|
||||
*/
|
||||
|
||||
/*
|
||||
* Read and lock the log file.
|
||||
*/
|
||||
hash = HashUrl(ArgUrl);
|
||||
lptr = GetLogData(hash);
|
||||
if (lptr == NULL)
|
||||
{
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
|
||||
ret = FindLogData(lptr, ArgUrl, &alist, &acnt);
|
||||
/*
|
||||
* If there are annotation on this URL, we need to read each one,
|
||||
* and extract the title, user, and date to make the list
|
||||
* of group annotation to be placed at the end of the
|
||||
* document.
|
||||
*/
|
||||
if ((ret > 0)&&(acnt > 0))
|
||||
{
|
||||
char *host;
|
||||
|
||||
host = full_hostname();
|
||||
fprintf(out, "<H2>Group Annotations</H2>\n");
|
||||
fprintf(out, "<UL>\n");
|
||||
for (i=0; i<acnt; i++)
|
||||
{
|
||||
ret = ReadAnnData(hash, alist[i],
|
||||
&ArgTitle, &ArgUser, &ArgDate);
|
||||
if (ret != 1)
|
||||
{
|
||||
UnlockIt();
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
fprintf(out, "<LI> <A HREF=\"http://%s:%d%s/%d-%d.html\">%s</A> (%s)\n",
|
||||
host, ANN_PORT, ANN_VIRTUAL_DIR, hash,
|
||||
alist[i],
|
||||
ArgTitle, ArgDate);
|
||||
}
|
||||
fprintf(out, "</UL>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
UnlockIt();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We know we have received a CHANGE_ANN request. Process it.
|
||||
*/
|
||||
void Change_request(FILE *in, FILE *out)
|
||||
{
|
||||
int i, ret, hash;
|
||||
LogData *lptr;
|
||||
char *file, *data, *ptr;
|
||||
char *url;
|
||||
|
||||
/*
|
||||
* Read the args, and make sure we have a URL to
|
||||
* change the annotation on.
|
||||
*/
|
||||
ReadArgs(in, out);
|
||||
if (ArgUrl == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
/*
|
||||
fprintf(out, "<PRE>\n");
|
||||
fprintf(out, "Got a Delete-Url-Annotations Request\n");
|
||||
fprintf(out, "URL = %s\nGroup = %s\nUser = %s\nDate = %s\n",
|
||||
ArgUrl, ArgGroup, ArgUser, ArgDate);
|
||||
fprintf(out, "Length = %d\n", ArgLength);
|
||||
*/
|
||||
data = NULL;
|
||||
/*
|
||||
* Get the data for the new annotation.
|
||||
*/
|
||||
if (ArgLength > 0)
|
||||
{
|
||||
data = (char *)malloc(ArgLength * sizeof(char));
|
||||
if (data == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
ret = fread(data, sizeof(char), ArgLength, in);
|
||||
if (ret != ArgLength)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct the full path to the annotation to
|
||||
* be changed.
|
||||
*/
|
||||
file = (char *)malloc(MAX_STRING_LEN * sizeof(char));
|
||||
if (file == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
ptr = strrchr(ArgUrl, '/');
|
||||
if (ptr == NULL)
|
||||
{
|
||||
sprintf(file, "%s/%s", ANN_DIR, ArgUrl);
|
||||
if (sscanf(ArgUrl, "%d-%d.html", &hash, &i) !=
|
||||
2)
|
||||
{
|
||||
free(file);
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(file, "%s%s", ANN_DIR, ptr);
|
||||
ptr++;
|
||||
if (sscanf(ptr, "%d-%d.html", &hash, &i) !=
|
||||
2)
|
||||
{
|
||||
free(file);
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and lock the logfile.
|
||||
*/
|
||||
lptr = GetLogData(hash);
|
||||
if (lptr == NULL)
|
||||
{
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
|
||||
url = HashtoUrl(lptr, i);
|
||||
if (url == NULL)
|
||||
{
|
||||
UnlockIt();
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the old annotation.
|
||||
*/
|
||||
ret = unlink(file);
|
||||
if (ret != 0)
|
||||
{
|
||||
fprintf(out, "Deletion of %s failed.\n", file);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(out, "Deletion of %s success!\n", file);
|
||||
}
|
||||
free(file);
|
||||
|
||||
/*
|
||||
* Write the new annotation.
|
||||
*/
|
||||
ret = WriteAnnData(url, hash, i,
|
||||
ArgTitle, ArgUser, ArgDate, data, ArgLength, 1);
|
||||
free(url);
|
||||
if (ret < 0)
|
||||
{
|
||||
UnlockIt();
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
|
||||
UnlockIt();
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We know we have received a DELETE_ANN request. Process it.
|
||||
*/
|
||||
void Delete_request(FILE *in, FILE *out)
|
||||
{
|
||||
int i, ret, hash;
|
||||
char *data, *ptr;
|
||||
LogData *lptr;
|
||||
|
||||
/*
|
||||
* Read the args, and make sure we have a URL to
|
||||
* delete
|
||||
*/
|
||||
ReadArgs(in, out);
|
||||
if (ArgUrl == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
/*
|
||||
fprintf(out, "<PRE>\n");
|
||||
fprintf(out, "Got a Delete-Url-Annotations Request\n");
|
||||
fprintf(out, "URL = %s\nGroup = %s\nUser = %s\nDate = %s\n",
|
||||
ArgUrl, ArgGroup, ArgUser, ArgDate);
|
||||
fprintf(out, "Length = %d\n", ArgLength);
|
||||
*/
|
||||
|
||||
/*
|
||||
* Construct the full path to the annotation to be deleted.
|
||||
*/
|
||||
data = (char *)malloc(MAX_STRING_LEN * sizeof(char));
|
||||
if (data == NULL)
|
||||
{
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
ptr = strrchr(ArgUrl, '/');
|
||||
if (ptr == NULL)
|
||||
{
|
||||
sprintf(data, "%s/%s", ANN_DIR, ArgUrl);
|
||||
if (sscanf(ArgUrl, "%d-%d.html", &hash, &i) !=
|
||||
2)
|
||||
{
|
||||
free(data);
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(data, "%s%s", ANN_DIR, ptr);
|
||||
ptr++;
|
||||
if (sscanf(ptr, "%d-%d.html", &hash, &i) !=
|
||||
2)
|
||||
{
|
||||
free(data);
|
||||
client_error(out, BAD_ANN_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read an lock the log file
|
||||
*/
|
||||
lptr = GetLogData(hash);
|
||||
if (lptr == NULL)
|
||||
{
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete this annotation from the logfile.
|
||||
*/
|
||||
ret = DeleteLogData(lptr, i);
|
||||
if (ret != 1)
|
||||
{
|
||||
UnlockIt();
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the actual annotation, and any associated
|
||||
* audio annotations.
|
||||
*/
|
||||
ret = unlink(data);
|
||||
if (ret != 0)
|
||||
{
|
||||
fprintf(out, "Deletion of %s failed.\n", data);
|
||||
}
|
||||
else
|
||||
{
|
||||
char afile[MAX_STRING_LEN];
|
||||
|
||||
fprintf(out, "Deletion of %s success!\n", data);
|
||||
/*
|
||||
* Remove any associated audio files
|
||||
*/
|
||||
sprintf(afile, "%s.au", data);
|
||||
unlink(afile);
|
||||
sprintf(afile, "%s.aiff", data);
|
||||
unlink(afile);
|
||||
}
|
||||
free(data);
|
||||
|
||||
/*
|
||||
* Write the new log, unlock it, and free the data.
|
||||
*/
|
||||
ret = WriteLogData(lptr);
|
||||
if (ret < 0)
|
||||
{
|
||||
client_error(out, ANN_SERVER_ERROR);
|
||||
}
|
||||
FreeLogData(lptr);
|
||||
}
|
||||
|
||||
#endif /* ANNOTATIONS */
|
129
contrib/gopher_fixes
Normal file
129
contrib/gopher_fixes
Normal file
@ -0,0 +1,129 @@
|
||||
9a10
|
||||
> #define ROOT_MENU "Root Menu"
|
||||
11d11
|
||||
<
|
||||
149a150,165
|
||||
> /* /gopher-data/ hack by Ian C Blenke */
|
||||
> if(ar[i]->host[0]) {
|
||||
> char host[MAX_STRING_LEN];
|
||||
>
|
||||
> HOSTNAME(host,MAX_STRING_LEN);
|
||||
> if(strncmp(ar[i]->host, host, strlen(host))==0)
|
||||
> /* problem: if we fall through here we may get // */
|
||||
> if(ar[i]->path[0]) {
|
||||
> fprintf(fd,
|
||||
> "<LI> <A NAME=%d HREF=\"/gopher-data%s\"",
|
||||
> lnum++, (ar[i]->path)+1);
|
||||
> fprintf(fd,">%s</A>%c",ar[i]->name,LINEFEED);
|
||||
> break;
|
||||
> } /* fall through if not type 0 or 1 */
|
||||
> }
|
||||
>
|
||||
291a308,359
|
||||
> /* GN - Patch #1 - for looks - Ian C. Blenke */
|
||||
> void get_link_name_from_dir(char *dir, char *munge, char *return_name)
|
||||
> {
|
||||
> char munged_name[MAX_STRING_LEN];
|
||||
> int i, len, count;
|
||||
> char dir_name[MAX_STRING_LEN];
|
||||
> gopher_ent head;
|
||||
> gopher_ent *p, *r;
|
||||
>
|
||||
> /* strip out the actual parent path name */
|
||||
> len=strlen(dir);
|
||||
> while(len>0)
|
||||
> {
|
||||
> if(dir[len]=='/') break;
|
||||
> len--;
|
||||
> }
|
||||
> strncpy(dir_name, dir, len);
|
||||
> dir_name[len]='\0';
|
||||
> /* strip out the virtual parent path name */
|
||||
> strcpy(munged_name, munge+GOPHER_LOC_LEN);
|
||||
> len=strlen(munged_name);
|
||||
> if((len==0)||(len==1))
|
||||
> {
|
||||
> strcpy(return_name, ROOT_MENU);
|
||||
> return;
|
||||
> }
|
||||
> while(len>1)
|
||||
> {
|
||||
> if(munged_name[len]=='/') break;
|
||||
> len--;
|
||||
> }
|
||||
> munged_name[len-1]='\0';
|
||||
> strcpy(munged_name, munged_name+len);
|
||||
>
|
||||
> /* parse the parent menu file */
|
||||
> head.next=NULL;
|
||||
> count=add_links_from_file(dir_name, "menu", &head);
|
||||
> p=&head;
|
||||
>
|
||||
> /* loop through the chain */
|
||||
> for(i=0; i<count; i++)
|
||||
> {
|
||||
> if(strcmp((p->path)+2, munged_name)==0)
|
||||
> strcpy(return_name, p->name);
|
||||
> r=p->next;
|
||||
> free(p);
|
||||
> p=r;
|
||||
> if(p==NULL) break;
|
||||
> }
|
||||
> return;
|
||||
> }
|
||||
>
|
||||
293,294c361
|
||||
< DIR *d;
|
||||
< struct DIR_TYPE *dstruct;
|
||||
---
|
||||
> FILE *fmenu;
|
||||
298a366,367
|
||||
> char menu_name[MAX_STRING_LEN];
|
||||
> char parent_name[MAX_STRING_LEN];
|
||||
305a375
|
||||
> make_full_path(name, "menu", menu_name);
|
||||
307,308c377,378
|
||||
< if(!(d=opendir(name)))
|
||||
< client_error(fd,BAD_FILE);
|
||||
---
|
||||
> if(!(fmenu=fopen(menu_name, "r")))
|
||||
> client_error(fd, BAD_FILE);
|
||||
310,311c380
|
||||
< while(dstruct=readdir(d)) {
|
||||
< char *fn;
|
||||
---
|
||||
> fclose(fmenu);
|
||||
313,315c382
|
||||
< fn=dstruct->d_name;
|
||||
< if(ignore(fn))
|
||||
< continue;
|
||||
---
|
||||
> num_entries=add_links_from_file(name, "menu", &head);
|
||||
317,329c384,391
|
||||
< if(fn[0]=='.') {
|
||||
< if(!strcmp(fn,".cap"))
|
||||
< cap_file=1;
|
||||
< else
|
||||
< num_entries+=add_links_from_file(name,fn,&head);
|
||||
< }
|
||||
< else {
|
||||
< add_local_link(fn,&head);
|
||||
< num_entries++;
|
||||
< }
|
||||
< }
|
||||
< fprintf(fd,"<TITLE> Gopher Index of %s</TITLE>%c",unmunged_name,LINEFEED);
|
||||
< fprintf(fd,"<H1>Gopher Index of %s</H1>%c",unmunged_name,LINEFEED);
|
||||
---
|
||||
> get_link_name_from_dir(name, unmunged_name, parent_name);
|
||||
> fprintf(fd,"<HEADER>%c",LINEFEED);
|
||||
> fprintf(fd,"<TITLE> %s </TITLE>%c",parent_name,LINEFEED);
|
||||
> fprintf(fd,"<BASE HREF=\"http://su102w.ess.harris.com\">%c", LINEFEED);
|
||||
> fprintf(fd,"</HEADER>%c",LINEFEED);
|
||||
> fprintf(fd,"<BODY>%c", LINEFEED);
|
||||
> fprintf(fd,"<H1>Harris ESS InfoServer Gopher</H1>%c",LINEFEED);
|
||||
> fprintf(fd,"<H2> %s </H2>%c", parent_name, LINEFEED);
|
||||
338c400
|
||||
< closedir(d);
|
||||
---
|
||||
> fprintf(fd,"</BODY>%c", LINEFEED);
|
||||
|
340
contrib/listen.c
Normal file
340
contrib/listen.c
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Startup program for netrek. Listens for connections, then forks off
|
||||
* servers. Based on code written by Brett McCoy, but heavily modified.
|
||||
*
|
||||
* To compile, first change the defines for SERVER, LOGFILE, DEF_PORT to what
|
||||
* you want. Then use:
|
||||
*
|
||||
* cc -O listen.c -o listen
|
||||
*
|
||||
* 'listen' only accepts one option, '-p' to override the default port to
|
||||
* listen on. Use it like: 'listen -p 2592'.
|
||||
*
|
||||
* Note that descriptor 2 is duped to descriptor 1, so that stdout and
|
||||
* stderr go to the same file.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include <varargs.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SERVER "/stg1/rob/netrek/lib/ntserv" /* filename of server */
|
||||
#define LOGFILE "/dev/null" /* filename of log file */
|
||||
#define DEF_PORT 2592 /* port to listen on */
|
||||
|
||||
int listenSock;
|
||||
short port = DEF_PORT;
|
||||
char *program;
|
||||
|
||||
char * dateTime();
|
||||
void detach();
|
||||
void getConnections();
|
||||
void getListenSock();
|
||||
void multClose();
|
||||
void reaper();
|
||||
void terminate();
|
||||
|
||||
/*
|
||||
* Error reporting functions ripped from my library.
|
||||
*/
|
||||
void syserr();
|
||||
void warnerr();
|
||||
void fatlerr();
|
||||
void err();
|
||||
char *lasterr();
|
||||
|
||||
struct hostent * gethostbyaddr();
|
||||
char * inet_ntoa();
|
||||
|
||||
|
||||
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
|
||||
if (argv[i][0] == '-') {
|
||||
if (strcmp(argv[i]+1, "p") == 0) {
|
||||
port = atoi(argv[i+1]);
|
||||
} else {
|
||||
fatlerr(1, 0, "unrecognized option: %s.", argv[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
program = argv[0]; /* let err functions know our name */
|
||||
detach(); /* detach from terminal, close files, etc. */
|
||||
getListenSock();
|
||||
signal(SIGCHLD, reaper);
|
||||
signal(SIGTERM, terminate);
|
||||
|
||||
while (1) {
|
||||
getConnections();
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Detach process in various ways.
|
||||
*/
|
||||
|
||||
void
|
||||
detach() {
|
||||
int fd, rc, mode;
|
||||
|
||||
mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR;
|
||||
if ((fd = open(LOGFILE, O_WRONLY|O_CREAT|O_APPEND, mode)) == -1)
|
||||
syserr(1, "detach", "couldn't open log file. [%s]", dateTime());
|
||||
dup2(fd, 2);
|
||||
dup2(fd, 1);
|
||||
multClose(1, 2, -1); /* close all other file descriptors */
|
||||
warnerr(0, "started at %s on port %d.", dateTime(), port);
|
||||
|
||||
/* fork once to escape the shells job control */
|
||||
if ((rc = fork()) > 0)
|
||||
exit(0);
|
||||
else if (rc < 0)
|
||||
syserr(1, "detach", "couldn't fork. [%s]", dateTime());
|
||||
|
||||
/* now detach from the controlling terminal */
|
||||
if ((fd = open("/dev/tty", O_RDWR, 0)) == -1) {
|
||||
warnerr("detach", "couldn't open tty, assuming still okay. [%s]",
|
||||
dateTime());
|
||||
return;
|
||||
}
|
||||
ioctl(fd, TIOCNOTTY, 0);
|
||||
close(fd);
|
||||
setsid(); /* make us a new process group/session */
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*/
|
||||
|
||||
void
|
||||
getListenSock() {
|
||||
struct sockaddr_in addr;
|
||||
|
||||
if ((listenSock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
syserr(1, "getListenSock", "can't create listen socket. [%s]",
|
||||
dateTime());
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
if (bind(listenSock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
|
||||
syserr(1, "getListenSock", "can't bind listen socket. [%s]",
|
||||
dateTime());
|
||||
|
||||
if (listen(listenSock, 5) != 0)
|
||||
syserr(1, "getListenSock", "can't listen to socket. [%s]", dateTime());
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*/
|
||||
|
||||
void
|
||||
getConnections() {
|
||||
int len, sock, pid;
|
||||
struct sockaddr_in addr;
|
||||
struct hostent *he;
|
||||
char host[100];
|
||||
|
||||
len = sizeof(addr);
|
||||
while ((sock = accept(listenSock, (struct sockaddr *) &addr, &len)) < 0) {
|
||||
/* if we got interrupted by a dying child, just try again */
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
syserr(1, "getConnections",
|
||||
"accept() on listen socket failed. [%s]", dateTime());
|
||||
}
|
||||
|
||||
/* get the host name */
|
||||
he = gethostbyaddr((char *) &addr.sin_addr.s_addr,
|
||||
sizeof(addr.sin_addr.s_addr), AF_INET);
|
||||
if (he != 0)
|
||||
strcpy(host, he->h_name);
|
||||
else
|
||||
strcpy(host, inet_ntoa((u_long) &addr.sin_addr));
|
||||
warnerr(0, "connection from %s. [%s]", host, dateTime());
|
||||
|
||||
/* fork off a server */
|
||||
if ((pid = fork()) == 0) {
|
||||
dup2(sock, 0);
|
||||
multClose(0, 1, 2, -1); /* close everything else */
|
||||
if (execl(SERVER, SERVER, host, 0) != 0)
|
||||
syserr(1, "getConnections", "couldn't execl %s as the server. [%s]",
|
||||
SERVER, dateTime());
|
||||
} else if (pid < 0)
|
||||
syserr(1, "getConnections", "can't fork. [%s]", dateTime());
|
||||
|
||||
close(sock);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Returns a string containing the date and time. String area is static
|
||||
* and reused.
|
||||
*/
|
||||
|
||||
char *
|
||||
dateTime() {
|
||||
time_t t;
|
||||
char *s;
|
||||
|
||||
time(&t);
|
||||
s = ctime(&t);
|
||||
s[24] = '\0'; /* wipe-out the newline */
|
||||
return s;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Handler for SIGTERM. Closes and shutdowns everything.
|
||||
*/
|
||||
|
||||
void
|
||||
terminate() {
|
||||
int s;
|
||||
|
||||
fatlerr(1, "terminate", "killed. [%s]", dateTime());
|
||||
|
||||
/* shutdown and close everything */
|
||||
for (s = getdtablesize(); s >= 0; s--) {
|
||||
shutdown(s, 2);
|
||||
close(s);
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Waits on zombie children.
|
||||
*/
|
||||
|
||||
void
|
||||
reaper() {
|
||||
while (wait3(0, WNOHANG, 0) > 0);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Close all file descriptors except the ones specified in the argument list.
|
||||
* The list of file descriptors is terminated with -1 as the last arg.
|
||||
*/
|
||||
|
||||
void
|
||||
multClose(va_alist)
|
||||
va_dcl
|
||||
{
|
||||
va_list args;
|
||||
int fds[100], nfds, fd, ts, i, j;
|
||||
|
||||
/* get all descriptors to be saved into the array fds */
|
||||
va_start(args);
|
||||
for (nfds = 0; nfds < 99; nfds++) {
|
||||
if ((fd = va_arg(args, int)) == -1)
|
||||
break;
|
||||
else
|
||||
fds[nfds] = fd;
|
||||
}
|
||||
|
||||
ts = getdtablesize();
|
||||
|
||||
/* close all descriptors, but first check the fds array to see if this
|
||||
* one is an exception */
|
||||
for (i = 0; i < ts; i++) {
|
||||
for (j = 0; j < nfds; j++)
|
||||
if (i == fds[j]) break;
|
||||
if (j == nfds) close(i);
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Error reporting functions taken from my library.
|
||||
*/
|
||||
|
||||
extern int sys_nerr;
|
||||
extern char *sys_errlist[];
|
||||
extern int errno;
|
||||
|
||||
void
|
||||
syserr(va_alist)
|
||||
va_dcl
|
||||
{
|
||||
va_list args;
|
||||
int rc;
|
||||
|
||||
va_start(args);
|
||||
rc = va_arg(args, int);
|
||||
err(args);
|
||||
if (errno < sys_nerr)
|
||||
fprintf(stderr, " system message: %s\n", sys_errlist[errno]);
|
||||
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
void
|
||||
warnerr(va_alist)
|
||||
va_dcl
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args);
|
||||
err(args);
|
||||
}
|
||||
|
||||
void
|
||||
fatlerr(va_alist)
|
||||
va_dcl
|
||||
{
|
||||
va_list args;
|
||||
int rc;
|
||||
|
||||
va_start(args);
|
||||
rc = va_arg(args, int);
|
||||
err(args);
|
||||
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
void
|
||||
err(args)
|
||||
va_list args;
|
||||
{
|
||||
|
||||
char *func, *fmt;
|
||||
|
||||
if (program != 0)
|
||||
fprintf(stderr, "%s", program);
|
||||
func = va_arg(args, char *);
|
||||
if (func != 0 && strcmp(func, "") != 0)
|
||||
fprintf(stderr, "(%s)", func);
|
||||
fprintf(stderr, ": ");
|
||||
|
||||
fmt = va_arg(args, char *);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fputc('\n', stderr);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
char *
|
||||
lasterr() {
|
||||
if (errno < sys_nerr)
|
||||
return sys_errlist[errno];
|
||||
else
|
||||
return "No message text for this error.";
|
||||
}
|
||||
|
224
contrib/standalone.c
Normal file
224
contrib/standalone.c
Normal file
@ -0,0 +1,224 @@
|
||||
/* Main program for httpd - allows operation as a standalone program
|
||||
* without running from inetd or as an inetd child.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/wait.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
extern int getopt(int argc, char ** argv, const char * optstring);
|
||||
extern char *optarg;
|
||||
extern int optind, opterr;
|
||||
|
||||
#include "httpd.h"
|
||||
|
||||
void
|
||||
DoNothing()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
SetAlarm()
|
||||
{
|
||||
alarm(10);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char ** argv)
|
||||
{
|
||||
int srvsock;
|
||||
struct sockaddr_in iso;
|
||||
int srvport;
|
||||
char * progname;
|
||||
char * config_file=CONFIG_FILE;
|
||||
char * portname = "http";
|
||||
int standalone = 0;
|
||||
int inetd = 0;
|
||||
int badarg = 0;
|
||||
int c;
|
||||
char * errptr;
|
||||
struct sigaction sa;
|
||||
|
||||
memset((void *)&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_handler = DoNothing;
|
||||
sigfillset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGCLD, &sa, NULL);
|
||||
sa.sa_handler = SetAlarm;
|
||||
sigaction(SIGALRM, &sa, NULL);
|
||||
alarm(10);
|
||||
errno = 0;
|
||||
progname = strrchr(argv[0], '/');
|
||||
if (progname == NULL) {
|
||||
progname = argv[0];
|
||||
} else {
|
||||
++progname;
|
||||
}
|
||||
while ((c = getopt(argc, argv, "Hf:p:si")) != EOF) {
|
||||
switch(c){
|
||||
case 'f':
|
||||
config_file = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
fprintf(stderr,"\
|
||||
%s - a simple http daemon\n\
|
||||
Usage: %s [-H] [-f file] [-p port] [-s] [-i]\n\
|
||||
\n\
|
||||
-H print this message and exit\n\
|
||||
-f file specify name of config file (default is %s)\n\
|
||||
-p port specify symbolic or numeric port to listen on (default is http)\n\
|
||||
-s act as a standalone server rather than run under inetd\n\
|
||||
-i assume startup was from inetd.\n\
|
||||
\n\
|
||||
Note that one of the -i or -s options must be given.\n",
|
||||
progname, progname, CONFIG_FILE);
|
||||
exit(0);
|
||||
break;
|
||||
case 'p':
|
||||
portname = optarg;
|
||||
break;
|
||||
case 's':
|
||||
standalone = 1;
|
||||
inetd = 0;
|
||||
break;
|
||||
case 'i':
|
||||
inetd = 1;
|
||||
standalone = 0;
|
||||
break;
|
||||
default:
|
||||
badarg = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc > 0) {
|
||||
badarg = 1;
|
||||
}
|
||||
if (badarg) {
|
||||
fprintf(stderr, "%s: incorrect arguments, use -H option for help.\n",
|
||||
progname);
|
||||
exit(2);
|
||||
}
|
||||
if (standalone == 0 && inetd == 0) {
|
||||
fprintf(stderr,
|
||||
"%s: must give one of -i or -s options (see -H for help).\n",
|
||||
progname);
|
||||
exit(2);
|
||||
}
|
||||
if (inetd) {
|
||||
/* If this is supposed to run from inetd, just start processing requests.
|
||||
*/
|
||||
read_config(config_file,stdout);
|
||||
process_request(stdin,stdout);
|
||||
|
||||
fclose(stdin);
|
||||
fclose(stdout);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* This must be a standalone server - do the inetd work myself, forking
|
||||
* off a child to handle each new connection.
|
||||
*/
|
||||
|
||||
srvport = (int)strtol(portname, &errptr, 10);
|
||||
if (errptr == NULL || errptr == portname || *errptr != '\0') {
|
||||
struct servent *sp;
|
||||
sp = getservbyname(portname, "tcp");
|
||||
if (sp == NULL) {
|
||||
fprintf(stderr, "%s: unable to lookup service \"%s\".\n",
|
||||
progname, portname);
|
||||
exit(2);
|
||||
}
|
||||
srvport = sp->s_port;
|
||||
}
|
||||
srvsock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (srvsock < 0) {
|
||||
fprintf(stderr, "%s: socket() call failed with errno %d (%s)\n",
|
||||
progname, errno, strerror(errno));
|
||||
exit(2);
|
||||
}
|
||||
memset(&iso, 0, sizeof(iso));
|
||||
iso.sin_family = AF_INET;
|
||||
iso.sin_addr.s_addr = INADDR_ANY;
|
||||
iso.sin_port = srvport;
|
||||
errno = 0;
|
||||
if (bind(srvsock, (struct sockaddr *)&iso, sizeof(iso)) == -1) {
|
||||
fprintf(stderr, "%s: bind() call failed with errno %d (%s)\n",
|
||||
progname, errno, strerror(errno));
|
||||
exit(2);
|
||||
}
|
||||
errno = 0;
|
||||
if (listen(srvsock, 5) == -1) {
|
||||
fprintf(stderr, "%s: listen() call failed with errno %d (%s)\n",
|
||||
progname, errno, strerror(errno));
|
||||
exit(2);
|
||||
}
|
||||
for ( ; ; ) {
|
||||
struct sockaddr_in fromaddr;
|
||||
int fromlen;
|
||||
int newfd;
|
||||
int childpid;
|
||||
|
||||
memset(&fromaddr, 0, sizeof(fromaddr));
|
||||
fromaddr.sin_family = AF_INET;
|
||||
fromlen = sizeof(fromaddr);
|
||||
errno = 0;
|
||||
newfd = accept(srvsock, (struct sockaddr *)&fromaddr, &fromlen);
|
||||
if (newfd < 0) {
|
||||
if (errno == EINTR) {
|
||||
/* This must have been a SIGCLD or SIGALRM awakening us from a
|
||||
* deep slumber. Just reap children to get rid of zombies and
|
||||
* loop again.
|
||||
*/
|
||||
int stat;
|
||||
int rval;
|
||||
|
||||
while ((rval = waitpid((pid_t)-1, &stat, WNOHANG)) > 0);
|
||||
} else {
|
||||
fprintf(stderr, "%s: accept() call failed with errno %d (%s)\n",
|
||||
progname, errno, strerror(errno));
|
||||
exit(2);
|
||||
}
|
||||
} else {
|
||||
childpid = fork();
|
||||
if (childpid < 0) {
|
||||
fprintf(stderr, "%s: fork() call failed with errno %d (%s)\n",
|
||||
progname, errno, strerror(errno));
|
||||
exit(2);
|
||||
} else if (childpid == 0) {
|
||||
/* This is the child. Set stdin and stderr to newfd and
|
||||
* process httpd type stuff. Alos clear any alarms and
|
||||
* signal handlers.
|
||||
*/
|
||||
alarm(0);
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sigaction(SIGALRM, &sa, NULL);
|
||||
sigaction(SIGCLD, &sa, NULL);
|
||||
close(0);
|
||||
close(1);
|
||||
dup2(newfd,0);
|
||||
dup2(newfd,1);
|
||||
close(newfd);
|
||||
setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
|
||||
read_config(config_file,stderr);
|
||||
process_request(stdin,stdout);
|
||||
fclose(stdin);
|
||||
fclose(stdout);
|
||||
exit(0);
|
||||
} else {
|
||||
/* In parent, just close the new socket and listen again
|
||||
*/
|
||||
close(newfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
errors/bad_ann_request.html
Normal file
5
errors/bad_ann_request.html
Normal file
@ -0,0 +1,5 @@
|
||||
<TITLE>Annotation error</TITLE>
|
||||
<H1>Bad Annotation Request</H1>
|
||||
|
||||
|
||||
Your World Wide Web browser sent an annotation request which this server was unable to understand.
|
6
errors/bad_ann_server.html
Normal file
6
errors/bad_ann_server.html
Normal file
@ -0,0 +1,6 @@
|
||||
<TITLE>Misconfigured Annotation Server</TITLE>
|
||||
<H1>Misconfigured Annotation Server</H1>
|
||||
|
||||
|
||||
This annotation server has not been set up properly or has been corrupted.
|
||||
Please contact the administrators to inform them of this fact.
|
7
errors/bad_file.html
Normal file
7
errors/bad_file.html
Normal file
@ -0,0 +1,7 @@
|
||||
<TITLE>Bad File Request</TITLE>
|
||||
|
||||
<H1>Bad File Request</H1>
|
||||
|
||||
|
||||
Your browser attempted to access a file which doesn't exist on this
|
||||
server, or a file for which it does not have permission to read.
|
6
errors/bad_request.html
Normal file
6
errors/bad_request.html
Normal file
@ -0,0 +1,6 @@
|
||||
<TITLE>Browser error</TITLE>
|
||||
<H1>Bad Request</H1>
|
||||
|
||||
|
||||
Your World Wide Web browser sent a request which this server was unable
|
||||
to understand.
|
6
errors/bad_server.html
Normal file
6
errors/bad_server.html
Normal file
@ -0,0 +1,6 @@
|
||||
<TITLE>Misconfigured Server</TITLE>
|
||||
<H1>Misconfigured Server</H1>
|
||||
|
||||
|
||||
This server has not been set up properly by the maintainers of this node.
|
||||
Please contact the administrators to inform them of this fact.
|
8
errors/gopher_error.html
Normal file
8
errors/gopher_error.html
Normal file
@ -0,0 +1,8 @@
|
||||
<TITLE>Gopher Setup Error</TITLE>
|
||||
<H1>Gopher Setup Error</H1>
|
||||
|
||||
This server had a problem reading a gopher directory. Please contact its
|
||||
administrators. <P>
|
||||
|
||||
|
||||
|
7
errors/unimplemented.html
Normal file
7
errors/unimplemented.html
Normal file
@ -0,0 +1,7 @@
|
||||
<TITLE>Unimplemented Request</TITLE>
|
||||
<H1>Unimplemented Request</H1>
|
||||
|
||||
|
||||
|
||||
Your browser requested a service which this server does not know how to
|
||||
resolve.
|
94
http_config.c
Normal file
94
http_config.c
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* http_config.c: auxillary functions for reading httpd's config file
|
||||
* and converting filenames into a namespace
|
||||
*
|
||||
* Rob McCool 3/21/93
|
||||
*
|
||||
*/
|
||||
|
||||
#include "httpd.h"
|
||||
|
||||
typedef struct {
|
||||
char name[MAX_STRING_LEN];
|
||||
char real[MAX_STRING_LEN];
|
||||
} alias;
|
||||
|
||||
static int num_aliases;
|
||||
alias aliases[MAX_ALIASES];
|
||||
|
||||
|
||||
char original_name[MAX_STRING_LEN]; /* !!! FIX THIS !!! */
|
||||
|
||||
void unmunge_name(char *name)
|
||||
{
|
||||
strcpy(name,original_name);
|
||||
}
|
||||
|
||||
void translate_name(char *name)
|
||||
{
|
||||
register int x,l;
|
||||
|
||||
strcpy(original_name,name);
|
||||
|
||||
for(x=0;x<num_aliases;x++) {
|
||||
l=strlen(aliases[x].name);
|
||||
if(!(strncmp(name,aliases[x].name,l)))
|
||||
strsubfirst(l,name,aliases[x].real);
|
||||
}
|
||||
}
|
||||
|
||||
/* debugging */
|
||||
void dump_aliases()
|
||||
{
|
||||
register int x;
|
||||
char blorf[MAX_STRING_LEN];
|
||||
|
||||
for(x=0;x<num_aliases;x++)
|
||||
printf("%s:%s\n",aliases[x].name,aliases[x].real);
|
||||
strcpy(blorf,"./../.././.././.././../etc/passwd");
|
||||
translate_name(blorf);
|
||||
printf("translated %s to %s\n","./../.././.././.././../etc/passwd",blorf);
|
||||
strcpy(blorf,"../.././../../../../../././../././etc/passwd");
|
||||
translate_name(blorf);
|
||||
}
|
||||
|
||||
int read_config(char *name, FILE *errors)
|
||||
{
|
||||
FILE *cfg;
|
||||
char config_line[MAX_STRING_LEN];
|
||||
|
||||
if(!(cfg=fopen(name,"r")))
|
||||
server_error(errors,CONFIG_FILENAME);
|
||||
|
||||
num_aliases=0;
|
||||
while(fgets(config_line,MAX_STRING_LEN,cfg))
|
||||
if((config_line[0] != '\0') && (config_line[0] != '#') &&
|
||||
(config_line[0] != '\n')) {
|
||||
register int x=0,y,z;
|
||||
|
||||
x=0;
|
||||
while((config_line[x] != '\0') && (config_line[x] != ':'))
|
||||
x++;
|
||||
if(config_line[x] == '\0')
|
||||
server_error(errors,CONFIG_SYNTAX);
|
||||
|
||||
config_line[x]='\0';
|
||||
strcpy(aliases[num_aliases].name,config_line);
|
||||
|
||||
y=x+1;
|
||||
if(config_line[y]=='\0') server_error(errors,CONFIG_SYNTAX);
|
||||
|
||||
while(config_line[y] != '\0') y++;
|
||||
if(config_line[y-1]=='\n')
|
||||
config_line[--y]='\0';
|
||||
strcpy_nocrlf(aliases[num_aliases].real,&config_line[x+1]);
|
||||
|
||||
if((x==1) && (config_line[y-1] != '/')) {
|
||||
aliases[num_aliases].real[y-2]='/'; /* y-x-1 */
|
||||
aliases[num_aliases].real[y]='\0'; /* y-x-1 +1 */
|
||||
}
|
||||
translate_name(aliases[num_aliases].name);
|
||||
num_aliases++;
|
||||
}
|
||||
}
|
||||
|
125
http_dir.c
Normal file
125
http_dir.c
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* http_dir.c: Handles the on-the-fly html index generation
|
||||
*
|
||||
* Rob McCool
|
||||
* 3/23/93
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* httpd.h includes proper directory file */
|
||||
#include "httpd.h"
|
||||
|
||||
void output_directories(char **ar,int n,char *name,FILE *fd)
|
||||
{
|
||||
int x,lnum=0;
|
||||
|
||||
if(!strcmp(name,"/")) name[0]='\0';
|
||||
for(x=0;x<n;x++) {
|
||||
if(strcmp(ar[x],".")) {
|
||||
fprintf(fd,"<LI> <A NAME=%d HREF=\"",++lnum);
|
||||
if(strcmp(ar[x],".."))
|
||||
{
|
||||
if (name[strlen(name)-1] == '/')
|
||||
fprintf(fd,"%s%s\">%s</A>",name,ar[x],ar[x]);
|
||||
else
|
||||
fprintf(fd,"%s/%s\">%s</A>",name,ar[x],ar[x]);
|
||||
}
|
||||
else {
|
||||
char blorf[MAX_STRING_LEN];
|
||||
strcpy(blorf,name);
|
||||
if (name[strlen(name)-1] == '/')
|
||||
strcat(blorf,"..");
|
||||
else
|
||||
strcat(blorf,"/..");
|
||||
getparents(blorf);
|
||||
if(blorf[0]=='\0') {
|
||||
blorf[0]='/';
|
||||
blorf[1]='\0';
|
||||
}
|
||||
fprintf(fd,"%s\">%s</A>",blorf,"Parent Directory");
|
||||
}
|
||||
fputc(LINEFEED,fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int dsortf(char **s1,char **s2)
|
||||
{
|
||||
return(strcmp(*s1,*s2));
|
||||
}
|
||||
|
||||
|
||||
void index_directory(char *name, FILE *fd)
|
||||
{
|
||||
DIR *d;
|
||||
struct DIR_TYPE *dstruct;
|
||||
int num_ent=0,x;
|
||||
struct ent *head,*p,*q;
|
||||
char **ar;
|
||||
char unmunged_name[MAX_STRING_LEN];
|
||||
|
||||
strcpy(unmunged_name,name);
|
||||
unmunge_name(unmunged_name);
|
||||
|
||||
#ifdef GROK_GOPHER
|
||||
if(!strncmp(unmunged_name,GOPHER_LOCATION,GOPHER_LOC_LEN)) {
|
||||
gopher_index(name,fd);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!(d=opendir(name)))
|
||||
client_error(fd,BAD_FILE);
|
||||
|
||||
/* MUST RE-TRANSLATE DIRECTORY NAME SO USER DOES NOT SEE TRUE NAMES */
|
||||
strcpy(name,unmunged_name);
|
||||
|
||||
/* Spew HTML preamble */
|
||||
fprintf(fd,"<TITLE>Index of %s</TITLE>",name);
|
||||
fputc(LINEFEED,fd);
|
||||
fprintf(fd,"<H1>Index of %s</H1>",name);
|
||||
fputc(LINEFEED,fd);
|
||||
|
||||
fprintf(fd,"<UL>");
|
||||
fputc(LINEFEED,fd);
|
||||
|
||||
/*
|
||||
* Since we don't know how many dir. entries there are, put them into a
|
||||
* linked list and then arrayificate them so qsort can use them.
|
||||
*/
|
||||
head=(struct ent *)malloc(sizeof(struct ent));
|
||||
q=head;
|
||||
while(dstruct=readdir(d)) {
|
||||
p=(struct ent *)malloc(sizeof(struct ent));
|
||||
p->name=strdup(dstruct->d_name);
|
||||
p->next=NULL;
|
||||
q->next=p;
|
||||
q=p;
|
||||
num_ent++;
|
||||
}
|
||||
ar=(char **) malloc(num_ent*sizeof(char *));
|
||||
p=head;
|
||||
x=0;
|
||||
while(p=p->next)
|
||||
ar[x++]=p->name;
|
||||
|
||||
qsort((void *)ar,num_ent,sizeof(char *),
|
||||
#ifdef ULTRIX_BRAIN_DEATH
|
||||
(int (*))dsortf);
|
||||
#else
|
||||
(int (*)(const void *,const void *))dsortf);
|
||||
#endif
|
||||
|
||||
output_directories(ar,num_ent,name,fd);
|
||||
free(ar);
|
||||
p=head->next;
|
||||
q=head;
|
||||
while(p=p->next) {
|
||||
free(q->name);
|
||||
free(q);
|
||||
q=p;
|
||||
}
|
||||
closedir(d);
|
||||
fprintf(fd,"</UL>");
|
||||
}
|
341
http_gopher.c
Normal file
341
http_gopher.c
Normal file
@ -0,0 +1,341 @@
|
||||
/*
|
||||
* http_gopher.c: groks gopher directories and spits back HTML links
|
||||
*
|
||||
* This is big and ugly.
|
||||
*
|
||||
* Rob McCool (robm@ncsa.uiuc.edu)
|
||||
*/
|
||||
|
||||
#include "httpd.h"
|
||||
|
||||
|
||||
#ifdef GROK_GOPHER
|
||||
/* Great green globs of greasy grimy gopher guts... */
|
||||
|
||||
|
||||
typedef struct glist {
|
||||
char type;
|
||||
char name[MAX_STRING_LEN];
|
||||
int number;
|
||||
char host[MAX_STRING_LEN];
|
||||
int port;
|
||||
char path[MAX_STRING_LEN];
|
||||
struct glist *next;
|
||||
} gopher_ent;
|
||||
|
||||
/* Number is optional */
|
||||
#define PATH 1
|
||||
#define TYPE (1<<1)
|
||||
#define NAME (1<<2)
|
||||
#define PORT (1<<3)
|
||||
#define HOST (1<<4)
|
||||
#define ALL (PATH | TYPE | NAME | PORT | HOST)
|
||||
|
||||
#define NUM_IGNORE 9
|
||||
|
||||
static char *ignore_files[]=
|
||||
{
|
||||
".",
|
||||
"..",
|
||||
"bin",
|
||||
"core",
|
||||
"dev",
|
||||
"etc",
|
||||
"home",
|
||||
"usr",
|
||||
"lost+found"
|
||||
};
|
||||
|
||||
static FILE *fd;
|
||||
|
||||
int ignore(char *name) {
|
||||
int x;
|
||||
|
||||
for(x=0;x<NUM_IGNORE;x++)
|
||||
if(!strcmp(ignore_files[x],name))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void process_cap_file(char *name,gopher_ent **ary,int n) {
|
||||
char path[MAX_STRING_LEN];
|
||||
DIR *d;FILE *f;
|
||||
struct DIR_TYPE *cap;
|
||||
int l,i;
|
||||
|
||||
make_full_path(name,".cap",path);
|
||||
l=strlen(path);
|
||||
if(!(d=opendir(path)))
|
||||
return;
|
||||
|
||||
path[l++]='/';
|
||||
while(cap=readdir(d)) {
|
||||
strcpy(&path[l],cap->d_name);
|
||||
if(!is_directory(path)) {
|
||||
if(f=fopen(path,"r")) {
|
||||
while(!feof(f)) {
|
||||
char line[MAX_STRING_LEN];
|
||||
fgets(line,MAX_STRING_LEN,f);
|
||||
if(!strncmp(line,"Name=",5)) {
|
||||
for(i=0;i<n;i++) {
|
||||
if(!strcmp(ary[i]->path,cap->d_name)) {
|
||||
strcpy_nocrlf(ary[i]->name,&line[5]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!strncmp(line,"Numb=",5)) {
|
||||
for(i=0;i<n;i++) {
|
||||
if(!strcmp(ary[i]->path,cap->d_name)) {
|
||||
ary[i]->number=atoi(&line[5]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
int gsortf(gopher_ent **g1,gopher_ent **g2) {
|
||||
register int i1=(*g1)->number,i2=(*g2)->number;
|
||||
|
||||
if((i1 >= 0) && (i2 >= 0)) {
|
||||
if(i1 == i2) return 0;
|
||||
else if(i1 > i2) return 1;
|
||||
else return -1;
|
||||
}
|
||||
else if(i1 >= 0) return -1;
|
||||
else if(i2 >= 0) return 1;
|
||||
|
||||
else return(strcmp((*g1)->name,(*g2)->name));
|
||||
}
|
||||
|
||||
gopher_ent **sort_list(gopher_ent *head,char *name,int n,int cap_file) {
|
||||
gopher_ent **ret;
|
||||
gopher_ent *p=head->next;
|
||||
int i=0;
|
||||
|
||||
if(!(ret=(gopher_ent **)malloc(n*sizeof(gopher_ent *))))
|
||||
server_error(fd,MEMORY);
|
||||
|
||||
while(p) {
|
||||
ret[i++]=p;
|
||||
p=p->next;
|
||||
}
|
||||
if(cap_file) process_cap_file(name,ret,n);
|
||||
qsort((void *)ret,n,sizeof(gopher_ent *),
|
||||
#ifdef ULTRIX_BRAIN_DEATH
|
||||
(int (*))gsortf);
|
||||
#else
|
||||
(int (*)(const void *,const void *))gsortf);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
void spew_and_free_array(char *name,gopher_ent **ar,int n) {
|
||||
int lnum=0,i;
|
||||
|
||||
if(name[0]=='/' && !(name[1])) name[0]='\0';
|
||||
|
||||
|
||||
for(i=0;i<n;i++) {
|
||||
switch(ar[i]->type) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '9':
|
||||
case 'g':
|
||||
case 'I':
|
||||
case 'h':
|
||||
case 's':
|
||||
/* THIS NEEDS WORK... */
|
||||
if(!strncmp(ar[i]->path,"ftp:",4)) {
|
||||
int y=4;
|
||||
|
||||
fprintf(fd,"<LI> <A NAME=%d HREF=\"file://",lnum++);
|
||||
while(ar[i]->path[y]!='@' && ar[i]->path[y])
|
||||
fputc(ar[i]->path[y++],fd);
|
||||
if(ar[i]->path[y])
|
||||
while(ar[i]->path[++y])
|
||||
fputc(ar[i]->path[y],fd);
|
||||
else
|
||||
fputc('/',fd);
|
||||
fprintf(fd,"\">%s</A>%c",ar[i]->name,LINEFEED);
|
||||
break;
|
||||
}
|
||||
if(ar[i]->host[0]) {
|
||||
char *host;
|
||||
|
||||
host = full_hostname();
|
||||
/* problem: if we fall through here we may get // */
|
||||
if(strcmp(host,ar[i]->host)) {
|
||||
if(ar[i]->path[0]) {
|
||||
fprintf(fd,
|
||||
"<LI> <A NAME=%d HREF=\"gopher://%s:%d/%c%s\"",
|
||||
lnum++,ar[i]->host,ar[i]->port,ar[i]->path[0],
|
||||
ar[i]->path);
|
||||
}
|
||||
else {
|
||||
fprintf(fd,"<LI> <A NAME=%d HREF=\"gopher://%s:%d/\"",
|
||||
lnum++,ar[i]->host,ar[i]->port);
|
||||
}
|
||||
fprintf(fd,">%s</A>%c",ar[i]->name,LINEFEED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* check the path to see if it starts with 0/ or 1/ */
|
||||
if((ar[i]->path[0] == '0') && (ar[i]->path[1] == '/'))
|
||||
strsubfirst(2,ar[i]->path,"");
|
||||
else if((ar[i]->path[0] == '1') && (ar[i]->path[1] == '/'))
|
||||
strsubfirst(2,ar[i]->path,"");
|
||||
fprintf(fd,"<LI> <A NAME=%d HREF=\"%s/%s\">%s</A>%c",
|
||||
lnum++,name,ar[i]->path,ar[i]->name,LINEFEED);
|
||||
break;
|
||||
case '8':
|
||||
fprintf(fd,"<LI> <A NAME=%d HREF=\"telnet://%s:%d/\">%s</A>%c",
|
||||
lnum++,ar[i]->host,ar[i]->port,ar[i]->name,LINEFEED);
|
||||
break;
|
||||
case 'T':
|
||||
fprintf(fd,"<LI> <A NAME=%d HREF=\"tn3270://%s:%d/\">%s</A>%c",
|
||||
lnum++,ar[i]->host,ar[i]->port,ar[i]->name,LINEFEED);
|
||||
break;
|
||||
}
|
||||
free(ar[i]);
|
||||
}
|
||||
free(ar);
|
||||
}
|
||||
|
||||
gopher_ent *add_local_link(char *name, gopher_ent *head) {
|
||||
gopher_ent *p;
|
||||
|
||||
if(!(p=(gopher_ent *)malloc(sizeof(gopher_ent))))
|
||||
server_error(fd,MEMORY);
|
||||
p->type='0';
|
||||
p->number=-1;
|
||||
strcpy(p->path,name);
|
||||
strcpy(p->name,name);
|
||||
p->host[0]='\0';
|
||||
p->port=-1;
|
||||
p->next=head->next;
|
||||
head->next=p;
|
||||
}
|
||||
|
||||
int add_links_from_file(char *dir,char *name,gopher_ent *head) {
|
||||
char tstr[MAX_STRING_LEN];
|
||||
FILE *f;
|
||||
gopher_ent *p;
|
||||
int n=0;
|
||||
|
||||
make_full_path(dir,name,tstr);
|
||||
/* Gopher is stupid. */
|
||||
if(!(f=fopen(tstr,"r"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
while(!feof(f)) {
|
||||
int g;
|
||||
|
||||
if(!(p=(gopher_ent *)malloc(sizeof(gopher_ent))))
|
||||
server_error(fd,MEMORY);
|
||||
p->number=-1;
|
||||
g=0;
|
||||
while(g!=ALL) {
|
||||
if(feof(f)) {
|
||||
free(p);
|
||||
p=NULL;
|
||||
break;
|
||||
}
|
||||
fgets(tstr,MAX_STRING_LEN,f);
|
||||
if(!strncmp(tstr,"Name=",5)) {
|
||||
strcpy_nocrlf(p->name,&tstr[5]);
|
||||
g|=NAME;
|
||||
}
|
||||
else if(!strncmp(tstr,"Host=",5)) {
|
||||
strcpy_nocrlf(p->host,&tstr[5]);
|
||||
g|=HOST;
|
||||
}
|
||||
else if(!strncmp(tstr,"Path=",5)) {
|
||||
strcpy_nocrlf(p->path,&tstr[5]);
|
||||
g|=PATH;
|
||||
}
|
||||
else if(!strncmp(tstr,"Port=",5)) {
|
||||
if(tstr[5])
|
||||
sscanf(&tstr[5],"%d",&p->port);
|
||||
else p->port=-1;
|
||||
g|=PORT;
|
||||
}
|
||||
else if(!strncmp(tstr,"Type=",5)) {
|
||||
p->type=tstr[5];
|
||||
g|=TYPE;
|
||||
}
|
||||
else if(!strncmp(tstr,"Numb=",5)) {
|
||||
if(tstr[5])
|
||||
sscanf(&tstr[5],"%d",&p->number);
|
||||
}
|
||||
}
|
||||
if(p) {
|
||||
p->next=head->next;
|
||||
head->next=p;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return(n);
|
||||
}
|
||||
|
||||
void gopher_index(char *name, FILE *outfile) {
|
||||
DIR *d;
|
||||
struct DIR_TYPE *dstruct;
|
||||
gopher_ent head;
|
||||
gopher_ent **sorted;
|
||||
int num_entries;
|
||||
char unmunged_name[MAX_STRING_LEN];
|
||||
int cap_file=0;
|
||||
|
||||
fd=outfile;
|
||||
head.next=NULL;
|
||||
|
||||
strcpy(unmunged_name,name);
|
||||
unmunge_name(unmunged_name);
|
||||
|
||||
if(!(d=opendir(name)))
|
||||
client_error(fd,BAD_FILE);
|
||||
|
||||
while(dstruct=readdir(d)) {
|
||||
char *fn;
|
||||
|
||||
fn=dstruct->d_name;
|
||||
if(ignore(fn))
|
||||
continue;
|
||||
|
||||
if(fn[0]=='.') {
|
||||
if(!strcmp(fn,".cap"))
|
||||
cap_file=1;
|
||||
else
|
||||
num_entries+=add_links_from_file(name,fn,&head);
|
||||
}
|
||||
else {
|
||||
add_local_link(fn,&head);
|
||||
num_entries++;
|
||||
}
|
||||
}
|
||||
fprintf(fd,"<TITLE> Gopher Index of %s</TITLE>%c",unmunged_name,LINEFEED);
|
||||
fprintf(fd,"<H1>Gopher Index of %s</H1>%c",unmunged_name,LINEFEED);
|
||||
fprintf(fd,"<UL>%c",LINEFEED);
|
||||
|
||||
if(num_entries) {
|
||||
sorted=sort_list(&head,name,num_entries,cap_file);
|
||||
spew_and_free_array(unmunged_name,sorted,num_entries);
|
||||
}
|
||||
|
||||
fprintf(fd,"</UL>%c",LINEFEED);
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
#endif
|
303
http_request.c
Normal file
303
http_request.c
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
* http_request.c: functions to get and process requests
|
||||
*
|
||||
* Rob McCool 3/21/93
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "httpd.h"
|
||||
#ifdef LOGGING
|
||||
#include <arpa/inet.h> /* for inet_ntoa */
|
||||
#include <time.h> /* for ctime */
|
||||
#endif /* LOGGING */
|
||||
|
||||
#ifdef ANNOTATIONS
|
||||
#define COMMANDS 5
|
||||
#else
|
||||
#define COMMANDS 1
|
||||
#endif
|
||||
|
||||
static char *commands[]={
|
||||
"GET ",
|
||||
#ifdef ANNOTATIONS
|
||||
"ANN_GET ",
|
||||
"ANN_SET ",
|
||||
"ANN_CHANGE ",
|
||||
"ANN_DELETE ",
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef OLD
|
||||
void send_fd(FILE *f, FILE *fd)
|
||||
{
|
||||
int num_chars=0;
|
||||
|
||||
while(!feof(f)) {
|
||||
char c;
|
||||
|
||||
c=fgetc(f);
|
||||
|
||||
#ifdef TRUNCATE_LEN
|
||||
++num_chars;
|
||||
|
||||
if((c==LINEFEED) || (c==CRETURN) || (num_chars==TRUNCATE_LEN)) {
|
||||
fputc(LINEFEED,fd);
|
||||
|
||||
if(c==CRETURN && !feof(f)) {
|
||||
++num_chars;
|
||||
c=fgetc(f);
|
||||
if(c!=LINEFEED)
|
||||
fputc(c,fd);
|
||||
}
|
||||
else if(num_chars==TRUNCATE_LEN) {
|
||||
while((c!=LINEFEED) && !feof(f))
|
||||
c=fgetc(f);
|
||||
}
|
||||
num_chars=0;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
fputc(c,fd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void send_fd(FILE *f, FILE *fd)
|
||||
{
|
||||
int num_chars=0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
char c;
|
||||
|
||||
c = fgetc(f);
|
||||
if (feof(f))
|
||||
goto done;
|
||||
|
||||
#ifdef TRUNCATE_LEN
|
||||
++num_chars;
|
||||
|
||||
if((c==LINEFEED) || (c==CRETURN) || (num_chars==TRUNCATE_LEN)) {
|
||||
fputc(LINEFEED,fd);
|
||||
|
||||
if(c==CRETURN && !feof(f)) {
|
||||
++num_chars;
|
||||
c=fgetc(f);
|
||||
if(c!=LINEFEED)
|
||||
fputc(c,fd);
|
||||
}
|
||||
else if(num_chars==TRUNCATE_LEN) {
|
||||
while((c!=LINEFEED) && !feof(f))
|
||||
c=fgetc(f);
|
||||
}
|
||||
num_chars=0;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
fputc(c,fd);
|
||||
}
|
||||
|
||||
done:
|
||||
return;
|
||||
}
|
||||
|
||||
void send_file(char *file, FILE *fd) {
|
||||
FILE *f;
|
||||
|
||||
if(!(f=fopen(file,"r")))
|
||||
client_error(fd,BAD_FILE);
|
||||
|
||||
send_fd(f,fd);
|
||||
}
|
||||
|
||||
void send_error_file(char *file,FILE *fd) {
|
||||
FILE *f;
|
||||
|
||||
if(!(f=fopen(file,"r"))) {
|
||||
fprintf(fd,"<TITLE>Bad configuration</TITLE>%c",LINEFEED);
|
||||
fprintf(fd,"<H1>Bad configuration</H1>%c",LINEFEED);
|
||||
fprintf(fd,
|
||||
"%cThis server cannot find an error file to send you.<P>%c",
|
||||
LINEFEED,LINEFEED);
|
||||
exit(1);
|
||||
}
|
||||
send_fd(f,fd);
|
||||
}
|
||||
|
||||
|
||||
/* GET */
|
||||
void send_node(char *file,FILE *fd)
|
||||
{
|
||||
struct stat finfo;
|
||||
|
||||
if(stat(file,&finfo) == -1)
|
||||
client_error(fd,BAD_FILE);
|
||||
|
||||
if(S_ISDIR(finfo.st_mode)) {
|
||||
char name[MAX_STRING_LEN];
|
||||
|
||||
strcpy(name,file);
|
||||
/* Bugfix in case there's no trailing slash. */
|
||||
if (name[strlen(name)-1] != '/')
|
||||
strcat(name, "/");
|
||||
strcat(name,HTML_DIR_CONTENT);
|
||||
if(stat(name,&finfo) == -1)
|
||||
/* index file not found; get dir. list and send it back as HTML */
|
||||
index_directory(file,fd);
|
||||
else send_file(name,fd);
|
||||
}
|
||||
|
||||
else if(S_ISREG(finfo.st_mode))
|
||||
send_file(file,fd);
|
||||
|
||||
else client_error(fd,BAD_FILE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void process_request(FILE *in, FILE *out)
|
||||
{
|
||||
char cmd_line[MAX_STRING_LEN],*ret;
|
||||
register int x,y,z;
|
||||
#ifdef LOGGING
|
||||
char *who_called = NULL;
|
||||
struct sockaddr addr;
|
||||
int len, retval;
|
||||
FILE *fp;
|
||||
time_t time_val = time(NULL);
|
||||
char *time_string = ctime(&time_val);
|
||||
#endif /* LOGGING */
|
||||
|
||||
for (x=0; x<MAX_STRING_LEN; x++)
|
||||
{
|
||||
cmd_line[x] = (char)fgetc(in);
|
||||
if ((cmd_line[x] == ' ')||(cmd_line[x] == LINEFEED))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((x == MAX_STRING_LEN)||(cmd_line[x] == LINEFEED))
|
||||
{
|
||||
client_error(out,BAD_REQUEST);
|
||||
}
|
||||
cmd_line[x + 1] = '\0';
|
||||
|
||||
/* Currently, this does not take into account execution of different cmds */
|
||||
for(y=0;y<COMMANDS;y++) {
|
||||
if(!(strncmp(commands[y],cmd_line,strlen(commands[y])))) {
|
||||
int val;
|
||||
|
||||
val = fgetc(in);
|
||||
if (val != '/')
|
||||
{
|
||||
client_error(out,BAD_FILE); /* FIX */
|
||||
}
|
||||
|
||||
#ifdef ANNOTATIONS
|
||||
switch(y) {
|
||||
case 0: /* GET */
|
||||
break;
|
||||
case 1: /* ANN_GET */
|
||||
Get_request(in, out);
|
||||
return;
|
||||
break;
|
||||
case 2: /* ANN_SET */
|
||||
Set_request(in, out);
|
||||
return;
|
||||
break;
|
||||
case 3: /* ANN_CHANGE */
|
||||
Change_request(in, out);
|
||||
return;
|
||||
break;
|
||||
case 4: /* ANN_DELETE */
|
||||
Delete_request(in, out);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!(ret=(char *)malloc(MAX_STRING_LEN*sizeof(char))))
|
||||
server_error(out,MEMORY);
|
||||
|
||||
x++;
|
||||
cmd_line[x] = '/';
|
||||
z = x;
|
||||
x++;
|
||||
val = fgetc(in);
|
||||
while ((x < (MAX_STRING_LEN - 1))&&(val != EOF)&&
|
||||
(val != LINEFEED)&&(val != CRETURN)&&(!isspace(val)))
|
||||
{
|
||||
cmd_line[x] = (char)val;
|
||||
x++;
|
||||
val = fgetc(in);
|
||||
}
|
||||
cmd_line[x] = '\0';
|
||||
|
||||
#ifdef LOGGING
|
||||
/*
|
||||
* Get a sockaddr structure filled in for our peer.
|
||||
*/
|
||||
len = sizeof(struct sockaddr);
|
||||
retval = getpeername(fileno(stdin), &addr, &len);
|
||||
if (retval == 0)
|
||||
{
|
||||
struct in_addr *iaddr;
|
||||
struct hostent *hptr;
|
||||
|
||||
iaddr = &(((struct sockaddr_in *)&addr)->sin_addr);
|
||||
hptr = gethostbyaddr((char *)iaddr,
|
||||
sizeof(struct in_addr), AF_INET);
|
||||
if (hptr != NULL)
|
||||
{
|
||||
who_called = (char *)malloc(strlen(hptr->h_name) + 1);
|
||||
strcpy(who_called, hptr->h_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *iname;
|
||||
|
||||
iname = inet_ntoa(*iaddr);
|
||||
if (iname != NULL)
|
||||
{
|
||||
who_called = (char *)malloc(strlen(iname) + 1);
|
||||
strcpy(who_called, iname);
|
||||
}
|
||||
else
|
||||
{
|
||||
who_called = (char *)malloc(
|
||||
strlen("UNKNOWN_CALLER") + 1);
|
||||
strcpy(who_called, "UNKNOWN_CALLER");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
who_called = (char *)malloc(strlen("UNKNOWN_CALLER") + 1);
|
||||
strcpy(who_called, "UNKNOWN_CALLER");
|
||||
}
|
||||
|
||||
fp = fopen(LOGFILE, "a");
|
||||
if (fp)
|
||||
{
|
||||
time_string[strlen(time_string) - 1] = '\0';
|
||||
fprintf(fp, "%s\t[%s] %s\n", who_called, time_string, cmd_line);
|
||||
fclose(fp);
|
||||
}
|
||||
#endif /* LOGGING */
|
||||
|
||||
strcpy_nocrlf(ret, &cmd_line[z]);
|
||||
getparents(ret);
|
||||
translate_name(ret);
|
||||
switch(y) {
|
||||
case 0: /* GET */
|
||||
if(ret[0]!='/') client_error(out,BAD_FILE); /* FIX */
|
||||
send_node(ret,out);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
client_error(out,BAD_REQUEST);
|
||||
}
|
||||
|
160
httpd.c
Normal file
160
httpd.c
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* httpd.c: simple http daemon for answering WWW file requests
|
||||
*
|
||||
*
|
||||
* Rob McCool 3/21/93
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "httpd.h"
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <errno.h>
|
||||
|
||||
void usage(char *bin) {
|
||||
fprintf(stderr,"Usage: %s [-s] [-f filename] [-p portnum] [-u userid] [-g groupid]\n",bin);
|
||||
fprintf(stderr,"-f filename : specify an alternate config file\n");
|
||||
fprintf(stderr,"-s : Run as standalone server instead of from inetd\n");
|
||||
fprintf(stderr,"-p portnumber : alternate port number.\n");
|
||||
fprintf(stderr,"(server must initially run as root for ports under 1024.)\n");
|
||||
fprintf(stderr,"\nThe following two options require the daemon to be initially run as root:\n");
|
||||
fprintf(stderr,"-u userid : run as named user-id\n");
|
||||
fprintf(stderr,"-g groupid : run as named group-id\n");
|
||||
fprintf(stderr,"A user or group number may be specified with a # as the first character.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void detach() {
|
||||
int x;
|
||||
|
||||
if((x = fork()) > 0)
|
||||
exit(0);
|
||||
else if(x == -1)
|
||||
server_error(stdout,FORK);
|
||||
|
||||
#ifdef BSD
|
||||
setpgrp(0,getpid());
|
||||
if((x=open("/dev/tty",O_RDWR,0)) >= 0) {
|
||||
ioctl(x,TIOCNOTTY,0);
|
||||
close(x);
|
||||
setsid();
|
||||
}
|
||||
#else
|
||||
setpgrp();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BSD
|
||||
void ign() {
|
||||
int status;
|
||||
pid_t pid;
|
||||
|
||||
while( (pid = wait3(&status, WNOHANG, NULL)) > 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void standalone_main(int port, uid_t uid, gid_t gid) {
|
||||
int sd,csd, clen,pid;
|
||||
struct sockaddr_in sa_server,sa_client;
|
||||
|
||||
detach();
|
||||
|
||||
if ((sd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1)
|
||||
server_error(stdout,SOCKET);
|
||||
|
||||
bzero((char *) &sa_server, sizeof(sa_server));
|
||||
sa_server.sin_family=AF_INET;
|
||||
sa_server.sin_addr.s_addr=htonl(INADDR_ANY);
|
||||
sa_server.sin_port=htons(port);
|
||||
if(bind(sd,(struct sockaddr *) &sa_server,sizeof(sa_server)) == -1) {
|
||||
perror("bind");
|
||||
server_error(stdout,SOCKET_BIND);
|
||||
}
|
||||
listen(sd,5);
|
||||
|
||||
#ifdef BSD
|
||||
signal(SIGCHLD,(void (*)())ign);
|
||||
#else
|
||||
signal(SIGCHLD,SIG_IGN);
|
||||
#endif
|
||||
|
||||
/* we're finished getting the socket, switch to running userid */
|
||||
setuid(uid);
|
||||
setgid(gid);
|
||||
while(1) {
|
||||
retry:
|
||||
clen=sizeof(sa_client);
|
||||
if((csd=accept(sd,&sa_client,&clen)) == -1) {
|
||||
if(errno == EINTR) {
|
||||
#ifdef BSD
|
||||
ign();
|
||||
#endif
|
||||
goto retry;
|
||||
}
|
||||
perror("accept");
|
||||
server_error(stdout,SOCKET_ACCEPT);
|
||||
}
|
||||
if((pid = fork()) == -1)
|
||||
server_error(stdout,FORK);
|
||||
else if(!pid) {
|
||||
close(0);
|
||||
close(1);
|
||||
dup2(csd,0);
|
||||
dup2(csd,1);
|
||||
close(sd);
|
||||
process_request(stdin,stdout);
|
||||
fclose(stdin);
|
||||
fclose(stdout);
|
||||
exit(0);
|
||||
}
|
||||
close(csd);
|
||||
}
|
||||
}
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char config_file[MAX_STRING_LEN];
|
||||
int standalone=0,c,port = HTTPD_PORT;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
uid = uname2id(DEFAULT_USER);
|
||||
gid = gname2id(DEFAULT_GROUP);
|
||||
|
||||
strcpy(config_file,CONFIG_FILE);
|
||||
while((c = getopt(argc,argv,"sf:p:u:g:")) != -1) {
|
||||
switch(c) {
|
||||
case 's':
|
||||
standalone=1;
|
||||
break;
|
||||
case 'f':
|
||||
strcpy(config_file,optarg);
|
||||
break;
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
uid = uname2id(optarg);
|
||||
break;
|
||||
case 'g':
|
||||
gid = gname2id(optarg);
|
||||
break;
|
||||
case '?':
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
read_config(config_file,stdout);
|
||||
|
||||
if(standalone)
|
||||
standalone_main(port,uid,gid);
|
||||
else
|
||||
process_request(stdin,stdout);
|
||||
fclose(stdin);
|
||||
fclose(stdout);
|
||||
exit(0);
|
||||
}
|
31
httpd.conf
Normal file
31
httpd.conf
Normal file
@ -0,0 +1,31 @@
|
||||
# Sample httpd.conf file
|
||||
#
|
||||
# Only directories can be mapped.
|
||||
#
|
||||
# The format is:
|
||||
#
|
||||
# alias:real
|
||||
#
|
||||
# where alias is the name that appears to the user, and real is the
|
||||
# object's real location. Files are specified by an alias with no trailing
|
||||
# /, and directories need trailing slashes the real name.
|
||||
#
|
||||
# Aliases are self-stacking: If you first map something, then map
|
||||
# something else, the second will be interpreted with all of the previous
|
||||
# aliases expanded.
|
||||
#
|
||||
# To deny permission to a directory, map it to /deny or something
|
||||
# non-existent.
|
||||
#
|
||||
# For instance, to map the root directory to /u/web-docs, you would use the
|
||||
# line:
|
||||
# /:/u/web-docs/
|
||||
#
|
||||
#
|
||||
# After that line, all real directory names will be relative to the new root,
|
||||
# and all incoming requests will be relative to that new root.
|
||||
#
|
||||
# Then, if you want to deny permission to /u/web-docs/private, you would use:
|
||||
# /private:/deny
|
||||
|
||||
/:/tmp
|
256
httpd.h
Normal file
256
httpd.h
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* httpd.h: header for simple (ha! not anymore) http daemon
|
||||
*/
|
||||
|
||||
/* ----------------------------- config file ------------------------------ */
|
||||
|
||||
/* Define this to be the default configuration file */
|
||||
#define CONFIG_FILE "/usr/local/etc/httpd/httpd.conf"
|
||||
|
||||
/* -------------- Port number for server running standalone --------------- */
|
||||
|
||||
#define HTTPD_PORT 80
|
||||
|
||||
/* --------- Default user name and group name running standalone ---------- */
|
||||
/* --- These may be specified as numbers by placing a # before a number --- */
|
||||
|
||||
#define DEFAULT_USER "nobody"
|
||||
#define DEFAULT_GROUP "#-1"
|
||||
|
||||
/* Define if your system calls are BSD-style */
|
||||
#undef BSD
|
||||
|
||||
/* ---------------------------- access logging ---------------------------- */
|
||||
|
||||
/* If you want to have server access logging, define this. */
|
||||
#define LOGGING
|
||||
|
||||
#ifdef LOGGING
|
||||
|
||||
/* The name of the log file -- this must be writeable by the userid
|
||||
the server is run under (usually 'nobody'). */
|
||||
#define LOGFILE "/usr/local/etc/httpd/access_log"
|
||||
|
||||
#endif /* LOGGING */
|
||||
|
||||
/* ---------------------------- gopher support ---------------------------- */
|
||||
|
||||
/* Define this if you want to support . links and .cap gopherisms */
|
||||
/* This has other effects, see README.GOPHER for details. */
|
||||
#undef GROK_GOPHER
|
||||
|
||||
#ifdef GROK_GOPHER
|
||||
|
||||
/* Set this to the port # of your gopher server */
|
||||
#define GOPHERPORT 70
|
||||
|
||||
/* This is the virtual location of the Gopher data directory. You should
|
||||
establish a mapping from this to your real Gopher data directory in
|
||||
httpd.conf, as explained in README.GOPHER. */
|
||||
#define GOPHER_LOCATION "/gopher-data"
|
||||
#define GOPHER_LOC_LEN 12
|
||||
|
||||
#endif /* GROK_GOPHER */
|
||||
|
||||
/* ----------------------- group annotation support ----------------------- */
|
||||
|
||||
/* Define this if you want this server to serve as a group annotation
|
||||
server. See README for details. */
|
||||
#undef ANNOTATIONS
|
||||
/* #define ANNOTATIONS */
|
||||
|
||||
#ifdef ANNOTATIONS
|
||||
|
||||
/* The physical directory where all group annotations will be stored.
|
||||
THIS MUST BE WRITEABLE by the user ID under which you run httpd
|
||||
(usually 'nobody'). */
|
||||
#define ANN_DIR "/usr/local/etc/httpd/annotations"
|
||||
|
||||
/* The virtual directory (i.e., from the outside coming into the server)
|
||||
where the group annotations will be located.
|
||||
|
||||
E.g., if you normally map / to /usr/local/etc/httpd in httpd.conf,
|
||||
and you have ANN_DIR set to /usr/local/etc/httpd/annotations, then
|
||||
this should be "/annotations" (note the slash).
|
||||
|
||||
This means that the annotation directory must be browsable
|
||||
according to the standard rules in httpd.conf. */
|
||||
#define ANN_VIRTUAL_DIR "/annotations"
|
||||
|
||||
/* This is your port number, same number used in /etc/services. */
|
||||
#define ANN_PORT 80
|
||||
|
||||
#endif
|
||||
|
||||
/* ------------------------- other customizations ------------------------- */
|
||||
|
||||
/* Define this if you want errors logged to syslog and your system has the
|
||||
syslog() system call and syslog.h */
|
||||
#undef SYSLOG
|
||||
|
||||
/* The default string lengths */
|
||||
#define MAX_STRING_LEN 256
|
||||
|
||||
/* The maximum number of aliases in the config file */
|
||||
#define MAX_ALIASES 20
|
||||
|
||||
/* Define this to be what your HTML directory content files are called */
|
||||
#define HTML_DIR_CONTENT "index.html"
|
||||
|
||||
/* Set this to the hardcoded fully qualified machine name.
|
||||
|
||||
You may #undef this if you choose, and httpd will then use the
|
||||
gethostname() and gethostbyname() calls to figure out the fully
|
||||
qualified machine name on the fly; HOWEVER, gethostbyname() called
|
||||
on the name of the local host coredumps on some systems. */
|
||||
/* #define HARDCODED_HOSTNAME "machine.foo.edu" */
|
||||
#undef HARDCODED_HOSTNAME
|
||||
|
||||
/*
|
||||
* The number of characters to truncate the outgoing file's lines to
|
||||
* "Well behaved" servers should truncate this to 80. Some link pathnames
|
||||
* tend to go ballistic, so I would set this higher.
|
||||
*
|
||||
* If you want to serve JPEG's and other such binary files, #undef this. */
|
||||
#undef TRUNCATE_LEN
|
||||
|
||||
/*
|
||||
* The particular directory style your system supports. If you have dirent.h
|
||||
* in /usr/include (POSIX) or /usr/include/sys (SYSV), #include
|
||||
* that file and define DIR_TYPE to be dirent. Otherwise, if you have
|
||||
* /usr/include/sys/dir.h, define DIR_TYPE to be direct and include that
|
||||
* file. If you have neither, I'm confused.
|
||||
*/
|
||||
#include <dirent.h>
|
||||
#define DIR_TYPE dirent
|
||||
|
||||
/* ------------------------------ error urls ------------------------------ */
|
||||
|
||||
/* Error files. BE SURE THESE WORK! NOTE THAT ALIASES ARE NOT TRANSLATED! */
|
||||
#define SERVER_ERROR "/usr/local/etc/httpd/errors/bad_server.html"
|
||||
#define ANN_SERVER_ERROR "/usr/local/etc/httpd/errors/bad_ann_server.html"
|
||||
#define BAD_REQUEST "/usr/local/etc/httpd/errors/bad_request.html"
|
||||
#define BAD_ANN_REQUEST "/usr/local/etc/httpd/errors/bad_ann_request.html"
|
||||
#define BAD_FILE "/usr/local/etc/httpd/errors/bad_file.html"
|
||||
#define UNIMPLEMENTED "/usr/local/etc/httpd/errors/unimplemented.html"
|
||||
#define GOPHER_ERROR "/usr/local/etc/httpd/errors/gopher.html"
|
||||
|
||||
/* ----------------------------- other stuff ------------------------------ */
|
||||
|
||||
/* You shouldn't have to edit anything below this line. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#ifdef NEXT_BRAIN_DEATH
|
||||
#define S_ISDIR(m) (((m)&(S_IFMT)) == (S_IFDIR))
|
||||
#define S_ISREG(m) (((m)&(S_IFMT)) == (S_IFREG))
|
||||
#endif
|
||||
|
||||
#ifdef ultrix
|
||||
#define ULTRIX_BRAIN_DEATH
|
||||
#define NEED_STRDUP
|
||||
#endif
|
||||
|
||||
#ifdef SYSLOG
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
/* Just in case your linefeed isn't the one the other end is expecting. */
|
||||
#define LINEFEED 10
|
||||
#define CRETURN 13
|
||||
|
||||
/* Messages which are logged upon system errors. */
|
||||
#define CONFIG_FILENAME \
|
||||
"httpd: configuration file not found or permission denied"
|
||||
#define CONFIG_SYNTAX "httpd: syntax error in configuration file"
|
||||
#define MEMORY "httpd: ran out of memory"
|
||||
#define FORK "httpd: could not fork"
|
||||
#define SOCKET "httpd: socket problem"
|
||||
#define SOCKET_BIND "httpd: socket bind problem"
|
||||
#define SOCKET_ACCEPT "httpd: connection accept problem"
|
||||
#define BAD_USERNAME "httpd: bad username"
|
||||
#define BAD_GROUPNAME "httpd: bad groupname"
|
||||
|
||||
|
||||
#ifdef SYSLOG
|
||||
#define msg_out(fd,msg) (syslog(LOG_ERR,(msg)))
|
||||
#else
|
||||
#if 1
|
||||
#define msg_out(fd,msg) fprintf((fd),(msg))
|
||||
#else
|
||||
#define msg_out(fd,msg) (fprintf((fd),(msg)),send_error_file((msg),(fd)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* log_error prints a server error to the appropriate places and exits */
|
||||
#define server_error(fd,msg) (msg_out(fd,msg),send_error_file(msg,fd),\
|
||||
exit(0))
|
||||
#define client_error(fd,msg) (send_error_file(msg,fd),exit(0))
|
||||
|
||||
|
||||
struct ent {
|
||||
char *name;
|
||||
struct ent *next;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
char *url;
|
||||
int acnt;
|
||||
int *ann_array;
|
||||
} LogRec;
|
||||
|
||||
typedef struct {
|
||||
int hash;
|
||||
int cnt;
|
||||
LogRec *logs;
|
||||
} LogData;
|
||||
|
||||
|
||||
/* Function prototypes. */
|
||||
extern int read_config(char *name, FILE *errors);
|
||||
extern void send_node(char *name, FILE *fd);
|
||||
extern void translate_name(char *name);
|
||||
extern void process_request(FILE *in, FILE *out);
|
||||
extern void Get_request(FILE *in, FILE *out);
|
||||
extern void Set_request(FILE *in, FILE *out);
|
||||
extern void Change_request(FILE *in, FILE *out);
|
||||
extern void Delete_request(FILE *in, FILE *out);
|
||||
extern void strsubfirst(int start,char *dest, char *src);
|
||||
extern void strcpy_nocrlf(char *dest, char *src);
|
||||
extern char *strsub(char *src, char *oldstr, char *newstr);
|
||||
extern void make_full_path(char *src1,char *src2,char *dst);
|
||||
extern int is_directory(char *name);
|
||||
extern char *full_hostname(void);
|
||||
extern void index_directory(char *name, FILE *fd);
|
||||
extern void getparents(char *name);
|
||||
extern void gopher_index(char *name,FILE *fd);
|
||||
extern void send_error_file(char *file,FILE *fd);
|
||||
extern void send_file(char *file,FILE *fd);
|
||||
extern void send_error_file(char *file,FILE *fd);
|
||||
extern LogData *GetLogData(int hash);
|
||||
extern int FindLogData(LogData *lptr, char *url, int **alist, int *acnt);
|
||||
extern int AddLogData(LogData *lptr, int indx, char *url);
|
||||
extern char *HashtoUrl(LogData *lptr, int indx);
|
||||
extern int WriteLogData(LogData *lptr);
|
||||
extern void FreeLogData(LogData *lptr);
|
||||
extern char *WriteAudioData(int hash, int indx, char *data, int len, char *type, int no_change);
|
||||
extern int WriteAnnData(char *url, int hash, int indx, char *title, char *user, char *date, char *data, int len, int no_change);
|
||||
extern int ReadAnnData(int hash, int indx, char **title, char **user, char **date);
|
||||
extern int DeleteLogData(LogData *lptr, int indx);
|
||||
extern void UnlockIt(void);
|
||||
extern uid_t uname2id(char *name);
|
||||
extern gid_t gname2id(char *name);
|
||||
|
||||
#ifdef NEED_STRDUP
|
||||
extern char *strdup (char *str);
|
||||
#endif
|
167
util.c
Normal file
167
util.c
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* str.c: string utility things
|
||||
*
|
||||
* 3/21/93 Rob McCool
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include "httpd.h"
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
void strcpy_nocrlf(char *dest, char *src)
|
||||
{
|
||||
register char c = *src;
|
||||
|
||||
while((c!='\0') && (c!=LINEFEED) && (c!=CRETURN))
|
||||
c=(*dest++ = *src++);
|
||||
|
||||
if(c!='\0') *(dest-1)='\0';
|
||||
}
|
||||
|
||||
void strsubfirst(int start,char *dest, char *src)
|
||||
{
|
||||
char tmp[MAX_STRING_LEN];
|
||||
|
||||
strcpy(tmp,&dest[start]);
|
||||
strcpy(dest,src);
|
||||
strcpy(&dest[strlen(src)],tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse .. so we don't compromise security
|
||||
*/
|
||||
void getparents(char *name)
|
||||
{
|
||||
int l=0,w=0;
|
||||
const char *lookfor="..";
|
||||
|
||||
while(name[l]!='\0') {
|
||||
if(name[l]!=lookfor[w]) (w>0 ? (l-=(w-1),w=0) : l++);
|
||||
else {
|
||||
if(lookfor[++w]=='\0') {
|
||||
if((name[l+1]=='\0') || (name[l+1]=='/')) {
|
||||
register int m=l+1,n;
|
||||
|
||||
l=l-3;
|
||||
if(l>=0) {
|
||||
while((l!=0) && (name[l]!='/')) --l;
|
||||
}
|
||||
else l=0;
|
||||
n=l;
|
||||
while(name[n]=name[m]) (++n,++m);
|
||||
w=0;
|
||||
}
|
||||
else w=0;
|
||||
}
|
||||
else ++l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *strsub(char *src, char *oldstr, char *newstr) {
|
||||
register char *t=src,*o,*nsrc=src;
|
||||
int oldlen,newlen;
|
||||
char *tosender;
|
||||
|
||||
oldlen=strlen(oldstr);
|
||||
newlen=strlen(newstr);
|
||||
|
||||
/* If the new string is longer, assume the worst: our new string has to
|
||||
* replace every letter in the source with the new. If not, don't thrash
|
||||
* malloc */
|
||||
if(oldlen<newlen)
|
||||
nsrc=(char *)malloc(strlen(src)+(newlen-oldlen)*(strlen(src)/oldlen));
|
||||
|
||||
tosender=nsrc;
|
||||
o=oldstr;
|
||||
while(*t) {
|
||||
if(*t != *o) *nsrc++ = *t++;
|
||||
else {
|
||||
register char *tmp=++t;
|
||||
register int n=oldlen-1;
|
||||
|
||||
++o; /* advance o (we advanced t above) */
|
||||
while((*tmp++ == *o++) && n) {
|
||||
--n;
|
||||
}
|
||||
tmp=newstr;
|
||||
if(!n) { /* we matched */
|
||||
while(*nsrc++ = *tmp++);
|
||||
t+=oldlen-1;nsrc--;
|
||||
}
|
||||
else *nsrc++ = *(t-1); /* we missed, so put the char into new str */
|
||||
o=oldstr; /* no matter what, we damaged o */
|
||||
}
|
||||
}
|
||||
*nsrc='\0';
|
||||
/* not calling free is very anti-social */
|
||||
if(oldlen<newlen) free(src);
|
||||
return tosender;
|
||||
}
|
||||
|
||||
void make_full_path(char *src1,char *src2,char *dst) {
|
||||
while(*dst++ = *src1++);
|
||||
*(dst-1)='/';
|
||||
while(*dst++ = *src2++);
|
||||
}
|
||||
|
||||
int is_directory(char *path) {
|
||||
struct stat finfo;
|
||||
|
||||
if(stat(path,&finfo) == -1)
|
||||
return 0; /* in error condition, just return no */
|
||||
|
||||
return(S_ISDIR(finfo.st_mode));
|
||||
}
|
||||
|
||||
char *full_hostname (void)
|
||||
{
|
||||
#ifdef HARDCODED_HOSTNAME
|
||||
return HARDCODED_HOSTNAME;
|
||||
#else
|
||||
char str[128];
|
||||
int len = 128;
|
||||
|
||||
gethostname (str, len);
|
||||
return gethostbyname(str)->h_name;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NEED_STRDUP
|
||||
char *strdup (char *str)
|
||||
{
|
||||
char *dup;
|
||||
|
||||
dup = (char *)malloc (strlen (str) + 1);
|
||||
dup = strcpy (dup, str);
|
||||
|
||||
return dup;
|
||||
}
|
||||
#endif
|
||||
|
||||
uid_t uname2id(char *name) {
|
||||
struct passwd *ent;
|
||||
|
||||
if(name[0] == '#')
|
||||
return(atoi(&name[1]));
|
||||
|
||||
if(!(ent = getpwnam(name)))
|
||||
server_error(stdout,BAD_USERNAME);
|
||||
|
||||
else return(ent->pw_uid);
|
||||
}
|
||||
|
||||
gid_t gname2id(char *name) {
|
||||
struct group *ent;
|
||||
|
||||
if(name[0] == '#')
|
||||
return(atoi(&name[1]));
|
||||
|
||||
if(!(ent = getgrnam(name)))
|
||||
server_error(stdout,BAD_GROUPNAME);
|
||||
|
||||
else return(ent->gr_gid);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user