diff --git a/README b/README index 581e7fb..04ee360 100644 --- a/README +++ b/README @@ -1,15 +1,37 @@ +NCSA httpd 1.2 is now available via anonymous FTP from +ftp://ftp.ncsa.uiuc.edu/Web/ncsa_httpd/current. Note that there is no +longer a binary distributed for the DECstation MIPS machines running +Ultrix as our DECstation 5000 died and it hasn't been fixed as of yet. -Documentation for httpd has been moved online. See the URL -here for online hypertext -documentation including installation instructions, feature list, -demonstration, etc. +NCSA httpd is an HTTP/1.0 compatible server for hypermedia documents. +It runs on a plethora of UNIX systems. NCSA httpd is also compatible +with the Common Gateway Interface which allows interactive document +generation. -Documentation can also be found where you found this .tar file, in a file -called httpd_docs.tar.Z. +All of the documentation for NCSA httpd is online hypertext. See +http://hoohoo.ncsa.uiuc.edu/. If you cannot use a networked WWW browser to +read it, retrieve the file /Web/ncsa_httpd/current/httpd_docs.tar.Z from +ftp.ncsa.uiuc.edu and use your WWW browser in local only mode to get things +started. ---Rob, Marc, and Eric httpd@ncsa.uiuc.edu +Note that the new features introduced with this release required some +internal restructuring of the code. This may cause some bugs. If you +are pleased with your current setup and do not need the new features +you may consider waiting before upgrading in order to make sure that +any problems (we expect none, but who ever does?) are worked out prior +to your upgrade. +By popular demand I have written a number of tutorials about various +aspects of NCSA httpd configuration. See +http://hoohoo.ncsa.uiuc.edu/docs/tutorials/ for an index. + +Thanks all +--Rob and Eric + +httpd@ncsa.uiuc.edu + +---- 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 @@ -26,3 +48,76 @@ FOR THE SOFTWARE AND/OR DOCUMENTATION PROVIDED, INCLUDING, WITHOUT LIMITATION, WARRANTY OF MERCHANTABILITY AND WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. +---- + +All of the following information is available at +http://hoohoo.ncsa.uiuc.edu/docs/Upgrade.html in hypertext form. You +should probably read that instead of this primitive plaintext. + +New features + + * The CGI interface is now revised to version 1.1. + + * A new logfile format was agreed upon which should simplify the + lives of the authors of logfile analyzers if commonly adopted. + + * CGI scripts are now allowed anywhere. + + * The server side includes interface has been completely + rewritten. It is NOT compatible with your old INC SRV documents + but a filter is provided to transform your old documents into + the new format. + + * There is a new access control option to disable symbolic links + only if the owner of the pointer is not the same as the + owner of that which is pointed to. This means your users can + have symbolic links to things they own, but not to dangerous + things like /etc. + + * A new access control method called mutual-failure has been + added. This method is a bit unorthodox, but allows + you to allow hosts from one domain while excluding certain hosts + (such as public access machines) from that domain. + + * Wildcard expressions are now allowed in various areas of server + configuration, to allow patterns to be specified. This is most + useful in the Directory directive. + + * Directory indexing has been revamped. It looks much different, + and I've written a short tutorial on how to set it up. + + * Access Control Files now allow the indexing directives as well + as the DefaultType directive. + + * require user now allows quotes for PGP usernames with spaces. + + * Server now explicitly kills CGI scripts when the client aborts + or upon timeout + + * Server now verifies the DNS hostname it gets from the IP number + to prevent PTR spoofs. + + * Support for 304 and If-modified-since. + + * Support for CGI PUT and DELETE scripts. This will become + important later for group annotation features. + +Bug fixes + +All of the known bugs in 1.1 have now been fixed. Now it's time to +find the ones I introduce with 1.2. + + * Fixed problem running scripts in ServerMode inetd under IRIX. + + * Fixed bad port problem under OSF/1. + + * Inserted missing return statement for the IdentityCheck directive. + + * Fixed problem whereby errors would stop being logged after a restart. + + * Fixed 256 character limitation on CGI URLs. + + * Access control fix from 1.1 integrated but moved in order to + allow certain CGI scripts to continue functioning. + + diff --git a/cgi-bin/date b/cgi-bin/date index 0347210..fcdce04 100755 --- a/cgi-bin/date +++ b/cgi-bin/date @@ -1,6 +1,6 @@ #!/bin/sh -DATE=`which date` +DATE=/bin/date echo Content-type: text/plain echo diff --git a/cgi-bin/finger b/cgi-bin/finger index 0f36457..3b8d624 100755 --- a/cgi-bin/finger +++ b/cgi-bin/finger @@ -1,6 +1,6 @@ #!/bin/sh -FINGER=`which finger` +FINGER=/usr/ucb/finger echo Content-type: text/html echo diff --git a/cgi-bin/fortune b/cgi-bin/fortune index 8f72a51..3166e97 100755 --- a/cgi-bin/fortune +++ b/cgi-bin/fortune @@ -1,6 +1,6 @@ #!/bin/sh -FORTUNE=`which fortune` +FORTUNE=/usr/games/fortune echo Content-type: text/plain echo diff --git a/cgi-bin/imagemap b/cgi-bin/imagemap deleted file mode 100755 index 63d4401..0000000 Binary files a/cgi-bin/imagemap and /dev/null differ diff --git a/cgi-bin/test-cgi b/cgi-bin/test-cgi index d1a58eb..9748971 100755 --- a/cgi-bin/test-cgi +++ b/cgi-bin/test-cgi @@ -9,7 +9,6 @@ echo echo argc is $#. argv is "$*". echo -echo PATH = $PATH echo SERVER_SOFTWARE = $SERVER_SOFTWARE echo SERVER_NAME = $SERVER_NAME echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE @@ -17,10 +16,10 @@ echo SERVER_PROTOCOL = $SERVER_PROTOCOL echo SERVER_PORT = $SERVER_PORT echo REQUEST_METHOD = $REQUEST_METHOD echo HTTP_ACCEPT = "$HTTP_ACCEPT" -echo PATH_INFO = $PATH_INFO -echo PATH_TRANSLATED = $PATH_TRANSLATED -echo SCRIPT_NAME = $SCRIPT_NAME -echo QUERY_STRING = $QUERY_STRING +echo PATH_INFO = "$PATH_INFO" +echo PATH_TRANSLATED = "$PATH_TRANSLATED" +echo SCRIPT_NAME = "$SCRIPT_NAME" +echo QUERY_STRING = "$QUERY_STRING" echo REMOTE_HOST = $REMOTE_HOST echo REMOTE_ADDR = $REMOTE_ADDR echo REMOTE_USER = $REMOTE_USER diff --git a/cgi-bin/uptime b/cgi-bin/uptime index 20209d8..c05baea 100755 --- a/cgi-bin/uptime +++ b/cgi-bin/uptime @@ -1,6 +1,6 @@ #!/bin/sh -UPTIME=`which uptime` +UPTIME=/usr/ucb/uptime echo Content-type: text/plain echo diff --git a/cgi-bin/wais.pl b/cgi-bin/wais.pl index 1df081d..f3a967f 100755 --- a/cgi-bin/wais.pl +++ b/cgi-bin/wais.pl @@ -2,7 +2,7 @@ # # wais.pl -- WAIS search interface # -# wais.pl,v 1.1 1993/12/31 09:30:56 robm Exp +# wais.pl,v 1.2 1994/04/10 05:33:29 robm Exp # # Tony Sanders , Nov 1993 # @@ -84,4 +84,5 @@ sub docdone { $score = $headline = $lines = $bytes = $type = $date = ''; } +open (STDERR,"> /dev/null"); eval '&do_wais'; diff --git a/cgi-src/Makefile b/cgi-src/Makefile index 90a1f94..05dcbdf 100755 --- a/cgi-src/Makefile +++ b/cgi-src/Makefile @@ -57,5 +57,5 @@ change-passwd: change-passwd.o util.o clean: - rm -f *.o ../cgi-bin/post-query ../cgi-bin/query ../cgi-bin/change-passwd ../cgi-bin/phf ../cgi-bin/jj + rm -f *.o ../cgi-bin/post-query ../cgi-bin/query ../cgi-bin/change-passwd ../cgi-bin/phf ../cgi-bin/jj ../cgi-bin/imagemap diff --git a/cgi-src/imagemap.c b/cgi-src/imagemap.c index b9dbd20..b8e12d6 100644 --- a/cgi-src/imagemap.c +++ b/cgi-src/imagemap.c @@ -31,7 +31,7 @@ int isname(char); int main(int argc, char **argv) { - char input[MAXLINE], *mapname, def[MAXLINE], conf[80]; + char input[MAXLINE], *mapname, def[MAXLINE], conf[MAXLINE]; double testpoint[2], pointarray[MAXVERTS][2]; int i, j, k; FILE *fp; @@ -63,11 +63,11 @@ int main(int argc, char **argv) confname[i] = input[i]; confname[i] = '\0'; if(!strcmp(confname,mapname)) - break; + goto found; } if(feof(fp)) servererr("Map not found in configuration file."); - + found: fclose(fp); while(isspace(input[i]) || input[i] == ':') ++i; diff --git a/cgi-src/phf.c b/cgi-src/phf.c index 9ca34f5..fbb1f42 100755 --- a/cgi-src/phf.c +++ b/cgi-src/phf.c @@ -178,11 +178,17 @@ main(int argc, char *argv[]) { strcpy(commandstr, "/usr/local/bin/ph -m "); if (strlen(serverstr)) { strcat(commandstr, " -s "); + /* RM 2/22/94 oops */ + escape_shell_cmd(serverstr); strcat(commandstr, serverstr); strcat(commandstr, " "); } + escape_shell_cmd(typestr); strcat(commandstr, typestr); - if (atleastonereturn) strcat(commandstr, returnstr); + if (atleastonereturn) { + escape_shell_cmd(returnstr); + strcat(commandstr, returnstr); + } printf("%s%c", commandstr, LF); printf("
%c", LF);
diff --git a/cgi-src/util.c b/cgi-src/util.c
index 3f37f39..0901a66 100644
--- a/cgi-src/util.c
+++ b/cgi-src/util.c
@@ -120,3 +120,28 @@ void send_fd(FILE *f, FILE *fd)
         fputc(c,fd);
     }
 }
+
+int ind(char *s, char c) {
+    register int x;
+
+    for(x=0;s[x];x++)
+        if(s[x] == c) return x;
+
+    return -1;
+}
+
+void escape_shell_cmd(char *cmd) {
+    register int x,y,l;
+
+    l=strlen(cmd);
+    for(x=0;cmd[x];x++) {
+        if(ind("&;`'\"|*?~<>^()[]{}$\\",cmd[x]) != -1){
+            for(y=l+1;y>x;y--)
+                cmd[y] = cmd[y-1];
+            l++; /* length has been increased */
+            cmd[x] = '\\';
+            x++; /* skip the character */
+        }
+    }
+}
+
diff --git a/conf/httpd.conf-dist b/conf/httpd.conf-dist
index e25f76a..0ad9036 100644
--- a/conf/httpd.conf-dist
+++ b/conf/httpd.conf-dist
@@ -52,8 +52,12 @@ TransferLog logs/access_log
 PidFile logs/httpd.pid
 
 # ServerName allows you to set a host name which is sent back to clients for
-#your server if it's different than the one the program would get (i.e. use
-#"www" instead of the host's real name).
+# your server if it's different than the one the program would get (i.e. use
+# "www" instead of the host's real name).
+#
+# Note: You cannot just invent host names and hope they work. The name you 
+# define here must be a valid DNS name for your host. If you don't understand
+# this, ask your network administrator.
 
 #ServerName new.host.name
 
diff --git a/conf/srm.conf-dist b/conf/srm.conf-dist
index 6940570..779b578 100644
--- a/conf/srm.conf-dist
+++ b/conf/srm.conf-dist
@@ -1,6 +1,9 @@
 # With this document, you define the name space that users see of your http
 # server.
 
+# See the tutorials at http://hoohoo.ncsa.uiuc.edu/docs/tutorials/ for
+# more information.
+
 # Rob (robm@ncsa.uiuc.edu)
 
 
@@ -27,13 +30,15 @@ FancyIndexing on
 # AddIcon tells the server which icon to show for different files or filename
 # extensions
 
-AddIcon /icons/text.xbm .html .txt
-AddIcon /icons/image.xbm .gif .jpg .xbm .tiff
-AddIcon /icons/sound.xbm .au
-AddIcon /icons/movie.xbm .mpg
+AddIconByType (TXT,/icons/text.xbm) text/*
+AddIconByType (IMG,/icons/image.xbm) image/*
+AddIconByType (SND,/icons/sound.xbm) audio/*
+AddIcon /icons/movie.xbm .mpg .qt
 AddIcon /icons/binary.xbm .bin
+
 AddIcon /icons/back.xbm ..
-AddIcon /icons/menu.xbm **DIRECTORY**
+AddIcon /icons/menu.xbm ^^DIRECTORY^^
+AddIcon /icons/blank.xbm ^^BLANKICON^^
 
 # DefaultIcon is which icon to show for files which do not have an icon
 # explicitly set.
@@ -49,13 +54,17 @@ DefaultIcon /icons/unknown.xbm
 #
 # The server will first look for name.html, include it if found, and it will
 # then look for name and include it as plaintext if found.
+#
+# HeaderName is the name of a file which should be prepended to
+# directory indexes. 
 
 ReadmeName README
+HeaderName HEADER
 
 # IndexIgnore is a set of filenames which directory indexing should ignore
 # Format: IndexIgnore name1 name2...
 
-IndexIgnore /.htaccess ~ #
+IndexIgnore */.??* *~ *# */HEADER* */README*
 
 # AccessFileName: The name of the file to look for in each directory
 # for access control information.
@@ -71,6 +80,12 @@ DefaultType text/plain
 # make certain files to be certain types.
 # Format: AddType type/subtype ext1
 
+# AddEncoding allows you to have certain browsers (Mosaic/X 2.1+) uncompress
+# information on the fly. Note: Not all browsers support this.
+
+#AddEncoding x-compress Z
+#AddEncoding x-gzip gz
+
 # Redirect allows you to tell clients about documents which used to exist in
 # your server's namespace, but do not anymore. This allows you to tell the
 # clients where to look for the relocated document.
@@ -86,3 +101,9 @@ Alias /icons/ /usr/local/etc/httpd/icons/
 # Format: ScriptAlias fakename realname
 
 ScriptAlias /cgi-bin/ /usr/local/etc/httpd/cgi-bin/
+
+# If you want to use server side includes, or CGI outside
+# ScriptAliased directories, uncomment the following lines.
+
+#AddType text/x-server-parsed-html .shtml
+#AddType application/x-httpd-cgi .cgi
diff --git a/icons/blank.xbm b/icons/blank.xbm
new file mode 100644
index 0000000..eb10641
--- /dev/null
+++ b/icons/blank.xbm
@@ -0,0 +1,9 @@
+#define blank_width 20
+#define blank_height 23
+static char blank_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
diff --git a/src/Makefile b/src/Makefile
index 50de6a6..2f0b5bd 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -4,13 +4,13 @@
 #CC= cc
 # For Suns or other non-ANSI platforms. Please make sure your gcc is
 # 2.0 or later, as 1.40 seems to create bad code for the Sun 4.
-CC= gcc
+CC= gcc 
 
 # For optimization
-#CFLAGS= -O2
+CFLAGS= -O2
 # For debugging information
-#CFLAGS= -g -DPEM_AUTH
-CFLAGS= -g
+#CFLAGS= -g -DPEM_AUTH 
+#CFLAGS= -g
 
 # Place here any extra libraries you may need to link to. You
 # shouldn't have to.
@@ -34,7 +34,7 @@ AUX_CFLAGS= -DSUNOS4
 # For HP-UX
 #AUX_CFLAGS= -DHPUX
 # For AIX
-#AUX_CFLAGS= -DAIX
+#AUX_CFLAGS= -DAIX -U__STR__
 # For Ultrix
 #AUX_CFLAGS= -DULTRIX
 # For DEC OSF/1
@@ -47,6 +47,14 @@ AUX_CFLAGS= -DSUNOS4
 #AUX_CFLAGS= -DLINUX
 # For A/UX
 #AUX_CFLAGS= -DAUX
+# For SCO ODT
+# libcrypt_i available from sosco.sco.com, files /SLS/lng225b.Z and
+# /SLS/lng225b.ltr.Z
+#AUX_CFLAGS= -DSCO
+#EXTRA_LIBS= -lPW -lsocket -lmalloc -lcrypt_i
+# For SVR4
+#AUX_CFLAGS= -DSVR4
+#EXTRA_LIBS= -lsocket -lnsl -lc
 
 # Place here any flags you may need upon linking, such as a flag to
 # prevent dynamic linking (if desired)
@@ -56,7 +64,8 @@ LFLAGS=
 
 OBJS=http_config.o httpd.o http_request.o util.o http_dir.o \
 http_alias.o http_log.o http_mime.o http_access.o http_auth.o \
-http_get.o http_put.o http_script.o rfc931.o
+http_get.o http_post.o http_script.o http_include.o rfc931.o \
+http_put.o http_delete.o
 
 .c.o:
 	$(CC) -c $(CFLAGS) $(AUX_CFLAGS) $<
diff --git a/src/Makefile~ b/src/Makefile~
deleted file mode 100644
index 099a509..0000000
--- a/src/Makefile~
+++ /dev/null
@@ -1,100 +0,0 @@
-# Makefile for NCSA's httpd. 
-
-# For normal machines with ANSI compilers
-#CC= cc
-# For Suns or other non-ANSI platforms. Please make sure your gcc is
-# 2.0 or later, as 1.40 seems to create bad code for the Sun 4.
-CC= gcc
-
-# For optimization
-#CFLAGS= -O2
-# For debugging information
-CFLAGS= -g -DPEM_AUTH
-#CFLAGS= -g
-
-# Place here any extra libraries you may need to link to. You
-# shouldn't have to.
-EXTRA_LIBS=
-
-# AUX_CFLAGS are system-specific control flags.
-# NOTE: IF YOU DO NOT CHOOSE ONE OF THESE, EDIT httpd.h AND CHOOSE
-# SETTINGS FOR THE SYSTEM FLAGS. IF YOU DON'T, BAD THINGS WILL HAPPEN.
-
-# For SunOS 4
-AUX_CFLAGS= -DSUNOS4
-# For Solaris 2. NOTE: Lots of problems have been identified with compiling
-# httpd under Solaris. Use with caution. If you see aberrant behavior with
-# httpd under Solaris 2, please mail us.
-#AUX_CFLAGS= -DSOLARIS2
-#EXTRA_LIBS= -lsocket -lnsl
-# For SGI IRIX. Use the EXTRA_LIBS line if you're using NIS and want
-# user-supported directories
-#AUX_CFLAGS= -DIRIX
-#EXTRA_LIBS= -lsun
-# For HP-UX
-#AUX_CFLAGS= -DHPUX
-# For AIX
-#AUX_CFLAGS= -DAIX
-# For Ultrix
-#AUX_CFLAGS= -DULTRIX
-# For DEC OSF/1
-#AUX_CFLAGS= -DOSF1
-# For NeXT
-#AUX_CFLAGS= -DNEXT
-# For Sequent
-#AUX_CFLAGS= -DSEQUENT
-# For Linux -m486 ONLY IF YOU HAVE 486 BINARY SUPPORT IN KERNEL
-#AUX_CFLAGS= -DLINUX
-# For A/UX
-#AUX_CFLAGS= -DAUX
-
-# Place here any flags you may need upon linking, such as a flag to
-# prevent dynamic linking (if desired)
-LFLAGS= 
-
-# You shouldn't have to edit anything else.
-
-OBJS=http_config.o httpd.o http_request.o util.o http_dir.o \
-http_alias.o http_log.o http_mime.o http_access.o http_auth.o \
-http_get.o http_put.o http_script.o rfc931.o
-
-.c.o:
-	$(CC) -c $(CFLAGS) $(AUX_CFLAGS) $<
-
-all: httpd
-
-ibm:
-	make tar AUX_CFLAGS=-DAIX CC=gcc CFLAGS=-O2
-
-sun:
-	make tar AUX_CFLAGS=-DSUNOS4 CC=gcc CFLAGS=-O2
-
-hp:
-	make tar AUX_CFLAGS=-DHPUX CC=gcc CFLAGS=-O2
-
-sgi:
-	make tar AUX_CFLAGS=-DIRIX EXTRA_LIBS=-lsun CC=cc CFLAGS=-O2
-
-decmips:
-	make tar AUX_CFLAGS=-DULTRIX CC=cc CFLAGS=-O2
-
-decaxp:
-	make tar AUX_CFLAGS=-DOSF1 CC=cc CFLAGS=-O2
-
-httpd: $(OBJS)
-	$(CC) $(LFLAGS) -o httpd $(OBJS) $(EXTRA_LIBS)
-
-purify: $(OBJS)
-	purify -logfile=/X11/robm/httpd/logs/pure_log \
-	       -program-name=/X11/robm/httpd/src/httpd \
-	$(CC) $(LFLAGS) -o httpd $(OBJS) $(EXTRA_LIBS)
-
-tar: $(OBJS)
-	$(CC) $(LFLAGS) -o ../httpd $(OBJS) $(EXTRA_LIBS)
-	rm -f $(OBJS)
-
-
-$(OBJS): Makefile httpd.h
-
-clean:
-	rm -f ../httpd httpd $(OBJS) *pure*
diff --git a/src/http_access.c b/src/http_access.c
index 2f1e28d..df9f1e9 100644
--- a/src/http_access.c
+++ b/src/http_access.c
@@ -73,16 +73,17 @@ void check_dir_access(int x, int m, int *w, int *n) {
             *w=1;
         if(find_deny(x,m))
             *w=0;
-        if(sec[x].num_auth[m])
-            *n=x;
-    } else {
+    } else if(sec[x].order[m] == DENY_THEN_ALLOW) {
         if(find_deny(x,m))
             *w=0;
         if(find_allow(x,m))
             *w=1;
-        if(sec[x].num_auth[m])
-            *n=x;
     }
+    else
+        *w = find_allow(x,m) && (!find_deny(x,m));
+
+    if(sec[x].num_auth[m])
+        *n=x;
 }
 
 void evaluate_access(char *p, struct stat *finfo, int m, int *allow, 
@@ -91,11 +92,14 @@ void evaluate_access(char *p, struct stat *finfo, int m, int *allow,
     int will_allow, need_auth, num_dirs;
     char opts[MAX_STRING_LEN], override[MAX_STRING_LEN];
     char path[MAX_STRING_LEN], d[MAX_STRING_LEN];
+    char errstr[MAX_STRING_LEN];
     register int x,y,z,n;
 
     if(S_ISDIR(finfo->st_mode)) strcpy_dir(path,p);
     else strcpy(path,p);
 
+    no2slash(path);
+
     num_dirs = count_dirs(path);
     will_allow=1;need_auth=-1;
     auth_type=NULL;auth_name=NULL;auth_pwfile=NULL;auth_grpfile=NULL;
@@ -106,55 +110,101 @@ void evaluate_access(char *p, struct stat *finfo, int m, int *allow,
     }
 
     for(x=0;xst_mode))) && (!(opts[n] & OPT_SYM_LINKS))) {
-        struct stat fi;
+    if((!(S_ISDIR(finfo->st_mode))) && 
+       ((!(opts[n] & OPT_SYM_LINKS)) || (opts[x] & OPT_SYM_OWNER))) {
+        struct stat fi,lfi;
         lstat(path,&fi);
         if(!(S_ISREG(fi.st_mode))) {
-            char errstr[MAX_STRING_LEN];
-            sprintf(errstr,"httpd: will not follow link %s",path);
-            log_error(errstr);
-            *allow=0;
-            *allow_options = OPT_NONE;
-            return;
+            if(opts[n] & OPT_SYM_OWNER) {
+                char realpath[512];
+                int bsz;
+                
+                if((bsz = readlink(path,realpath,256)) == -1)
+                    goto gong;
+                realpath[bsz] = '\0';
+                if(realpath[0] != '/') {
+                    char t[256];
+                    strcpy(t,"../");
+                    strcpy(&t[3],realpath);
+                    make_full_path(path,t,realpath);
+                    getparents(realpath);
+                }
+                lstat(realpath,&lfi);
+                if(fi.st_uid != lfi.st_uid)
+                    goto gong;
+            }
+            else {
+              gong:
+                sprintf(errstr,"httpd: will not follow link %s",path);
+                log_error(errstr);
+                *allow=0;
+                *allow_options = OPT_NONE;
+                return;
+            }
         }
     }
     *allow = will_allow;
@@ -179,6 +229,14 @@ void kill_security() {
             for(y=0;y');
-        n=parse_access_dir(f,n,OR_ALL,w,NULL,stderr);
+        n=parse_access_dir(f,n,OR_ALL,w,NULL,errors);
     }
     fclose(f);
 }
 
-int get_pw(char *user, char *pw) {
+int get_pw(char *user, char *pw, FILE *errors) {
     FILE *f;
     char errstr[MAX_STRING_LEN];
     char l[MAX_STRING_LEN];
@@ -788,7 +886,7 @@ int get_pw(char *user, char *pw) {
 
     if(!(f=fopen(auth_pwfile,"r"))) {
         sprintf(errstr,"Could not open user file %s",auth_pwfile);
-        die(SERVER_ERROR,errstr,stdout); /* AAAAAAAAGH stdout */
+        die(SERVER_ERROR,errstr,errors);
     }
     while(!(cfg_getline(l,MAX_STRING_LEN,f))) {
         if((l[0] == '#') || (!l[0])) continue;
@@ -868,12 +966,12 @@ void kill_group() {
     }   
 }
 
-void read_config()
+void read_config(FILE *errors)
 {
     reset_aliases();
-    process_server_config();
-    init_mime();
+    process_server_config(errors);
+    init_mime(errors);
     init_indexing();
-    process_resource_config();
-    process_access_config();
+    process_resource_config(errors);
+    process_access_config(errors);
 }
diff --git a/src/http_delete.c b/src/http_delete.c
new file mode 100644
index 0000000..6b48fcd
--- /dev/null
+++ b/src/http_delete.c
@@ -0,0 +1,57 @@
+/*
+ * http_delete.c: Handles DELETE
+ * 
+ * Rob McCool
+ * 
+ */
+
+#include "httpd.h"
+
+
+void handle_delete(char *name, char *args, int in, FILE *out) {
+    struct stat finfo;
+    char ct_bak[MAX_STRING_LEN];
+
+    if(stat(name,&finfo) == -1) {
+        if(find_script("DELETE",name,args,in,out))
+            return;
+        if(errno==ENOENT) {
+            log_reason("file does not exist",name);
+            unmunge_name(name);
+            die(NOT_FOUND,name,out);
+        }
+        else {
+            log_reason("file permissions deny server access",name);
+            unmunge_name(name);
+            die(FORBIDDEN,name,out);
+        }
+    }
+    probe_content_type(name);
+    if(!strcmp(content_type,CGI_MAGIC_TYPE)) {
+        strcpy(content_type,ct_bak);
+        send_cgi("DELETE",name,"",args,&finfo,in,out);
+        return;
+    }
+    /* Not a script, do group ann thang */
+    die(NOT_IMPLEMENTED,"DELETE to non-script",out);
+}
+
+
+void delete_node(char *name, char *args, int in, FILE *out) {
+    struct stat finfo;
+    int s;
+
+    s=translate_name(name,out);
+
+    switch(s) {
+      case STD_DOCUMENT:
+        handle_delete(name,args,in,out);
+        return;
+      case REDIRECT_URL:
+        die(REDIRECT,name,out);
+      case SCRIPT_CGI:
+        exec_cgi_script("DELETE",name,args,in,out);
+      default:
+        die(NOT_IMPLEMENTED,"NCSA script exeuction of delete",out);
+    }
+}
diff --git a/src/http_dir.c b/src/http_dir.c
index df93f53..cb48376 100644
--- a/src/http_dir.c
+++ b/src/http_dir.c
@@ -13,31 +13,46 @@
 struct ent {
     char *name;
     char *icon;
+    char *alt;
     char *desc;
     size_t size;
+    time_t lm;
     struct ent *next;
 };
 
+
 struct item {
-    char *msg;
-    char *fn;
+    int type;
+    char *apply_to;
+    char *apply_path;
+    char *data;
     struct item *next;
 };
 
-static struct item *icon_list, *desc_list, *ign_list;
+
+static struct item *icon_list, *alt_list, *desc_list, *ign_list;
+static struct item *hdr_list, *rdme_list, *opts_list;
+
+static int dir_opts;
 
 void init_indexing() {
     icon_list = NULL;
+    alt_list = NULL;
     desc_list = NULL;
     ign_list = NULL;
+
+    hdr_list = NULL;
+    rdme_list = NULL;
+    opts_list = NULL;
 }
 
 void kill_item_list(struct item *p) {
     struct item *q;
 
     while(p) {
-        if(p->msg) free(p->msg);
-        if(p->fn) free(p->fn);
+        if(p->apply_to) free(p->apply_to);
+        if(p->apply_path) free(p->apply_path);
+        if(p->data) free(p->data);
         q = p;
         p = p->next;
         free(q);
@@ -46,202 +61,444 @@ void kill_item_list(struct item *p) {
 
 void kill_indexing() {
     kill_item_list(icon_list);
+    kill_item_list(alt_list);
     kill_item_list(desc_list);
     kill_item_list(ign_list);
+
+    kill_item_list(hdr_list);
+    kill_item_list(rdme_list);
+    kill_item_list(opts_list);
 }
 
-void add_icon(char *icon, char *ext, FILE *out) {
+struct item *new_item(int type, char *to, char *path, char *data, FILE *out) 
+{
     struct item *p;
 
     if(!(p = (struct item *)malloc(sizeof(struct item))))
-        die(NO_MEMORY,"add_icon",out);
-    if(!(p->msg = strdup(icon)))
-        die(NO_MEMORY,"add_icon",out);
-    if(!(p->fn = strdup(ext)))
-        die(NO_MEMORY,"add_icon",out);
+        die(NO_MEMORY,"new_item",out);
+
+    p->type = type;
+    if(data) {
+        if(!(p->data = strdup(data)))
+            die(NO_MEMORY,"new_item",out);
+    } else
+        p->data = NULL;
+
+    if(to) {
+        if(!(p->apply_to = (char *)malloc(strlen(to) + 2)))
+            die(NO_MEMORY,"new_item",out);
+        if((type == BY_PATH) && (!is_matchexp(to))) {
+            p->apply_to[0] = '*';
+            strcpy(&p->apply_to[1],to);
+        } else
+            strcpy(p->apply_to,to);
+    } else
+        p->apply_to = NULL;
+
+    if(!(p->apply_path = (char *)malloc(strlen(path) + 2)))
+        die(NO_MEMORY,"new_item",out);
+    sprintf(p->apply_path,"%s*",path);
+
+    return p;
+}
+
+void add_alt(int type, char *alt, char *to, char *path, FILE *out) {
+    struct item *p;
+
+    if(type == BY_PATH) {
+        if(!strcmp(to,"**DIRECTORY**"))
+            strcpy(to,"^^DIRECTORY^^");
+    }
+    p = new_item(type,to,path,alt,out);
+    p->next = alt_list;
+    alt_list = p;
+}
+
+void add_icon(int type, char *icon, char *to, char *path, FILE *out) {
+    struct item *p;
+    char iconbak[MAX_STRING_LEN];
+
+    strcpy(iconbak,icon);
+    if(icon[0] == '(') {
+        char alt[MAX_STRING_LEN];
+        getword(alt,iconbak,',');
+        add_alt(type,&alt[1],to,path,out);
+        iconbak[strlen(iconbak) - 1] = '\0';
+    }
+    if(type == BY_PATH) {
+        if(!strcmp(to,"**DIRECTORY**"))
+            strcpy(to,"^^DIRECTORY^^");
+    }
+    p = new_item(type,to,path,iconbak,out);
     p->next = icon_list;
     icon_list = p;
 }
 
-char *find_icon(char *path) {
-    struct item *p = icon_list;
-    int pl, el;
-
-    pl = strlen(path);
-    while(p) {
-        el = strlen(p->fn);
-        if(el <= pl)
-            if(!strcmp(&path[pl - el],p->fn))
-                return p->msg;
-        p = p->next;
-    }
-    return NULL;
-}
-
-void add_desc(char *desc, char *ext, FILE *out) {
+void add_desc(int type, char *desc, char *to, char *path, FILE *out) {
     struct item *p;
 
-    if(!(p = (struct item *)malloc(sizeof(struct item))))
-        die(NO_MEMORY,"add_desc",out);
-    if(!(p->msg = strdup(desc)))
-        die(NO_MEMORY,"add_desc",out);
-    if(!(p->fn = strdup(ext)))
-        die(NO_MEMORY,"add_desc",out);
+    p = new_item(type,to,path,desc,out);
     p->next = desc_list;
     desc_list = p;
 }
 
-char *find_desc(char *path) {
-    struct item *p = desc_list;
-    int pl, el;
+void add_ignore(char *ext, char *path, FILE *out) {
+    struct item *p;
+
+    p = new_item(0,ext,path,NULL,out);
+    p->next = ign_list;
+    ign_list = p;
+}
+
+void add_header(char *name, char *path, FILE *out) {
+    struct item *p;
+
+    p = new_item(0,NULL,path,name,out);
+    p->next = hdr_list;
+    hdr_list = p;
+}
+
+void add_readme(char *name, char *path, FILE *out) {
+    struct item *p;
+
+    p = new_item(0,NULL,path,name,out);
+    p->next = rdme_list;
+    rdme_list = p;
+}
+
+
+void add_opts_int(int opts, char *path, FILE *out) {
+    struct item *p;
+
+    p = new_item(0,NULL,path,NULL,out);
+    p->type = opts;
+    p->next = opts_list;
+    opts_list = p;
+}
+
+void add_opts(char *optstr, char *path, FILE *out) {
+    char w[MAX_STRING_LEN];
+    int opts = 0;
+
+    while(optstr[0]) {
+        cfg_getword(w,optstr);
+        if(!strcasecmp(w,"FancyIndexing"))
+            opts |= FANCY_INDEXING;
+        else if(!strcasecmp(w,"IconsAreLinks"))
+            opts |= ICONS_ARE_LINKS;
+        else if(!strcasecmp(w,"ScanHTMLTitles"))
+            opts |= SCAN_HTML_TITLES;
+        else if(!strcasecmp(w,"None"))
+            opts = 0;
+    }
+    add_opts_int(opts,path,out);
+}
+
+
+char *find_item(struct item *list, char *path, int path_only) {
+    struct item *p = list;
+    char *t;
 
-    pl = strlen(path);
     while(p) {
-        el = strlen(p->fn);
-        if(el <= pl)
-            if(!strcmp(&path[pl - el],p->fn))
-                return p->msg;
+        /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */
+        if((path[0] == '^') || (!strcmp_match(path,p->apply_path))) {
+            if(!(p->apply_to))
+                return p->data;
+            else if(p->type == BY_PATH) {
+                if(!strcmp_match(path,p->apply_to))
+                    return p->data;
+            } else if(!path_only) {
+                char pathbak[MAX_STRING_LEN];
+
+                strcpy(pathbak,path);
+                content_encoding[0] = '\0';
+                set_content_type(pathbak);
+                if(!content_encoding[0]) {
+                    if(p->type == BY_TYPE) {
+                        if(!strcmp_match(content_type,p->apply_to))
+                            return p->data;
+                    }
+                } else {
+                    if(p->type == BY_ENCODING) {
+                        if(!strcmp_match(content_encoding,p->apply_to))
+                            return p->data;
+                    }
+                }
+            }
+        }
         p = p->next;
     }
     return NULL;
 }
 
-void add_ignore(char *ext,FILE *out) {
-    struct item *p;
+#define find_icon(p,t) find_item(icon_list,p,t)
+#define find_alt(p) find_item(alt_list,p,0)
+#define find_desc(p) find_item(desc_list,p,0)
+#define find_header(p) find_item(hdr_list,p,0)
+#define find_readme(p) find_item(rdme_list,p,0)
 
-    if(!(p = (struct item *)malloc(sizeof(struct item))))
-        die(NO_MEMORY,"add_ignore",out);
-    p->msg = NULL;
-    if(!(p->fn = strdup(ext)))
-        die(NO_MEMORY,"add_ignore",out);
-    p->next = ign_list;
-    ign_list = p;
-}
 
 int ignore_entry(char *path) {
-    struct item *p;
-    int pl,el;
+    struct item *p = ign_list;
 
-    pl = strlen(path);
-    p = ign_list;
     while(p) {
-        el = strlen(p->fn);
-        if(el <= pl)
-            if(!strcmp(&path[pl - el],p->fn))
+        if(!strcmp_match(path,p->apply_path))
+            if(!strcmp_match(path,p->apply_to))
                 return 1;
         p = p->next;
     }
     return 0;
 }
 
-void insert_readme(char *name, FILE *fd) {
+int find_opts(char *path) {
+    struct item *p = opts_list;
+
+    while(p) {
+        if(!strcmp_match(path,p->apply_path))
+            return p->type;
+        p = p->next;
+    }
+    return 0;
+}
+
+int insert_readme(char *name, char *readme_fname, int rule, FILE *fd) {
     char fn[MAX_STRING_LEN];
     FILE *r;
     struct stat finfo;
     int plaintext=0;
 
-    if(!readme_fname[0])
-        return;
     make_full_path(name,readme_fname,fn);
     strcat(fn,".html");
     if(stat(fn,&finfo) == -1) {
         fn[strlen(fn)-5] = '\0';
         if(stat(fn,&finfo) == -1)
-            return;
+            return 0;
         plaintext=1;
-        fprintf(fd,"
%c",LF);
+        if(rule) bytes_sent += fprintf(fd,"
%c",LF); + bytes_sent += fprintf(fd,"
%c",LF);
     }
-    else fprintf(fd,"
"); + else if(rule) bytes_sent += fprintf(fd,"
%c",LF); if(!(r = fopen(fn,"r"))) - return; - send_fd(r,fd,""); + return 0; + send_fd(r,fd,NULL); fclose(r); if(plaintext) - fprintf(fd,"
%c",LF); + bytes_sent += fprintf(fd,"
%c",LF); + return 1; +} + + +char *find_title(char *filename) { + char titlebuf[MAX_STRING_LEN], *find = ""; + char filebak[MAX_STRING_LEN]; + FILE *thefile; + int x,n,p; + + content_encoding[0] = '\0'; + strcpy(filebak,filename); + set_content_type(filebak); + if((!strcmp(content_type,"text/html")) && (!content_encoding[0])) { + if(!(thefile = fopen(filename,"r"))) + return NULL; + n = fread(titlebuf,sizeof(char),MAX_STRING_LEN - 1,thefile); + titlebuf[n] = '\0'; + for(x=0,p=0;titlebuf[x];x++) { + if(titlebuf[x] == find[p]) { + if(!find[++p]) { + if((p = ind(&titlebuf[++x],'<')) != -1) + titlebuf[x+p] = '\0'; + return strdup(&titlebuf[x]); + } + } else p=0; + } + return NULL; + } + content_encoding[0] = '\0'; + return NULL; } struct ent *make_dir_entry(char *path, char *name, FILE *out) { struct ent *p; struct stat finfo; - char t[MAX_STRING_LEN]; + char t[MAX_STRING_LEN], t2[MAX_STRING_LEN]; if((name[0] == '.') && (!name[1])) return(NULL); make_full_path(path,name,t); + if(ignore_entry(t)) return(NULL); if(!(p=(struct ent *)malloc(sizeof(struct ent)))) die(NO_MEMORY,"make_dir_entry",out); - if(!(p->name=strdup(name))) + if(!(p->name=(char *)malloc(strlen(name) + 2))) die(NO_MEMORY,"make_dir_entry",out); - if(fancy_indexing) { - p->icon = find_icon(t); - if((!fancy_indexing) || stat(t,&finfo) == -1) { + if(dir_opts & FANCY_INDEXING) { + p->alt = find_alt(t); + if((!(dir_opts & FANCY_INDEXING)) || stat(t,&finfo) == -1) { + strcpy(p->name,name); p->size = -1; + p->icon = NULL; + p->desc = NULL; + p->lm = -1; } else { + p->lm = finfo.st_mtime; if(S_ISDIR(finfo.st_mode)) { - if(!p->icon) - p->icon = find_icon("**DIRECTORY**"); + if(!(p->icon = find_icon(t,1))) + p->icon = find_icon("^^DIRECTORY^^",1); + p->alt = "DIR"; p->size = -1; + strcpy_dir(p->name,name); } - else + else { + p->icon = find_icon(t,0); p->size = finfo.st_size; + strcpy(p->name,name); + } } - p->desc = find_desc(t); + if(p->desc = find_desc(t)) + p->desc = strdup(p->desc); + if((!p->desc) && (dir_opts & SCAN_HTML_TITLES)) + p->desc = find_title(t); } else { p->icon = NULL; + p->alt = NULL; p->desc = NULL; p->size = -1; + p->lm = -1; + strcpy(p->name,name); } return(p); } + +void send_size(size_t size, FILE *fd) { + char schar; + + if(size == -1) { + fputs(" -",fd); + bytes_sent += 5; + } + else { + if(!size) { + fputs(" 0K",fd); + bytes_sent += 5; + } + else if(size < 1024) { + fputs(" 1K",fd); + bytes_sent += 5; + } + else if(size < 1048576) + bytes_sent += fprintf(fd,"%4dK",size / 1024); + else + bytes_sent += fprintf(fd,"%4dM",size / 1048576); + } +} + + void output_directories(struct ent **ar,int n,char *name,FILE *fd) { - int x; - char blorf[MAX_STRING_LEN]; + int x,pad; + char anchor[HUGE_STRING_LEN],t[MAX_STRING_LEN],t2[MAX_STRING_LEN]; + char *tp; if(name[0] == '\0') { name[0] = '/'; name[1] = '\0'; } /* aaaaargh Solaris sucks. */ fflush(fd); + + if(dir_opts & FANCY_INDEXING) { + fputs("<PRE>",fd); + bytes_sent += 5; + if(tp = find_icon("^^BLANKICON^^",1)) + bytes_sent += fprintf(fd,"<IMG SRC=\"%s\" ALT=\" \"> ",tp); + bytes_sent += fprintf(fd, +"Name Last modified Size Description%c<HR>%c",LF,LF); + } + else { + fputs("<UL>",fd); + bytes_sent += 4; + } + for(x=0;x<n;x++) { - fprintf(fd,"<%s>%c",(fancy_indexing ? "DD" : "LI"),LF); - if(fancy_indexing && ((ar[x]->icon) || default_icon[0])) { - fprintf(fd,"<IMG SRC=\"%s\"> ", - ar[x]->icon ? ar[x]->icon : default_icon); - } - if(strcmp(ar[x]->name,"..")) { - make_full_path(name,ar[x]->name,blorf); - escape_url(blorf); - fprintf(fd,"<A NAME=\"%s\" HREF=\"%s\">%s</A>",ar[x]->name, - blorf,ar[x]->name); + if((!strcmp(ar[x]->name,"../")) || (!strcmp(ar[x]->name,".."))) { + make_full_path(name,"..",t); + getparents(t); + if(t[0] == '\0') { + t[0] = '/'; t[1] = '\0'; + } + sprintf(anchor,"<A HREF=\"%s\">",t); + strcpy(t2,"Parent Directory</A>"); } else { - make_full_path(name,"..",blorf); - getparents(blorf); - if(blorf[0] == '\0') { - blorf[0] = '/'; blorf[1] = '\0'; + strcpy(t,ar[x]->name); + strcpy(t2,t); + if(strlen(t2) > 21) { + t2[21] = '>'; + t2[22] = '\0'; } - escape_url(blorf); - fprintf(fd,"<A HREF=\"%s\">%s</A>",blorf,"Parent Directory"); + strcat(t2,"</A>"); + escape_url(t); + sprintf(anchor,"<A NAME=\"%s\" HREF=\"%s\">",t,t); } - if(fancy_indexing) { - if(ar[x]->desc) - fprintf(fd," : %s",ar[x]->desc,LF); - if(ar[x]->size != -1) - fprintf(fd," (%d bytes)",ar[x]->size); + escape_url(t); + + if(dir_opts & FANCY_INDEXING) { + if(dir_opts & ICONS_ARE_LINKS) + bytes_sent += fprintf(fd,"%s",anchor); + if((ar[x]->icon) || default_icon[0]) { + bytes_sent += fprintf(fd,"<IMG SRC=\"%s\" ALT=\"[%s]\">", + ar[x]->icon ? ar[x]->icon : default_icon, + ar[x]->alt ? ar[x]->alt : " "); + } + if(dir_opts & ICONS_ARE_LINKS) { + fputs("</A>",fd); + bytes_sent += 4; + } + bytes_sent += fprintf(fd," %s",anchor); + bytes_sent += fprintf(fd,"%-27.27s",t2); + if(ar[x]->lm != -1) { + struct tm *ts = localtime(&ar[x]->lm); + strftime(t,MAX_STRING_LEN,"%d-%h-%y %H:%M ",ts); + fputs(t,fd); + bytes_sent += strlen(t); + } + else { + fputs(" ",fd); + bytes_sent += 17; + } + send_size(ar[x]->size,fd); + fputs(" ",fd); + bytes_sent += 2; + if(ar[x]->desc) { + if(strlen(ar[x]->desc) > 23) { + ar[x]->desc[23] = '>'; + ar[x]->desc[24] = '\0'; + } + bytes_sent += fprintf(fd,"%s",ar[x]->desc); + } } + else + bytes_sent += fprintf(fd,"<LI> %s %s",anchor,t2); fputc(LF,fd); + ++bytes_sent; + } + if(dir_opts & FANCY_INDEXING) { + fputs("</PRE>",fd); + bytes_sent += 6; + } + else { + fputs("</UL>",fd); + bytes_sent += 5; } } + int dsortf(struct ent **s1,struct ent **s2) { return(strcmp((*s1)->name,(*s2)->name)); @@ -256,26 +513,33 @@ void index_directory(char *name, FILE *fd) struct ent *head,*p,*q; struct ent **ar; char unmunged_name[MAX_STRING_LEN]; + char *tmp; - strcpy(unmunged_name,name); + strcpy_dir(unmunged_name,name); unmunge_name(unmunged_name); if(!(d=opendir(name))) die(FORBIDDEN,unmunged_name,fd); strcpy(content_type,"text/html"); + status = 200; if(!assbackwards) send_http_header(fd); if(header_only) return; -/* Spew HTML preamble */ - fprintf(fd,"<HEAD><TITLE>Index of %s",unmunged_name); - fputc(LF,fd); - fprintf(fd,"

Index of %s

",unmunged_name); - fputc(LF,fd); + bytes_sent = 0; - fprintf(fd,fancy_indexing ? "
%c" : "
    %c",LF); + dir_opts = find_opts(name); + +/* Spew HTML preamble */ + bytes_sent += fprintf(fd,"Index of %s", + unmunged_name); + fputc(LF,fd); + ++bytes_sent; + + if((!(tmp = find_header(name))) || (!(insert_readme(name,tmp,0,fd)))) + bytes_sent += fprintf(fd,"

    Index of %s

    %c",unmunged_name,LF); /* * Since we don't know how many dir. entries there are, put them into a @@ -310,12 +574,21 @@ void index_directory(char *name, FILE *fd) while(q) { p=q->next; free(q->name); + if(q->desc) + free(q->desc); free(q); q=p; } closedir(d); - fprintf(fd,fancy_indexing ? "
" : ""); - if(fancy_indexing) - insert_readme(name,fd); - fprintf(fd,""); + if(dir_opts & FANCY_INDEXING) + if(tmp = find_readme(name)) + insert_readme(name,tmp,1,fd); + else { + fputs("",fd); + bytes_sent += 5; + } + + fputs("",fd); + bytes_sent += 7; + log_transaction(); } diff --git a/src/http_get.c b/src/http_get.c index d2c53a8..cf8e4b7 100644 --- a/src/http_get.c +++ b/src/http_get.c @@ -5,8 +5,6 @@ * */ - - #include "httpd.h" @@ -16,34 +14,106 @@ int allow; char allow_options; -void send_file(char *file, FILE *fd, char *args) { +void send_file(char *file, FILE *fd, char *path_args, char *args) { FILE *f; struct stat finfo; + set_content_type(file); + + if((allow_options & OPT_INCLUDES) && + (!strcmp(content_type,INCLUDES_MAGIC_TYPE)) && + (!content_encoding[0])) + { + status = 200; + bytes_sent = 0; + send_parsed_file(file,fd,path_args,args, + allow_options & OPT_INCNOEXEC); + log_transaction(); + return; + } if(!(f=fopen(file,"r"))) { log_reason("file permissions deny server access",file); unmunge_name(file); die(FORBIDDEN,file,fd); /* we've already established that it exists */ } - set_content_type(file); + status = 200; + bytes_sent = 0; if(!assbackwards) { fstat(fileno(f),&finfo); - if(!((allow_options & OPT_INCLUDES) && is_content_type("text/html"))) - set_content_length(finfo.st_size); - set_last_modified(finfo.st_mtime); + set_content_length(finfo.st_size); + set_last_modified(finfo.st_mtime,fd); send_http_header(fd); } num_includes=0; if(!header_only) - send_fd(f,fd,args); + send_fd(f,fd,NULL); + log_transaction(); + fclose(f); } +void send_cgi(char *method, char *file, char *path_args, char *args, + struct stat *finfo, int in, FILE *fd) +{ + char **env; + int m; -void send_node(char *file, char *args, FILE *fd) + if((!strcmp(method,"GET")) || (!strcmp(method,"HEAD"))) + m = M_GET; + else if(!strcmp(method,"POST")) + m = M_POST; + else if(!strcmp(method,"PUT")) + m = M_PUT; + else if(!strcmp(method,"DELETE")) + m = M_DELETE; + + evaluate_access(file,finfo,m,&allow,&allow_options,fd); + if((!allow) || (!(allow_options & OPT_EXECCGI))) { + log_reason("client denied by server configuration",file); + unmunge_name(file); + die(FORBIDDEN,file,fd); + } + if(!(env = add_common_vars(in_headers_env,fd))) + die(NO_MEMORY,"send_cgi",fd); + bytes_sent = 0; + if(cgi_stub(method,file,path_args,args,env,finfo,in,fd) == REDIRECT_URL) + die(REDIRECT,location,fd); + free_env(env); + log_transaction(); +} + +void send_node(char *file, char *args, int in, FILE *fd) { struct stat finfo; + char pa[MAX_STRING_LEN]; + pa[0] = '\0'; if(stat(file,&finfo) == -1) { + /* Look for script or include document */ + int n=count_dirs(file),i,l; + char t[MAX_STRING_LEN]; + + for(i=n;i;--i) { + make_dirstr(file,i,t); + probe_content_type(t); + if(!strcmp(content_type,CGI_MAGIC_TYPE)) { + if(stat(t,&finfo) == -1) + continue; + if(!(S_ISREG(finfo.st_mode))) + break; + l=strlen(t); + strcpy(pa,&file[l]); + file[l] = '\0'; + send_cgi("GET",file,pa,args,&finfo,in,fd); + return; + } else if(!strcmp(content_type,INCLUDES_MAGIC_TYPE)) { + if(stat(t,&finfo) == -1) + continue; + l=strlen(t); + strcpy(pa,&file[l]); + file[l] = '\0'; + goto send_regular; + } + } if(errno==ENOENT) { log_reason("file does not exist",file); unmunge_name(file); @@ -55,6 +125,13 @@ void send_node(char *file, char *args, FILE *fd) die(FORBIDDEN,file,fd); } } + probe_content_type(file); + if(S_ISREG(finfo.st_mode) && (!strcmp(content_type,CGI_MAGIC_TYPE))) { + send_cgi("GET",file,"",args,&finfo,in,fd); + return; + } + + send_regular: /* aaaaack */ evaluate_access(file,&finfo,M_GET,&allow,&allow_options, fd); if(!allow) { log_reason("client denied by server configuration",file); @@ -65,6 +142,13 @@ void send_node(char *file, char *args, FILE *fd) if(S_ISDIR(finfo.st_mode)) { char ifile[MAX_STRING_LEN]; + if(file[strlen(file) - 1] != '/') { + char url[MAX_STRING_LEN]; + strcpy_dir(ifile,file); + unmunge_name(ifile); + construct_url(url,ifile); + die(REDIRECT,url,fd); + } make_full_path(file,index_name,ifile); if(stat(ifile,&finfo) == -1) { if(allow_options & OPT_INDEXES) @@ -76,19 +160,16 @@ void send_node(char *file, char *args, FILE *fd) } } else { - if(file[strlen(file) - 1] != '/') { - char url[MAX_STRING_LEN]; - strcpy_dir(ifile,file); - unmunge_name(ifile); - construct_url(url,ifile); - die(REDIRECT,url,fd); - } - send_file(ifile,fd,args); + probe_content_type(ifile); + if(!strcmp(content_type,CGI_MAGIC_TYPE)) + send_cgi("GET",ifile,pa,args,&finfo,in,fd); + else + send_file(ifile,fd,pa,args); } return; } if(S_ISREG(finfo.st_mode)) - send_file(file,fd,args); + send_file(file,fd,pa,args); else { log_reason("improper file type",file); unmunge_name(file); @@ -106,12 +187,12 @@ void process_get(int in, FILE *out, char *m, char *url, char *args) { s=translate_name(url,out); switch(s) { case STD_DOCUMENT: - send_node(url,args,out); + send_node(url,args,in,out); return; case REDIRECT_URL: die(REDIRECT,url,out); case SCRIPT_NCSA: - exec_get_NCSA(url,args,out); + exec_get_NCSA(url,args,in,out); return; case SCRIPT_CGI: exec_cgi_script(m,url,args,in,out); diff --git a/src/http_include.c b/src/http_include.c new file mode 100644 index 0000000..61cfe76 --- /dev/null +++ b/src/http_include.c @@ -0,0 +1,619 @@ +/* + * http_include.c: Handles the server-parsed HTML documents + * + * Rob McCool + * + */ + +#include "httpd.h" + +#define STARTING_SEQUENCE "" +#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]" +#define DEFAULT_TIME_FORMAT "%A, %d-%h-%y %T %Z" +#define SIZEFMT_BYTES 0 +#define SIZEFMT_KMG 1 + +/* These are stored statically so that they can be reformatted quickly */ +static time_t date,lm; + +/* ------------------------ Environment function -------------------------- */ + +#define NUM_INCLUDE_VARS 5 + +char **add_include_vars(char **env,char *file, char *path_args, char *args, + char *timefmt,FILE *out) +{ + int x; + struct stat finfo; + char ufile[HUGE_STRING_LEN]; + char *t; + + if(!(env = new_env(env,NUM_INCLUDE_VARS,&x))) + die(NO_MEMORY,"add_include_vars",out); + date = time(NULL); + env[x++] = make_env_str("DATE_LOCAL",ht_time(date,timefmt,0),out); + env[x++] = make_env_str("DATE_GMT",ht_time(date,timefmt,1),out); + + if(stat(file,&finfo) != -1) { + lm = finfo.st_mtime; + env[x++] = make_env_str("LAST_MODIFIED",ht_time(lm,timefmt,1),out); + } + strcpy(ufile,file); + unmunge_name(ufile); + env[x++] = make_env_str("DOCUMENT_URI",ufile,out); + if(t = strrchr(ufile,'/')) + ++t; + else + t = ufile; + env[x++] = make_env_str("DOCUMENT_NAME",t,out); + env[x] = NULL; + return env; +} + +#define GET_CHAR(f,c,r) \ + { \ + int i = getc(f); \ + if(feof(f) || ferror(f) || (i == -1)) { \ + fclose(f); \ + return r; \ + } \ + c = (char)i; \ + } + +/* --------------------------- Parser functions --------------------------- */ + +int find_string(FILE *in,char *str, FILE *out) { + int x,l=strlen(str),p; + char c; + + p=0; + while(1) { + GET_CHAR(in,c,1); + if(c == str[p]) { + if((++p) == l) + return 0; + } + else { + if(out) { + if(p) { + for(x=0;x') { + strcpy(tag,"done"); + return tag; + } + } + } + /* this parser is very rigid, needs quotes around value and no spaces */ + while(1) { + if(++n == MAX_STRING_LEN) { + t[MAX_STRING_LEN - 1] = '\0'; + return NULL; + } + if((*t = c) == '\\') { + GET_CHAR(in,c,NULL); + *t = c; + } else if(*t == '=') { + *t++ = '\0'; + tag_val = t; + GET_CHAR(in,c,NULL); + if(c == '\"') { + while(1) { + GET_CHAR(in,c,NULL); + if(++n == MAX_STRING_LEN) { + t[MAX_STRING_LEN - 1] = '\0'; + return NULL; + } + if((*t = c) == '\\') { + GET_CHAR(in,c,NULL); + *t = c; + } else if(*t == '\"') { + *t = '\0'; + return tag_val; + } + ++t; + } + } else + return NULL; + } + ++t; + GET_CHAR(in,c,NULL); + } +} + +int get_directive(FILE *in,char *d) { + char c; + + /* skip initial whitespace */ + while(1) { + GET_CHAR(in,c,1); + if(!isspace(c)) + break; + } + /* now get directive */ + while(1) { + *d++ = c; + GET_CHAR(in,c,1); + if(isspace(c)) + break; + } + *d = '\0'; + return 0; +} + +/* --------------------------- Action handlers ---------------------------- */ + +int send_included_file(char *file, FILE *out, char *fn) { + FILE *f; + struct stat finfo; + int allow;char op,i; + + if(stat(file,&finfo) == -1) + return -1; + evaluate_access(file,&finfo,M_GET,&allow,&op,out); + if(!allow) + return -1; + set_content_type(file); + if((op & OPT_INCLUDES) && (!strcmp(content_type,INCLUDES_MAGIC_TYPE))) { + send_parsed_file(file,out,"","",op & OPT_INCNOEXEC); + chdir_file(fn); /* grumble */ + } + else if(!strcmp(content_type,CGI_MAGIC_TYPE)) + return -1; + else { + if(!(f=fopen(file,"r"))) + return -1; + send_fd(f,out,NULL); + fclose(f); + } + return 0; +} + +int handle_include(FILE *in, FILE *out, char *fn, char *error) { + char tag[MAX_STRING_LEN],errstr[MAX_STRING_LEN]; + char *tag_val; + + while(1) { + if(!(tag_val = get_tag(in,tag))) + return 1; + if(!strcmp(tag,"file")) { + char dir[MAX_STRING_LEN],to_send[MAX_STRING_LEN]; + + getparents(tag_val); /* get rid of any nasties */ + getwd(dir); + make_full_path(dir,tag_val,to_send); + if(send_included_file(to_send,out,fn)) { + sprintf(errstr,"unable to include %s in parsed file %s", + tag_val, fn); + log_error_noclose(errstr); + bytes_sent += fprintf(out,"%s",error); + } + } + else if(!strcmp(tag,"virtual")) { + if(translate_name(tag_val,out) != STD_DOCUMENT) { + bytes_sent += fprintf(out,"%s",error); + log_error_noclose(errstr); + } + else if(send_included_file(tag_val,out,fn)) { + sprintf(errstr,"unable to include %s in parsed file %s", + tag_val, fn); + log_error_noclose(errstr); + bytes_sent += fprintf(out,"%s",error); + } + } + else if(!strcmp(tag,"done")) + return 0; + else { + sprintf(errstr,"unknown parameter %s to tag echo in %s",tag,fn); + log_error_noclose(errstr); + bytes_sent += fprintf(out,"%s",error); + } + } +} + +int handle_echo(FILE *in, FILE *out, char *file, char *error, char **env) { + char tag[MAX_STRING_LEN]; + char *tag_val; + + while(1) { + if(!(tag_val = get_tag(in,tag))) + return 1; + if(!strcmp(tag,"var")) { + int x,i; + + for(x=0;env[x] != NULL; x++) { + i = ind(env[x],'='); + if(!strncmp(env[x],tag_val,i)) { + bytes_sent += fprintf(out,"%s",&env[x][i+1]); + break; + } + } + if(!env[x]) bytes_sent += fprintf(out,"(none)"); + } else if(!strcmp(tag,"done")) + return 0; + else { + char errstr[MAX_STRING_LEN]; + sprintf(errstr,"unknown parameter %s to tag echo in %s",tag,file); + log_error_noclose(errstr); + bytes_sent += fprintf(out,"%s",error); + } + } +} + +int include_cgi(char *s, char *pargs, char *args, char **env, FILE *out) +{ + char *argp,op,d[HUGE_STRING_LEN]; + int allow,check_cgiopt; + struct stat finfo; + + getparents(s); + if(s[0] == '/') { + strcpy(d,s); + if(translate_name(d,out) != SCRIPT_CGI) + return -1; + check_cgiopt=0; + } else { + char dir[MAX_STRING_LEN]; + getwd(dir); + make_full_path(dir,s,d); + check_cgiopt=1; + } + /* No hardwired path info or query allowed */ + if(stat(d,&finfo) == -1) + return -1; + + evaluate_access(d,&finfo,M_GET,&allow,&op,out); + if((!allow) || (check_cgiopt && (!(op & OPT_EXECCGI)))) + return -1; + + if(cgi_stub("GET",d,pargs,args,env,&finfo,-1,out) == REDIRECT_URL) + bytes_sent += fprintf(out,"%s",location,location); + return 0; +} + +static int ipid; +void kill_include_child() { + char errstr[MAX_STRING_LEN]; + sprintf(errstr,"killing command process %d",ipid); + log_error_noclose(errstr); + kill(ipid,SIGKILL); + waitpid(ipid,NULL,0); +} + +int include_cmd(char *s, char *pargs, char *args, char **env, FILE *out) { + int p[2],x; + FILE *f; + + if(pipe(p) == -1) + die(SERVER_ERROR,"httpd: could not create IPC pipe",out); + if((ipid = fork()) == -1) + die(SERVER_ERROR,"httpd: could not fork new process",out); + if(!ipid) { + char *argv0; + + if(pargs[0] || args[0]) { + if(!(env = new_env(env,4,&x))) + return -1; + if(pargs[0]) { + char p2[HUGE_STRING_LEN]; + + escape_shell_cmd(pargs); + env[x++] = make_env_str("PATH_INFO",pargs,out); + strcpy(p2,pargs); + translate_name(p2,out); + env[x++] = make_env_str("PATH_TRANSLATED",p2,out); + } + if(args[0]) { + env[x++] = make_env_str("QUERY_STRING",args,out); + unescape_url(args); + escape_shell_cmd(args); + env[x++] = make_env_str("QUERY_STRING_UNESCAPED",args,out); + } + env[x] = NULL; + } + + close(p[0]); + if(p[1] != STDOUT_FILENO) { + dup2(p[1],STDOUT_FILENO); + close(p[1]); + } + error_log2stderr(); + if(!(argv0 = strrchr(SHELL_PATH,'/'))) + argv0=SHELL_PATH; + if(execle(SHELL_PATH,argv0,"-c",s,(char *)0,env) == -1) { + fprintf(stderr,"httpd: exec of %s failed, errno is %d\n", + SHELL_PATH,errno); + exit(1); + } + } + close(p[1]); + if(!(f=fdopen(p[0],"r"))) { + waitpid(ipid,NULL,0); + return -1; + } + send_fd(f,out,kill_include_child); + fclose(f); + waitpid(ipid,NULL,0); + return 0; +} + + +int handle_exec(FILE *in, FILE *out, char *file, char *path_args, char *args, + char *error, char **env) +{ + char tag[MAX_STRING_LEN],errstr[MAX_STRING_LEN]; + char *tag_val; + + while(1) { + if(!(tag_val = get_tag(in,tag))) + return 1; + if(!strcmp(tag,"cmd")) { + if(include_cmd(tag_val,path_args,args,env,out) == -1) { + sprintf(errstr,"invalid command exec %s in %s",tag_val,file); + log_error_noclose(errstr); + bytes_sent += fprintf(out,"%s",error); + } + /* just in case some stooge changed directories */ + chdir_file(file); + } + else if(!strcmp(tag,"cgi")) { + if(include_cgi(tag_val,path_args,args,env,out) == -1) { + sprintf(errstr,"invalid CGI ref %s in %s",tag_val,file); + log_error_noclose(errstr); + bytes_sent += fprintf(out,"%s",error); + } + /* grumble groan */ + chdir_file(file); + } + else if(!strcmp(tag,"done")) + return 0; + else { + char errstr[MAX_STRING_LEN]; + sprintf(errstr,"unknown parameter %s to tag echo in %s",tag,file); + log_error_noclose(errstr); + bytes_sent += fprintf(out,"%s",error); + } + } + +} + +int handle_config(FILE *in, FILE *out, char *file, char *error, char *tf, + int *sizefmt, char **env) { + char tag[MAX_STRING_LEN]; + char *tag_val; + + while(1) { + if(!(tag_val = get_tag(in,tag))) + return 1; + if(!strcmp(tag,"errmsg")) + strcpy(error,tag_val); + else if(!strcmp(tag,"timefmt")) { + strcpy(tf,tag_val); + /* Replace DATE* and LAST_MODIFIED (they should be first) */ + free(env[0]); + env[0] = make_env_str("DATE_LOCAL",ht_time(date,tf,0),out); + free(env[1]); + env[1] = make_env_str("DATE_GMT",ht_time(date,tf,1),out); + if(!strncmp(env[2],"LAST_MODIFIED",13)) { + free(env[2]); + env[2] = make_env_str("LAST_MODIFIED",ht_time(lm,tf,1),out); + } + } + else if(!strcmp(tag,"sizefmt")) { + if(!strcmp(tag_val,"bytes")) + *sizefmt = SIZEFMT_BYTES; + else if(!strcmp(tag_val,"abbrev")) + *sizefmt = SIZEFMT_KMG; + } + else if(!strcmp(tag,"done")) + return 0; + else { + char errstr[MAX_STRING_LEN]; + sprintf(errstr,"unknown parameter %s to tag config in %s", + tag,file); + log_error_noclose(errstr); + bytes_sent += fprintf(out,"%s",error); + } + } +} + + + +int find_file(FILE *out, char *file, char *directive, char *tag, + char *tag_val, struct stat *finfo, char *error) +{ + char errstr[MAX_STRING_LEN], dir[MAX_STRING_LEN], to_send[MAX_STRING_LEN]; + + if(!strcmp(tag,"file")) { + getparents(tag_val); /* get rid of any nasties */ + getwd(dir); + make_full_path(dir,tag_val,to_send); + if(stat(to_send,finfo) == -1) { + sprintf(errstr, + "unable to get information about %s in parsed file %s", + to_send,file); + log_error_noclose(errstr); + bytes_sent += fprintf(out,"%s",error); + return -1; + } + return 0; + } + else if(!strcmp(tag,"virtual")) { + if(translate_name(tag_val,out) != STD_DOCUMENT) { + bytes_sent += fprintf(out,"%s",error); + log_error_noclose(errstr); + } + else if(stat(tag_val,finfo) == -1) { + sprintf(errstr, + "unable to get information about %s in parsed file %s", + to_send,file); + log_error_noclose(errstr); + bytes_sent += fprintf(out,"%s",error); + return -1; + } + return 0; + } + else { + sprintf(errstr,"unknown parameter %s to tag %s in %s", + tag,directive,file); + log_error_noclose(errstr); + bytes_sent += fprintf(out,"%s",error); + return -1; + } +} + + +int handle_fsize(FILE *in, FILE *out, char *file, char *error, int sizefmt, + char **env) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + struct stat finfo; + + while(1) { + if(!(tag_val = get_tag(in,tag))) + return 1; + else if(!strcmp(tag,"done")) + return 0; + else if(!find_file(out,file,"fsize",tag,tag_val,&finfo,error)) { + if(sizefmt == SIZEFMT_KMG) { + send_size(finfo.st_size,out); + bytes_sent += 5; + } + else { + int l,x; + sprintf(tag,"%ld",finfo.st_size); + l = strlen(tag); /* grrr */ + for(x=0;xtm_mday,mon[t->tm_mon],1900 + t->tm_year, + t->tm_hour,t->tm_min,t->tm_sec); + + strftime(tstr,MAX_STRING_LEN,"%d/%h/%Y:%H:%M:%S",t); + + sprintf(str,"%s %s %s [%s %c%02d%02d] \"%s\" ", + remote_name, + (do_rfc931 ? remote_logname : "-"), + (user[0] ? user : "-"), + tstr, + sign, + timz/3600, + timz%3600, + the_request); + if(status != -1) + sprintf(str,"%s%d ",str,status); + else + strcat(str,"- "); + + if(bytes_sent != -1) + sprintf(str,"%s%d",str,bytes_sent); + else + strcat(str,"- "); + fprintf(xfer_log,"%s\n",str); + fclose(xfer_log); } void log_error(char *err) { @@ -60,6 +115,11 @@ void log_error(char *err) { fclose(error_log); } +void log_error_noclose(char *err) { + fprintf(error_log, "[%s] %s\n",get_time(),err); + fflush(error_log); +} + void log_reason(char *reason, char *file) { char t[MAX_STRING_LEN]; @@ -76,8 +136,7 @@ void begin_http_header(FILE *fd, char *msg) { void error_head(FILE *fd, char *err) { if(!assbackwards) { begin_http_header(fd,err); - fprintf(fd,"Content-type: text/html%c",LF); - fprintf(fd,"%c",LF); + fprintf(fd,"Content-type: text/html%c%c",LF,LF); } if(!header_only) { fprintf(fd,"%s%c",err,LF); @@ -95,6 +154,7 @@ void die(int type, char *err_string, FILE *fd) { switch(type) { case REDIRECT: + status = 302; if(!assbackwards) { begin_http_header(fd,"302 Found"); fprintf(fd,"Location: %s%c",err_string,LF); @@ -106,9 +166,17 @@ void die(int type, char *err_string, FILE *fd) { fprintf(fd,"This document has moved here.

%c", err_string,LF); break; + case USE_LOCAL_COPY: + status = USE_LOCAL_COPY; + begin_http_header(fd,"304 Not modified"); + fputc(LF,fd); + header_only = 1; + break; case AUTH_REQUIRED: + status = 401; if(!assbackwards) { begin_http_header(fd,"401 Unauthorized"); + fprintf(fd,"Content-type: text/html%c",LF); fprintf(fd,"WWW-Authenticate: %s%c%c",err_string,LF,LF); } if(header_only) break; @@ -117,6 +185,7 @@ void die(int type, char *err_string, FILE *fd) { fprintf(fd,"authentication failed.%c",LF); break; case BAD_REQUEST: + status = 400; error_head(fd,"400 Bad Request"); if(header_only) break; fprintf(fd,"Your client sent a query that this server could not%c",LF); @@ -124,6 +193,7 @@ void die(int type, char *err_string, FILE *fd) { fprintf(fd,"Reason: %s

%c",err_string,LF); break; case FORBIDDEN: + status = 403; error_head(fd,"403 Forbidden"); if(header_only) break; fprintf(fd,"Your client does not have permission to get URL %s ", @@ -131,12 +201,14 @@ void die(int type, char *err_string, FILE *fd) { fprintf(fd,"from this server.

%c",LF); break; case NOT_FOUND: + status = 404; error_head(fd,"404 Not Found"); if(header_only) break; fprintf(fd,"The requested URL %s was not found on this server.

%c", err_string,LF); break; case SERVER_ERROR: + status = 500; error_head(fd,"500 Server Error"); log_error(err_string); if(header_only) @@ -150,16 +222,8 @@ void die(int type, char *err_string, FILE *fd) { fprintf(fd,"anything you might have done that may have caused%c",LF); fprintf(fd,"the error.

%c",LF); break; - case INCLUDE_ERROR: -/* error_head(fd,"500 Server Error"); */ - log_error(err_string); - if(header_only) break; - fprintf(fd,"[we're sorry, the following error has occurred: %s]%c", - err_string,LF); - fflush(fd); - htexit(1,fd); - break; case NOT_IMPLEMENTED: + status = 501; error_head(fd,"501 Not Implemented"); if(header_only) break; fprintf(fd,"We are sorry to be unable to perform the method %s", @@ -171,9 +235,18 @@ void die(int type, char *err_string, FILE *fd) { SERVER_VERSION,LF); fprintf(fd,"to

%s

%c",SERVER_SUPPORT,LF); break; + case NO_MEMORY: + log_error("httpd: memory exhausted"); + status = 500; + error_head(fd,"500 Server Error"); + if(header_only) break; + fprintf(fd,"The server has temporarily run out of resources for%c",LF); + fprintf(fd,"your request. Please try again at a later time.

%c",LF); + break; } if(!header_only) fprintf(fd,"%c",LF); fflush(fd); + log_transaction(); htexit(1,fd); } diff --git a/src/http_mime.c b/src/http_mime.c index 1d38610..32b962a 100644 --- a/src/http_mime.c +++ b/src/http_mime.c @@ -14,7 +14,11 @@ struct mime_ext { struct mime_ext *next; }; +#if 0 #define hash(i) (isalpha(i) ? (tolower(i)) - 'a' : 26) +#else +#define hash(i) ((i) % 27) +#endif /* Hash table */ struct mime_ext *types[27]; @@ -23,14 +27,19 @@ struct mime_ext *encoding_types; int content_length; char content_type[MAX_STRING_LEN]; +char content_encoding[MAX_STRING_LEN]; char location[MAX_STRING_LEN]; static char last_modified[MAX_STRING_LEN]; -static char content_encoding[MAX_STRING_LEN]; -char http_accept[HUGE_STRING_LEN]; char auth_line[MAX_STRING_LEN]; +char *out_headers; +char **in_headers_env; +char *status_line; +char ims[MAX_STRING_LEN]; /* If-modified-since */ + + void hash_insert(struct mime_ext *me) { register int i = hash(me->ext[0]); register struct mime_ext *p, *q; @@ -151,7 +160,7 @@ int is_content_type(char *type) { return(!strcmp(content_type,type)); } -void set_content_type(char *file) { +void find_ct(char *file, int store_encoding) { int i,l,l2; struct mime_ext *p; char fn[MAX_STRING_LEN]; @@ -165,10 +174,13 @@ void set_content_type(char *file) { while(p) { if(!strcmp(p->ext,&fn[i])) { fn[i-1] = '\0'; - if(content_encoding[0]) - sprintf(content_encoding,"%s, %s",content_encoding,p->ct); - else - strcpy(content_encoding,p->ct); + if(store_encoding) { + if(content_encoding[0]) + sprintf(content_encoding,"%s, %s",content_encoding, + p->ct); + else + strcpy(content_encoding,p->ct); + } if((i=rind(fn,'.')) < 0) break; ++i; @@ -208,20 +220,57 @@ void set_content_type(char *file) { strcpy(content_type,default_type); } + + +void probe_content_type(char *file) { + find_ct(file,0); +} + +void set_content_type(char *file) { + find_ct(file,1); +} + int scan_script_header(FILE *f, FILE *fd) { - char l[MAX_STRING_LEN]; char w[MAX_STRING_LEN]; + char *l; + int p; while(1) { - if(getline(l,MAX_STRING_LEN,fileno(f),timeout)) + if(getline(w,MAX_STRING_LEN-1,fileno(f),timeout)) die(SERVER_ERROR,"httpd: malformed header from script",fd); - if(l[0] == '\0') return is_url(location); - getword(w,l,':'); + if(w[0] == '\0') return is_url(location); + if(!(l = strchr(w,':'))) + l = w; + *l++ = '\0'; if(!strcasecmp(w,"Content-type")) sscanf(l,"%s",content_type); else if(!strcasecmp(w,"Location")) sscanf(l,"%s",location); + else if(!strcasecmp(w,"Status")) { + for(p=0;isspace(l[p]);p++); + sscanf(&l[p],"%d",&status); + if(!(status_line = strdup(&l[p]))) + die(NO_MEMORY,"scan_script_header",fd); + } + else { + *(--l) = ':'; + for(p=0;w[p];p++); + w[p] = LF; + w[++p] = '\0'; + if(!out_headers) { + if(!(out_headers = strdup(w))) + die(NO_MEMORY,"scan_script_header",fd); + } + else { + int loh = strlen(out_headers); + out_headers = (char *) realloc(out_headers, + (loh+strlen(w)+1)*sizeof(char)); + if(!out_headers) + die(NO_MEMORY,"scan_script_header",fd); + strcpy(&out_headers[loh],w); + } + } } } @@ -257,41 +306,109 @@ void set_content_length(int l) { content_length = l; } -void set_last_modified(time_t t) { +void set_last_modified(time_t t, FILE *out) { strcpy(last_modified,gm_timestr_822(t)); + + if(!ims[0]) + return; + + if(later_than(last_modified, ims)) + die(USE_LOCAL_COPY,NULL,out); } -void get_mime_headers(int fd) { - char l[MAX_STRING_LEN]; - char w[MAX_STRING_LEN]; - +void init_header_vars() { content_type[0] = '\0'; last_modified[0] = '\0'; content_length = -1; auth_line[0] = '\0'; content_encoding[0] = '\0'; - http_accept[0] = '\0'; location[0] = '\0'; + ims[0] = '\0'; + status_line = NULL; + out_headers = NULL; + in_headers_env = NULL; +} - while(!(getline(l,MAX_STRING_LEN,fd,timeout))) { - getword(w,l,':'); - if(!strcasecmp(w,"Content-type")) - sscanf(l,"%s",content_type); - else if(!strcasecmp(w,"Authorization")) - strcpy(auth_line,l); - else if(!strcasecmp(w,"Content-length")) - sscanf(l,"%d",&content_length); - else if(!strcasecmp(w,"Accept")) { - if(http_accept[0]) - sprintf(http_accept,"%s, %s%c",http_accept,l,'\0'); - else - strcpy(http_accept,l); +int merge_header(char *h, char *v, FILE *out) { + register int x,l,lt; + char **t; + + for(l=0;h[l];++l); + h[l] = '='; + h[++l] = '\0'; + + for(t=in_headers_env;*t;++t) { + if(!strncmp(*t,h,l)) { + lt = strlen(*t); + if(!(*t = (char *) realloc(*t,(lt+strlen(v)+3)*sizeof(char)))) + die(NO_MEMORY,"merge_header",out); + (*t)[lt++] = ','; + (*t)[lt++] = ' '; + strcpy(&((*t)[lt]),v); + return 1; } - else if(!l[0]) + } + h[l-1] = '\0'; + return 0; +} + +void get_mime_headers(int fd, FILE *out) { + char w[MAX_STRING_LEN]; + char l[MAX_STRING_LEN]; + int num_inh, num_processed; + char *t; + + num_inh = 0; + num_processed = 0; + + while(!(getline(w,MAX_STRING_LEN-1,fd,timeout))) { + if(!w[0]) return; + if((++num_processed) > MAX_HEADERS) + die(BAD_REQUEST,"too many header lines",out); + if(!(t = strchr(w,':'))) + continue; + *t++ = '\0'; + while(isspace(*t)) ++t; + strcpy(l,t); + + if(!strcasecmp(w,"Content-type")) { + strcpy(content_type,l); + continue; + } + if(!strcasecmp(w,"Authorization")) { + strcpy(auth_line,l); + continue; + } + if(!strcasecmp(w,"Content-length")) { + sscanf(l,"%d",&content_length); + continue; + } + if(!strcasecmp(w,"If-modified-since")) + strcpy(ims,l); + + http2cgi(w); + if(in_headers_env) { + if(!merge_header(w,l,out)) { + in_headers_env = + (char **) realloc(in_headers_env, + (num_inh+2)*sizeof(char *)); + if(!in_headers_env) + die(NO_MEMORY,"get_mime_headers",out); + in_headers_env[num_inh++] = make_env_str(w,l,out); + in_headers_env[num_inh] = NULL; + } + } + else { + if(!(in_headers_env = (char **) malloc(2*sizeof(char *)))) + die(NO_MEMORY,"get_mime_headers",out); + in_headers_env[num_inh++] = make_env_str(w,l,out); + in_headers_env[num_inh] = NULL; + } } } + void dump_default_header(FILE *fd) { fprintf(fd,"Date: %s%c",gm_timestr_822(time(NULL)),LF); fprintf(fd,"Server: %s%c",SERVER_VERSION,LF); @@ -299,7 +416,17 @@ void dump_default_header(FILE *fd) { } void send_http_header(FILE *fd) { - begin_http_header(fd,(location[0] ? "302 Found" : "200 OK")); + if(!status_line) { + if(location[0]) { + status = 302; + status_line = "302 Found"; + } + else { + status = 200; + status_line = "200 OK"; + } + } + begin_http_header(fd,status_line); if(content_type[0]) fprintf(fd,"Content-type: %s%c",content_type,LF); if(last_modified[0]) @@ -309,7 +436,8 @@ void send_http_header(FILE *fd) { if(location[0]) fprintf(fd,"Location: %s%c",location,LF); if(content_encoding[0]) - fprintf(fd,"Content-encoding: %s%c",content_encoding,LF,LF); - + fprintf(fd,"Content-encoding: %s%c",content_encoding,LF); + if(out_headers) + fprintf(fd,"%s",out_headers); fprintf(fd,"%c",LF); } diff --git a/src/http_post.c b/src/http_post.c new file mode 100644 index 0000000..3a0a9ad --- /dev/null +++ b/src/http_post.c @@ -0,0 +1,59 @@ +/* + * http_post.c: Handles POST + * + * Rob McCool + * + */ + +#include "httpd.h" + +void handle_post(char *name, char *args, int in, FILE *out) { + struct stat finfo; + char ct_bak[MAX_STRING_LEN]; + + strcpy(ct_bak,content_type); /* oop ack */ + if(stat(name,&finfo) == -1) { + if(find_script("POST",name,args,in,out)) + return; + if(errno==ENOENT) { + log_reason("file does not exist",name); + unmunge_name(name); + die(NOT_FOUND,name,out); + } + else { + log_reason("file permissions deny server access",name); + unmunge_name(name); + die(FORBIDDEN,name,out); + } + } + probe_content_type(name); + if(!strcmp(content_type,CGI_MAGIC_TYPE)) { + strcpy(content_type,ct_bak); + send_cgi("POST",name,"",args,&finfo,in,out); + return; + } + /* Not a script, do group ann thang */ + die(NOT_IMPLEMENTED,"POST to non-script",out); +} + + + +void post_node(char *name, char *args, int in, FILE *out) { + struct stat finfo; + int s; + + s=translate_name(name,out); + + switch(s) { + case STD_DOCUMENT: + handle_post(name,args,in,out); + return; + case REDIRECT_URL: + die(REDIRECT,name,out); + case SCRIPT_NCSA: + exec_post_NCSA(name,args,in,out); + return; + case SCRIPT_CGI: + exec_cgi_script("POST",name,args,in,out); + } +} diff --git a/src/http_put.c b/src/http_put.c index 8deee30..cd0e2c4 100644 --- a/src/http_put.c +++ b/src/http_put.c @@ -1,5 +1,5 @@ /* - * http_put.c: Handles PUT and POST + * http_put.c: Handles PUT * * Rob McCool * @@ -7,8 +7,38 @@ #include "httpd.h" +void handle_put(char *name, char *args, int in, FILE *out) { + struct stat finfo; + char ct_bak[MAX_STRING_LEN]; -void get_node(char *name, char *args, int in, FILE *out) { + strcpy(ct_bak,content_type); /* oop ack */ + if(stat(name,&finfo) == -1) { + if(find_script("PUT",name,args,in,out)) + return; + if(errno==ENOENT) { + log_reason("file does not exist",name); + unmunge_name(name); + die(NOT_FOUND,name,out); + } + else { + log_reason("file permissions deny server access",name); + unmunge_name(name); + die(FORBIDDEN,name,out); + } + } + probe_content_type(name); + if(!strcmp(content_type,CGI_MAGIC_TYPE)) { + strcpy(content_type,ct_bak); + send_cgi("PUT",name,"",args,&finfo,in,out); + return; + } + /* Not a script, do group ann thang */ + die(NOT_IMPLEMENTED,"PUT to non-script",out); +} + + + +void put_node(char *name, char *args, int in, FILE *out) { struct stat finfo; int s; @@ -16,14 +46,13 @@ void get_node(char *name, char *args, int in, FILE *out) { switch(s) { case STD_DOCUMENT: - die(NOT_IMPLEMENTED, - "POST access to area not configured as script area",out); + handle_put(name,args,in,out); + return; case REDIRECT_URL: die(REDIRECT,name,out); - case SCRIPT_NCSA: - exec_post_NCSA(name,args,in,out); - return; case SCRIPT_CGI: - exec_cgi_script("POST",name,args,in,out); + exec_cgi_script("PUT",name,args,in,out); + default: + die(NOT_IMPLEMENTED,"NCSA script exeuction of delete",out); } } diff --git a/src/http_request.c b/src/http_request.c index 5aded78..e78d201 100644 --- a/src/http_request.c +++ b/src/http_request.c @@ -3,14 +3,11 @@ * * Rob McCool 3/21/93 * - * Include code by Charles Henrich */ #include "httpd.h" -#define OUTBUFSIZE 1024 - int assbackwards; char *remote_host; char *remote_ip; @@ -18,212 +15,87 @@ char *remote_name; /* If RFC931 identity check is on, the remote user name */ char *remote_logname; - -/* - * Thanks to Charles Henrich - */ -void process_include(FILE *f, FILE *fd, char *incstring, char *args) -{ - FILE *ifp; - char srcfile[HUGE_STRING_LEN]; - char command[HUGE_STRING_LEN]; - char errstr[MAX_STRING_LEN]; - char cmd[10]; - - if(sscanf(incstring,"%s \"%[^\"]", cmd, srcfile) != 2) { - sprintf(errstr,"the include string %s was invalid",incstring); - die(INCLUDE_ERROR,errstr,fd); - } - - if(strncasecmp(cmd,"srv",3) != 0) { - fprintf(fd, "%s\r\n",cmd); - return; - } - - if(srcfile[0] != '|') { - if(num_includes > MAXINCLUDES) { - die(INCLUDE_ERROR, - "the maximum number of includes has been exceeded",fd); - } - - num_includes++; - - if(translate_name(srcfile,fd) != 0) - die(INCLUDE_ERROR,"non-standard file include",fd); - ifp=fopen(srcfile,"r"); - - if(ifp != NULL) { - send_fd(ifp, fd, ""); - fclose(ifp); - } - else { - sprintf(errstr,"the include file %s was invalid",srcfile); - die(INCLUDE_ERROR,errstr,fd); - } - } - else { -#ifndef PEM_AUTH - escape_shell_cmd(&srcfile[1]); - if(strncasecmp(cmd,"srvurl",6) == 0) { - escape_shell_cmd(args); - sprintf(command,"%s '%s'",&srcfile[1],args); - } - else { - strcpy(command,&srcfile[1]); - } - - ifp=popen(command,"r"); - - if(ifp != NULL) { - send_fd(ifp, fd, args); - pclose(ifp); - } - else { - sprintf(errstr,"the command %s is invalid",&srcfile[1]); - die(INCLUDE_ERROR,errstr,fd); - } -#else - int pid,p[2]; - - /* This is done because popen() seems to wait for the wrong */ - /* child process when encryption is being used. Suggestions */ - /* are welcome. */ - - if(pipe(p) == -1) - die(SERVER_ERROR,"Could not create an IPC pipe",fd); - if((pid = fork()) == -1) - die(SERVER_ERROR,"Could not fork a new process",fd); - if(!pid) { - char *argv0; - char *cmd; - - cmd = &srcfile[1]; - if(!(argv0 = strrchr(cmd,'/'))) - argv0 = cmd; - else - argv0++; - close(p[0]); - if(p[1] != STDOUT_FILENO) { - dup2(p[1],STDOUT_FILENO); - close(p[1]); - } - if(strncasecmp(cmd,"srvurl",6) == 0) { - escape_shell_cmd(args); - if(execlp(cmd,argv0,args,(char *)0) == -1) { - char errstr[MAX_STRING_LEN]; - sprintf(errstr,"could not execute included pgm %s",cmd); - die(INCLUDE_ERROR,errstr,fd); - } - } - else { - if(execlp(cmd,argv0,(char *)0) == -1) { - char errstr[MAX_STRING_LEN]; - sprintf(errstr,"could not execute included pgm %s",cmd); - die(INCLUDE_ERROR,errstr,fd); - } - } - } else { - close(p[1]); - if(!(ifp = fdopen(p[0],"r"))) - die(INCLUDE_ERROR,"could not open stream to include pipe",fd); - send_fd(ifp,fd,args); - fclose(ifp); - waitpid(pid,NULL,0); - } -#endif - } -} +static void (*exit_callback)(); void send_fd_timed_out() { char errstr[MAX_STRING_LEN]; + if(exit_callback) (*exit_callback)(); sprintf(errstr,"httpd: send timed out for %s",remote_host); log_error(errstr); + log_transaction(); fclose(stdin); fclose(stdout); exit(0); } -void send_fd(FILE *f, FILE *fd, char *args) +void send_fd(FILE *f, FILE *fd, void (*onexit)()) { - int num_chars=0; - char c; - struct stat finfo; + char buf[IOBUFSIZE]; + register int n,o,w; + exit_callback = onexit; signal(SIGALRM,send_fd_timed_out); signal(SIGPIPE,send_fd_timed_out); - if((allow_options & OPT_INCLUDES) && is_content_type("text/html")) { - char *find="st_mode)) { - strcpy(path_args,&path[strlen(t)]); - strcpy(path,t); + int l=strlen(t); + strcpy(path_args,&path[l]); + path[l] = '\0'; return; } } @@ -65,82 +79,96 @@ void get_path_info(char *path, char *path_args, FILE *out, die(NOT_FOUND,path,out); } -#define MAX_CGI_VARS 18 +#define MAX_COMMON_VARS 9 +#define MAX_CGI_VARS (MAX_COMMON_VARS+9) -void exec_cgi_script(char *method, char *path, char *args, int in, FILE *out) +char **add_cgi_vars(char **env, + char *method, char *path, char *path_args, char *args, + int *content, + FILE *out) { - int pid, p[2]; - int content, nph; - char cl[MAX_STRING_LEN],t[MAX_STRING_LEN],t2[MAX_STRING_LEN]; - char path_args[MAX_STRING_LEN]; - char *argv0,**env; - FILE *psin; - struct stat finfo; - register int n,x; + int x; + char t[HUGE_STRING_LEN],t2[HUGE_STRING_LEN]; - get_path_info(path,path_args,out,&finfo); - - if(!can_exec(&finfo)) { - unmunge_name(path); - die(FORBIDDEN,path,out); - } + if(!(env = new_env(env,MAX_CGI_VARS,&x))) + die(NO_MEMORY,"add_cgi_vars",out); - /* BAD -- method specific */ - evaluate_access(path,&finfo,((!strcmp(method,"POST")) ? M_POST : M_GET), - &allow,&allow_options,out); - if(!allow) { - log_reason("client denied by server configuration",path); - unmunge_name(path); - die(FORBIDDEN,path,out); - } + env[x++] = make_env_str("GATEWAY_INTERFACE","CGI/1.1",out); - if(!(env = (char **)malloc((MAX_CGI_VARS + 2) * sizeof(char *)))) - die(NO_MEMORY,"exec_cgi_script",out); - n = 0; - env[n++] = make_env_str("PATH",getenv("PATH"),out); - env[n++] = make_env_str("SERVER_SOFTWARE",SERVER_VERSION,out); - env[n++] = make_env_str("SERVER_NAME",server_hostname,out); - env[n++] = make_env_str("GATEWAY_INTERFACE","CGI/1.0",out); - - sprintf(t,"%d",port); - env[n++] = make_env_str("SERVER_PORT",t,out); - - env[n++] = make_env_str("SERVER_PROTOCOL", + env[x++] = make_env_str("SERVER_PROTOCOL", (assbackwards ? "HTTP/0.9" : "HTTP/1.0"),out); - env[n++] = make_env_str("REQUEST_METHOD",method,out); - env[n++] = make_env_str("HTTP_ACCEPT",http_accept,out); - if(path_args[0]) { - env[n++] = make_env_str("PATH_INFO",path_args,out); - strcpy(t2,path_args); - translate_name(t2,out); - env[n++] = make_env_str("PATH_TRANSLATED",t2,out); - } + env[x++] = make_env_str("REQUEST_METHOD",method,out); + strcpy(t,path); unmunge_name(t); - env[n++] = make_env_str("SCRIPT_NAME",t,out); - env[n++] = make_env_str("QUERY_STRING",args,out); - env[n++] = make_env_str("REMOTE_HOST",remote_name,out); - env[n++] = make_env_str("REMOTE_ADDR",remote_ip,out); - if(user[0]) - env[n++] = make_env_str("REMOTE_USER",user,out); - if(auth_type) - env[n++] = make_env_str("AUTH_TYPE",auth_type,out); - - if(do_rfc931) - env[n++] = make_env_str("REMOTE_IDENT",remote_logname,out); - content=0; - if((!strcmp(method,"POST")) || (!strcmp(method,"PUT"))) { - content=1; - sprintf(cl,"%d",content_length); - env[n++] = make_env_str("CONTENT_TYPE",content_type,out); - env[n++] = make_env_str("CONTENT_LENGTH",cl,out); + env[x++] = make_env_str("SCRIPT_NAME",t,out); + if(path_args[0]) { + env[x++] = make_env_str("PATH_INFO",path_args,out); + strcpy(t2,path_args); + translate_name(t2,out); + env[x++] = make_env_str("PATH_TRANSLATED",t2,out); + } + env[x++] = make_env_str("QUERY_STRING",args,out); + + if(content) { + *content=0; + if((!strcmp(method,"POST")) || (!strcmp(method,"PUT"))) { + *content=1; + sprintf(t,"%d",content_length); + env[x++] = make_env_str("CONTENT_TYPE",content_type,out); + env[x++] = make_env_str("CONTENT_LENGTH",t,out); + } + } + env[x] = NULL; + return env; +} + +char **add_common_vars(char **env,FILE *out) { + char t[HUGE_STRING_LEN],*env_path; + int x; + + if(!(env = new_env(env,MAX_COMMON_VARS,&x))) + die(NO_MEMORY,"add_common_vars",out); + + if(!(env_path = getenv("PATH"))) + env_path=DEFAULT_PATH; + env[x++] = make_env_str("PATH",env_path,out); + env[x++] = make_env_str("SERVER_SOFTWARE",SERVER_VERSION,out); + env[x++] = make_env_str("SERVER_NAME",server_hostname,out); + sprintf(t,"%d",port); + env[x++] = make_env_str("SERVER_PORT",t,out); + env[x++] = make_env_str("REMOTE_HOST",remote_name,out); + env[x++] = make_env_str("REMOTE_ADDR",remote_ip,out); + if(user[0]) + env[x++] = make_env_str("REMOTE_USER",user,out); + if(auth_type) + env[x++] = make_env_str("AUTH_TYPE",auth_type,out); + if(do_rfc931) + env[x++] = make_env_str("REMOTE_IDENT",remote_logname,out); + env[x] = NULL; + return env; +} + +int cgi_stub(char *method, char *path, char *path_args, char *args, + char **env, struct stat *finfo, int in, FILE *out) +{ + int p[2]; + int content, nph; + char *argv0; + FILE *psin; + register int x; + + if(!can_exec(finfo)) { + unmunge_name(path); + die(FORBIDDEN,path,out); } - env[n] = NULL; if((argv0 = strrchr(path,'/')) != NULL) argv0++; else argv0 = path; + chdir_file(path); + if(pipe(p) < 0) die(SERVER_ERROR,"httpd: could not create IPC pipe",out); if((pid = fork()) < 0) @@ -148,6 +176,8 @@ void exec_cgi_script(char *method, char *path, char *args, int in, FILE *out) nph = (strncmp(argv0,"nph-",4) ? 0 : 1); if(!pid) { + close(p[0]); + env = add_cgi_vars(env,method,path,path_args,args,&content,out); if(content) if(in != STDIN_FILENO) { dup2(in,STDIN_FILENO); @@ -164,6 +194,7 @@ void exec_cgi_script(char *method, char *path, char *args, int in, FILE *out) close(p[1]); } } + error_log2stderr(); /* Only ISINDEX scripts get decoded arguments. */ if((!args[0]) || (ind(args,'=') >= 0)) { if(execle(path,argv0,(char *)0,env) == -1) { @@ -181,9 +212,6 @@ void exec_cgi_script(char *method, char *path, char *args, int in, FILE *out) } } else { - for(x=0;x + #if !defined(NEXT) && !defined(CONVEXOS) #include #define DIR_TYPE dirent @@ -158,9 +169,6 @@ typedef int pid_t; /* Max. number of security defines */ #define MAX_SECURITY 50 -/* Max. number of include files */ -#define MAXINCLUDES 20 - /* Default administrator's address */ #define DEFAULT_ADMIN "[no address given]" @@ -218,6 +226,11 @@ typedef int pid_t; /* The default directory in user's home dir */ #define DEFAULT_USER_DIR "public_html" +/* The default path for CGI scripts if none is currently set */ +#define DEFAULT_PATH "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin" + +/* The path to the Bourne shell, for parsed docs */ +#define SHELL_PATH "/bin/sh" /* The default string lengths */ #define MAX_STRING_LEN 256 @@ -226,28 +239,34 @@ typedef int pid_t; /* The timeout for waiting for messages */ #define DEFAULT_TIMEOUT 1200 +/* The size of the server's internal read-write buffers */ +#define IOBUFSIZE 8192 + +/* The number of header lines we will accept from a client */ +#define MAX_HEADERS 200 /* ------------------------------ error types ------------------------------ */ -#define SERVER_VERSION "NCSA/1.1" +#define SERVER_VERSION "NCSA/1.2" #define SERVER_PROTOCOL "HTTP/1.0" #define SERVER_SUPPORT "httpd@ncsa.uiuc.edu" #define DOCUMENT_FOLLOWS 200 #define REDIRECT 302 +#define USE_LOCAL_COPY 304 #define BAD_REQUEST 400 #define AUTH_REQUIRED 401 #define FORBIDDEN 403 #define NOT_FOUND 404 #define SERVER_ERROR 500 #define NOT_IMPLEMENTED 501 -#define INCLUDE_ERROR 6991 #define NO_MEMORY 6992 -#define METHODS 3 +#define METHODS 4 #define M_GET 0 #define M_PUT 1 #define M_POST 2 +#define M_DELETE 3 /* Object types */ #define REDIRECT_URL -1 @@ -259,21 +278,35 @@ typedef int pid_t; #define OPT_INDEXES 1 #define OPT_INCLUDES 2 #define OPT_SYM_LINKS 4 -#define OPT_UNSET 8 -#define OPT_ALL (OPT_INDEXES|OPT_INCLUDES|OPT_SYM_LINKS) +#define OPT_EXECCGI 8 +#define OPT_UNSET 16 +#define OPT_INCNOEXEC 32 +#define OPT_SYM_OWNER 64 +#define OPT_ALL (OPT_INDEXES|OPT_INCLUDES|OPT_SYM_LINKS|OPT_EXECCGI) #define OR_NONE 0 #define OR_LIMIT 1 #define OR_OPTIONS 2 #define OR_FILEINFO 4 -#define OR_AUTHCFG 16 -#define OR_ALL (OR_LIMIT|OR_OPTIONS|OR_FILEINFO|OR_AUTHCFG) +#define OR_AUTHCFG 8 +#define OR_INDEXES 16 +#define OR_ALL (OR_LIMIT|OR_OPTIONS|OR_FILEINFO|OR_AUTHCFG|OR_INDEXES) +#define CGI_MAGIC_TYPE "application/x-httpd-cgi" +#define INCLUDES_MAGIC_TYPE "text/x-server-parsed-html" + +/* For directory indexing */ +#define BY_PATH 0 +#define BY_TYPE 1 +#define BY_ENCODING 2 + +#define FANCY_INDEXING 1 +#define ICONS_ARE_LINKS 2 +#define SCAN_HTML_TITLES 4 #include #include #include -#include #include #include #include @@ -305,6 +338,7 @@ typedef int pid_t; /* For access control */ #define DENY_THEN_ALLOW 0 #define ALLOW_THEN_DENY 1 +#define MUTUAL_FAILURE 2 /* Struct shared by access and auth */ typedef struct { @@ -328,7 +362,7 @@ typedef struct { char *deny[METHODS][MAX_SECURITY]; } security_data; -/* Globals */ +/* Global, global, who's got the globals? */ /* Server config */ extern int standalone; @@ -363,6 +397,7 @@ extern char access_name[MAX_STRING_LEN]; extern char document_root[MAX_STRING_LEN]; extern char default_type[MAX_STRING_LEN]; extern char default_icon[MAX_STRING_LEN]; +extern char blank_icon[MAX_STRING_LEN]; extern int fancy_indexing; extern char readme_fname[MAX_STRING_LEN]; @@ -396,14 +431,21 @@ extern int dirs_in_alias; extern char auth_line[MAX_STRING_LEN]; extern int content_length; extern char content_type[MAX_STRING_LEN]; +extern char content_encoding[MAX_STRING_LEN]; extern char location[MAX_STRING_LEN]; -extern char http_accept[HUGE_STRING_LEN]; +extern char **in_headers_env; + +/* http_log */ +extern FILE *error_log; +extern int bytes_sent; +extern int status; + /* Function prototypes. */ /* http_config */ void read_config(); void parse_htaccess(char *dir, char override, FILE *out); -int get_pw(char *user, char *pw); +int get_pw(char *user, char *pw, FILE *errors); int in_group(char *user, char *group); int init_group(char *grpfile, FILE *out); void kill_group(); @@ -418,44 +460,69 @@ void unmunge_name(char *name); /* http_request */ void process_request(int in, FILE *out); -void send_fd(FILE *f, FILE *fd, char *args); +void send_fd(FILE *f, FILE *fd, void (*callback)()); void send_fd_timed_out(); +int find_script(char *method, char *name, char *args, int in, FILE *out); /* http_get */ -void send_file(char *file,FILE *fd, char *args); +void send_file(char *file,FILE *fd, char *path_args, char *args); void process_include(FILE *f, FILE *fd, char *incstring, char *args); -void send_node(char *name, char *args, FILE *fd); +void send_node(char *name, char *args, int in, FILE *fd); void process_get(int in, FILE *out, char *m, char *url, char *args); +/* http_post */ +void post_node(char *name, char *args, int in, FILE *out); + /* http_put */ -void get_node(char *name, char *args, int in, FILE *out); +void put_node(char *name, char *args, int in, FILE *out); + +/* http_delete */ +void delete_node(char *name, char *args, int in, FILE *out); /* http_script */ void exec_cgi_script(char *method, char *path, char *args, int in, FILE *out); -void exec_get_NCSA(char *path, char *args, FILE *fd); +int cgi_stub(char *method, char *path, char *path_args, char *args, + char **env, struct stat *finfo, int in, FILE *out); +void exec_get_NCSA(char *path, char *args, int in, FILE *fd); void exec_post_NCSA(char *path, char *args, int in, FILE *out); +char **add_common_vars(char **env, FILE *out); +void get_path_info(char *path, char *path_args, FILE *out, + struct stat *finfo); /* http_dir */ -extern void index_directory(char *name, FILE *fd); -extern void add_icon(char *icon, char *ext, FILE *out); -extern void add_desc(char *desc, char *ext, FILE *out); -extern void add_ignore(char *ext, FILE *out); -extern void init_indexing(); -extern void kill_indexing(); +void index_directory(char *name, FILE *fd); +void add_icon(int type, char *icon, char *to, char *path, FILE *out); +void add_alt(int type, char *alt, char *to, char *path, FILE *out); +void add_desc(int type, char *desc, char *to, char *path, FILE *out); +void add_ignore(char *ext, char *path, FILE *out); +void add_header(char *name, char *path, FILE *out); +void add_readme(char *name, char *path, FILE *out); +void add_opts(char *optstr, char *path, FILE *out); +void add_opts_int(int opts, char *path, FILE *out); +void send_size(size_t size, FILE *fd); + +void init_indexing(); +void kill_indexing(); /* http_log */ -void log_transaction(char *cmd_line); +void record_request(char *cmd_line); void log_error(char *err); +void log_error_noclose(char *err); void log_reason(char *reason, char *file); void die(int type, char *err_string, FILE *fd); void open_logs(); void close_logs(); void begin_http_header(FILE *fd, char *msg); +void error_log2stderr(); +void log_transaction(); /* http_mime */ -void get_mime_headers(int fd); +void get_mime_headers(int fd, FILE *out); +void init_header_vars(); void send_http_header(FILE *fd); void set_content_type(char *fn); +void set_last_modified(time_t t, FILE *out); +void probe_content_type(char *fn); int scan_script_header(FILE *f, FILE *fd); void add_type(char *fn, char *t,FILE *out); void add_encoding(char *fn, char *t,FILE *out); @@ -474,15 +541,25 @@ void kill_security(); /* http_auth */ void check_auth(security_data *s, int m, FILE *out); #ifdef PEM_AUTH -int pem_decrypt(int sfd, char *req, FILE **out); +int decrypt_request(int sfd, char *req, FILE **out); void htexit(int status, FILE *out); #endif +/* http_include */ +void send_parsed_file(char *file, FILE *fd, char *path_args, char *args, + int noexec); + /* util */ +void chdir_file(char *file); +void http2cgi(char *w); +int later_than(char *l, char *i); +int strcmp_match(char *str, char *exp); +int is_matchexp(char *str); void strsubfirst(int start,char *dest, char *src); void make_full_path(char *src1,char *src2,char *dst); int is_directory(char *name); void getparents(char *name); +void no2slash(char *name); uid_t uname2id(char *name); gid_t gname2id(char *name); int getline(char *s, int n, int f, unsigned int timeout); @@ -492,6 +569,8 @@ void cfg_getword(char *word, char *line); void get_remote_host(int fd); char *get_time(); char *gm_timestr_822(time_t t); +char *ht_time(time_t t, char *fmt, int gmt); +struct tm *get_gmtoff(long *tz); void make_dirstr(char *s, int n, char *d); int count_dirs(char *path); void strcpy_dir(char *d, char *s); @@ -506,12 +585,14 @@ void uudecode(char *s,unsigned char *d,int dl); char *strdup (char *str); #endif #ifdef NEED_STRCASECMP -char *strcasecmp(const char *s1, const char *s2); +int strcasecmp(const char *s1, const char *s2); #endif #ifdef NEED_STRNCASECMP -char *strncasecmp(const char *s1, const char *s2, int n); +int strncasecmp(const char *s1, const char *s2, int n); #endif char *make_env_str(char *n, char *v, FILE *out); +char **new_env(char **env, int to_add, int *pos); +void free_env(char **env); int ind(char *s, char c); int rind(char *s, char c); void construct_url(char *d, char *s); diff --git a/src/util.c b/src/util.c index d2cb4a7..fa2a4e7 100644 --- a/src/util.c +++ b/src/util.c @@ -19,15 +19,139 @@ char *get_time() { } char *gm_timestr_822(time_t sec) { - struct tm *t; - static char ts[MAX_STRING_LEN]; + /* HUH??? Why is the GMT hardcode necessary? */ + return ht_time(sec,"%A, %d-%h-%y %T GMT", 1); +} + +char *ht_time(time_t t, char *fmt, int gmt) { + static char ts[MAX_STRING_LEN]; + struct tm *tms; + + tms = (gmt ? gmtime(&t) : localtime(&t)); - t = gmtime(&sec); /* check return code? */ - strftime(ts,MAX_STRING_LEN,"%A, %d-%h-%y %T GMT",t); + strftime(ts,MAX_STRING_LEN,fmt,tms); return ts; } +/* What a pain in the ass. */ +struct tm *get_gmtoff(long *tz) { + time_t tt; + struct tm *t; + + tt = time(NULL); + t = localtime(&tt); +#ifdef BSD + *tz = t->tm_gmtoff; +#else + *tz = - timezone; + if(t->tm_isdst) + *tz += 3600; +#endif + return t; +} + +/* What another pain in the ass. */ + +static char *months[] = { + "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" +}; + +static int ydays[] = { + 0,31,59,90,120,151,181,212,243,273,304,334 +}; + +int find_month(char *mon) { + register int x; + + for(x=0;x<12;x++) + if(!strcmp(months[x],mon)) + return x; + return -1; +} + +#define find_yday(mon,day) (ydays[mon] + day) + +int later_than(char *last_modified, char *ims) { + char idate[MAX_STRING_LEN], ldate[MAX_STRING_LEN]; + char itime[MAX_STRING_LEN], ltime[MAX_STRING_LEN]; + char w[MAX_STRING_LEN]; + int lday,lmon,lyear, iday,imon,iyear, lhour,lmin, ihour,imin, x; + long lsec, isec; + + sscanf(ims,"%*s %s %s",idate,itime); + sscanf(last_modified,"%*s %s %s",ldate,ltime); + + getword(w,idate,'-'); + sscanf(w,"%d",&iday); + getword(w,ldate,'-'); + sscanf(w,"%d",&lday); + + getword(w,idate,'-'); + imon = find_month(w); + getword(w,ldate,'-'); + lmon = find_month(w); + + sscanf(idate,"%d",&iyear); + sscanf(ldate,"%d",&lyear); + + x = lyear - iyear; + if(x > 0) return 0; + if(x < 0) return 1; + + x = find_yday(lmon, lday) - find_yday(imon,iday); + if(x > 0) return 0; + if(x < 0) return 1; + + sscanf(itime,"%d:%d:%ld",&ihour,&imin,&isec); + sscanf(ltime,"%d:%d:%ld",&lhour,&lmin,&lsec); + + isec += (imin*60) + (ihour*3600); + lsec += (lmin*60) + (lhour*3600); + + x = lsec - isec; + if(x > 0) return 0; + + return 1; +} + + + + +/* Match = 0, NoMatch = 1, Abort = -1 */ +/* Based loosely on sections of wildmat.c by Rich Salz */ +int strcmp_match(char *str, char *exp) { + int x,y; + + for(x=0,y=0;exp[y];++y,++x) { + if((!str[x]) && (exp[y] != '*')) + return -1; + if(exp[y] == '*') { + while(exp[++y] == '*'); + if(!exp[y]) + return 0; + while(str[x]) { + int ret; + if((ret = strcmp_match(&str[x++],&exp[y])) != 1) + return ret; + } + return -1; + } else + if((exp[y] != '?') && (str[x] != exp[y])) + return 1; + } + return (str[x] != '\0'); +} + +int is_matchexp(char *str) { + register int x; + + for(x=0;str[x];x++) + if((str[x] == '*') || (str[x] == '?')) + return 1; + return 0; +} + void strsubfirst(int start,char *dest, char *src) { char tmp[MAX_STRING_LEN]; @@ -69,6 +193,15 @@ void getparents(char *name) } } +void no2slash(char *name) { + register int x,y; + + for(x=0; name[x]; x++) + if(x && (name[x-1] == '/') && (name[x] == '/')) + for(y=x+1;name[y-1];y++) + name[y-1] = name[y]; +} + void make_dirstr(char *s, int n, char *d) { register int x,f; @@ -101,6 +234,23 @@ void strcpy_dir(char *d, char *s) { d[x] = '\0'; } +void chdir_file(char *file) { + int i; + + if((i = rind(file,'/')) == -1) + return; + file[i] = '\0'; + chdir(file); + file[i] = '/'; +} + +void http2cgi(char *w) { + register int x; + + for(x=strlen(w);x != -1; --x) + w[x+5]= (w[x] == '-' ? '_' : toupper(w[x])); + strncpy(w,"HTTP_",5); +} void getline_timed_out() { char errstr[MAX_STRING_LEN]; @@ -113,12 +263,15 @@ void getline_timed_out() { } int getline(char *s, int n, int f, unsigned int timeout) { - register int i=0; + register int i=0, ret; signal(SIGALRM,getline_timed_out); alarm(timeout); while(1) { - if(read(f,&s[i],1) <= 0) { + if((ret = read(f,&s[i],1)) <= 0) { + /* Mmmmm, Solaris. */ + if((ret == -1) && (errno == EINTR)) + continue; s[i] = '\0'; return 1; } @@ -311,6 +464,34 @@ char *make_env_str(char *name, char *value, FILE *out) { return t; } +char **new_env(char **env, int to_add, int *pos) { + if(!env) { + *pos = 0; + return (char **)malloc((to_add+1)*sizeof(char *)); + } + else { + int x; + char **newenv; + + for(x=0;env[x];x++); + if(!(newenv = (char **)malloc((to_add+x+1)*(sizeof(char *))))) + return NULL; + for(x=0;env[x];x++) + newenv[x] = env[x]; + *pos = x; + free(env); + return newenv; + } +} + +void free_env(char **env) { + int x; + + for(x=0;env[x];x++) + free(env[x]); + free(env); +} + int can_exec(struct stat *finfo) { if(user_id == finfo->st_uid) if(finfo->st_mode & S_IXUSR) @@ -453,8 +634,20 @@ int get_portnum(int sd,FILE *out) { len = sizeof(struct sockaddr); if(getsockname(sd,&addr,&len) < 0) die(SERVER_ERROR,"could not get port number",out); + return ntohs(((struct sockaddr_in *)&addr)->sin_port); +} - return(((struct sockaddr_in *)&addr)->sin_port); +char *find_fqdn(struct hostent *p) { + int x; + + if(ind(p->h_name,'.') == -1) { + for(x=0;p->h_aliases[x];++x) { + if((ind(p->h_aliases[x],'.') != -1) && + (!strncmp(p->h_aliases[x],p->h_name,strlen(p->h_name)))) + return strdup(p->h_aliases[x]); + } + return NULL; + } else return strdup(p->h_name); } void get_remote_host(int fd) { @@ -462,9 +655,9 @@ void get_remote_host(int fd) { int len; struct in_addr *iaddr; struct hostent *hptr; - + len = sizeof(struct sockaddr); - + if ((getpeername(fd, &addr, &len)) < 0) { remote_host=NULL; remote_ip=NULL; @@ -479,7 +672,21 @@ void get_remote_host(int fd) { remote_name = remote_host; } else remote_host = NULL; - + + /* Grrr. Check THAT name to make sure it's really the name of the addr. */ + /* Code from Harald Hanche-Olsen */ + if(remote_host) { + char **haddr; + hptr = gethostbyname(remote_host); + if (hptr) { + for(haddr=hptr->h_addr_list;*haddr;haddr++) { + if(((struct in_addr *)(*haddr))->s_addr == iaddr->s_addr) + break; + } + } + if((!hptr) || (!(*haddr))) + remote_host = NULL; + } remote_ip = inet_ntoa(*iaddr); if(!remote_host) remote_name = remote_ip; @@ -489,8 +696,7 @@ char *get_remote_logname(FILE *fd) { int len; char *result; #ifdef NEXT - struct sockaddr_in sa_server; - struct sockaddr sa_client; + struct sockaddr sa_server, sa_client; #else struct sockaddr_in sa_server,sa_client; #endif @@ -515,9 +721,7 @@ void get_local_host() if(!server_hostname) { struct hostent *p; gethostname(str, len); - if(p=gethostbyname(str)) - server_hostname = strdup(p->h_name); - else { + if((!(p=gethostbyname(str))) || (!(server_hostname = find_fqdn(p)))) { fprintf(stderr,"httpd: cannot determine local host name.\n"); fprintf(stderr,"Use ServerName to set it manually.\n"); exit(1); diff --git a/support/Makefile b/support/Makefile index a722fd7..d88e073 100755 --- a/support/Makefile +++ b/support/Makefile @@ -7,6 +7,8 @@ CC= gcc #CFLAGS= -O2 #For debugging CFLAGS= -g +# For SCO ODT +#EXTRA_LIBS= -lcrypt_i RM= /bin/rm -f #--- You shouldn't have to edit anything else. --- @@ -14,7 +16,7 @@ RM= /bin/rm -f .c.o: $(CC) -c $(CFLAGS) $< -all: htpasswd unescape +all: htpasswd unescape inc2shtml ibm: $(OBJS) make all CC=gcc @@ -38,11 +40,14 @@ tar: htpasswd unescape $(RM) htpasswd unescape htpasswd: htpasswd.c - $(CC) $(CFLAGS) htpasswd.c -o htpasswd + $(CC) $(CFLAGS) htpasswd.c -o htpasswd $(EXTRA_LIBS) unescape: unescape.c $(CC) $(CFLAGS) unescape.c -o unescape + +inc2shtml: inc2shtml.c + $(CC) $(CFLAGS) inc2shtml.c -o inc2shtml clean: - rm -f htpasswd + rm -f htpasswd unescape inc2shtml diff --git a/support/WebReport2.2 b/support/WebReport similarity index 53% rename from support/WebReport2.2 rename to support/WebReport index a16506a..fa4366f 100644 --- a/support/WebReport2.2 +++ b/support/WebReport @@ -24,21 +24,23 @@ ######################################################################### # To install - Please remove everthing above the #! line. # Change the perl path to reflect the location of your perl installation. -# Then answer the following questions. +# Then answer the following question. -# What is the Name of your server? (Not necessary if ServerName is -# defined in your httpd.conf file. -$WebServer = www.ncsa.uiuc.edu; +# Whereis your web log? +$WebLOG = "/web1/http/logs/access_log"; -# What directory holds your configuration files? -$CONF_PATH = "/web1/http/conf"; - -# If you are generating/maintaining HTML Reports what is the Menu Document -# called? Set this to "/dev/null" if you don't want to maintain a menu doc. +# Whereis your HTML Report Menu Document? $MenuDoc = "/General/WebReports.html"; +# What is the name of your Web Server? +$WebServer = "www.ncsa.uiuc.edu"; + +# Where is your httpd.conf file? +$HTTPD_CONF = "/web1/http/conf/httpd.conf"; + # In which directory do you want the output file placed? $Output_Path = "/tmp"; +#$Output_Path = "WebLogs/Reports"; ###################################################################### # DO NOT CHANGE ANTHING BELOW THIS LINE FOR DISTRIBUTION. @@ -51,19 +53,9 @@ $Output_Path = "/tmp"; # Hyperlinks now relative to the Root of the Web Server. # Thanks to Mark Scott. # V1.2 Added -V: Verbose output of progress through log during run. -# V1.1 Release 10/05/93 -# V2.0 Changed default to verbose. Now -n option supresses output -# for use in crontabs -# V2.1 Released 12/11/93 -# Made many changes to make compatible with new release of NCSA_HTTPD -# Unfortunately this kludged version wasn't very stable or robust. -# V2.2 Released 1/21/94 -# Completely rewritten to accomodate new features inherent in current -# releases of NCSA_HTTPD ###################################################################### - sub USAGE { -print "USAGE: WebReport [-adtfhe] [-H] [-s] [-vN] [-n] [-l logname] +print "USAGE: WebReport [-adtfhe] [-H] [-M] [-s] [-vN] [-l logname] [-o output directory] [keys] a All d Daily @@ -75,27 +67,27 @@ print "USAGE: WebReport [-adtfhe] [-H] [-s] [-vN] [-n] [-l logname] l Name of the log file o Location of the output file H Print in html format - vN Report Verbosity - n Supress Program Verbosity + M Update the html Menu Document + vN Verbosity D Current Day only\n"; print " keys To limit the report to specific days, times, files, and/or hosts. Any combination of keys may be specified. See examples below. - Use the following conventions for the best results: + Try to use the following conventions for the best results: Search for a date: 'nn' ie: 10 Search for a time: 'nn:' ie: 10: Search for a Day: 'Xxx' ie: Sun or Sunday Search for a file: 'xxx' ie: software or 'xx/' you may need a leading or '/xx' or trailing '/' - Search for a domain: '.xxx' ie: .edu or ncsa.\n"; + Search for a domain: '.xxx' ie: .edu or ncsa\n"; print " WebReport will try to 'do the right thing' but it is safer to limit the type of search by using one of the -d, -t, -f, or -h flags. EXAMPLES: - WebReport -f Thu Friday 10 11 14 15 Software + WebReport Thu Friday 10 11 14 15 Software Will report on the number of people who accessed the Software files on Thursday and Friday during the 10am, 11am, 2pm, @@ -103,12 +95,15 @@ print " WebReport will try to 'do the right thing' but it is safer WebReport -f edu Will report on file accesses that have the edu substring in them. - WebReport -h .edu - Will report on hosts with edu in the name.\n"; -print " WebReport -Hneshfl /var/log/access_log - Will silently generate an error report, - a summary report, a host access report, and a - file access report -- all in html format, and + WebReport -h edu + Will report on hosts with edu in the name. + WebReport -fh edu + Will report on both hosts and files with + edu in them.\n"; +print " WebReport -HMeshfl /var/log/access_log + Will generate an error report, a summary + report, a host access report, and a file + access report -- all in html format, and will automatically update an html Menu document with links to these reports.\n\n"; @@ -139,42 +134,24 @@ else { ($day, $month, $date, $time, $TZ, $year) = split (" ",$DATE); } -&Getopts('VadDtfhesHMnl:o:v:'); - -if ($opt_V) { -print "WARNING: -V is no longer a valid option. Default is verbose. -Use -n if you wish to supress output\n"; -$USAGE; -} - -if ($opt_M) { -print "WARNING: -M is no longer a valid option. If -H option is used, -then the Menu Document will be updated automatically.\n"; -$USAGE; -} - -$silent = 1 if ($opt_n); +&Getopts('adDtfhesHMVl:o:v:'); # Print Usage If no options are passed on the command line. &USAGE if (!($opt_d) &! ($opt_t) &! ($opt_f) &! ($opt_e) &! ($opt_h) &! ($opt_s) &! ($opt_a) &! ($opt_D)); # If an alternate logfile has been requested if (defined $opt_l) { - $TransferLog = $opt_l; + $WebLOG = $opt_l; } # If an alternate output directory has been requested if (defined $opt_o) { $Output_Path = $opt_o; - chop $Output_Path if ($Output_Path =~ /\n/); - chop $Output_Path if ($Output_Path =~ /\/$/); } - # If you want a printout for today only if ($opt_D) { $SearchDate = "\\b" . $date . "\\b" . "|"; -$SearchDescriptor = $SearchDescriptor . "Today, "; } # If there are no search key limiters. @@ -191,27 +168,21 @@ else { for ($i=0;$i<=$#ARGV;$i++) { if ($ARGV[$i] =~ /Mon|Tue|Wed|Thu|Fri|Sat|Sun/) { $SearchDay = $SearchDay . $ARGV[$i] . "|"; - $SearchDescriptor = $SearchDescriptor . "Day: " . $ARGV[$i] . ", "; } elsif ($ARGV[$i] =~ /\d+:/) { chop $ARGV[$i]; - #chop $ARGV[$i] if ($ARGV[$i] =~ /\n/); $SearchTime = $SearchTime . $ARGV[$i] . "|"; - $SearchDescriptor = $SearchDescriptor . "Time: " . $ARGV[$i] . ":00" . ", "; } elsif ($ARGV[$i] =~ /\./) { - #chop $ARGV[$i]; + chop $ARGV[$i]; $SearchHost = $SearchHost . $ARGV[$i] . "|"; - $SearchDescriptor = $SearchDescriptor . "Host: " . $ARGV[$i] . ", "; } elsif ($ARGV[$i] =~ /\d+/) { die "This does not make sense with the -D option\n" if ($opt_D); $SearchDate = $SearchDate . "\\b" . $ARGV[$i] . "\\b" . "|"; - $SearchDescriptor = $SearchDescriptor . "Date: " . $ARGV[$i] . ", "; } else { $SearchOthers = $SearchOthers . $ARGV[$i] . "|"; - $SearchDescriptor = $SearchDescriptor . "Others: " . $ARGV[$i] . ", "; } } } @@ -255,130 +226,65 @@ else { $NO_OTHERS_KEY = 1; } -# Examine httpd.conf -open (HTTP_CONF,"$CONF_PATH/httpd.conf") || die "Couldn't open $CONF_PATH/httpd.conf\n"; -while () { - chop if (/\n/); - if (!(/\#/)) { - ($AdminHeading,$ServerAdmin) = split (/ /) if (/ServerAdmin/) &! (/\#/); - ($ServerHeading,$WebServer) = split (/ /) if (/ServerName/); - ($RootHeading,$ServerRoot) = split (/ /) if (/ServerRoot/); - ($ErrorHeading,$ErrorLog) = split (/ /) if (/ErrorLog/); - ($TransferHeading,$TransferLog) = split (/ /) if ((/TransferLog/) &! (defined $opt_l)); - ($PidHeading,$PidFile) = split (/ /) if (/ PidFile/); - ($AccessHeading,$AccessConf) = split (/ /) if (/ AccessConfig/); - ($SRMHeading,$SRMConf) = split (/ /) if (/ ResourceConfig/); + +# Get a snapshot of the existing files in the Web Subtree +open (WEBROOT,"$HTTPD_CONF") || die "Couldn't open $HTTPD_CONF\n"; +while () { + if ($_ =~ /^\/:/) { + chop; + ($root,$root_alias) = split (/:/,$_); + } + elsif ($_ =~ /DocumentRoot/) { + chop; + ($root,$root_alias) = split (/ /,$_); + } +} +close WEBROOT; - } -} - -$ServerRoot = "/usr/local/httpd" if (!(defined $ServerRoot)); - -if (defined $ErrorLog) { - $ErrorLog = "$ServerRoot/$ErrorLog" if ($ErrorLog !~ /^\//); -} -else { - $ErrorLog = "$ServerRoot/logs/error_log"; -} - -if (defined $TransferLog) { - $TransferLog = "$ServerRoot/$TransferLog" if (($TransferLog !~ /^\//) &! ($opt_l)); -} -else { - $TransferLog = "$ServerRoot/logs/access_log"; -} - -if (defined $PidFile) { - $PidFile = "$ServerRoot/$PidFile" if ($PidFile !~ /^\//); -} -else { - $PidFile = "$ServerRoot/logs/pid.httpd"; -} -if (defined $AccessConf) { - $AccessConf = "$ServerRoot/$AccessConf" if ($AccessConf !~ /^\//); -} -else { - $AccessConf = "$ServerRoot/conf/access.conf"; -} - -if (defined $SRMConf) { - $SRMConf = "$ServerRoot/$SRMConf" if ($SRMConf !~ /^\//); -} -else { - $SRMConf = "$ServerRoot/conf/srm.conf"; -} -close HTTP_CONF; - -#Examine srm.conf -open (SRM_CONF,"$SRMConf") || die "Couldn't open $SRMConf\n"; -while () { -chop if (/\n/); - if (!(/\#/)) { -# Define root alias - ($RootHeading,$DocumentRoot) = split (/ /,$_) if (/DocumentRoot/); - ($UserHeading,$UserDir) = split (/ /,$_) if (/UserDir/); - ($IndexHeading,$DirIndex) = split (/ /,$_) if (/DirectoryIndex/); - if ((/Redirect/) &! (/\#/)){ - ($RedirectHeading,$LocalFile,$RDFile) = split (/ /); - $RDFILES{$LocalFile} = $RDFile; - } - if ((/Alias/) &! (/\#/)){ - ($AliasHeading,$AliasName,$RealName) = split (/ /); - $RealName = "$ServerRoot/$RealName" if ($RealName !~ /^\//); - chop ($RealName) if ($RealName =~ /\/$/); - $REALNAME{$AliasName} = $RealName; - } - - } -} - -@AKA = keys(%REALNAME); -@GO_HTTP = keys(%RDFILES); -$Output_Path = "$DocumentRoot/$Output_Path" if ($Output_Path !~ /^\//); - - -if (!($silent)) { -print "\nName of Server: $WebServer -Location of Log: $TransferLog -Output Directory: $Output_Path"; -print " -Type Requests for: Map To: -=================================================================== -Aliases: / ==> $DocumentRoot +if ($opt_V) { +print "Name of Server: $WebServer +Root of Server: $root_alias +Location of Log: $WebLOG +Output Directory: $Output_Path "; -if ($#AKA > -1) { -for ($i=0;$i <= $#AKA;$i++) { -printf ("%-16s%-20s==> %s\n"," ",$AKA[$i],$REALNAME{$AKA[$i]}); +print "Checking existence of files\n"; } -} -if ($#GO_HTTP > -1) { -printf ("%-16s%-20s==> %s\n","Redirects:",$GO_HTTP[0],$RDFILES{$GO_HTTP[0]}); -for ($i=1;$i <= $#GO_HTTP;$i++) { -printf ("%-16s%-20s==> %s\n"," ",$GO_HTTP[$i],$RDFILES{$GO_HTTP[$i]}); -} -} -printf ("%-16s%-20s==> ~UserName/%s\n","User Directory:","~",$UserDir) if (defined $UserDir); -print "\n"; +close STDERR if (!($opt_V)); -print "\nType of Report(s): "; -print "Long, " if ($opt_a); -print "Error, " if ($opt_e); -print "Summary, " if ($opt_s); -print "Hosts, " if ($opt_h); -print "Daily, " if ($opt_d); -print "Hourly, " if ($opt_t); -print "Files, " if ($opt_f); -print "\n"; -if (defined ($SearchDescriptor)) { - chop ($SearchDescriptor); - print "Search Keys: $SearchDescriptor\n\n"; +open (LS,"ls -sLR $root_alias |") || die "Couldn't get a listing of existing files\n"; +$counter = 0; +while (){ next if (/^\n/ || /^total/); +if ($opt_V) { + $counter ++; + + if ($counter % 70 != 0) { + print STDERR "."; + } + else { + print STDERR " $counter\n"; + print STDERR "."; + } +} + + if ($_ =~ /^\//) { + $_ =~ s/$root_alias//; + chop; + chop; + $path = $subdir . "/"; + $SIZE{$path} = 0; + $subdir = $_; + next; } + ($size,$filename) = split (' ',$_); + $path = $subdir . "/" . $filename; + $SIZE{$path} = $size; } +close LS; +open STDERR if (!($opt_V)); +print "\nDone Checking existence of files.\n$counter files exist within your Web Directory Structure.\n" if ($opt_V); -# Set the output buffer to 1 -select ((select(STDOUT),$|=1)[0]); -open (LOG,$TransferLog) || die "Couldn't open log file\n"; +open (LOG,$WebLOG) || die "Couldn't open log file\n"; # Process the information in the log. # Set the array information and determine the maximum value for @@ -392,118 +298,54 @@ open (LOG,$TransferLog) || die "Couldn't open log file\n"; $RECORD_NUMBER = 1; $FileMax=1; +if ($opt_V) { +print "\n\nProcessing log ... -if (!($silent)) { -print "Sizing your log ..."; -# Determine the size (lines) of the log file - open (WC,"wc -l $TransferLog |") || die "Can't open $TransferLog\n"; +0 10 20 30 40 50 60 70 80 90 100 +| | | | | | | | | | | +"; + open (WC,"wc -l $WebLOG |"); while () { - ($LINES) = split (/\//,$_); + $LINES = $_; } close WC; - print "$LINES entries \n"; - $RunTime = sprintf ("%d %s",(($LINES/24)/60),"minutes"); - $RunTime = sprintf ("%d %s",($RunTime/60),"hours") if ($RunTime >= 60); - print "Will take approximately $RunTime to process\n"; - $counter = 0; - $star_percentage = sprintf ("%.2d",$LINES * .02); - -print "\nProcessing log ... -(each star represents $star_percentage entries) - -0 10 20 30 40 50 60 70 80 90 100% -|----|----|----|----|----|----|----|----|----|----| -"; - + $line_percentage = sprintf ("%d.0",$LINES/78); + $counter = 1; } while () { -undef ($MODpath); -undef ($changes); -if (!($silent)) { +if ($opt_V) { $counter ++; - if ($counter % 2) { - printf ("%c%s",010,"\\"); - } - else { - printf ("%c%s",010,"\/"); - } - - $counter_percentage = sprintf ("%.2g",$counter/$LINES); - if ($counter_percentage >= .02) { - printf ("%c%s",010,"\* "); - $stars ++; + last if ($counter > $LINES); + if ($counter == $line_percentage) { + print STDERR "*"; $counter = 0; } } - ($host,$day,$mon,$date,$time,$year,$cmd,$GETpath) = split (/\s+/,$_); + ($host,$day,$mon,$date,$time,$year,$cmd,$path) = split (/\s+/,$_); ($hour,$minutes,$seconds) = split (/:/,$time); # remove leading [ $day = substr($day,1,3); # remove trailing [ chop $year; - # Try to correct for extraneous characters in 'GET'. Remove direct # references to the server and strip out double //'s - $GETpath =~ s/^$WebServer//; - $GETpath =~ s/\/\//\//g; - if ($GETpath =~ /^\/$/) { - $GETpath = "$DocumentRoot"; - } - -# If the filename is aliased then convert alias to real name. -foreach $i (0 .. $#AKA) { - $GETpath =~ s/$AKA[$i]/$REALNAME{$AKA[$i]}/g; -} - -# If the filename is redirected to another server then convert -# to redirect. The MODpath creates a virtual directory for -# clarity in the report. -foreach $i (0 .. $#GO_HTTP) { - if ($GETpath =~ /$GO_HTTP[$i]/) { - $file_type = "redirect"; - $GETpath = "/Redirect to $RDFILES{$GO_HTTP[$i]}"; - } -} - -# If the filename refers to a users' directory then -# split the path, determine the user's home directory -# (perl does not understand the '~' as 'home') and -# reconstruct the path. - if ($GETpath =~ /^\~/){ - ($user_name,$remainder) = split (/\//,$GETpath); - $user_name =~ s/\~//; - $home = (getpwnam($user_name))[7]; - $GETpath =~ s/\~\w+/$home\/$UserDir/; - } - -# If it is not an alias, redirect, or user directory then it -# must be relative to DocumentRoot - $GETpath = "$DocumentRoot$GETpath" if ($GETpath !~ /^\//); - - if (($GETpath =~ /\/$/) && ($file_type !~ /redirect/)) { + $path =~ s/^$WebServer//; + $path =~ s/\/\//\//g; + if ($path =~ /\/$/) { $file_type = "directory"; - $GETpath =~ s/\/+$//; } else { $file_type = "file"; } - -# If it has had the Server Root prepended to it, then convert -# it to "ServerRoot" for shorthanding the report. - - $PRINTpath = $GETpath; - $PRINTpath =~ s/$ServerRoot/\/ServerRoot/; - $PRINTpath =~ s/$DocumentRoot/\/DocumentRoot/; - if ($RECORD_NUMBER eq 1) { $start_date = "$mon $date, $year @ $time"; $menu_start_date = "$mon $date"; } # This ridiculously long conditional just tries to do the right thing with # possible search key limiters -if (($NO_KEY) || ($NO_DAY_KEY || ($day =~ /$SearchDay/)) && ($NO_DATE_KEY || ($date =~ /$SearchDate/)) && ($NO_TIME_KEY || ($hour =~ /$SearchTime/)) && ($NO_HOST_KEY || ($host =~ /$SearchHost/)) && ($NO_OTHERS_KEY || ($path =~ (/$SearchOthers/)))) { +if (($NO_KEY) || ($NO_DAY_KEY || ($day =~ /$SearchDay/)) && ($NO_DATE_KEY || ($date =~ /$SearchDate/)) && ($NO_TIME_KEY || ($hour =~ /$SearchTime/)) && ($NO_HOST_KEY || ($host =~ /$SearchHost/)) && ($NO_OTHERS_KEY || ($path =~ (/$SearchOthers/) || ($host =~ /$SearchOthers/)))) { $connections ++; $Connect{$day} ++; $DayMax = $Connect{$day} if ($Connect{$day} > $DayMax); @@ -512,68 +354,50 @@ if (($NO_KEY) || ($NO_DAY_KEY || ($day =~ /$SearchDay/)) && ($NO_DATE_KEY || ($d $Machines{$host} ++; $HostMax = $Machines{$host} if ($Machines{$host} > $HostMax); -# If the size of this file hasn't already been determined then try to get -# it's size. If this returns 0, then it doesn't exist and is therefore an -# error. -if ($file_type !~ /redirect/) { - if (!(defined ($SIZE{$GETpath}))) { - $SIZE{$GETpath} = (-s "$GETpath"); - if (!(defined $SIZE{$GETpath})) { - $EXIST_ERRORS{$GETpath} ++; - $EXIST_ERRORS ++; - next; - } - else { - $SIZE{$GETpath} = sprintf("%9.0d",$SIZE{$GETpath}/1024); - } +# If this file wasn't seen when trying to get sizes for files then it doesn't +# exist and is therefore an error. + if (!(defined ($SIZE{$path}))) { + $EXIST_ERRORS{$path} ++; + $EXIST_ERRORS ++; + next; } else { - $KBYTES{$day} = $KBYTES{$day} + $SIZE{$GETpath}; + $KBYTES{$day} = $KBYTES{$day} + $SIZE{$path}; $SizeDayMax = $KBYTES{$day} if ($KBYTES{$day} > $SizeDayMax); - $KBYTES{$hour} = $KBYTES{$hour} + $SIZE{$GETpath}; + $KBYTES{$hour} = $KBYTES{$hour} + $SIZE{$path}; $SizeHourMax = $KBYTES{$hour} if ($KBYTES{$hour} > $SizeHourMax); } # Get subtotals for each directory in the path name of the file -# This is strictly for the report breakdown. - undef ($ParentDir); - @Tree = split (/\//,$PRINTpath); + $ParentDir = ''; + @Tree = split (/\//,$path); for ($i=1;$i < $#Tree;$i++) { + next if $Tree[$i] !~ /[a-zA-Z0-9\-]+/; $ParentDir = $ParentDir . "/" . $Tree[$i]; $Directory{$ParentDir} ++; - $KBYTES{$ParentDir} = $KBYTES{$ParentDir} + $SIZE{$GETpath}; + $KBYTES{$ParentDir} = $KBYTES{$ParentDir} + $SIZE{$path}; } if ($file_type =~ /file/) { - $TOTAL_KBYTES = $TOTAL_KBYTES + $SIZE{$GETpath}; - $KBYTES{$GETpath} = $KBYTES{$GETpath} + $SIZE{$GETpath}; + $TOTAL_KBYTES = $TOTAL_KBYTES + $SIZE{$path}; + $KBYTES{$path} = $KBYTES{$path} + $SIZE{$path}; $FILE_ACCESSES ++; - $Directory{$GETpath} ++; + $Directory{$path} ++; + #$FileMax = $Directory{$path} if ($Directory{$path} > $FileMax); } if ($file_type =~ /directory/) { $DIRECTORY_ACCESSES ++; - $Directory{$GETpath} ++; + chop $path; + $Directory{$path} ++; + #$FileMax = $Directory{$path} if ($Directory{$path} > $FileMax); } -} -else { - $REDIRECTS ++; - $KBYTES{$GETpath} = "N/A"; - $Directory{$GETpath} ++; -} + $RECORD_NUMBER ++; } } - close LOG; - -# This just finishes the thermometer if the -# fractional percentage does not allow printing -# of the last star. -if (!($silent) && ($stars < 51)){ - printf ("%c%s",010,"\*"); -} -print "\n\n" if (!($silent)); +print "\n" if ($opt_V); # Set the end date variable to that of the last entry in the log. @@ -585,14 +409,12 @@ $OutFile_Prefix = "$Output_Path/$date$mon$year"; # HyperLink path for files $HyperLink_tmp = $OutFile_Prefix; -$HyperLink_tmp =~ s/$DocumentRoot//; -#$HyperLink_tmp =~ s/(.*) (\.+\/) ($UserDir)/\~\2\/\3/; -$HyperLink_tmp =~ s/(\w*\W*) (\w+\/)($UserDir)/\~\2\/\3/; +$HyperLink_tmp =~ s/$root_alias//; $HyperLink_Prefix = "http://$WebServer$HyperLink_tmp"; # HyperLink path for Menu Document $MenuLink = $MenuDoc; -$MenuLink =~ s/$DocumentRoot//; +$MenuLink =~ s/$root_alias//; $MenuDocLink = "http://$WebServer$MenuLink"; # This number represents either the number of subdirectory levels to report @@ -604,11 +426,9 @@ else { $verbosity_limit = 100; } -# Summary report if ($opt_s) { $OutFile = $OutFile_Prefix . ".sum"; - $OutFile = $OutFile . ".html" if ($opt_H); - print "Writing: $OutFile\n" if (!($silent)); + print STDERR "Writing: $OutFile\n"; &PRINT_HEADING; open (OUTPUT,">> $OutFile") || die "Can't create $OutFile\n"; if ($opt_H) { @@ -620,67 +440,55 @@ Access Report Summary

"; } close OUTPUT; - &DO_DAILY(summary); - &DO_HOURLY(summary); + &DO_DAILY; + &DO_HOURLY; &DO_FILES(summary); } -# Error Report if ($opt_e) { - $OutFile = $OutFile_Prefix . ".error"; - $OutFile = $OutFile . ".html" if ($opt_H); - print "Writing: $OutFile\n" if (!($silent)); - &DO_ERRORS; +$OutFile = $OutFile_Prefix . ".error"; +print STDERR "Writing: $OutFile\n"; +&DO_ERRORS; } -# File Report if ($opt_f) { - $OutFile = $OutFile_Prefix . ".files"; - $OutFile = $OutFile . ".html" if ($opt_H); - print "Writing: $OutFile\n" if (!($silent)); - &DO_FILES(no_summary); +$OutFile = $OutFile_Prefix . ".files"; +print STDERR "Writing: $OutFile\n"; +&DO_FILES(no_summary); } -# Host Report if ($opt_h) { - $OutFile = $OutFile_Prefix . ".hosts"; - $OutFile = $OutFile . ".html" if ($opt_H); - print "Writing: $OutFile\n" if (!($silent)); - &DO_HOSTS(no_summary); +$OutFile = $OutFile_Prefix . ".hosts"; +print STDERR "Writing: $OutFile\n"; +&DO_HOSTS; } -# Daily Report if ($opt_d) { - $OutFile = $OutFile_Prefix . ".daily"; - $OutFile = $OutFile . ".html" if ($opt_H); - print "Writing: $OutFile\n" if (!($silent)); - &DO_DAILY(no_summary); +$OutFile = $OutFile_Prefix . ".daily"; +print STDERR "Writing: $OutFile\n"; +&DO_DAILY; } -# Time Report if ($opt_t) { - $OutFile = $OutFile_Prefix . ".hourly"; - $OutFile = $OutFile . ".html" if ($opt_H); - print "Writing: $OutFile\n" if (!($silent)); - &DO_HOURLY(no_summary); +$OutFile = $OutFile_Prefix . ".hourly"; +print STDERR "Writing: $OutFile\n"; +&DO_HOURLY; } -# All if ($opt_a) { $OutFile = $OutFile_Prefix . ".long"; - $OutFile = $OutFile . ".html" if ($opt_H); - print "Writing: $OutFile\n" if (!($silent)); +print STDERR "Writing: $OutFile\n"; &PRINT_HEADING; - &DO_DAILY(no_summary); - &DO_HOURLY(no_summary); - &DO_HOSTS(no_summary); - &DO_FILES(no_summary); + &DO_DAILY; + &DO_HOURLY; + &DO_HOSTS; + &DO_FILES; } -if ($opt_H) { +if ($opt_M) { if (!(-e "$MenuDoc")) { open (MENU_WRITE,"> $MenuDoc")|| die "Can't create Menu Document\n"; print MENU_WRITE "W3 Server Report Menu @@ -701,13 +509,13 @@ while () { if (/\/) { print MENU_WRITE "$_\n"; print MENU_WRITE "
  • $menu_start_date to $menu_end_date: "; -print MENU_WRITE "Summary/" if ($opt_s); -print MENU_WRITE "File Accesses/" if ($opt_f); -print MENU_WRITE "Host Accesses/" if ($opt_h); -print MENU_WRITE "Errors/" if ($opt_e); -print MENU_WRITE "Daily Accesses/" if ($opt_d); -print MENU_WRITE "Hourly Accesses/" if ($opt_t); -print MENU_WRITE "Full Report/" if ($opt_a); +print MENU_WRITE "Summary/" if ($opt_s); +print MENU_WRITE "File Accesses/" if ($opt_f); +print MENU_WRITE "Host Accesses/" if ($opt_h); +print MENU_WRITE "Errors/" if ($opt_e); +print MENU_WRITE "Daily Accesses/" if ($opt_d); +print MENU_WRITE "Hourly Accesses/" if ($opt_t); +print MENU_WRITE "Full Report/" if ($opt_a); print MENU_WRITE "\n"; } else { @@ -721,9 +529,15 @@ chmod (0644,$MenuDoc); } sub DO_ERRORS { - open (OUTPUT,"> $OutFile") || die "Can't create $OutFile\n"; - $TITLE = "Missing File Report"; - &HYPER_LINK(error) if ($opt_H); +open (OUTPUT,"> $OutFile") || die "Can't create $OutFile\n"; +if ($opt_H) { +print OUTPUT "
    +Missing File Report
    +

    Missing File Report

    +See Also: Report Menu / Summary / File Accesses / Host Accesses; + +"; +} print OUTPUT "==================================================================\n"; print OUTPUT " Web Error History\n"; @@ -772,6 +586,11 @@ printf OUTPUT "
    \n" if ($opt_H); close OUTPUT; } + + + + + sub DO_DAILY { # Set an array to convert 3 letter weekday abbreviations to full names. @weekdays = ('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'); @@ -780,19 +599,16 @@ $SubTotal=0; # Print the connections/weekday data print OUTPUT "\n"; print OUTPUT "

    \n" if ($opt_H); -$TITLE = "Connections & Byte Count Per Week Day\n"; -print OUTPUT $TITLE if (!($opt_H)); +print OUTPUT "Connections & Byte Count Per Week Day\n"; print OUTPUT "

    \n" if ($opt_H); -#$TITLE = "Daily Report"; -$arg = pop(@_); -if ($arg =~ /^summary$/) { - &HYPER_LINK(daily_sum) if ($opt_H); -} -else { - &HYPER_LINK(daily) if ($opt_H); +if ($opt_H) { +print OUTPUT "
    +See Also: Report Menu/File Access Report/Host Accesses/Errors
    +
    +";
     }
     
    -        printf OUTPUT ("%-17s%-30s\n%s\n"," Day","Results ( . -> Connections, * -> Kbytes )","---------------------------------------------------------------------");
    +        printf OUTPUT ("%-12s%-30s\n%s\n"," Day","Connections","---------------------------------------------------------------------");
     
     # Print the output for each record.
     
    @@ -816,7 +632,7 @@ $index = substr($heading,0,3);
     	}
     # Print them.
     	if (($index =~ /$SearchDay/) || $NO_DAY_KEY) {
    -       	 printf OUTPUT ("%-15s%9s %s\n%-15s%9s %s\n",$heading,$Connect{$index},$Access_graph,"        ",$KBYTES{$index},$Size_graph);
    +       	 printf OUTPUT ("%-15s%9s %s\n%-15s%9s %s\n",$heading,$Connect{$index},$Access_graph,"  Kbytes",$KBYTES{$index},$Size_graph);
     	$SubTotal = $SubTotal + $Connect{$index};
     	}
     }
    @@ -834,19 +650,15 @@ $SubTotal=0;
     @Hours = ('00','01','02','03','04','05','06','07','08','09',10 .. 23);
     print OUTPUT "\n";
     print OUTPUT "

    \n" if ($opt_H); -$TITLE = "Connections & Byte Count Per Hour\n"; -print OUTPUT $TITLE if (!($opt_H)); +print OUTPUT "Connections & Byte Count Per Hour\n"; print OUTPUT "

    \n" if ($opt_H); -#$TITLE = "Hourly Report"; -$arg = pop(@_); -if ($arg =~ /^summary$/) { - &HYPER_LINK(hourly_sum) if ($opt_H); -} -else { - &HYPER_LINK(hourly) if ($opt_H); -} +if ($opt_H) { +print OUTPUT "
    +See Also: Report Menu/File Access Report/Host Accesses/Errors
     
    -        printf OUTPUT ("%-17s%-30s\n%s\n"," Hour","Results ( . -> Connections, * -> Kbytes )","---------------------------------------------------------------------");
    +";
    +}
    +        printf OUTPUT ("%-12s%-30s\n%s\n"," Hour","Connections","---------------------------------------------------------------------");
     foreach $heading (@Hours) {
             if ($HourMax > 0 ){
             $Access_graph = '.' x ($Connect{$heading} / $HourMax * 50);
    @@ -866,11 +678,11 @@ foreach $heading (@Hours) {
     	$SubTotal = $SubTotal + $Connect{$heading};
     # Change the heading to read 'Midnight' if appropriate
             if ($heading == '00') {
    -       	 	printf OUTPUT ("%-15s%9s %s\n%-15s%9s %s\n","Midnight",$Connect{$heading},$Access_graph,"        ",$KBYTES{$heading},$Size_graph);
    +       	 	printf OUTPUT ("%-15s%9s %s\n%-15s%9s %s\n","Midnight",$Connect{$heading},$Access_graph,"  Kbytes",$KBYTES{$heading},$Size_graph);
             }
     # Change the heading to read 'Noon' if appropriate
            elsif ($heading == '12') {
    -       	 	printf OUTPUT ("%-15s%9s %s\n%-15s%9s %s\n","Noon",$Connect{$heading},$Access_graph,"        ",$KBYTES{$heading},$Size_graph);
    +       	 	printf OUTPUT ("%-15s%9s %s\n%-15s%9s %s\n","Noon",$Connect{$heading},$Access_graph,"  Kbytes",$KBYTES{$heading},$Size_graph);
             }
     # Else just print the hour by number.
             else {
    @@ -882,7 +694,7 @@ foreach $heading (@Hours) {
     			$modifier = "PM"; 
     			$print_heading = $heading - 12;
     		}
    -       	 	printf OUTPUT ("%3s %-11s%9s %s\n%-15s%9s %s\n",$print_heading,$modifier,$Connect{$heading},$Access_graph,"        ",$KBYTES{$heading},$Size_graph);
    +       	 	printf OUTPUT ("%3s %-11s%9s %s\n%-15s%9s %s\n",$print_heading,$modifier,$Connect{$heading},$Access_graph,"  Kbytes",$KBYTES{$heading},$Size_graph);
             }
     }
     }
    @@ -893,26 +705,32 @@ close OUTPUT;
     }
     
     sub DO_FILES {
    +$arg = pop(@_);
     open (OUTPUT,">> $OutFile") || die "Can't create $OutFile\n";
     print OUTPUT "\n";
     print OUTPUT "

    \n" if ($opt_H); -$arg = pop(@_); -$local_verbosity = $verbosity_limit; -if (($arg =~ /^summary$/) || defined ($opt_v)){ - $TITLE = "Abbreviated Access Report for Directories and Files\n"; - print OUTPUT $TITLE if (!($opt_H)); - $local_verbosity = 2 if (!(defined ($opt_v))); +if (($arg =~ /summary/) || defined ($opt_v)){ +print OUTPUT "Abbreviated Access Report for Directories and Files\n"; } else { - $TITLE = "Access Report for Directories and Files\n"; -print OUTPUT $TITLE if (!($opt_H)); +print OUTPUT "Access Report for Directories and Files\n" if ($arg =~ /summary/); } - print OUTPUT "

    \n" if ($opt_H); -#$TITLE = "File Access Report"; -&HYPER_LINK(file_sum) if (($opt_H) && ($arg =~ /^summary$/)); -&HYPER_LINK(file) if (($opt_H) && ($arg =~ /^no_summary$/)); +if ($opt_H) { + if ($arg =~ /summary/) { +print OUTPUT "
    + See Also: Report Menu / Complete File Access Report / Host Accesses / Errors
     
    +";
    +	}
    +	else {
    +print OUTPUT "
    +File Access Report
    +See Also: Report Menu / Summary / Host Accesses / Errors
    +
    +";
    +	}
    +}
     $SubTotal=0;
     print OUTPUT "\n";
     printf OUTPUT ("%s%32s       %s\n%s\n","Directory or File Accessed","Files","Kb","-------------------------------------------------------------------------");
    @@ -923,9 +741,6 @@ printf OUTPUT ("%s%32s       %s\n%s\n","Directory or File Accessed","Files","Kb"
     @sortedDIRS = sort @DIRS;
     
     foreach $heading (@sortedDIRS) {
    -#@previous_heading = "";
    -$PrintHeading = "";
    -$subdir = "";
     $StringLength = 0;
     
     # Create an array consisting of the names in the full path without
    @@ -933,6 +748,7 @@ $StringLength = 0;
             @heading_format = split (/\//,$heading);
     	
     $PRINT_ME = 1;
    +#$PRINT_ME = 1 if ($heading =~ /$SearchOthers/);
     
     # Iterate through this list of file names that defines the path.  For each
     # name in this path that is the same as the path of the previously printed 
    @@ -940,36 +756,38 @@ $PRINT_ME = 1;
     # This is all to prevent the duplication of full pathnames in the output 
     # to make it more readable.
     
    -for ($i=1;$i <= $#heading_format;$i++) {
    -	$subdir = $subdir . "/" . $heading_format[$i];
    -	if ($heading_format[$i] eq $previous_heading[$i]) {
    -		$PrintHeading = "." . $PrintHeading;
    -	}
    -	else {
    -		$PrintHeading = $PrintHeading . $heading_format[$i];
    +        for ($i=1;$i < $#heading_format;$i++) {
    +                if ($heading_format[$i] eq $previous_heading[$i]) {
    +                        $heading_format[$i] = ".";
    +                }
    +        }
     
    -		$StringLength = length ($PrintHeading);
    -		#if ($StringLength > 50){
    -		#   $PrintHeading = substr ($PrintHeading,0,(47 - $i)) . "...";
    -		#   $StringLength = 50;
    -		#}
    +	$StringLength = length ($heading_format[$i]) + $i;
    +	if ($StringLength > 50){
    +		$heading_format[$i] = substr ($heading_format[$i],0,(47 - $i)) . "...";
    +		$StringLength = 50;
    +	}
     
     # Insert a line of tildes from the filename to it's data to make viewing easier.
    -$Spaces = "~" x (57 - ($StringLength + length ($Directory{$subdir})));
    -$PrintHeading = "$PrintHeading $Spaces ";
    +$Spaces = "~" x (57 - ($StringLength + length ($Directory{$heading})));
    +$heading_format[$i] = "$heading_format[$i] $Spaces ";
    +
    +# If this is the root menu then label it as such.
    +        if ($heading eq "") {
    +                @heading_format = "Main_Menu ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ";
    +        }
    +
    +# Set the printed array element delimiter to null
    +$" = "";
     
     # Print it.
    -		if ($i <= $local_verbosity) {
    -#print STDERR "$subdir\n";
    -			if ($PRINT_ME || $NO_OTHERS_KEY) {
    -			   $SubTotal = $SubTotal + $Directory{$subdir};
    -			   $KBYTES{$subdir} = ">1" if $KBYTES{$subdir} == 0;
    -         		    printf OUTPUT ("%s%s%9s Kb\n","$PrintHeading",$Directory{$subdir},$KBYTES{$subdir});
    -			}
    -		}
    -$PrintHeading = "." x $i;
    +if ($i <= $verbosity_limit) {
    +	if ($PRINT_ME || $NO_OTHERS_KEY) {
    +	$SubTotal = $SubTotal + $Directory{$heading};
    +        printf OUTPUT ("%s%s%9s Kb\n","@heading_format",$Directory{$heading},$KBYTES{$heading});
     	}
     }
    +
     # Set this to be the previously printed filename.
             @previous_heading = split (/\//,$heading);
     	$PRINT_ME = 0;
    @@ -983,18 +801,15 @@ close OUTPUT;
     sub DO_HOSTS {
     open (OUTPUT,">> $OutFile") || die "Can't create $OutFile\n";
     print OUTPUT "

    \n" if ($opt_H); -$TITLE = "Connections per Host\n"; -print OUTPUT $TITLE if (!($opt_H)); +print OUTPUT "Connections per Host\n"; print OUTPUT "

    \n" if ($opt_H); -#$TITLE = "Host Access Report"; -$arg = pop(@_); -if ($arg =~ /^summary$/) { - &HYPER_LINK(hosts_sum) if ($opt_H); -} -else { - &HYPER_LINK(hosts) if ($opt_H); -} +if ($opt_H) { +print OUTPUT "
    +Host Access Report
    +See Also: Report Menu / Access Report Summary / File Accesses / Errors
     
    +";
    +}
     $SubTotal=0;
     
     # Print the name of each host and the number of time they connected.
    @@ -1034,27 +849,7 @@ printf OUTPUT ("%-40s%8s\n","Total Number of Connections:",$connections);
     printf OUTPUT ("%-40s%8s\n","Total Number of KiloBytes Retrieved:",$TOTAL_KBYTES);
     printf OUTPUT ("%-40s%8s\n","Total Number of Directories Browsed:",$DIRECTORY_ACCESSES);
     printf OUTPUT ("%-40s%8s\n","Total Number of Nonexistent Files:",$EXIST_ERRORS);
    -printf OUTPUT ("%-40s%8s\n","Total Number of Redirected Accesses:",$REDIRECTS);
     print OUTPUT "\n";
     close OUTPUT;
     chmod 0644,$OutFile;
     }
    -
    -sub HYPER_LINK {
    -$THIS_ENTRY = pop(@_);
    -print OUTPUT " 
    \n";
    -if (!(@_ =~ /summary/)) {
    -print OUTPUT "$TITLE
    -

    $TITLE

    -"; -} -print OUTPUT "See Also: Report Menu / "; -print OUTPUT "Full Report /" if (($opt_a) && ($THIS_ENTRY !~ /long/)); -print OUTPUT "Error Report /" if (($opt_e) && ($THIS_ENTRY !~ /error/)); -print OUTPUT "Summary /" if (($opt_s) && ($THIS_ENTRY !~ /sum/)); -print OUTPUT "Detailed File Accesses /" if (($opt_f) && ($THIS_ENTRY !~ /^file$/)); -print OUTPUT "Daily Accesses /" if (($opt_d) && ($THIS_ENTRY !~ /daily/)); -print OUTPUT "Hourly Accesses /" if (($opt_t)&& ($THIS_ENTRY !~ /hourly/)); -print OUTPUT "Host Accesses" if (($opt_h)&& ($THIS_ENTRY !~ /hosts/)); -print OUTPUT "\n"; -} diff --git a/support/inc2shtml.c b/support/inc2shtml.c new file mode 100644 index 0000000..d40032a --- /dev/null +++ b/support/inc2shtml.c @@ -0,0 +1,102 @@ +/* + * inc2shtml: Convert httpd <1.1 style includes to 1.2 style + * + * Rob McCool + * + * Usage: inc2shtml [filename] + * + * If filename is given, this program will open filename. If not, it will + * look on stdin. It will output the new shtml file on stdout. + */ + + +#include + +#define MAX_STRING_LEN 256 + +void usage(char *argv0) { + fprintf(stderr,"Usage: %s [filename]\n",argv0); + fprintf(stderr,"If filename is given, this program will open filename.\n"); + fprintf(stderr,"If not, it will look on stdin for the inc file.\n"); + fprintf(stderr, + "In either case, it will write the new shtml file on stdout.\n"); + exit(1); +} + +void translate_tag(char *tag, FILE *fd) { + char *tp = tag, *tp2; + int url; + + url = (*tp == 'U' || *tp == 'u' ? 1 : 0); + + while(*tp++ != '\"'); + tp2 = tp + 1; + while(*tp2 != '\"') ++tp2; + *tp2 = '\0'; + if(*tp == '|') { + fprintf(fd,"",fd); + } else + fprintf(fd,"",tp); +} + +main(int argc, char *argv[]) { + FILE *f; + int c,x,p; + char c2; + char *lookfor = "