diff options
author | Thomas E. Dickey <dickey@invisible-island.net> | 1996-09-02 19:39:24 -0400 |
---|---|---|
committer | Thomas E. Dickey <dickey@invisible-island.net> | 1996-09-02 19:39:24 -0400 |
commit | e087f6d44e87f489fcb3056e86319ebba4218156 (patch) | |
tree | d045b58011bfbbf5186d34c4fed9e0dedb363275 /WWW | |
download | lynx-snapshots-e087f6d44e87f489fcb3056e86319ebba4218156.tar.gz |
snapshot of project "lynx", label v2_6
Diffstat (limited to 'WWW')
140 files changed, 51667 insertions, 0 deletions
diff --git a/WWW/BUILD b/WWW/BUILD new file mode 100644 index 00000000..5fda9e0e --- /dev/null +++ b/WWW/BUILD @@ -0,0 +1,42 @@ +#! /bin/csh +# Build all WWW Code for this platform +# +# Figure out what sort of unix this is +# (NeXT machines don't have uname!) + +set UNAME=NeXT +if (-e /usr/bin/uname) set UNAME=`/usr/bin/uname` +if (-e /bin/uname) set UNAME=`/bin/uname` +if (-e /usr/apollo/bin) set UNAME=`ver sys5.3 /bin/uname` +if ( $UNAME == "" ) then + if (-r /NextApps ) set UNAME=next +endif +# +setenv UNAME $UNAME +# For apollo, must use bsd mode. Also, WWW_MACH not inherited through make! +if ($UNAME == "DomainOS") setenv WWW_MACH apollo_m68k +if ($UNAME == next) setenv WWW_MACH next +if ($UNAME == "HP-UX") setenv WWW_MACH snake +if ($UNAME == "IRIX") setenv WWW_MACH sgi +if ($UNAME == "SunOS") setenv WWW_MACH sun4 +if ($UNAME == "ULTRIX") setenv WWW_MACH decstation +if ($UNAME == "AIX") setenv WWW_MACH rs6000 +if ($UNAME == "OSF1") setenv WWW_MACH osf1 + +if ($WWW_MACH == "") then + echo "Please edit BUILD file to include your machine OS" + echo "and mail differences back to www-bug@info.cern.ch + exit -99 +endif +echo "________________________________________________________________" +echo "WWW build for machine type: " $WWW_MACH + +# Now go do build + +# We don't want SHELL set to something funny to screw up make + +(cd All/Implementation; unsetenv SHELL; make) +set stat = $status +echo +echo "WWW build for " $WWW_MACH " done. status = " $stat +exit $stat diff --git a/WWW/Copyright.txt b/WWW/Copyright.txt new file mode 100644 index 00000000..3d7397bb --- /dev/null +++ b/WWW/Copyright.txt @@ -0,0 +1,22 @@ + Copyright -- /hypertext + COPYRIGHT CERN 1990-1993 + + Except where specifically placed in the public domain, the information (of + all forms) in these directories is the intellectual property of the European + Laboratory for Particle Physics (known as CERN). No guarantee whatsoever is + provided by CERN. No liability whatsoever is accepted for any loss or damage + of any kind resulting from any defect or inaccuracy in this information or + code. + + The conditions for public domain and other access to the code are defined in + distribution conditions of WWW code[1] + + Tim Berners-Lee[2] + + CERN + + 1211 Geneva 23, Switzerland + + Tel +41(22)767 3755, Fax +41(22)767 7155, Email: tbl@cernvax.cern.ch + + diff --git a/WWW/Library/Implementation/BSDI_Makefile b/WWW/Library/Implementation/BSDI_Makefile new file mode 100644 index 00000000..65653ca3 --- /dev/null +++ b/WWW/Library/Implementation/BSDI_Makefile @@ -0,0 +1,413 @@ +# Make WWW under svr4 +# + +# For W3 distribution, machine type for subdirectories +WWW_MACH = svr4 + +# The ASIS repository's name for the machine we are on +ASIS_MACH = generic/svr4 + + +CFLAGS = -O -DDEBUG -DUSE_DIRENT -DSVR4 -DNO_FILIO_H +LFLAGS = +CC = cc + +# Directory for installed binary: +BINDIR = /usr/local/bin + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +# Where is the W3 object library? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +# Common Makefile for W3 Library Code +# ----------------------------------- +# +# (c) CERN 1990, 1991 -- see Copyright.html for conditions +# +# This file should be invariant between systems. +# DEPENDENCIES NOT COMPLETE @@ +# +# make Compile and link the software (private version) +# make install Copy it into the system (implies make) +# make update Copy installed version into installed version +# make uninstall Unlink installed version from the system +# make clean Remove intermediate files +# make cleanall Remove intremediate files and products +# +# Macros required to be defined already for make: +# +# CC The C compiler +# CFLAGS Flags for $(CC) -- except the -I which are below +# LFLAGS Flags for ld +# LYFLAGS Flags for Lynx +# +# WWW The WWW source tree directory +# +# Macros needed for make install: +# +# LIBDIR Directory for installed library +#______________________________________________________________________ + +# If this env var is set to something else Some makes will use that instead +SHELL = /bin/sh + +# .h files are distributed but originally are made from the +# self-documenting hypertext files. + +.SUFFIXES: .h .html +.html.h: +# - chmod +w $*.h + www -w90 -na -to text/x-c $*.html > $*.h +# chmod -w $*.h + +# If this is actually run in a subdirectory, +# +# WWW = ../../.. +# WWW = ../.. For [cernlib] build in this directory + +WC = $(WWW)/Library +CMN = $(WWW)/Library/Implementation/ +VMS = $(CMN)vms +# Where shall we put the objects and built library? + +LOB = $(WTMP)/Library/$(WWW_MACH) + +# Only needed if HTWAIS.c is to be compiled. Put into your Makefile.include +# uncomment these and fill in WAISINC for adding direct wais access +# to Lynx. +#HTWAIS = $(LOB)/HTWAIS.o +#WAIS = YES +#WAISINC = -I../../../../freeWAIS-0.202/ir +#WAISCFLAGS = -DDIRECT_WAIS +# + +# This path, if relative, is taken relative to the directory +# in which this makefile is, not the pwd. This screws up the +# recursive invocation +# include $(CMN)Version.make +VC = 2.14 + +# XMOsAIC hack is only for server to cope with xmosaic kludge for mmedia +# +# add -DNEW_GATEWAY here for the new gateway config stuff +CFLAGS2 = $(CFLAGS) $(LYFLAGS) $(WAISCFLAGS) -I$(CMN) -DXMOSAIC_HACK -DACCESS_AUTH + +CERNLIBBIN = $(WWW)/bin + +COMMON = $(LOB)/HTParse.o $(LOB)/HTAccess.o $(LOB)/HTTP.o \ + $(LOB)/HTFile.o $(LOB)/HTBTree.o $(LOB)/HTFTP.o $(LOB)/HTTCP.o \ + $(LOB)/SGML.o $(LOB)/HTMLDTD.o $(LOB)/HTChunk.o \ + $(LOB)/HTPlain.o $(LOB)/HTWriter.o $(LOB)/HTFWriter.o \ + $(LOB)/HTMLGen.o \ + $(LOB)/HTAtom.o $(LOB)/HTAnchor.o $(LOB)/HTStyle.o \ + $(LOB)/HTList.o $(LOB)/HTString.o $(LOB)/HTAlert.o \ + $(LOB)/HTRules.o $(LOB)/HTFormat.o $(LOB)/HTInit.o $(LOB)/HTMIME.o \ + $(LOB)/HTHistory.o $(LOB)/HTNews.o $(LOB)/HTGopher.o \ + $(LOB)/HTTelnet.o $(LOB)/HTFinger.o $(LOB)/HTWSRC.o $(HTWAIS) \ + $(LOB)/HTAAUtil.o $(LOB)/HTAAServ.o $(LOB)/HTAABrow.o \ + $(LOB)/HTAAFile.o $(LOB)/HTPasswd.o $(LOB)/HTGroup.o \ + $(LOB)/HTACL.o $(LOB)/HTAuth.o $(LOB)/HTAAProt.o \ + $(LOB)/HTAssoc.o $(LOB)/HTLex.o $(LOB)/HTUU.o + +CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTFile.c \ + $(CMN)HTBTree.c \ + $(CMN)HTFTP.c $(CMN)HTTCP.c $(CMN)SGML.c \ + $(CMN)HTMLDTD.c \ + $(CMN)HTPlain.c $(CMN)HTWriter.c $(CMN)HTFWriter.c \ + $(CMN)HTMLGen.c \ + $(CMN)HTChunk.c $(CMN)HTAtom.c $(CMN)HTAnchor.c $(CMN)HTStyle.c \ + $(CMN)HTList.c $(CMN)HTString.c $(CMN)HTAlert.c $(CMN)HTRules.c \ + $(CMN)HTFormat.c $(CMN)HTInit.c $(CMN)HTMIME.c $(CMN)HTHistory.c \ + $(CMN)HTNews.c $(CMN)HTGopher.c $(CMN)HTTelnet.c \ + $(CMN)HTFinger.c $(CMN)HTWAIS.c $(CMN)HTWSRC.c \ + $(CMN)HTAAUtil.c $(CMN)HTAAServ.c $(CMN)HTAABrow.c \ + $(CMN)HTAAFile.c $(CMN)HTPasswd.c $(CMN)HTGroup.c \ + $(CMN)HTACL.c $(CMN)HTAuth.c $(CMN)HTAAProt.c \ + $(CMN)HTAssoc.c $(CMN)HTLex.c $(CMN)HTUU.c + +HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTFile.h \ + $(CMN)HTBTree.h $(CMN)HTFTP.h $(CMN)HTTCP.h \ + $(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \ + $(CMN)HTPlain.h $(CMN)HTWriter.h \ + $(CMN)HTFWriter.h $(CMN)HTMLGen.h \ + $(CMN)HTStream.h \ + $(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \ + $(CMN)HTList.h \ + $(CMN)HTString.h $(CMN)HTAlert.h $(CMN)HTRules.h \ + $(CMN)HTFormat.h $(CMN)HTInit.h \ + $(CMN)HTMIME.h $(CMN)HTHistory.h $(CMN)HTNews.h \ + $(CMN)HTGopher.h \ + $(CMN)HTUtils.h $(CMN)tcp.h $(CMN)HText.h \ + $(CMN)HTTelnet.h $(CMN)HTFinger.h \ + $(CMN)HTWAIS.h $(CMN)HTWSRC.h \ + $(CMN)HTAAUtil.h $(CMN)HTAAServ.h $(CMN)HTAABrow.h \ + $(CMN)HTAAFile.h $(CMN)HTPasswd.h $(CMN)HTGroup.h \ + $(CMN)HTACL.h $(CMN)HTAuth.h $(CMN)HTAAProt.h \ + $(CMN)HTAssoc.h $(CMN)HTLex.h $(CMN)HTUU.h + +SOURCES = $(CFILES) $(HFILES) $(CMN)Version.make \ + $(CMN)CommonMakefile $(CMN)Makefile \ + $(WWW)/README.txt $(WWW)/Copyright.txt $(WWW)/BUILD $(WWW)/Makefile +SPECIFIC = $(WWW)/All/*/Makefile.include $(WWW)/All/Implementation/Makefile* \ + $(VMS)/descrip.mms $(VMS)/build_multinet.com \ + $(VMS)/COPYING.LIB $(VMS)/setup.com $(VMS)/multinet.opt \ + $(VMS)/patchlevel.h $(VMS)/ufc-crypt.h \ + $(VMS)/crypt.c $(VMS)/crypt_util.c \ + $(VMS)/getline.c $(VMS)/getpass.c \ + $(VMS)/HTVMSUtils.h $(VMS)/HTVMSUtils.c + + +# Library +# +# On SGI, ranlib is unnecessary and does not exist so we ignore errors +# for that step +$(LOB)/libwww.a : $(COMMON) + ar r $(LOB)/libwww.a $(COMMON) + -ranlib $(LOB)/libwww.a + +# Clean up everything generatable except final products +clean : + rm $(LOB)/*.o $(LOB)/.created + -rmdir $(LOB) + +# Clean up everything generatable including final products + +cleanall : clean + rm $(LOB)/libwww.a + +# Install W3 library into system space (not normally necessary) + +install : libwww.a + if [ ! -r $(LIBDIR) ] mkdir $(LIBDIR) + cp libwww.a $(LIBDIR)/libwww.a + +uninstall : + rm $(LIBDIR)/libwww.a + +# Distribution use only: +# ---------------------- + +# Needs www version 2.4 or later to do this +inc : $(HFILES) + echo Include files generated from hypertext. + +binary : /pub/www/bin/$(WWW_MACH)/libwww_$(VC).a + echo FTP archive binary Libray $(VC) for $(WWW_MACH) up to date. + + +/pub/www/bin/$(WWW_MACH)/libwww_$(VC).a : libwww.a + -mkdir /pub/www/bin/$(WWW_MACH) + cp libwww.a /pub/www/bin/$(WWW_MACH)/libwww_$(VC).a + +# Source Distribution: + +distribute : /pub/www/README.txt /pub/www/Copyright.txt + (cd $(WWW)/..; WWW=WWW ABS=`pwd`/ make $(MFLAGS) \ + -f WWW/Library/Implementation/CommonMakefile \ + /pub/www/src/WWWLibrary_$(VC).tar.Z) + (cd ../Implementation; cvs tag \ + `sed -e 's/VC = /v/' Version.make | sed -e 's?\.?/?'` ) + echo Distribution of Library version $(VC) up to date. + +/pub/www/src/WWWLibrary_$(VC).tar.Z : $(SOURCES) + tar cf /pub/www/src/WWWLibrary_$(VC).tar \ + $(SOURCES) $(SPECIFIC) $(WC)/*/Makefile + compress /pub/www/src/WWWLibrary_$(VC).tar + + +# Hypertext supplied in text format +# --------------------------------- + +$(WWW)/README.txt : $(WWW)/../README.html + www -n -p66 http://www.w3.org/hypertext/README.html \ + > $(WWW)/README.txt +/pub/www/README.txt : $(WWW)/README.txt + cp $(WWW)/README.txt /pub/www/README.txt + +$(WWW)/Copyright.txt : $(WWW)/../Copyright.html + www -n -p66 http://www.w3.org/hypertext/Copyright.html \ + > $(WWW)/Copyright.txt +/pub/www/Copyright.txt : $(WWW)/Copyright.txt + cp $(WWW)/Copyright.txt /pub/www/Copyright.txt + +# Common code +# ----------- + +# Directory for object files - .created checks it exists + +OE = $(LOB)/.created +$(OE) : + if [ ! -r $(WTMP) ] ; then mkdir $(WTMP); else echo OK ; fi + if [ ! -r $(WTMP)/Library ] ; then mkdir $(WTMP)/Library; else echo OK ; fi + if [ ! -r $(WTMP)/Library/$(WWW_MACH) ] ; \ + then mkdir $(WTMP)/Library/$(WWW_MACH); else echo OK ; fi + touch $@ + +$(LOB)/HTList.o : $(OE) $(CMN)HTList.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTList.c + +$(LOB)/HTAnchor.o : $(OE) $(CMN)HTAnchor.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAnchor.c + +$(LOB)/HTFormat.o : $(OE) $(CMN)HTFormat.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFormat.c + +$(LOB)/HTInit.o : $(OE) $(CMN)HTInit.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTInit.c + +$(LOB)/HTMIME.o : $(OE) $(CMN)HTMIME.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMIME.c + +$(LOB)/HTHistory.o : $(OE) $(CMN)HTHistory.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTHistory.c + +$(LOB)/HTNews.o : $(OE) $(CMN)HTNews.c $(CMN)HTUtils.h $(CMN)HTList.h\ + $(CMN)HTMLDTD.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTNews.c + +$(LOB)/HTGopher.o : $(OE) $(CMN)HTGopher.c $(CMN)HTUtils.h $(CMN)HTList.h \ + $(CMN)HTMLDTD.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGopher.c + +$(LOB)/HTTelnet.o : $(OE) $(CMN)HTTelnet.c $(CMN)HTUtils.h $(CMN)HTTelnet.h $(CMN)../../../userdefs.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTelnet.c + +$(LOB)/HTFinger.o : $(OE) $(CMN)HTFinger.c $(CMN)HTUtils.h $(CMN)HTList.h \ + $(CMN)HTMLDTD.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFinger.c + +$(LOB)/HTStyle.o : $(OE) $(CMN)HTStyle.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTStyle.c + +$(LOB)/HTAtom.o : $(OE) $(CMN)HTAtom.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAtom.c + +$(LOB)/HTChunk.o : $(OE) $(CMN)HTChunk.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTChunk.c + +$(LOB)/HTString.o : $(OE) $(CMN)HTString.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTString.c + +$(LOB)/HTAlert.o : $(OE) $(CMN)HTAlert.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTAlert.c + +$(LOB)/HTRules.o : $(OE) $(CMN)HTRules.c $(CMN)HTUtils.h $(CMN)Version.make \ + $(CMN)HTAAServ.h $(CMN)HTAAProt.h + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTRules.c + +$(LOB)/SGML.o : $(OE) $(CMN)SGML.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)SGML.c + +$(LOB)/HTMLGen.o : $(OE) $(CMN)HTMLGen.c $(CMN)HTUtils.h $(CMN)HTMLDTD.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLGen.c + +$(LOB)/HTMLDTD.o : $(OE) $(CMN)HTMLDTD.c $(CMN)SGML.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLDTD.c + +$(LOB)/HTPlain.o : $(OE) $(CMN)HTPlain.c $(CMN)HTPlain.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPlain.c + +$(LOB)/HTWAIS.o : $(OE) $(CMN)HTWAIS.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(WAISINC) $(CMN)HTWAIS.c + +$(LOB)/HTWSRC.o : $(OE) $(CMN)HTWSRC.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWSRC.c + +$(LOB)/HTWriter.o : $(OE) $(CMN)HTWriter.c $(CMN)HTWriter.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWriter.c + +$(LOB)/HTFWriter.o : $(OE) $(CMN)HTFWriter.c $(CMN)HTFWriter.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFWriter.c + + +# Access Authorization + +$(LOB)/HTAAUtil.o : $(OE) $(CMN)HTAAUtil.c $(CMN)HTAAUtil.h \ + $(CMN)HTUtils.h $(CMN)HTString.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAUtil.c + +$(LOB)/HTAAFile.o : $(OE) $(CMN)HTAAFile.c $(CMN)HTAAFile.h \ + $(CMN)HTAAUtil.h $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAFile.c + +$(LOB)/HTPasswd.o : $(OE) $(CMN)HTPasswd.c $(CMN)HTPasswd.h \ + $(CMN)HTAAUtil.h $(CMN)HTAAFile.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPasswd.c + +$(LOB)/HTGroup.o : $(OE) $(CMN)HTGroup.c $(CMN)HTGroup.h \ + $(CMN)HTAAUtil.h $(CMN)HTAAFile.h \ + $(CMN)HTAssoc.h $(CMN)HTLex.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGroup.c + +$(LOB)/HTACL.o : $(OE) $(CMN)HTACL.c $(CMN)HTACL.h \ + $(CMN)HTAAUtil.h $(CMN)HTAAFile.h $(CMN)HTGroup.h \ + $(CMN)HTAssoc.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTACL.c + +$(LOB)/HTAuth.o : $(OE) $(CMN)HTAuth.c $(CMN)HTAuth.h \ + $(CMN)HTAAUtil.h $(CMN)HTPasswd.h $(CMN)HTAAFile.h \ + $(CMN)HTAssoc.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAuth.c + +$(LOB)/HTAAServ.o : $(OE) $(CMN)HTAAServ.c $(CMN)HTAAServ.h \ + $(CMN)HTAAUtil.h $(CMN)HTAAFile.h $(CMN)HTPasswd.h \ + $(CMN)HTGroup.h $(CMN)HTACL.h $(CMN)HTAuth.h \ + $(CMN)HTUU.h $(CMN)HTParse.h $(CMN)HTList.h \ + $(CMN)HTUtils.h $(CMN)HTString.h $(CMN)HTRules.h \ + $(CMN)HTAAProt.h $(CMN)HTAssoc.h $(CMN)HTLex.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAServ.c + +$(LOB)/HTAABrow.o : $(OE) $(CMN)HTAABrow.c $(CMN)HTAABrow.h \ + $(CMN)HTAAUtil.h $(CMN)HTUU.h \ + $(CMN)HTUtils.h $(CMN)HTString.h \ + $(CMN)HTParse.h $(CMN)HTList.h $(CMN)HTAlert.h \ + $(CMN)HTAssoc.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAABrow.c + +$(LOB)/HTAAProt.o : $(OE) $(CMN)HTAAProt.c $(CMN)HTAAProt.h \ + $(CMN)HTUtils.h $(CMN)HTAAUtil.h $(CMN)HTAAFile.h \ + $(CMN)HTAssoc.h $(CMN)HTLex.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAProt.c + +$(LOB)/HTAssoc.o : $(OE) $(CMN)HTAssoc.c $(CMN)HTAssoc.h \ + $(CMN)HTUtils.h $(CMN)HTString.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAssoc.c + +$(LOB)/HTLex.o : $(OE) $(CMN)HTLex.c $(CMN)HTLex.h $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTLex.c + +$(LOB)/HTUU.o : $(OE) $(CMN)HTUU.c $(CMN)HTUU.h $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTUU.c + + +# Communications & Files + +$(LOB)/HTTP.o : $(OE) $(CMN)HTTP.c $(CMN)HTUtils.h $(CMN)HTAABrow.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTP.c + +$(LOB)/HTTCP.o : $(OE) $(CMN)HTTCP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTCP.c + +$(LOB)/HTFile.o : $(OE) $(CMN)HTFile.c $(CMN)HTUtils.h \ + $(CMN)HTMLDTD.h $(CMN)HTAAServ.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFile.c + +$(LOB)/HTBTree.o : $(OE) $(CMN)HTBTree.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTBTree.c + +$(LOB)/HTFTP.o : $(OE) $(CMN)HTFTP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFTP.c + +$(LOB)/HTAccess.o : $(OE) $(CMN)HTAccess.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAccess.c + +$(LOB)/HTParse.o : $(OE) $(CMN)HTParse.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTParse.c + diff --git a/WWW/Library/Implementation/CommonMakefile b/WWW/Library/Implementation/CommonMakefile new file mode 100644 index 00000000..bd32f131 --- /dev/null +++ b/WWW/Library/Implementation/CommonMakefile @@ -0,0 +1,376 @@ +# Common Makefile for W3 Library Code +# ----------------------------------- +# +# (c) CERN 1990, 1991 -- see Copyright.html for conditions +# +# This file should be invariant between systems. +# DEPENDENCIES NOT COMPLETE @@ +# +# make Compile and link the software (private version) +# make install Copy it into the system (implies make) +# make update Copy installed version into installed version +# make uninstall Unlink installed version from the system +# make clean Remove intermediate files +# make cleanall Remove intremediate files and products +# +# Macros required to be defined already for make: +# +# CC The C compiler +# CFLAGS Flags for $(CC) -- except the -I which are below +# LFLAGS Flags for ld +# LYFLAGS Flags for Lynx +# +# WWW The WWW source tree directory +# +# Macros needed for make install: +# +# LIBDIR Directory for installed library +#______________________________________________________________________ + +# If this env var is set to something else Some makes will use that instead +SHELL = /bin/sh + +# .h files are distributed but originally are made from the +# self-documenting hypertext files. + +.SUFFIXES: .h .html +.html.h: +# - chmod +w $*.h + www -w90 -na -to text/x-c $*.html > $*.h +# chmod -w $*.h + +# If this is actually run in a subdirectory, +# +# WWW = ../../.. +# WWW = ../.. For [cernlib] build in this directory + +WC = $(WWW)/Library +CMN = $(WWW)/Library/Implementation/ +VMS = $(CMN)vms +# Where shall we put the objects and built library? + +LOB = $(WTMP)/Library/$(WWW_MACH) + +# Only needed if HTWAIS.c is to be compiled. Put into your Makefile.include +# uncomment these and fill in WAISINC for adding direct wais access +# to Lynx. +#HTWAIS = $(LOB)/HTWAIS.o +#WAIS = YES +#WAISINC = -I../../../../freeWAIS-0.202/ir +#WAISCFLAGS = -DDIRECT_WAIS +# + +# This path, if relative, is taken relative to the directory +# in which this makefile is, not the pwd. This screws up the +# recursive invocation +# include $(CMN)Version.make +include $(ABS)$(WWW)/Library/Implementation/Version.make + +# XMOsAIC hack is only for server to cope with xmosaic kludge for mmedia +# +# add -DNEW_GATEWAY here for the new gateway config stuff +CFLAGS2 = $(CFLAGS) $(LYFLAGS) $(WAISCFLAGS) -I$(CMN) -DXMOSAIC_HACK -DACCESS_AUTH + +CERNLIBBIN = $(WWW)/bin + +COMMON = $(LOB)/HTParse.o $(LOB)/HTAccess.o $(LOB)/HTTP.o \ + $(LOB)/HTFile.o $(LOB)/HTBTree.o $(LOB)/HTFTP.o $(LOB)/HTTCP.o \ + $(LOB)/SGML.o $(LOB)/HTMLDTD.o $(LOB)/HTChunk.o \ + $(LOB)/HTPlain.o $(LOB)/HTWriter.o \ + $(LOB)/HTMLGen.o \ + $(LOB)/HTAtom.o $(LOB)/HTAnchor.o $(LOB)/HTStyle.o \ + $(LOB)/HTList.o $(LOB)/HTString.o \ + $(LOB)/HTRules.o $(LOB)/HTFormat.o $(LOB)/HTMIME.o \ + $(LOB)/HTHistory.o $(LOB)/HTNews.o $(LOB)/HTGopher.o \ + $(LOB)/HTTelnet.o $(LOB)/HTFinger.o $(LOB)/HTWSRC.o $(HTWAIS) \ + $(LOB)/HTAAUtil.o $(LOB)/HTAAServ.o $(LOB)/HTAABrow.o \ + $(LOB)/HTAAFile.o $(LOB)/HTPasswd.o $(LOB)/HTGroup.o \ + $(LOB)/HTACL.o $(LOB)/HTAuth.o $(LOB)/HTAAProt.o \ + $(LOB)/HTAssoc.o $(LOB)/HTLex.o $(LOB)/HTUU.o + +CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTFile.c \ + $(CMN)HTBTree.c \ + $(CMN)HTFTP.c $(CMN)HTTCP.c $(CMN)SGML.c \ + $(CMN)HTMLDTD.c \ + $(CMN)HTPlain.c $(CMN)HTWriter.c \ + $(CMN)HTMLGen.c \ + $(CMN)HTChunk.c $(CMN)HTAtom.c $(CMN)HTAnchor.c $(CMN)HTStyle.c \ + $(CMN)HTList.c $(CMN)HTString.c $(CMN)HTRules.c \ + $(CMN)HTFormat.c $(CMN)HTMIME.c $(CMN)HTHistory.c \ + $(CMN)HTNews.c $(CMN)HTGopher.c $(CMN)HTTelnet.c \ + $(CMN)HTFinger.c $(CMN)HTWAIS.c $(CMN)HTWSRC.c \ + $(CMN)HTAAUtil.c $(CMN)HTAAServ.c $(CMN)HTAABrow.c \ + $(CMN)HTAAFile.c $(CMN)HTPasswd.c $(CMN)HTGroup.c \ + $(CMN)HTACL.c $(CMN)HTAuth.c $(CMN)HTAAProt.c \ + $(CMN)HTAssoc.c $(CMN)HTLex.c $(CMN)HTUU.c + +HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTFile.h \ + $(CMN)HTBTree.h $(CMN)HTFTP.h $(CMN)HTTCP.h \ + $(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \ + $(CMN)HTPlain.h $(CMN)HTWriter.h \ + $(CMN)HTFWriter.h $(CMN)HTMLGen.h \ + $(CMN)HTStream.h \ + $(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \ + $(CMN)HTList.h \ + $(CMN)HTString.h $(CMN)HTAlert.h $(CMN)HTRules.h \ + $(CMN)HTFormat.h $(CMN)HTInit.h \ + $(CMN)HTMIME.h $(CMN)HTHistory.h $(CMN)HTNews.h \ + $(CMN)HTGopher.h \ + $(CMN)HTUtils.h $(CMN)tcp.h $(CMN)HText.h \ + $(CMN)HTTelnet.h $(CMN)HTFinger.h \ + $(CMN)HTWAIS.h $(CMN)HTWSRC.h \ + $(CMN)HTAAUtil.h $(CMN)HTAAServ.h $(CMN)HTAABrow.h \ + $(CMN)HTAAFile.h $(CMN)HTPasswd.h $(CMN)HTGroup.h \ + $(CMN)HTACL.h $(CMN)HTAuth.h $(CMN)HTAAProt.h \ + $(CMN)HTAssoc.h $(CMN)HTLex.h $(CMN)HTUU.h + +SOURCES = $(CFILES) $(HFILES) $(CMN)Version.make \ + $(CMN)CommonMakefile $(CMN)Makefile \ + $(WWW)/README.txt $(WWW)/Copyright.txt $(WWW)/BUILD $(WWW)/Makefile +SPECIFIC = $(WWW)/All/*/Makefile.include $(WWW)/All/Implementation/Makefile* \ + $(VMS)/descrip.mms $(VMS)/build_multinet.com \ + $(VMS)/COPYING.LIB $(VMS)/setup.com $(VMS)/multinet.opt \ + $(VMS)/patchlevel.h $(VMS)/ufc-crypt.h \ + $(VMS)/crypt.c $(VMS)/crypt_util.c \ + $(VMS)/getline.c $(VMS)/getpass.c \ + $(VMS)/HTVMSUtils.h $(VMS)/HTVMSUtils.c + + +# Library +# +# On SGI, ranlib is unnecessary and does not exist so we ignore errors +# for that step +$(LOB)/libwww.a : $(COMMON) + ar r $(LOB)/libwww.a $(COMMON) + -ranlib $(LOB)/libwww.a + +# Clean up everything generatable except final products +clean : + rm $(LOB)/*.o $(LOB)/.created + -rmdir $(LOB) + +# Clean up everything generatable including final products + +cleanall : clean + rm $(LOB)/libwww.a + +# Install W3 library into system space (not normally necessary) + +install : libwww.a + if [ ! -r $(LIBDIR) ] mkdir $(LIBDIR) + cp libwww.a $(LIBDIR)/libwww.a + +uninstall : + rm $(LIBDIR)/libwww.a + +# Distribution use only: +# ---------------------- + +# Needs www version 2.4 or later to do this +inc : $(HFILES) + echo Include files generated from hypertext. + +binary : /pub/www/bin/$(WWW_MACH)/libwww_$(VC).a + echo FTP archive binary Libray $(VC) for $(WWW_MACH) up to date. + + +/pub/www/bin/$(WWW_MACH)/libwww_$(VC).a : libwww.a + -mkdir /pub/www/bin/$(WWW_MACH) + cp libwww.a /pub/www/bin/$(WWW_MACH)/libwww_$(VC).a + +# Source Distribution: + +distribute : /pub/www/README.txt /pub/www/Copyright.txt + (cd $(WWW)/..; WWW=WWW ABS=`pwd`/ make $(MFLAGS) \ + -f WWW/Library/Implementation/CommonMakefile \ + /pub/www/src/WWWLibrary_$(VC).tar.Z) + (cd ../Implementation; cvs tag \ + `sed -e 's/VC = /v/' Version.make | sed -e 's?\.?/?'` ) + echo Distribution of Library version $(VC) up to date. + +/pub/www/src/WWWLibrary_$(VC).tar.Z : $(SOURCES) + tar cf /pub/www/src/WWWLibrary_$(VC).tar \ + $(SOURCES) $(SPECIFIC) $(WC)/*/Makefile + compress /pub/www/src/WWWLibrary_$(VC).tar + + +# Hypertext supplied in text format +# --------------------------------- + +$(WWW)/README.txt : $(WWW)/../README.html + www -n -p66 http://www.w3.org/hypertext/README.html \ + > $(WWW)/README.txt +/pub/www/README.txt : $(WWW)/README.txt + cp $(WWW)/README.txt /pub/www/README.txt + +$(WWW)/Copyright.txt : $(WWW)/../Copyright.html + www -n -p66 http://www.w3.org/hypertext/Copyright.html \ + > $(WWW)/Copyright.txt +/pub/www/Copyright.txt : $(WWW)/Copyright.txt + cp $(WWW)/Copyright.txt /pub/www/Copyright.txt + +# Common code +# ----------- + +# Directory for object files - .created checks it exists + +OE = $(LOB)/.created +$(OE) : + if [ ! -r $(WTMP) ] ; then mkdir $(WTMP); else echo OK ; fi + if [ ! -r $(WTMP)/Library ] ; then mkdir $(WTMP)/Library; else echo OK ; fi + if [ ! -r $(WTMP)/Library/$(WWW_MACH) ] ; \ + then mkdir $(WTMP)/Library/$(WWW_MACH); else echo OK ; fi + touch $@ + +$(LOB)/HTList.o : $(OE) $(CMN)HTList.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTList.c + +$(LOB)/HTAnchor.o : $(OE) $(CMN)HTAnchor.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAnchor.c + +$(LOB)/HTFormat.o : $(OE) $(CMN)HTFormat.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFormat.c + +$(LOB)/HTMIME.o : $(OE) $(CMN)HTMIME.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMIME.c + +$(LOB)/HTHistory.o : $(OE) $(CMN)HTHistory.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTHistory.c + +$(LOB)/HTNews.o : $(OE) $(CMN)HTNews.c $(CMN)HTUtils.h $(CMN)HTList.h\ + $(CMN)HTMLDTD.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTNews.c + +$(LOB)/HTGopher.o : $(OE) $(CMN)HTGopher.c $(CMN)HTUtils.h $(CMN)HTList.h \ + $(CMN)HTMLDTD.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGopher.c + +$(LOB)/HTTelnet.o : $(OE) $(CMN)HTTelnet.c $(CMN)HTUtils.h $(CMN)HTTelnet.h $(CMN)../../../userdefs.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTelnet.c + +$(LOB)/HTFinger.o : $(OE) $(CMN)HTFinger.c $(CMN)HTUtils.h $(CMN)HTList.h \ + $(CMN)HTMLDTD.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFinger.c + +$(LOB)/HTStyle.o : $(OE) $(CMN)HTStyle.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTStyle.c + +$(LOB)/HTAtom.o : $(OE) $(CMN)HTAtom.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAtom.c + +$(LOB)/HTChunk.o : $(OE) $(CMN)HTChunk.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTChunk.c + +$(LOB)/HTString.o : $(OE) $(CMN)HTString.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTString.c + +$(LOB)/HTRules.o : $(OE) $(CMN)HTRules.c $(CMN)HTUtils.h $(CMN)Version.make \ + $(CMN)HTAAServ.h $(CMN)HTAAProt.h + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTRules.c + +$(LOB)/SGML.o : $(OE) $(CMN)SGML.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)SGML.c + +$(LOB)/HTMLGen.o : $(OE) $(CMN)HTMLGen.c $(CMN)HTUtils.h $(CMN)HTMLDTD.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLGen.c + +$(LOB)/HTMLDTD.o : $(OE) $(CMN)HTMLDTD.c $(CMN)SGML.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLDTD.c + +$(LOB)/HTPlain.o : $(OE) $(CMN)HTPlain.c $(CMN)HTPlain.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPlain.c + +$(LOB)/HTWAIS.o : $(OE) $(CMN)HTWAIS.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(WAISINC) $(CMN)HTWAIS.c + +$(LOB)/HTWSRC.o : $(OE) $(CMN)HTWSRC.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWSRC.c + +$(LOB)/HTWriter.o : $(OE) $(CMN)HTWriter.c $(CMN)HTWriter.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWriter.c + + +# Access Authorization + +$(LOB)/HTAAUtil.o : $(OE) $(CMN)HTAAUtil.c $(CMN)HTAAUtil.h \ + $(CMN)HTUtils.h $(CMN)HTString.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAUtil.c + +$(LOB)/HTAAFile.o : $(OE) $(CMN)HTAAFile.c $(CMN)HTAAFile.h \ + $(CMN)HTAAUtil.h $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAFile.c + +$(LOB)/HTPasswd.o : $(OE) $(CMN)HTPasswd.c $(CMN)HTPasswd.h \ + $(CMN)HTAAUtil.h $(CMN)HTAAFile.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPasswd.c + +$(LOB)/HTGroup.o : $(OE) $(CMN)HTGroup.c $(CMN)HTGroup.h \ + $(CMN)HTAAUtil.h $(CMN)HTAAFile.h \ + $(CMN)HTAssoc.h $(CMN)HTLex.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGroup.c + +$(LOB)/HTACL.o : $(OE) $(CMN)HTACL.c $(CMN)HTACL.h \ + $(CMN)HTAAUtil.h $(CMN)HTAAFile.h $(CMN)HTGroup.h \ + $(CMN)HTAssoc.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTACL.c + +$(LOB)/HTAuth.o : $(OE) $(CMN)HTAuth.c $(CMN)HTAuth.h \ + $(CMN)HTAAUtil.h $(CMN)HTPasswd.h $(CMN)HTAAFile.h \ + $(CMN)HTAssoc.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAuth.c + +$(LOB)/HTAAServ.o : $(OE) $(CMN)HTAAServ.c $(CMN)HTAAServ.h \ + $(CMN)HTAAUtil.h $(CMN)HTAAFile.h $(CMN)HTPasswd.h \ + $(CMN)HTGroup.h $(CMN)HTACL.h $(CMN)HTAuth.h \ + $(CMN)HTUU.h $(CMN)HTParse.h $(CMN)HTList.h \ + $(CMN)HTUtils.h $(CMN)HTString.h $(CMN)HTRules.h \ + $(CMN)HTAAProt.h $(CMN)HTAssoc.h $(CMN)HTLex.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAServ.c + +$(LOB)/HTAABrow.o : $(OE) $(CMN)HTAABrow.c $(CMN)HTAABrow.h \ + $(CMN)HTAAUtil.h $(CMN)HTUU.h \ + $(CMN)HTUtils.h $(CMN)HTString.h \ + $(CMN)HTParse.h $(CMN)HTList.h $(CMN)HTAlert.h \ + $(CMN)HTAssoc.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAABrow.c + +$(LOB)/HTAAProt.o : $(OE) $(CMN)HTAAProt.c $(CMN)HTAAProt.h \ + $(CMN)HTUtils.h $(CMN)HTAAUtil.h $(CMN)HTAAFile.h \ + $(CMN)HTAssoc.h $(CMN)HTLex.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAProt.c + +$(LOB)/HTAssoc.o : $(OE) $(CMN)HTAssoc.c $(CMN)HTAssoc.h \ + $(CMN)HTUtils.h $(CMN)HTString.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAssoc.c + +$(LOB)/HTLex.o : $(OE) $(CMN)HTLex.c $(CMN)HTLex.h $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTLex.c + +$(LOB)/HTUU.o : $(OE) $(CMN)HTUU.c $(CMN)HTUU.h $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTUU.c + + +# Communications & Files + +$(LOB)/HTTP.o : $(OE) $(CMN)HTTP.c $(CMN)HTUtils.h $(CMN)HTAABrow.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTP.c + +$(LOB)/HTTCP.o : $(OE) $(CMN)HTTCP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTCP.c + +$(LOB)/HTFile.o : $(OE) $(CMN)HTFile.c $(CMN)HTUtils.h \ + $(CMN)HTMLDTD.h $(CMN)HTAAServ.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFile.c + +$(LOB)/HTBTree.o : $(OE) $(CMN)HTBTree.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTBTree.c + +$(LOB)/HTFTP.o : $(OE) $(CMN)HTFTP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFTP.c + +$(LOB)/HTAccess.o : $(OE) $(CMN)HTAccess.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAccess.c + +$(LOB)/HTParse.o : $(OE) $(CMN)HTParse.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTParse.c + diff --git a/WWW/Library/Implementation/HTAABrow.c b/WWW/Library/Implementation/HTAABrow.c new file mode 100644 index 00000000..caf1cbd8 --- /dev/null +++ b/WWW/Library/Implementation/HTAABrow.c @@ -0,0 +1,1051 @@ + +/* MODULE HTAABrow.c +** BROWSER SIDE ACCESS AUTHORIZATION MODULE +** +** Containts the code for keeping track on server hostnames, +** port numbers, scheme names, usernames, passwords +** (and servers' public keys). +** +** IMPORTANT: +** Routines in this module use dynamic allocation, but free +** automatically all the memory reserved by them. +** +** Therefore the caller never has to (and never should) +** free() any object returned by these functions. +** +** Therefore also all the strings returned by this package +** are only valid until the next call to the same function +** is made. This approach is selected, because of the nature +** of access authorization: no string returned by the package +** needs to be valid longer than until the next call. +** +** This also makes it easy to plug the AA package in: +** you don't have to ponder whether to free() something +** here or is it done somewhere else (because it is always +** done somewhere else). +** +** The strings that the package needs to store are copied +** so the original strings given as parameters to AA +** functions may be freed or modified with no side effects. +** +** The AA package does not free() anything else than what +** it has itself allocated. +** +** AUTHORS: +** AL Ari Luotonen luotonen@dxcern.cern.ch +** +** HISTORY: +** Oct 17 AL Made corrections suggested by marca: +** Added if (!realm->username) return NULL; +** Changed some ""s to NULLs. +** Now doing calloc() to init uuencode source; +** otherwise HTUU_encode() reads uninitialized memory +** every now and then (not a real bug but not pretty). +** Corrected the formula for uuencode destination size. +** BUGS: +** +** +*/ + +#include "HTUtils.h" +#include <string.h> /* strchr() */ +#include "HTString.h" +#include "HTParse.h" /* URL parsing function */ +#include "HTList.h" /* HTList object */ +#include "HTAlert.h" /* HTConfirm(), HTPrompt() */ +#include "HTAAUtil.h" /* AA common to both sides */ +#include "HTAssoc.h" /* Assoc list */ +#include "HTAABrow.h" /* Implemented here */ +#include "HTUU.h" /* Uuencoding and uudecoding */ + +#include "LYLeaks.h" + +extern BOOL using_proxy; /* Are we using an HTTP gateway? */ + +/* +** Local datatype definitions +** +** HTAAServer contains all the information about one server. +*/ +typedef struct { + + char * hostname; /* Host's name */ + int portnumber; /* Port number */ + HTList * setups; /* List of protection setups */ + /* on this server; i.e. valid */ + /* authentication schemes and */ + /* templates when to use them. */ + /* This is actually a list of */ + /* HTAASetup objects. */ + HTList * realms; /* Information about passwords */ +} HTAAServer; + +/* +** HTAASetup contains information about one server's one +** protected tree of documents. +*/ +typedef struct { + HTAAServer *server; /* Which server serves this tree */ + char * template; /* Template for this tree */ + HTList * valid_schemes; /* Valid authentic.schemes */ + HTAssocList**scheme_specifics;/* Scheme specific params */ + BOOL retry; /* Failed last time -- reprompt (or whatever)*/ +} HTAASetup; + +/* +** Information about usernames and passwords in +** Basic and Pubkey authentication schemes; +*/ +typedef struct { + char * realmname; /* Password domain name */ + char * username; /* Username in that domain */ + char * password; /* Corresponding password */ +} HTAARealm; + +/* +** To free off all globals. - FM +*/ +PRIVATE void free_HTAAGlobals NOPARAMS; +PRIVATE BOOL free_HTAAGlobalsSet = FALSE; +PRIVATE char *HTAA_composeAuthResult = NULL; +PRIVATE char *compose_auth_stringResult = NULL; /* Uuencoded presentation */ + +/* +** Module-wide global variables +*/ +PRIVATE HTList *server_table = NULL; /* Browser's info about servers */ +PRIVATE char *secret_key = NULL; /* Browser's latest secret key */ +PRIVATE HTAASetup *current_setup= NULL; /* The server setup we are currently */ + /* talking to */ +PRIVATE char *current_hostname = NULL; /* The server's name and portnumber */ +PRIVATE int current_portnumber = 80; /* where we are currently trying to */ + /* connect. */ +PRIVATE char *current_docname = NULL; /* The document's name we are */ + /* trying to access. */ +PRIVATE char *HTAAForwardAuth = NULL; /* Authorization: line to forward */ + /* (used by gateway httpds) */ + + +/*** HTAAForwardAuth for enabling gateway-httpds to forward Authorization ***/ + +PUBLIC void HTAAForwardAuth_set ARGS2(CONST char *, scheme_name, + CONST char *, scheme_specifics) +{ + int len = 20 + (scheme_name ? strlen(scheme_name) : 0) + + (scheme_specifics ? strlen(scheme_specifics) : 0); + + FREE(HTAAForwardAuth); + if (!(HTAAForwardAuth = (char*)calloc(1, sizeof(char) * len))) + outofmem(__FILE__, "HTAAForwardAuth_set"); + + strcpy(HTAAForwardAuth, "Authorization: "); + if (scheme_name) { + strcat(HTAAForwardAuth, scheme_name); + strcat(HTAAForwardAuth, " "); + if (scheme_specifics) { + strcat(HTAAForwardAuth, scheme_specifics); + } + } +} + +PUBLIC void HTAAForwardAuth_reset NOARGS +{ + FREE(HTAAForwardAuth); +} + + +/**************************** HTAAServer ***********************************/ + +PRIVATE void HTAASetup_delete PARAMS((HTAASetup * killme)); /* Forward */ + +/* PRIVATE HTAAServer_new() +** ALLOCATE A NEW NODE TO HOLD SERVER INFO +** AND ADD IT TO THE LIST OF SERVERS +** ON ENTRY: +** hostname is the name of the host that the server +** is running in. +** portnumber is the portnumber which the server listens. +** +** ON EXIT: +** returns the newly-allocated node with all the strings +** duplicated. +** Strings will be automatically freed by +** the function HTAAServer_delete(), which also +** frees the node itself. +*/ +PRIVATE HTAAServer *HTAAServer_new ARGS2(CONST char*, hostname, + int, portnumber) +{ + HTAAServer *server; + + if (!(server = (HTAAServer *)calloc(1, sizeof(HTAAServer)))) + outofmem(__FILE__, "HTAAServer_new"); + + server->hostname = NULL; + server->portnumber = (portnumber > 0 ? portnumber : 80); + server->setups = HTList_new(); + server->realms = HTList_new(); + + if (hostname) + StrAllocCopy(server->hostname, hostname); + + if (!server_table) + server_table = HTList_new(); + + HTList_addObject(server_table, (void*)server); + + return server; +} + + +/* PRIVATE HTAAServer_delete() +** +** DELETE THE ENTRY FOR THE SERVER FROM THE HOST TABLE, +** AND FREE THE MEMORY USED BY IT. +** +** ON ENTRY: +** killme points to the HTAAServer to be freed. +** +** ON EXIT: +** returns nothing. +*/ +PRIVATE void HTAAServer_delete ARGS1(HTAAServer *, killme) +{ + int n, i; + HTAASetup *setup; + HTAARealm *realm; + HTList *cur; + + if (killme) { + if (killme->setups != NULL) { + n = HTList_count(killme->setups); + for (i = (n - 1); i >= 0; i--) { + if ((setup = (HTAASetup*)HTList_objectAt(killme->setups, + i)) != NULL) { + HTAASetup_delete(setup); + setup = NULL; + } + } + HTList_delete(killme->setups); + killme->setups = NULL; + } + + cur = killme->realms; + while (NULL != (realm = (HTAARealm*)HTList_nextObject(cur))) { + FREE(realm->realmname); + FREE(realm->username); + FREE(realm->password); + FREE(realm); + } + HTList_delete(killme->realms); + killme->realms = NULL; + + FREE(killme->hostname); + + HTList_removeObject(server_table, (void*)killme); + FREE(killme); + } +} + +/* PRIVATE HTAAServer_lookup() +** LOOK UP SERVER BY HOSTNAME AND PORTNUMBER +** ON ENTRY: +** hostname obvious. +** portnumber if non-positive defaults to 80. +** +** Looks up the server in the module-global server_table. +** +** ON EXIT: +** returns pointer to a HTAAServer structure +** representing the looked-up server. +** NULL, if not found. +*/ +PRIVATE HTAAServer *HTAAServer_lookup ARGS2(CONST char *, hostname, + int, portnumber) +{ + if (hostname) { + HTList *cur = server_table; + HTAAServer *server; + + if (portnumber <= 0) + portnumber = 80; + + while (NULL != (server = (HTAAServer*)HTList_nextObject(cur))) { + if (server->portnumber == portnumber && + 0==strcmp(server->hostname, hostname)) + return server; + } + } + return NULL; /* NULL parameter, or not found */ +} + + +/*************************** HTAASetup *******************************/ + +/* PRIVATE HTAASetup_lookup() +** FIGURE OUT WHICH AUTHENTICATION SETUP THE SERVER +** IS USING FOR A GIVEN FILE ON A GIVEN HOST AND PORT +** +** ON ENTRY: +** hostname is the name of the server host machine. +** portnumber is the port that the server is running in. +** docname is the (URL-)pathname of the document we +** are trying to access. +** +** This function goes through the information known about +** all the setups of the server, and finds out if the given +** filename resides in one of the protected directories. +** +** ON EXIT: +** returns NULL if no match. +** Otherwise, a HTAASetup structure representing +** the protected server setup on the corresponding +** document tree. +** +*/ +PRIVATE HTAASetup *HTAASetup_lookup ARGS3(CONST char *, hostname, + int, portnumber, + CONST char *, docname) +{ + HTAAServer *server; + HTAASetup *setup; + + if (portnumber <= 0) + portnumber = 80; + + if (hostname && docname && *hostname && *docname && + NULL != (server = HTAAServer_lookup(hostname, portnumber))) { + + HTList *cur = server->setups; + + if (TRACE) + fprintf(stderr, "%s (%s:%d:%s)\n", + "HTAASetup_lookup: resolving setup for", + hostname, portnumber, docname); + + while (NULL != (setup = (HTAASetup*)HTList_nextObject(cur))) { + if (HTAA_templateMatch(setup->template, docname)) { + if (TRACE) + fprintf(stderr, "%s `%s' %s `%s'\n", + "HTAASetup_lookup:", docname, + "matched template", setup->template); + return setup; + } + else if (TRACE) + fprintf(stderr, "%s `%s' %s `%s'\n", + "HTAASetup_lookup:", docname, + "did NOT match template", setup->template); + } /* while setups remain */ + } /* if valid parameters and server found */ + + if (TRACE) + fprintf(stderr, "%s `%s' %s\n", + "HTAASetup_lookup: No template matched", + (docname ? docname : "(null)"), + "(so probably not protected)"); + + return NULL; /* NULL in parameters, or not found */ +} + +/* PRIVATE HTAASetup_new() +** CREATE A NEW SETUP NODE +** ON ENTRY: +** server is a pointer to a HTAAServer structure +** to which this setup belongs. +** template documents matching this template +** are protected according to this setup. +** valid_schemes a list containing all valid authentication +** schemes for this setup. +** If NULL, all schemes are disallowed. +** scheme_specifics is an array of assoc lists, which +** contain scheme specific parameters given +** by server in Authenticate: fields. +** If NULL, all scheme specifics are +** set to NULL. +** ON EXIT: +** returns a new HTAASetup node, and also adds it as +** part of the HTAAServer given as parameter. +*/ +PRIVATE HTAASetup *HTAASetup_new ARGS4(HTAAServer *, server, + char *, template, + HTList *, valid_schemes, + HTAssocList **, scheme_specifics) +{ + HTAASetup *setup; + + if (!server || !template || !*template) + return NULL; + + if (!(setup = (HTAASetup*)calloc(1, sizeof(HTAASetup)))) + outofmem(__FILE__, "HTAASetup_new"); + + setup->retry = NO; + setup->server = server; + setup->template = NULL; + if (template) + StrAllocCopy(setup->template, template); + setup->valid_schemes = valid_schemes; + setup->scheme_specifics = scheme_specifics; + + HTList_addObject(server->setups, (void*)setup); + + return setup; +} + +/* PRIVATE HTAASetup_delete() +** FREE A HTAASetup STRUCTURE +** ON ENTRY: +** killme is a pointer to the structure to free(). +** +** ON EXIT: +** returns nothing. +*/ +PRIVATE void HTAASetup_delete ARGS1(HTAASetup *, killme) +{ + int scheme; + + if (killme) { + FREE(killme->template); + if (killme->valid_schemes) + HTList_delete(killme->valid_schemes); + killme->valid_schemes = NULL; + for (scheme = 0; scheme < HTAA_MAX_SCHEMES; scheme++) + if (killme->scheme_specifics[scheme]) + HTAssocList_delete(killme->scheme_specifics[scheme]); + FREE(killme->scheme_specifics); + FREE(killme); + } +} + +/* PRIVATE HTAASetup_updateSpecifics() +* COPY SCHEME SPECIFIC PARAMETERS +** TO HTAASetup STRUCTURE +** ON ENTRY: +** setup destination setup structure. +** specifics string array containing scheme +** specific parameters for each scheme. +** If NULL, all the scheme specific +** parameters are set to NULL. +** +** ON EXIT: +** returns nothing. +*/ +PRIVATE void HTAASetup_updateSpecifics ARGS2(HTAASetup *, setup, + HTAssocList **, specifics) +{ + int scheme; + + if (setup) { + if (setup->scheme_specifics) { + for (scheme=0; scheme < HTAA_MAX_SCHEMES; scheme++) { + if (setup->scheme_specifics[scheme]) + HTAssocList_delete(setup->scheme_specifics[scheme]); + } + FREE(setup->scheme_specifics); + } + setup->scheme_specifics = specifics; + } +} + + +/*************************** HTAARealm **********************************/ + +/* PRIVATE HTAARealm_lookup() +** LOOKUP HTAARealm STRUCTURE BY REALM NAME +** ON ENTRY: +** realm_table a list of realm objects. +** realmname is the name of realm to look for. +** +** ON EXIT: +** returns the realm. NULL, if not found. +*/ +PRIVATE HTAARealm *HTAARealm_lookup ARGS2(HTList *, realm_table, + CONST char *, realmname) +{ + if (realm_table && realmname) { + HTList *cur = realm_table; + HTAARealm *realm; + + while (NULL != (realm = (HTAARealm*)HTList_nextObject(cur))) { + if (0==strcmp(realm->realmname, realmname)) + return realm; + } + } + return NULL; /* No table, NULL param, or not found */ +} + +/* PRIVATE HTAARealm_new() +** CREATE A NODE CONTAINING USERNAME AND +** PASSWORD USED FOR THE GIVEN REALM. +** IF REALM ALREADY EXISTS, CHANGE +** USERNAME/PASSWORD. +** ON ENTRY: +** realm_table a list of realms to where to add +** the new one, too. +** realmname is the name of the password domain. +** username and +** password are what you can expect them to be. +** +** ON EXIT: +** returns the created realm. +*/ +PRIVATE HTAARealm *HTAARealm_new ARGS4(HTList *, realm_table, + CONST char *, realmname, + CONST char *, username, + CONST char *, password) +{ + HTAARealm *realm; + + realm = HTAARealm_lookup(realm_table, realmname); + + if (!realm) { + if (!(realm = (HTAARealm*)calloc(1, sizeof(HTAARealm)))) + outofmem(__FILE__, "HTAARealm_new"); + realm->realmname = NULL; + realm->username = NULL; + realm->password = NULL; + StrAllocCopy(realm->realmname, realmname); + if (realm_table) + HTList_addObject(realm_table, (void*)realm); + } + if (username) + StrAllocCopy(realm->username, username); + if (password) + StrAllocCopy(realm->password, password); + + return realm; +} + + +/***************** Basic and Pubkey Authentication ************************/ + +/* PRIVATE compose_auth_string() +** +** COMPOSE Basic OR Pubkey AUTHENTICATION STRING; +** PROMPTS FOR USERNAME AND PASSWORD IF NEEDED +** +** ON ENTRY: +** scheme is either HTAA_BASIC or HTAA_PUBKEY. +** realmname is the password domain name. +** +** ON EXIT: +** returns a newly composed authorization string, +** (with, of course, a newly generated secret +** key and fresh timestamp, if Pubkey-scheme +** is being used). +** NULL, if something fails. +** NOTE: +** Like throughout the entire AA package, no string or structure +** returned by AA package needs to (or should) be freed. +** +*/ +PRIVATE char *compose_auth_string ARGS2(HTAAScheme, scheme, + HTAASetup *, setup) +{ + char *cleartext = NULL; /* Cleartext presentation */ + char *ciphertext = NULL; /* Encrypted presentation */ + int len; + char *msg = NULL; + char *username = NULL; + char *password = NULL; + char *realmname = NULL; + char *theHost = NULL; + char *proxiedHost = NULL; + HTAARealm *realm; + char *inet_addr = "0.0.0.0"; /* Change... @@@@ */ + char *timestamp = "42"; /* ... these @@@@ */ + + + FREE(compose_auth_stringResult); /* From previous call */ + + if ((scheme != HTAA_BASIC && scheme != HTAA_PUBKEY) || !setup || + !setup->scheme_specifics || !setup->scheme_specifics[scheme] || + !setup->server || !setup->server->realms) + return NULL; + + realmname = HTAssocList_lookup(setup->scheme_specifics[scheme], "realm"); + if (!realmname) + return NULL; + + realm = HTAARealm_lookup(setup->server->realms, realmname); + if (!(realm && + realm->username && *realm->username && + realm->password && *realm->password) || setup->retry) { + if (!realm) { + if (TRACE) + fprintf(stderr, "%s `%s' %s\n", + "compose_auth_string: realm:", realmname, + "not found -- creating"); + realm = HTAARealm_new(setup->server->realms, realmname, NULL,NULL); + } + len = strlen(realm->realmname) + + strlen(setup->server->hostname ? + setup->server->hostname : "??") + 40; + if (!(msg = (char*)calloc(1, sizeof(char) * len))) + outofmem(__FILE__, "compose_auth_string"); + if (using_proxy && setup->template) { + proxiedHost = HTParse(setup->template, "", PARSE_HOST); + if (proxiedHost && *proxiedHost != '\0') { + theHost = proxiedHost; + } + } + if (!theHost) + theHost = setup->server->hostname; + sprintf(msg, "Enter username for %s at %s:", + realm->realmname, + theHost ? theHost : "??"); + FREE(proxiedHost); + username = realm->username; + password = NULL; + HTPromptUsernameAndPassword(msg, &username, &password); + + FREE(msg); + FREE(realm->username); + FREE(realm->password); + realm->username = username; + realm->password = password; + + if (!realm->username || !realm->password) { + /* + * Signals to retry. - FM + */ + return NULL; + } else if (*realm->username == '\0' || *realm->password == '\0') { + /* + * Signals to abort. - FM + */ + StrAllocCopy(compose_auth_stringResult, ""); + return compose_auth_stringResult; + } + } + + len = strlen(realm->username ? realm->username : "") + + strlen(realm->password ? realm->password : "") + 3; + + if (scheme == HTAA_PUBKEY) { +#ifdef PUBKEY + /* Generate new secret key */ + StrAllocCopy(secret_key, HTAA_generateRandomKey()); +#endif /* PUBKEY */ + /* Room for secret key, timestamp and inet address */ + len += strlen(secret_key ? secret_key : "") + 30; + } else { + FREE(secret_key); + } + + if (!(cleartext = (char*)calloc(1, sizeof(char) * len))) + outofmem(__FILE__, "compose_auth_string"); + + if (realm->username) + strcpy(cleartext, realm->username); + else + *cleartext = '\0'; + + strcat(cleartext, ":"); + + if (realm->password) + strcat(cleartext, realm->password); + + if (scheme == HTAA_PUBKEY) { + strcat(cleartext, ":"); + strcat(cleartext, inet_addr); + strcat(cleartext, ":"); + strcat(cleartext, timestamp); + strcat(cleartext, ":"); + if (secret_key) + strcat(cleartext, secret_key); + + if (!((ciphertext = (char *)calloc(1, (sizeof(char) * 2) * len)) && + (compose_auth_stringResult = + (char *)calloc(1, (sizeof(char) * 3) * len)))) + outofmem(__FILE__, "compose_auth_string"); +#ifdef PUBKEY + HTPK_encrypt(cleartext, ciphertext, server->public_key); + HTUU_encode((unsigned char *)ciphertext, strlen(ciphertext), + compose_auth_stringResult); +#endif /* PUBKEY */ + FREE(cleartext); + FREE(ciphertext); + } + else { /* scheme == HTAA_BASIC */ + if (!(compose_auth_stringResult = + (char*)calloc(1, (4 * ((len+2)/3)) + 1))) + outofmem(__FILE__, "compose_auth_string"); + HTUU_encode((unsigned char *)cleartext, strlen(cleartext), + compose_auth_stringResult); + FREE(cleartext); + } + return compose_auth_stringResult; +} + +/* BROWSER PRIVATE HTAA_selectScheme() +** SELECT THE AUTHENTICATION SCHEME TO USE +** ON ENTRY: +** setup is the server setup structure which can +** be used to make the decision about the +** used scheme. +** +** When new authentication methods are added to library +** this function makes the decision about which one to +** use at a given time. This can be done by inspecting +** environment variables etc. +** +** Currently only searches for the first valid scheme, +** and if nothing found suggests Basic scheme; +** +** ON EXIT: +** returns the authentication scheme to use. +*/ +PRIVATE HTAAScheme HTAA_selectScheme ARGS1(HTAASetup *, setup) +{ + HTAAScheme scheme; + + if (setup && setup->valid_schemes) { + for (scheme=HTAA_BASIC; scheme < HTAA_MAX_SCHEMES; scheme++) + if (-1 < HTList_indexOf(setup->valid_schemes, (void*)scheme)) + return scheme; + } + return HTAA_BASIC; +} + +/* +** Purpose: Free off all module globals. +** Arguments: void +** Return Value: void +** Remarks/Portability/Dependencies/Restrictions: +** To be used at program exit. +** Revision History: +** 06-19-96 created - FM +*/ +PRIVATE void free_HTAAGlobals NOARGS +{ + HTAAServer * server; + int n, i; + + if (server_table != NULL) { + n = HTList_count(server_table); + for (i = (n - 1); i >= 0; i--) { + if ((server = (HTAAServer*)HTList_objectAt(server_table, + i)) != NULL) { + HTAAServer_delete(server); + server = NULL; + } + } + HTList_delete(server_table); + server_table = NULL; + } + + HTAAForwardAuth_reset(); + FREE(HTAA_composeAuthResult); + FREE(current_hostname); + FREE(current_docname); + FREE(compose_auth_stringResult); + FREE(secret_key); +} + +/* BROWSER PUBLIC HTAA_composeAuth() +** +** SELECT THE AUTHENTICATION SCHEME AND +** COMPOSE THE ENTIRE AUTHORIZATION HEADER LINE +** IF WE ALREADY KNOW THAT THE HOST REQUIRES AUTHENTICATION +** +** ON ENTRY: +** hostname is the hostname of the server. +** portnumber is the portnumber in which the server runs. +** docname is the pathname of the document (as in URL) +** +** ON EXIT: +** returns NULL, if no authorization seems to be needed, or +** if it is the entire Authorization: line, e.g. +** +** "Authorization: Basic username:password" +** +** As usual, this string is automatically freed. +*/ +PUBLIC char *HTAA_composeAuth ARGS3(CONST char *, hostname, + CONST int, portnumber, + CONST char *, docname) +{ + char *auth_string; + BOOL retry; + HTAAScheme scheme; + int len; + + /* + ** Setup atexit() freeing if not done already. - FM + */ + if (!free_HTAAGlobalsSet) { + atexit(free_HTAAGlobals); + free_HTAAGlobalsSet = TRUE; + } + + /* + ** Make gateway httpds pass authorization field as it was received. + ** (This still doesn't really work because Authenticate: headers + ** from remote server are not forwarded to client yet so it cannot + ** really know that it should send authorization; I will not + ** implement it yet because I feel we will soon change radically + ** the way requests are represented to allow multithreading + ** on server-side. Life is hard.) + */ + if (HTAAForwardAuth) { + if (TRACE) + fprintf(stderr, "HTAA_composeAuth: %s\n", + "Forwarding received authorization"); + StrAllocCopy(HTAA_composeAuthResult, HTAAForwardAuth); + HTAAForwardAuth_reset(); /* Just a precaution */ + return HTAA_composeAuthResult; + } + + FREE(HTAA_composeAuthResult); /* From previous call */ + + if (TRACE) + fprintf(stderr, + "Composing Authorization for %s:%d/%s\n", + hostname, portnumber, docname); + + if (current_portnumber != portnumber || + !current_hostname || !current_docname || + !hostname || !docname || + 0 != strcmp(current_hostname, hostname) || + 0 != strcmp(current_docname, docname)) { + + retry = NO; + + current_portnumber = portnumber; + + if (hostname) + StrAllocCopy(current_hostname, hostname); + else + FREE(current_hostname); + + if (docname) + StrAllocCopy(current_docname, docname); + else + FREE(current_docname); + } else { + retry = YES; + } + + if (!current_setup || !retry) + current_setup = HTAASetup_lookup(hostname, portnumber, docname); + + if (!current_setup) + return NULL; + + + switch (scheme = HTAA_selectScheme(current_setup)) { + case HTAA_BASIC: + case HTAA_PUBKEY: + auth_string = compose_auth_string(scheme, current_setup); + break; + case HTAA_KERBEROS_V4: + /* OTHER AUTHENTICATION ROUTINES ARE CALLED HERE */ + default: + { + char msg[100]; + sprintf(msg, "%s %s `%s'", + "This client doesn't know how to compose authentication", + "information for scheme", HTAAScheme_name(scheme)); + HTAlert(msg); + auth_string = NULL; + } + } /* switch scheme */ + + current_setup->retry = NO; + + if (!auth_string) + /* + * Signal a failure. - FM + */ + return NULL; /* Added by marca. */ + if (*auth_string == '\0') { + /* + * Signal an abort. - FM + */ + StrAllocCopy(HTAA_composeAuthResult, ""); + return(HTAA_composeAuthResult); + } + + len = strlen(auth_string) + strlen((char *)HTAAScheme_name(scheme)) + 20; + if (!(HTAA_composeAuthResult = + (char*)calloc(1, sizeof(char) * len))) + outofmem(__FILE__, "HTAA_composeAuth"); + strcpy(HTAA_composeAuthResult, "Authorization: "); + strcat(HTAA_composeAuthResult, HTAAScheme_name(scheme)); + strcat(HTAA_composeAuthResult, " "); + strcat(HTAA_composeAuthResult, auth_string); + return HTAA_composeAuthResult; +} + +/* BROWSER PUBLIC HTAA_shouldRetryWithAuth() +** +** DETERMINES IF WE SHOULD RETRY THE SERVER +** WITH AUTHORIZATION +** (OR IF ALREADY RETRIED, WITH A DIFFERENT +** USERNAME AND/OR PASSWORD (IF MISSPELLED)) +** ON ENTRY: +** start_of_headers is the first block already read from socket, +** but status line skipped; i.e. points to the +** start of the header section. +** length is the remaining length of the first block. +** soc is the socket to read the rest of server reply. +** +** This function should only be called when +** server has replied with a 401 (Unauthorized) +** status code. +** ON EXIT: +** returns YES, if connection should be retried. +** The node containing all the necessary +** information is +** * either constructed if it does not exist +** * or password is reset to NULL to indicate +** that username and password should be +** reprompted when composing Authorization: +** field (in function HTAA_composeAuth()). +** NO, otherwise. +*/ +PUBLIC BOOL HTAA_shouldRetryWithAuth ARGS4(char *, start_of_headers, + int, length, + void *, handle, + int, soc) +{ + HTAAScheme scheme; + char *line = NULL; + int num_schemes = 0; + HTList *valid_schemes = HTList_new(); + HTAssocList **scheme_specifics = NULL; + char *template = NULL; + char *temp = NULL; + + /* + ** Setup atexit() freeing if not done already. - FM + */ + if (!free_HTAAGlobalsSet) { + atexit(free_HTAAGlobals); + free_HTAAGlobalsSet = TRUE; + } + + /* + ** Read server reply header lines + */ + if (TRACE) + fprintf(stderr, "Server reply header lines:\n"); + + HTAA_setupReader(start_of_headers, length, handle, soc); + while (NULL != (line = HTAA_getUnfoldedLine()) && *line != '\0') { + if (TRACE) + fprintf(stderr, "%s\n", line); + + if (strchr(line, ':')) { /* Valid header line */ + + char *p = line; + char *fieldname = HTNextField(&p); + char *arg1 = HTNextField(&p); + char *args = p; + + if (0==strcasecomp(fieldname, "WWW-Authenticate:")) { + if (!(arg1 && *arg1 && args && *args)) { + temp = (char *)calloc(1, strlen(line) + + (arg1 ? strlen(arg1) : 0) + + (args ? strlen(args) : 0) + 24); + if (!temp) + outofmem(__FILE__, "HTAA_shouldRetryWithAuth"); + sprintf(temp, "Invalid header '%s%s%s%s%s'", line, + ((arg1 && *arg1) ? " " : ""), + ((arg1 && *arg1) ? arg1 : ""), + ((args && *args) ? " " : ""), + ((args && *args) ? args : "")); + HTAlert(temp); + FREE(temp); + } + else if (HTAA_UNKNOWN != (scheme = HTAAScheme_enum(arg1))) { + HTList_addObject(valid_schemes, (void*)scheme); + if (!scheme_specifics) { + int i; + scheme_specifics = (HTAssocList**) + calloc(1, HTAA_MAX_SCHEMES * sizeof(HTAssocList*)); + if (!scheme_specifics) + outofmem(__FILE__, "HTAA_shouldRetryWithAuth"); + for (i=0; i < HTAA_MAX_SCHEMES; i++) + scheme_specifics[i] = NULL; + } + scheme_specifics[scheme] = HTAA_parseArgList(args); + num_schemes++; + } + else if (TRACE) { + fprintf(stderr, "Unknown scheme `%s' %s\n", + (arg1 ? arg1 : "(null)"), + "in WWW-Authenticate: field"); + } + } + + else if (0==strcasecomp(fieldname, "WWW-Protection-Template:")) { + if (TRACE) + fprintf(stderr, "Protection template set to `%s'\n", arg1); + StrAllocCopy(template, arg1); + } + + } /* if a valid header line */ + else if (TRACE) { + fprintf(stderr, "Invalid header line `%s' ignored\n", line); + } /* else invalid header line */ + + FREE(line); + } /* while header lines remain */ + FREE(line); + + + /* + ** So should we retry with authorization + */ + if (num_schemes == 0) { /* No authentication valid */ + current_setup = NULL; + return NO; + } + + if (current_setup && current_setup->server) { + /* So we have already tried with authorization. */ + /* Either we don't have access or username or */ + /* password was misspelled. */ + + /* Update scheme-specific parameters */ + /* (in case they have expired by chance). */ + HTAASetup_updateSpecifics(current_setup, scheme_specifics); + + if (NO == HTConfirm("Authorization failed. Retry?")) { + current_setup = NULL; + return NO; + } /* HTConfirm(...) == NO */ + else { /* re-ask username+password (if misspelled) */ + current_setup->retry = YES; + return YES; + } /* HTConfirm(...) == YES */ + } /* if current_setup != NULL */ + + else { /* current_setup == NULL, i.e. we have a */ + /* first connection to a protected server or */ + /* the server serves a wider set of documents */ + /* than we expected so far. */ + + HTAAServer *server = HTAAServer_lookup(current_hostname, + current_portnumber); + if (!server) { + server = HTAAServer_new(current_hostname, + current_portnumber); + } + if (!template) + template = HTAA_makeProtectionTemplate(current_docname); + current_setup = HTAASetup_new(server, + template, + valid_schemes, + scheme_specifics); + FREE(template); + + HTAlert("Access without authorization denied -- retrying"); + return YES; + } /* else current_setup == NULL */ + + /* Never reached */ +} + diff --git a/WWW/Library/Implementation/HTAABrow.h b/WWW/Library/Implementation/HTAABrow.h new file mode 100644 index 00000000..cfe155dc --- /dev/null +++ b/WWW/Library/Implementation/HTAABrow.h @@ -0,0 +1,138 @@ +/* BROWSER SIDE ACCESS AUTHORIZATION MODULE + + This module is the browser side interface to Access Authorization (AA) package. It + contains code only for browser. + + Important to know about memory allocation: + + Routines in this module use dynamic allocation, but free automatically all the memory + reserved by them. + + Therefore the caller never has to (and never should) free() any object returned by + these functions. + + Therefore also all the strings returned by this package are only valid until the next + call to the same function is made. This approach is selected, because of the nature of + access authorization: no string returned by the package needs to be valid longer than + until the next call. + + This also makes it easy to plug the AA package in: you don't have to ponder whether to + free()something here or is it done somewhere else (because it is always done somewhere + else). + + The strings that the package needs to store are copied so the original strings given as + parameters to AA functions may be freed or modified with no side effects. + + Also note:The AA package does not free() anything else than what it has itself + allocated. + + */ + +#ifndef HTAABROW_H +#define HTAABROW_H + +#ifndef HTUTILS_H +#include "HTUtils.h" /* BOOL, PARAMS, ARGS */ +#endif /* HTUTILS_H */ +#include "HTAAUtil.h" /* Common parts of AA */ + + +#ifdef SHORT_NAMES +#define HTAAcoAu HTAA_composeAuth +#define HTAAsRWA HTAA_shouldRetryWithAuth +#endif /*SHORT_NAMES*/ + +/* + +Routines for Browser Side Recording of AA Info + + Most of the browser-side AA is done by the following two functions (which are called + from file HTTP.c so the browsers using libwww only need to be linked with the new + library and not be changed at all): + + HTAA_composeAuth() composes the Authorization: line contents, if the AA package + thinks that the given document is protected. Otherwise this function returns NULL. + This function also calls the functions HTPrompt(),HTPromptPassword() and HTConfirm() + to get the username, password and some confirmation from the user. + + HTAA_shouldRetryWithAuth() determines whether to retry the request with AA or with a + new AA (in case username or password was misspelled). + + */ + +/* PUBLIC HTAA_composeAuth() +** +** COMPOSE THE ENTIRE AUTHORIZATION HEADER LINE IF WE +** ALREADY KNOW, THAT THE HOST MIGHT REQUIRE AUTHORIZATION +** +** ON ENTRY: +** hostname is the hostname of the server. +** portnumber is the portnumber in which the server runs. +** docname is the pathname of the document (as in URL) +** +** ON EXIT: +** returns NULL, if no authorization seems to be needed, or +** if it is the entire Authorization: line, e.g. +** +** "Authorization: basic username:password" +** +** As usual, this string is automatically freed. +*/ +PUBLIC char *HTAA_composeAuth PARAMS((CONST char * hostname, + CONST int portnumber, + CONST char * docname)); + + +/* BROWSER PUBLIC HTAA_shouldRetryWithAuth() +** +** DETERMINES IF WE SHOULD RETRY THE SERVER +** WITH AUTHORIZATION +** (OR IF ALREADY RETRIED, WITH A DIFFERENT +** USERNAME AND/OR PASSWORD (IF MISSPELLED)) +** ON ENTRY: +** start_of_headers is the first block already read from socket, +** but status line skipped; i.e. points to the +** start of the header section. +** length is the remaining length of the first block. +** soc is the socket to read the rest of server reply. +** +** This function should only be called when +** server has replied with a 401 (Unauthorized) +** status code. +** ON EXIT: +** returns YES, if connection should be retried. +** The node containing all the necessary +** information is +** * either constructed if it does not exist +** * or password is reset to NULL to indicate +** that username and password should be +** reprompted when composing Authorization: +** field (in function HTAA_composeAuth()). +** NO, otherwise. +*/ +PUBLIC BOOL HTAA_shouldRetryWithAuth PARAMS((char * start_of_headers, + int length, + void * handle, + int soc)); +/* + +Enabling Gateway httpds to Forward Authorization + + These functions should only be called from daemon code, and HTAAForwardAuth_reset() + must be called before the next request is handled to make sure that authorization + string isn't cached in daemon so that other people can access private files using + somebody elses previous authorization information. + + */ + +PUBLIC void HTAAForwardAuth_set PARAMS((CONST char * scheme_name, + CONST char * scheme_specifics)); +PUBLIC void HTAAForwardAuth_reset NOPARAMS; +/* + + */ + +#endif /* NOT HTAABROW_H */ +/* + + End of file HTAABrow.h. */ diff --git a/WWW/Library/Implementation/HTAAFile.c b/WWW/Library/Implementation/HTAAFile.c new file mode 100644 index 00000000..15c2d547 --- /dev/null +++ b/WWW/Library/Implementation/HTAAFile.c @@ -0,0 +1,210 @@ + +/* MODULE HTAAFile.c +** FILE ROUTINES FOR AUTHENTICATION +** (PASSWD AND GROUP FILES) AND +** ACCESS CONTROL LIST (.www_acl) +** AUTHORS: +** AL Ari Luotonen luotonen@dxcern.cern.ch +** +** HISTORY: +** +** +** BUGS: +** +** +*/ + + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ + +#include "tcp.h" /* Macro FROMASCII() */ +/*#include <stdio.h> included by HTUtils.h -- FM *//* FILE */ +#include <string.h> +#include "HTAAUtil.h" /* Common utilities used in AA */ +#include "HTAAFile.h" /* Implemented here */ + +#include "LYLeaks.h" + +#define SPACE ' ' +#define TAB '\t' + + + +/* PUBLIC HTAAFile_nextRec() +** GO TO THE BEGINNING OF THE NEXT RECORD +** ON ENTRY: +** fp is the file from which records are read from. +** +** ON EXIT: +** returns nothing. File read pointer is located at the beginning +** of the next record. Handles continuation lines +** (lines ending in comma indicate a following +** continuation line). +** +*/ +PUBLIC void HTAAFile_nextRec ARGS1(FILE *, fp) +{ + int ch = getc(fp); + int last = (char)0; + + do { + while (ch != EOF && ch != CR && ch != LF) { + if (ch != ' ' && ch != '\t') + last = ch; /* Last non-whitespace */ + ch = getc(fp); /* Skip until end-of-line */ + } + while (ch != EOF && + (ch == CR || ch == LF))/*Skip carriage returns and linefeeds*/ + ch = getc(fp); + if (ch != EOF) + ungetc(ch, fp); + } while (last == ',' && ch != EOF); /* Skip also continuation lines */ +} + + +/* PRIVATE read_item() +** READ AN ITEM FROM A PASSWORD, GROUP +** OR ACCESS CONTROL LIST FILE +** i.e. either a field, or a list item. +** ON ENTRY: +** fp is the file to read the characters from +** contents is the character array to put the characters +** reading_list if TRUE, read a list item (ends either in +** acomma or acolon), +** if FALSE, read a field (ends in acolon). +** max_len is the maximum number of characters that may +** be read (i.e. the size of dest minus one for +** terminating null). +** ON EXIT: +** returns the terminating character +** (i.e. either separator or CR or LF or EOF). +** contents contains a null-terminated string representing +** the read field. +** NOTE 1: +** Ignores leading and trailing blanks and tabs. +** NOTE 2: +** If the item is more than max_len characters +** long, the rest of the characters in that item +** are ignored. However, contents is always +** null-terminated! +*/ +PRIVATE int read_item ARGS4(FILE *, fp, + char *, contents, + BOOL, reading_list, + int, max_len) +{ + char * dest = contents; + char * end = contents; + int cnt = 0; + int ch = getc(fp); + + while (SPACE == ch || TAB == ch) /* Skip spaces and tabs */ + ch = getc(fp); + + while (ch != FIELD_SEPARATOR && + (!reading_list || ch != LIST_SEPARATOR) && + ch != CR && ch != LF && ch != EOF && cnt < max_len) { + *(dest++) = ch; + cnt++; + if (ch != SPACE && ch != TAB) + end = dest; + ch = getc(fp); + } /* while not eol or eof or too many read */ + + if (cnt == max_len) { + /* If the field was too long (or exactly maximum) ignore the rest */ + while (ch != FIELD_SEPARATOR && + (!reading_list || ch != LIST_SEPARATOR) && + ch != CR && ch != LF && ch != EOF) + ch = getc(fp); + } + + if (ch == CR || ch == LF) + ungetc(ch, fp); /* Push back the record separator (NL or LF) */ + + /* Terminate the string, truncating trailing whitespace off. + ** Otherwise (if whitespace would be included), here would + ** be *dest='\0'; and cnt -= ... would be left out. + */ + *end = '\0'; + cnt -= dest-end; + + return ch; /* Return the terminating character */ +} + + + +/* PUBLIC HTAAFile_readField() +** READ A FIELD FROM A PASSWORD, GROUP +** OR ACCESS CONTROL LIST FILE +** i.e. an item terminated by colon, +** end-of-line, or end-of-file. +** ON ENTRY: +** fp is the file to read the characters from +** contents is the character array to put the characters +** max_len is the maximum number of characters that may +** be read (i.e. the size of dest minus one for +** terminating null). +** ON EXIT: +** returns the terminating character +** (i.e. either separator or CR or LF or EOF). +** contents contains a null-terminated string representing +** the read field. +** NOTE 1: +** Ignores leading and trailing blanks and tabs. +** NOTE 2: +** If the field is more than max_len characters +** long, the rest of the characters in that item +** are ignored. However, contents is always +** null-terminated! +*/ +PUBLIC int HTAAFile_readField ARGS3(FILE *, fp, + char *, contents, + int, max_len) +{ + return read_item(fp, contents, NO, max_len); +} + + + + +/* PUBLIC HTAAFile_readList() +** +** READ A LIST OF STRINGS SEPARATED BY COMMAS +** (FROM A GROUP OR ACCESS CONTROL LIST FILE) +** ON ENTRY: +** fp is a pointer to the input file. +** result is the list to which append the read items. +** max_len is the maximum number of characters in each +** list entry (extra characters are ignored). +** ON EXIT: +** returns the number of items read. +** +*/ +PUBLIC int HTAAFile_readList ARGS3(FILE *, fp, + HTList *, result, + int, max_len) +{ + char *item = NULL; + char terminator; + int cnt = 0; + + do { + if (!item && !(item = (char*)malloc(max_len+1))) + outofmem(__FILE__, "HTAAFile_readList"); + terminator = read_item(fp, item, YES, max_len); + if (strlen(item) > 0) { + cnt++; + HTList_addObject(result, (void*)item); + item = NULL; + } + } while (terminator != FIELD_SEPARATOR && + terminator != CR && terminator != LF && + terminator != EOF); + + FREE(item); /* This was not needed */ + return cnt; +} + diff --git a/WWW/Library/Implementation/HTAAFile.h b/WWW/Library/Implementation/HTAAFile.h new file mode 100644 index 00000000..f6f8ac50 --- /dev/null +++ b/WWW/Library/Implementation/HTAAFile.h @@ -0,0 +1,126 @@ +/* FILE ROUTINES FOR ACCESS AUTHORIZATION PACKAGE + + This module implements the routines used for accessing (and parsing) the files used in + the access authorization: + + password file + + group file + + access control list (ACL) file + + */ + + +#ifndef HTAAFILE_H +#define HTAAFILE_H + +#ifndef HTUTILS_H +#include "HTUtils.h" /* BOOL, PARAMS, ARGS */ +#endif /* HTUTILS_H */ +/*#include <stdio.h> included by HTUtils.h -- FM *//* FILE */ +#include "HTList.h" /* HTList */ + +#ifdef SHORT_NAMES +#define HTAAFnRe HTAAFile_nextRec +#define HTAAFrFi HTAAFile_readField +#define HTAAFrLi HTAAFile_readList +#endif /*SHORT_NAMES*/ + + +/* Used field separators */ + +#define FIELD_SEPARATOR ':' /* Used to separate fields */ +#define LIST_SEPARATOR ',' /* Used to separate items in a list */ + /* in group and ALC files. */ + +/* + +Naming conventions + + Record is an entire line in file. + + Field is an entity separated by colons and/or by end-of-line. + + List is a field in which there are items separated by commas. + +Record-oriented Read Routines + + Password, group and ACL are internally read in by the following functions: + + HTAAFile_nextRec() skips to the beginning of the next record (must be called even + after the last field of a record is read to proceed to the next + record). + + HTAAFile_readField() reads a field (separated by colons). + + HTAAFile_readList() reads a field containing a comma-separated list of items. + + */ + +/* PUBLIC HTAAFile_nextRec() +** GO TO THE BEGINNING OF THE NEXT RECORD +** ON ENTRY: +** fp is the file from which records are read from. +** +** ON EXIT: +** returns nothing. File read pointer is located at the beginning +** of the next record. +** +*/ +PUBLIC void HTAAFile_nextRec PARAMS((FILE * fp)); + + +/* PUBLIC HTAAFile_readField() +** READ A FIELD FROM A PASSWORD, GROUP +** OR ACCESS CONTROL LIST FILE +** i.e. an item terminated by colon, +** end-of-line, or end-of-file. +** ON ENTRY: +** fp is the file to read the characters from +** contents is the character array to put the characters +** max_len is the maximum number of characters that may +** be read (i.e. the size of dest minus one for +** terminating null). +** ON EXIT: +** returns the terminating character +** (i.e. either separator or CR or LF or EOF). +** contents contains a null-terminated string representing +** the read field. +** NOTE 1: +** Ignores leading and trailing blanks and tabs. +** NOTE 2: +** If the field is more than max_len characters +** long, the rest of the characters in that item +** are ignored. However, contents is always +** null-terminated! +*/ +PUBLIC int HTAAFile_readField PARAMS((FILE * fp, + char * contents, + int max_len)); + + +/* PUBLIC HTAAFile_readList() +** +** READ A LIST OF STRINGS SEPARATED BY COMMAS +** (FROM A GROUP OR ACCESS CONTROL LIST FILE) +** ON ENTRY: +** fp is a pointer to the input file. +** result is the list to which append the read items. +** max_len is the maximum number of characters in each +** list entry (extra characters are ignored). +** ON EXIT: +** returns the number of items read. +** +*/ +PUBLIC int HTAAFile_readList PARAMS((FILE * fp, + HTList * result, + int max_len)); +/* + + */ + +#endif /* not HTAAFILE_H */ +/* + + End of file HTAAFile.h. */ diff --git a/WWW/Library/Implementation/HTAAProt.c b/WWW/Library/Implementation/HTAAProt.c new file mode 100644 index 00000000..751b51fc --- /dev/null +++ b/WWW/Library/Implementation/HTAAProt.c @@ -0,0 +1,592 @@ + +/* MODULE HTAAProt.c +** PROTECTION FILE PARSING MODULE +** +** AUTHORS: +** AL Ari Luotonen luotonen@dxcern.cern.ch +** MD Mark Donszelmann duns@vxdeop.cern.ch +** +** HISTORY: +** 20 Oct 93 AL Now finds uid/gid for nobody/nogroup by name +** (doesn't use default 65534 right away). +** Also understands negative uids/gids. +** 14 Nov 93 MD Added VMS compatibility +** +** BUGS: +** +** +*/ + +#include "HTUtils.h" + +#include <string.h> +#ifndef VMS +#include <pwd.h> /* Unix password file routine: getpwnam() */ +#include <grp.h> /* Unix group file routine: getgrnam() */ +#endif /* not VMS */ + +#include "HTAAUtil.h" +#include "HTAAFile.h" +#include "HTLex.h" /* Lexical analysor */ +#include "HTAssoc.h" /* Association list */ +#include "HTAAProt.h" /* Implemented here */ + +#include "LYLeaks.h" + +/* +** Protection setup caching +*/ +typedef struct { + char * prot_filename; + HTAAProt * prot; +} HTAAProtCache; + +PRIVATE HTList * prot_cache = NULL; /* Protection setup cache. */ +PRIVATE HTAAProt *default_prot = NULL; /* Default protection. */ +PRIVATE HTAAProt *current_prot = NULL; /* Current protection mode */ + /* which is set up by callbacks */ + /* from the rule system when */ + /* a "protect" rule is matched. */ + + +/* PRIVATE isNumber() +** DOES A CHARACTER STRING REPRESENT A NUMBER +*/ +PRIVATE BOOL isNumber ARGS1(CONST char *, s) +{ + CONST char *cur = s; + + if (!s || !*s) return NO; + + if (*cur == '-') + cur++; /* Allow initial minus sign in a number */ + + while (*cur) { + if (*cur < '0' || *cur > '9') + return NO; + cur++; + } + return YES; +} + + +#ifdef VMS +/* PUBLIC HTAA_getUidName() +** GET THE USER ID NAME (VMS ONLY) +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns the user name +** Default is "" (nobody). +*/ +PUBLIC char * HTAA_getUidName NOARGS +{ + if (current_prot && current_prot->uid_name + && (0 != strcmp(current_prot->uid_name,"nobody")) ) + return(current_prot->uid_name); + else + return(""); +} + +/* PUBLIC HTAA_getFileName +** GET THE FILENAME (VMS ONLY) +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns the filename +*/ +PUBLIC char * HTAA_getFileName NOARGS +{ + if (current_prot && current_prot->filename) + return(current_prot->filename); + else + return(""); +} + +#else /* not VMS */ + +/* PUBLIC HTAA_getUid() +** GET THE USER ID TO CHANGE THE PROCESS UID TO +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns the uid number to give to setuid() system call. +** Default is 65534 (nobody). +*/ +PUBLIC int HTAA_getUid NOARGS +{ + struct passwd *pw = NULL; + + if (current_prot && current_prot->uid_name) { + if (isNumber(current_prot->uid_name)) { + if (NULL != (pw = getpwuid(atoi(current_prot->uid_name)))) { + if (TRACE) fprintf(stderr, + "%s(%s) returned (%s:%s:%d:%d:...)\n", + "HTAA_getUid: getpwuid", + current_prot->uid_name, + pw->pw_name, pw->pw_passwd, + pw->pw_uid, pw->pw_gid); + return pw->pw_uid; + } + } + else { /* User name (not a number) */ + if (NULL != (pw = getpwnam(current_prot->uid_name))) { + if (TRACE) fprintf(stderr, "%s(\"%s\") %s (%s:%s:%d:%d:...)\n", + "HTAA_getUid: getpwnam", + current_prot->uid_name, "returned", + pw->pw_name, pw->pw_passwd, + pw->pw_uid, pw->pw_gid); + return pw->pw_uid; + } + } + } + /* + ** Ok, then let's get uid for nobody. + */ + if (NULL != (pw = getpwnam("nobody"))) { + if (TRACE) fprintf(stderr, "HTAA_getUid: Uid for `nobody' is %d\n", + pw->pw_uid); + return pw->pw_uid; + } + /* + ** Ok, then use default. + */ + return 65534; /* nobody */ +} + + +/* PUBLIC HTAA_getGid() +** GET THE GROUP ID TO CHANGE THE PROCESS GID TO +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns the uid number to give to setgid() system call. +** Default is 65534 (nogroup). +*/ +PUBLIC int HTAA_getGid NOARGS +{ + struct group *gr = NULL; + + if (current_prot && current_prot->gid_name) { + if (isNumber(current_prot->gid_name)) { + if (NULL != (gr = getgrgid(atoi(current_prot->gid_name)))) { + if (TRACE) fprintf(stderr, + "%s(%s) returned (%s:%s:%d:...)\n", + "HTAA_getGid: getgrgid", + current_prot->gid_name, + gr->gr_name, gr->gr_passwd, gr->gr_gid); + return gr->gr_gid; + } + } + else { /* Group name (not number) */ + if (NULL != (gr = getgrnam(current_prot->gid_name))) { + if (TRACE) fprintf(stderr, + "%s(\"%s\") returned (%s:%s:%d:...)\n", + "HTAA_getGid: getgrnam", + current_prot->gid_name, + gr->gr_name, gr->gr_passwd, gr->gr_gid); + return gr->gr_gid; + } + } + } + /* + ** Ok, then let's get gid for nogroup. + */ + if (NULL != (gr = getgrnam("nogroup"))) { + if (TRACE) fprintf(stderr, "HTAA_getGid: Gid for `nogroup' is %d\n", + gr->gr_gid); + return gr->gr_gid; + } + /* + ** Ok, then use default. + */ + return 65534; /* nogroup */ +} +#endif /* not VMS */ + + +/* PRIVATE HTAA_setIds() +** SET UID AND GID (AS NAMES OR NUMBERS) +** TO HTAAProt STRUCTURE +** ON ENTRY: +** prot destination. +** ids is a string like "james.www" or "1422.69" etc. +** giving uid and gid. +** +** ON EXIT: +** returns nothing. +*/ +PRIVATE void HTAA_setIds ARGS2(HTAAProt *, prot, + CONST char *, ids) +{ + if (ids) { + char *local_copy = NULL; + char *point; + + StrAllocCopy(local_copy, ids); + point = strchr(local_copy, '.'); + if (point) { + *(point++) = (char)0; + StrAllocCopy(prot->gid_name, point); + } + else { + StrAllocCopy(prot->gid_name, "nogroup"); + } + StrAllocCopy(prot->uid_name, local_copy); + FREE(local_copy); + } + else { + StrAllocCopy(prot->uid_name, "nobody"); + StrAllocCopy(prot->gid_name, "nogroup"); + } +} + + +/* PRIVATE HTAA_parseProtFile() +** PARSE A PROTECTION SETUP FILE AND +** PUT THE RESULT IN A HTAAProt STRUCTURE +** ON ENTRY: +** prot destination structure. +** fp open protection file. +** +** ON EXIT: +** returns nothing. +*/ +PRIVATE void HTAA_parseProtFile ARGS2(HTAAProt *, prot, + FILE *, fp) +{ + if (prot && fp) { + LexItem lex_item; + char *fieldname = NULL; + + while (LEX_EOF != (lex_item = lex(fp))) { + + while (lex_item == LEX_REC_SEP) /* Ignore empty lines */ + lex_item = lex(fp); + + if (lex_item == LEX_EOF) /* End of file */ + break; + + if (lex_item == LEX_ALPH_STR) { /* Valid setup record */ + + StrAllocCopy(fieldname, HTlex_buffer); + + if (LEX_FIELD_SEP != (lex_item = lex(fp))) + unlex(lex_item); /* If someone wants to use colon */ + /* after field name it's ok, but */ + /* not required. Here we read it.*/ + + if (0==strncasecomp(fieldname, "Auth", 4)) { + lex_item = lex(fp); + while (lex_item == LEX_ALPH_STR) { + HTAAScheme scheme = HTAAScheme_enum(HTlex_buffer); + if (scheme != HTAA_UNKNOWN) { + if (!prot->valid_schemes) + prot->valid_schemes = HTList_new(); + HTList_addObject(prot->valid_schemes,(void*)scheme); + if (TRACE) fprintf(stderr, "%s %s `%s'\n", + "HTAA_parseProtFile: valid", + "authentication scheme:", + HTAAScheme_name(scheme)); + } + else if (TRACE) fprintf(stderr, "%s %s `%s'\n", + "HTAA_parseProtFile: unknown", + "authentication scheme:", + HTlex_buffer); + + if (LEX_ITEM_SEP != (lex_item = lex(fp))) + break; + /* + ** Here lex_item == LEX_ITEM_SEP; after item separator + ** it is ok to have one or more newlines (LEX_REC_SEP) + ** and they are ignored (continuation line). + */ + do { + lex_item = lex(fp); + } while (lex_item == LEX_REC_SEP); + } /* while items in list */ + } /* if "Authenticate" */ + + else if (0==strncasecomp(fieldname, "mask", 4)) { + prot->mask_group = HTAA_parseGroupDef(fp); + lex_item=LEX_REC_SEP; /*groupdef parser read this already*/ + if (TRACE) { + if (prot->mask_group) { + fprintf(stderr, + "HTAA_parseProtFile: Mask group:\n"); + HTAA_printGroupDef(prot->mask_group); + } else fprintf(stderr, "HTAA_parseProtFile: %s\n", + "Mask group syntax error"); + } + } /* if "Mask" */ + + else { /* Just a name-value pair, put it to assoclist */ + + if (LEX_ALPH_STR == (lex_item = lex(fp))) { + if (!prot->values) + prot->values = HTAssocList_new(); + HTAssocList_add(prot->values, fieldname, HTlex_buffer); + lex_item = lex(fp); /* Read record separator */ + if (TRACE) fprintf(stderr, + "%s `%s' bound to value `%s'\n", + "HTAA_parseProtFile: Name", + fieldname, HTlex_buffer); + } + } /* else name-value pair */ + + } /* if valid field */ + + if (lex_item != LEX_EOF && lex_item != LEX_REC_SEP) { + if (TRACE) fprintf(stderr, "%s %s %d (that line ignored)\n", + "HTAA_parseProtFile: Syntax error", + "in protection setup file at line", + HTlex_line); + do { + lex_item = lex(fp); + } while (lex_item != LEX_EOF && lex_item != LEX_REC_SEP); + } /* if syntax error */ + } /* while not end-of-file */ + FREE(fieldname); + } /* if valid parameters */ +} + + +/* PRIVATE HTAAProt_new() +** ALLOCATE A NEW HTAAProt STRUCTURE AND +** INITIALIZE IT FROM PROTECTION SETUP FILE +** ON ENTRY: +** cur_docname current filename after rule translations. +** prot_filename protection setup file name. +** If NULL, not an error. +** ids Uid and gid names or numbers, +** examples: +** james ( <=> james.nogroup) +** .www ( <=> nobody.www) +** james.www +** james.69 +** 1422.69 +** 1422.www +** +** May be NULL, defaults to nobody.nogroup. +** Should be NULL, if prot_file is NULL. +** +** ON EXIT: +** returns returns a new and initialized protection +** setup structure. +** If setup file is already read in (found +** in cache), only sets uid_name and gid +** fields, and returns that. +*/ +PRIVATE HTAAProt *HTAAProt_new ARGS3(CONST char *, cur_docname, + CONST char *, prot_filename, + CONST char *, ids) +{ + HTList *cur = prot_cache; + HTAAProtCache *cache_item = NULL; + HTAAProt *prot; + FILE *fp; + + if (!prot_cache) + prot_cache = HTList_new(); + + while (NULL != (cache_item = (HTAAProtCache*)HTList_nextObject(cur))) { + if (!strcmp(cache_item->prot_filename, prot_filename)) + break; + } + if (cache_item) { + prot = cache_item->prot; + if (TRACE) fprintf(stderr, "%s `%s' already in cache\n", + "HTAAProt_new: Protection file", prot_filename); + } else { + if (TRACE) fprintf(stderr, + "HTAAProt_new: Loading protection file `%s'\n", + prot_filename); + + if (!(prot = (HTAAProt*)calloc(1, sizeof(HTAAProt)))) + outofmem(__FILE__, "HTAAProt_new"); + + prot->template = NULL; + prot->filename = NULL; + prot->uid_name = NULL; + prot->gid_name = NULL; + prot->valid_schemes = HTList_new(); + prot->mask_group= NULL; /* Masking disabled by defaults */ + prot->values = HTAssocList_new(); + + if (prot_filename && NULL != (fp = fopen(prot_filename, "r"))) { + HTAA_parseProtFile(prot, fp); + fclose(fp); + if (!(cache_item = + (HTAAProtCache*)calloc(1, sizeof(HTAAProtCache)))) + outofmem(__FILE__, "HTAAProt_new"); + cache_item->prot = prot; + cache_item->prot_filename = NULL; + StrAllocCopy(cache_item->prot_filename, prot_filename); + HTList_addObject(prot_cache, (void*)cache_item); + } + else if (TRACE) fprintf(stderr, "HTAAProt_new: %s `%s'\n", + "Unable to open protection setup file", + (prot_filename ? prot_filename : "(null)")); + } + + if (cur_docname) + StrAllocCopy(prot->filename, cur_docname); + HTAA_setIds(prot, ids); + + return prot; +} + + +/* PUBLIC HTAA_setDefaultProtection() +** SET THE DEFAULT PROTECTION MODE +** (called by rule system when a +** "defprot" rule is matched) +** ON ENTRY: +** cur_docname is the current result of rule translations. +** prot_filename is the protection setup file (second argument +** for "defprot" rule, optional) +** ids contains user and group names separated by +** a dot, corresponding to the uid +** gid under which the server should run, +** default is "nobody.nogroup" (third argument +** for "defprot" rule, optional; can be given +** only if protection setup file is also given). +** +** ON EXIT: +** returns nothing. +** Sets the module-wide variable default_prot. +*/ +PUBLIC void HTAA_setDefaultProtection ARGS3(CONST char *, cur_docname, + CONST char *, prot_filename, + CONST char *, ids) +{ + default_prot = NULL; /* Not free()'d because this is in cache */ + + if (prot_filename) { + default_prot = HTAAProt_new(cur_docname, prot_filename, ids); + } else { + if (TRACE) fprintf(stderr, "%s %s\n", + "HTAA_setDefaultProtection: ERROR: Protection file", + "not specified (obligatory for DefProt rule)!!\n"); + } +} + + +/* PUBLIC HTAA_setCurrentProtection() +** SET THE CURRENT PROTECTION MODE +** (called by rule system when a +** "protect" rule is matched) +** ON ENTRY: +** cur_docname is the current result of rule translations. +** prot_filename is the protection setup file (second argument +** for "protect" rule, optional) +** ids contains user and group names separated by +** a dot, corresponding to the uid +** gid under which the server should run, +** default is "nobody.nogroup" (third argument +** for "protect" rule, optional; can be given +** only if protection setup file is also given). +** +** ON EXIT: +** returns nothing. +** Sets the module-wide variable current_prot. +*/ +PUBLIC void HTAA_setCurrentProtection ARGS3(CONST char *, cur_docname, + CONST char *, prot_filename, + CONST char *, ids) +{ + current_prot = NULL; /* Not free()'d because this is in cache */ + + if (prot_filename) { + current_prot = HTAAProt_new(cur_docname, prot_filename, ids); + } else { + if (default_prot) { + current_prot = default_prot; + HTAA_setIds(current_prot, ids); + if (TRACE) fprintf(stderr, "%s %s %s\n", + "HTAA_setCurrentProtection: Protection file", + "not specified for Protect rule", + "-- using default protection"); + } else { + if (TRACE) fprintf(stderr, "%s %s %s\n", + "HTAA_setCurrentProtection: ERROR: Protection", + "file not specified for Protect rule, and", + "default protection is not set!!"); + } + } +} + + +/* PUBLIC HTAA_getCurrentProtection() +** GET CURRENT PROTECTION SETUP STRUCTURE +** (this is set up by callbacks made from +** the rule system when matching "protect" +** (and "defprot") rules) +** ON ENTRY: +** HTTranslate() must have been called before calling +** this function. +** +** ON EXIT: +** returns a HTAAProt structure representing the +** protection setup of the HTTranslate()'d file. +** This must not be free()'d. +*/ +PUBLIC HTAAProt *HTAA_getCurrentProtection NOARGS +{ + return current_prot; +} + + +/* PUBLIC HTAA_getDefaultProtection() +** GET DEFAULT PROTECTION SETUP STRUCTURE +** AND SET IT TO CURRENT PROTECTION +** (this is set up by callbacks made from +** the rule system when matching "defprot" +** rules) +** ON ENTRY: +** HTTranslate() must have been called before calling +** this function. +** +** ON EXIT: +** returns a HTAAProt structure representing the +** default protection setup of the HTTranslate()'d +** file (if HTAA_getCurrentProtection() returned +** NULL, i.e. if there is no "protect" rule +** but ACL exists, and we need to know default +** protection settings). +** This must not be free()'d. +** IMPORTANT: +** As a side-effect this tells the protection system that +** the file is in fact protected and sets the current +** protection mode to default. +*/ +PUBLIC HTAAProt *HTAA_getDefaultProtection NOARGS +{ + if (!current_prot) { + current_prot = default_prot; + default_prot = NULL; + } + return current_prot; +} + + +/* SERVER INTERNAL HTAA_clearProtections() +** CLEAR DOCUMENT PROTECTION MODE +** (ALSO DEFAULT PROTECTION) +** (called by the rule system) +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns nothing. +** Frees the memory used by protection information. +*/ +PUBLIC void HTAA_clearProtections NOARGS +{ + current_prot = NULL; /* These are not freed because */ + default_prot = NULL; /* they are actually in cache. */ +} diff --git a/WWW/Library/Implementation/HTAAProt.h b/WWW/Library/Implementation/HTAAProt.h new file mode 100644 index 00000000..73bb7c45 --- /dev/null +++ b/WWW/Library/Implementation/HTAAProt.h @@ -0,0 +1,231 @@ +/* PROTECTION SETUP FILE + + */ + +#ifndef HTAAPROT_H +#define HTAAPROT_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTGroup.h" +#include "HTAssoc.h" + +#ifdef SHORT_NAMES +#define HTAAgUid HTAA_getUid +#define HTAAgGid HTAA_getGid +#define HTAAgDPr HTAA_setDefaultProtection +#define HTAAsCPr HTAA_setCurrentProtection +#define HTAAgCPr HTAA_getCurrentProtection +#define HTAAgDPr HTAA_getDefaultProtection +#define HTAAclPr HTAA_clearProtections +#endif /*SHORT_NAMES*/ +/* + +Server's Representation of Document (Tree) Protections + + */ + +typedef struct { + char * template; /* Template for this protection */ + char * filename; /* Current document file */ + char * uid_name; /* Effective uid (name of it) */ + char * gid_name; /* Effective gid (name of it) */ + GroupDef * mask_group; /* Allowed users and IP addresses */ + HTList * valid_schemes;/* Valid authentication schemes */ + HTAssocList * values; /* Association list for scheme specific */ + /* parameters. */ +} HTAAProt; +/* + +Callbacks for rule system + + The following three functioncs are called by the rule system: + + HTAA_clearProtections() when starting to translate a filename + + HTAA_setDefaultProtection() when "defprot" rule is matched + + HTAA_setCurrentProtection() when "protect" rule is matched + + Protection setup files are cached by these functions. + + */ + +/* PUBLIC HTAA_setDefaultProtection() +** SET THE DEFAULT PROTECTION MODE +** (called by rule system when a +** "defprot" rule is matched) +** ON ENTRY: +** cur_docname is the current result of rule translations. +** prot_filename is the protection setup file (second argument +** for "defprot" rule, optional) +** eff_ids contains user and group names separated by +** a dot, corresponding to the effective uid +** gid under which the server should run, +** default is "nobody.nogroup" (third argument +** for "defprot" rule, optional; can be given +** only if protection setup file is also given). +** +** ON EXIT: +** returns nothing. +** Sets the module-wide variable default_prot. +*/ +PUBLIC void HTAA_setDefaultProtection PARAMS((CONST char * cur_docname, + CONST char * prot_filename, + CONST char * eff_ids)); + + + +/* PUBLIC HTAA_setCurrentProtection() +** SET THE CURRENT PROTECTION MODE +** (called by rule system when a +** "protect" rule is matched) +** ON ENTRY: +** cur_docname is the current result of rule translations. +** prot_filename is the protection setup file (second argument +** for "protect" rule, optional) +** eff_ids contains user and group names separated by +** a dot, corresponding to the effective uid +** gid under which the server should run, +** default is "nobody.nogroup" (third argument +** for "protect" rule, optional; can be given +** only if protection setup file is also given). +** +** ON EXIT: +** returns nothing. +** Sets the module-wide variable current_prot. +*/ +PUBLIC void HTAA_setCurrentProtection PARAMS((CONST char * cur_docname, + CONST char * prot_filename, + CONST char * eff_ids)); + + +/* SERVER INTERNAL HTAA_clearProtections() +** CLEAR DOCUMENT PROTECTION MODE +** (ALSO DEFAULT PROTECTION) +** (called by the rule system) +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns nothing. +** Frees the memory used by protection information. +*/ +PUBLIC void HTAA_clearProtections NOPARAMS; +/* + +Getting Protection Settings + + HTAA_getCurrentProtection() returns the current protection mode (if there was a + "protect" rule). NULL, if no "protect" rule has been matched. + + HTAA_getDefaultProtection() sets the current protection mode to what it was set to + by "defprot" rule and also returns it (therefore after this call also + HTAA_getCurrentProtection() returns the same structure. + + */ + +/* PUBLIC HTAA_getCurrentProtection() +** GET CURRENT PROTECTION SETUP STRUCTURE +** (this is set up by callbacks made from +** the rule system when matching "protect" +** (and "defprot") rules) +** ON ENTRY: +** HTTranslate() must have been called before calling +** this function. +** +** ON EXIT: +** returns a HTAAProt structure representing the +** protection setup of the HTTranslate()'d file. +** This must not be free()'d. +*/ +PUBLIC HTAAProt *HTAA_getCurrentProtection NOPARAMS; + + + +/* PUBLIC HTAA_getDefaultProtection() +** GET DEFAULT PROTECTION SETUP STRUCTURE +** (this is set up by callbacks made from +** the rule system when matching "defprot" +** rules) +** ON ENTRY: +** HTTranslate() must have been called before calling +** this function. +** +** ON EXIT: +** returns a HTAAProt structure representing the +** default protection setup of the HTTranslate()'d +** file (if HTAA_getCurrentProtection() returned +** NULL, i.e. if there is no "protect" rule +** but ACL exists, and we need to know default +** protection settings). +** This must not be free()'d. +*/ +PUBLIC HTAAProt *HTAA_getDefaultProtection NOPARAMS; +/* + +Get User and Group IDs to Which Set to + + */ + +#ifndef VMS +/* PUBLIC HTAA_getUid() +** GET THE USER ID TO CHANGE THE PROCESS UID TO +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns the uid number to give to setuid() system call. +** Default is 65534 (nobody). +*/ +PUBLIC int HTAA_getUid NOPARAMS; + + +/* PUBLIC HTAA_getGid() +** GET THE GROUP ID TO CHANGE THE PROCESS GID TO +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns the uid number to give to setgid() system call. +** Default is 65534 (nogroup). +*/ +PUBLIC int HTAA_getGid NOPARAMS; +#endif /* not VMS */ +/* + + For VMS: + + */ + +#ifdef VMS +/* PUBLIC HTAA_getUidName() +** GET THE USER ID NAME (VMS ONLY) +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns the user name +** Default is "" (nobody). +*/ +PUBLIC char * HTAA_getUidName NOPARAMS; + +/* PUBLIC HTAA_getFileName +** GET THE FILENAME (VMS ONLY) +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns the filename +*/ +PUBLIC char * HTAA_getFileName NOPARAMS; +#endif /* VMS */ +/* + + */ + +#endif /* not HTAAPROT_H */ +/* + + End of file HTAAProt.h. */ diff --git a/WWW/Library/Implementation/HTAAServ.c b/WWW/Library/Implementation/HTAAServ.c new file mode 100644 index 00000000..7a0cdef6 --- /dev/null +++ b/WWW/Library/Implementation/HTAAServ.c @@ -0,0 +1,686 @@ + +/* MODULE HTAAServ.c +** SERVER SIDE ACCESS AUTHORIZATION MODULE +** +** Contains the means for checking the user access +** authorization for a file. +** +** IMPORTANT: +** Routines in this module use dynamic allocation, but free +** automatically all the memory reserved by them. +** +** Therefore the caller never has to (and never should) +** free() any object returned by these functions. +** +** Therefore also all the strings returned by this package +** are only valid until the next call to the same function +** is made. This approach is selected, because of the nature +** of access authorization: no string returned by the package +** needs to be valid longer than until the next call. +** +** This also makes it easy to plug the AA package in: +** you don't have to ponder whether to free() something +** here or is it done somewhere else (because it is always +** done somewhere else). +** +** The strings that the package needs to store are copied +** so the original strings given as parameters to AA +** functions may be freed or modified with no side effects. +** +** The AA package does not free() anything else than what +** it has itself allocated. +** +** AUTHORS: +** AL Ari Luotonen luotonen@dxcern.cern.ch +** +** HISTORY: +** +** +** BUGS: +** +** +*/ + +#include "HTUtils.h" + +/*#include <stdio.h> included by HTUtils.h -- FM *//* FILE */ +#include <string.h> /* strchr() */ + +#include "HTString.h" +#include "HTAccess.h" /* HTSecure */ +#include "HTFile.h" /* HTLocalName */ +#include "HTRules.h" /* */ +#include "HTParse.h" /* URL parsing function */ +#include "HTList.h" /* HTList object */ + +#include "HTAAUtil.h" /* AA common parts */ +#include "HTAuth.h" /* Authentication */ +#include "HTACL.h" /* Access Control List */ +#include "HTGroup.h" /* Group handling */ +#include "HTAAProt.h" /* Protection file parsing */ +#include "HTAAServ.h" /* Implemented here */ + +#include "LYLeaks.h" + +/* +** Global variables +*/ +PUBLIC time_t theTime; + + +/* +** Module-wide global variables +*/ +PRIVATE FILE * htaa_logfile = NULL; /* Log file */ +PRIVATE HTAAUser *htaa_user = NULL; /* Authenticated user */ +PRIVATE HTAAFailReasonType HTAAFailReason = HTAA_OK; /* AA fail reason */ + + +/* SERVER PUBLIC HTAA_statusMessage() +** RETURN A STRING EXPLAINING ACCESS +** AUTHORIZATION FAILURE +** (Can be used in server reply status line +** with 401/403 replies.) +** ON EXIT: +** returns a string containing the error message +** corresponding to internal HTAAFailReason. +*/ +PUBLIC char *HTAA_statusMessage NOARGS +{ + switch (HTAAFailReason) { + + /* 401 cases */ + case HTAA_NO_AUTH: + return "Unauthorized -- authentication failed"; + break; + case HTAA_NOT_MEMBER: + return "Unauthorized to access the document"; + break; + + /* 403 cases */ + case HTAA_BY_RULE: + return "Forbidden -- by rule"; + break; + case HTAA_IP_MASK: + return "Forbidden -- server refuses to serve to your IP address"; + break; + case HTAA_NO_ACL: + case HTAA_NO_ENTRY: + return "Forbidden -- access to file is never allowed"; + break; + case HTAA_SETUP_ERROR: + return "Forbidden -- server protection setup error"; + break; + case HTAA_DOTDOT: + return "Forbidden -- URL containing /../ disallowed"; + break; + case HTAA_HTBIN: + return "Forbidden -- /htbin feature not enabled on this server"; + break; + + /* 404 cases */ + case HTAA_NOT_FOUND: + return "Not found -- file doesn't exist or is read protected"; + break; + + /* Success */ + case HTAA_OK: + return "AA: Access should be ok but something went wrong"; + break; + + case HTAA_OK_GATEWAY: + return "AA check bypassed (gatewaying) but something went wrong"; + break; + + /* Others */ + default: + return "Access denied -- unable to specify reason (bug)"; + + } /* switch */ +} + + +PRIVATE char *status_name ARGS1(HTAAFailReasonType, reason) +{ + switch (HTAAFailReason) { + + /* 401 cases */ + case HTAA_NO_AUTH: + return "NO-AUTHENTICATION"; + break; + case HTAA_NOT_MEMBER: + return "NOT-AUTHORIZED"; + break; + + /* 403 cases */ + case HTAA_BY_RULE: + return "FORB-RULE"; + break; + case HTAA_IP_MASK: + return "FORB-IP"; + break; + case HTAA_NO_ACL: + return "NO-ACL-FILE"; + break; + case HTAA_NO_ENTRY: + return "NO-ACL-ENTRY"; + break; + case HTAA_SETUP_ERROR: + return "SETUP-ERROR"; + break; + case HTAA_DOTDOT: + return "SLASH-DOT-DOT"; + break; + case HTAA_HTBIN: + return "HTBIN-OFF"; + break; + + /* 404 cases */ + case HTAA_NOT_FOUND: + return "NOT-FOUND"; + break; + + /* Success */ + case HTAA_OK: + return "OK"; + break; + case HTAA_OK_GATEWAY: + return "OK-GATEWAY"; + break; + + /* Others */ + default: + return "SERVER-BUG"; + } /* switch */ +} + + +/* PRIVATE check_uthorization() +** CHECK IF USER IS AUTHORIZED TO ACCESS A FILE +** ON ENTRY: +** pathname is the physical file pathname +** to access. +** method method, e.g. METHOD_GET, METHOD_PUT, ... +** scheme authentication scheme. +** scheme_specifics authentication string (or other +** scheme specific parameters, like +** Kerberos-ticket). +** +** ON EXIT: +** returns HTAA_OK on success. +** Otherwise the reason for failing. +** NOTE: +** This function does not check whether the file +** exists or not -- so the status 404 Not found +** must be returned from somewhere else (this is +** to avoid unnecessary overhead of opening the +** file twice). +*/ +PRIVATE HTAAFailReasonType check_authorization ARGS4(CONST char *, pathname, + HTAAMethod, method, + HTAAScheme, scheme, + char *, scheme_specifics) +{ + HTAAFailReasonType reason; + GroupDef *allowed_groups; + FILE *acl_file = NULL; + HTAAProt *prot = NULL; /* Protection mode */ + + htaa_user = NULL; + + if (!pathname) { + if (TRACE) + fprintf(stderr, "HTAA_checkAuthorization: Forbidden by rule\n"); + return HTAA_BY_RULE; + } + if (TRACE) + fprintf(stderr, "%s `%s' %s %s\n", + "HTAA_checkAuthorization: translated path:", + pathname, "method:", HTAAMethod_name(method)); + + /* + ** Get protection setting (set up by callbacks from rule system) + ** NULL, if not protected by a "protect" rule. + */ + prot = HTAA_getCurrentProtection(); + + /* + ** Check ACL existence + */ + if (!(acl_file = HTAA_openAcl(pathname))) { + if (prot) { /* protect rule, but no ACL */ + if (prot->mask_group) { + /* + ** Only mask enabled, check that + */ + GroupDefList *group_def_list = + HTAA_readGroupFile(HTAssocList_lookup(prot->values, + "group")); + /* + ** Authenticate if authentication info given + */ + if (scheme != HTAA_UNKNOWN && scheme != HTAA_NONE) { + htaa_user = HTAA_authenticate(scheme, + scheme_specifics, + prot); + if (TRACE) + fprintf(stderr, "Authentication returned: %s\n", + (htaa_user ? htaa_user->username + : "NOT-AUTHENTICATED")); + } + HTAA_resolveGroupReferences(prot->mask_group, group_def_list); + reason = HTAA_userAndInetInGroup(prot->mask_group, + htaa_user + ? htaa_user->username : "", + HTClientHost, + NULL); + if (TRACE) { + if (reason != HTAA_OK) + fprintf(stderr, "%s %s %s %s\n", + "HTAA_checkAuthorization: access denied", + "by mask (no ACL, only Protect rule)", + "host", HTClientHost); + else + fprintf(stderr, "%s %s %s %s\n", + "HTAA_checkAuthorization: request from", + HTClientHost, + "accepted by only mask match (no ACL, only", + "Protect rule, and only mask enabled)"); + } + return reason; + } + else { /* 403 Forbidden */ + if (TRACE) + fprintf(stderr, "%s %s\n", + "HTAA_checkAuthorization: Protected, but", + "no mask group nor ACL -- forbidden"); + return HTAA_NO_ACL; + } + } + else { /* No protect rule and no ACL => OK 200 */ + if (TRACE) + fprintf(stderr, "HTAA_checkAuthorization: %s\n", + "no protect rule nor ACL -- ok\n"); + return HTAA_OK; + } + } + + /* + ** Now we know that ACL exists + */ + if (!prot) { /* Not protected by "protect" rule */ + if (TRACE) + fprintf(stderr, "HTAA_checkAuthorization: default protection\n"); + prot = HTAA_getDefaultProtection(); /* Also sets current protection */ + + if (!prot) { /* @@ Default protection not set ?? */ + if (TRACE) + fprintf(stderr, "%s %s\n", + "HTAA_checkAuthorization: default protection", + "not set (internal server error)!!"); + return HTAA_SETUP_ERROR; + } + } + + /* + ** Now we know that document is protected and ACL exists. + ** Check against ACL entry. + */ + { + GroupDefList *group_def_list = + HTAA_readGroupFile(HTAssocList_lookup(prot->values, "group")); + + /* + ** Authenticate now that we know protection mode + */ + if (scheme != HTAA_UNKNOWN && scheme != HTAA_NONE) { + htaa_user = HTAA_authenticate(scheme, + scheme_specifics, + prot); + if (TRACE) + fprintf(stderr, "Authentication returned: %s\n", + (htaa_user + ? htaa_user->username : "NOT-AUTHENTICATED")); + } + /* + ** Check mask group + */ + if (prot->mask_group) { + HTAA_resolveGroupReferences(prot->mask_group, group_def_list); + reason=HTAA_userAndInetInGroup(prot->mask_group, + htaa_user ? htaa_user->username : "", + HTClientHost, + NULL); + if (reason != HTAA_OK) { + if (TRACE) + fprintf(stderr, "%s %s %s\n", + "HTAA_checkAuthorization: access denied", + "by mask, host:", HTClientHost); + return reason; + } + else { + if (TRACE) + fprintf(stderr, "%s %s %s %s %s\n", + "HTAA_checkAuthorization: request from", + HTClientHost, + "accepted by just mask group match", + "(no ACL, only Protect rule, and only", + "mask enabled)"); + /* And continue authorization checking */ + } + } + /* + ** Get ACL entries; get first one first, the loop others + ** Remember, allowed_groups is automatically freed by + ** HTAA_getAclEntry(). + */ + allowed_groups = HTAA_getAclEntry(acl_file, pathname, method); + if (!allowed_groups) { + if (TRACE) + fprintf(stderr, "%s `%s' %s\n", + "No entry for file", pathname, "in ACL"); + HTAA_closeAcl(acl_file); + return HTAA_NO_ENTRY; /* Forbidden -- no entry in the ACL */ + } + else { + do { + HTAA_resolveGroupReferences(allowed_groups, group_def_list); + reason = HTAA_userAndInetInGroup(allowed_groups, + htaa_user + ? htaa_user->username : "", + HTClientHost, + NULL); + if (reason == HTAA_OK) { + HTAA_closeAcl(acl_file); + return HTAA_OK; /* OK */ + } + allowed_groups = HTAA_getAclEntry(acl_file, pathname, method); + } while (allowed_groups); + HTAA_closeAcl(acl_file); + return HTAA_NOT_MEMBER; /* Unauthorized */ + } + } +} + + +/* PUBLIC HTAA_checkAuthorization() +** CHECK IF USER IS AUTHORIZED TO ACCESS A FILE +** ON ENTRY: +** url is the document to be accessed. +** method_name name of the method, e.g. "GET" +** scheme_name authentication scheme name. +** scheme_specifics authentication string (or other +** scheme specific parameters, like +** Kerberos-ticket). +** +** ON EXIT: +** returns status codes uniform with those of HTTP: +** 200 OK if file access is ok. +** 401 Unauthorized if user is not authorized to +** access the file. +** 403 Forbidden if there is no entry for the +** requested file in the ACL. +** +** NOTE: +** This function does not check whether the file +** exists or not -- so the status 404 Not found +** must be returned from somewhere else (this is +** to avoid unnecessary overhead of opening the +** file twice). +** +*/ +PUBLIC int HTAA_checkAuthorization ARGS4(CONST char *, url, + CONST char *, method_name, + CONST char *, scheme_name, + char *, scheme_specifics) +{ + static char *pathname = NULL; + char *local_copy = NULL; + HTAAMethod method = HTAAMethod_enum(method_name); + HTAAScheme scheme = HTAAScheme_enum(scheme_name); + + HTAAFailReason = HTAA_OK; + + /* + ** Translate into absolute pathname, and + ** check for "protect" and "defprot" rules. + */ + FREE(pathname); /* From previous call */ + StrAllocCopy(local_copy, url); + { + char *keywords = strchr(local_copy, '?'); + if (keywords) + *keywords = '\0'; /* Chop off keywords */ + } + HTSimplify(local_copy); /* Remove ".." etc. */ + + /* HTSimplify will leave in a "/../" at the top, which can + ** be a security hole. + */ + if (strstr(local_copy, "/../")) { + if (TRACE) + fprintf(stderr, "HTAA_checkAuthorization: %s (`%s')\n", + "Illegal attempt to use /../", url); + HTAAFailReason = HTAA_DOTDOT; + } + else { + pathname = HTTranslate(local_copy); /* Translate rules even if */ + /* a /htbin call to set up */ + /* protections. */ + if (0 == strncmp(local_copy, "/htbin/", 7)) { + if (!HTBinDir) + HTAAFailReason = HTAA_HTBIN; + else { + char *end = strchr(local_copy+7, '/'); + if (end) + *end = '\0'; + FREE(pathname); + pathname=(char*)malloc(strlen(HTBinDir)+strlen(local_copy)+1); + strcpy(pathname, HTBinDir); + strcat(pathname, local_copy+6); + } + } + + if (!pathname) { /* Forbidden by rule */ + if (TRACE) + fprintf(stderr, "HTAA_checkAuthorization: Forbidden by rule\n"); + HTAAFailReason = HTAA_BY_RULE; + } + else if (HTAAFailReason != HTAA_HTBIN) { + /* pathname != NULL */ + char *access = HTParse(pathname, "", PARSE_ACCESS); + if (!*access || 0 == strcmp(access,"file")) { /*Local file, do AA*/ + if (!HTSecure && 0 != strncmp(local_copy, "/htbin/", 7)) { + char *localname = HTLocalName(pathname); + FREE(pathname); + pathname = localname; + } + HTAAFailReason = check_authorization(pathname, method, + scheme, scheme_specifics); + } + else { /* Not local access */ + HTAAFailReason = HTAA_OK_GATEWAY; + if (TRACE) + fprintf(stderr, + "HTAA_checkAuthorization: %s (%s access)\n", + "Gatewaying -- skipping authorization check", + access); + } + } /* pathname */ + } + FREE(local_copy); + + if (htaa_logfile) { + time(&theTime); + fprintf(htaa_logfile, "%24.24s %s %s %s %s %s\n", + ctime(&theTime), + HTClientHost ? HTClientHost : "local", + method_name, + url, + status_name(HTAAFailReason), + htaa_user && htaa_user->username + ? htaa_user->username : ""); + fflush(htaa_logfile); /* Actually update it on disk */ + if (TRACE) + fprintf(stderr, "Log: %24.24s %s %s %s %s %s\n", + ctime(&theTime), + HTClientHost ? HTClientHost : "local", + method_name, + url, + status_name(HTAAFailReason), + htaa_user && htaa_user->username + ? htaa_user->username : ""); + } + + switch (HTAAFailReason) { + + case HTAA_NO_AUTH: + case HTAA_NOT_MEMBER: + return 401; + break; + + case HTAA_BY_RULE: + case HTAA_IP_MASK: + case HTAA_NO_ACL: + case HTAA_NO_ENTRY: + case HTAA_SETUP_ERROR: + case HTAA_DOTDOT: + case HTAA_HTBIN: + return 403; + break; + + case HTAA_NOT_FOUND: + return 404; + break; + + case HTAA_OK: + case HTAA_OK_GATEWAY: + return 200; + break; + + default: + return 500; + } /* switch */ +} + + +/* PRIVATE compose_scheme_specifics() +** COMPOSE SCHEME-SPECIFIC PARAMETERS +** TO BE SENT ALONG WITH SERVER REPLY +** IN THE WWW-Authenticate: FIELD. +** ON ENTRY: +** scheme is the authentication scheme for which +** parameters are asked for. +** prot protection setup structure. +** +** ON EXIT: +** returns scheme specific parameters in an +** auto-freed string. +*/ +PRIVATE char *compose_scheme_specifics ARGS2(HTAAScheme, scheme, + HTAAProt *, prot) +{ + static char *result = NULL; + + FREE(result); /* From previous call */ + + switch (scheme) { + case HTAA_BASIC: + { + char *realm = HTAssocList_lookup(prot->values, "server"); + result = (char*)malloc(60); + sprintf(result, "realm=\"%s\"", + (realm ? realm : "UNKNOWN")); + return result; + } + break; + + case HTAA_PUBKEY: + { + char *realm = HTAssocList_lookup(prot->values, "server"); + result = (char*)malloc(200); + sprintf(result, "realm=\"%s\", key=\"%s\"", + (realm ? realm : "UNKNOWN"), + "PUBKEY-NOT-IMPLEMENTED"); + return result; + } + break; + default: + return NULL; + } +} + + +/* SERVER PUBLIC HTAA_composeAuthHeaders() +** COMPOSE WWW-Authenticate: HEADER LINES +** INDICATING VALID AUTHENTICATION SCHEMES +** FOR THE REQUESTED DOCUMENT +** ON ENTRY: +** No parameters, but HTAA_checkAuthorization() must +** just before have failed because a wrong (or none) +** authentication scheme was used. +** +** ON EXIT: +** returns a buffer containing all the WWW-Authenticate: +** fields including CRLFs (this buffer is auto-freed). +** NULL, if authentication won't help in accessing +** the requested document. +** +*/ +PUBLIC char *HTAA_composeAuthHeaders NOARGS +{ + static char *result = NULL; + HTAAScheme scheme; + char *scheme_name; + char *scheme_params; + HTAAProt *prot = HTAA_getCurrentProtection(); + + if (!prot) { + if (TRACE) + fprintf(stderr, "%s %s\n", + "HTAA_composeAuthHeaders: Document not protected", + "-- why was this function called??"); + return NULL; + } + else if (TRACE) + fprintf(stderr, "HTAA_composeAuthHeaders: for file `%s'\n", + prot->filename); + + FREE(result); /* From previous call */ + if (!(result = (char*)malloc(4096))) /* @@ */ + outofmem(__FILE__, "HTAA_composeAuthHeaders"); + *result = '\0'; + + for (scheme=0; scheme < HTAA_MAX_SCHEMES; scheme++) { + if (-1 < HTList_indexOf(prot->valid_schemes, (void*)scheme)) { + if ((scheme_name = HTAAScheme_name(scheme))) { + scheme_params = compose_scheme_specifics(scheme,prot); + strcat(result, "WWW-Authenticate: "); + strcat(result, scheme_name); + if (scheme_params) { + strcat(result, " "); + strcat(result, scheme_params); + } + strcat(result, "\r\n"); + } /* scheme name found */ + else if (TRACE) + fprintf(stderr, "HTAA_composeAuthHeaders: %s %d\n", + "No name found for scheme number", scheme); + } /* scheme valid for requested document */ + } /* for every scheme */ + + return result; +} + + +/* PUBLIC HTAA_startLogging() +** START UP ACCESS AUTHORIZATION LOGGING +** ON ENTRY: +** fp is the open log file. +** +*/ +PUBLIC void HTAA_startLogging ARGS1(FILE *, fp) +{ + htaa_logfile = fp; +} + diff --git a/WWW/Library/Implementation/HTAAServ.h b/WWW/Library/Implementation/HTAAServ.h new file mode 100644 index 00000000..aa350a01 --- /dev/null +++ b/WWW/Library/Implementation/HTAAServ.h @@ -0,0 +1,146 @@ +/* SERVER SIDE ACCESS AUTHORIZATION MODULE + + This module is the server side interface to Access Authorization (AA) package. It + contains code only for server. + + Important to know about memory allocation: + + Routines in this module use dynamic allocation, but free automatically all the memory + reserved by them. + + Therefore the caller never has to (and never should) free() any object returned by + these functions. + + Therefore also all the strings returned by this package are only valid until the next + call to the same function is made. This approach is selected, because of the nature of + access authorization: no string returned by the package needs to be valid longer than + until the next call. + + This also makes it easy to plug the AA package in: you don't have to ponder whether to + free()something here or is it done somewhere else (because it is always done somewhere + else). + + The strings that the package needs to store are copied so the original strings given as + parameters to AA functions may be freed or modified with no side effects. + + Also note:The AA package does not free() anything else than what it has itself + allocated. + + */ + +#ifndef HTAASERV_H +#define HTAASERV_H + +#ifndef HTUTILS_H +#include "HTUtils.h" /* BOOL, PARAMS, ARGS */ +#endif /* HTUTILS_H */ +/*#include <stdio.h> included by HTUtils.h -- FM *//* FILE */ +#include "HTRules.h" /* This module interacts with rule system */ +#include "HTAAUtil.h" /* Common parts of AA */ +#include "HTAuth.h" /* Authentication */ + + +#ifdef SHORT_NAMES +#define HTAAstMs HTAA_statusMessage +#define HTAAchAu HTAA_checkAuthorization +#define HTAAcoAH HTAA_composeAuthHeaders +#define HTAAsLog HTAA_startLogging +#endif /*SHORT_NAMES*/ + +/* + +Check Access Authorization + + HTAA_checkAuthorization() is the main access authorization function. + + */ + +/* PUBLIC HTAA_checkAuthorization() +** CHECK IF USER IS AUTHORIZED TO ACCESS A FILE +** ON ENTRY: +** url is the document to be accessed. +** method_name name of the method, e.g. "GET" +** scheme_name authentication scheme name. +** scheme_specifics authentication string (or other +** scheme specific parameters, like +** Kerberos-ticket). +** +** ON EXIT: +** returns status codes uniform with those of HTTP: +** 200 OK if file access is ok. +** 401 Unauthorized if user is not authorized to +** access the file. +** 403 Forbidden if there is no entry for the +** requested file in the ACL. +** +** NOTE: +** This function does not check whether the file +** exists or not -- so the status 404 Not found +** must be returned from somewhere else (this is +** to avoid unnecessary overhead of opening the +** file twice). +** +*/ +PUBLIC int HTAA_checkAuthorization PARAMS((CONST char * url, + CONST char * method_name, + CONST char * scheme_name, + char * scheme_specifics)); +/* + +Compose Status Line Message + + */ + +/* SERVER PUBLIC HTAA_statusMessage() +** RETURN A STRING EXPLAINING ACCESS +** AUTHORIZATION FAILURE +** (Can be used in server reply status line +** with 401/403 replies.) +** ON EXIT: +** returns a string containing the error message +** corresponding to internal HTAAFailReason. +*/ +PUBLIC char *HTAA_statusMessage NOPARAMS; +/* + +Compose "Authenticate:" Header Lines for Server Reply + + */ + +/* SERVER PUBLIC HTAA_composeAuthHeaders() +** COMPOSE WWW-Authenticate: HEADER LINES +** INDICATING VALID AUTHENTICATION SCHEMES +** FOR THE REQUESTED DOCUMENT +** ON ENTRY: +** No parameters, but HTAA_checkAuthorization() must +** just before have failed because a wrong (or none) +** authentication scheme was used. +** +** ON EXIT: +** returns a buffer containing all the WWW-Authenticate: +** fields including CRLFs (this buffer is auto-freed). +** NULL, if authentication won't help in accessing +** the requested document. +*/ +PUBLIC char *HTAA_composeAuthHeaders NOPARAMS; +/* + +Start Access Authorization Logging + + */ + +/* PUBLIC HTAA_startLogging() +** START UP ACCESS AUTHORIZATION LOGGING +** ON ENTRY: +** fp is the open log file. +** +*/ +PUBLIC void HTAA_startLogging PARAMS((FILE * fp)); +/* + + */ + +#endif /* NOT HTAASERV_H */ +/* + + End of file HTAAServ.h. */ diff --git a/WWW/Library/Implementation/HTAAUtil.c b/WWW/Library/Implementation/HTAAUtil.c new file mode 100644 index 00000000..c1af8c24 --- /dev/null +++ b/WWW/Library/Implementation/HTAAUtil.c @@ -0,0 +1,640 @@ + +/* MODULE HTAAUtil.c +** COMMON PARTS OF ACCESS AUTHORIZATION MODULE +** FOR BOTH SERVER AND BROWSER +** +** IMPORTANT: +** Routines in this module use dynamic allocation, but free +** automatically all the memory reserved by them. +** +** Therefore the caller never has to (and never should) +** free() any object returned by these functions. +** +** Therefore also all the strings returned by this package +** are only valid until the next call to the same function +** is made. This approach is selected, because of the nature +** of access authorization: no string returned by the package +** needs to be valid longer than until the next call. +** +** This also makes it easy to plug the AA package in: +** you don't have to ponder whether to free() something +** here or is it done somewhere else (because it is always +** done somewhere else). +** +** The strings that the package needs to store are copied +** so the original strings given as parameters to AA +** functions may be freed or modified with no side effects. +** +** The AA package does not free() anything else than what +** it has itself allocated. +** +** AA (Access Authorization) package means modules which +** names start with HTAA. +** +** AUTHORS: +** AL Ari Luotonen luotonen@dxcern.cern.ch +** MD Mark Donszelmann duns@vxdeop.cern.ch +** +** HISTORY: +** 8 Nov 93 MD (VMS only) Added case insensitive comparison in HTAA_templateCaseMatch +** +** +** BUGS: +** +** +*/ + +#include "HTUtils.h" +#include "tcp.h" /* NETREAD() etc. */ +#include <string.h> +#include "HTAAUtil.h" /* Implemented here */ +#include "HTAssoc.h" /* Assoc list */ +#include "HTTCP.h" +#include "HTAlert.h" + +#ifdef USE_SSL +#ifdef VMS +#include "[-.-.-.refssl]ssl.h" +#else +#include "../../../refssl/ssl.h" +#endif /* VMS */ +PRIVATE SSLHandle * ssl_handle=NULL; /* The SSL handle */ +#endif /* USE_SSL */ + +#include "LYLeaks.h" + +/* PUBLIC HTAAScheme_enum() +** TRANSLATE SCHEME NAME INTO +** A SCHEME ENUMERATION +** +** ON ENTRY: +** name is a string representing the scheme name. +** +** ON EXIT: +** returns the enumerated constant for that scheme. +*/ +PUBLIC HTAAScheme HTAAScheme_enum ARGS1(CONST char*, name) +{ + char *upcased = NULL; + char *cur; + + if (!name) + return HTAA_UNKNOWN; + + StrAllocCopy(upcased, name); + cur = upcased; + while (*cur) { + *cur = TOUPPER(*cur); + cur++; + } + + if (!strncmp(upcased, "NONE", 4)) { + FREE(upcased); + return HTAA_NONE; + } else if (!strncmp(upcased, "BASIC", 5)) { + FREE(upcased); + return HTAA_BASIC; + } else if (!strncmp(upcased, "PUBKEY", 6)) { + FREE(upcased); + return HTAA_PUBKEY; + } else if (!strncmp(upcased, "KERBEROSV4", 10)) { + FREE(upcased); + return HTAA_KERBEROS_V4; + } else if (!strncmp(upcased, "KERBEROSV5", 10)) { + FREE(upcased); + return HTAA_KERBEROS_V5; + } else { + FREE(upcased); + return HTAA_UNKNOWN; + } +} + + +/* PUBLIC HTAAScheme_name() +** GET THE NAME OF A GIVEN SCHEME +** ON ENTRY: +** scheme is one of the scheme enum values: +** HTAA_NONE, HTAA_BASIC, HTAA_PUBKEY, ... +** +** ON EXIT: +** returns the name of the scheme, i.e. +** "None", "Basic", "Pubkey", ... +*/ +PUBLIC char *HTAAScheme_name ARGS1(HTAAScheme, scheme) +{ + switch (scheme) { + case HTAA_NONE: + return "None"; + break; + case HTAA_BASIC: + return "Basic"; + break; + case HTAA_PUBKEY: + return "Pubkey"; + break; + case HTAA_KERBEROS_V4: + return "KerberosV4"; + break; + case HTAA_KERBEROS_V5: + return "KerberosV5"; + break; + case HTAA_UNKNOWN: + return "UNKNOWN"; + break; + default: + return "THIS-IS-A-BUG"; + } +} + + +/* PUBLIC HTAAMethod_enum() +** TRANSLATE METHOD NAME INTO AN ENUMERATED VALUE +** ON ENTRY: +** name is the method name to translate. +** +** ON EXIT: +** returns HTAAMethod enumerated value corresponding +** to the given name. +*/ +PUBLIC HTAAMethod HTAAMethod_enum ARGS1(CONST char *, name) +{ + char tmp[MAX_METHODNAME_LEN+1]; + CONST char *src = name; + char *dest = tmp; + + if (!name) + return METHOD_UNKNOWN; + + while (*src) { + *dest = TOUPPER(*src); + dest++; + src++; + } + *dest = 0; + + if (0==strcmp(tmp, "GET")) + return METHOD_GET; + else if (0==strcmp(tmp, "PUT")) + return METHOD_PUT; + else + return METHOD_UNKNOWN; +} + + +/* PUBLIC HTAAMethod_name() +** GET THE NAME OF A GIVEN METHOD +** ON ENTRY: +** method is one of the method enum values: +** METHOD_GET, METHOD_PUT, ... +** +** ON EXIT: +** returns the name of the scheme, i.e. +** "GET", "PUT", ... +*/ +PUBLIC char *HTAAMethod_name ARGS1(HTAAMethod, method) +{ + switch (method) { + case METHOD_GET: + return "GET"; + break; + case METHOD_PUT: + return "PUT"; + break; + case METHOD_UNKNOWN: + return "UNKNOWN"; + break; + default: + return "THIS-IS-A-BUG"; + } +} + + +/* PUBLIC HTAAMethod_inList() +** IS A METHOD IN A LIST OF METHOD NAMES +** ON ENTRY: +** method is the method to look for. +** list is a list of method names. +** +** ON EXIT: +** returns YES, if method was found. +** NO, if not found. +*/ +PUBLIC BOOL HTAAMethod_inList ARGS2(HTAAMethod, method, + HTList *, list) +{ + HTList *cur = list; + char *item; + + while (NULL != (item = (char*)HTList_nextObject(cur))) { + if (TRACE) + fprintf(stderr, " %s", item); + if (method == HTAAMethod_enum(item)) + return YES; + } + + return NO; /* Not found */ +} + + +/* PUBLIC HTAA_templateMatch() +** STRING COMPARISON FUNCTION FOR FILE NAMES +** WITH ONE WILDCARD * IN THE TEMPLATE +** NOTE: +** This is essentially the same code as in HTRules.c, but it +** cannot be used because it is embedded in between other code. +** (In fact, HTRules.c should use this routine, but then this +** routine would have to be more sophisticated... why is life +** sometimes so hard...) +** +** ON ENTRY: +** template is a template string to match the file name +** agaist, may contain a single wildcard +** character * which matches zero or more +** arbitrary characters. +** filename is the filename (or pathname) to be matched +** agaist the template. +** +** ON EXIT: +** returns YES, if filename matches the template. +** NO, otherwise. +*/ +PUBLIC BOOL HTAA_templateMatch ARGS2(CONST char *, template, + CONST char *, filename) +{ + CONST char *p = template; + CONST char *q = filename; + int m; + + for (; *p && *q && *p == *q; p++, q++) /* Find first mismatch */ + ; /* do nothing else */ + + if (!*p && !*q) + return YES; /* Equally long equal strings */ + else if ('*' == *p) { /* Wildcard */ + p++; /* Skip wildcard character */ + m = strlen(q) - strlen(p); /* Amount to match to wildcard */ + if (m < 0) + return NO; /* No match, filename too short */ + else { /* Skip the matched characters and compare */ + if (strcmp(p, q+m)) + return NO; /* Tail mismatch */ + else + return YES; /* Tail match */ + } + } /* if wildcard */ + else + return NO; /* Length or character mismatch */ +} + + +/* PUBLIC HTAA_templateCaseMatch() +** STRING COMPARISON FUNCTION FOR FILE NAMES +** WITH ONE WILDCARD * IN THE TEMPLATE (Case Insensitive) +** NOTE: +** This is essentially the same code as in HTAA_templateMatch, but +** it compares case insensitive (for VMS). Reason for this routine +** is that HTAA_templateMatch gets called from several places, also +** there where a case sensitive match is needed, so one cannot just +** change the HTAA_templateMatch routine for VMS. +** +** ON ENTRY: +** template is a template string to match the file name +** agaist, may contain a single wildcard +** character * which matches zero or more +** arbitrary characters. +** filename is the filename (or pathname) to be matched +** agaist the template. +** +** ON EXIT: +** returns YES, if filename matches the template. +** NO, otherwise. +*/ +PUBLIC BOOL HTAA_templateCaseMatch ARGS2(CONST char *, template, + CONST char *, filename) +{ + CONST char *p = template; + CONST char *q = filename; + int m; + + /* Find first mismatch */ + for (; *p && *q && TOUPPER(*p) == TOUPPER(*q); p++, q++) + ; /* do nothing else */ + + if (!*p && !*q) + return YES; /* Equally long equal strings */ + else if ('*' == *p) { /* Wildcard */ + p++; /* Skip wildcard character */ + m = strlen(q) - strlen(p); /* Amount to match to wildcard */ + if (m < 0) + return NO; /* No match, filename too short */ + else { /* Skip the matched characters and compare */ + if (strcasecomp(p, q+m)) + return NO; /* Tail mismatch */ + else + return YES; /* Tail match */ + } + } /* if wildcard */ + else + return NO; /* Length or character mismatch */ +} + + +/* PUBLIC HTAA_makeProtectionTemplate() +** CREATE A PROTECTION TEMPLATE FOR THE FILES +** IN THE SAME DIRECTORY AS THE GIVEN FILE +** (Used by server if there is no fancier way for +** it to tell the client, and by browser if server +** didn't send WWW-ProtectionTemplate: field) +** ON ENTRY: +** docname is the document pathname (from URL). +** +** ON EXIT: +** returns a template matching docname, and other files +** files in that directory. +** +** E.g. /foo/bar/x.html => /foo/bar/ * +** ^ +** Space only to prevent it from +** being a comment marker here, +** there really isn't any space. +*/ +PUBLIC char *HTAA_makeProtectionTemplate ARGS1(CONST char *, docname) +{ + char *template = NULL; + char *slash = NULL; + + if (docname) { + StrAllocCopy(template, docname); + slash = strrchr(template, '/'); + if (slash) + slash++; + else + slash = template; + *slash = '\0'; + StrAllocCat(template, "*"); + } + else + StrAllocCopy(template, "*"); + + if (TRACE) + fprintf(stderr, "make_template: made template `%s' for file `%s'\n", + template, docname); + + return template; +} + + +/* +** Skip leading whitespace from *s forward +*/ +#define SKIPWS(s) while (*s==' ' || *s=='\t') s++; + +/* +** Kill trailing whitespace starting from *(s-1) backwords +*/ +#define KILLWS(s) {char *c=s-1; while (*c==' ' || *c=='\t') *(c--)='\0';} + + +/* PUBLIC HTAA_parseArgList() +** PARSE AN ARGUMENT LIST GIVEN IN A HEADER FIELD +** ON ENTRY: +** str is a comma-separated list: +** +** item, item, item +** where +** item ::= value +** | name=value +** | name="value" +** +** Leading and trailing whitespace is ignored +** everywhere except inside quotes, so the following +** examples are equal: +** +** name=value,foo=bar +** name="value",foo="bar" +** name = value , foo = bar +** name = "value" , foo = "bar" +** +** ON EXIT: +** returns a list of name-value pairs (actually HTAssocList*). +** For items with no name, just value, the name is +** the number of order number of that item. E.g. +** "1" for the first, etc. +*/ +PUBLIC HTAssocList *HTAA_parseArgList ARGS1(char *, str) +{ + HTAssocList *assoc_list = HTAssocList_new(); + char *cur = NULL; + char *name = NULL; + int index = 0; + + if (!str) + return assoc_list; + + while (*str) { + SKIPWS(str); /* Skip leading whitespace */ + cur = str; + index++; + + while (*cur && *cur != '=' && *cur != ',') + cur++; /* Find end of name (or lonely value without a name) */ + KILLWS(cur); /* Kill trailing whitespace */ + + if (*cur == '=') { /* Name followed by a value */ + *(cur++) = '\0'; /* Terminate name */ + StrAllocCopy(name, str); + SKIPWS(cur); /* Skip WS leading the value */ + str = cur; + if (*str == '"') { /* Quoted value */ + str++; + cur = str; + while (*cur && *cur != '"') + cur++; + if (*cur == '"') + *(cur++) = '\0'; /* Terminate value */ + /* else it is lacking terminating quote */ + SKIPWS(cur); /* Skip WS leading comma */ + if (*cur == ',') + cur++; /* Skip separating colon */ + } + else { /* Unquoted value */ + while (*cur && *cur != ',') + cur++; + KILLWS(cur); /* Kill trailing whitespace */ + if (*cur == ',') + *(cur++) = '\0'; + /* else *cur already NULL */ + } + } + else { /* No name, just a value */ + if (*cur == ',') + *(cur++) = '\0'; /* Terminate value */ + /* else last value on line (already terminated by NULL) */ + StrAllocCopy(name, "nnn"); /* Room for item order number */ + sprintf(name, "%d", index); /* Item order number for name */ + } + HTAssocList_add(assoc_list, name, str); + str = cur; + } /* while *str */ + + FREE(name); + return assoc_list; +} + + +/************** HEADER LINE READER -- DOES UNFOLDING *************************/ + +#define BUFFER_SIZE 1024 + +PRIVATE char buffer[BUFFER_SIZE + 1]; +PRIVATE char *start_pointer = buffer; +PRIVATE char *end_pointer = buffer; +PRIVATE int in_soc = -1; + +/* PUBLIC HTAA_setupReader() +** SET UP HEADER LINE READER, i.e. give +** the already-read-but-not-yet-processed +** buffer of text to be read before more +** is read from the socket. +** ON ENTRY: +** start_of_headers is a pointer to a buffer containing +** the beginning of the header lines +** (rest will be read from a socket). +** length is the number of valid characters in +** 'start_of_headers' buffer. +** soc is the socket to use when start_of_headers +** buffer is used up. +** ON EXIT: +** returns nothing. +** Subsequent calls to HTAA_getUnfoldedLine() +** will use this buffer first and then +** proceed to read from socket. +*/ +PUBLIC void HTAA_setupReader ARGS4(char *, start_of_headers, + int, length, + void *, handle, + int, soc) +{ + start_pointer = buffer; + if (start_of_headers) { + strncpy(buffer, start_of_headers, length); + buffer[length] = '\0'; + end_pointer = buffer + length; + } + else { + *start_pointer = '\0'; + end_pointer = start_pointer; + } + in_soc = soc; +#ifdef USE_SSL + ssl_handle = (SSLHandle *)handle; +#endif /* USE_SSL */ +} + + +/* PUBLIC HTAA_getUnfoldedLine() +** READ AN UNFOLDED HEADER LINE FROM SOCKET +** ON ENTRY: +** HTAA_setupReader must absolutely be called before +** this function to set up internal buffer. +** +** ON EXIT: +** returns a newly-allocated character string representing +** the read line. The line is unfolded, i.e. +** lines that begin with whitespace are appended +** to current line. E.g. +** +** Field-Name: Blaa-Blaa +** This-Is-A-Continuation-Line +** Here-Is_Another +** +** is seen by the caller as: +** +** Field-Name: Blaa-Blaa This-Is-A-Continuation-Line Here-Is_Another +** +*/ +PUBLIC char *HTAA_getUnfoldedLine NOARGS +{ + char *line = NULL; + char *cur; + int count; + BOOL peek_for_folding = NO; + + if (in_soc < 0) { + if (TRACE) + fprintf(stderr, "%s %s\n", + "HTAA_getUnfoldedLine: buffer not initialized", + "with function HTAA_setupReader()"); + return NULL; + } + + for(;;) { + + /* Reading from socket */ + + if (start_pointer >= end_pointer) {/*Read the next block and continue*/ +#ifdef USE_SSL + if (ssl_handle) + count = SSL_Read(ssl_handle, buffer, BUFFER_SIZE); + else + count = NETREAD(in_soc, buffer, BUFFER_SIZE); +#else + count = NETREAD(in_soc, buffer, BUFFER_SIZE); +#endif /* USE_SSL */ + if (count <= 0) { + in_soc = -1; + return line; + } + start_pointer = buffer; + end_pointer = buffer + count; + *end_pointer = '\0'; +#ifdef NOT_ASCII + cur = start_pointer; + while (cur < end_pointer) { + *cur = TOASCII(*cur); + cur++; + } +#endif /*NOT_ASCII*/ + } + cur = start_pointer; + + + /* Unfolding */ + + if (peek_for_folding) { + if (*cur != ' ' && *cur != '\t') + return line; /* Ok, no continuation line */ + else /* So this is a continuation line, continue */ + peek_for_folding = NO; + } + + + /* Finding end-of-line */ + + while (cur < end_pointer && *cur != '\n') /* Find the end-of-line */ + cur++; /* (or end-of-buffer). */ + + + /* Terminating line */ + + if (cur < end_pointer) { /* So *cur==LF, terminate line */ + *cur = '\0'; /* Overwrite LF */ + if (*(cur-1) == '\r') + *(cur-1) = '\0'; /* Overwrite CR */ + peek_for_folding = YES; /* Check for a continuation line */ + } + + + /* Copying the result */ + + if (line) + StrAllocCat(line, start_pointer); /* Append */ + else + StrAllocCopy(line, start_pointer); /* A new line */ + + start_pointer = cur+1; /* Skip the read line */ + + } /* forever */ +} + + diff --git a/WWW/Library/Implementation/HTAAUtil.h b/WWW/Library/Implementation/HTAAUtil.h new file mode 100644 index 00000000..226f1547 --- /dev/null +++ b/WWW/Library/Implementation/HTAAUtil.h @@ -0,0 +1,361 @@ +/* Utilities for the Authorization parts of libwww + COMMON PARTS OF AUTHORIZATION MODULE TO BOTH SERVER AND BROWSER + + This module is the interface to the common parts of Access Authorization (AA) package + for both server and browser. Important to know about memory allocation: + + Routines in this module use dynamic allocation, but free automatically all the memory + reserved by them. + + Therefore the caller never has to (and never should) free() any object returned by + these functions. + + Therefore also all the strings returned by this package are only valid until the next + call to the same function is made. This approach is selected, because of the nature of + access authorization: no string returned by the package needs to be valid longer than + until the next call. + + This also makes it easy to plug the AA package in: you don't have to ponder whether to + free() something here or is it done somewhere else (because it is always done somewhere + else). + + The strings that the package needs to store are copied so the original strings given as + parameters to AA functions may be freed or modified with no side effects. + + Also note: The AA package does not free() anything else than what it has itself + allocated. + + */ + +#ifndef HTAAUTIL_H +#define HTAAUTIL_H + +#ifndef HTUTILS_H +#include "HTUtils.h" /* BOOL, PARAMS, ARGS */ +#endif /* HTUTILS_H */ +#include "tcp.h" +#include "HTList.h" + +#ifdef SHORT_NAMES +#define HTAASenu HTAAScheme_enum +#define HTAASnam HTAAScheme_name +#define HTAAMenu HTAAMethod_enum +#define HTAAMnam HTAAMethod_name +#define HTAAMinL HTAAMethod_inList +#define HTAAteMa HTAA_templateMatch +#define HTAAmaPT HTAA_makeProtectionTemplate +#define HTAApALi HTAA_parseArgList +#define HTAAsuRe HTAA_setupReader +#define HTAAgUfL HTAA_getUnfoldedLine +#endif /*SHORT_NAMES*/ + + +/* + +Default filenames + + */ +#ifndef PASSWD_FILE +#define PASSWD_FILE "/home2/luotonen/passwd" +#endif + +#ifndef GROUP_FILE +#define GROUP_FILE "/home2/luotonen/group" +#endif + +#define ACL_FILE_NAME ".www_acl" + + +/* +** Numeric constants +*/ +#define MAX_USERNAME_LEN 16 /* @@ Longest allowed username */ +#define MAX_PASSWORD_LEN 3*13 /* @@ Longest allowed password */ + /* (encrypted, so really only 3*8)*/ +#define MAX_METHODNAME_LEN 12 /* @@ Longest allowed method name */ +#define MAX_FIELDNAME_LEN 16 /* @@ Longest field name in */ + /* protection setup file */ +#define MAX_PATHNAME_LEN 80 /* @@ Longest passwd/group file */ + /* patname to allow */ + +/* +** Helpful macros +*/ +#define FREE(x) if (x) {free(x); x = NULL;} + +/* + +Datatype definitions + + HTAASCHEME + + The enumeration HTAAScheme represents the possible authentication schemes used by the + WWW Access Authorization. + + */ + +typedef enum { + HTAA_UNKNOWN, + HTAA_NONE, + HTAA_BASIC, + HTAA_PUBKEY, + HTAA_KERBEROS_V4, + HTAA_KERBEROS_V5, + HTAA_MAX_SCHEMES /* THIS MUST ALWAYS BE LAST! Number of schemes */ +} HTAAScheme; + +/* + + ENUMERATION TO REPRESENT HTTP METHODS + + */ + +typedef enum { + METHOD_UNKNOWN, + METHOD_GET, + METHOD_PUT +} HTAAMethod; + +/* + +Authentication Schemes + + */ + +/* PUBLIC HTAAScheme_enum() +** TRANSLATE SCHEME NAME TO A SCHEME ENUMERATION +** ON ENTRY: +** name is a string representing the scheme name. +** +** ON EXIT: +** returns the enumerated constant for that scheme. +*/ +PUBLIC HTAAScheme HTAAScheme_enum PARAMS((CONST char* name)); + + +/* PUBLIC HTAAScheme_name() +** GET THE NAME OF A GIVEN SCHEME +** ON ENTRY: +** scheme is one of the scheme enum values: +** HTAA_NONE, HTAA_BASIC, HTAA_PUBKEY, ... +** +** ON EXIT: +** returns the name of the scheme, i.e. +** "none", "basic", "pubkey", ... +*/ +PUBLIC char *HTAAScheme_name PARAMS((HTAAScheme scheme)); + +/* + +Methods + + */ + +/* PUBLIC HTAAMethod_enum() +** TRANSLATE METHOD NAME INTO AN ENUMERATED VALUE +** ON ENTRY: +** name is the method name to translate. +** +** ON EXIT: +** returns HTAAMethod enumerated value corresponding +** to the given name. +*/ +PUBLIC HTAAMethod HTAAMethod_enum PARAMS((CONST char * name)); + + +/* PUBLIC HTAAMethod_name() +** GET THE NAME OF A GIVEN METHOD +** ON ENTRY: +** method is one of the method enum values: +** METHOD_GET, METHOD_PUT, ... +** +** ON EXIT: +** returns the name of the scheme, i.e. +** "GET", "PUT", ... +*/ +PUBLIC char *HTAAMethod_name PARAMS((HTAAMethod method)); + + +/* PUBLIC HTAAMethod_inList() +** IS A METHOD IN A LIST OF METHOD NAMES +** ON ENTRY: +** method is the method to look for. +** list is a list of method names. +** +** ON EXIT: +** returns YES, if method was found. +** NO, if not found. +*/ +PUBLIC BOOL HTAAMethod_inList PARAMS((HTAAMethod method, + HTList * list)); +/* + +Match Template Against Filename + + */ + +/* PUBLIC HTAA_templateMatch() +** STRING COMPARISON FUNCTION FOR FILE NAMES +** WITH ONE WILDCARD * IN THE TEMPLATE +** NOTE: +** This is essentially the same code as in HTRules.c, but it +** cannot be used because it is embedded in between other code. +** (In fact, HTRules.c should use this routine, but then this +** routine would have to be more sophisticated... why is life +** sometimes so hard...) +** +** ON ENTRY: +** template is a template string to match the file name +** agaist, may contain a single wildcard +** character * which matches zero or more +** arbitrary characters. +** filename is the filename (or pathname) to be matched +** agaist the template. +** +** ON EXIT: +** returns YES, if filename matches the template. +** NO, otherwise. +*/ +PUBLIC BOOL HTAA_templateMatch PARAMS((CONST char * template, + CONST char * filename)); + + +/* PUBLIC HTAA_templateCaseMatch() +** STRING COMPARISON FUNCTION FOR FILE NAMES +** WITH ONE WILDCARD * IN THE TEMPLATE (Case Insensitive) +** NOTE: +** This is essentially the same code as in HTAA_templateMatch, but +** it compares case insensitive (for VMS). Reason for this routine +** is that HTAA_templateMatch gets called from several places, also +** there where a case sensitive match is needed, so one cannot just +** change the HTAA_templateMatch routine for VMS. +** +** ON ENTRY: +** template is a template string to match the file name +** agaist, may contain a single wildcard +** character * which matches zero or more +** arbitrary characters. +** filename is the filename (or pathname) to be matched +** agaist the template. +** +** ON EXIT: +** returns YES, if filename matches the template. +** NO, otherwise. +*/ +PUBLIC BOOL HTAA_templateCaseMatch PARAMS((CONST char * template, + CONST char * filename)); + + +/* PUBLIC HTAA_makeProtectionTemplate() +** CREATE A PROTECTION TEMPLATE FOR THE FILES +** IN THE SAME DIRECTORY AS THE GIVEN FILE +** (Used by server if there is no fancier way for +** it to tell the client, and by browser if server +** didn't send WWW-ProtectionTemplate: field) +** ON ENTRY: +** docname is the document pathname (from URL). +** +** ON EXIT: +** returns a template matching docname, and other files +** files in that directory. +** +** E.g. /foo/bar/x.html => /foo/bar/ * +** ^ +** Space only to prevent it from +** being a comment marker here, +** there really isn't any space. +*/ +PUBLIC char *HTAA_makeProtectionTemplate PARAMS((CONST char * docname)); +/* + +MIME Argument List Parser + + */ + + +/* PUBLIC HTAA_parseArgList() +** PARSE AN ARGUMENT LIST GIVEN IN A HEADER FIELD +** ON ENTRY: +** str is a comma-separated list: +** +** item, item, item +** where +** item ::= value +** | name=value +** | name="value" +** +** Leading and trailing whitespace is ignored +** everywhere except inside quotes, so the following +** examples are equal: +** +** name=value,foo=bar +** name="value",foo="bar" +** name = value , foo = bar +** name = "value" , foo = "bar" +** +** ON EXIT: +** returns a list of name-value pairs (actually HTAssocList*). +** For items with no name, just value, the name is +** the number of order number of that item. E.g. +** "1" for the first, etc. +*/ +PUBLIC HTList *HTAA_parseArgList PARAMS((char * str)); + +/* + +Header Line Reader + + */ + +/* PUBLIC HTAA_setupReader() +** SET UP HEADER LINE READER, i.e. give +** the already-read-but-not-yet-processed +** buffer of text to be read before more +** is read from the socket. +** ON ENTRY: +** start_of_headers is a pointer to a buffer containing +** the beginning of the header lines +** (rest will be read from a socket). +** length is the number of valid characters in +** 'start_of_headers' buffer. +** soc is the socket to use when start_of_headers +** buffer is used up. +** ON EXIT: +** returns nothing. +** Subsequent calls to HTAA_getUnfoldedLine() +** will use this buffer first and then +** proceed to read from socket. +*/ +PUBLIC void HTAA_setupReader PARAMS((char * start_of_headers, + int length, + void * handle, + int soc)); + + +/* PUBLIC HTAA_getUnfoldedLine() +** READ AN UNFOLDED HEADER LINE FROM SOCKET +** ON ENTRY: +** HTAA_setupReader must absolutely be called before +** this function to set up internal buffer. +** +** ON EXIT: +** returns a newly-allocated character string representing +** the read line. The line is unfolded, i.e. +** lines that begin with whitespace are appended +** to current line. E.g. +** +** Field-Name: Blaa-Blaa +** This-Is-A-Continuation-Line +** Here-Is_Another +** +** is seen by the caller as: +** +** Field-Name: Blaa-Blaa This-Is-A-Continuation-Line Here-Is_Another +** +*/ +PUBLIC char *HTAA_getUnfoldedLine NOPARAMS; + +#endif /* NOT HTAAUTIL_H */ +/* + + End of file HTAAUtil.h. */ diff --git a/WWW/Library/Implementation/HTACL.c b/WWW/Library/Implementation/HTACL.c new file mode 100644 index 00000000..aeafcafd --- /dev/null +++ b/WWW/Library/Implementation/HTACL.c @@ -0,0 +1,221 @@ + +/* MODULE HTACL.c +** ACCESS CONTROL LIST ROUTINES +** +** AUTHORS: +** AL Ari Luotonen luotonen@dxcern.cern.ch +** MD Mark Donszelmann duns@vxdeop.cern.ch +** +** HISTORY: +** 8 Nov 93 MD (VMS only) case insensitive compare reading acl entry, filename +** +** +** BUGS: +** +** +*/ + + +#include "HTUtils.h" + +/*#include <stdio.h> included by HTUtils.h -- FM *//* FILE */ +#include <string.h> + +#include "HTAAFile.h" /* File routines */ +#include "HTGroup.h" /* GroupDef */ +#include "HTACL.h" /* Implemented here */ + +#include "LYLeaks.h" + +/* PUBLIC HTAA_getAclFilename() +** RESOLVE THE FULL PATHNAME OF ACL FILE FOR A GIVEN FILE +** ON ENTRY: +** path is the pathname of the file for which to +** ACL file should be found. +** +** ACL filename is computed by replacing +** the filename by .www_acl in the pathname +** (this is done to a local copy, of course). +** +** ON EXIT: +** returns the absolute pathname of ACL file +** (which is automatically freed next time +** this fuction is called). +*/ +PUBLIC char *HTAA_getAclFilename ARGS1(CONST char *, pathname) +{ + static char * local_copy = NULL; + static char * acl_path = NULL; + char * directory = NULL; + char * filename = NULL; + + StrAllocCopy(local_copy, pathname); /* Also frees local_copy */ + /* from previous call. */ + + directory = local_copy; + filename = strrchr(directory, '/'); + if (!filename) { /* No path in front of filename */ + directory = "."; /* So use current directory */ + filename = local_copy; /* and the pathname itself is the filename */ + } + else { + *filename = '\0'; /* Truncate filename off from directory path */ + filename++; /* and the filename begins from the next character */ + } + + StrAllocCopy(acl_path, directory); /* Also frees acl_path */ + /* from previous call. */ + StrAllocCat(acl_path, "/"); + StrAllocCat(acl_path, ACL_FILE_NAME); + + return acl_path; +} + + +/* PUBLIC HTAA_openAcl() +** OPEN THE ACL FILE FOR THE GIVEN DOCUMENT +** ON ENTRY: +** pathname is the absolute pathname of +** the file to be accessed. +** +** ON EXIT: +** returns the FILE* to open ACL. +** NULL, if ACL not found. +*/ +PUBLIC FILE *HTAA_openAcl ARGS1(CONST char *, pathname) +{ + return fopen(HTAA_getAclFilename(pathname), "r"); +} + + +/* PUBLIC HTAA_closeAcl() +** CLOSE ACL FILE +** ON ENTRY: +** acl_file is Access Control List file to close. +** +** ON EXIT: +** returns nothing. +*/ +PUBLIC void HTAA_closeAcl ARGS1(FILE *, acl_file) +{ + if (acl_file) fclose(acl_file); +} + + +/* PUBLIC HTAA_getAclEntry() +** CONSULT THE ACCESS CONTROL LIST AND +** GIVE A LIST OF GROUPS (AND USERS) +** AUTHORIZED TO ACCESS A GIVEN FILE +** ON ENTRY: +** acl_file is an open ACL file. +** pathname is the absolute pathname of +** the file to be accessed. +** method is the method for which access is wanted. +** +** ALC FILE FORMAT: +** +** template : method, method, ... : group@addr, user, group, ... +** +** The last item is in fact in exactly the same format as +** group definition in group file, i.e. everything that +** follows the 'groupname:' part, +** e.g. +** user, group, user@address, group@address, +** (user,group,...)@(address, address, ...) +** +** ON EXIT: +** returns NULL, if there is no entry for the file in the ACL, +** or ACL doesn't exist. +** If there is, a GroupDef object containing the +** group and user names allowed to access the file +** is returned (this is automatically freed +** next time this function is called). +** IMPORTANT: +** Returns the first entry with matching template and +** method. This function should be called multiple times +** to process all the valid entries (until it returns NULL). +** This is because there can be multiple entries like: +** +** *.html : get,put : ari,timbl,robert +** *.html : get : jim,james,jonathan,jojo +** +** NOTE: +** The returned group definition may well contain references +** to groups defined in group file. Therefore these references +** must be resolved according to that rule file by function +** HTAA_resolveGroupReferences() (group file is read in by +** HTAA_readGroupFile()) and after that access authorization +** can be checked with function HTAA_userAndInetGroup(). +*/ +PUBLIC GroupDef *HTAA_getAclEntry ARGS3(FILE *, acl_file, + CONST char *, pathname, + HTAAMethod, method) +{ + static GroupDef * group_def = NULL; + CONST char * filename; + int len; + char *buf; + + if (!acl_file) return NULL; /* ACL doesn't exist */ + + if (group_def) { + GroupDef_delete(group_def); /* From previous call */ + group_def = NULL; + } + + if (!(filename = strrchr(pathname, '/'))) + filename = pathname; + else filename++; /* Skip slash */ + + len = strlen(filename); + + if (!(buf = (char*)malloc((strlen(filename)+2)*sizeof(char)))) + outofmem(__FILE__, "HTAA_getAuthorizedGroups"); + + while (EOF != HTAAFile_readField(acl_file, buf, len+1)) { +#ifdef VMS + if (HTAA_templateCaseMatch(buf, filename)) { +#else /* not VMS */ + if (HTAA_templateMatch(buf, filename)) { +#endif /* not VMS */ + HTList *methods = HTList_new(); + HTAAFile_readList(acl_file, methods, MAX_METHODNAME_LEN); + if (TRACE) { + fprintf(stderr, + "Filename '%s' matched template '%s', allowed methods:", + filename, buf); + } + if (HTAAMethod_inList(method, methods)) { /* right method? */ + if (TRACE) + fprintf(stderr, " METHOD OK\n"); + HTList_delete(methods); + methods = NULL; + FREE(buf); + group_def = HTAA_parseGroupDef(acl_file); + /* + ** HTAA_parseGroupDef() already reads the record + ** separator so we don't call HTAAFile_nextRec(). + */ + return group_def; + } else if (TRACE) { + fprintf(stderr, " METHOD NOT FOUND\n"); + } + HTList_delete(methods); + methods = NULL; + } /* if template match */ + else { + if (TRACE) { + fprintf(stderr, + "Filename '%s' didn't match template '%s'\n", + filename, buf); + } + } + + HTAAFile_nextRec(acl_file); + } /* while not eof */ + FREE(buf); + + return NULL; /* No entry for requested file */ + /* (or an empty entry). */ +} + diff --git a/WWW/Library/Implementation/HTACL.h b/WWW/Library/Implementation/HTACL.h new file mode 100644 index 00000000..d6ae3c6c --- /dev/null +++ b/WWW/Library/Implementation/HTACL.h @@ -0,0 +1,110 @@ +/* ACCESS CONTROL LIST ROUTINES + + */ + +#ifndef HTACL_H +#define HTACL_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTAAUtil.h" +#include "HTGroup.h" + +#ifdef SHORT_NAMES +#define HTAAgAFn HTAA_getAclFilename +#define HTAAoACL HTAA_openAcl +#define HTAAcACL HTAA_closeAcl +#define HTAAgAEn HTAA_getAclEntry +#endif /* SHORT_NAMES */ + +/* + +Opening Access Control List File + + */ + +/* PUBLIC HTAA_openAcl() +** OPEN THE ACL FILE FOR THE GIVEN DOCUMENT +** ON ENTRY: +** pathname is the absolute pathname of +** the file to be accessed. +** +** ON EXIT: +** returns the FILE* to open ACL. +** NULL, if ACL not found. +*/ +PUBLIC FILE *HTAA_openAcl PARAMS((CONST char * pathname)); + + +/* PUBLIC HTAA_closeAcl() +** CLOSE ACL FILE +** ON ENTRY: +** acl_file is Access Control List file to close. +** +** ON EXIT: +** returns nothing. +*/ +PUBLIC void HTAA_closeAcl PARAMS((FILE * acl_file)); +/* + +Getting ACL Entry + + */ + +/* PUBLIC HTAA_getAclEntry() +** CONSULT THE ACCESS CONTROL LIST AND +** GIVE A LIST OF GROUPS (AND USERS) +** AUTHORIZED TO ACCESS A GIVEN FILE +** ON ENTRY: +** acl_file is an open ACL file. +** pathname is the absolute pathname of +** the file to be accessed. +** method is the method for which access is wanted. +** +** ALC FILE FORMAT: +** +** template : method, method, ... : group@addr, user, group, ... +** +** The last item is in fact in exactly the same format as +** group definition in group file, i.e. everything that +** follows the 'groupname:' part, +** e.g. +** user, group, user@address, group@address, +** (user,group,...)@(address, address, ...) +** +** ON EXIT: +** returns NULL, if there is no entry for the file in the ACL, +** or ACL doesn't exist. +** If there is, a GroupDef object containing the +** group and user names allowed to access the file +** is returned (this is automatically freed +** next time this function is called). +** IMPORTANT: +** Returns the first entry with matching template and +** method. This function should be called multiple times +** to process all the valid entries (until it returns NULL). +** This is because there can be multiple entries like: +** +** *.html : get,put : ari,timbl,robert +** *.html : get : jim,james,jonathan,jojo +** +** NOTE: +** The returned group definition may well contain references +** to groups defined in group file. Therefore these references +** must be resolved according to that rule file by function +** HTAA_resolveGroupReferences() (group file is read in by +** HTAA_readGroupFile()) and after that access authorization +** can be checked with function HTAA_userAndInetGroup(). +*/ +PUBLIC GroupDef *HTAA_getAclEntry PARAMS((FILE * acl_file, + CONST char * pathname, + HTAAMethod method)); +/* + + */ + +#endif /* not HTACL_H */ +/* + + End of file HTACL.h. */ diff --git a/WWW/Library/Implementation/HTAccess.c b/WWW/Library/Implementation/HTAccess.c new file mode 100644 index 00000000..1d9725f5 --- /dev/null +++ b/WWW/Library/Implementation/HTAccess.c @@ -0,0 +1,1199 @@ +/* Access Manager HTAccess.c +** ============== +** +** Authors +** TBL Tim Berners-Lee timbl@info.cern.ch +** JFG Jean-Francois Groff jfg@dxcern.cern.ch +** DD Denis DeLaRoca (310) 825-4580 <CSP1DWD@mvs.oac.ucla.edu> +** FM Foteos Macrides macrides@sci.wfeb.edu +** PDM Danny Mayer mayer@ljo.dec.com +** +** History +** 8 Jun 92 Telnet hopping prohibited as telnet is not secure TBL +** 26 Jun 92 When over DECnet, suppressed FTP, Gopher and News. JFG +** 6 Oct 92 Moved HTClientHost and logfile into here. TBL +** 17 Dec 92 Tn3270 added, bug fix. DD +** 4 Feb 93 Access registration, Search escapes bad chars TBL +** PARAMETERS TO HTSEARCH AND HTLOADRELATIVE CHANGED +** 28 May 93 WAIS gateway explicit if no WAIS library linked in. +** 31 May 94 Added DIRECT_WAIS support for VMS. FM +** 27 Jan 95 Fixed proxy support to use NNTPSERVER for checking +** whether or not to use the proxy server. PDM +** 27 Jan 95 Ensured that proxy service will be overridden for files +** on the local host (because HTLoadFile() doesn't try ftp +** for those) and will substitute ftp for remote files. FM +** 28 Jan 95 Tweeked PDM's proxy override mods to handle port info +** for news and wais URL's. FM +** +** Bugs +** This module assumes that that the graphic object is hypertext, as it +** needs to select it when it has been loaded. A superclass needs to be +** defined which accepts select and select_anchor. +*/ + +#ifdef VMS +#define DIRECT_WAIS +#endif /* VMS */ + +#ifndef DEFAULT_WAIS_GATEWAY +#define DEFAULT_WAIS_GATEWAY "http://www.w3.org:8001" +#endif + +#include "HTUtils.h" +#include "HTAlert.h" +/* Implements: +*/ +#include "HTAccess.h" + +/* Uses: +*/ +#include "HTParse.h" +#include "HTML.h" /* SCW */ + +#ifndef NO_RULES +#include "HTRules.h" +#endif + +/*#include <stdio.h> included by HTUtils.h -- FM */ + +#include "HTList.h" +#include "HText.h" /* See bugs above */ +#include "HTAlert.h" +#include "HTCJK.h" + +#include "LYexit.h" +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +extern HTCJKlang HTCJK; + +/* +** These flags may be set to modify the operation of this module +*/ +PUBLIC char * HTClientHost = NULL; /* Name of remote login host if any */ +PUBLIC FILE * HTlogfile = NULL; /* File to which to output one-liners */ +PUBLIC BOOL HTSecure = NO; /* Disable access for telnet users? */ + +PUBLIC BOOL using_proxy = NO; /* are we using a proxy gateway? */ + +/* +** To generate other things, play with these: +*/ +PUBLIC HTFormat HTOutputFormat = NULL; +PUBLIC HTStream* HTOutputStream = NULL; /* For non-interactive, set this */ + +PRIVATE HTList * protocols = NULL; /* List of registered protocol descriptors */ + +PUBLIC char *use_this_url_instead = NULL; + + +PRIVATE void free_protocols NOARGS +{ + HTList_delete(protocols); + protocols = NULL; +} + +/* Register a Protocol HTRegisterProtocol +** ------------------- +*/ +PUBLIC BOOL HTRegisterProtocol ARGS1( + HTProtocol *, protocol) +{ + if (!protocols) { + protocols = HTList_new(); + atexit(free_protocols); + } + HTList_addObject(protocols, protocol); + return YES; +} + + +/* Register all known protocols +** ---------------------------- +** +** Add to or subtract from this list if you add or remove protocol modules. +** This routine is called the first time the protocol list is needed, +** unless any protocols are already registered, in which case it is not called. +** Therefore the application can override this list. +** +** Compiling with NO_INIT prevents all known protocols from being forced +** in at link time. +*/ +#ifndef NO_INIT +PRIVATE void HTAccessInit NOARGS /* Call me once */ +{ +#ifdef GLOBALREF_IS_MACRO +extern GLOBALREF (HTProtocol, HTTP); +extern GLOBALREF (HTProtocol, HTTPS); +extern GLOBALREF (HTProtocol, HTFile); +extern GLOBALREF (HTProtocol, HTTelnet); +extern GLOBALREF (HTProtocol, HTTn3270); +extern GLOBALREF (HTProtocol, HTRlogin); +#ifndef DECNET +extern GLOBALREF (HTProtocol, HTFTP); +extern GLOBALREF (HTProtocol, HTNews); +extern GLOBALREF (HTProtocol, HTNNTP); +extern GLOBALREF (HTProtocol, HTSNews); +extern GLOBALREF (HTProtocol, HTGopher); +extern GLOBALREF (HTProtocol, HTCSO); +extern GLOBALREF (HTProtocol, HTFinger); +#ifdef DIRECT_WAIS +extern GLOBALREF (HTProtocol, HTWAIS); +#endif /* DIRECT_WAIS */ +#endif /* !DECNET */ +#else +GLOBALREF HTProtocol HTTP, HTTPS, HTFile, HTTelnet, HTTn3270, HTRlogin; +#ifndef DECNET +GLOBALREF HTProtocol HTFTP, HTNews, HTNNTP, HTSNews, HTGopher, HTCSO; +GLOBALREF HTProtocol HTFinger; +#ifdef DIRECT_WAIS +GLOBALREF HTProtocol HTWAIS; +#endif /* DIRECT_WAIS */ +#endif /* !DECNET */ +#endif /* GLOBALREF_IS_MACRO */ + HTRegisterProtocol(&HTTP); + HTRegisterProtocol(&HTTPS); + HTRegisterProtocol(&HTFile); + HTRegisterProtocol(&HTTelnet); + HTRegisterProtocol(&HTTn3270); + HTRegisterProtocol(&HTRlogin); +#ifndef DECNET + HTRegisterProtocol(&HTFTP); + HTRegisterProtocol(&HTNews); + HTRegisterProtocol(&HTNNTP); + HTRegisterProtocol(&HTSNews); + HTRegisterProtocol(&HTGopher); + HTRegisterProtocol(&HTCSO); + HTRegisterProtocol(&HTFinger); +#ifdef DIRECT_WAIS + HTRegisterProtocol(&HTWAIS); +#endif /* DIRECT_WAIS */ +#endif /* !DECNET */ + LYRegisterLynxProtocols(); +} +#endif /* !NO_INIT */ + +/* override_proxy() +** +** Check the no_proxy environment variable to get the list +** of hosts for which proxy server is not consulted. +** +** no_proxy is a comma- or space-separated list of machine +** or domain names, with optional :port part. If no :port +** part is present, it applies to all ports on that domain. +** +** Example: +** no_proxy="cern.ch,some.domain:8001" +** +** Use "*" to override all proxy service: +** no_proxy="*" +*/ +PRIVATE BOOL override_proxy ARGS1(CONST char *, addr) +{ + CONST char * no_proxy = getenv("no_proxy"); + char * p = NULL; + char * host = NULL; + char * access = NULL; + int port = 0; + int h_len = 0; + + /* + * Check for global override. + */ + if (no_proxy) { + if (!strcmp(no_proxy, "*")) + return YES; + } + + /* + * Never proxy file:// URLs if they are on the local host. + * HTLoadFile() will not attempt ftp for those if direct + * access fails. We'll check that first, in case no_proxy + * hasn't been defined. - FM + */ + if (!addr) + return NO; + if (!(host = HTParse(addr, "", PARSE_HOST))) + return NO; + if (!*host) { + FREE(host); + return NO; + } + + if((access = HTParse(addr, "", PARSE_ACCESS))) { + if (0==strcmp("file", access) && + (0==strcmp(host, "localhost") || +#ifdef VMS + 0==strcasecomp(host, HTHostName()))) +#else + 0==strcmp(host, HTHostName()))) +#endif /* VMS */ + { + FREE(host); + FREE(access); + return YES; + } + FREE(access); + } + + if (!no_proxy) { + FREE(host); + return NO; + } + + if (NULL != (p = strchr(host, ':'))) { /* Port specified */ + *p++ = 0; /* Chop off port */ + port = atoi(p); + } else { /* Use default port */ + access = HTParse(addr, "", PARSE_ACCESS); + if (access) { + if (!strcmp(access,"http")) port = 80; + else if (!strcmp(access,"https")) port = 443; + else if (!strcmp(access,"ftp")) port = 21; + else if (!strcmp(access,"gopher")) port = 70; + else if (!strcmp(access,"cso")) port = 105; + else if (!strcmp(access,"news")) port = 119; + else if (!strcmp(access,"snews")) port = 563; + else if (!strcmp(access,"nntp")) port = 119; + else if (!strcmp(access,"wais")) port = 210; + else if (!strcmp(access,"finger")) port = 79; + FREE(access); + } + } + if (!port) + port = 80; /* Default */ + h_len = strlen(host); + + while (*no_proxy) { + CONST char * end; + CONST char * colon = NULL; + int templ_port = 0; + int t_len; + + while (*no_proxy && (WHITE(*no_proxy) || *no_proxy==',')) + no_proxy++; /* Skip whitespace and separators */ + + end = no_proxy; + while (*end && !WHITE(*end) && *end != ',') { /* Find separator */ + if (*end==':') colon = end; /* Port number given */ + end++; + } + + if (colon) { + templ_port = atoi(colon+1); + t_len = colon - no_proxy; + } + else { + t_len = end - no_proxy; + } + + if ((!templ_port || templ_port == port) && + (t_len > 0 && t_len <= h_len && + !strncmp(host + h_len - t_len, no_proxy, t_len))) { + FREE(host); + return YES; + } + if (*end) + no_proxy = end+1; + else + break; + } + + FREE(host); + return NO; +} + + +/* Find physical name and access protocol +** -------------------------------------- +** +** +** On entry, +** addr must point to the fully qualified hypertext reference. +** anchor a pareent anchor with whose address is addr +** +** On exit, +** returns HT_NO_ACCESS Error has occured. +** HT_OK Success +** +*/ +PRIVATE int get_physical ARGS2( + CONST char *, addr, + HTParentAnchor *, anchor) +{ + char * access=NULL; /* Name of access method */ + char * physical=NULL; + char * Server_addr=NULL; + +#ifndef NO_RULES + physical = HTTranslate(addr); + if (!physical) { + return HT_FORBIDDEN; + } + if (anchor->isISMAPScript == TRUE) { + StrAllocCat(physical, "?0,0"); + if(TRACE) + fprintf(stderr,"HTAccess: Appending '?0,0' coordinate pair.\n"); + } + HTAnchor_setPhysical(anchor, physical); + FREE(physical); /* free our copy */ +#else + if (anchor->isISMAPScript == TRUE) { + StrAllocCopy(physical, addr); + StrAllocCat(physical, "?0,0"); + if(TRACE) + fprintf(stderr,"HTAccess: Appending '?0,0' coordinate pair.\n"); + HTAnchor_setPhysical(anchor, physical); + FREE(physical); /* free our copy */ + } else { + HTAnchor_setPhysical(anchor, addr); + } +#endif /* NO_RULES */ + + access = HTParse(HTAnchor_physical(anchor), + "file:", PARSE_ACCESS); + +/* Check whether gateway access has been set up for this +** +** This function can be replaced by the rule system above. +*/ +#define USE_GATEWAYS +#ifdef USE_GATEWAYS + /* + * Make sure the using_proxy variable is false. + */ + using_proxy = NO; + + /* + * News is different, so we need to check the name of the server, + * as well as the default port for selective exclusions. + */ + if (strcasecomp(access, "news") == 0) { + char *host = NULL; + if ((host = HTParse(addr, "", PARSE_HOST))) { + if (!(strchr(host, ':'))) { + StrAllocCopy(Server_addr, "news://"); + StrAllocCat(Server_addr, host); + StrAllocCat(Server_addr, ":119/"); + } + FREE(host); + } else if (getenv("NNTPSERVER")) { + StrAllocCopy(Server_addr, "news://"); + StrAllocCat(Server_addr, (char *)getenv("NNTPSERVER")); + StrAllocCat(Server_addr, ":119/"); + } + } + /* + * Wais also needs checking of the default port for selective exclusions. + */ + else if (strcasecomp(access, "wais") == 0) { + char *host = NULL; + if ((host = HTParse(addr, "", PARSE_HOST))) { + if (!(strchr(host, ':'))) { + StrAllocCopy(Server_addr, "wais://"); + StrAllocCat(Server_addr, host); + StrAllocCat(Server_addr, ":210/"); + } + FREE(host); + } + else + StrAllocCopy(Server_addr, addr); + } + else { + StrAllocCopy(Server_addr, addr); + } + + if(!override_proxy(Server_addr)) { + char * gateway_parameter, *gateway, *proxy; + + /* search for gateways */ + gateway_parameter = (char *)malloc(strlen(access)+20); + if (gateway_parameter == NULL) + outofmem(__FILE__, "HTLoad"); + strcpy(gateway_parameter, "WWW_"); + strcat(gateway_parameter, access); + strcat(gateway_parameter, "_GATEWAY"); + gateway = (char *)getenv(gateway_parameter); /* coerce for decstation */ + + /* search for proxy servers */ + /* + * If we got to here, a file URL is for ftp on a remote host. - FM + */ + if (0==strcmp(access, "file")) + strcpy(gateway_parameter, "ftp"); + else + strcpy(gateway_parameter, access); + strcat(gateway_parameter, "_proxy"); + proxy = (char *)getenv(gateway_parameter); + FREE(gateway_parameter); + + if(TRACE && gateway) + fprintf(stderr,"Gateway found: %s\n",gateway); + if(TRACE && proxy) + fprintf(stderr,"proxy server found: %s\n",proxy); + +#ifndef DIRECT_WAIS + if (!gateway && 0==strcmp(access, "wais")) { + gateway = DEFAULT_WAIS_GATEWAY; + } +#endif /* direct wais */ + + /* proxy servers have precedence over gateway servers */ + if(proxy) { + char * gatewayed=0; + StrAllocCopy(gatewayed,proxy); + /* + * Ensure that the proxy server uses ftp for file URLs. - FM + */ + if (0==strncmp(addr, "file", 4)) { + StrAllocCat(gatewayed, "ftp"); + StrAllocCat(gatewayed, addr+4); + } else + StrAllocCat(gatewayed, addr); + using_proxy = YES; + if (anchor->isISMAPScript == TRUE) + StrAllocCat(gatewayed, "?0,0"); + HTAnchor_setPhysical(anchor, gatewayed); + FREE(gatewayed); + FREE(access); + + access = HTParse(HTAnchor_physical(anchor), + "http:", PARSE_ACCESS); + + } else if (gateway) { + char * path = HTParse(addr, "", + PARSE_HOST + PARSE_PATH + PARSE_PUNCTUATION); + /* Chop leading / off to make host into part of path */ + char * gatewayed = HTParse(path+1, gateway, PARSE_ALL); + FREE(path); + HTAnchor_setPhysical(anchor, gatewayed); + FREE(gatewayed); + FREE(access); + + access = HTParse(HTAnchor_physical(anchor), + "http:", PARSE_ACCESS); + } + } + FREE(Server_addr); +#endif /* use gateways */ + + + /* + * Search registered protocols to find suitable one. + */ + { + int i, n; +#ifndef NO_INIT + if (!protocols) HTAccessInit(); +#endif + n = HTList_count(protocols); + for (i=0; i<n; i++) { + HTProtocol *p = (HTProtocol *)HTList_objectAt(protocols, i); + if (strcmp(p->name, access)==0) { + HTAnchor_setProtocol(anchor, p); + FREE(access); + return (HT_OK); + } + } + } + + FREE(access); + return HT_NO_ACCESS; +} + + +/* Load a document +** --------------- +** +** This is an internal routine, which has an address AND a matching +** anchor. (The public routines are called with one OR the other.) +** +** On entry, +** addr must point to the fully qualified hypertext reference. +** anchor a pareent anchor with whose address is addr +** +** On exit, +** returns <0 Error has occured. +** HT_LOADED Success +** HT_NO_DATA Success, but no document loaded. +** (telnet sesssion started etc) +** +*/ +PRIVATE int HTLoad ARGS4( + CONST char *, addr, + HTParentAnchor *, anchor, + HTFormat, format_out, + HTStream *, sink) +{ + HTProtocol *p; + int status = get_physical(addr, anchor); + if (status == HT_FORBIDDEN) { + return HTLoadError(sink, 500, "Access forbidden by rule"); + } + if (status < 0) + return status; /* Can't resolve or forbidden */ + + p = (HTProtocol *)HTAnchor_protocol(anchor); + anchor->underway = TRUE; /* Hack to deal with caching */ + status= (*(p->load))(HTAnchor_physical(anchor), + anchor, format_out, sink); + anchor->underway = FALSE; + return status; +} + + +/* Get a save stream for a document +** -------------------------------- +*/ +PUBLIC HTStream *HTSaveStream ARGS1(HTParentAnchor *, anchor) +{ + HTProtocol *p = (HTProtocol *)HTAnchor_protocol(anchor); + if (!p) + return NULL; + + return (*p->saveStream)(anchor); + +} + + +/* Load a document - with logging etc +** ---------------------------------- +** +** - Checks or documents already loaded +** - Logs the access +** - Allows stdin filter option +** - Trace ouput and error messages +** +** On Entry, +** anchor is the node_anchor for the document +** full_address The address of the document to be accessed. +** filter if YES, treat stdin as HTML +** +** On Exit, +** returns YES Success in opening document +** NO Failure +** +*/ +PRIVATE BOOL HTLoadDocument ARGS4( + CONST char *, full_address, + HTParentAnchor *, anchor, + HTFormat, format_out, + HTStream*, sink) + +{ + int status; + HText * text; + char * address_to_load = (char *)full_address; + extern char LYforce_no_cache; /* from GridText.c */ + extern char LYoverride_no_cache; /* from LYMainLoop.c */ + extern BOOL HText_hasNoCacheSet PARAMS((HText *text)); /* in GridText.c */ + extern BOOL reloading; + extern char *redirecting_url; + extern BOOL permanent_redirection; + char *cp; + BOOL ForcingNoCache = LYforce_no_cache; + static int redirection_attempts = 0; + +#ifdef DIRED_SUPPORT + extern BOOLEAN lynx_edit_mode; +#endif + + if (TRACE) + fprintf (stderr, "HTAccess: loading document %s\n", address_to_load); + + /* + * Free use_this_url_instead and reset permanent_redirection + * if not done elsewhere. - FM + */ + FREE(use_this_url_instead); + permanent_redirection = FALSE; + + /* + * Make sure some yoyo doesn't send us 'round in circles + * with redirecting URLs that point back to themselves. + * We'll set the HTTP/1.1 limit of 5 redirections per + * requested URL from a user. - FM + */ + if (redirection_attempts > 5) { + redirection_attempts = 0; + HTAlert("Redirection limit of 5 URL's reached."); + return NO; + } + + /* + * If we don't have POST content, check whether this is a previous + * redirecting URL, and keep re-checking until we get to the final + * destination or redirection limit. If we do have POST content, + * we didn't allow permanent redirection, and an interactive user + * will be deciding whether to keep redirecting. - FM + */ + if (!anchor->post_data) { + while ((cp = HTAnchor_physical(anchor)) && + !strncmp(cp, "Location=", 9)) { + DocAddress NewDoc; + + if (TRACE) { + fprintf (stderr, "HTAccess: '%s' is a redirection URL.\n", + anchor->address); + fprintf (stderr, "HTAccess: Redirecting to '%s'\n", cp+9); + } + + /* + * Don't exceed the redirection_attempts limit. - FM + */ + if (++redirection_attempts > 5) { + HTAlert("Redirection limit of 5 URL's reached."); + redirection_attempts = 0; + FREE(use_this_url_instead); + return NO; + } + + /* + * Set up the redirection. - FM + */ + StrAllocCopy(use_this_url_instead, cp+9); + NewDoc.address = use_this_url_instead; + NewDoc.post_data = NULL; + NewDoc.post_content_type = NULL; + NewDoc.isHEAD = anchor->isHEAD; + anchor = (HTParentAnchor *)HTAnchor_findAddress(&NewDoc); + } + } + /* + * If we had previous redirection, go back and check out + * that the URL under the current restrictions. - FM + */ + if (use_this_url_instead) { + FREE(redirecting_url); + return(NO); + } + + /* + * See if we can use an already loaded document. + */ + if (!LYforce_no_cache && (text = (HText *)HTAnchor_document(anchor))) { + /* + * Already loaded. Check it it's OK to use it. - FM + */ + if (LYoverride_no_cache || !HText_hasNoCacheSet(text)) { + if (TRACE) + fprintf(stderr, "HTAccess: Document already in memory.\n"); + HText_select(text); + +#ifdef DIRED_SUPPORT + if (HTAnchor_format(anchor) == WWW_DIRED) + lynx_edit_mode = TRUE; +#endif + redirection_attempts = 0; + return YES; + } else { + reloading = TRUE; + ForcingNoCache = YES; + if (TRACE) + fprintf(stderr, "HTAccess: Auto-reloading document.\n"); + } + } + + /* + * Get the document from the net. If we are auto-reloading, + * the previous rendition will be freed in conjunction with + * loading of the new rendition. - FM + */ + LYforce_no_cache = NO; /* reset after each time through */ + status = HTLoad(address_to_load, anchor, format_out, sink); + if (TRACE) { + fprintf(stderr, "HTAccess: status=%d\n", status); + } + + /* + * Log the access if necessary. + */ + if (HTlogfile) { + time_t theTime; + time(&theTime); + fprintf(HTlogfile, "%24.24s %s %s %s\n", + ctime(&theTime), + HTClientHost ? HTClientHost : "local", + status<0 ? "FAIL" : "GET", + full_address); + fflush(HTlogfile); /* Actually update it on disk */ + if (TRACE) fprintf(stderr, "Log: %24.24s %s %s %s\n", + ctime(&theTime), + HTClientHost ? HTClientHost : "local", + status<0 ? "FAIL" : "GET", + full_address); + } + + /* + * Check out what we received from the net. + */ + if (status == HT_REDIRECTING) { + /* Exported from HTMIME.c, of all places. *//** NO!! - FM **/ + /* + * Doing this via HTMIME.c meant that the redirection cover + * page was already loaded before we learned that we want a + * different URL. Also, changing anchor->address, as Lynx + * was doing, meant we could never again access its hash + * table entry, creating an insolvable memory leak. Instead, + * if we had a 301 status and set permanent_redirection, + * we'll load the new URL in anchor->physical, preceded by a + * token, which we can check to make replacements on subsequent + * access attempts. We'll check recursively, and retrieve the + * final URL if we had multiple redirections to it. If we just + * went to HTLoad now, as Lou originally had this, we couldn't do + * Lynx's security checks and alternate handling of some URL types. + * So, instead, we'll go all the way back to the top of getfile + * in LYGetFile.c when the status is HT_REDIRECTING. This may + * seem bizarre, but it works like a charm! - FM + */ + if (TRACE) { + fprintf(stderr, "HTAccess: '%s' is a redirection URL.\n", + address_to_load); + fprintf(stderr, "HTAccess: Redirecting to '%s'\n", + redirecting_url); + } + /* + * Prevent circular references. + */ + if (strcmp(address_to_load, redirecting_url)) { /* if different */ + /* + * Load token and redirecting url into anchor->physical + * if we had 301 Permanent redirection. HTTP.c does not + * allow this if we have POST content. - FM + */ + if (permanent_redirection) { + StrAllocCopy(anchor->physical, "Location="); + StrAllocCat(anchor->physical, redirecting_url); + } + + /* + * Set up flags before return to getfile. - FM + */ + StrAllocCopy(use_this_url_instead, redirecting_url); + if (ForcingNoCache) + LYforce_no_cache = YES; + ++redirection_attempts; + FREE(redirecting_url); + permanent_redirection = FALSE; + return(NO); + } + ++redirection_attempts; + FREE(redirecting_url); + permanent_redirection = FALSE; + return(YES); + } + + /* + * We did not receive a redirecting URL. - FM + */ + redirection_attempts = 0; + FREE(redirecting_url); + permanent_redirection = FALSE; + + if (status == HT_LOADED) { + if (TRACE) { + fprintf(stderr, "HTAccess: `%s' has been accessed.\n", + full_address); + } + return YES; + } + + if (status == HT_NO_DATA) { + if (TRACE) { + fprintf(stderr, + "HTAccess: `%s' has been accessed, No data left.\n", + full_address); + } + return NO; + } + + if (status == HT_NOT_LOADED) { + if (TRACE) { + fprintf(stderr, + "HTAccess: `%s' has been accessed, No data loaded.\n", + full_address); + } + return NO; + } + + if (status == HT_INTERRUPTED) { + if (TRACE) { + fprintf(stderr, + "HTAccess: `%s' has been accessed, transfer interrupted.\n", + full_address); + } +/* _HTProgress("Data transfer interrupted."); */ + return NO; + } + + if (status <= 0) { /* Failure in accessing a document */ + char *temp = NULL; + StrAllocCopy(temp, "Can't Access `"); + StrAllocCat(temp, full_address); + StrAllocCat(temp, "'"); + _HTProgress(temp); + FREE(temp); + if (TRACE) fprintf(stderr, + "HTAccess: Can't access `%s'\n", full_address); + HTLoadError(sink, 500, "Unable to access document."); + return NO; + } + + /* + * If you get this, then please find which routine is returning + * a positive unrecognised error code! + */ + fprintf(stderr, + "**** HTAccess: socket or file number returned by obsolete load routine!\n"); + fprintf(stderr, + "**** HTAccess: Internal software error. Please mail lynx_dev@sig.net!\n"); + fprintf(stderr, "**** HTAccess: Status returned was: %d\n",status); + exit(-1); + +} /* HTLoadDocument */ + + +/* Load a document from absolute name +** --------------- +** +** On Entry, +** addr The absolute address of the document to be accessed. +** filter if YES, treat document as HTML +** +** On Exit, +** returns YES Success in opening document +** NO Failure +** +** +*/ + +PUBLIC BOOL HTLoadAbsolute ARGS1(CONST DocAddress *,docaddr) +{ + return HTLoadDocument(docaddr->address, + HTAnchor_parent(HTAnchor_findAddress(docaddr)), + HTOutputFormat ? HTOutputFormat : WWW_PRESENT, + HTOutputStream); +} + + +#ifdef NOT_USED_CODE +/* Load a document from absolute name to stream +** -------------------------------------------- +** +** On Entry, +** addr The absolute address of the document to be accessed. +** sink if non-NULL, send data down this stream +** +** On Exit, +** returns YES Success in opening document +** NO Failure +** +** +*/ +PUBLIC BOOL HTLoadToStream ARGS3( + CONST char *, addr, + BOOL, filter, + HTStream *, sink) +{ + return HTLoadDocument(addr, + HTAnchor_parent(HTAnchor_findAddress(addr)), + HTOutputFormat ? HTOutputFormat : WWW_PRESENT, + sink); +} +#endif /* NOT_USED_CODE */ + + +/* Load a document from relative name +** --------------- +** +** On Entry, +** relative_name The relative address of the document +** to be accessed. +** +** On Exit, +** returns YES Success in opening document +** NO Failure +** +** +*/ +PUBLIC BOOL HTLoadRelative ARGS2( + CONST char *, relative_name, + HTParentAnchor *, here) +{ + DocAddress full_address; + BOOL result; + char * mycopy = 0; + char * stripped = 0; + char * current_address = + HTAnchor_address((HTAnchor*)here); + + full_address.address = 0; + full_address.post_data = 0; + full_address.post_content_type = 0; + full_address.isHEAD = here->isHEAD; + + StrAllocCopy(mycopy, relative_name); + + stripped = HTStrip(mycopy); + full_address.address = HTParse(stripped, + current_address, + PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); + result = HTLoadAbsolute(&full_address); + /* + ** If we got redirection, result will be NO, but use_this_url_instead + ** will be set. The calling routine should check both and do whatever + ** is appropriate. - FM + */ + FREE(full_address.address); + FREE(current_address); + FREE(mycopy); /* Memory leak fixed 10/7/92 -- JFG */ + return result; +} + + +/* Load if necessary, and select an anchor +** -------------------------------------- +** +** On Entry, +** destination The child or parenet anchor to be loaded. +** +** On Exit, +** returns YES Success +** NO Failure +** +*/ +PUBLIC BOOL HTLoadAnchor ARGS1(HTAnchor *,destination) +{ + HTParentAnchor * parent; + BOOL loaded = NO; + if (!destination) return NO; /* No link */ + + parent = HTAnchor_parent(destination); + + if (HTAnchor_document(parent) == NULL) { /* If not alread loaded */ + /* TBL 921202 */ + + BOOL result; + char * address = HTAnchor_address((HTAnchor*) parent); + result = HTLoadDocument(address, parent, + HTOutputFormat ? HTOutputFormat : WWW_PRESENT, + HTOutputStream); + FREE(address); + if (!result) return NO; + loaded = YES; + } + + { + HText *text = (HText*)HTAnchor_document(parent); + if (destination != (HTAnchor *)parent) { /* If child anchor */ + HText_selectAnchor(text, + (HTChildAnchor*)destination); /* Double display? @@ */ + } else { + if (!loaded) HText_select(text); + } + } + return YES; + +} /* HTLoadAnchor */ + + +/* Search +** ------ +** Performs a keyword search on word given by the user. Adds the keyword to +** the end of the current address and attempts to open the new address. +** +** On Entry, +** *keywords space-separated keyword list or similar search list +** here is anchor search is to be done on. +*/ +PRIVATE char hex(i) + int i; +{ + char * hexchars = "0123456789ABCDEF"; + return hexchars[i]; +} + +PUBLIC BOOL HTSearch ARGS2( + CONST char *, keywords, + HTParentAnchor *, here) +{ + +#define acceptable \ +"1234567890abcdefghijlkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_" + + char *q, *u; + CONST char * p, *s, *e; /* Pointers into keywords */ + char * address = 0; + BOOL result; + char * escaped = malloc(strlen(keywords)*3+1); + static CONST BOOL isAcceptable[96] = + + /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + { 0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0, /* 2x !"#$%&'()*+,-./ */ + 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4x @ABCDEFGHIJKLMNO */ + 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* 5X PQRSTUVWXYZ[\]^_ */ + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6x `abcdefghijklmno */ + 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 }; /* 7X pqrstuvwxyz{\}~ DEL */ + + if (escaped == NULL) + outofmem(__FILE__, "HTSearch"); + + StrAllocCopy(address, here->isIndexAction); + + /* + * Convert spaces to + and hex escape unacceptable characters. + */ + for(s=keywords; *s && WHITE(*s); s++) /*scan */ ; /* Skip white space */ + for(e = s + strlen(s); e>s && WHITE(*(e-1)) ; e--); /* Skip trailers */ + for(q=escaped, p=s; p<e; p++) { /* scan stripped field */ + unsigned char c = (unsigned char)TOASCII(*p); + if (WHITE(*p)) { + *q++ = '+'; + } else if (HTCJK != NOCJK) { + *q++ = *p; + } else if (c>=32 && c<=(unsigned char)127 && isAcceptable[c-32]) { + *q++ = *p; /* 930706 TBL for MVS bug */ + } else { + *q++ = '%'; + *q++ = hex((int)(c >> 4)); + *q++ = hex((int)(c & 15)); + } + } /* Loop over string */ + + *q=0; + /* terminate escaped sctring */ + u=strchr(address, '?'); /* Find old search string */ + if (u) *u = 0; /* Chop old search off */ + + StrAllocCat(address, "?"); + StrAllocCat(address, escaped); + FREE(escaped); + result = HTLoadRelative(address, here); + FREE(address); + + /* + ** If we got redirection, result will be NO, but use_this_url_instead + ** will be set. The calling routine should check both and do whatever + ** is appropriate. Only an http server (not a gopher or wais server) + ** could return redirection. Lynx will go all the way back to its + ** mainloop() and subject a redirecting URL to all of its security and + ** restrictions checks. - FM + */ + return result; +} + + +/* Search Given Indexname +** ------ +** Performs a keyword search on word given by the user. Adds the keyword to +** the end of the current address and attempts to open the new address. +** +** On Entry, +** *keywords space-separated keyword list or similar search list +** *addres is name of object search is to be done on. +*/ +PUBLIC BOOL HTSearchAbsolute ARGS2( + CONST char *, keywords, + CONST char *, indexname) +{ + DocAddress abs_doc; + HTParentAnchor * anchor; + abs_doc.address = (char *)indexname; + abs_doc.post_data = 0; + abs_doc.post_content_type = 0; + abs_doc.isHEAD = FALSE; + + anchor = (HTParentAnchor*) HTAnchor_findAddress(&abs_doc); + return HTSearch(keywords, anchor); +} + +#ifdef NOT_USED_CODE +/* Generate the anchor for the home page +** ------------------------------------- +** +** As it involves file access, this should only be done once +** when the program first runs. +** This is a default algorithm -- browser don't HAVE to use this. +** But consistency betwen browsers is STRONGLY recommended! +** +** Priority order is: +** +** 1 WWW_HOME environment variable (logical name, etc) +** 2 ~/WWW/default.html +** 3 /usr/local/bin/default.html +** 4 http://www.w3.org/default.html +** +*/ +PUBLIC HTParentAnchor * HTHomeAnchor NOARGS +{ + char * my_home_document = NULL; + char * home = (char *)getenv(LOGICAL_DEFAULT); + char * ref; + HTParentAnchor * anchor; + + if (home) { + StrAllocCopy(my_home_document, home); + +/* Someone telnets in, they get a special home. +*/ +#define MAX_FILE_NAME 1024 /* @@@ */ + } else if (HTClientHost) { /* Telnet server */ + FILE * fp = fopen(REMOTE_POINTER, "r"); + char * status; + if (fp) { + my_home_document = (char*) malloc(MAX_FILE_NAME); + status = fgets(my_home_document, MAX_FILE_NAME, fp); + if (!status) { + FREE(my_home_document); + } + fclose(fp); + } + if (!my_home_document) StrAllocCopy(my_home_document, REMOTE_ADDRESS); + } + +#ifdef unix + if (!my_home_document) { + FILE * fp = NULL; + CONST char * home = (CONST char*)getenv("HOME"); + if (home) { + my_home_document = (char *)malloc( + strlen(home)+1+ strlen(PERSONAL_DEFAULT)+1); + if (my_home_document == NULL) + outofmem(__FILE__, "HTLocalName"); + sprintf(my_home_document, "%s/%s", home, PERSONAL_DEFAULT); + fp = fopen(my_home_document, "r"); + } + + if (!fp) { + StrAllocCopy(my_home_document, LOCAL_DEFAULT_FILE); + fp = fopen(my_home_document, "r"); + } + if (fp) { + fclose(fp); + } else { + if (TRACE) fprintf(stderr, + "HTBrowse: No local home document ~/%s or %s\n", + PERSONAL_DEFAULT, LOCAL_DEFAULT_FILE); + FREE(my_home_document); + } + } +#endif /* unix */ + ref = HTParse( my_home_document ? my_home_document : + HTClientHost ? REMOTE_ADDRESS + : LAST_RESORT, + "file:", + PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); + if (my_home_document) { + if (TRACE) fprintf(stderr, + "HTAccess: Using custom home page %s i.e. address %s\n", + my_home_document, ref); + FREE(my_home_document); + } + anchor = (HTParentAnchor*) HTAnchor_findAddress(ref); + FREE(ref); + return anchor; +} +#endif /* NOT_USED_CODE */ + diff --git a/WWW/Library/Implementation/HTAccess.h b/WWW/Library/Implementation/HTAccess.h new file mode 100644 index 00000000..f0c7e794 --- /dev/null +++ b/WWW/Library/Implementation/HTAccess.h @@ -0,0 +1,306 @@ +/* HTAccess: Access manager for libwww + ACCESS MANAGER + + This module keeps a list of valid protocol (naming scheme) specifiers with associated + access code. It allows documents to be loaded given various combinations of + parameters. New access protocols may be registered at any time. + + Part of the libwww library . + + */ +#ifndef HTACCESS_H +#define HTACCESS_H + +extern char * use_this_url_instead; + +/* Definition uses: +*/ +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "tcp.h" +#include "HTAnchor.h" +#include "HTFormat.h" + +#ifdef SHORT_NAMES +#define HTClientHost HTClHost +#define HTSearchAbsolute HTSeAbso +#define HTOutputStream HTOuStre +#define HTOutputFormat HTOuForm +#endif + +/* Return codes from load routines: +** +** These codes may be returned by the protocol modules, +** and by the HTLoad routines. +** In general, positive codes are OK and negative ones are bad. +*/ + +#define HT_NO_DATA -9999 /* return code: OK but no data was loaded */ + /* Typically, other app started or forked */ + +/* + +Default Addresses + + These control the home page selection. To mess with these for normal browses is asking + for user confusion. + + */ +#define LOGICAL_DEFAULT "WWW_HOME" /* Defined to be the home page */ + +#ifndef PERSONAL_DEFAULT +#define PERSONAL_DEFAULT "WWW/default.html" /* in home directory */ +#endif +#ifndef LOCAL_DEFAULT_FILE +#define LOCAL_DEFAULT_FILE "/usr/local/lib/WWW/default.html" +#endif +/* If one telnets to a www access point, + it will look in this file for home page */ +#ifndef REMOTE_POINTER +#define REMOTE_POINTER "/etc/www-remote.url" /* can't be file */ +#endif +/* and if that fails it will use this. */ +#ifndef REMOTE_ADDRESS +#define REMOTE_ADDRESS "http://www.w3.org/remote.html" /* can't be file */ +#endif + +/* If run from telnet daemon and no -l specified, use this file: +*/ +#ifndef DEFAULT_LOGFILE +#define DEFAULT_LOGFILE "/usr/adm/www-log/www-log" +#endif + +/* If the home page isn't found, use this file: +*/ +#ifndef LAST_RESORT +#define LAST_RESORT "http://www.w3.org/default.html" +#endif + + +/* + +Flags which may be set to control this module + + */ +#ifdef NOT +extern int HTDiag; /* Flag: load source as plain text */ +#endif /* NOT */ +extern char * HTClientHost; /* Name or number of telnetting host */ +extern FILE * HTlogfile; /* File to output one-liners to */ +extern BOOL HTSecure; /* Disable security holes? */ +extern HTStream* HTOutputStream; /* For non-interactive, set this */ +extern HTFormat HTOutputFormat; /* To convert on load, set this */ + + + +/* + +Load a document from relative name + + ON ENTRY, + + relative_name The relative address of the file to be accessed. + + here The anchor of the object being searched + + ON EXIT, + + returns YES Success in opening file + + NO Failure + + */ +extern BOOL HTLoadRelative PARAMS(( + CONST char * relative_name, + HTParentAnchor * here)); + + +/* + +Load a document from absolute name + + ON ENTRY, + + addr The absolute address of the document to be accessed. + + filter if YES, treat document as HTML + + */ + +/* + + ON EXIT, + + */ + +/* + + returns YES Success in opening document + + NO Failure + + */ +extern BOOL HTLoadAbsolute PARAMS((CONST DocAddress * addr)); + + +/* + +Load a document from absolute name to a stream + + ON ENTRY, + + addr The absolute address of the document to be accessed. + + filter if YES, treat document as HTML + + ON EXIT, + + returns YES Success in opening document + + NO Failure + + Note: This is equivalent to HTLoadDocument + + */ +extern BOOL HTLoadToStream PARAMS((CONST char * addr, BOOL filter, + HTStream * sink)); + + +/* + +Load if necessary, and select an anchor + + ON ENTRY, + + destination The child or parenet anchor to be loaded. + + */ + +/* + + ON EXIT, + + */ + +/* + + returns YES Success + + returns NO Failure + + */ + + + +extern BOOL HTLoadAnchor PARAMS((HTAnchor * destination)); + + +/* + +Make a stream for Saving object back + + ON ENTRY, + + anchor is valid anchor which has previously beeing loaded + + ON EXIT, + + returns 0 if error else a stream to save the object to. + + */ + + +extern HTStream * HTSaveStream PARAMS((HTParentAnchor * anchor)); + + +/* + +Search + + Performs a search on word given by the user. Adds the search words to the end of the + current address and attempts to open the new address. + + ON ENTRY, + + *keywords space-separated keyword list or similar search list + + here The anchor of the object being searched + + */ +extern BOOL HTSearch PARAMS((CONST char * keywords, HTParentAnchor* here)); + + +/* + +Search Given Indexname + + Performs a keyword search on word given by the user. Adds the keyword to the end of + the current address and attempts to open the new address. + + ON ENTRY, + + *keywords space-separated keyword list or similar search list + + *indexname is name of object search is to be done on. + + */ +extern BOOL HTSearchAbsolute PARAMS(( + CONST char * keywords, + CONST char * indexname)); + + +/* + +Register an access method + + */ + +typedef struct _HTProtocol { + char * name; + + int (*load)PARAMS(( + CONST char * full_address, + HTParentAnchor * anchor, + HTFormat format_out, + HTStream* sink)); + + HTStream* (*saveStream)PARAMS((HTParentAnchor * anchor)); + +} HTProtocol; + +extern BOOL HTRegisterProtocol PARAMS((HTProtocol * protocol)); + + +/* + +Generate the anchor for the home page + + */ + +/* + + As it involves file access, this should only be done once when the program first runs. + This is a default algorithm -- browser don't HAVE to use this. + + */ +extern HTParentAnchor * HTHomeAnchor NOPARAMS; + +/* + +Return Host Name + + */ +extern CONST char * HTHostName NOPARAMS; + +/* + +For registering protocols supported by Lynx + +*/ +extern void LYRegisterLynxProtocols NOARGS; + +#endif /* HTACCESS_H */ +/* + + end of HTAccess */ diff --git a/WWW/Library/Implementation/HTAlert.c b/WWW/Library/Implementation/HTAlert.c new file mode 100644 index 00000000..769db178 --- /dev/null +++ b/WWW/Library/Implementation/HTAlert.c @@ -0,0 +1,125 @@ +/* Displaying messages and getting input for LineMode Browser +** ========================================================== +** +** REPLACE THIS MODULE with a GUI version in a GUI environment! +** +** History: +** Jun 92 Created May 1992 By C.T. Barker +** Feb 93 Simplified, portablised TBL +** Sep 93 Corrected 3 bugs in HTConfirm() :-( AL +*/ + + +#include "HTUtils.h" +#include "tcp.h" /* for TOUPPER */ + +#include "HTAlert.h" + +#include <ctype.h> /* for toupper - should be in tcp.h */ +#ifdef VMS +extern char * getpass PARAMS((CONST char *prompt)); +#endif /* VMS */ + +#include "LYLeaks.h" + +PUBLIC void HTAlert ARGS1(CONST char *, Msg) +{ +#ifdef NeXTStep + NXRunAlertPanel(NULL, "%s", NULL, NULL, NULL, Msg); +#else + fprintf(stderr, "WWW Alert: %s\n", Msg); +#endif +} + + +PUBLIC void HTProgress ARGS1(CONST char *, Msg) +{ + fprintf(stderr, " %s ...\n", Msg); +} + + +PUBLIC BOOL HTConfirm ARGS1(CONST char *, Msg) +{ + char Reply[4]; /* One more for terminating NULL -- AL */ + char *URep; + + fprintf(stderr, "WWW: %s (y/n) ", Msg); + /* (y/n) came twice -- AL */ + + fgets(Reply, 4, stdin); /* get reply, max 3 characters */ + URep=Reply; + while (*URep) { + if (*URep == '\n') { + *URep = (char)0; /* Overwrite newline */ + break; + } + *URep=TOUPPER(*URep); + URep++; /* This was previously embedded in the TOUPPER */ + /* call an it became evaluated twice because */ + /* TOUPPER is a macro -- AL */ + } + + if ((strcmp(Reply,"YES")==0) || (strcmp(Reply,"Y")==0)) + return(YES); + else + return(NO); +} + +/* Prompt for answer and get text back +*/ +PUBLIC char * HTPrompt ARGS2(CONST char *, Msg, CONST char *, deflt) +{ + char Tmp[200]; + char * rep = 0; + fprintf(stderr, "WWW: %s", Msg); + if (deflt) fprintf(stderr, " (RETURN for [%s]) ", deflt); + + fgets(Tmp, 200, stdin); + Tmp[strlen(Tmp)-1] = (char)0; /* Overwrite newline */ + + StrAllocCopy(rep, *Tmp ? Tmp : deflt); + return rep; +} + + +/* Prompt for password without echoing the reply +*/ +PUBLIC char * HTPromptPassword ARGS1(CONST char *, Msg) +{ + char *result = NULL; + char *pw = (char*)getpass(Msg ? Msg : "Password: "); + + StrAllocCopy(result, pw); + return result; +} + + +/* Prompt both username and password HTPromptUsernameAndPassword() +** --------------------------------- +** On entry, +** Msg is the prompting message. +** *username and +** *password are char pointers; they are changed +** to point to result strings. +** +** If *username is not NULL, it is taken +** to point to a default value. +** Initial value of *password is +** completely discarded. +** +** On exit, +** *username and *password point to newly allocated +** strings -- original strings pointed to by them +** are NOT freed. +** +*/ +PUBLIC void HTPromptUsernameAndPassword ARGS3(CONST char *, Msg, + char **, username, + char **, password) +{ + if (Msg) + fprintf(stderr, "WWW: %s\n", Msg); + *username = HTPrompt("Username: ", *username); + *password = HTPromptPassword("Password: "); +} + diff --git a/WWW/Library/Implementation/HTAlert.h b/WWW/Library/Implementation/HTAlert.h new file mode 100644 index 00000000..8ce0be53 --- /dev/null +++ b/WWW/Library/Implementation/HTAlert.h @@ -0,0 +1,86 @@ +/* */ + +/* Displaying messages and getting input for WWW Library +** ===================================================== +** +** May 92 Created By C.T. Barker +** Feb 93 Portablized etc TBL +*/ + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "tcp.h" + +/* Display a message and get the input +** +** On entry, +** Msg is the message. +** +** On exit, +** Return value is malloc'd string which must be freed. +*/ +extern char * HTPrompt PARAMS((CONST char * Msg, CONST char * deflt)); + + +/* Display a message, don't wait for input +** +** On entry, +** The input is a list of parameters for printf. +*/ +extern void HTAlert PARAMS((CONST char * Msg)); + + +/* Display a progress message for information (and diagnostics) only +** +** On entry, +** The input is a list of parameters for printf. +*/ +extern void HTProgress PARAMS((CONST char * Msg)); +extern BOOLEAN mustshow; +#define _HTProgress(msg) mustshow = TRUE, HTProgress(msg) + + +/* Display a message, then wait for 'yes' or 'no'. +** +** On entry, +** Takes a list of parameters for printf. +** +** On exit, +** If the user enters 'YES', returns TRUE, returns FALSE +** otherwise. +*/ +extern BOOL HTConfirm PARAMS ((CONST char * Msg)); + + +/* Prompt for password without echoing the reply +*/ +extern char * HTPromptPassword PARAMS((CONST char * Msg)); + +/* Prompt both username and password HTPromptUsernameAndPassword() +** --------------------------------- +** On entry, +** Msg is the prompting message. +** *username and +** *password are char pointers; they are changed +** to point to result strings. +** +** If *username is not NULL, it is taken +** to point to a default value. +** Initial value of *password is +** completely discarded. +** +** On exit, +** *username and *password point to newly allocated +** strings -- original strings pointed to by them +** are NOT freed. +** +*/ +extern void HTPromptUsernameAndPassword PARAMS(( + CONST char * Msg, + char ** username, + char ** password)); + +/* + + */ diff --git a/WWW/Library/Implementation/HTAnchor.c b/WWW/Library/Implementation/HTAnchor.c new file mode 100644 index 00000000..3172f1a2 --- /dev/null +++ b/WWW/Library/Implementation/HTAnchor.c @@ -0,0 +1,999 @@ +/* Hypertext "Anchor" Object HTAnchor.c +** ========================== +** +** An anchor represents a region of a hypertext document which is linked to +** another anchor in the same or a different document. +** +** History +** +** Nov 1990 Written in Objective-C for the NeXT browser (TBL) +** 24-Oct-1991 (JFG), written in C, browser-independant +** 21-Nov-1991 (JFG), first complete version +** +** (c) Copyright CERN 1991 - See Copyright.html +*/ + +#define HASH_SIZE 101 /* Arbitrary prime. Memory/speed tradeoff */ + +#include "HTUtils.h" +#include "tcp.h" +#include <ctype.h> +#include "HTAnchor.h" +#include "HTParse.h" + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +#ifdef NOT_DEFINED +/* + * This is the hashing function used to determine which list in the + * adult_table a parent anchor should be put in. This is a + * much simpler function than the original used. + */ +#define HASH_FUNCTION(cp_address) ((unsigned short int)strlen(cp_address) *\ + (unsigned short int)TOUPPER(*cp_address) % HASH_SIZE) +#endif /* NOT_DEFINED */ +/* + * This is the original function. We'll use it again. - FM + */ +PRIVATE int HASH_FUNCTION ARGS1( + CONST char *, cp_address) +{ + int hash; + unsigned char *p; + + for (p = (unsigned char *)cp_address, hash = 0; *p; p++) + hash = (int) (hash * 3 + (*(unsigned char*)p)) % HASH_SIZE; + + return hash; +} + +typedef struct _HyperDoc Hyperdoc; +#ifdef VMS +struct _HyperDoc { + int junk; /* VMS cannot handle pointers to undefined structs */ +}; +#endif /* VMS */ + +PRIVATE HTList **adult_table = 0; /* Point to table of lists of all parents */ + +/* Creation Methods +** ================ +** +** Do not use "new" by itself outside this module. In order to enforce +** consistency, we insist that you furnish more information about the +** anchor you are creating : use newWithParent or newWithAddress. +*/ + +PRIVATE HTParentAnchor * HTParentAnchor_new NOARGS +{ + HTParentAnchor *newAnchor = + (HTParentAnchor *)calloc(1, sizeof(HTParentAnchor)); /* zero-filled */ + newAnchor->parent = newAnchor; + newAnchor->isISMAPScript = FALSE; /* Lynx appends ?0,0 if TRUE. - FM */ + newAnchor->isHEAD = FALSE; /* HEAD request if TRUE. - FM */ + newAnchor->FileCache = NULL; /* Path to a disk-cached copy. - FM */ + newAnchor->cache_control = NULL; /* Cache-Control. - FM */ + newAnchor->no_cache = FALSE; /* no-cache? - FM */ + newAnchor->content_type = NULL; /* Content-Type. - FM */ + newAnchor->content_language = NULL; /* Content-Language. - FM */ + newAnchor->content_encoding = NULL; /* Compression algorith. - FM */ + newAnchor->RevTitle = NULL; /* TITLE for a LINK with REV. - FM */ + return newAnchor; +} + +PRIVATE HTChildAnchor * HTChildAnchor_new NOARGS +{ + return (HTChildAnchor *)calloc(1, sizeof(HTChildAnchor)); /* zero-filled */ +} + + +#ifdef CASE_INSENSITIVE_ANCHORS +/* Case insensitive string comparison +** ---------------------------------- +** On entry, +** s Points to one string, null terminated +** t points to the other. +** On exit, +** returns YES if the strings are equivalent ignoring case +** NO if they differ in more than their case. +*/ + +PRIVATE BOOL HTEquivalent ARGS2( + CONST char *, s, + CONST char *, t) +{ + if (s && t) { /* Make sure they point to something */ + for (; *s && *t; s++, t++) { + if (TOUPPER(*s) != TOUPPER(*t)) { + return NO; + } + } + return TOUPPER(*s) == TOUPPER(*t); + } else { + return s == t; /* Two NULLs are equivalent, aren't they ? */ + } +} + +#else + +/* Case sensitive string comparison +** ---------------------------------- +** On entry, +** s Points to one string, null terminated +** t points to the other. +** On exit, +** returns YES if the strings are identical or both NULL +** NO if they differ. +*/ + +PRIVATE BOOL HTIdentical ARGS2( + CONST char *, s, + CONST char *, t) +{ + if (s && t) { /* Make sure they point to something */ + for (; *s && *t; s++, t++) { + if (*s != *t) { + return NO; + } + } + return *s == *t; + } else { + return s == t; /* Two NULLs are identical, aren't they ? */ + } +} +#endif /* CASE_INSENSITIVE_ANCHORS */ + + +/* Create new or find old sub-anchor +** --------------------------------- +** +** Me one is for a new anchor being edited into an existing +** document. The parent anchor must already exist. +*/ + +PUBLIC HTChildAnchor * HTAnchor_findChild ARGS2( + HTParentAnchor *, parent, + CONST char *, tag) +{ + HTChildAnchor *child; + HTList *kids; + + if (!parent) { + if (TRACE) + fprintf(stderr, "HTAnchor_findChild called with NULL parent.\n"); + return NULL; + } + if (kids = parent->children) { /* parent has children : search them */ + if (tag && *tag) { /* TBL */ + while (NULL != (child=(HTChildAnchor*)HTList_nextObject(kids))) { +#ifdef CASE_INSENSITIVE_ANCHORS + if (HTEquivalent(child->tag, tag)) { /* Case insensitive */ +#else + if (HTIdentical(child->tag, tag)) { /* Case sensitive - FM */ +#endif /* CASE_INSENSITIVE_ANCHORS */ + if (TRACE) + fprintf(stderr, + "Child anchor %p of parent %p with name `%s' already exists.\n", + (void*)child, (void*)parent, tag); + return child; + } + } + } /* end if tag is void */ + } else { /* parent doesn't have any children yet : create family */ + parent->children = HTList_new(); + } + + child = HTChildAnchor_new(); + if (TRACE) + fprintf(stderr, + "new Anchor %p named `%s' is child of %p\n", + (void*)child, + (int)tag ? tag : (CONST char *)"" , + (void*)parent); /* int for apollo */ + HTList_addObject (parent->children, child); + child->parent = parent; + StrAllocCopy(child->tag, tag); + return child; +} + + +/* Create or find a child anchor with a possible link +** -------------------------------------------------- +** +** Create new anchor with a given parent and possibly +** a name, and possibly a link to a _relatively_ named anchor. +** (Code originally in ParseHTML.h) +*/ +PUBLIC HTChildAnchor * HTAnchor_findChildAndLink ARGS4( + HTParentAnchor *, parent, /* May not be 0 */ + CONST char *, tag, /* May be "" or 0 */ + CONST char *, href, /* May be "" or 0 */ + HTLinkType *, ltype) /* May be 0 */ +{ + HTChildAnchor * child = HTAnchor_findChild(parent, tag); + + if (TRACE) + fprintf(stderr,"Entered HTAnchor_findChildAndLink\n"); + + if (href && *href) { + char *relative_to = HTAnchor_address((HTAnchor *)parent); + DocAddress parsed_doc; + HTAnchor * dest; + + parsed_doc.address = HTParse(href, relative_to, PARSE_ALL); + parsed_doc.post_data = NULL; + parsed_doc.post_content_type = NULL; + parsed_doc.isHEAD = FALSE; + dest = HTAnchor_findAddress(&parsed_doc); + + HTAnchor_link((HTAnchor *) child, dest, ltype); + FREE(parsed_doc.address); + FREE(relative_to); + } + return child; +} + +/* +** Function for freeing the adult hash table. - FM +*/ +PRIVATE void free_adult_table NOARGS +{ + int i_counter; + HTList * HTAp_freeme; + HTParentAnchor * parent; + /* + * Loop through all lists. + */ + for (i_counter = 0; i_counter < HASH_SIZE; i_counter++) { + /* + ** Loop through the list. + */ + while (adult_table[i_counter] != NULL) { + /* + ** Free off items - FM + */ + HTAp_freeme = adult_table[i_counter]; + adult_table[i_counter] = HTAp_freeme->next; + if (HTAp_freeme->object) { + parent = (HTParentAnchor *)HTAp_freeme->object; + HTAnchor_delete(parent); + } + FREE(HTAp_freeme); + } + } + FREE(adult_table); +} + +/* Create new or find old named anchor +** ----------------------------------- +** +** Me one is for a reference which is found in a document, and might +** not be already loaded. +** Note: You are not guaranteed a new anchor -- you might get an old one, +** like with fonts. +*/ + +PUBLIC HTAnchor * HTAnchor_findAddress ARGS1( + CONST DocAddress *, newdoc) +{ + /* Anchor tag specified ? */ + char *tag = HTParse(newdoc->address, "", PARSE_ANCHOR); + + if (TRACE) + fprintf(stderr,"Entered HTAnchor_findAddress\n"); + + /* + ** If the address represents a sub-anchor, we recursively load its + ** parent, then we create a child anchor within that document. + */ + if (*tag) { + DocAddress parsed_doc; + HTParentAnchor * foundParent; + HTChildAnchor * foundAnchor; + + parsed_doc.address = HTParse(newdoc->address, "", + PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION); + parsed_doc.post_data = newdoc->post_data; + parsed_doc.post_content_type = newdoc->post_content_type; + parsed_doc.isHEAD = newdoc->isHEAD; + + foundParent = (HTParentAnchor *) HTAnchor_findAddress (&parsed_doc); + foundAnchor = HTAnchor_findChild (foundParent, tag); + FREE(parsed_doc.address); + FREE(tag); + return (HTAnchor *) foundAnchor; + } else { + /* + ** If the address has no anchor tag, + ** check whether we have this node. + */ + int hash; + HTList * adults; + HTList *grownups; + HTParentAnchor * foundAnchor; + + FREE(tag); + + /* + ** Select list from hash table, + */ + hash = HASH_FUNCTION(newdoc->address); + if (!adult_table) { + adult_table = (HTList**) calloc(HASH_SIZE, sizeof(HTList*)); + atexit(free_adult_table); + } + if (!adult_table[hash]) + adult_table[hash] = HTList_new(); + adults = adult_table[hash]; + + /* + ** Search list for anchor. + */ + grownups = adults; + while (NULL != (foundAnchor = + (HTParentAnchor*)HTList_nextObject(grownups))) { +#ifdef CASE_INSENSITIVE_ANCHORS + if (HTEquivalent(foundAnchor->address, newdoc->address) && + HTEquivalent(foundAnchor->post_data, newdoc->post_data) && + foundAnchor->isHEAD == newdoc->isHEAD) { +#else + if (HTIdentical(foundAnchor->address, newdoc->address) && + HTIdentical(foundAnchor->post_data, newdoc->post_data) && + foundAnchor->isHEAD == newdoc->isHEAD) { +#endif /* CASE_INSENSITIVE_ANCHORS */ + if (TRACE) + fprintf(stderr, + "Anchor %p with address `%s' already exists.\n", + (void*)foundAnchor, newdoc->address); + return (HTAnchor *) foundAnchor; + } + } + + /* + ** Node not found: create new anchor. + */ + foundAnchor = HTParentAnchor_new(); + if (TRACE) + fprintf(stderr, + "New anchor %p has hash %d and address `%s'\n", + (void*)foundAnchor, hash, newdoc->address); + StrAllocCopy(foundAnchor->address, newdoc->address); + if (newdoc->post_data) + StrAllocCopy(foundAnchor->post_data, newdoc->post_data); + if (newdoc->post_content_type) + StrAllocCopy(foundAnchor->post_content_type, + newdoc->post_content_type); + foundAnchor->isHEAD = newdoc->isHEAD; + HTList_addObject (adults, foundAnchor); + return (HTAnchor *) foundAnchor; + } +} + + +/* Delete an anchor and possibly related things (auto garbage collection) +** -------------------------------------------- +** +** The anchor is only deleted if the corresponding document is not loaded. +** All outgoing links from parent and children are deleted, and this anchor +** is removed from the sources list of all its targets. +** We also try to delete the targets whose documents are not loaded. +** If this anchor's source list is empty, we delete it and its children. +*/ + +PRIVATE void deleteLinks ARGS1( + HTAnchor *, me) +{ + /* + * Memory leaks fixed. + * 05-27-94 Lynx 2-3-1 Garrett Arch Blythe + */ + + /* + * Anchor is NULL, do nothing. + */ + if (!me) { + return; + } + + /* + * Unregister me with our mainLink destination anchor's parent. + */ + if (me->mainLink.dest) { + HTParentAnchor *parent = me->mainLink.dest->parent; + + /* + * Remove me from the parent's sources so that the + * parent knows one less anchor is it's dest. + */ + if (!HTList_isEmpty(parent->sources)) { + /* + * Really should only need to deregister once. + */ + HTList_removeObject(parent->sources, (void *)me); + } + + /* + * Test here to avoid calling overhead. + * If the parent has no loaded document, then we should + * tell it to attempt to delete itself. + * Don't do this jass if the anchor passed in is the same + * as the anchor to delete. + * Also, don't do this if the destination parent is our + * parent. + */ + if (!parent->document && + parent != (HTParentAnchor *)me && + me->parent != parent) { + HTAnchor_delete(parent); + } + + /* + * At this point, we haven't a mainLink. Set it to be + * so. + * Leave the HTAtom pointed to by type up to other code to + * handle (reusable, near static). + */ + me->mainLink.dest = NULL; + me->mainLink.type = NULL; + } + + /* + * Check for extra destinations in our links list. + */ + if (!HTList_isEmpty(me->links)) { + HTLink *target; + HTParentAnchor *parent; + + /* + * Take out our extra non mainLinks one by one, calling + * their parents to know that they are no longer + * the destination of me's anchor. + */ + while (target = (HTLink *)HTList_removeLastObject(me->links)) { + parent = target->dest->parent; + if (!HTList_isEmpty(parent->sources)) { + /* + * Only need to tell destination parent + * anchor once. + */ + HTList_removeObject(parent->sources, (void *)me); + } + + /* + * Avoid calling overhead. + * If the parent hasn't a loaded document, then + * we will attempt to have the parent + * delete itself. + * Don't call twice if this is the same anchor + * that we are trying to delete. + * Also, don't do this if we are trying to delete + * our parent. + */ + if (!parent->document && + (HTParentAnchor *)me != parent && + me->parent != parent) { + HTAnchor_delete(parent); + } + } + + /* + * At this point, me no longer has any destination in + * the links list. Get rid of it. + */ + if (me->links) { + HTList_delete(me->links); + me->links = NULL; + } + } + + /* + * Catch in case links list exists but nothing in it. + */ + if (me->links) { + HTList_delete(me->links); + me->links = NULL; + } +} + +PUBLIC BOOL HTAnchor_delete ARGS1( + HTParentAnchor *, me) +{ + /* + * Memory leaks fixed. + * 05-27-94 Lynx 2-3-1 Garrett Arch Blythe + */ + HTList *cur; + HTChildAnchor *child; + + /* + * Do nothing if nothing to do. + */ + if (!me) { + return(NO); + } + + /* + * Don't delete if document is loaded or being loaded. + */ + if (me->document || me->underway) { + return(NO); + } + + /* + * Recursively try to delete destination anchors of this parent. + * In any event, this will tell all destination anchors that we + * no longer consider them a destination. + */ + deleteLinks((HTAnchor *)me); + + /* + * There are still incoming links to this one (we are the + * destination of another anchor). + * Don't actually delete this anchor, but children are OK to + * delete their links. + */ + if (!HTList_isEmpty(me->sources)) { + /* + * Delete all outgoing links from children, do not + * delete the children, though. + */ + if (!HTList_isEmpty(me->children)) { + cur = me->children; + while (child = (HTChildAnchor *)HTList_nextObject(cur)) { + if (child != NULL) { + deleteLinks((HTAnchor *)child); + } + } + } + + /* + * Can't delete parent, still have sources. + */ + return(NO); + } + + /* + * No more incoming links : kill everything + * First, recursively delete children and their links. + */ + if (!HTList_isEmpty(me->children)) { + while (child = (HTChildAnchor *)HTList_removeLastObject( + me->children)) { + if (child) { + deleteLinks((HTAnchor *)child); + if (child->tag) { + FREE(child->tag); + } + FREE(child); + } + } + } + + /* + * Delete our empty list of children. + */ + if (me->children) { + HTList_delete(me->children); + me->children = NULL; + } + + /* + * Delete our empty list of sources. + */ + if (me->sources) { + HTList_delete(me->sources); + me->sources = NULL; + } + + /* + * Delete the methods list. + */ + if (me->methods) { + /* + * Leave what the methods point to up in memory for + * other code (near static stuff). + */ + HTList_delete(me->methods); + me->methods = NULL; + } + + /* + * Free up all allocated members. + */ + FREE(me->charset); + FREE(me->isIndexAction); + FREE(me->isIndexPrompt); + FREE(me->title); + FREE(me->physical); + FREE(me->post_data); + FREE(me->post_content_type); + FREE(me->owner); + FREE(me->RevTitle); + if (me->FileCache) { + FILE *fd; + if (fd = fopen(me->FileCache, "r")) { + fclose(fd); + remove(me->FileCache); + } + FREE(me->FileCache); + } + FREE(me->cache_control); + FREE(me->content_type); + FREE(me->content_language); + FREE(me->content_encoding); + + /* + * Remove ourselves from the hash table's list. + */ + if (adult_table) { + unsigned short int usi_hash = HASH_FUNCTION(me->address); + + if (adult_table[usi_hash]) { + HTList_removeObject(adult_table[usi_hash], (void *)me); + } + } + + /* + * Original code wanted a way to clean out the HTFormat if no + * longer needed (ref count?). I'll leave it alone since + * those HTAtom objects are a little harder to know where + * they are being referenced all at one time. (near static) + */ + + /* + * Free the address. + */ + FREE(me->address); + + /* + * Finally, kill the parent anchor passed in. + */ + FREE(me); + + return(YES); +} + + +/* Move an anchor to the head of the list of its siblings +** ------------------------------------------------------ +** +** This is to ensure that an anchor which might have already existed +** is put in the correct order as we load the document. +*/ + +PUBLIC void HTAnchor_makeLastChild ARGS1( + HTChildAnchor *, me) +{ + if (me->parent != (HTParentAnchor *) me) { /* Make sure it's a child */ + HTList * siblings = me->parent->children; + HTList_removeObject (siblings, me); + HTList_addObject (siblings, me); + } +} + +/* Data access functions +** --------------------- +*/ + +PUBLIC HTParentAnchor * HTAnchor_parent ARGS1( + HTAnchor *, me) +{ + return me ? me->parent : NULL; +} + +PUBLIC void HTAnchor_setDocument ARGS2( + HTParentAnchor *, me, + HyperDoc *, doc) +{ + if (me) + me->document = doc; +} + +PUBLIC HyperDoc * HTAnchor_document ARGS1( + HTParentAnchor *, me) +{ + return me ? me->document : NULL; +} + + +/* We don't want code to change an address after anchor creation... yet ? +PUBLIC void HTAnchor_setAddress ARGS2( + HTAnchor *, me, + char *, addr) +{ + if (me) + StrAllocCopy (me->parent->address, addr); +} +*/ + +PUBLIC char * HTAnchor_address ARGS1( + HTAnchor *, me) +{ + char *addr = NULL; + + if (me) { + if (((HTParentAnchor *)me == me->parent) || + !((HTChildAnchor *)me)->tag) { /* it's an adult or no tag */ + StrAllocCopy(addr, me->parent->address); + } else { /* it's a named child */ + addr = malloc(2 + + strlen(me->parent->address) + + strlen(((HTChildAnchor *)me)->tag)); + if (addr == NULL) + outofmem(__FILE__, "HTAnchor_address"); + sprintf(addr, "%s#%s", + me->parent->address, ((HTChildAnchor *)me)->tag); + } + } + return addr; +} + +PUBLIC void HTAnchor_setFormat ARGS2( + HTParentAnchor *, me, + HTFormat, form) +{ + if (me) + me->format = form; +} + +PUBLIC HTFormat HTAnchor_format ARGS1( + HTParentAnchor *, me) +{ + return me ? me->format : NULL; +} + +PUBLIC void HTAnchor_setIndex ARGS2( + HTParentAnchor *, me, + char *, address) +{ + if (me) { + me->isIndex = YES; + StrAllocCopy(me->isIndexAction, address); + } +} + +PUBLIC void HTAnchor_setPrompt ARGS2( + HTParentAnchor *, me, + char *, prompt) +{ + if (me) { + StrAllocCopy(me->isIndexPrompt, prompt); + } +} + +PUBLIC BOOL HTAnchor_isIndex ARGS1( + HTParentAnchor *, me) +{ + return me ? me->isIndex : NO; +} + +PUBLIC BOOL HTAnchor_hasChildren ARGS1( + HTParentAnchor *, me) +{ + return me ? ! HTList_isEmpty(me->children) : NO; +} + +/* Title handling +*/ +PUBLIC CONST char * HTAnchor_title ARGS1( + HTParentAnchor *, me) +{ + return me ? me->title : 0; +} + +PUBLIC void HTAnchor_setTitle ARGS2( + HTParentAnchor *, me, + CONST char *, title) +{ + int i; + + if (me) { + StrAllocCopy(me->title, title); + for (i = 0; me->title[i]; i++) { + if ((unsigned char)me->title[i] == 1 || + (unsigned char)me->title[i] == 2) { + me->title[i] = ' '; + } + } + } +} + +PUBLIC void HTAnchor_appendTitle ARGS2( + HTParentAnchor *, me, + CONST char *, title) +{ + int i; + + if (me) { + StrAllocCat(me->title, title); + for (i = 0; me->title[i]; i++) { + if ((unsigned char)me->title[i] == 1 || + (unsigned char)me->title[i] == 2) { + me->title[i] = ' '; + } + } + } +} + +/* Owner handling +*/ +PUBLIC CONST char * HTAnchor_owner ARGS1( + HTParentAnchor *, me) +{ + return (me ? me->owner : 0); +} + +PUBLIC void HTAnchor_setOwner ARGS2( + HTParentAnchor *, me, + CONST char *, owner) +{ + if (me) { + StrAllocCopy(me->owner, owner); + } +} + +/* TITLE handling in LINKs with REV="made" or REV="owner" +*/ +PUBLIC CONST char * HTAnchor_RevTitle ARGS1( + HTParentAnchor *, me) +{ + return (me ? me->RevTitle : 0); +} + +PUBLIC void HTAnchor_setRevTitle ARGS2( + HTParentAnchor *, me, + CONST char *, title) +{ + int i; + + if (me) { + StrAllocCopy(me->RevTitle, title); + for (i = 0; me->RevTitle[i]; i++) { + if ((unsigned char)me->RevTitle[i] == 1 || + (unsigned char)me->RevTitle[i] == 2) { + me->RevTitle[i] = ' '; + } + } + } +} + +/* Link me Anchor to another given one +** ------------------------------------- +*/ + +PUBLIC BOOL HTAnchor_link ARGS3( + HTAnchor *, source, + HTAnchor *, destination, + HTLinkType *, type) +{ + if (!(source && destination)) + return NO; /* Can't link to/from non-existing anchor */ + if (TRACE) + fprintf(stderr, + "Linking anchor %p to anchor %p\n", source, destination); + if (!source->mainLink.dest) { + source->mainLink.dest = destination; + source->mainLink.type = type; + } else { + HTLink * newLink = (HTLink *) calloc (1, sizeof (HTLink)); + if (newLink == NULL) + outofmem(__FILE__, "HTAnchor_link"); + newLink->dest = destination; + newLink->type = type; + if (!source->links) + source->links = HTList_new(); + HTList_addObject (source->links, newLink); + } + if (!destination->parent->sources) + destination->parent->sources = HTList_new(); + HTList_addObject (destination->parent->sources, source); + return YES; /* Success */ +} + + +/* Manipulation of links +** --------------------- +*/ + +PUBLIC HTAnchor * HTAnchor_followMainLink ARGS1( + HTAnchor *, me) +{ + return me->mainLink.dest; +} + +PUBLIC HTAnchor * HTAnchor_followTypedLink ARGS2( + HTAnchor *, me, + HTLinkType *, type) +{ + if (me->mainLink.type == type) + return me->mainLink.dest; + if (me->links) { + HTList *links = me->links; + HTLink *link; + while (NULL != (link=(HTLink*)HTList_nextObject(links))) { + if (link->type == type) { + return link->dest; + } + } + } + return NULL; /* No link of me type */ +} + + +/* Make main link +*/ +PUBLIC BOOL HTAnchor_makeMainLink ARGS2( + HTAnchor *, me, + HTLink *, movingLink) +{ + /* Check that everything's OK */ + if (!(me && HTList_removeObject (me->links, movingLink))) { + return NO; /* link not found or NULL anchor */ + } else { + /* First push current main link onto top of links list */ + HTLink *newLink = (HTLink*) calloc (1, sizeof (HTLink)); + if (newLink == NULL) + outofmem(__FILE__, "HTAnchor_makeMainLink"); + memcpy((void *)newLink, + (CONST char *)&me->mainLink, sizeof (HTLink)); + HTList_addObject (me->links, newLink); + + /* Now make movingLink the new main link, and free it */ + memcpy((void *)&me->mainLink, + (CONST void *)movingLink, sizeof (HTLink)); + FREE(movingLink); + return YES; + } +} + + +/* Methods List +** ------------ +*/ + +PUBLIC HTList * HTAnchor_methods ARGS1( + HTParentAnchor *, me) +{ + if (!me->methods) { + me->methods = HTList_new(); + } + return me->methods; +} + +/* Protocol +** -------- +*/ + +PUBLIC void * HTAnchor_protocol ARGS1( + HTParentAnchor *, me) +{ + return me->protocol; +} + +PUBLIC void HTAnchor_setProtocol ARGS2( + HTParentAnchor *, me, + void*, protocol) +{ + me->protocol = protocol; +} + +/* Physical Address +** ---------------- +*/ + +PUBLIC char * HTAnchor_physical ARGS1( + HTParentAnchor *, me) +{ + return me->physical; +} + +PUBLIC void HTAnchor_setPhysical ARGS2( + HTParentAnchor *, me, + char *, physical) +{ + if (me) { + StrAllocCopy(me->physical, physical); + } +} diff --git a/WWW/Library/Implementation/HTAnchor.h b/WWW/Library/Implementation/HTAnchor.h new file mode 100644 index 00000000..e9a137cb --- /dev/null +++ b/WWW/Library/Implementation/HTAnchor.h @@ -0,0 +1,358 @@ +/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTAnchor.html + */ + +/* Hypertext "Anchor" Object HTAnchor.h +** ========================== +** +** An anchor represents a region of a hypertext document which is linked +** to another anchor in the same or a different document. +*/ + +#ifndef HTANCHOR_H +#define HTANCHOR_H + +/* Version 0 (TBL) written in Objective-C for the NeXT browser */ +/* Version 1 of 24-Oct-1991 (JFG), written in C, browser-independant */ + +#include "HTList.h" +#include "HTAtom.h" + +#ifdef SHORT_NAMES +#define HTAnchor_findChild HTAnFiCh +#define HTAnchor_findChildAndLink HTAnFiLi +#define HTAnchor_findAddress HTAnFiAd +#define HTAnchor_delete HTAnDele +#define HTAnchor_makeLastChild HTAnMaLa +#define HTAnchor_parent HTAnPare +#define HTAnchor_setDocument HTAnSeDo +#define HTAnchor_document HTAnDocu +#define HTAnchor_setFormat HTAnSeFo +#define HTAnchor_format HTAnForm +#define HTAnchor_setIndex HTAnSeIn +#define HTAnchor_setPrompt HTAnSePr +#define HTAnchor_isIndex HTAnIsIn +#define HTAnchor_address HTAnAddr +#define HTAnchor_hasChildren HTAnHaCh +#define HTAnchor_title HTAnTitl +#define HTAnchor_setTitle HTAnSeTi +#define HTAnchor_appendTitle HTAnApTi +#define HTAnchor_link HTAnLink +#define HTAnchor_followMainLink HTAnFoMa +#define HTAnchor_followTypedLink HTAnFoTy +#define HTAnchor_makeMainLink HTAnMaMa +#define HTAnchor_setProtocol HTAnSePr +#define HTAnchor_protocol HTAnProt +#define HTAnchor_physical HTAnPhys +#define HTAnchor_setPhysical HTAnSePh +#define HTAnchor_methods HtAnMeth +#endif + +/* Main definition of anchor +** ========================= +*/ + +typedef struct _HyperDoc HyperDoc; /* Ready for forward references */ +typedef struct _HTAnchor HTAnchor; +typedef struct _HTParentAnchor HTParentAnchor; + +/* After definition of HTFormat: */ +#include "HTFormat.h" + +typedef HTAtom HTLinkType; + +typedef struct { + HTAnchor * dest; /* The anchor to which this leads */ + HTLinkType * type; /* Semantics of this link */ +} HTLink; + +struct _HTAnchor { /* Generic anchor : just links */ + HTLink mainLink; /* Main (or default) destination of this */ + HTList * links; /* List of extra links from this, if any */ + /* We separate the first link from the others to avoid too many small mallocs + involved by a list creation. Most anchors only point to one place. */ + HTParentAnchor * parent; /* Parent of this anchor (self for adults) */ +}; + +struct _HTParentAnchor { + /* Common part from the generic anchor structure */ + HTLink mainLink; /* Main (or default) destination of this */ + HTList * links; /* List of extra links from this, if any */ + HTParentAnchor * parent; /* Parent of this anchor (self) */ + + /* ParentAnchor-specific information */ + HTList * children; /* Subanchors of this, if any */ + HTList * sources; /* List of anchors pointing to this, if any */ + HyperDoc * document; /* The document within which this is an anchor */ + char * address; /* Absolute address of this node */ + char * post_data; /* posting data */ + char * post_content_type; /* type of post data */ + HTFormat format; /* Pointer to node format descriptor */ + char * charset; /* Pointer to character set (kludge, for now */ + BOOL isIndex; /* Acceptance of a keyword search */ + char * isIndexAction; /* URL of isIndex server */ + char * isIndexPrompt; /* Prompt for isIndex query */ + char * title; /* Title of document */ + char * owner; /* Owner of document */ + char * RevTitle; /* TITLE in REV="made" or REV="owner" LINK */ + + HTList* methods; /* Methods available as HTAtoms */ + void * protocol; /* Protocol object */ + char * physical; /* Physical address */ + BOOL underway; /* Document about to be attached to it */ + BOOL isISMAPScript; /* Script for clickable image map */ + BOOL isHEAD; /* Document is headers from a HEAD request */ + char * FileCache; /* Path to a disk-cached copy */ + char * cache_control; /* Cache-Control */ + BOOL no_cache; /* Cache-Control, Pragma or META "no-cache"? */ + char * content_type; /* Content-Type */ + char * content_language; /* Content-Language */ + char * content_encoding; /* Compression algorithm */ +}; + +typedef struct { + /* Common part from the generic anchor structure */ + HTLink mainLink; /* Main (or default) destination of this */ + HTList * links; /* List of extra links from this, if any */ + HTParentAnchor * parent; /* Parent of this anchor */ + + /* ChildAnchor-specific information */ + char * tag; /* Address of this anchor relative to parent */ +} HTChildAnchor; + + +/* DocAddress structure is used for loading an absolute anchor with all + * needed information including posting data and post content type. + */ + +typedef struct _DocAddress { + char * address; + char * post_data; + char * post_content_type; + BOOL isHEAD; +} DocAddress; + +/* Create new or find old sub-anchor +** --------------------------------- +** +** This one is for a new anchor being edited into an existing +** document. The parent anchor must already exist. +*/ + +extern HTChildAnchor * HTAnchor_findChild + PARAMS( + (HTParentAnchor *parent, + CONST char *tag) + ); + +/* Create or find a child anchor with a possible link +** -------------------------------------------------- +** +** Create new anchor with a given parent and possibly +** a name, and possibly a link to a _relatively_ named anchor. +** (Code originally in ParseHTML.h) +*/ +extern HTChildAnchor * HTAnchor_findChildAndLink + PARAMS(( + HTParentAnchor * parent, /* May not be 0 */ + CONST char * tag, /* May be "" or 0 */ + CONST char * href, /* May be "" or 0 */ + HTLinkType * ltype /* May be 0 */ + )); + + +/* Create new or find old named anchor +** ----------------------------------- +** +** This one is for a reference which is found in a document, and might +** not be already loaded. +** Note: You are not guaranteed a new anchor -- you might get an old one, +** like with fonts. +*/ + +extern HTAnchor * HTAnchor_findAddress PARAMS((CONST DocAddress * address)); + + +/* Delete an anchor and possibly related things (auto garbage collection) +** -------------------------------------------- +** +** The anchor is only deleted if the corresponding document is not loaded. +** All outgoing links from parent and children are deleted, and this anchor +** is removed from the sources list of all its targets. +** We also try to delete the targets whose documents are not loaded. +** If this anchor's source list is empty, we delete it and its children. +*/ + +extern BOOL HTAnchor_delete + PARAMS( + (HTParentAnchor *me) + ); + + +/* Move an anchor to the head of the list of its siblings +** ------------------------------------------------------ +** +** This is to ensure that an anchor which might have already existed +** is put in the correct order as we load the document. +*/ + +extern void HTAnchor_makeLastChild + PARAMS( + (HTChildAnchor *me) + ); + +/* Data access functions +** --------------------- +*/ + +extern HTParentAnchor * HTAnchor_parent + PARAMS( + (HTAnchor *me) + ); + +extern void HTAnchor_setDocument + PARAMS( + (HTParentAnchor *me, HyperDoc *doc) + ); + +extern HyperDoc * HTAnchor_document + PARAMS( + (HTParentAnchor *me) + ); +/* We don't want code to change an address after anchor creation... yet ? +extern void HTAnchor_setAddress + PARAMS( + (HTAnchor *me, char *addr) + ); +*/ + +/* Returns the full URI of the anchor, child or parent +** as a malloc'd string to be freed by the caller. +*/ +extern char * HTAnchor_address + PARAMS( + (HTAnchor *me) + ); + +extern void HTAnchor_setFormat + PARAMS( + (HTParentAnchor *me, HTFormat form) + ); + +extern HTFormat HTAnchor_format + PARAMS( + (HTParentAnchor *me) + ); + +extern void HTAnchor_setIndex + PARAMS( + (HTParentAnchor *me, char * address) + ); + +extern void HTAnchor_setPrompt + PARAMS( + (HTParentAnchor *me, char * prompt) + ); + +extern BOOL HTAnchor_isIndex + PARAMS( + (HTParentAnchor *me) + ); + +extern BOOL HTAnchor_hasChildren + PARAMS( + (HTParentAnchor *me) + ); + +/* Title handling +*/ +extern CONST char * HTAnchor_title + PARAMS( + (HTParentAnchor *me) + ); + +extern void HTAnchor_setTitle + PARAMS( + (HTParentAnchor *me, CONST char * title) + ); + +extern void HTAnchor_appendTitle + PARAMS( + (HTParentAnchor *me, CONST char * title) + ); + +/* Owner handling +*/ +extern CONST char * HTAnchor_owner + PARAMS( + (HTParentAnchor *me) + ); + +extern void HTAnchor_setOwner + PARAMS( + (HTParentAnchor *me, CONST char * owner) + ); + +/* TITLE handling in LINKs with REV="made" or REV="owner" +*/ +extern CONST char * HTAnchor_RevTitle + PARAMS( + (HTParentAnchor *me) + ); + +extern void HTAnchor_setRevTitle + PARAMS( + (HTParentAnchor *me, CONST char * title) + ); + +/* Link this Anchor to another given one +** ------------------------------------- +*/ + +extern BOOL HTAnchor_link + PARAMS( + (HTAnchor *source, HTAnchor *destination, HTLinkType *type) + ); + +/* Manipulation of links +** --------------------- +*/ + +extern HTAnchor * HTAnchor_followMainLink + PARAMS( + (HTAnchor *me) + ); + +extern HTAnchor * HTAnchor_followTypedLink + PARAMS( + (HTAnchor *me, HTLinkType *type) + ); + +extern BOOL HTAnchor_makeMainLink + PARAMS( + (HTAnchor *me, HTLink *movingLink) + ); + +/* Read and write methods +** ---------------------- +*/ +extern HTList * HTAnchor_methods PARAMS((HTParentAnchor *me)); + +/* Protocol +** -------- +*/ +extern void * HTAnchor_protocol PARAMS((HTParentAnchor * me)); +extern void HTAnchor_setProtocol PARAMS((HTParentAnchor * me, + void* protocol)); + +/* Physical address +** ---------------- +*/ +extern char * HTAnchor_physical PARAMS((HTParentAnchor * me)); +extern void HTAnchor_setPhysical PARAMS((HTParentAnchor * me, + char * protocol)); + +#endif /* HTANCHOR_H */ + +/* + + */ diff --git a/WWW/Library/Implementation/HTAssoc.c b/WWW/Library/Implementation/HTAssoc.c new file mode 100644 index 00000000..e63c0213 --- /dev/null +++ b/WWW/Library/Implementation/HTAssoc.c @@ -0,0 +1,87 @@ + +/* MODULE HTAssoc.c +** ASSOCIATION LIST FOR STORING NAME-VALUE PAIRS. +** NAMES NOT CASE SENSITIVE, AND ONLY COMMON LENGTH +** IS CHECKED (allows abbreviations; well, length is +** taken from lookup-up name, so if table contains +** a shorter abbrev it is not found). +** AUTHORS: +** AL Ari Luotonen luotonen@dxcern.cern.ch +** +** HISTORY: +** +** +** BUGS: +** +** +*/ + + +#include "HTUtils.h" + +#include <string.h> + +#include "HTAAUtil.h" +#include "HTAssoc.h" +#include "HTString.h" + +#include "LYLeaks.h" + +PUBLIC HTAssocList *HTAssocList_new NOARGS +{ + return HTList_new(); +} + + +PUBLIC void HTAssocList_delete ARGS1(HTAssocList *, alist) +{ + if (alist) { + HTAssocList *cur = alist; + HTAssoc *assoc; + while (NULL != (assoc = (HTAssoc*)HTList_nextObject(cur))) { + FREE(assoc->name); + FREE(assoc->value); + FREE(assoc); + } + HTList_delete(alist); + alist = NULL; + } +} + + +PUBLIC void HTAssocList_add ARGS3(HTAssocList *, alist, + CONST char *, name, + CONST char *, value) +{ + HTAssoc *assoc; + + if (alist) { + if (!(assoc = (HTAssoc*)malloc(sizeof(HTAssoc)))) + outofmem(__FILE__, "HTAssoc_add"); + assoc->name = NULL; + assoc->value = NULL; + + if (name) + StrAllocCopy(assoc->name, name); + if (value) + StrAllocCopy(assoc->value, value); + HTList_addObject(alist, (void*)assoc); + } else if (TRACE) { + fprintf(stderr, "HTAssoc_add: ERROR: assoc list NULL!!\n"); + } +} + + +PUBLIC char *HTAssocList_lookup ARGS2(HTAssocList *, alist, + CONST char *, name) +{ + HTAssocList *cur = alist; + HTAssoc *assoc; + + while (NULL != (assoc = (HTAssoc*)HTList_nextObject(cur))) { + if (!strncasecomp(assoc->name, name, strlen(name))) + return assoc->value; + } + return NULL; +} + diff --git a/WWW/Library/Implementation/HTAssoc.h b/WWW/Library/Implementation/HTAssoc.h new file mode 100644 index 00000000..71b0c918 --- /dev/null +++ b/WWW/Library/Implementation/HTAssoc.h @@ -0,0 +1,44 @@ +/* ASSOCIATION LIST FOR STORING NAME-VALUE PAIRS + + Lookups from assosiation list are not case-sensitive. + + */ + +#ifndef HTASSOC_H +#define HTASSOC_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTList.h" + + +#ifdef SHORT_NAMES +#define HTAL_new HTAssocList_new +#define HTAL_del HTAssocList_delete +#define HTAL_add HTAssocList_add +#define HTAL_lup HTAssocList_lookup +#endif /*SHORT_NAMES*/ + +typedef HTList HTAssocList; + +typedef struct { + char * name; + char * value; +} HTAssoc; + + +PUBLIC HTAssocList *HTAssocList_new NOPARAMS; +PUBLIC void HTAssocList_delete PARAMS((HTAssocList * alist)); + +PUBLIC void HTAssocList_add PARAMS((HTAssocList * alist, + CONST char * name, + CONST char * value)); + +PUBLIC char *HTAssocList_lookup PARAMS((HTAssocList * alist, + CONST char * name)); + +#endif /* not HTASSOC_H */ +/* + + End of file HTAssoc.h. */ diff --git a/WWW/Library/Implementation/HTAtom.c b/WWW/Library/Implementation/HTAtom.c new file mode 100644 index 00000000..8286f49b --- /dev/null +++ b/WWW/Library/Implementation/HTAtom.c @@ -0,0 +1,169 @@ +/* Atoms: Names to numbers HTAtom.c +** ======================= +** +** Atoms are names which are given representative pointer values +** so that they can be stored more efficiently, and comparisons +** for equality done more efficiently. +** +** Atoms are kept in a hash table consisting of an array of linked lists. +** +** Authors: +** TBL Tim Berners-Lee, WorldWideWeb project, CERN +** (c) Copyright CERN 1991 - See Copyright.html +** +*/ +#include "HTUtils.h" + +#define HASH_SIZE 101 /* Tunable */ +#include "HTAtom.h" + +/*#include <stdio.h> included by HTUtils.h -- FM *//* joe@athena, TBL 921019 */ +#include <string.h> + +#include "HTList.h" + +#include "LYexit.h" +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +PRIVATE HTAtom * hash_table[HASH_SIZE]; +PRIVATE BOOL initialised = NO; + +/* + * To free off all atoms. + */ +PRIVATE void free_atoms NOPARAMS; + +/* + * Alternate hashing function. + */ +#define HASH_FUNCTION(cp_hash) ((strlen(cp_hash) * *cp_hash) % HASH_SIZE) + +PUBLIC HTAtom * HTAtom_for ARGS1(CONST char *, string) +{ + int hash; + HTAtom * a; + + /* First time around, clear hash table + */ + /* + * Memory leak fixed. + * 05-29-94 Lynx 2-3-1 Garrett Arch Blythe + */ + if (!initialised) { + int i; + for (i = 0; i < HASH_SIZE; i++) + hash_table[i] = (HTAtom *) 0; + initialised = YES; + atexit(free_atoms); + } + + /* Generate hash function + */ + hash = HASH_FUNCTION(string); + + /* Search for the string in the list + */ + for (a = hash_table[hash]; a; a = a->next) { + if (0 == strcasecomp(a->name, string)) { + /* if (TRACE) fprintf(stderr, + "HTAtom: Old atom %p for `%s'\n", a, string); */ + return a; /* Found: return it */ + } + } + + /* Generate a new entry + */ + a = (HTAtom *)malloc(sizeof(*a)); + if (a == NULL) + outofmem(__FILE__, "HTAtom_for"); + a->name = (char *)malloc(strlen(string)+1); + if (a->name == NULL) + outofmem(__FILE__, "HTAtom_for"); + strcpy(a->name, string); + a->next = hash_table[hash]; /* Put onto the head of list */ + hash_table[hash] = a; +#ifdef NOT_DEFINED + if (TRACE) + fprintf(stderr, "HTAtom: New atom %p for `%s'\n", a, string); +#endif /* NOT_DEFINED */ + return a; +} + +/* + * Purpose: Free off all atoms. + * Arguments: void + * Return Value: void + * Remarks/Portability/Dependencies/Restrictions: + * To be used at program exit. + * Revision History: + * 05-29-94 created Lynx 2-3-1 Garrett Arch Blythe + */ +PRIVATE void free_atoms NOARGS +{ + auto int i_counter; + HTAtom *HTAp_freeme; + /* + * Loop through all lists of atoms. + */ + for (i_counter = 0; i_counter < HASH_SIZE; i_counter++) { + /* + * Loop through the list. + */ + while (hash_table[i_counter] != NULL) { + /* + * Free off atoms and any members. + */ + HTAp_freeme = hash_table[i_counter]; + hash_table[i_counter] = HTAp_freeme->next; + FREE(HTAp_freeme->name); + FREE(HTAp_freeme); + } + } +} + +PRIVATE BOOL mime_match ARGS2(CONST char *, name, + CONST char *, templ) +{ + if (name && templ) { + static char *n1 = NULL; + static char *t1 = NULL; + char *n2; + char *t2; + + StrAllocCopy(n1, name); /* These also free the ones */ + StrAllocCopy(t1, templ); /* from previous call. */ + + if (!(n2 = strchr(n1, '/')) || !(t2 = strchr(t1, '/'))) + return NO; + + *(n2++) = (char)0; + *(t2++) = (char)0; + + if ((0 == strcmp(t1, "*") || 0 == strcmp(t1, n1)) && + (0 == strcmp(t2, "*") || 0 == strcmp(t2, n2))) + return YES; + } + return NO; +} + + +PUBLIC HTList *HTAtom_templateMatches ARGS1(CONST char *, templ) +{ + HTList *matches = HTList_new(); + + if (initialised && templ) { + int i; + HTAtom *cur; + + for (i = 0; i < HASH_SIZE; i++) { + for (cur = hash_table[i]; cur; cur = cur->next) { + if (mime_match(cur->name, templ)) + HTList_addObject(matches, (void*)cur); + } + } + } + return matches; +} + diff --git a/WWW/Library/Implementation/HTAtom.h b/WWW/Library/Implementation/HTAtom.h new file mode 100644 index 00000000..b8dd10ec --- /dev/null +++ b/WWW/Library/Implementation/HTAtom.h @@ -0,0 +1,49 @@ +/* */ + +/* Atoms: Names to numbers HTAtom.h +** ======================= +** +** Atoms are names which are given representative pointer values +** so that they can be stored more efficiently, and compaisons +** for equality done more efficiently. +** +** HTAtom_for(string) returns a representative value such that it +** will always (within one run of the program) return the same +** value for the same given string. +** +** Authors: +** TBL Tim Berners-Lee, WorldWideWeb project, CERN +** +** (c) Copyright CERN 1991 - See Copyright.html +** +*/ + +#ifndef HTATOM_H +#define HTATOM_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTList.h" + +#ifdef SHORT_NAMES +#define HTAt_for HTAtom_for +#define HTAt_tMa HTAtom_templateMatches +#endif /*SHORT_NAMES*/ + +typedef struct _HTAtom HTAtom; +struct _HTAtom { + HTAtom * next; + char * name; +}; /* struct _HTAtom */ + + +PUBLIC HTAtom * HTAtom_for PARAMS((CONST char * string)); +PUBLIC HTList * HTAtom_templateMatches PARAMS((CONST char * templ)); + +#define HTAtom_name(a) ((a)->name) + +#endif /* HTATOM_H */ +/* + + */ diff --git a/WWW/Library/Implementation/HTAuth.c b/WWW/Library/Implementation/HTAuth.c new file mode 100644 index 00000000..7f7b363b --- /dev/null +++ b/WWW/Library/Implementation/HTAuth.c @@ -0,0 +1,210 @@ + +/* MODULE HTAuth.c +** USER AUTHENTICATION +** +** AUTHORS: +** AL Ari Luotonen luotonen@dxcern.cern.ch +** +** HISTORY: +** AL 14.10.93 Fixed the colon-not-allowed-in-password-bug. +** +** BUGS: +** +** +*/ + +#include "HTUtils.h" +#include <string.h> +#include "HTPasswd.h" /* Password file routines */ +#include "HTAssoc.h" +#include "HTAuth.h" /* Implemented here */ +#include "HTUU.h" /* Uuencoding and uudecoding */ + +#include "LYLeaks.h" + +/* PRIVATE decompose_auth_string() +** DECOMPOSE AUTHENTICATION STRING +** FOR BASIC OR PUBKEY SCHEME +** ON ENTRY: +** authstring is the authorization string received +** from browser. +** +** ON EXIT: +** returns a node representing the user information +** (as always, this is automatically freed +** by AA package). +*/ +PRIVATE HTAAUser *decompose_auth_string ARGS2(char *, authstring, + HTAAScheme, scheme) +{ + static HTAAUser *user = NULL; + static char *cleartext = NULL; + char *username = NULL; + char *password = NULL; + char *inet_addr = NULL; + char *timestamp = NULL; + char *browsers_key = NULL; + + if (!user && !(user = (HTAAUser*)malloc(sizeof(HTAAUser)))) /* Allocated */ + outofmem(__FILE__, "decompose_auth_string"); /* only once */ + + user->scheme = scheme; + user->username = NULL; /* Not freed, because freeing */ + user->password = NULL; /* cleartext also frees these */ + user->inet_addr = NULL; /* See below: || */ + user->timestamp = NULL; /* || */ + user->secret_key = NULL; /* || */ + /* \/ */ + FREE(cleartext); /* From previous call. */ + /* NOTE: parts of this memory are pointed to by */ + /* pointers in HTAAUser structure. Therefore, */ + /* this also frees all the strings pointed to */ + /* by the static 'user'. */ + + if (!authstring || !*authstring || + scheme != HTAA_BASIC || scheme == HTAA_PUBKEY) + return NULL; + + if (scheme == HTAA_PUBKEY) { /* Decrypt authentication string */ + int bytes_decoded; + char *ciphertext; + int len = strlen(authstring) + 1; + + if (!(ciphertext = (char*)malloc(len)) || + !(cleartext = (char*)malloc(len))) + outofmem(__FILE__, "decompose_auth_string"); + + bytes_decoded = HTUU_decode(authstring, + (unsigned char *)ciphertext, len); + ciphertext[bytes_decoded] = (char)0; +#ifdef PUBKEY + HTPK_decrypt(ciphertext, cleartext, private_key); +#endif + FREE(ciphertext); + } + else { /* Just uudecode */ + int bytes_decoded; + int len = strlen(authstring) + 1; + + if (!(cleartext = (char*)malloc(len))) + outofmem(__FILE__, "decompose_auth_string"); + bytes_decoded = HTUU_decode(authstring, + (unsigned char *)cleartext, len); + cleartext[bytes_decoded] = (char)0; + } + + +/* +** Extract username and password (for both schemes) +*/ + username = cleartext; + if (!(password = strchr(cleartext, ':'))) { + if (TRACE) + fprintf(stderr, "%s %s\n", + "decompose_auth_string: password field", + "missing in authentication string.\n"); + return NULL; + } + *(password++) = '\0'; + +/* +** Extract rest of the fields +*/ + if (scheme == HTAA_PUBKEY) { + if ( !(inet_addr =strchr(password, ':')) || + (*(inet_addr++) ='\0'), !(timestamp =strchr(inet_addr,':')) || + (*(timestamp++) ='\0'), !(browsers_key=strchr(timestamp,':')) || + (*(browsers_key++)='\0')) { + + if (TRACE) fprintf(stderr, "%s %s\n", + "decompose_auth_string: Pubkey scheme", + "fields missing in authentication string"); + return NULL; + } + } + +/* +** Set the fields into the result +*/ + user->username = username; + user->password = password; + user->inet_addr = inet_addr; + user->timestamp = timestamp; + user->secret_key = browsers_key; + + if (TRACE) { + if (scheme==HTAA_BASIC) + fprintf(stderr, "decompose_auth_string: %s (%s,%s)\n", + "Basic scheme authentication string:", + username, password); + else + fprintf(stderr, "decompose_auth_string: %s (%s,%s,%s,%s,%s)\n", + "Pubkey scheme authentication string:", + username, password, inet_addr, timestamp, browsers_key); + } + + return user; +} + + + +PRIVATE BOOL HTAA_checkTimeStamp ARGS1(CONST char *, timestamp) +{ + return NO; /* This is just a stub */ +} + + +PRIVATE BOOL HTAA_checkInetAddress ARGS1(CONST char *, inet_addr) +{ + return NO; /* This is just a stub */ +} + + +/* SERVER PUBLIC HTAA_authenticate() +** AUTHENTICATE USER +** ON ENTRY: +** scheme used authentication scheme. +** scheme_specifics the scheme specific parameters +** (authentication string for Basic and +** Pubkey schemes). +** prot is the protection information structure +** for the file. +** +** ON EXIT: +** returns NULL, if authentication failed. +** Otherwise a pointer to a structure +** representing authenticated user, +** which should not be freed. +*/ +PUBLIC HTAAUser *HTAA_authenticate ARGS3(HTAAScheme, scheme, + char *, scheme_specifics, + HTAAProt *, prot) +{ + if (HTAA_UNKNOWN == scheme || !prot || + -1 == HTList_indexOf(prot->valid_schemes, (void*)scheme)) + return NULL; + + switch (scheme) { + case HTAA_BASIC: + case HTAA_PUBKEY: + { + HTAAUser *user = decompose_auth_string(scheme_specifics, scheme); + /* Remember, user is auto-freed */ + if (user && + HTAA_checkPassword(user->username, + user->password, + HTAssocList_lookup(prot->values, "passw")) && + (HTAA_BASIC == scheme || + (HTAA_checkTimeStamp(user->timestamp) && + HTAA_checkInetAddress(user->inet_addr)))) + return user; + else + return NULL; + } + break; + default: + /* Other authentication routines go here */ + return NULL; + } +} + diff --git a/WWW/Library/Implementation/HTAuth.h b/WWW/Library/Implementation/HTAuth.h new file mode 100644 index 00000000..6dc4c516 --- /dev/null +++ b/WWW/Library/Implementation/HTAuth.h @@ -0,0 +1,66 @@ +/* AUTHENTICATION MODULE + + This is the authentication module. By modifying the function HTAA_authenticate() it can + be made to support external authentication methods. + + */ + +#ifndef HTAUTH_H +#define HTAUTH_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTAAUtil.h" +#include "HTAAProt.h" + + +#ifdef SHORT_NAMES +#define HTAAauth HTAA_authenticate +#endif /* SHORT_NAMES */ + + +/* +** Server's representation of a user (fields in authentication string) +*/ +typedef struct { + HTAAScheme scheme; /* Scheme used to authenticate this user */ + char * username; + char * password; + char * inet_addr; + char * timestamp; + char * secret_key; +} HTAAUser; +/* + +User Authentication + + */ + +/* SERVER PUBLIC HTAA_authenticate() +** AUTHENTICATE USER +** ON ENTRY: +** scheme used authentication scheme. +** scheme_specifics the scheme specific parameters +** (authentication string for Basic and +** Pubkey schemes). +** prot is the protection information structure +** for the file. +** +** ON EXIT: +** returns NULL, if authentication failed. +** Otherwise a pointer to a structure +** representing authenticated user, +** which should not be freed. +*/ +PUBLIC HTAAUser *HTAA_authenticate PARAMS((HTAAScheme scheme, + char * scheme_specifics, + HTAAProt * prot)); +/* + + */ + +#endif /* not HTAUTH_H */ +/* + + End of file HTAuth.h. */ diff --git a/WWW/Library/Implementation/HTBTree.c b/WWW/Library/Implementation/HTBTree.c new file mode 100644 index 00000000..cc90111a --- /dev/null +++ b/WWW/Library/Implementation/HTBTree.c @@ -0,0 +1,720 @@ +/* Binary Tree for sorting things +** ============================== +** Author: Arthur Secret +** +** 4 March 94: Bug fixed in the balancing procedure +** +*/ + + +#include "HTUtils.h" +#include "HTBTree.h" +#ifndef __STRICT_BSD__ +#include <stdlib.h> +#endif +#include <string.h> + +#define MAXIMUM(a,b) ((a)>(b)?(a):(b)) + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + + +PUBLIC HTBTree * HTBTree_new ARGS1(HTComparer, comp) + /********************************************************* + ** This function returns an HTBTree with memory allocated + ** for it when given a mean to compare things + */ +{ + HTBTree * tree = (HTBTree *)malloc(sizeof(HTBTree)); + if (tree==NULL) outofmem(__FILE__, "HTBTree_new"); + + tree->compare = comp; + tree->top = NULL; + + return tree; +} + + + + +PRIVATE void HTBTElement_free ARGS1(HTBTElement*, element) + /********************************************************** + ** This void will free the memory allocated for one element + */ +{ + if (element) { + if (element->left != NULL) + HTBTElement_free(element->left); + if (element->right != NULL) + HTBTElement_free(element->right); + FREE(element); + } +} + +PUBLIC void HTBTree_free ARGS1(HTBTree*, tree) + /************************************************************** + ** This void will free the memory allocated for the whole tree + */ +{ + HTBTElement_free(tree->top); + FREE(tree); +} + + + + +PRIVATE void HTBTElementAndObject_free ARGS1(HTBTElement*, element) + /********************************************************** + ** This void will free the memory allocated for one element + */ +{ + if (element) { /* Just in case nothing was in the tree anyway */ + if (element->left != NULL) + HTBTElementAndObject_free(element->left); + if (element->right != NULL) + HTBTElementAndObject_free(element->right); + FREE(element->object); + FREE(element); + } +} + +PUBLIC void HTBTreeAndObject_free ARGS1(HTBTree*, tree) + /************************************************************** + ** This void will free the memory allocated for the whole tree + */ +{ + HTBTElementAndObject_free(tree->top); + FREE(tree); +} + + + + +PUBLIC void HTBTree_add ARGS2( + HTBTree*, tree, + void*, object) + /********************************************************************** + ** This void is the core of HTBTree.c . It will + ** 1/ add a new element to the tree at the right place + ** so that the tree remains sorted + ** 2/ balance the tree to be as fast as possible when reading it + */ +{ + HTBTElement * father_of_element; + HTBTElement * added_element; + HTBTElement * forefather_of_element; + HTBTElement * father_of_forefather; + BOOL father_found,top_found; + int depth,depth2,corrections; + /* father_of_element is a pointer to the structure that is the father of the + ** new object "object". + ** added_element is a pointer to the structure that contains or will contain + ** the new object "object". + ** father_of_forefather and forefather_of_element are pointers that are used + ** to modify the depths of upper elements, when needed. + ** + ** father_found indicates by a value NO when the future father of "object" + ** is found. + ** top_found indicates by a value NO when, in case of a difference of depths + ** < 2, the top of the tree is encountered and forbids any further try to + ** balance the tree. + ** corrections is an integer used to avoid infinite loops in cases + ** such as: + ** + ** 3 3 + ** 4 4 + ** 5 5 + ** + ** 3 is used here to show that it need not be the top of the tree. + */ + + /* + ** 1/ Adding of the element to the binary tree + */ + + if (tree->top == NULL) + { + tree->top = (HTBTElement *)malloc(sizeof(HTBTElement)); + if (tree->top == NULL) outofmem(__FILE__, "HTBTree_add"); + tree->top->up = NULL; + tree->top->object = object; + tree->top->left = NULL; + tree->top->left_depth = 0; + tree->top->right = NULL; + tree->top->right_depth = 0; + } + else + { + father_found = YES; + father_of_element = tree->top; + added_element = NULL; + father_of_forefather = NULL; + forefather_of_element = NULL; + while (father_found) + { + if (tree->compare(object,father_of_element->object)<0) + { + if (father_of_element->left != NULL) + father_of_element = father_of_element->left; + else + { + father_found = NO; + father_of_element->left = + (HTBTElement *)malloc(sizeof(HTBTElement)); + if (father_of_element->left==NULL) + outofmem(__FILE__, "HTBTree_add"); + added_element = father_of_element->left; + added_element->up = father_of_element; + added_element->object = object; + added_element->left = NULL; + added_element->left_depth = 0; + added_element->right = NULL; + added_element->right_depth = 0; + } + } + if (tree->compare(object,father_of_element->object)>=0) + { + if (father_of_element->right != NULL) + father_of_element = father_of_element->right; + else + { + father_found = NO; + father_of_element->right = + (HTBTElement *)malloc(sizeof(HTBTElement)); + if (father_of_element->right==NULL) + outofmem(__FILE__, "HTBTree_add"); + added_element = father_of_element->right; + added_element->up = father_of_element; + added_element->object = object; + added_element->left = NULL; + added_element->left_depth = 0; + added_element->right = NULL; + added_element->right_depth = 0; + } + } + } + /* + ** Changing of all depths that need to be changed + */ + father_of_forefather = father_of_element; + forefather_of_element = added_element; + do + { + if (father_of_forefather->left == forefather_of_element) + { + depth = father_of_forefather->left_depth; + father_of_forefather->left_depth = 1 + + MAXIMUM(forefather_of_element->right_depth, + forefather_of_element->left_depth); + depth2 = father_of_forefather->left_depth; + } + else + { + depth = father_of_forefather->right_depth; + father_of_forefather->right_depth = 1 + + MAXIMUM(forefather_of_element->right_depth, + forefather_of_element->left_depth); + depth2 = father_of_forefather->right_depth; + } + forefather_of_element = father_of_forefather; + father_of_forefather = father_of_forefather->up; + } while ((depth != depth2) && (father_of_forefather != NULL)); + + + + /* + ** 2/ Balancing the binary tree, if necessary + */ + top_found = YES; + corrections = 0; + while ((top_found) && (corrections < 7)) + { + if ((abs(father_of_element->left_depth + - father_of_element->right_depth)) < 2) + { + if (father_of_element->up != NULL) + father_of_element = father_of_element->up; + else top_found = NO; + } + else + { /* We start the process of balancing */ + + corrections = corrections + 1; + /* + ** corrections is an integer used to avoid infinite + ** loops in cases such as: + ** + ** 3 3 + ** 4 4 + ** 5 5 + ** + ** 3 is used to show that it need not be the top of the tree + ** But let's avoid these two exceptions anyhow + ** with the two following conditions (4 March 94 - AS) + */ + + if ((father_of_element->left == NULL) + && (father_of_element->right->right == NULL) + && (father_of_element->right->left->left == NULL) + && (father_of_element->right->left->right == NULL)) + corrections = 7; + + if ((father_of_element->right == NULL) + && (father_of_element->left->left == NULL) + && (father_of_element->left->right->right == NULL) + && (father_of_element->left->right->left == NULL)) + corrections = 7; + + + if (father_of_element->left_depth > father_of_element->right_depth) + { + added_element = father_of_element->left; + father_of_element->left_depth = added_element->right_depth; + added_element->right_depth = 1 + + MAXIMUM(father_of_element->right_depth, + father_of_element->left_depth); + if (father_of_element->up != NULL) + { + /* Bug fixed in March 94 - AS */ + BOOL first_time; + + father_of_forefather = father_of_element->up; + forefather_of_element = added_element; + first_time = YES; + do + { + if (father_of_forefather->left + == forefather_of_element->up) + { + depth = father_of_forefather->left_depth; + if (first_time) + { + father_of_forefather->left_depth = 1 + + MAXIMUM(forefather_of_element->left_depth, + forefather_of_element->right_depth); + first_time = NO; + } + else + father_of_forefather->left_depth = 1 + + MAXIMUM(forefather_of_element->up->left_depth, + forefather_of_element->up->right_depth); + + depth2 = father_of_forefather->left_depth; + } + else + { + depth = father_of_forefather->right_depth; + if (first_time) + { + father_of_forefather->right_depth = 1 + + MAXIMUM(forefather_of_element->left_depth, + forefather_of_element->right_depth); + first_time = NO; + } + else + father_of_forefather->right_depth = 1 + + MAXIMUM(forefather_of_element->up->left_depth, + forefather_of_element->up->right_depth); + depth2 = father_of_forefather->right_depth; + } + forefather_of_element = forefather_of_element->up; + father_of_forefather = father_of_forefather->up; + } while ((depth != depth2) && + (father_of_forefather != NULL)); + father_of_forefather = father_of_element->up; + if (father_of_forefather->left == father_of_element) + { + /* + ** 3 3 + ** 4 5 + ** When tree 5 6 becomes 7 4 + ** 7 8 8 6 + ** + ** 3 is used to show that it may not be the top of the + ** tree. + */ + father_of_forefather->left = added_element; + father_of_element->left = added_element->right; + added_element->right = father_of_element; + } + if (father_of_forefather->right == father_of_element) + { + /* + ** 3 3 + ** 4 5 + ** When tree 5 6 becomes 7 4 + ** 7 8 8 6 + ** + ** 3 is used to show that it may not be the top of the + ** tree + */ + father_of_forefather->right = added_element; + father_of_element->left = added_element->right; + added_element->right = father_of_element; + } + added_element->up = father_of_forefather; + } + else + { + /* + ** + ** 1 2 + ** When tree 2 3 becomes 4 1 + ** 4 5 5 3 + ** + ** 1 is used to show that it is the top of the tree + */ + added_element->up = NULL; + father_of_element->left = added_element->right; + added_element->right = father_of_element; + } + father_of_element->up = added_element; + if (father_of_element->left != NULL) + father_of_element->left->up = father_of_element; + } + else + { + added_element = father_of_element->right; + father_of_element->right_depth = added_element->left_depth; + added_element->left_depth = 1 + + MAXIMUM(father_of_element->right_depth, + father_of_element->left_depth); + if (father_of_element->up != NULL) + /* Bug fixed in March 94 - AS */ + { + BOOL first_time; + + father_of_forefather = father_of_element->up; + forefather_of_element = added_element; + first_time = YES; + do + { + if (father_of_forefather->left + == forefather_of_element->up) + { + depth = father_of_forefather->left_depth; + if (first_time) + { + father_of_forefather->left_depth = 1 + + MAXIMUM(forefather_of_element->left_depth, + forefather_of_element->right_depth); + first_time = NO; + } + else + father_of_forefather->left_depth = 1 + + MAXIMUM(forefather_of_element->up->left_depth, + forefather_of_element->up->right_depth); + depth2 = father_of_forefather->left_depth; + } + else + { + depth = father_of_forefather->right_depth; + if (first_time) + { + father_of_forefather->right_depth = 1 + + MAXIMUM(forefather_of_element->left_depth, + forefather_of_element->right_depth); + first_time = NO; + } + else + father_of_forefather->right_depth = 1 + + MAXIMUM(forefather_of_element->up->left_depth, + forefather_of_element->up->right_depth); + depth2 = father_of_forefather->right_depth; + } + father_of_forefather = father_of_forefather->up; + forefather_of_element = forefather_of_element->up; + } while ((depth != depth2) && + (father_of_forefather != NULL)); + father_of_forefather = father_of_element->up; + if (father_of_forefather->left == father_of_element) + { + /* + ** 3 3 + ** 4 6 + ** When tree 5 6 becomes 4 8 + ** 7 8 5 7 + ** + ** 3 is used to show that it may not be the top of the + ** tree. + */ + father_of_forefather->left = added_element; + father_of_element->right = added_element->left; + added_element->left = father_of_element; + } + if (father_of_forefather->right == father_of_element) + { + /* + ** 3 3 + ** 4 6 + ** When tree 5 6 becomes 4 8 + ** 7 8 5 7 + ** + ** 3 is used to show that it may not be the top of the + ** tree + */ + father_of_forefather->right = added_element; + father_of_element->right = added_element->left; + added_element->left = father_of_element; + } + added_element->up = father_of_forefather; + } + else + { + /* + ** + ** 1 3 + ** When tree 2 3 becomes 1 5 + ** 4 5 2 4 + ** + ** 1 is used to show that it is the top of the tree. + */ + added_element->up = NULL; + father_of_element->right = added_element->left; + added_element->left = father_of_element; + } + father_of_element->up = added_element; + if (father_of_element->right != NULL) + father_of_element->right->up = father_of_element; + } + } + } + while (father_of_element->up != NULL) + { + father_of_element = father_of_element->up; + } + tree->top = father_of_element; + } +} + + + +PUBLIC HTBTElement * HTBTree_next ARGS2( + HTBTree*, tree, + HTBTElement*, ele) + /************************************************************************** + ** this function returns a pointer to the leftmost element if ele is NULL, + ** and to the next object to the right otherways. + ** If no elements left, returns a pointer to NULL. + */ +{ + HTBTElement * father_of_element; + HTBTElement * father_of_forefather; + + if (ele == NULL) + { + father_of_element = tree->top; + if (father_of_element != NULL) + while (father_of_element->left != NULL) + father_of_element = father_of_element->left; + } + else + { + father_of_element = ele; + if (father_of_element->right != NULL) + { + father_of_element = father_of_element->right; + while (father_of_element->left != NULL) + father_of_element = father_of_element->left; + } + else + { + father_of_forefather = father_of_element->up; + while (father_of_forefather && + (father_of_forefather->right == father_of_element)) + { + father_of_element = father_of_forefather; + father_of_forefather = father_of_element->up; + } + father_of_element = father_of_forefather; + } + } +#ifdef BTREE_TRACE + /* The option -DBTREE_TRACE will give much more information + ** about the way the process is running, for debugging matters + */ + if (father_of_element != NULL) + { + printf("\nObject = %s\t",(char *)father_of_element->object); + if (father_of_element->up != NULL) + printf("Objet du pere = %s\n", + (char *)father_of_element->up->object); + else printf("Pas de Pere\n"); + if (father_of_element->left != NULL) + printf("Objet du fils gauche = %s\t", + (char *)father_of_element->left->object); + else printf("Pas de fils gauche\t"); + if (father_of_element->right != NULL) + printf("Objet du fils droit = %s\n", + (char *)father_of_element->right->object); + else printf("Pas de fils droit\n"); + printf("Profondeur gauche = %i\t",father_of_element->left_depth); + printf("Profondeur droite = %i\n",father_of_element->right_depth); + printf(" **************\n"); + } +#endif + return father_of_element; +} + + +#ifdef TEST +main () + /****************************************************** + ** This is just a test to show how to handle HTBTree.c + */ +{ + HTBTree * tree; + HTBTElement * next_element; + + tree = HTBTree_new((HTComparer)strcasecomp); + HTBTree_add(tree,"hypertext"); + HTBTree_add(tree,"Addressing"); + HTBTree_add(tree,"X11"); + HTBTree_add(tree,"Tools"); + HTBTree_add(tree,"Proposal.wn"); + HTBTree_add(tree,"Protocols"); + HTBTree_add(tree,"NeXT"); + HTBTree_add(tree,"Daemon"); + HTBTree_add(tree,"Test"); + HTBTree_add(tree,"Administration"); + HTBTree_add(tree,"LineMode"); + HTBTree_add(tree,"DesignIssues"); + HTBTree_add(tree,"MarkUp"); + HTBTree_add(tree,"Macintosh"); + HTBTree_add(tree,"Proposal.rtf.wn"); + HTBTree_add(tree,"FIND"); + HTBTree_add(tree,"Paper"); + HTBTree_add(tree,"Tcl"); + HTBTree_add(tree,"Talks"); + HTBTree_add(tree,"Architecture"); + HTBTree_add(tree,"VMSHelp"); + HTBTree_add(tree,"Provider"); + HTBTree_add(tree,"Archive"); + HTBTree_add(tree,"SLAC"); + HTBTree_add(tree,"Project"); + HTBTree_add(tree,"News"); + HTBTree_add(tree,"Viola"); + HTBTree_add(tree,"Users"); + HTBTree_add(tree,"FAQ"); + HTBTree_add(tree,"WorkingNotes"); + HTBTree_add(tree,"Windows"); + HTBTree_add(tree,"FineWWW"); + HTBTree_add(tree,"Frame"); + HTBTree_add(tree,"XMosaic"); + HTBTree_add(tree,"People"); + HTBTree_add(tree,"All"); + HTBTree_add(tree,"Curses"); + HTBTree_add(tree,"Erwise"); + HTBTree_add(tree,"Carl"); + HTBTree_add(tree,"MidasWWW"); + HTBTree_add(tree,"XPM"); + HTBTree_add(tree,"MailRobot"); + HTBTree_add(tree,"Illustrations"); + HTBTree_add(tree,"VMClient"); + HTBTree_add(tree,"XPA"); + HTBTree_add(tree,"Clients.html"); + HTBTree_add(tree,"Library"); + HTBTree_add(tree,"CERNLIB_Distribution"); + HTBTree_add(tree,"libHTML"); + HTBTree_add(tree,"WindowsPC"); + HTBTree_add(tree,"tkWWW"); + HTBTree_add(tree,"tk2.3"); + HTBTree_add(tree,"CVS-RCS"); + HTBTree_add(tree,"DecnetSockets"); + HTBTree_add(tree,"SGMLStream"); + HTBTree_add(tree,"NextStep"); + HTBTree_add(tree,"CVSRepository_old"); + HTBTree_add(tree,"ArthurSecret"); + HTBTree_add(tree,"CVSROOT"); + HTBTree_add(tree,"HytelnetGate"); + HTBTree_add(tree,"cern.www.new.src"); + HTBTree_add(tree,"Conditions"); + HTBTree_add(tree,"HTMLGate"); + HTBTree_add(tree,"Makefile"); + HTBTree_add(tree,"Newsgroups.html"); + HTBTree_add(tree,"People.html"); + HTBTree_add(tree,"Bugs.html"); + HTBTree_add(tree,"Summary.html"); + HTBTree_add(tree,"zDesignIssues.wn"); + HTBTree_add(tree,"HT.draw"); + HTBTree_add(tree,"HTandCERN.wn"); + HTBTree_add(tree,"Ideas.wn"); + HTBTree_add(tree,"MarkUp.wn"); + HTBTree_add(tree,"Proposal.html"); + HTBTree_add(tree,"SearchPanel.draw"); + HTBTree_add(tree,"Comments.wn"); + HTBTree_add(tree,"Xanadu.html"); + HTBTree_add(tree,"Storinglinks.html"); + HTBTree_add(tree,"TheW3Book.html"); + HTBTree_add(tree,"Talk_Feb-91.html"); + HTBTree_add(tree,"JFosterEntry.txt"); + HTBTree_add(tree,"Summary.txt"); + HTBTree_add(tree,"Bibliography.html"); + HTBTree_add(tree,"HTandCern.txt"); + HTBTree_add(tree,"Talk.draw"); + HTBTree_add(tree,"zDesignNotes.html"); + HTBTree_add(tree,"Link.html"); + HTBTree_add(tree,"Status.html"); + HTBTree_add(tree,"http.txt"); + HTBTree_add(tree,"People.html~"); + HTBTree_add(tree,"TAGS"); + HTBTree_add(tree,"summary.txt"); + HTBTree_add(tree,"Technical.html"); + HTBTree_add(tree,"Terms.html"); + HTBTree_add(tree,"JANETAccess.html"); + HTBTree_add(tree,"People.txt"); + HTBTree_add(tree,"README.txt"); + HTBTree_add(tree,"CodingStandards.html"); + HTBTree_add(tree,"Copyright.txt"); + HTBTree_add(tree,"Status_old.html"); + HTBTree_add(tree,"patches~"); + HTBTree_add(tree,"RelatedProducts.html"); + HTBTree_add(tree,"Implementation"); + HTBTree_add(tree,"History.html"); + HTBTree_add(tree,"Makefile.bak"); + HTBTree_add(tree,"Makefile.old"); + HTBTree_add(tree,"Policy.html"); + HTBTree_add(tree,"WhatIs.html"); + HTBTree_add(tree,"TheProject.html"); + HTBTree_add(tree,"Notation.html"); + HTBTree_add(tree,"Helping.html"); + HTBTree_add(tree,"Cyber-WWW.sit.Hqx"); + HTBTree_add(tree,"Glossary.html"); + HTBTree_add(tree,"maketags.html"); + HTBTree_add(tree,"IntroCS.html"); + HTBTree_add(tree,"Contrib"); + HTBTree_add(tree,"Help.html"); + HTBTree_add(tree,"CodeManagExec"); + HTBTree_add(tree,"HT-0.1draz"); + HTBTree_add(tree,"Cello"); + HTBTree_add(tree,"TOPUB"); + HTBTree_add(tree,"BUILD"); + HTBTree_add(tree,"BUILDALL"); + HTBTree_add(tree,"Lynx"); + HTBTree_add(tree,"ArthurLibrary"); + HTBTree_add(tree,"RashtyClient"); + HTBTree_add(tree,"#History.html#"); + HTBTree_add(tree,"PerlServers"); + HTBTree_add(tree,"modules"); + HTBTree_add(tree,"NCSA_httpd"); + HTBTree_add(tree,"MAIL2HTML"); + HTBTree_add(tree,"core"); + HTBTree_add(tree,"EmacsWWW"); +#ifdef BTREE_TRACE + printf("\nTreeTopObject=%s\n\n",tree->top->object); +#endif + next_element = HTBTree_next(tree,NULL); + while (next_element != NULL) + { +#ifndef BTREE_TRACE + printf("The next element is %s\n",next_element->object); +#endif + next_element = HTBTree_next(tree,next_element); + } + HTBTree_free(tree); +} + + +#endif diff --git a/WWW/Library/Implementation/HTBTree.h b/WWW/Library/Implementation/HTBTree.h new file mode 100644 index 00000000..55bfe6ab --- /dev/null +++ b/WWW/Library/Implementation/HTBTree.h @@ -0,0 +1,104 @@ +/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTBTree.html + BALANCED BINARY TREE FOR SORTING THINGS + + Tree creation, traversal and freeing. User-supplied comparison routine. + + Author: Arthur Secret, CERN. Public domain. Please mail bugs and changes to + www-request@info.cern.ch + + part of libWWW + + */ +#ifdef SHORT_NAMES +#define HTBTree_new HTBTNew +#define HTBTree_free HTBTFree +#define HTBTreeAndObject_free HTBTAOFr +#define HTBTree_add HTBTAdd +#define HTBTree_next HTBTNext +/* #define HTBTree_object HTBTObje already a macro */ +#endif + + +/* + +Data structures + + */ +typedef struct _HTBTree_element { + void *object; /* User object */ + struct _HTBTree_element *up; + struct _HTBTree_element *left; + int left_depth; + struct _HTBTree_element *right; + int right_depth; +} HTBTElement; + +typedef int (*HTComparer) PARAMS((void * a, void * b)); + +typedef struct _HTBTree_top { + HTComparer compare; + struct _HTBTree_element *top; +} HTBTree; + + +/* + +Create a binary tree given its discrimination routine + + */ +extern HTBTree * HTBTree_new PARAMS((HTComparer comp)); + + + +/* + +Free storage of the tree but not of the objects + + */ +extern void HTBTree_free PARAMS((HTBTree* tree)); + + + +/* + +Free storage of the tree and of the objects + + */ +extern void HTBTreeAndObject_free PARAMS((HTBTree* tree)); + + + +/* + +Add an object to a binary tree + + */ + +extern void HTBTree_add PARAMS((HTBTree* tree, void * object)); + + +/* + +Find user object for element + + */ +#define HTBTree_object(element) ((element)->object) + + +/* + +Find next element in depth-first order + + ON ENTRY, + + ele if NULL, start with leftmost element. if != 0 give next object to + the right. + + returns Pointer to element ot NULL if none left. + + */ +extern HTBTElement * HTBTree_next PARAMS((HTBTree* tree, HTBTElement * ele)); + +/* + + end */ diff --git a/WWW/Library/Implementation/HTCJK.h b/WWW/Library/Implementation/HTCJK.h new file mode 100644 index 00000000..8944cd42 --- /dev/null +++ b/WWW/Library/Implementation/HTCJK.h @@ -0,0 +1,110 @@ +/* CJK character converter HTCJK.h +** ======================= +** +** Added 11-Jun-96 by FM, based on jiscode.h for +** Yutaka Sato's (ysato@etl.go.jp) SJIS.c, and +** Takuya ASADA's (asada@three-a.co.jp) CJK patches. +** (see SGML.c). +** +*/ + +#ifndef HTCJK_H +#define HTCJK_H + +/* +** STATUS CHANGE CODES +*/ +#ifdef ESC +#undef ESC +#endif /* ESC */ +#define ESC '\033' +#define TO_2BCODE '$' +#define TO_1BCODE '(' + +#define TO_KANA '\016' +#define TO_KANAOUT '\017' + +#define TO_KANJI "\033$B" +#define TO_HANJI "\033$A" +#define TO_HANGUL "\033$(C" +#define TO_ASCII "\033(B" + +#define IS_SJIS_LO(lo) ((0x40<=lo)&&(lo!=0x7F)&&(lo<=0xFC)) +#define IS_SJIS_HI1(hi) ((0x81<=hi)&&(hi<=0x9F)) /* 1st lev. */ +#define IS_SJIS_HI2(hi) ((0xE0<=hi)&&(hi<=0xEF)) /* 2nd lev. */ +#define IS_SJIS(hi,lo,in_sjis) (!IS_SJIS_LO(lo)?0:IS_SJIS_HI1(hi)?(in_sjis=1):in_sjis&&IS_SJIS_HI2(hi)) + +#define IS_EUC_LOS(lo) ((0x21<=lo)&&(lo<=0x7E)) /* standard */ +#define IS_EUC_LOX(lo) ((0xA1<=lo)&&(lo<=0xFE)) /* extended */ +#define IS_EUC_HI(hi) ((0xA1<=hi)&&(hi<=0xFE)) +#define IS_EUC(hi,lo) IS_EUC_HI(hi) && (IS_EUC_LOS(lo) || IS_EUC_LOX(lo)) + +#define IS_BIG5_LOS(lo) ((0x40<=lo)&&(lo<=0x7E)) /* standard */ +#define IS_BIG5_LOX(lo) ((0xA1<=lo)&&(lo<=0xFE)) /* extended */ +#define IS_BIG5_HI(hi) ((0xA1<=hi)&&(hi<=0xFE)) +#define IS_BIG5(hi,lo) IS_BIG5_HI(hi) && (IS_BIG5_LOS(lo) || IS_BIG5_LOX(lo)) + +typedef enum _HTkcode {NOKANJI, EUC, SJIS, JIS} HTkcode; +typedef enum _HTCJKlang {NOCJK, JAPANESE, CHINESE, KOREAN, TAIPEI} HTCJKlang; + +/* +** Function prototypes. +*/ +extern void JISx0201TO0208_EUC PARAMS(( + register unsigned char IHI, + register unsigned char ILO, + register unsigned char * OHI, + register unsigned char * OLO)); + +extern unsigned char * SJIS_TO_JIS1 PARAMS(( + register unsigned char HI, + register unsigned char LO, + register unsigned char * JCODE)); + +extern unsigned char * JIS_TO_SJIS1 PARAMS(( + register unsigned char HI, + register unsigned char LO, + register unsigned char * SJCODE)); + +extern unsigned char * EUC_TO_SJIS1 PARAMS(( + unsigned char HI, + unsigned char LO, + register unsigned char * SJCODE)); + +extern void JISx0201TO0208_SJIS PARAMS(( + register unsigned char I, + register unsigned char * OHI, + register unsigned char * OLO)); + +extern unsigned char * SJIS_TO_EUC1 PARAMS(( + unsigned char HI, + unsigned char LO, + unsigned char * EUC)); + +extern unsigned char * SJIS_TO_EUC PARAMS(( + unsigned char * src, + unsigned char * dst)); + +extern unsigned char * EUC_TO_SJIS PARAMS(( + unsigned char * src, + unsigned char * dst)); + +extern unsigned char * EUC_TO_JIS PARAMS(( + unsigned char * src, + unsigned char * dst, + CONST char * toK, + CONST char * toA)); + +extern unsigned char * TO_EUC PARAMS(( + unsigned char * jis, + unsigned char * euc)); + +extern void TO_SJIS PARAMS(( + unsigned char * any, + unsigned char * sjis)); + +extern void TO_JIS PARAMS(( + unsigned char * any, + unsigned char * jis)); + +#endif /* HTCJK_H */ diff --git a/WWW/Library/Implementation/HTChunk.c b/WWW/Library/Implementation/HTChunk.c new file mode 100644 index 00000000..fc46ac4a --- /dev/null +++ b/WWW/Library/Implementation/HTChunk.c @@ -0,0 +1,100 @@ +/* Chunk handling: Flexible arrays +** =============================== +** +*/ + +#include "HTUtils.h" +#include "HTChunk.h" +/*#include <stdio.h> included by HTUtils.h -- FM */ + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +/* Create a chunk with a certain allocation unit +** -------------- +*/ +PUBLIC HTChunk * HTChunkCreate ARGS1 (int,grow) +{ + HTChunk * ch = (HTChunk *) calloc(1, sizeof(HTChunk)); + if (ch == NULL) + outofmem(__FILE__, "creation of chunk"); + + ch->data = 0; + ch->growby = grow; + ch->size = 0; + ch->allocated = 0; + return ch; +} + + +/* Clear a chunk of all data +** -------------------------- +*/ +PUBLIC void HTChunkClear ARGS1 (HTChunk *,ch) +{ + FREE(ch->data); + ch->size = 0; + ch->allocated = 0; +} + + +/* Free a chunk +** ------------ +*/ +PUBLIC void HTChunkFree ARGS1 (HTChunk *,ch) +{ + FREE(ch->data); + FREE(ch); +} + + +/* Append a character +** ------------------ +*/ +PUBLIC void HTChunkPutc ARGS2 (HTChunk *,ch, char,c) +{ + if (ch->size >= ch->allocated) { + ch->allocated = ch->allocated + ch->growby; + ch->data = ch->data ? (char *)realloc(ch->data, ch->allocated) + : (char *)calloc(1, ch->allocated); + if (!ch->data) + outofmem(__FILE__, "HTChunkPutc"); + } + ch->data[ch->size++] = c; +} + + +/* Ensure a certain size +** --------------------- +*/ +PUBLIC void HTChunkEnsure ARGS2 (HTChunk *,ch, int,needed) +{ + if (needed <= ch->allocated) return; + ch->allocated = needed-1 - ((needed-1) % ch->growby) + + ch->growby; /* Round up */ + ch->data = ch->data ? (char *)realloc(ch->data, ch->allocated) + : (char *)calloc(1, ch->allocated); + if (ch->data == NULL) + outofmem(__FILE__, "HTChunkEnsure"); +} + + +/* Terminate a chunk +** ----------------- +*/ +PUBLIC void HTChunkTerminate ARGS1 (HTChunk *,ch) +{ + HTChunkPutc(ch, (char)0); +} + + +/* Append a string +** --------------- +*/ +PUBLIC void HTChunkPuts ARGS2 (HTChunk *,ch, CONST char *,s) +{ + CONST char * p; + for (p=s; *p; p++) + HTChunkPutc(ch, *p); +} diff --git a/WWW/Library/Implementation/HTChunk.h b/WWW/Library/Implementation/HTChunk.h new file mode 100644 index 00000000..260f798a --- /dev/null +++ b/WWW/Library/Implementation/HTChunk.h @@ -0,0 +1,160 @@ +/* HTChunk: Flexible array handling for libwww + CHUNK HANDLING: + FLEXIBLE ARRAYS + + This module implements a flexible array. It is a general utility module. A chunk is a + structure which may be extended. These routines create and append data to chunks, + automatically reallocating them as necessary. + + */ +typedef struct { + int size; /* In bytes */ + int growby; /* Allocation unit in bytes */ + int allocated; /* Current size of *data */ + char * data; /* Pointer to malloced area or 0 */ +} HTChunk; + + +#ifdef SHORT_NAMES +#define HTChunkClear HTChClea +#define HTChunkPutc HTChPutc +#define HTChunkPuts HTChPuts +#define HTChunkCreate HTChCrea +#define HTChunkTerminate HTChTerm +#define HTChunkEnsure HtChEnsu +#endif + + +/* + +Create new chunk + + ON ENTRY, + + growby The number of bytes to allocate at a time when the chunk is + later extended. Arbitrary but normally a trade-off time vs. + memory + + ON EXIT, + + returns A chunk pointer to the new chunk, + + */ + +extern HTChunk * HTChunkCreate PARAMS((int growby)); + + +/* + +Free a chunk + + ON ENTRY, + + ch A valid chunk pointer made by HTChunkCreate() + + ON EXIT, + + ch is invalid and may not be used. + + */ + +extern void HTChunkFree PARAMS((HTChunk * ch)); + + +/* + +Clear a chunk + + ON ENTRY, + + ch A valid chunk pointer made by HTChunkCreate() + + ON EXIT, + + *ch The size of the chunk is zero. + + */ + +extern void HTChunkClear PARAMS((HTChunk * ch)); + + +/* + +Ensure a chunk has a certain space in + + ON ENTRY, + + ch A valid chunk pointer made by HTChunkCreate() + + s The size required + + ON EXIT, + + *ch Has size at least s + + */ + +extern void HTChunkEnsure PARAMS((HTChunk * ch, int s)); + + +/* + +Append a character to a chunk + + ON ENTRY, + + ch A valid chunk pointer made by HTChunkCreate() + + c The character to be appended + + ON EXIT, + + *ch Is one character bigger + + */ +extern void HTChunkPutc PARAMS((HTChunk * ch, char c)); + +/* + +Append a string to a chunk + + ON ENTRY, + + ch A valid chunk pointer made by HTChunkCreate() + + str Tpoints to a zero-terminated string to be appended + + ON EXIT, + + *ch Is bigger by strlen(str) + + */ + + +extern void HTChunkPuts PARAMS((HTChunk * ch, const char *str)); + + +/* + +Append a zero character to a chunk + + */ + +/* + + ON ENTRY, + + ch A valid chunk pointer made by HTChunkCreate() + + ON EXIT, + + *ch Is one character bigger + + */ + + +extern void HTChunkTerminate PARAMS((HTChunk * ch)); + +/* + + end */ diff --git a/WWW/Library/Implementation/HTFTP.c b/WWW/Library/Implementation/HTFTP.c new file mode 100644 index 00000000..9d19610f --- /dev/null +++ b/WWW/Library/Implementation/HTFTP.c @@ -0,0 +1,3214 @@ +/* File Transfer Protocol (FTP) Client +** for a WorldWideWeb browser +** =================================== +** +** A cache of control connections is kept. +** +** Note: Port allocation +** +** It is essential that the port is allocated by the system, rather +** than chosen in rotation by us (POLL_PORTS), or the following +** problem occurs. +** +** It seems that an attempt by the server to connect to a port which has +** been used recently by a listen on the same socket, or by another +** socket this or another process causes a hangup of (almost exactly) +** one minute. Therefore, we have to use a rotating port number. +** The problem remains that if the application is run twice in quick +** succession, it will hang for what remains of a minute. +** +** Authors +** TBL Tim Berners-lee <timbl@info.cern.ch> +** DD Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu> +** LM Lou Montulli <montulli@ukanaix.cc.ukans.edu> +** FM Foteos Macrides <macrides@sci.wfeb.edu> +** History: +** 2 May 91 Written TBL, as a part of the WorldWideWeb project. +** 15 Jan 92 Bug fix: close() was used for NETCLOSE for control soc +** 10 Feb 92 Retry if cached connection times out or breaks +** 8 Dec 92 Bug fix 921208 TBL after DD +** 17 Dec 92 Anon FTP password now just WWWuser@ suggested by DD +** fails on princeton.edu! +** 27 Dec 93 (FM) Fixed up so FTP now works with VMS hosts. Path +** must be Unix-style and cannot include the device +** or top directory. +** ?? ??? ?? (LM) Added code to prompt and send passwords for non +** anonymous FTP +** 25 Mar 94 (LM) Added code to recognize different ftp server types +** and code to parse dates and sizes on most hosts. +** 27 Mar 93 (FM) Added code for getting dates and sizes on VMS hosts. +** +** Options: +** LISTEN We listen, the other guy connects for data. +** Otherwise, other way round, but problem finding our +** internet address! +** +** Notes: +** Portions Copyright 1994 Trustees of Dartmouth College +** Code for recognizing different FTP servers and +** parsing "ls -l" output taken from Macintosh Fetch +** program with permission from Jim Matthews, +** Dartmouth Software Development Team. +*/ + +/* +** If LISTEN is not defined, PASV is used instead of PORT, and not +** all FTP servers support PASV, so define it unless there is no +** alternative for your system. +*/ +#ifndef NOPORT +#define LISTEN /* @@@@ Test LJM */ +#endif /* !NOPORT */ + +/* +BUGS: @@@ Limit connection cache size! + Error reporting to user. + 400 & 500 errors are acked by user with windows. + Use configuration file for user names + +** Note for portablility this version does not use select() and +** so does not watch the control and data channels at the +** same time. +*/ + +#include "HTUtils.h" +#include "tcp.h" + +#include "HTAlert.h" + +#include "HTFTP.h" /* Implemented here */ + +/* this define should be in HTFont.h :( */ +#define HT_NON_BREAK_SPACE ((char)1) /* For now */ + +#define REPEAT_PORT /* Give the port number for each file */ +#define REPEAT_LISTEN /* Close each listen socket and open a new one */ + +/* define POLL_PORTS If allocation does not work, poll ourselves.*/ +#define LISTEN_BACKLOG 2 /* Number of pending connect requests (TCP)*/ + +#define FIRST_TCP_PORT 1024 /* Region to try for a listening port */ +#define LAST_TCP_PORT 5999 + +#define LINE_LENGTH 256 +#define COMMAND_LENGTH 256 + +#define INFINITY 512 + +#include "HTParse.h" +#include "HTTCP.h" +#include "HTAnchor.h" +#include "HTFile.h" /* For HTFileFormat() */ +#include "HTBTree.h" +#include "HTChunk.h" +#include "HTAlert.h" +#ifndef IPPORT_FTP +#define IPPORT_FTP 21 +#endif /* !IPORT_FTP */ + +#include "LYLeaks.h" + +#ifdef REMOVED_CODE +extern char *malloc(); +extern void free(); +extern char *strncpy(); +#endif /* REMOVED_CODE */ + +typedef struct _connection { + struct _connection * next; /* Link on list */ + u_long addr; /* IP address */ + int socket; /* Socket number for communication */ + BOOL binary; /* Binary mode? */ +} connection; + +#ifndef NIL +#define NIL 0 +#endif /* !NIL */ + +/* Hypertext object building machinery +*/ +#include "HTML.h" + +#define PUTC(c) (*targetClass.put_character)(target, c) +#define PUTS(s) (*targetClass.put_string)(target, s) +#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0) +#define END(e) (*targetClass.end_element)(target, e, 0) +#define FREE_TARGET (*targetClass._free)(target) +#define ABORT_TARGET (*targetClass._free)(target) +struct _HTStructured { + CONST HTStructuredClass * isa; + /* ... */ +}; + +#define FREE(x) if (x) {free(x); x = NULL;} + +extern int HTCheckForInterrupt NOPARAMS; + + +/* Global Variables +** --------------------- +*/ +PUBLIC BOOLEAN HTfileSortMethod = FILE_BY_NAME; +PRIVATE char ThisYear[8]; +PRIVATE char LastYear[8]; +PRIVATE int TheDate; +PRIVATE BOOLEAN HaveYears = FALSE; +#ifdef SOCKS +extern BOOLEAN socks_flag; +extern unsigned long socks_bind_remoteAddr; +#endif /* SOCKS */ +extern char *personal_mail_address; + +/* Module-Wide Variables +** --------------------- +*/ +PRIVATE connection * connections = 0; /* Linked list of connections */ +PRIVATE char response_text[LINE_LENGTH+1];/* Last response from NewsHost */ +PRIVATE connection * control; /* Current connection */ +PRIVATE int data_soc = -1; /* Socket for data transfer =invalid */ + +#define GENERIC_SERVER 0 +#define MACHTEN_SERVER 1 +#define UNIX_SERVER 2 +#define VMS_SERVER 3 +#define CMS_SERVER 4 +#define DCTS_SERVER 5 +#define TCPC_SERVER 6 +#define PETER_LEWIS_SERVER 7 +#define NCSA_SERVER 8 +#define WINDOWS_NT_SERVER 9 +#define MS_WINDOWS_SERVER 10 +#define MSDOS_SERVER 11 + +PRIVATE int server_type = GENERIC_SERVER; /* the type of ftp host */ +PRIVATE int unsure_type = FALSE; /* sure about the type? */ +PRIVATE BOOLEAN use_list = FALSE; /* use the LIST command? */ + +PRIVATE interrupted_in_next_data_char = FALSE; + +#ifdef POLL_PORTS +PRIVATE unsigned short port_number = FIRST_TCP_PORT; +#endif /* POLL_PORTS */ + +#ifdef LISTEN +PRIVATE int master_socket = -1; /* Listening socket = invalid */ +PRIVATE char port_command[255]; /* Command for setting the port */ +PRIVATE fd_set open_sockets; /* Mask of active channels */ +PRIVATE int num_sockets; /* Number of sockets to scan */ +#else +PRIVATE unsigned short passive_port; /* Port server specified for data */ +#endif /* LISTEN */ + + +#define NEXT_CHAR HTGetCharacter() /* Use function in HTFormat.c */ + +#define DATA_BUFFER_SIZE 2048 +PRIVATE char data_buffer[DATA_BUFFER_SIZE]; /* Input data buffer */ +PRIVATE char * data_read_pointer; +PRIVATE char * data_write_pointer; +#define NEXT_DATA_CHAR next_data_char() + + +/* PUBLIC HTMake_VMS_name() +** CONVERTS WWW name into a VMS name +** ON ENTRY: +** nn Node Name (optional) +** fn WWW file name +** +** ON EXIT: +** returns vms file specification +** +** Bug: Returns pointer to static -- non-reentrant +*/ +PUBLIC char * HTMake_VMS_name ARGS2( + CONST char *, nn, + CONST char *, fn) +{ + +/* We try converting the filename into Files-11 syntax. That is, we assume +** first that the file is, like us, on a VMS node. We try remote +** (or local) DECnet access. Files-11, VMS, VAX and DECnet +** are trademarks of Digital Equipment Corporation. +** The node is assumed to be local if the hostname WITHOUT DOMAIN +** matches the local one. @@@ +*/ + static char vmsname[INFINITY]; /* returned */ + char * filename = (char*)malloc(strlen(fn)+1); + char * nodename = (char*)malloc(strlen(nn)+2+1); /* Copies to hack */ + char *second; /* 2nd slash */ + char *last; /* last slash */ + + char * hostname = (char *)HTHostName(); + + if (!filename || !nodename) + outofmem(__FILE__, "HTVMSname"); + strcpy(filename, fn); + strcpy(nodename, ""); /* On same node? Yes if node names match */ + if (strncmp(nn, "localhost", 9)) { + char *p, *q; + for (p = hostname, q = (char *)nn; + *p && *p != '.' && *q && *q != '.'; p++, q++){ + if (TOUPPER(*p) != TOUPPER(*q)) { + strcpy(nodename, nn); + q = strchr(nodename, '.'); /* Mismatch */ + if (q) + *q = '\0'; /* Chop domain */ + strcat(nodename, "::"); /* Try decnet anyway */ + break; + } + } + } + + second = strchr(filename+1, '/'); /* 2nd slash */ + last = strrchr(filename, '/'); /* last slash */ + + if (!second) { /* Only one slash */ + sprintf(vmsname, "%s%s", nodename, filename + 1); + } else if (second == last) { /* Exactly two slashes */ + *second = '\0'; /* Split filename from disk */ + sprintf(vmsname, "%s%s:%s", nodename, filename+1, second+1); + *second = '/'; /* restore */ + } else { /* More than two slashes */ + char * p; + *second = '\0'; /* Split disk from directories */ + *last = '\0'; /* Split dir from filename */ + sprintf(vmsname, "%s%s:[%s]%s", + nodename, filename+1, second+1, last+1); + *second = *last = '/'; /* restore filename */ + for (p = strchr(vmsname, '['); *p!=']'; p++) + if (*p == '/') + *p = '.'; /* Convert dir sep. to dots */ + } + FREE(nodename); + FREE(filename); + return vmsname; +} + +/* Procedure: Read a character from the data connection +** ---------------------------------------------------- +*/ +PRIVATE char next_data_char NOARGS +{ + int status; + if (data_read_pointer >= data_write_pointer) { + status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE); + if (status == HT_INTERRUPTED) + interrupted_in_next_data_char = 1; + if (status <= 0) + return (char)-1; + data_write_pointer = data_buffer + status; + data_read_pointer = data_buffer; + } +#ifdef NOT_ASCII + { + char c = *data_read_pointer++; + return FROMASCII(c); + } +#else + return *data_read_pointer++; +#endif /* NOT_ASCII */ +} + + +/* Close an individual connection +** +*/ +PRIVATE int close_connection ARGS1( + connection *, con) +{ + connection * scan; + int status = NETCLOSE(con->socket); + if (TRACE) + fprintf(stderr, "HTFTP: Closing control socket %d\n", con->socket); + con->socket = -1; + if (connections == con) { + connections = con->next; + return status; + } + for (scan = connections; scan; scan = scan->next) { + if (scan->next == con) { + scan->next = con->next; /* Unlink */ + if (control == con) + control = (connection*)0; + return status; + } /*if */ + } /* for */ + return -1; /* very strange -- was not on list. */ +} + +PRIVATE char *help_message_buffer = NULL; /* global :( */ + +PRIVATE void init_help_message_cache NOARGS +{ + FREE(help_message_buffer); +} + +PRIVATE void help_message_cache_add ARGS1( + char *, string) +{ + if (help_message_buffer) + StrAllocCat(help_message_buffer, string); + else + StrAllocCopy(help_message_buffer, string); + + if (TRACE) + fprintf(stderr,"Adding message to help cache: %s\n",string); +} + +PRIVATE char *help_message_cache_non_empty NOARGS +{ + return(help_message_buffer); +} +PRIVATE char *help_message_cache_contents NOARGS +{ + return(help_message_buffer); +} + +/* Execute Command and get Response +** -------------------------------- +** +** See the state machine illustrated in RFC959, p57. This implements +** one command/reply sequence. It also interprets lines which are to +** be continued, which are marked with a "-" immediately after the +** status code. +** +** Continuation then goes on until a line with a matching reply code +** an a space after it. +** +** On entry, +** con points to the connection which is established. +** cmd points to a command, or is NIL to just get the response. +** +** The command is terminated with the CRLF pair. +** +** On exit, +** returns: The first digit of the reply type, +** or negative for communication failure. +*/ +PRIVATE int response ARGS1( + char *, cmd) +{ + int result; /* Three-digit decimal code */ + int continuation_response = -1; + int status; + extern int interrupted_in_htgetcharacter; + + if (!control) { + if (TRACE) + fprintf(stderr, "HTFTP: No control connection set up!!\n"); + return -99; + } + + if (cmd) { + if (TRACE) + fprintf(stderr, " Tx: %s", cmd); +#ifdef NOT_ASCII + { + char * p; + for (p = cmd; *p; p++) { + *p = TOASCII(*p); + } + } +#endif /* NOT_ASCII */ + status = NETWRITE(control->socket, cmd, (int)strlen(cmd)); + if (status < 0) { + if (TRACE) + fprintf(stderr, + "HTFTP: Error %d sending command: closing socket %d\n", + status, control->socket); + close_connection(control); + return status; + } + } + + do { + char *p = response_text; + for (;;) { + if (((*p++ = NEXT_CHAR) == LF) + || (p == &response_text[LINE_LENGTH])) { + + char continuation; + + if (interrupted_in_htgetcharacter) + { + if (TRACE) + fprintf (stderr, + "HTFTP: Interrupted in HTGetCharacter, apparently.\n"); + NETCLOSE (control->socket); + control->socket = -1; + return HT_INTERRUPTED; + } + + *p = '\0'; /* Terminate the string */ + if (TRACE) + fprintf(stderr, " Rx: %s", response_text); + + /* Check for login or help messages */ + if (!strncmp(response_text,"230-",4) || + !strncmp(response_text,"250-",4) || + !strncmp(response_text,"220-",4)) + help_message_cache_add(response_text+4); + + sscanf(response_text, "%d%c", &result, &continuation); + if (continuation_response == -1) { + if (continuation == '-') /* start continuation */ + continuation_response = result; + } else { /* continuing */ + if (continuation_response == result && + continuation == ' ') + continuation_response = -1; /* ended */ + } + break; + } /* if end of line */ + + if (interrupted_in_htgetcharacter) + { + if (TRACE) + fprintf (stderr, + "HTFTP: Interrupted in HTGetCharacter, apparently.\n"); + NETCLOSE (control->socket); + control->socket = -1; + return HT_INTERRUPTED; + } + + if (*(p-1) == (char) EOF) { + if (TRACE) + fprintf(stderr, "Error on rx: closing socket %d\n", + control->socket); + strcpy(response_text, "000 *** TCP read error on response\n"); + close_connection(control); + return -1; /* End of file on response */ + } + } /* Loop over characters */ + + } while (continuation_response != -1); + + if (result == 421) { + if (TRACE) + fprintf(stderr, "HTFTP: They close so we close socket %d\n", + control->socket); + close_connection(control); + return -1; + } + if ((result == 255 && server_type == CMS_SERVER) && + (0 == strncasecomp(cmd, "CWD", 3) || + 0 == strcasecomp(cmd, "CDUP"))) { + /* + ** Alas, CMS returns 255 on failure to CWD to parent of root. - PG + */ + result = 555; + } + return result/100; +} + +/* this function should try to set the macintosh server into binary mode + */ +PRIVATE int set_mac_binary NOARGS +{ + /* try to set mac binary mode */ + return(2 == response("MACB\r\n")); +} + +/* This function gets the current working directory to help + * determine what kind of host it is + */ + +PRIVATE void get_ftp_pwd ARGS2( + int *, server_type, + BOOLEAN *, use_list) +{ + + char *cp; + /* get the working directory (to see what it looks like) */ + int status = response("PWD\r\n"); + if (status < 0) { + return; + } else { + cp = strchr(response_text+5,'"'); + if (cp) + *cp = '\0'; + if (*server_type == TCPC_SERVER) { + *server_type = ((response_text[5] == '/') ? + NCSA_SERVER : TCPC_SERVER); + if (TRACE) + fprintf(stderr, "HTFTP: Treating as %s server.\n", + ((*server_type == NCSA_SERVER) ? + "NCSA" : "TCPC")); + } else if (response_text[5] == '/') { + /* path names beginning with / imply Unix, + * right? + */ + if (set_mac_binary()) { + *server_type = NCSA_SERVER; + if (TRACE) + fprintf(stderr, "HTFTP: Treating as NCSA server.\n"); + } else { + *server_type = UNIX_SERVER; + *use_list = TRUE; + if (TRACE) + fprintf(stderr, "HTFTP: Treating as Unix server.\n"); + } + return; + } else if (response_text[strlen(response_text)-1] == ']') { + /* path names ending with ] imply VMS, right? */ + *server_type = VMS_SERVER; + *use_list = TRUE; + if (TRACE) + fprintf(stderr, "HTFTP: Treating as VMS server.\n"); + } else { + *server_type = GENERIC_SERVER; + if (TRACE) + fprintf(stderr, "HTFTP: Treating as Generic server.\n"); + } + + if ((*server_type == NCSA_SERVER) || + (*server_type == TCPC_SERVER) || + (*server_type == PETER_LEWIS_SERVER)) + set_mac_binary(); + } +} + +/* Get a valid connection to the host +** ---------------------------------- +** +** On entry, +** arg points to the name of the host in a hypertext address +** On exit, +** returns <0 if error +** socket number if success +** +** This routine takes care of managing timed-out connections, and +** limiting the number of connections in use at any one time. +** +** It ensures that all connections are logged in if they exist. +** It ensures they have the port number transferred. +*/ +PRIVATE int get_connection ARGS1( + CONST char *, arg) +{ + int status; + char * command; + connection * con; + char * username=NULL; + char * password=NULL; + static char *user_entered_password=NULL; + static char *last_username_and_host=NULL; + + /* + ** Allocate and init control struct. + */ + con = (connection *)calloc(1, sizeof(connection)); + + if (!arg) return -1; /* Bad if no name sepcified */ + if (!*arg) return -1; /* Bad if name had zero length */ + +/* Get node name: +*/ + { + char *p1 = HTParse(arg, "", PARSE_HOST); + char *p2 = strrchr(p1, '@'); /* user? */ + char * pw = NULL; + + if (p2 != NULL) { + username = p1; + *p2 = '\0'; /* terminate */ + p1 = p2+1; /* point to host */ + pw = strchr(username, ':'); + if (pw != NULL) { + *pw++ = '\0'; + password = HTUnEscape(pw); + } + if (*username) + HTUnEscape(username); + + /* if the password doesn't exist then we are going to have + * to ask the user for it. The only problem is that we + * don't want to ask for it every time, so we will store + * away in a primitive fashion. + */ + if (!password) { + char tmp[256]; + + sprintf(tmp, "%s@%s", username, p1); + /* if the user@host is not equal to the last time through + * or user_entered_password has no data then we need + * to ask the user for the password + */ + if (!last_username_and_host || + strcmp(tmp, last_username_and_host) || + !user_entered_password) { + + StrAllocCopy(last_username_and_host, tmp); + sprintf(tmp, "Enter password for user %s@%s:", + username, p1); + FREE(user_entered_password); + user_entered_password = (char *)HTPromptPassword(tmp); + + } /* else we already know the password */ + password = user_entered_password; + } + } + + if (!username) + FREE(p1); + } /* scope of p1 */ + + + con->socket = -1; + status = HTDoConnect (arg, "FTP", IPPORT_FTP, (int *)&con->socket); + + if (status < 0) + { + if (TRACE) + { + if (status == HT_INTERRUPTED) + fprintf (stderr, + "HTFTP: Interrupted on connect\n"); + else + fprintf(stderr, + "HTFTP: Unable to connect to remote host for `%s'.\n", + arg); + } + if (status == HT_INTERRUPTED) + _HTProgress ("Connection interrupted."); + else + HTAlert("Unable to connect to FTP host."); + if (con->socket != -1) + { + NETCLOSE(con->socket); + } + + FREE(username); + FREE(con); + return status; /* Bad return */ + } + + if (TRACE) + fprintf(stderr, "FTP connected, socket %ld\n", (long)con); + control = con; /* Current control connection */ + + /* Initialise buffering for control connection */ + HTInitInput(control->socket); + init_help_message_cache(); /* Clear the login message buffer. */ + + +/* Now we log in Look up username, prompt for pw. +*/ + { + int status = response((char *)0); /* Get greeting */ + + if (status == HT_INTERRUPTED) + { + if (TRACE) + fprintf (stderr, + "HTFTP: Interrupted at beginning of login.\n"); + _HTProgress ("Connection interrupted."); + NETCLOSE(control->socket); + control->socket = -1; + return HT_INTERRUPTED; + } + if (status == 2) { /* Send username */ + if (username && *username) { + command = (char*)malloc(10+strlen(username)+2+1); + if (command == NULL) + outofmem(__FILE__, "get_connection"); + sprintf(command, "USER %s%c%c", username, CR, LF); + } else { + command = (char*)malloc(24); + if (command == NULL) + outofmem(__FILE__, "get_connection"); + sprintf(command, "USER anonymous%c%c", CR, LF); + } + status = response(command); + FREE(command); + if (status == HT_INTERRUPTED) + { + if (TRACE) + fprintf (stderr, + "HTFTP: Interrupted while sending username.\n"); + _HTProgress ("Connection interrupted."); + NETCLOSE(control->socket); + control->socket = -1; + return HT_INTERRUPTED; + } + } + if (status == 3) { /* Send password */ + if (password) { + /* + * We have non-zero length password, so send it. - FM + */ + command = (char*)malloc(10+strlen(password)+2+1); + if (command == NULL) + outofmem(__FILE__, "get_connection"); + sprintf(command, "PASS %s%c%c", password, CR, LF); + } else { + /* + * Create and send a mail address as the password. - FM + */ + char *user = NULL; + char *host = NULL; + char * cp; + + if (personal_mail_address && *personal_mail_address) { + /* + * We have a non-zero length personal + * mail address, so use that. - FM + */ + StrAllocCopy(user, personal_mail_address); + if ((cp=strchr(user, '@')) != NULL) { + *cp++ = '\0'; + host = cp; + } else { + host = (char *)HTHostName(); + } + } else { + /* + * Use an environment variable and the host global. - FM + */ + if ((cp=getenv("USER")) != NULL) + StrAllocCopy(user, cp); + else + StrAllocCopy(user, "WWWuser"); + host = (char *)HTHostName(); + } + + /* + * If host is not fully qualified, suppress it + * as ftp.uu.net prefers a blank to a bad name + */ + if (!(host) || strchr(host, '.') == NULL) + host = ""; + + command = (char*)malloc(10+strlen(user)+1+strlen(host)+2+1); + if (command == NULL) + outofmem(__FILE__, "get_connection"); + sprintf(command, "PASS %s@%s%c%c", user, host, CR, LF); + FREE(user); + } + status = response(command); + FREE(command); + if (status == HT_INTERRUPTED) + { + if (TRACE) + fprintf (stderr, + "HTFTP: Interrupted while sending password.\n"); + _HTProgress ("Connection interrupted."); + NETCLOSE(control->socket); + control->socket = -1; + return HT_INTERRUPTED; + } + } + FREE(username); + + if (status == 3) { + char temp[80]; + sprintf(temp, "ACCT noaccount%c%c", CR, LF); + status = response(temp); + if (status == HT_INTERRUPTED) + { + if (TRACE) + fprintf (stderr, + "HTFTP: Interrupted while sending password.\n"); + _HTProgress ("Connection interrupted."); + NETCLOSE(control->socket); + control->socket = -1; + return HT_INTERRUPTED; + } + + } + if (status != 2) { + if (TRACE) + fprintf(stderr, "HTFTP: Login fail: %s", response_text); + /* if (control->socket > 0) close_connection(control->socket); */ + return -1; /* Bad return */ + } + if (TRACE) fprintf(stderr, "HTFTP: Logged in.\n"); + + /** Check for host type **/ + server_type = GENERIC_SERVER; /* reset */ + use_list = FALSE; /* reset */ + if ((status=response("SYST\r\n")) == 2) { + /* we got a line -- what kind of server are we talking to? */ + if (strncmp(response_text+4, + "UNIX Type: L8 MAC-OS MachTen", 28) == 0) { + server_type = MACHTEN_SERVER; + use_list = TRUE; + if (TRACE) + fprintf(stderr, "HTFTP: Treating as MachTen server.\n"); + + } else if (strstr(response_text+4, "UNIX") != NULL || + strstr(response_text+4, "Unix") != NULL) { + server_type = UNIX_SERVER; + use_list = TRUE; + if (TRACE) + fprintf(stderr, "HTFTP: Treating as Unix server.\n"); + + } else if (strstr(response_text+4, "MSDOS") != NULL) { + server_type = MSDOS_SERVER; + use_list = TRUE; + if (TRACE) + fprintf(stderr, + "HTFTP: Treating as MSDOS (Unix emulation) server.\n"); + + } else if (strncmp(response_text+4, "VMS", 3) == 0) { + server_type = VMS_SERVER; + use_list = TRUE; + if (TRACE) + fprintf(stderr, "HTFTP: Treating as VMS server.\n"); + + } else if ((strncmp(response_text+4, "VM/CMS", 6) == 0) || + (strncmp(response_text+4, "VM ", 3) == 0)) { + server_type = CMS_SERVER; + use_list = TRUE; + if (TRACE) + fprintf(stderr, "HTFTP: Treating as CMS server.\n"); + + } else if (strncmp(response_text+4, "DCTS", 4) == 0) { + server_type = DCTS_SERVER; + if (TRACE) + fprintf(stderr, "HTFTP: Treating as DCTS server.\n"); + + } else if (strstr(response_text+4, "MAC-OS TCP/Connect II") != NULL) { + server_type = TCPC_SERVER; + if (TRACE) + fprintf(stderr, "HTFTP: Looks like a TCPC server.\n"); + get_ftp_pwd(&server_type, &use_list); + unsure_type = TRUE; + + } else if (strncmp(response_text+4, "MACOS Peter's Server", 20) == 0) { + server_type = PETER_LEWIS_SERVER; + use_list = TRUE; + set_mac_binary(); + if (TRACE) + fprintf(stderr, + "HTFTP: Treating as Peter Lewis (MACOS) server.\n"); + + } else if (strncmp(response_text+4, "Windows_NT", 10) == 0) { + server_type = WINDOWS_NT_SERVER; + use_list = TRUE; + if (TRACE) + fprintf(stderr, "HTFTP: Treating as Window_NT server.\n"); + + } else if (strncmp(response_text+4, "MS Windows", 10) == 0) { + server_type = MS_WINDOWS_SERVER; + use_list = TRUE; + if (TRACE) + fprintf(stderr, "HTFTP: Treating as MS Windows server.\n"); + + } else { + server_type = GENERIC_SERVER; + if (TRACE) + fprintf(stderr, "HTFTP: Ugh! A Generic server.\n"); + get_ftp_pwd(&server_type, &use_list); + unsure_type = TRUE; + } + } else { + /* SYST fails :( try to get the type from the PWD command */ + get_ftp_pwd(&server_type, &use_list); + } + +/* Now we inform the server of the port number we will listen on +*/ +#ifdef NOTREPEAT_PORT + { + int status = response(port_command); + if (status != 2) { + if (control->socket) + close_connection(control->socket); + return -status; /* Bad return */ + } + if (TRACE) + fprintf(stderr, "HTFTP: Port defined.\n"); + } +#endif /* NOTREPEAT_PORT */ + return con->socket; /* Good return */ + } /* Scope of con */ +} + + +#ifdef LISTEN + +/* Close Master (listening) socket +** ------------------------------- +** +** +*/ +PRIVATE int close_master_socket NOARGS +{ + int status; + FD_CLR(master_socket, &open_sockets); + status = NETCLOSE(master_socket); + if (TRACE) + fprintf(stderr, "HTFTP: Closed master socket %d\n", master_socket); + master_socket = -1; + if (status < 0) + return HTInetStatus("close master socket"); + else + return status; +} + + +/* Open a master socket for listening on +** ------------------------------------- +** +** When data is transferred, we open a port, and wait for the server to +** connect with the data. +** +** On entry, +** master_socket Must be negative if not set up already. +** On exit, +** Returns socket number if good +** less than zero if error. +** master_socket is socket number if good, else negative. +** port_number is valid if good. +*/ +PRIVATE int get_listen_socket NOARGS +{ + struct sockaddr_in soc_address; /* Binary network address */ + struct sockaddr_in* sin = &soc_address; + int new_socket; /* Will be master_socket */ + + + FD_ZERO(&open_sockets); /* Clear our record of open sockets */ + num_sockets = 0; + +#ifndef REPEAT_LISTEN + if (master_socket >= 0) + return master_socket; /* Done already */ +#endif /* !REPEAT_LISTEN */ + +/* Create internet socket +*/ + new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (new_socket < 0) + return HTInetStatus("socket for master socket"); + + if (TRACE) + fprintf(stderr, "HTFTP: Opened master socket number %d\n", new_socket); + +/* Search for a free port. +*/ + sin->sin_family = AF_INET; /* Family = internet, host order */ + sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */ +#ifdef POLL_PORTS + { + unsigned short old_port_number = port_number; + for (port_number = (old_port_number+1); ; port_number++) { + int status; + if (port_number > LAST_TCP_PORT) + port_number = FIRST_TCP_PORT; + if (port_number == old_port_number) { + return HTInetStatus("bind"); + } + soc_address.sin_port = htons(port_number); +#ifdef SOCKS + if (socks_flag) + if ((status=Rbind(new_socket, + (struct sockaddr*)&soc_address, + /* Cast to generic sockaddr */ + sizeof(soc_address) +#ifndef SHORTENED_RBIND + ,socks_bind_remoteAddr +#endif /* !SHORTENED_RBIND */ + )) == 0) + break; + else +#endif /* SOCKS */ + if ((status=bind(new_socket, + (struct sockaddr*)&soc_address, + /* Cast to generic sockaddr */ + sizeof(soc_address))) == 0) + break; + if (TRACE) + fprintf(stderr, + "TCP bind attempt to port %d yields %d, errno=%d\n", + port_number, status, SOCKET_ERRNO); + } /* for */ + } +#else + { + int status; + int address_length = sizeof(soc_address); +#ifdef SOCKS + if (socks_flag) + status = Rgetsockname(control->socket, + (struct sockaddr *)&soc_address, + (void *)&address_length); + else +#endif /* SOCKS */ + status = getsockname(control->socket, + (struct sockaddr *)&soc_address, + (void *)&address_length); + if (status<0) return HTInetStatus("getsockname"); + CTRACE(tfp, "HTFTP: This host is %s\n", + HTInetString(sin)); + + soc_address.sin_port = 0; /* Unspecified: please allocate */ +#ifdef SOCKS + if (socks_flag) + status=Rbind(new_socket, + (struct sockaddr*)&soc_address, + /* Cast to generic sockaddr */ + sizeof(soc_address) +#ifndef SHORTENED_RBIND + ,socks_bind_remoteAddr +#endif /* !SHORTENED_RBIND */ + ); + else +#endif /* SOCKS */ + status=bind(new_socket, + (struct sockaddr*)&soc_address, + /* Cast to generic sockaddr */ + sizeof(soc_address)); + if (status<0) return HTInetStatus("bind"); + + address_length = sizeof(soc_address); +#ifdef SOCKS + if (socks_flag) + status = Rgetsockname(new_socket, + (struct sockaddr*)&soc_address, + (void *)&address_length); + else +#endif /* SOCKS */ + status = getsockname(new_socket, + (struct sockaddr*)&soc_address, + (void *)&address_length); + if (status<0) return HTInetStatus("getsockname"); + } +#endif /* POLL_PORTS */ + + CTRACE(tfp, "HTFTP: bound to port %d on %s\n", + (int)ntohs(sin->sin_port), + HTInetString(sin)); + +#ifdef REPEAT_LISTEN + if (master_socket >= 0) + (void) close_master_socket(); +#endif /* REPEAD_LISTEN */ + + master_socket = new_socket; + +/* Now we must find out who we are to tell the other guy +*/ + (void)HTHostName(); /* Make address valid - doesn't work*/ + sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c", + (int)*((unsigned char *)(&sin->sin_addr)+0), + (int)*((unsigned char *)(&sin->sin_addr)+1), + (int)*((unsigned char *)(&sin->sin_addr)+2), + (int)*((unsigned char *)(&sin->sin_addr)+3), + (int)*((unsigned char *)(&sin->sin_port)+0), + (int)*((unsigned char *)(&sin->sin_port)+1), + CR, LF); + + +/* Inform TCP that we will accept connections +*/ + { + int status; +#ifdef SOCKS + if (socks_flag) + status = Rlisten(master_socket, 1); + else +#endif /* SOCKS */ + status = listen(master_socket, 1); + if (status < 0) { + master_socket = -1; + return HTInetStatus("listen"); + } + } + CTRACE(tfp, "TCP: Master socket(), bind() and listen() all OK\n"); + FD_SET(master_socket, &open_sockets); + if ((master_socket+1) > num_sockets) + num_sockets = master_socket+1; + + return master_socket; /* Good */ + +} /* get_listen_socket */ +#endif /* LISTEN */ + +PRIVATE char * months[12] = { + "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" +}; + +/* Procedure: Set the current and last year strings and date integer +** ----------------------------------------------------------------- +** +** Bug: +** This code is for sorting listings by date, if that option +** is selected in Lynx, and doesn't take into account time +** zones or ensure resetting at midnight, so the sort may not +** be perfect, but the actual date isn't changed in the display, +** i.e., the date is still correct. - FM +*/ +PRIVATE void set_years_and_date NOARGS +{ + char day[8], month[8], date[12]; + time_t NowTime; + int i; + + NowTime = time(NULL); + strncpy(day, (char *)ctime(&NowTime)+8, 2); + day[2] = '\0'; + if (day[0] == ' ') { + day[0] = '0'; + } + strncpy(month, (char *)ctime(&NowTime)+4, 3); + strncpy(month, (char *)ctime(&NowTime)+4, 3); + month[3] = '\0'; + for (i = 0; i < 12; i++) { + if (!strcasecomp(month, months[i])) { + break; + } + } + i++; + sprintf(month, "%s%d", (i < 10 ? "0" : ""), i); + sprintf(date, "9999%s%s", month, day); + TheDate = atoi(date); + strcpy(ThisYear, (char *)ctime(&NowTime)+20); + ThisYear[4] = '\0'; + sprintf(LastYear, "%d", (atoi(ThisYear) - 1)); + HaveYears = TRUE; +} + +typedef struct _EntryInfo { + char * filename; + char * type; + char * date; + unsigned int size; + BOOLEAN display; /* show this entry? */ +} EntryInfo; + +PRIVATE void free_entryinfo_struct_contents ARGS1( + EntryInfo *, entry_info) +{ + if (entry_info) { + FREE(entry_info->filename); + FREE(entry_info->type); + FREE(entry_info->date); + } + /* dont free the struct */ +} + +/* + * is_ls_date() -- + * Return TRUE if s points to a string of the form: + * "Sep 1 1990 " or + * "Sep 11 11:59 " or + * "Dec 12 1989 " or + * "FCv 23 1990 " ... + */ +PRIVATE BOOLEAN is_ls_date ARGS1( + char *, s) +{ + /* must start with three alpha characters */ + if (!isalpha(*s++) || !isalpha(*s++) || !isalpha(*s++)) + return FALSE; + + /* space or HT_NON_BREAK_SPACE */ + if (!(*s == ' ' || *s == HT_NON_BREAK_SPACE)) { + s++; + return FALSE; + } + s++; + + /* space or digit */ + if (!(*s == ' ' || isdigit(*s))) { + s++; + return FALSE; + } + s++; + + /* digit */ + if (!isdigit(*s++)) + return FALSE; + + /* space */ + if (*s++ != ' ') + return FALSE; + + /* space or digit */ + if (!(*s == ' ' || isdigit(*s))) { + s++; + return FALSE; + } + s++; + + /* digit */ + if (!isdigit(*s++)) + return FALSE; + + /* colon or digit */ + if (!(*s == ':' || isdigit(*s))) { + s++; + return FALSE; + } + s++; + + /* digit */ + if (!isdigit(*s++)) + return FALSE; + + /* space or digit */ + if (!(*s == ' ' || isdigit(*s))) { + s++; + return FALSE; + } + s++; + + /* space */ + if (*s++ != ' ') + return FALSE; + + return TRUE; +} /* is_ls_date() */ + +/* + * parse_eplf_line() -- + * Extract the name, size, and date from an EPLF line. - 08-06-96 DJB + */ +PRIVATE void parse_eplf_line ARGS2( + char *, line, + EntryInfo *, info) +{ + char *cp = line; + char ct[26]; + unsigned long size; + time_t secs; + static time_t base; /* time() value on this OS in 1970 */ + static int flagbase = 0; + + if (!flagbase) { + struct tm t; + t.tm_year = 70; t.tm_mon = 0; t.tm_mday = 0; + t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0; + t.tm_isdst = -1; + base = mktime(&t); /* could return -1 */ + flagbase = 1; + } + + while (*cp) { + switch(*cp) { + case '\t': + StrAllocCopy(info->filename, cp + 1); + return; + case 's': + size = 0; + while (*(++cp) && (*cp != ',')) + size = (size * 10) + (*cp - '0'); + info->size = size; + break; + case 'm': + secs = 0; + while (*(++cp) && (*cp != ',')) + secs = (secs * 10) + (*cp - '0'); + secs += base; /* assumes that time_t is #seconds */ + strcpy(ct, ctime(&secs)); + ct[24] = 0; + StrAllocCopy(info->date, ct); + break; + case '/': + StrAllocCopy(info->type, "Directory"); + default: + while (*cp) { + if (*cp++ == ',') + break; + } + break; + } + } +} /* parse_eplf_line */ + +/* + * parse_ls_line() -- + * Extract the name, size, and date from an ls -l line. + */ +PRIVATE void parse_ls_line ARGS2( + char *, line, + EntryInfo *, entry_info) +{ + short i, j; + int base=1; + int size_num=0; + + for (i = strlen(line) - 1; + (i > 13) && (!isspace(line[i]) || !is_ls_date(&line[i-12])); i--) + ; /* null body */ + line[i] = '\0'; + if (i > 13) { + StrAllocCopy(entry_info->date, &line[i-12]); + /* replace the 4th location with nbsp if it is a space or zero */ + if (entry_info->date[4] == ' ' || entry_info->date[4] == '0') + entry_info->date[4] = HT_NON_BREAK_SPACE; + /* make sure year or time is flush right */ + if (entry_info->date[11] == ' ') { + for (j = 11; j > 6; j--) { + entry_info->date[j] = entry_info->date[j-1]; + } + } + } + j = i - 14; + while (isdigit(line[j])) { + size_num += (line[j] - '0') * base; + base *= 10; + j--; + } + entry_info->size = size_num; + StrAllocCopy(entry_info->filename, &line[i + 1]); +} /* parse_ls_line() */ + +/* + * parse_vms_dir_entry() + * Format the name, date, and size from a VMS LIST line + * into the EntryInfo structure - FM + */ +PRIVATE void parse_vms_dir_entry ARGS2( + char *, line, + EntryInfo *, entry_info) +{ + int i, j, ialloc; + char *cp, *cpd, *cps, date[16], *sp = " "; + + /** Get rid of blank lines, and information lines. **/ + /** Valid lines have the ';' version number token. **/ + if (!strlen(line) || (cp=strchr(line, ';')) == NULL) { + entry_info->display = FALSE; + return; + } + + /** Cut out file or directory name at VMS version number. **/ + *cp++ ='\0'; + StrAllocCopy(entry_info->filename,line); + + /** Cast VMS non-README file and directory names to lowercase. **/ + if (strstr(entry_info->filename, "READ") == NULL) { + for (i = 0; entry_info->filename[i]; i++) + entry_info->filename[i] = TOLOWER(entry_info->filename[i]); + } else { + i = strlen(entry_info->filename); + } + + /** Uppercase terminal .z's or _z's. **/ + if ((--i > 2) && + entry_info->filename[i] == 'z' && + (entry_info->filename[i-1] == '.' || + entry_info->filename[i-1] == '_')) + entry_info->filename[i] = 'Z'; + + /** Convert any tabs in rest of line to spaces. **/ + cps = cp-1; + while ((cps=strchr(cps+1, '\t')) != NULL) + *cps = ' '; + + /** Collapse serial spaces. **/ + i = 0; j = 1; + cps = cp; + while (cps[j] != '\0') { + if (cps[i] == ' ' && cps[j] == ' ') + j++; + else + cps[++i] = cps[j++]; + } + cps[++i] = '\0'; + + /* Set the years and date, if we don't have them yet. **/ + if (!HaveYears) { + set_years_and_date(); + } + + /** Track down the date. **/ + if ((cpd=strchr(cp, '-')) != NULL && + strlen(cpd) > 9 && isdigit(*(cpd-1)) && + isalpha(*(cpd+1)) && *(cpd+4) == '-') { + + /** Month **/ + *(cpd+4) = '\0'; + *(cpd+2) = TOLOWER(*(cpd+2)); + *(cpd+3) = TOLOWER(*(cpd+3)); + sprintf(date, "%s ", cpd+1); + *(cpd+4) = '-'; + + /** Day **/ + *cpd = '\0'; + if (isdigit(*(cpd-2))) + sprintf(date+4, "%s ", cpd-2); + else + sprintf(date+4, "%c%s ", HT_NON_BREAK_SPACE, cpd-1); + *cpd = '-'; + + /** Time or Year **/ + if (!strncmp(ThisYear, cpd+5, 4) && + strlen(cpd) > 15 && *(cpd+12) == ':') { + *(cpd+15) = '\0'; + sprintf(date+7, "%s", cpd+10); + *(cpd+15) = ' '; + } else { + *(cpd+9) = '\0'; + sprintf(date+7, " %s", cpd+5); + *(cpd+9) = ' '; + } + + StrAllocCopy(entry_info->date, date); + } + + /** Track down the size **/ + if ((cpd=strchr(cp, '/')) != NULL) { + /* Appears be in used/allocated format */ + cps = cpd; + while (isdigit(*(cps-1))) + cps--; + if (cps < cpd) + *cpd = '\0'; + entry_info->size = atoi(cps); + cps = cpd+1; + while (isdigit(*cps)) + cps++; + *cps = '\0'; + ialloc = atoi(cpd+1); + /* Check if used is in blocks or bytes */ + if (entry_info->size <= ialloc) + entry_info->size *= 512; + + } else if ((cps=strtok(cp, sp)) != NULL) { + /* We just initialized on the version number */ + /* Now let's hunt for a lone, size number */ + while ((cps=strtok(NULL, sp)) != NULL) { + cpd = cps; + while (isdigit(*cpd)) + cpd++; + if (*cpd == '\0') { + /* Assume it's blocks */ + entry_info->size = atoi(cps) * 512; + break; + } + } + } + + /** Wrap it up **/ + if (TRACE) + fprintf(stderr, "HTFTP: VMS filename: %s date: %s size: %d\n", + entry_info->filename, + entry_info->date ? entry_info->date : "", + entry_info->size); + return; +} /* parse_vms_dir_entry() */ + +/* + * parse_ms_windows_dir_entry() -- + * Format the name, date, and size from an MS_WINDOWS LIST line into + * the EntryInfo structure (assumes Chameleon NEWT format). - FM + */ +PRIVATE void parse_ms_windows_dir_entry ARGS2( + char *, line, + EntryInfo *, entry_info) +{ + char *cp = line; + char *cps, *cpd, date[16]; + char *end = line + strlen(line); + + /** Get rid of blank or junk lines. **/ + while (*cp && isspace(*cp)) + cp++; + if (!(*cp)) { + entry_info->display = FALSE; + return; + } + + /** Cut out file or directory name. **/ + cps = cp; + while (*cps && !isspace(*cps)) + cps++; + *cps++ ='\0'; + cpd = cps; + StrAllocCopy(entry_info->filename, cp); + + /** Track down the size **/ + if (cps < end) { + while (*cps && isspace(*cps)) + cps++; + cpd = cps; + while (*cpd && !isspace(*cpd)) + cpd++; + *cpd++ = '\0'; + if (isdigit(*cps)) { + entry_info->size = atoi(cps); + } else { + StrAllocCopy(entry_info->type, "Directory"); + } + } else { + StrAllocCopy(entry_info->type, ""); + } + + /* Set the years and date, if we don't have them yet. **/ + if (!HaveYears) { + set_years_and_date(); + } + + /** Track down the date. **/ + if (cpd < end) { + while (*cpd && isspace(*cpd)) + cpd++; + if (strlen(cpd) > 17) { + *(cpd+6) = '\0'; /* Month and Day */ + *(cpd+11) = '\0'; /* Year */ + *(cpd+17) = '\0'; /* Time */ + if (strcmp(ThisYear, cpd+7)) + /* Not this year, so show the year */ + sprintf(date, "%s %s", cpd, (cpd+7)); + else + /* Is this year, so show the time */ + sprintf(date, "%s %s", cpd, (cpd+12)); + StrAllocCopy(entry_info->date, date); + if (entry_info->date[4] == ' '|| entry_info->date[4] == '0') { + entry_info->date[4] = HT_NON_BREAK_SPACE; + } + } + } + + /** Wrap it up **/ + if (TRACE) + fprintf(stderr, "HTFTP: MS Windows filename: %s date: %s size: %d\n", + entry_info->filename, + entry_info->date ? entry_info->date : "", + entry_info->size); + return; +} /* parse_ms_windows_dir_entry */ + +/* + * parse_windows_nt_dir_entry() -- + * Format the name, date, and size from a WINDOWS_NT LIST line into + * the EntryInfo structure (assumes Chameleon NEWT format). - FM + */ +PRIVATE void parse_windows_nt_dir_entry ARGS2( + char *, line, + EntryInfo *, entry_info) +{ + char *cp = line; + char *cps, *cpd, date[16]; + char *end = line + strlen(line); + int i; + + /** Get rid of blank or junk lines. **/ + while (*cp && isspace(*cp)) + cp++; + if (!(*cp)) { + entry_info->display = FALSE; + return; + } + + /** Cut out file or directory name. **/ + cpd = cp; + cps = (end-1); + while (cps >= cpd && !isspace(*cps)) + cps--; + cp = (cps+1); + if (!strcmp(cp, ".") || !strcmp(cp, "..")) { + entry_info->display = FALSE; + return; + } + StrAllocCopy(entry_info->filename, cp); + if (cps < cpd) + return; + *cp = '\0'; + end = cp; + + /* Set the years and date, if we don't have them yet. **/ + if (!HaveYears) { + set_years_and_date(); + } + + /** Cut out the date. **/ + cp = cps = cpd; + while (*cps && !isspace(*cps)) + cps++; + *cps++ ='\0'; + if (cps > end) { + entry_info->display = FALSE; + return; + } + while (*cps && isspace(*cps)) + cps++; + cpd = cps; + while (*cps && !isspace(*cps)) + cps++; + *cps++ ='\0'; + if (cps > end || cpd == cps || strlen(cpd) < 7) { + entry_info->display = FALSE; + return; + } + if (strlen(cp) == 8 && + isdigit(*cp) && isdigit(*(cp+1)) && *(cp+2) == '-' && + isdigit(*(cp+3)) && isdigit(*(cp+1)) && *(cp+5) == '-') { + *(cp+2) = '\0'; /* Month */ + i = atoi(cp) - 1; + *(cp+5) = '\0'; /* Day */ + sprintf(date, "%s %s", months[i], (cp+3)); + if (date[4] == '0') + date[4] = ' '; + cp += 6; /* Year */ + if (strcmp((ThisYear+2), cp)) { + /* Not this year, so show the year */ + if (atoi(cp) < atoi((char *)&ThisYear[2])) { + sprintf((char *)&date[6], " %c%c%s", + ThisYear[0], ThisYear[1], cp); + } else { + sprintf((char *)&date[6], " %c%c%s", + LastYear[0], LastYear[1], cp); + } + } else { + /* Is this year, so show the time */ + *(cpd+2) = '\0'; /* Hour */ + i = atoi(cpd); + if (*(cpd+5) == 'P' || *(cpd+5) == 'p') + i += 12; + *(cpd+5) = '\0'; + sprintf((char*)&date[6], " %s%d:%s", + (i < 10 ? "0" : ""), i, (cpd+3)); + } + StrAllocCopy(entry_info->date, date); + if (entry_info->date[4] == ' '|| entry_info->date[4] == '0') { + entry_info->date[4] = HT_NON_BREAK_SPACE; + } + } + + /** Track down the size **/ + if (cps < end) { + while (*cps && isspace(*cps)) + cps++; + cpd = cps; + while (*cpd && !isspace(*cpd)) + cpd++; + *cpd = '\0'; + if (isdigit(*cps)) { + entry_info->size = atoi(cps); + } else { + StrAllocCopy(entry_info->type, "Directory"); + } + } else { + StrAllocCopy(entry_info->type, ""); + } + + /** Wrap it up **/ + if (TRACE) + fprintf(stderr, "HTFTP: Windows NT filename: %s date: %s size: %d\n", + entry_info->filename, + entry_info->date ? entry_info->date : "", + entry_info->size); + return; +} /* parse_windows_nt_dir_entry */ + +/* + * parse_cms_dir_entry() -- + * Format the name, date, and size from a VM/CMS line into + * the EntryInfo structure. - FM + */ +PRIVATE void parse_cms_dir_entry ARGS2( + char *, line, + EntryInfo *, entry_info) +{ + char *cp = line; + char *cps, *cpd, date[16]; + char *end = line + strlen(line); + int RecordLength = 0; + int Records = 0; + int i; + + /** Get rid of blank or junk lines. **/ + while (*cp && isspace(*cp)) + cp++; + if (!(*cp)) { + entry_info->display = FALSE; + return; + } + + /** Cut out file or directory name. **/ + cps = cp; + while (*cps && !isspace(*cps)) + cps++; + *cps++ ='\0'; + StrAllocCopy(entry_info->filename, cp); + if (strchr(entry_info->filename, '.') != NULL) + /** If we already have a dot, we did an NLST. **/ + return; + cp = cps; + while (*cp && isspace(*cp)) + cp++; + if (!(*cp)) { + /** If we don't have more, we've misparsed. **/ + FREE(entry_info->filename); + FREE(entry_info->type); + entry_info->display = FALSE; + return; + } + cps = cp; + while (*cps && !isspace(*cps)) + cps++; + *cps++ ='\0'; + if ((0 == strcasecomp(cp, "DIR")) && (cp - line) > 17) { + /** It's an SFS directory. **/ + StrAllocCopy(entry_info->type, "Directory"); + entry_info->size = 0; + } else { + /** It's a file. **/ + cp--; + *cp = '.'; + StrAllocCat(entry_info->filename, cp); + + /** Track down the VM/CMS RECFM or type. **/ + cp = cps; + if (cp < end) { + while (*cp && isspace(*cp)) + cp++; + cps = cp; + while (*cps && !isspace(*cps)) + cps++; + *cps++ = '\0'; + /** Check cp here, if it's relevant someday. **/ + } + } + + /** Track down the record length or dash. **/ + cp = cps; + if (cp < end) { + while (*cp && isspace(*cp)) + cp++; + cps = cp; + while (*cps && !isspace(*cps)) + cps++; + *cps++ = '\0'; + if (isdigit(*cp)) { + RecordLength = atoi(cp); + } + } + + /** Track down the number of records or the dash. **/ + cp = cps; + if (cps < end) { + while (*cp && isspace(*cp)) + cp++; + cps = cp; + while (*cps && !isspace(*cps)) + cps++; + *cps++ = '\0'; + if (isdigit(*cp)) { + Records = atoi(cp); + } + if (Records > 0 && RecordLength > 0) { + /** Compute an approximate size. **/ + entry_info->size = (Records * RecordLength); + } + } + + /** Set the years and date, if we don't have them yet. **/ + if (!HaveYears) { + set_years_and_date(); + } + + /** Track down the date. **/ + cpd = cps; + if (((cps < end) && + (cps = strchr(cpd, ':')) != NULL) && + (cps < (end - 3) && + isdigit(*(cps+1)) && isdigit(*(cps+2)) && *(cps+3) == ':')) { + cps += 3; + *cps = '\0'; + if ((cps - cpd) >= 14) { + cpd = (cps - 14); + *(cpd+2) = '\0'; /* Month */ + *(cpd+5) = '\0'; /* Day */ + *(cpd+8) = '\0'; /* Year */ + cps -= 5; /* Time */ + if (*cpd == ' ') + *cpd = '0'; + i = atoi(cpd) - 1; + sprintf(date, "%s %s", months[i], (cpd+3)); + if (date[4] == '0') + date[4] = ' '; + cpd += 6; /* Year */ + if (strcmp((ThisYear+2), cpd)) { + /* Not this year, so show the year. */ + if (atoi(cpd) < atoi((char *)&ThisYear[2])) { + sprintf((char *)&date[6], " %c%c%s", + ThisYear[0], ThisYear[1], cpd); + } else { + sprintf((char *)&date[6], " %c%c%s", + LastYear[0], LastYear[1], cpd); + } + } else { + /* Is this year, so show the time. */ + *(cps+2) = '\0'; /* Hour */ + i = atoi(cps); + sprintf((char*)&date[6], " %s%d:%s", + (i < 10 ? "0" : ""), i, (cps+3)); + } + StrAllocCopy(entry_info->date, date); + if (entry_info->date[4] == ' '|| entry_info->date[4] == '0') { + entry_info->date[4] = HT_NON_BREAK_SPACE; + } + } + } + + /** Wrap it up. **/ + if (TRACE) + fprintf(stderr, "HTFTP: VM/CMS filename: %s date: %s size: %d\n", + entry_info->filename, + entry_info->date ? entry_info->date : "", + entry_info->size); + return; +} /* parse_cms_dir_entry */ + +/* + * parse_dir_entry() + * Given a line of LIST/NLST output in entry, return results + * and a file/dir name in entry_info struct + * + * If first is true, this is the first name in a directory. + */ + +PRIVATE EntryInfo * parse_dir_entry ARGS2( + char *, entry, + BOOLEAN *, first) +{ + EntryInfo *entry_info; + int i; + int len; + BOOLEAN remove_size=FALSE; + char *cp; + + entry_info = (EntryInfo *)malloc(sizeof(EntryInfo)); + entry_info->filename = NULL; + entry_info->type = NULL; + entry_info->date = NULL; + entry_info->size = 0; + entry_info->display = TRUE; + + switch (server_type) { + case UNIX_SERVER: + case PETER_LEWIS_SERVER: + case MACHTEN_SERVER: + case MSDOS_SERVER: + /* + ** Check for EPLF output (local times). + */ + if (*entry == '+') { + parse_eplf_line(entry, entry_info); + break; + } + + /* + ** Interpret and edit LIST output from Unix server. + */ + len = strlen(entry); + if (*first) { + *first = FALSE; + if (!strncmp(entry, "total ", 6) || + strstr(entry, "not available") != NULL) { + entry_info->display=FALSE; + return(entry_info); + } else if (unsure_type) { + /* this isn't really a unix server! */ + server_type = GENERIC_SERVER; + entry_info->display=FALSE; + return(entry_info); + } + } + + /* + ** Check first character of ls -l output. + */ + if (TOUPPER(entry[0]) == 'D') { + /* + ** It's a directory. + */ + StrAllocCopy(entry_info->type, "Directory"); + remove_size=TRUE; /* size is not useful */ + } else if (entry[0] == 'l') { + /* + ** It's a symbolic link, does the user care about + ** knowing if it is symbolic? I think so since + ** it might be a directory. + */ + StrAllocCopy(entry_info->type, "Symbolic Link"); + remove_size=TRUE; /* size is not useful */ + + /* + ** Strip off " -> pathname". + */ + for (i = len - 1; (i > 3) && + (!isspace(entry[i]) || + (entry[i-1] != '>') || + (entry[i-2] != '-') || + (entry[i-3] != ' ')); i--) + ; /* null body */ + if (i > 3) { + entry[i-3] = '\0'; + len = i - 3; + } + } /* link */ + + parse_ls_line(entry, entry_info); + + if (!strcmp(entry_info->filename,"..") || + !strcmp(entry_info->filename,".")) + entry_info->display=FALSE; + /* + ** Goto the bottom and get real type. + */ + break; + + case VMS_SERVER: + /* + ** Interpret and edit LIST output from VMS server + ** and convert information lines to zero length. + */ + parse_vms_dir_entry(entry, entry_info); + + /* + ** Get rid of any junk lines. + */ + if (!entry_info->display) + return(entry_info); + + /* + ** Trim off VMS directory extensions. + */ + len = strlen(entry_info->filename); + if ((len > 4) && !strcmp(&entry_info->filename[len-4], ".dir")) { + entry_info->filename[len-4] = '\0'; + StrAllocCopy(entry_info->type, "Directory"); + remove_size=TRUE; /* size is not useful */ + } + /* + ** Goto the bottom and get real type. + */ + break; + + case MS_WINDOWS_SERVER: + /* + ** Interpret and edit LIST output from MS_WINDOWS server + ** and convert information lines to zero length. + */ + parse_ms_windows_dir_entry(entry, entry_info); + + /* + ** Get rid of any junk lines. + */ + if (!entry_info->display) + return(entry_info); + if (entry_info->type && *entry_info->type == '\0') { + FREE(entry_info->type); + return(entry_info); + } + /* + ** Goto the bottom and get real type. + */ + break; + + case WINDOWS_NT_SERVER: + /* + ** Interpret and edit LIST output from MS_WINDOWS server + ** and convert information lines to zero length. + */ + parse_windows_nt_dir_entry(entry, entry_info); + + /* + ** Get rid of any junk lines. + */ + if (!entry_info->display) + return(entry_info); + if (entry_info->type && *entry_info->type == '\0') { + FREE(entry_info->type); + return(entry_info); + } + /* + ** Goto the bottom and get real type. + */ + break; + + case CMS_SERVER: + { + /* + ** Interpret and edit LIST output from VM/CMS server + ** and convert any information lines to zero length. + */ + parse_cms_dir_entry(entry, entry_info); + + /* + ** Get rid of any junk lines. + */ + if (!entry_info->display) + return(entry_info); + if (entry_info->type && *entry_info->type == '\0') { + FREE(entry_info->type); + return(entry_info); + } + /* + ** Goto the bottom and get real type. + */ + break; + } + + case NCSA_SERVER: + case TCPC_SERVER: + /* + ** Directories identified by trailing "/" characters. + */ + StrAllocCopy(entry_info->filename, entry); + len = strlen(entry); + if (entry[len-1] == '/') { + /* + ** It's a dir, remove / and mark it as such. + */ + entry[len-1] = '\0'; + StrAllocCopy(entry_info->type, "Directory"); + remove_size=TRUE; /* size is not useful */ + } + /* + ** Goto the bottom and get real type. + */ + break; + + default: + /* + ** We can't tell if it is a directory since we only + ** did an NLST :( List bad file types anyways? NOT! + */ + StrAllocCopy(entry_info->filename, entry); + return(entry_info); /* mostly empty info */ + break; /* not needed */ + + } /* switch (server_type) */ + + if (remove_size && entry_info->size) { + entry_info->size = 0; + } + + if (entry_info->filename && strlen(entry_info->filename) > 3) { + if (((cp=strrchr(entry_info->filename, '.')) != NULL && + 0 == strncasecomp(cp, ".me", 3)) && + (cp[3] == '\0' || cp[3] == ';')) { + /* + ** Don't treat this as application/x-Troff-me + ** if it's a Unix server but has the string + ** "read.me", or if it's not a Unix server. - FM + */ + if ((server_type != UNIX_SERVER) || + (cp > (entry_info->filename + 3) && + 0 == strncasecomp((cp - 4), "read.me", 7))) { + StrAllocCopy(entry_info->type, "text/plain"); + } + } + } + + /* + ** Get real types eventually. + */ + if (!entry_info->type) { + char *cp; + HTFormat format; + HTAtom * encoding; /* @@ not used at all */ + format = HTFileFormat(entry_info->filename, &encoding); + + if (!strncmp(HTAtom_name(format), "application",11)) { + cp = HTAtom_name(format) + 12; + if (!strncmp(cp,"x-",2)) + cp += 2; + } else { + cp = HTAtom_name(format); + } + + StrAllocCopy(entry_info->type, cp); + } + + return(entry_info); +} /* parse_dir_entry */ + +PUBLIC int compare_EntryInfo_structs ARGS2( + EntryInfo *, entry1, + EntryInfo *, entry2) +{ + int i, status; + char date1[16], date2[16], time1[8], time2[8], month[4]; + + switch(HTfileSortMethod) { + case FILE_BY_SIZE: + /* both equal or both 0 */ + if (entry1->size == entry2->size) + return(strcmp(entry1->filename, entry2->filename)); + else + if (entry1->size > entry2->size) + return(1); + else + return(-1); + break; + + case FILE_BY_TYPE: + if (entry1->type && entry2->type) { + status = strcasecomp(entry1->type, entry2->type); + if (status) + return(status); + /* else fall to filename comparison */ + } + return (strcmp(entry1->filename, entry2->filename)); + break; + + case FILE_BY_DATE: + if (entry1->date && entry2->date) { + /* + ** Make sure we have the correct length. - FM + */ + if (strlen(entry1->date) != 12 || strlen(entry2->date) != 12) { + return(strcmp(entry1->filename, entry2->filename)); + } + /* + ** Set the years and date, + ** if we don't have them yet. + */ + if (!HaveYears) { + set_years_and_date(); + } + /* + ** Set up for sorting in reverse + ** chronological order. - FM + */ + if (entry1->date[9] == ':') { + strcpy(date1, "9999"); + strcpy(time1, (char *)&entry1->date[7]); + if (time1[0] == ' ') { + time1[0] = '0'; + } + } else { + strcpy(date1, (char *)&entry1->date[8]); + strcpy(time1, "00:00"); + } + strncpy(month, entry1->date, 3); + month[3] = '\0'; + for (i = 0; i < 12; i++) { + if (!strcasecomp(month, months[i])) { + break; + } + } + i++; + sprintf(month, "%s%d", (i < 10 ? "0" : ""), i); + strcat(date1, month); + strncat(date1, (char *)&entry1->date[4], 2); + date1[8] = '\0'; + if (date1[6] == ' ' || date1[6] == HT_NON_BREAK_SPACE) { + date1[6] = '0'; + } + if (date1[0] == '9' && atoi(date1) > TheDate) { + for (i = 0; i < 4; i++) { + date1[i] = LastYear[i]; + } + } + strcat(date1, time1); + if (entry2->date[9] == ':') { + strcpy(date2, "9999"); + strcpy(time2, (char *)&entry2->date[7]); + if (time2[0] == ' ') { + time2[0] = '0'; + } + } else { + strcpy(date2, (char *)&entry2->date[8]); + strcpy(time2, "00:00"); + } + strncpy(month, entry2->date, 3); + month[3] = '\0'; + for (i = 0; i < 12; i++) { + if (!strcasecomp(month, months[i])) { + break; + } + } + i++; + sprintf(month, "%s%d", (i < 10 ? "0" : ""), i); + strcat(date2, month); + strncat(date2, (char *)&entry2->date[4], 2); + date2[8] = '\0'; + if (date2[6] == ' ' || date2[6] == HT_NON_BREAK_SPACE) { + date2[6] = '0'; + } + if (date2[0] == '9' && atoi(date2) > TheDate) { + for (i = 0; i < 4; i++) { + date2[i] = LastYear[i]; + } + } + strcat(date2, time2); + /* + ** Do the comparison. - FM + */ + status = strcasecomp(date2, date1); + if (status) + return(status); + /* else fall to filename comparison */ + } + return (strcmp(entry1->filename, entry2->filename)); + break; + + case FILE_BY_NAME: + default: + return (strcmp(entry1->filename, entry2->filename)); + } +} + + +/* Read a directory into an hypertext object from the data socket +** -------------------------------------------------------------- +** +** On entry, +** anchor Parent anchor to link the this node to +** address Address of the directory +** On exit, +** returns HT_LOADED if OK +** <0 if error. +*/ +PRIVATE int read_directory ARGS4( + HTParentAnchor *, parent, + CONST char *, address, + HTFormat, format_out, + HTStream *, sink) +{ + int status; + BOOLEAN WasInterrupted = FALSE; + HTStructured* target = HTML_new(parent, format_out, sink); + HTStructuredClass targetClass; + char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION); + EntryInfo *entry_info; + BOOLEAN first=TRUE; + char string_buffer[64]; + char *lastpath=NULL;/* prefix for link, either "" (for root) or xxx */ + + targetClass = *(target->isa); + + _HTProgress ("Receiving FTP directory."); + HTDirTitles(target, (HTAnchor*)parent); + + data_read_pointer = data_write_pointer = data_buffer; + + if (*filename == '\0') { /* Empty filename: use root. */ + StrAllocCopy (lastpath, "/"); + } else if (!strcmp(filename,"/")) { /* Root path. */ + StrAllocCopy (lastpath, "/foo/.."); + } else { + char * p = strrchr(filename, '/'); /* Find the lastslash. */ + char *cp; + + if (server_type == CMS_SERVER) { + StrAllocCopy(lastpath, filename); /* Use absolute path for CMS. */ + } else { + StrAllocCopy(lastpath, p+1); /* Take slash off the beginning. */ + } + if ((cp = strrchr(lastpath, ';')) != NULL) { /* Trim type= param. */ + if (!strncasecomp((cp+1), "type=", 5)) { + if (TOUPPER(*(cp+6)) == 'D' || + TOUPPER(*(cp+6)) == 'A' || + TOUPPER(*(cp+6)) == 'I') + *cp = '\0'; + } + } + } + FREE (filename); + + + { + HTBTree * bt = HTBTree_new((HTComparer)compare_EntryInfo_structs); + char c; + HTChunk * chunk = HTChunkCreate(128); + int BytesReceived = 0; + int BytesReported = 0; + char NumBytes[64]; + PUTS("\n"); /* prettier LJM */ + for (c = 0; c != (char)EOF;) { /* For each entry in the directory */ + HTChunkClear(chunk); + + if (HTCheckForInterrupt()) { + WasInterrupted = TRUE; + if (BytesReceived) { + goto unload_btree; /* unload btree */ + } else { + ABORT_TARGET; + HTBTreeAndObject_free(bt); + return HT_INTERRUPTED; + } + } + + /* read directory entry + */ + for (;;) { /* Read in one line as filename */ + c = NEXT_DATA_CHAR; +AgainForMultiNet: + if (interrupted_in_next_data_char) { + WasInterrupted = TRUE; + if (BytesReceived) { + goto unload_btree; /* unload btree */ + } else { + ABORT_TARGET; + HTBTreeAndObject_free(bt); + return HT_INTERRUPTED; + } + } else if (c == CR || c == LF) { /* Terminator? */ + if (chunk->size != 0) { /* got some text */ + /* Deal with MultiNet's wrapping of long lines */ + if (server_type == VMS_SERVER) { + /* Deal with MultiNet's wrapping of long lines - F.M. */ + if (data_read_pointer < data_write_pointer && + *(data_read_pointer+1) == ' ') + data_read_pointer++; + else if (data_read_pointer >= data_write_pointer) { + status = NETREAD(data_soc, data_buffer, + DATA_BUFFER_SIZE); + if (status == HT_INTERRUPTED) { + interrupted_in_next_data_char = 1; + goto AgainForMultiNet; + } + if (status <= 0) { + c = (char)EOF; + break; + } + data_write_pointer = data_buffer + status; + data_read_pointer = data_buffer; + if (*data_read_pointer == ' ') + data_read_pointer++; + else + break; + } + else + break; + } + else + break; /* finish getting one entry */ + } + } else if (c == (char)EOF) { + break; /* End of file */ + } else { + HTChunkPutc(chunk, c); + } + } + HTChunkTerminate(chunk); + + BytesReceived += chunk->size; + if (BytesReceived > BytesReported + 1024) { + sprintf(NumBytes,"Transferred %d bytes",BytesReceived); + HTProgress(NumBytes); + BytesReported = BytesReceived; + } + + if (c == (char) EOF && chunk->size == 1) + /* 1 means empty: includes terminating 0 */ + break; + if (TRACE) + fprintf(stderr, "HTFTP: Line in %s is %s\n", + lastpath, chunk->data); + + entry_info = parse_dir_entry(chunk->data, &first); + if (entry_info->display) { + if (TRACE) + fprintf(stderr, "Adding file to BTree: %s\n", + entry_info->filename); + HTBTree_add(bt, (EntryInfo *)entry_info); + } + + } /* next entry */ + +unload_btree: + + HTChunkFree(chunk); + + /* print out the handy help message if it exits :) */ + if (help_message_cache_non_empty()) { + START(HTML_PRE); + START(HTML_HR); + PUTS(help_message_cache_contents()); + init_help_message_cache(); /* to free memory */ + START(HTML_HR); + } else { + START(HTML_PRE); + PUTS("\n"); + } + + /* Put up header + */ + /* PUTS(" Date Type Size Filename\n"); + */ + + /* Run through tree printing out in order + */ + { + HTBTElement * ele; + int i; + for (ele = HTBTree_next(bt, NULL); + ele != NULL; + ele = HTBTree_next(bt, ele)) { + entry_info = (EntryInfo *)HTBTree_object(ele); + + if (entry_info->date) { + PUTS(entry_info->date); + PUTS(" "); + } else { + PUTS(" * "); + } + + if (entry_info->type) { + for (i = 0; entry_info->type[i] != '\0' && i < 15; i++) + PUTC(entry_info->type[i]); + for (; i < 17; i++) + PUTC(' '); + } + + /* start the anchor */ + HTDirEntry(target, lastpath, entry_info->filename); + PUTS(entry_info->filename); + END(HTML_A); + + if (entry_info->size) { + if (entry_info->size < 1024) + sprintf(string_buffer, " %d bytes", + entry_info->size); + else + sprintf(string_buffer, " %dKb", + entry_info->size/1024); + PUTS(string_buffer); + } + + PUTC('\n'); /* end of this entry */ + + free_entryinfo_struct_contents(entry_info); + } + } + FREE_TARGET; + HTBTreeAndObject_free(bt); + } + + FREE(lastpath); + if (WasInterrupted || HTCheckForInterrupt()) { + if (server_type != CMS_SERVER) + response(NIL); + _HTProgress("Data transfer interrupted."); + return HT_LOADED; + } + if (server_type != CMS_SERVER) + response(NIL); + return HT_LOADED; +#ifdef NOTDEFINED + return response(NIL) == 2 ? HT_LOADED : -1; +#endif /* NOTDEFINED */ +} + +/* Retrieve File from Server +** ------------------------- +** +** On entry, +** name WWW address of a file: document, including hostname +** On exit, +** returns Socket number for file if good. +** <0 if bad. +*/ +PUBLIC int HTFTPLoad ARGS4( + CONST char *, name, + HTParentAnchor *, anchor, + HTFormat, format_out, + HTStream *, sink) +{ + BOOL isDirectory = NO; + int status; + int retry; /* How many times tried? */ + HTFormat format; + char command[LINE_LENGTH+1]; + + + /* set use_list to NOT since we don't know what kind of server + * this is yet. And set the type to GENERIC + */ + use_list = FALSE; + server_type = GENERIC_SERVER; + + for (retry = 0; retry < 2; retry++) { /* For timed out/broken connections */ + status = get_connection(name); + if (status < 0) + return status; + +#ifdef LISTEN + status = get_listen_socket(); + if (status < 0) { + NETCLOSE (control->socket); + control->socket = -1; + close_master_socket (); + /* HT_INTERRUPTED would fall through, if we could interrupt + somehow in the middle of it, which we currently can't. */ + return status; + } + +#ifdef REPEAT_PORT +/* Inform the server of the port number we will listen on +*/ + { + status = response(port_command); + if (status == HT_INTERRUPTED) { + if (TRACE) + fprintf (stderr, + "HTFTP: Interrupted in response (port_command)\n"); + _HTProgress ("Connection interrupted."); + NETCLOSE (control->socket); + control->socket = -1; + close_master_socket (); + return HT_INTERRUPTED; + } + if (status != 2) { /* Could have timed out */ + if (status < 0) + continue; /* try again - net error*/ + return -status; /* bad reply */ + } + if (TRACE) + fprintf(stderr, "HTFTP: Port defined.\n"); + } +#endif /* REPEAT_PORT */ +#else /* Use PASV */ +/* Tell the server to be passive +*/ + { + char *p; + int reply, h0, h1, h2, h3, p0, p1; /* Parts of reply */ + int status; + data_soc = status; + + sprintf(command, "PASV%c%c", CR, LF); + status = response(command); + if (status != 2) { + if (status < 0) + continue; /* retry or Bad return */ + return -status; /* bad reply */ + } + for (p = response_text; *p && *p != ','; p++) + ; /* null body */ + + while (--p > response_text && '0' <= *p && *p <= '9') + ; /* null body */ + + status = sscanf(p+1, "%d,%d,%d,%d,%d,%d", + &h0, &h1, &h2, &h3, &p0, &p1); + if (status < 4) { + fprintf(stderr, "HTFTP: PASV reply has no inet address!\n"); + return -99; + } + passive_port = (p0<<8) + p1; + if (TRACE) + fprintf(stderr, "HTFTP: Server is listening on port %d\n", + passive_port); + + +/* Open connection for data: +*/ + sprintf(command, + "ftp://%d.%d.%d.%d:%d/",h0,h1,h2,h3,passive_port); + status = HTDoConnect(name, "FTP", passive_port, &data_soc); + + if (status < 0) { + (void) HTInetStatus("connect for data"); + NETCLOSE(data_soc); + return status; /* Bad return */ + } + + if (TRACE) + fprintf(stderr, "FTP data connected, socket %d\n", data_soc); + } +#endif /* use PASV */ + status = 0; + break; /* No more retries */ + + } /* for retries */ + if (status < 0) + return status; /* Failed with this code */ + +/* Ask for the file: +*/ + { + char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION); + char *fname = filename; /** Save for subsequent free() **/ + BOOL binary; + HTAtom * encoding; + char *type = NULL; + char *cp; + + if (server_type == CMS_SERVER) { + /** If the unescaped path has a %2f, reject it as illegal. - FM **/ + if (((cp = strstr(filename, "%2")) != NULL) && + TOUPPER(cp[2]) == 'F') { + FREE(fname); + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + if (TRACE) { + fprintf(stderr, + "HTFTP: Rejecting path due to illegal escaped slash.\n"); + } + return -1; + } + } + + if (!*filename) { + StrAllocCopy(filename, "/"); + type = "D"; + } else if ((type = strrchr(filename, ';')) != NULL) { + /* + ** Check and trim the type= parameter. - FM + */ + if (!strncasecomp((type+1), "type=", 5)) { + switch(TOUPPER(*(type+6))) { + case 'D': + *type = '\0'; + type = "D"; + break; + case 'A': + *type = '\0'; + type = "A"; + break; + case 'I': + *type = '\0'; + type = "I"; + break; + default: + type = ""; + break; + } + if (!*filename) { + *filename = '/'; + *(filename+1) = '\0'; + } + } + if (TRACE && *type != '\0') { + fprintf(stderr, "HTFTP: type=%s\n", type); + } + } + HTUnEscape(filename); + if (TRACE) + fprintf(stderr, "HTFTP: UnEscaped %s\n", filename); + if (filename[1] == '~') { + /* + ** Check if translation of HOME as tilde is supported, + ** and adjust filename if so. - FM + */ + char *cp = NULL; + char *fn = NULL; + + if ((cp = strchr((filename+1), '/')) != NULL) { + *cp = '\0'; + } + sprintf(command, "PWD%c%c", CR, LF); + status = response(command); + if (status == 2 && response_text[5] == '/') { + sprintf(command, "CWD %s%c%c", (filename+1), CR, LF); + status = response(command); + if (status == 2) { + StrAllocCopy(fn, (filename+1)); + if (cp) { + *cp = '/'; + if (fn[strlen(fn)-1] != '/') { + StrAllocCat(fn, cp); + } else { + StrAllocCat(fn, (cp+1)); + } + } + FREE(fname); + fname = filename = fn; + } + } + } + if (strlen(filename) > 3) { + char *cp; + if (((cp=strrchr(filename, '.')) != NULL && + 0 == strncasecomp(cp, ".me", 3)) && + (cp[3] == '\0' || cp[3] == ';')) { + /* + ** Don't treat this as application/x-Troff-me + ** if it's a Unix server but has the string + ** "read.me", or if it's not a Unix server. - FM + */ + if ((server_type != UNIX_SERVER) || + (cp > (filename + 3) && + 0 == strncasecomp((cp - 4), "read.me", 7))) { + *cp = '\0'; + format = HTFileFormat(filename, &encoding); + *cp = '.'; + } else { + format = HTFileFormat(filename, &encoding); + } + } else { + format = HTFileFormat(filename, &encoding); + } + } else { + format = HTFileFormat(filename, &encoding); + } + format = HTCharsetFormat(format, anchor); + binary = (encoding != HTAtom_for("8bit") && + encoding != HTAtom_for("7bit")); + if (!binary && + /* + ** Force binary if we're in source, download or dump + ** mode and this is not a VM/CMS server, so we don't + ** get CRLF instead of LF (or CR) for newlines in text + ** files. Can't do this for VM/CMS or we'll get + ** raw EBCDIC. - FM + */ + (format_out == WWW_SOURCE || + format_out == HTAtom_for("www/download") || + format_out == HTAtom_for("www/dump")) && + (server_type != CMS_SERVER)) + binary = TRUE; + if (!binary && type && *type == 'I') { + /* + ** Force binary if we had ;type=I - FM + */ + binary = TRUE; + } else if (binary && type && *type == 'A') { + /* + ** Force ASCII if we had ;type=A - FM + */ + binary = FALSE; + } + if (binary != control->binary) { + /* + ** Act on our setting if not alread set. - FM + */ + char * mode = binary ? "I" : "A"; + sprintf(command, "TYPE %s%c%c", mode, CR, LF); + status = response(command); + if (status != 2) { + init_help_message_cache(); /* to free memory */ + return ((status < 0) ? status : -status); + } + control->binary = binary; + } + switch (server_type) { + /* + ** Handle what for Lynx are special case servers, e.g., + ** for which we respect RFC 1738, or which have known + ** conflicts in suffix mappings. - FM + */ + case VMS_SERVER: + { + char *cp1, *cp2; + BOOL included_device = FALSE; + /** Accept only Unix-style filename **/ + if (strchr(filename, ':') != NULL || + strchr(filename, '[') != NULL) { + FREE(fname); + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + if (TRACE) { + fprintf(stderr, + "HTFTP: Rejecting path due to non-Unix-style syntax.\n"); + } + return -1; + } + /** Handle any unescaped "/%2F" path **/ + if (!strncmp(filename, "//", 2)) { + int i; + included_device = TRUE; + for (i = 0; filename[(i+1)]; i++) + filename[i] = filename[(i+1)]; + filename[i] = '\0'; + if (TRACE) { + fprintf(stderr, "HTFTP: Trimmed '%s'\n", filename); + } + cp = HTMake_VMS_name("", filename); + if (TRACE) { + fprintf(stderr, "HTFTP: VMSized '%s'\n", cp); + } + if ((cp1=strrchr(cp, ']')) != NULL) { + cp1++; + for (i = 0; cp1[i]; i++) + filename[i] = cp1[i]; + filename[i] = '\0'; + if (TRACE) { + fprintf(stderr, "HTFTP: Filename '%s'\n", filename); + } + *cp1 = '\0'; + sprintf(command, "CWD %s%c%c", cp, CR, LF); + status = response (command); + if (status != 2) { + if ((cp1=strchr(cp, '[')) != NULL) { + *cp1++ = '\0'; + sprintf(command, "CWD %s%c%c", cp, CR, LF); + status = response (command); + if (status != 2) { + FREE(fname); + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + sprintf(command, "CWD [.%s%c%c", cp1, CR, LF); + status = response (command); + if (status != 2) { + FREE(fname); + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + } else { + FREE(fname); + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + } + } else if ((cp1=strchr(cp, ':')) != NULL && + strchr(cp, '[') == NULL && + strchr(cp, ']') == NULL) { + cp1++; + if (*cp1 != '\0') { + for (i = 0; cp1[i]; i++) + filename[i] = cp1[i]; + filename[i] = '\0'; + if (TRACE) { + fprintf(stderr, "HTFTP: Filename '%s'\n", filename); + } + *cp1 = '\0'; + strcat(cp, "["); + strcat(cp, filename); + strcat(cp, "]"); + sprintf(command, "CWD %s%c%c", cp, CR, LF); + status = response (command); + if (status != 2) { + *cp1 = '\0'; + strcat(cp, "[000000]"); + sprintf(command, "CWD %s%c%c", cp, CR, LF); + status = response (command); + if (status != 2) { + *cp1 = '\0'; + sprintf(command, "CWD %s%c%c", cp, CR, LF); + status = response (command); + if (status != 2) { + FREE(fname); + init_help_message_cache(); + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + } + } else { + strcpy(cp, "000000"); + filename = cp; + } + } + } else if (0==strcmp(cp, (filename+1))) { + sprintf(command, "CWD %s%c%c", cp, CR, LF); + status = response (command); + if (status != 2) { + strcat(cp, ":"); + sprintf(command, "CWD %s%c%c", cp, CR, LF); + status = response (command); + if (status != 2) { + FREE(fname); + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + } + strcpy(cp, "000000"); + filename = cp; + } + } + /** Trim trailing slash if filename is not the top directory **/ + if (strlen(filename) > 1 && filename[strlen(filename)-1] == '/') + filename[strlen(filename)-1] = '\0'; + +#ifdef MAINTAIN_CONNECTION /* Don't need this if always new connection - F.M. */ + if (!included_device) { + /** Get the current default VMS device:[directory] **/ + sprintf(command, "PWD%c%c", CR, LF); + status = response (command); + if (status != 2) { + FREE(fname); + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + /** Go to the VMS account's top directory **/ + if ((cp=strchr(response_text, '[')) != NULL && + (cp1=strrchr(response_text, ']')) != NULL) { + sprintf(command, "CWD %s", cp); + if ((cp2=strchr(cp, '.')) != NULL && cp2 < cp1) + sprintf(command+(cp2-cp)+4, "]%c%c", CR, LF); + else + sprintf(command+(cp1-cp)+4, "]%c%c", CR, LF); + status = response (command); + if (status != 2) { + FREE(fname); + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + } + } +#endif /* MAINTAIN_CONNECTION */ + + /** If we want the VMS account's top directory, list it now **/ + if ((included_device && 0==strcmp(filename, "000000")) || + (strlen(filename) == 1 && *filename == '/')) { + isDirectory = YES; + sprintf(command, "LIST%c%c", CR, LF); + status = response (command); + FREE(fname); + if (status != 1) { + /* Action not started */ + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + /** Big goto! **/ + goto listen; + } + /** Otherwise, go to appropriate directory and doctor filename **/ + if (!included_device && + (cp=strchr(filename, '/')) != NULL && + (cp1=strrchr(cp, '/')) != NULL && cp != cp1) { + sprintf(command, "CWD [.%s", cp+1); + sprintf(command+(cp1-cp)+5, "]%c%c", CR, LF); + while ((cp2=strrchr(command, '/')) != NULL) + *cp2 = '.'; + status = response(command); + if (status != 2) { + FREE(fname); + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + filename = cp1+1; + } else { + if (!included_device) { + filename += 1; + } + } + break; + } + case CMS_SERVER: + { + /* + ** If we want the CMS account's top directory, or a base + ** SFS or anonymous directory path (i.e., without a slash), + ** list it now. FM + */ + if ((strlen(filename) == 1 && *filename == '/') || + ((0 == strncasecomp((filename+1), "vmsysu:", 7)) && + (cp = strchr((filename+1), '.')) != NULL && + strchr(cp, '/') == NULL) || + (0 == strncasecomp(filename+1, "anonymou.", 9) && + strchr(filename+1, '/') == NULL)) { + if (filename[1] != '\0') { + sprintf(command, "CWD %s%c%c", (filename+1), CR, LF); + status = response(command); + if (status != 2) { + /* Action not started */ + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + } + isDirectory = YES; + if (use_list) + sprintf(command, "LIST%c%c", CR, LF); + else + sprintf(command, "NLST%c%c", CR, LF); + status = response (command); + FREE(fname); + if (status != 1) { + /* Action not started */ + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + /** Big goto! **/ + goto listen; + } + filename++; + + /** Otherwise, go to appropriate directory and adjust filename **/ + while ((cp = strchr(filename, '/')) != NULL) { + *cp++ = '\0'; + sprintf(command, "CWD %s%c%c", filename, CR, LF); + status = response(command); + if (status == 2) { + if (*cp == '\0') { + isDirectory = YES; + if (use_list) + sprintf(command, "LIST%c%c", CR, LF); + else + sprintf(command, "NLST%c%c", CR, LF); + status = response (command); + FREE(fname); + if (status != 1) { + /** Action not started **/ + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + return ((status < 0) ? status : -status); + } + /** Clear any messages from the login directory **/ + init_help_message_cache(); + /** Big goto! **/ + goto listen; + } + filename = cp; + } + } + break; + } + default: + /** Shift for any unescaped "/%2F" path **/ + if (!strncmp(filename, "//", 2)) + filename++; + break; + } + /* + ** Act on a file or listing request, or try to figure out + ** which we're dealing with if we don't know yet. - FM + */ + if (!(type) || (type && *type != 'D')) { + sprintf(command, "RETR %s%c%c", filename, CR, LF); + status = response(command); + } else { + status = 5; /* Failed status set as flag. - FM */ + } + if (status != 1) { /* Failed : try to CWD to it */ + /** Clear any login messages if this isn't the login directory **/ + if (strcmp(filename, "/")) + init_help_message_cache(); + + sprintf(command, "CWD %s%c%c", filename, CR, LF); + status = response(command); + + if (status == 2) { /* Successed : let's NAME LIST it */ + isDirectory = YES; + if (use_list) + sprintf(command, "LIST%c%c", CR, LF); + else + sprintf(command, "NLST%c%c", CR, LF); + status = response (command); + } + } + FREE(fname); + if (status != 1) { + init_help_message_cache(); /* to free memory */ + NETCLOSE(control->socket); + control->socket = -1; + if (status < 0) + return status; + else + return -status; + } + } + +listen: +#ifdef LISTEN +/* Wait for the connection +*/ + { + struct sockaddr_in soc_address; + int soc_addrlen=sizeof(soc_address); +#ifdef SOCKS + if (socks_flag) + status = Raccept(master_socket, + (struct sockaddr *)&soc_address, + (void *)&soc_addrlen); + else +#endif /* SOCKS */ + status = accept(master_socket, + (struct sockaddr *)&soc_address, + (void *)&soc_addrlen); + if (status < 0) { + init_help_message_cache(); /* to free memory */ + return HTInetStatus("accept"); + } + CTRACE(tfp, "TCP: Accepted new socket %d\n", status); + data_soc = status; + } +#else +/* @@ */ +#endif /* LISTEN */ + if (isDirectory) { + status = read_directory (anchor, name, format_out, sink); + NETCLOSE(data_soc); + NETCLOSE(control->socket); + control->socket = -1; + init_help_message_cache(); /* to free memory */ + return status; + /* returns HT_LOADED or error */ + } else { + int rv; + int len; + HTAtom * encoding; + char *FileName = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION); + + /** Clear any login messages **/ + init_help_message_cache(); + + /** Fake a Content-Encoding for compressed files. - FM **/ + HTUnEscape(FileName); + if ((len = strlen(FileName)) > 2) { + if ((FileName[len - 1] == 'Z') && + (FileName[len - 2] == '.' || + FileName[len - 2] == '-' || + FileName[len - 2] == '_')) { + + FileName[len - 2] = '\0'; + format = HTFileFormat(FileName, &encoding); + format = HTCharsetFormat(format, anchor); + StrAllocCopy(anchor->content_type, format->name); + StrAllocCopy(anchor->content_encoding, "x-compress"); + format = HTAtom_for("www/compressed"); + } else if ((len > 3) && + !strcasecomp((char *)&FileName[len - 2], "gz")) { + if (FileName[len - 3] == '.' || + FileName[len - 3] == '-' || + FileName[len - 3] == '_') { + FileName[len - 3] = '\0'; + format = HTFileFormat(FileName, &encoding); + format = HTCharsetFormat(format, anchor); + StrAllocCopy(anchor->content_type, format->name); + StrAllocCopy(anchor->content_encoding, "x-gzip"); + format = HTAtom_for("www/compressed"); + } + } + } + FREE(FileName); + + _HTProgress ("Receiving FTP file."); + rv = HTParseSocket(format, format_out, anchor, data_soc, sink); + + if (rv == HT_INTERRUPTED) + _HTProgress("Data transfer interrupted."); + + HTInitInput(control->socket); + /* Reset buffering to control connection DD 921208 */ + + status = NETCLOSE(data_soc); + if (TRACE) + fprintf(stderr, "HTFTP: Closing data socket %d\n", data_soc); + if (status < 0 && rv != HT_INTERRUPTED && rv != -1) { + (void) HTInetStatus("close"); /* Comment only */ + data_soc = -1; /* invalidate it */ + } else { + data_soc = -1; /* invalidate it */ + status = response(NIL); /* Pick up final reply */ + if (status != 2 && rv != HT_INTERRUPTED && rv != -1) { + init_help_message_cache(); /* to free memory */ + return HTLoadError(sink, 500, response_text); + } + } + + NETCLOSE(control->socket); + control->socket = -1; + init_help_message_cache(); /* to free memory */ + return HT_LOADED; + } +} /* open_file_read */ diff --git a/WWW/Library/Implementation/HTFTP.h b/WWW/Library/Implementation/HTFTP.h new file mode 100644 index 00000000..9fc4bf20 --- /dev/null +++ b/WWW/Library/Implementation/HTFTP.h @@ -0,0 +1,72 @@ +/* FTP access module for libwww + FTP ACCESS FUNCTIONS + + This isn't really a valid protocol module -- it is lumped together with HTFile . That + could be changed easily. + + Author: Tim Berners-Lee. Public Domain. Please mail changes to timbl@info.cern.ch + + */ +#ifndef HTFTP_H +#define HTFTP_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTAnchor.h" +#include "HTStream.h" +#include "HTParse.h" + +#define FILE_BY_NAME 0 +#define FILE_BY_TYPE 1 +#define FILE_BY_SIZE 2 +#define FILE_BY_DATE 3 +extern BOOLEAN HTfileSortMethod; /* specifies the method of sorting */ + + +/* PUBLIC HTMake_VMS_name() +** CONVERTS WWW name into a VMS name +** ON ENTRY: +** nn Node Name (optional) +** fn WWW file name +** +** ON EXIT: +** returns vms file specification +** +** Bug: Returns pointer to static -- non-reentrant +*/ +PUBLIC char * HTMake_VMS_name PARAMS(( + CONST char * nn, + CONST char * fn)); + + +/* + +Retrieve File from Server + + ON EXIT, + + returns Socket number for file if good.<0 if bad. + + */ +extern int HTFTPLoad PARAMS +(( + CONST char * name, + HTParentAnchor * anchor, + HTFormat format_out, + HTStream* sink +)); + + +/* + +Return Host Name + + */ +extern CONST char * HTHostName NOPARAMS; + +#endif + +/* + + end */ diff --git a/WWW/Library/Implementation/HTFWriter.c b/WWW/Library/Implementation/HTFWriter.c new file mode 100644 index 00000000..8ef9ba1b --- /dev/null +++ b/WWW/Library/Implementation/HTFWriter.c @@ -0,0 +1,358 @@ +/* FILE WRITER HTFWrite.h +** =========== +** +** This version of the stream object just writes to a C file. +** The file is assumed open and left open. +** +** Bugs: +** strings written must be less than buffer size. +*/ + +#include "HTUtils.h" + +#include "HTFWriter.h" + +#include "HTFormat.h" +#include "HTAlert.h" +#include "HTFile.h" + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +/* Stream Object +** ------------ +*/ + +struct _HTStream { + CONST HTStreamClass * isa; + + FILE * fp; + char * end_command; + char * remove_command; + BOOL announce; +}; + + +/*_________________________________________________________________________ +** +** B L A C K H O L E C L A S S +** +** There is only one black hole instance shared by anyone +** who wanst a black hole. These black holes don't radiate, +** they just absorb data. +*/ +PRIVATE void HTBlackHole_put_character ARGS2(HTStream *, me, char, c) +{} +PRIVATE void HTBlackHole_put_string ARGS2(HTStream *, me, CONST char*, s) +{} +PRIVATE void HTBlackHole_write ARGS3(HTStream *, me, CONST char*, s, int, l) +{} +PRIVATE void HTBlackHole_free ARGS1(HTStream *, me) +{} +PRIVATE void HTBlackHole_abort ARGS2(HTStream *, me, HTError, e) +{} + + +/* Black Hole stream +** ----------------- +*/ +PRIVATE CONST HTStreamClass HTBlackHoleClass = +{ + "BlackHole", + HTBlackHole_free, + HTBlackHole_abort, + HTBlackHole_put_character, HTBlackHole_put_string, + HTBlackHole_write +}; + +PRIVATE HTStream HTBlackHoleInstance = +{ + &HTBlackHoleClass, + NULL, + NULL, + NULL, + NO +}; + +/* Black hole craetion +*/ +PUBLIC HTStream * HTBlackHole NOARGS +{ + return &HTBlackHoleInstance; +} + + +/*_________________________________________________________________________ +** +** F I L E A C T I O N R O U T I N E S +** Bug: +** All errors are ignored. +*/ + +/* Character handling +** ------------------ +*/ + +PRIVATE void HTFWriter_put_character ARGS2(HTStream *, me, char, c) +{ + putc(c, me->fp); +} + + + +/* String handling +** --------------- +** +** Strings must be smaller than this buffer size. +*/ +PRIVATE void HTFWriter_put_string ARGS2(HTStream *, me, CONST char*, s) +{ + fputs(s, me->fp); +} + + +/* Buffer write. Buffers can (and should!) be big. +** ------------ +*/ +PRIVATE void HTFWriter_write ARGS3(HTStream *, me, CONST char*, s, int, l) +{ + fwrite(s, 1, l, me->fp); +} + + + + +/* Free an HTML object +** ------------------- +** +** Note that the SGML parsing context is freed, but the created +** object is not, +** as it takes on an existence of its own unless explicitly freed. +*/ +PRIVATE void HTFWriter_free ARGS1(HTStream *, me) +{ + fclose(me->fp); + if (me->end_command) { /* Temp file */ + _HTProgress(me->end_command); /* Tell user what's happening */ + system(me->end_command); + FREE(me->end_command); + if (me->remove_command) { + system(me->remove_command); + FREE(me->remove_command); + } + } + + FREE(me); +} + +/* End writing +*/ + +PRIVATE void HTFWriter_abort ARGS2(HTStream *, me, HTError, e) +{ + fclose(me->fp); + if (me->end_command) { /* Temp file */ + if (TRACE) fprintf(stderr, + "HTFWriter: Aborting: file not executed.\n"); + FREE(me->end_command); + if (me->remove_command) { + system(me->remove_command); + FREE(me->remove_command); + } + } + + FREE(me); +} + + + +/* Structured Object Class +** ----------------------- +*/ +PRIVATE CONST HTStreamClass HTFWriter = /* As opposed to print etc */ +{ + "FileWriter", + HTFWriter_free, + HTFWriter_abort, + HTFWriter_put_character, HTFWriter_put_string, + HTFWriter_write +}; + + +/* Subclass-specific Methods +** ------------------------- +*/ + +PUBLIC HTStream* HTFWriter_new ARGS1(FILE *, fp) +{ + HTStream* me; + + if (!fp) return NULL; + + me = (HTStream*)malloc(sizeof(*me)); + if (me == NULL) outofmem(__FILE__, "HTML_new"); + me->isa = &HTFWriter; + + me->fp = fp; + me->end_command = NULL; + me->remove_command = NULL; + me->announce = NO; + + return me; +} + +/* Make system command from template +** --------------------------------- +** +** See mailcap spec for description of template. +*/ +/* @@ to be written. sprintfs will do for now. */ + + + +/* Take action using a system command +** ---------------------------------- +** +** originally from Ghostview handling by Marc Andreseen. +** Creates temporary file, writes to it, executes system command +** on end-document. The suffix of the temp file can be given +** in case the application is fussy, or so that a generic opener can +** be used. +*/ +PUBLIC HTStream* HTSaveAndExecute ARGS3( + HTPresentation *, pres, + HTParentAnchor *, anchor, /* Not used */ + HTStream *, sink) /* Not used */ + +#ifdef unix +#define REMOVE_COMMAND "/bin/rm -f %s\n" +#endif +#ifdef VMS +#define REMOVE_COMMAND "delete/noconfirm/nolog %s.." +#endif + +#ifdef REMOVE_COMMAND +{ + char *fnam; + CONST char * suffix; + + HTStream* me; + + if (HTClientHost) { + HTAlert("Can't save data to file -- please run WWW locally"); + return HTBlackHole(); + } + + me = (HTStream*)malloc(sizeof(*me)); + if (me == NULL) outofmem(__FILE__, "Save and execute"); + me->isa = &HTFWriter; + + /* Save the file under a suitably suffixed name */ + + suffix = HTFileSuffix(pres->rep); + + fnam = (char *)malloc (L_tmpnam + 16 + strlen(suffix)); + tmpnam (fnam); + if (suffix) strcat(fnam, suffix); + + me->fp = fopen (fnam, "w"); + if (!me->fp) { + HTAlert("Can't open temporary file!"); + FREE(fnam); + FREE(me); + return NULL; + } + +/* Make command to process file +*/ + me->end_command = (char *)malloc ( + (strlen (pres->command) + 10+ 3*strlen(fnam)) + * sizeof (char)); + if (me == NULL) outofmem(__FILE__, "SaveAndExecute"); + + sprintf (me->end_command, pres->command, fnam, fnam, fnam); + + me->remove_command = NULL; /* If needed, put into end_command */ +#ifdef NOPE +/* Make command to delete file +*/ + me->remove_command = (char *)malloc ( + (strlen (REMOVE_COMMAND) + 10+ strlen(fnam)) + * sizeof (char)); + if (me == NULL) outofmem(__FILE__, "SaveAndExecute"); + + sprintf (me->remove_command, REMOVE_COMMAND, fnam); +#endif + + me->announce = NO; + FREE(fnam); + return me; +} + +#else /* can do remove */ +{ return NULL; } +#endif + + +/* Save Locally +** ------------ +** +** Bugs: +** GUI Apps should open local Save panel here really. +** +*/ +PUBLIC HTStream* HTSaveLocally ARGS3( + HTPresentation *, pres, + HTParentAnchor *, anchor, /* Not used */ + HTStream *, sink) /* Not used */ + +{ + char *fnam; + char *answer; + CONST char * suffix; + + HTStream* me; + + if (HTClientHost) { + HTAlert("Can't save data to file -- please run WWW locally"); + return HTBlackHole(); + } + + me = (HTStream*)malloc(sizeof(*me)); + if (me == NULL) outofmem(__FILE__, "SaveLocally"); + me->isa = &HTFWriter; + me->end_command = NULL; + me->remove_command = NULL; /* If needed, put into end_command */ + me->announce = YES; + + /* Save the file under a suitably suffixed name */ + + suffix = HTFileSuffix(pres->rep); + + fnam = (char *)malloc (L_tmpnam + 16 + strlen(suffix)); + tmpnam (fnam); + if (suffix) strcat(fnam, suffix); + + /* Save Panel */ + answer = HTPrompt("Give name of file to save in", fnam); + + FREE(fnam); + + me->fp = fopen (answer, "w"); + if (!me->fp) { + HTAlert("Can't open local file to write into."); + FREE(answer); + FREE(me); + return NULL; + } + + FREE(answer); + return me; +} + + + +/* Format Converter using system command +** ------------------------------------- +*/ diff --git a/WWW/Library/Implementation/HTFWriter.h b/WWW/Library/Implementation/HTFWriter.h new file mode 100644 index 00000000..052bdd7e --- /dev/null +++ b/WWW/Library/Implementation/HTFWriter.h @@ -0,0 +1,37 @@ +/* File Writer for libwww + C FILE WRITER + + It is useful to have both FWriter and Writer for environments in which fdopen() doesn't + exist for example. + + */ +#ifndef HTFWRITE_H +#define HTFWRITE_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTStream.h" +/*#include <stdio.h> included by HTUtils.h -- FM */ +#include "HTFormat.h" + +#ifdef SHORT_NAMES +#define HTFWriter_new HTFWnew +#endif + +extern HTStream * HTFWriter_new PARAMS((FILE * fp)); + +extern HTStream * HTSaveAndExecute PARAMS(( + HTPresentation * pres, + HTParentAnchor * anchor, /* Not used */ + HTStream * sink)); + +extern HTStream * HTSaveLocally PARAMS(( + HTPresentation * pres, + HTParentAnchor * anchor, /* Not used */ + HTStream * sink)); + +#endif +/* + + end */ diff --git a/WWW/Library/Implementation/HTFile.c b/WWW/Library/Implementation/HTFile.c new file mode 100644 index 00000000..9148f5d1 --- /dev/null +++ b/WWW/Library/Implementation/HTFile.c @@ -0,0 +1,1871 @@ +/* File Access HTFile.c +** =========== +** +** This is unix-specific code in general, with some VMS bits. +** These are routines for file access used by browsers. +** Development of this module for Unix DIRED_SUPPORT in Lynx +** regrettably has has been conducted in a manner with now +** creates a major impediment for hopes of adapting Lynx to +** a newer version of the library. +** +** History: +** Feb 91 Written Tim Berners-Lee CERN/CN +** Apr 91 vms-vms access included using DECnet syntax +** 26 Jun 92 (JFG) When running over DECnet, suppressed FTP. +** Fixed access bug for relative names on VMS. +** Sep 93 (MD) Access to VMS files allows sharing. +** 15 Nov 93 (MD) Moved HTVMSname to HTVMSUTILS.C +** 27 Dec 93 (FM) FTP now works with VMS hosts. +** FTP path must be Unix-style and cannot include +** the device or top directory. +*/ + +#ifndef VMS +/* #define LONG_LIST */ /* Define this for long style unix listings (ls -l) */ +/* #define NO_PARENT_DIR_REFERENCE */ /* Define this for no parent links */ +#endif /* !VMS */ + +#include "HTUtils.h" +#include "tcp.h" +#include "HTFile.h" /* Implemented here */ +#ifdef VMS +#include <stat.h> +#endif /* VMS */ + +#ifndef VMS +#ifdef LONG_LIST +#include <pwd.h> +#include <grp.h> +#endif /* LONG_LIST */ +#endif /* !VMS */ + +#define INFINITY 512 /* file name length @@ FIXME */ +#define MULTI_SUFFIX ".multi" /* Extension for scanning formats */ + +#define HT_EM_SPACE ((char)2) + +#define FREE(x) if (x) {free(x); x = NULL;} + +#ifdef VMS +#include "HTVMSUtils.h" +#endif /* VMS */ + +#include "HTParse.h" +#include "HTTCP.h" +#ifndef DECNET +#include "HTFTP.h" +#endif /* !DECNET */ +#include "HTAnchor.h" +#include "HTAtom.h" +#include "HTWriter.h" +#include "HTFWriter.h" +#include "HTInit.h" +#include "HTBTree.h" +#include "HTAlert.h" +#include "HTCJK.h" + +#include "LYexit.h" +#include "LYLeaks.h" + +typedef struct _HTSuffix { + char * suffix; + HTAtom * rep; + HTAtom * encoding; + float quality; +} HTSuffix; + +#ifndef NGROUPS +#ifdef NGROUPS_MAX +#define NGROUPS NGROUPS_MAX +#else +#define NGROUPS 32 +#endif /* NGROUPS_MAX */ +#endif /* NGROUPS */ + + +#ifdef USE_DIRENT /* Set this for Sys V systems */ +#define STRUCT_DIRENT struct dirent +#else +#define STRUCT_DIRENT struct direct +#endif /* USE_DIRENT */ + +#include "HTML.h" /* For directory object building */ + +#define PUTC(c) (*target->isa->put_character)(target, c) +#define PUTS(s) (*target->isa->put_string)(target, s) +#define START(e) (*target->isa->start_element)(target, e, 0, 0, 0) +#define END(e) (*target->isa->end_element)(target, e, 0) +#define FREE_TARGET (*target->isa->_free)(target) +struct _HTStructured { + CONST HTStructuredClass * isa; + /* ... */ +}; + + +/* Controlling globals +** +*/ + +PUBLIC int HTDirAccess = HT_DIR_OK; + +#ifdef DIRED_SUPPORT +PUBLIC int HTDirReadme = HT_DIR_README_NONE; +#define FILES_FIRST 1 +#define MIXED_STYLE 2 +extern BOOLEAN lynx_edit_mode; +extern BOOLEAN dir_list_style; +#else +PUBLIC int HTDirReadme = HT_DIR_README_TOP; +#endif /* DIRED_SUPPORT */ + +extern int current_char_set; +extern char *LYchar_set_names[]; +extern BOOL HTPassEightBitRaw; +extern HTCJKlang HTCJK; + +PRIVATE char *HTMountRoot = "/Net/"; /* Where to find mounts */ +#ifdef VMS +PRIVATE char *HTCacheRoot = "/WWW$SCRATCH"; /* Where to cache things */ +#else +PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_"; /* Where to cache things */ +#endif /* VMS */ + +/* PRIVATE char *HTSaveRoot = "$(HOME)/WWW/";*/ /* Where to save things */ + + +/* Suffix registration +*/ + +PRIVATE HTList * HTSuffixes = 0; +PRIVATE HTSuffix no_suffix = { "*", NULL, NULL, 1.0 }; +PRIVATE HTSuffix unknown_suffix = { "*.*", NULL, NULL, 1.0}; + +/* + * To free up the suffixes at program exit. + */ +PRIVATE void free_suffixes NOPARAMS; + +#ifdef LONG_LIST +PRIVATE void LYListFmtParse ARGS5( + char *, fmtstr, + char *, file, + HTStructured *, target, + char *, entry, + char *, tail) +{ + char c; + char *s; + char *end; + char *start; + char *str = NULL; + struct stat st; + char buf[512]; + char fmt[512]; + char type; + struct passwd *p; + struct group *g; + time_t now; + char *datestr; + int len; +#define SEC_PER_YEAR (60 * 60 * 24 * 365) + static char *pbits[] = { "---", "--x", "-w-", "-wx", + "r--", "r-x", "rw-", "rwx", 0 }; + static char *psbits[] = { "--S", "--s", "-wS", "-ws", + "r-S", "r-s", "rwS", "rws", 0 }; +#define PBIT(a, n, s) (s) ? psbits[((a) >> (n)) & 0x7] : \ + pbits[((a) >> (n)) & 0x7] + + if (lstat(file, &st) < 0) + fmtstr = "%a"; /* can't stat so just do anchor */ + + StrAllocCopy(str, fmtstr); + s = str; + end = str + strlen(str); + START(HTML_PRE); + while (*s) { + start = s; + while (*s) { + if (*s == '%') { + if (*(s+1) == '%') /* literal % */ + s++; + else + break; + } + s++; + } + /* s is positioned either at a % or at \0 */ + *s = '\0'; + if (s > start) { /* some literal chars. */ + PUTS(start); + } + if (s == end) + break; + start = ++s; + while (isdigit(*s) || *s == '.' || *s == '-') + s++; + c = *s; /* the format char. or \0 */ + *s = '\0'; + + switch (c) { + case '\0': + break; + + case 'A': + case 'a': /* anchor */ + HTDirEntry(target, tail, entry); + sprintf(fmt, "%%%ss", start); + sprintf(buf, fmt, entry); + PUTS(buf); + END(HTML_A); + if (c != 'A' && (st.st_mode & S_IFMT) == S_IFLNK && + (len = readlink(file, buf, sizeof(buf))) >= 0) { + PUTS(" -> "); + buf[len] = '\0'; + PUTS(buf); + } + *buf = '\0'; + break; + + case 'd': /* date */ + now = time(0); + datestr = ctime(&st.st_mtime); + if ((now - st.st_mtime) < SEC_PER_YEAR/2) + /* MMM DD HH:MM */ + sprintf(buf, "%.12s", datestr + 4); + else + /* MMM DD YYYY */ + sprintf(buf, "%.7s %.4s ", datestr + 4, + datestr + 20); + sprintf(fmt, "%%%ss", start); + sprintf(buf, fmt, buf); + break; + + case 's': /* size in bytes */ + sprintf(fmt, "%%%sd", start); + sprintf(buf, fmt, st.st_size); + break; + + case 'K': /* size in Kilobytes but not for directories */ + if ((st.st_mode & S_IFMT) == S_IFDIR) { + sprintf(fmt, "%%%ss ", start); + sprintf(buf, fmt, ""); + break; + } + /* FALL THROUGH */ + case 'k': /* size in Kilobytes */ + sprintf(fmt, "%%%sdK", start); + sprintf(buf, fmt, (st.st_size+1023)/1024); + break; + + case 'p': /* unix-style permission bits */ + switch(st.st_mode & S_IFMT) { + case S_IFIFO: type = 'p'; break; + case S_IFCHR: type = 'c'; break; + case S_IFDIR: type = 'd'; break; + case S_IFBLK: type = 'b'; break; + case S_IFREG: type = '-'; break; + case S_IFLNK: type = 'l'; break; +#ifdef S_IFSOCK + case S_IFSOCK: type = 's'; break; +#endif /* S_IFSOCK */ + default: type = '?'; break; + } + sprintf(buf, "%c%s%s%s", type, + PBIT(st.st_mode, 6, st.st_mode & S_ISUID), + PBIT(st.st_mode, 3, st.st_mode & S_ISGID), + PBIT(st.st_mode, 0, 0)); + sprintf(fmt, "%%%ss", start); + sprintf(buf, fmt, buf); + break; + + case 'o': /* owner */ + sprintf(fmt, "%%%ss", start); + p = getpwuid(st.st_uid); + if (p) { + sprintf(fmt, "%%%ss", start); + sprintf(buf, fmt, p->pw_name); + } else { + + sprintf(fmt, "%%%sd", start); + sprintf(buf, fmt, st.st_uid); + } + break; + + case 'g': /* group */ + g = getgrgid(st.st_gid); + if (g) { + sprintf(fmt, "%%%ss", start); + sprintf(buf, fmt, g->gr_name); + } else { + sprintf(fmt, "%%%sd", start); + sprintf(buf, fmt, st.st_gid); + } + break; + + case 'l': /* link count */ + sprintf(fmt, "%%%sd", start); + sprintf(buf, fmt, st.st_nlink); + break; + + default: + fprintf(stderr, + "Unknown format character `%c' in list format\n", c); + break; + } + PUTS(buf); + + s++; + } + END(HTML_PRE); + PUTS("\n"); + FREE(str); +} +#endif /* LONG_LIST */ + +/* Define the representation associated with a file suffix +** ------------------------------------------------------- +** +** Calling this with suffix set to "*" will set the default +** representation. +** Calling this with suffix set to "*.*" will set the default +** representation for unknown suffix files which contain a ".". +** +** If filename suffix is already defined its previous +** definition is overridden. +*/ +PUBLIC void HTSetSuffix ARGS4( + CONST char *, suffix, + CONST char *, representation, + CONST char *, encoding, + float, value) +{ + HTSuffix * suff; + + if (strcmp(suffix, "*") == 0) + suff = &no_suffix; + else if (strcmp(suffix, "*.*") == 0) + suff = &unknown_suffix; + else { + HTList *cur = HTSuffixes; + + while (NULL != (suff = (HTSuffix*)HTList_nextObject(cur))) { + if (suff->suffix && 0 == strcmp(suff->suffix, suffix)) + break; + } + if (!suff) { /* Not found -- create a new node */ + suff = (HTSuffix *) calloc(1, sizeof(HTSuffix)); + if (suff == NULL) + outofmem(__FILE__, "HTSetSuffix"); + + /* + * Memory leak fixed. + * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe + */ + if (!HTSuffixes) { + HTSuffixes = HTList_new(); + atexit(free_suffixes); + } + + HTList_addObject(HTSuffixes, suff); + + StrAllocCopy(suff->suffix, suffix); + } + } + + suff->rep = HTAtom_for(representation); + + /* + * Memory leak fixed. + * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe + * Invariant code removed. + */ + suff->encoding = HTAtom_for(encoding); + + suff->quality = value; +} + +/* +** Purpose: Free all added suffixes. +** Arguments: void +** Return Value: void +** Remarks/Portability/Dependencies/Restrictions: +** To be used at program exit. +** Revision History: +** 05-28-94 created Lynx 2-3-1 Garrett Arch Blythe +*/ +PRIVATE void free_suffixes NOARGS +{ + HTSuffix * suff = NULL; + + /* + * Loop through all suffixes. + */ + while (!HTList_isEmpty(HTSuffixes)) { + /* + * Free off each item and its members if need be. + */ + suff = (HTSuffix *)HTList_removeLastObject(HTSuffixes); + FREE(suff->suffix); + FREE(suff); + } + /* + * Free off the list itself. + */ + HTList_delete(HTSuffixes); + HTSuffixes = NULL; +} + + +/* Send README file +** +** If a README file exists, then it is inserted into the document here. +*/ +#ifdef GOT_READ_DIR +PRIVATE void do_readme ARGS2(HTStructured *, target, CONST char *, localname) +{ + FILE * fp; + char * readme_file_name = + malloc(strlen(localname)+ 1 + strlen(HT_DIR_README_FILE) + 1); + strcpy(readme_file_name, localname); + strcat(readme_file_name, "/"); + strcat(readme_file_name, HT_DIR_README_FILE); + + fp = fopen(readme_file_name, "r"); + + if (fp) { + HTStructuredClass targetClass; + + targetClass = *target->isa; /* (Can't init agregate in K&R) */ + START(HTML_PRE); + for (;;){ + char c = fgetc(fp); + if (c == (char)EOF) break; +#ifdef NOTDEFINED + switch (c) { + case '&': + case '<': + case '>': + PUTC('&'); + PUTC('#'); + PUTC((char)(c / 10)); + PUTC((char) (c % 10)); + PUTC(';'); + break; +/* case '\n': + PUTC('\r'); +Bug removed thanks to joe@athena.mit.edu */ + default: + PUTC(c); + } +#else + PUTC(c); +#endif /* NOTDEFINED */ + } + END(HTML_PRE); + fclose(fp); + } +} +#endif /* GOT_READ_DIR */ + + +/* Make the cache file name for a W3 document +** ------------------------------------------ +** Make up a suitable name for saving the node in +** +** E.g. /tmp/WWW_Cache_news/1234@cernvax.cern.ch +** /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx +** +** On exit, +** returns a malloc'ed string which must be freed by the caller. +*/ +PUBLIC char * HTCacheFileName ARGS1( + CONST char *, name) +{ + char * access = HTParse(name, "", PARSE_ACCESS); + char * host = HTParse(name, "", PARSE_HOST); + char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION); + + char * result; + result = (char *)malloc( + strlen(HTCacheRoot)+strlen(access) + +strlen(host)+strlen(path)+6+1); + if (result == NULL) + outofmem(__FILE__, "HTCacheFileName"); + sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path); + FREE(path); + FREE(access); + FREE(host); + return result; +} + + +/* Open a file for write, creating the path +** ---------------------------------------- +*/ +#ifdef NOT_IMPLEMENTED +PRIVATE int HTCreatePath ARGS1(CONST char *,path) +{ + return -1; +} +#endif /* NOT_IMPLEMENTED */ + +/* Convert filenames between local and WWW formats +** ----------------------------------------------- +** Make up a suitable name for saving the node in +** +** E.g. $(HOME)/WWW/news/1234@cernvax.cern.ch +** $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx +** +** On exit, +** returns a malloc'ed string which must be freed by the caller. +*/ +PUBLIC char * HTLocalName ARGS1( + CONST char *, name) +{ + char * access = HTParse(name, "", PARSE_ACCESS); + char * host = HTParse(name, "", PARSE_HOST); + char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION); + + HTUnEscape(path); /* Interpret % signs */ + + if (0 == strcmp(access, "file")) { /* local file */ + FREE(access); + if ((0 == strcasecomp(host, HTHostName())) || + (0 == strcasecomp(host, "localhost")) || !*host) { + FREE(host); + if (TRACE) + fprintf(stderr, "Node `%s' means path `%s'\n", name, path); + return(path); + } else { + char * result = (char *)malloc( + strlen("/Net/")+strlen(host)+strlen(path)+1); + if (result == NULL) + outofmem(__FILE__, "HTLocalName"); + sprintf(result, "%s%s%s", "/Net/", host, path); + FREE(host); + FREE(path); + if (TRACE) + fprintf(stderr, "Node `%s' means file `%s'\n", name, result); + return result; + } + } else { /* other access */ + char * result; +#ifdef VMS + char * home = getenv("HOME"); + if (!home) + home = HTCacheRoot; + else + home = HTVMS_wwwName(home); +#else + CONST char * home = (CONST char*)getenv("HOME"); + if (!home) + home = "/tmp"; +#endif /* VMS */ + result = (char *)malloc( + strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1); + if (result == NULL) + outofmem(__FILE__, "HTLocalName"); + sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path); + FREE(path); + FREE(access); + FREE(host); + return result; + } +} + + +/* Make a WWW name from a full local path name +** +** Bugs: +** At present, only the names of two network root nodes are hand-coded +** in and valid for the NeXT only. This should be configurable in +** the general case. +*/ + +PUBLIC char * WWW_nameOfFile ARGS1( + CONST char *, name) +{ + char * result; +#ifdef NeXT + if (0 == strncmp("/private/Net/", name, 13)) { + result = (char *)malloc(7+strlen(name+13)+1); + if (result == NULL) + outofmem(__FILE__, "WWW_nameOfFile"); + sprintf(result, "file://%s", name+13); + } else +#endif /* NeXT */ + if (0 == strncmp(HTMountRoot, name, 5)) { + result = (char *)malloc(7+strlen(name+5)+1); + if (result == NULL) + outofmem(__FILE__, "WWW_nameOfFile"); + sprintf(result, "file://%s", name+5); + } else { + result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1); + if (result == NULL) + outofmem(__FILE__, "WWW_nameOfFile"); + sprintf(result, "file://%s%s", HTHostName(), name); + } + if (TRACE) + fprintf(stderr, "File `%s'\n\tmeans node `%s'\n", name, result); + return result; +} + + +/* Determine a suitable suffix, given the representation +** ----------------------------------------------------- +** +** On entry, +** rep is the atomized MIME style representation +** +** On exit, +** returns a pointer to a suitable suffix string if one has been +** found, else "". +*/ +PUBLIC CONST char * HTFileSuffix ARGS1( + HTAtom*, rep) +{ + HTSuffix * suff; + int n; + int i; + +#define NO_INIT /* dont init anymore since I do it in Lynx at startup */ +#ifndef NO_INIT + if (!HTSuffixes) + HTFileInit(); +#endif /* !NO_INIT */ + n = HTList_count(HTSuffixes); + for (i = 0; i < n; i++) { + suff = (HTSuffix *)HTList_objectAt(HTSuffixes, i); + if (suff->rep == rep) { + return suff->suffix; /* OK -- found */ + } + } + return ""; /* Dunno */ +} + + +/* Determine file format from file name +** ------------------------------------ +** +** This version will return the representation and also set +** a variable for the encoding. +** +** It will handle for example x.txt, x.txt,Z, x.Z +*/ + +PUBLIC HTFormat HTFileFormat ARGS2( + CONST char *, filename, + HTAtom **, pencoding) +{ + HTSuffix * suff; + int n; + int i; + int lf; +#ifdef VMS + char *semicolon = NULL; +#endif /* VMS */ + extern char LYforce_HTML_mode; + + if (LYforce_HTML_mode) { + LYforce_HTML_mode = FALSE; + return WWW_HTML; + } + +#ifdef VMS + /* + * Trim at semicolon if a version number was + * included, so it doesn't interfere with the + * code for getting the MIME type. - FM + */ + if ((semicolon = strchr(filename, ';')) != NULL) + *semicolon = '\0'; +#endif /* VMS */ + +#ifndef NO_INIT + if (!HTSuffixes) + HTFileInit(); +#endif /* !NO_INIT */ + *pencoding = NULL; + lf = strlen(filename); + n = HTList_count(HTSuffixes); + for (i = 0; i < n; i++) { + int ls; + suff = (HTSuffix *)HTList_objectAt(HTSuffixes, i); + ls = strlen(suff->suffix); + if ((ls <= lf) && 0 == strcasecomp(suff->suffix, filename + lf - ls)) { + int j; + *pencoding = suff->encoding; + if (suff->rep) { +#ifdef VMS + if (semicolon != NULL) + *semicolon = ';'; +#endif /* VMS */ + return suff->rep; /* OK -- found */ + } + for (j = 0; j < n; j++) { /* Got encoding, need representation */ + int ls2; + suff = (HTSuffix *)HTList_objectAt(HTSuffixes, j); + ls2 = strlen(suff->suffix); + if ((ls <= lf) && 0 == strncasecomp( + suff->suffix, filename + lf - ls -ls2, ls2)) { + if (suff->rep) { +#ifdef VMS + if (semicolon != NULL) + *semicolon = ';'; +#endif /* VMS */ + return suff->rep; + } + } + } + + } + } + + /* defaults tree */ + + suff = strchr(filename, '.') ? /* Unknown suffix */ + ( unknown_suffix.rep ? &unknown_suffix : &no_suffix) + : &no_suffix; + + /* set default encoding unless found with suffix already */ + if (!*pencoding) + *pencoding = suff->encoding ? suff->encoding + : HTAtom_for("binary"); +#ifdef VMS + if (semicolon != NULL) + *semicolon = ';'; +#endif /* VMS */ + return suff->rep ? suff->rep : WWW_BINARY; +} + + +/* Revise the file format in relation to the Lynx charset. - FM +** ------------------------------------------------------- +** +** This checks the format associated with an anchor for +** an extended MIME Content-Type, and if a charset is +** indicated, sets Lynx up for proper handling in relation +** to the currently selected character set. - FM +*/ +PUBLIC HTFormat HTCharsetFormat ARGS2( + HTFormat, format, + HTParentAnchor *, anchor) + +{ + char *cp = NULL, *cp1, *cp2; + int i; + + FREE(anchor->charset); + StrAllocCopy(cp, format->name); + for (i = 0; cp[i]; i++) + cp[i] = TOLOWER(cp[i]); + if (((cp1 = strchr(cp, ';')) != NULL) && + (cp2 = strstr(cp1, "charset")) != NULL) { + if (TRACE) + fprintf(stderr, + "HTCharsetFormat: Extended MIME Content-Type is %s\n", + format->name); + cp2 += 7; + while (*cp2 == ' ' || *cp2 == '=') + cp2++; + if (!strncmp(cp2, "us-ascii", 8) || + !strncmp(cp2, "iso-8859-1", 10)) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-8859-1"); + HTCJK = NOCJK; + } else if (!strncmp(cp2, "iso-8859-2", 10) && + !strncmp(LYchar_set_names[current_char_set], + "ISO Latin 2", 11)) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-8859-2"); + HTPassEightBitRaw = TRUE; + } else if (!strncmp(cp2, "iso-8859-", 9) && + !strncmp(LYchar_set_names[current_char_set], + "Other ISO Latin", 15)) { + /* + ** Hope it's a match, for now. - FM + */ + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-8859- "); + anchor->charset[9] = cp2[9]; + HTPassEightBitRaw = TRUE; + HTAlert(anchor->charset); + } else if (!strncmp(cp2, "koi8-r", 6) && + !strncmp(LYchar_set_names[current_char_set], + "KOI8-R character set", 20)) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "koi8-r"); + HTPassEightBitRaw = TRUE; + } else if (!strncmp(cp2, "euc-jp", 6) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "euc-jp"); + } else if (!strncmp(cp2, "shift_jis", 9) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "shift_jis"); + } else if (!strncmp(cp2, "iso-2022-jp", 11) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-2022-jp"); + } else if (!strncmp(cp2, "iso-2022-jp-2", 13) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-2022-jp-2"); + } else if (!strncmp(cp2, "euc-kr", 6) && + HTCJK == KOREAN) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "euc-kr"); + } else if (!strncmp(cp2, "iso-2022-kr", 11) && + HTCJK == KOREAN) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-2022-kr"); + } else if ((!strncmp(cp2, "big5", 4) || + !strncmp(cp2, "cn-big5", 7)) && + HTCJK == TAIPEI) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "big5"); + } else if (!strncmp(cp2, "euc-cn", 6) && + HTCJK == CHINESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "euc-cn"); + } else if ((!strncmp(cp2, "gb2312", 6) || + !strncmp(cp2, "cn-gb", 5)) && + HTCJK == CHINESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "gb2312"); + } else if (!strncmp(cp2, "iso-2022-cn", 11) && + HTCJK == CHINESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-2022-cn"); + } + } + FREE(cp); + + return format; +} + + +/* Determine value from file name +** ------------------------------ +** +*/ + +PUBLIC float HTFileValue ARGS1( + CONST char *, filename) +{ + HTSuffix * suff; + int n; + int i; + int lf = strlen(filename); + +#ifndef NO_INIT + if (!HTSuffixes) + HTFileInit(); +#endif /* !NO_INIT */ + n = HTList_count(HTSuffixes); + for (i = 0; i < n; i++) { + int ls; + suff = (HTSuffix *)HTList_objectAt(HTSuffixes, i); + ls = strlen(suff->suffix); + if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) { + if (TRACE) + fprintf(stderr, "File: Value of %s is %.3f\n", + filename, suff->quality); + return suff->quality; /* OK -- found */ + } + } + return 0.3; /* Dunno! */ +} + + +/* Determine write access to a file +** -------------------------------- +** +** On exit, +** return value YES if file can be accessed and can be written to. +** +** Bugs: +** 1. No code for non-unix systems. +** 2. Isn't there a quicker way? +*/ + +#ifdef VMS +#define NO_GROUPS +#endif /* VMS */ +#ifdef NO_UNIX_IO +#define NO_GROUPS +#endif /* NO_UNIX_IO */ +#ifdef PCNFS +#define NO_GROUPS +#endif /* PCNFS */ + +PUBLIC BOOL HTEditable ARGS1( + CONST char *, filename) +{ +#ifdef NO_GROUPS + return NO; /* Safe answer till we find the correct algorithm */ +#else +#ifdef NeXT + int groups[NGROUPS]; +#else + gid_t groups[NGROUPS]; +#endif /* NeXT */ + uid_t myUid; + int ngroups; /* The number of groups */ + struct stat fileStatus; + int i; + + if (stat(filename, &fileStatus)) /* Get details of filename */ + return NO; /* Can't even access file! */ + + ngroups = getgroups(NGROUPS, groups); /* Groups to which I belong */ + myUid = geteuid(); /* Get my user identifier */ + + if (TRACE) { + int i; + fprintf(stderr, + "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (", + (unsigned int) fileStatus.st_mode, fileStatus.st_uid, + fileStatus.st_gid, + myUid, ngroups); + for (i=0; i<ngroups; i++) fprintf(stderr, " %d", groups[i]); + fprintf(stderr, ")\n"); + } + + if (fileStatus.st_mode & 0002) /* I can write anyway? */ + return YES; + + if ((fileStatus.st_mode & 0200) /* I can write my own file? */ + && (fileStatus.st_uid == myUid)) + return YES; + + if (fileStatus.st_mode & 0020) /* Group I am in can write? */ + { + for (i=0; i<ngroups; i++) { + if (groups[i] == fileStatus.st_gid) + return YES; + } + } + if (TRACE) + fprintf(stderr, "\tFile is not editable.\n"); + return NO; /* If no excuse, can't do */ +#endif /* NO_GROUPS */ +} + + +/* Make a save stream +** ------------------ +** +** The stream must be used for writing back the file. +** @@@ no backup done +*/ +PUBLIC HTStream * HTFileSaveStream ARGS1( + HTParentAnchor *, anchor) +{ + + CONST char * addr = HTAnchor_address((HTAnchor*)anchor); + char * localname = HTLocalName(addr); + + FILE* fp = fopen(localname, "w"); + if (!fp) + return NULL; + + return HTFWriter_new(fp); + +} + +/* Output one directory entry +** +*/ +PUBLIC void HTDirEntry ARGS3( + HTStructured *, target, + CONST char *, tail, + CONST char *, entry) +{ + char * relative; + char * escaped = HTEscape(entry, URL_XPALPHAS); + + + if (tail == NULL || *tail == '\0') { + /* handle extra slash at end of path */ + HTStartAnchor(target, NULL, escaped); + } else { + /* If empty tail, gives absolute ref below */ + relative = (char*) malloc(strlen(tail) + strlen(escaped)+2); + if (relative == NULL) + outofmem(__FILE__, "DirRead"); + sprintf(relative, "%s/%s", tail, escaped); + HTStartAnchor(target, NULL, relative); + FREE(relative); + } + FREE(escaped); +} + +/* Output parent directory entry +** +** This gives the TITLE and H1 header, and also a link +** to the parent directory if appropriate. +*/ +PUBLIC void HTDirTitles ARGS2( + HTStructured *, target, + HTAnchor * , anchor) +{ + char * logical = HTAnchor_address(anchor); + char * path = HTParse(logical, "", PARSE_PATH + PARSE_PUNCTUATION); + char * current; + char * cp = NULL; + + /* Trim out the ;type= parameter, if present. - FM */ + if ((cp = strrchr(path, ';')) != NULL) { + if (!strncasecomp((cp+1), "type=", 5)) { + if (TOUPPER(*(cp+6)) == 'D' || + TOUPPER(*(cp+6)) == 'A' || + TOUPPER(*(cp+6)) == 'I') + *cp = '\0'; + } + } + current = strrchr(path, '/'); /* last part or "" */ + + { + char * printable = NULL; + +#ifdef DIRED_SUPPORT + if (0 == strncasecomp(path, "/%2F", 4)) + StrAllocCopy(printable, (path+1)); + else + StrAllocCopy(printable, path); + if (0 == strncasecomp(printable, "/vmsysu%2b", 10) || + 0 == strncasecomp(printable, "/anonymou.", 10)) { + StrAllocCopy(cp, (printable+1)); + StrAllocCopy(printable, cp); + FREE(cp); + } +#else + StrAllocCopy(printable, (current + 1)); +#endif /* DIRED_SUPPORT */ + + START(HTML_HEAD); + PUTS("\n"); + HTUnEscape(printable); + START(HTML_TITLE); + PUTS(*printable ? printable : "Welcome"); + PUTS(" directory"); + END(HTML_TITLE); + PUTS("\n"); + END(HTML_HEAD); + PUTS("\n"); + +#ifdef DIRED_SUPPORT + START(HTML_H2); + PUTS(*printable ? "Current directory is " : ""); + PUTS(*printable ? printable : "Welcome"); + END(HTML_H2); + PUTS("\n"); +#else + START(HTML_H1); + PUTS(*printable ? printable : "Welcome"); + END(HTML_H1); + PUTS("\n"); +#endif /* DIRED_SUPPORT */ + if (((0 == strncasecomp(printable, "vmsysu:", 7)) && + (cp = strchr(printable, '.')) != NULL && + strchr(cp, '/') == NULL) || + (0 == strncasecomp(printable, "anonymou.", 9) && + strchr(printable, '/') == NULL)) { + FREE(printable); + FREE(logical); + FREE(path); + return; + } + FREE(printable); + } + +#ifndef NO_PARENT_DIR_REFERENCE + /* Make link back to parent directory + */ + + if (current && current[1]) { /* was a slash AND something else too */ + char * parent; + char * relative; + *current++ = 0; + parent = strrchr(path, '/'); /* penultimate slash */ + + if ((parent && 0 == strncasecomp(parent, "/%2F", 4)) || + 0 == strncasecomp(current, "%2F", 3)) { + FREE(logical); + FREE(path); + return; + } + + relative = (char*) malloc(strlen(current) + 4); + if (relative == NULL) + outofmem(__FILE__, "DirRead"); + sprintf(relative, "%s/..", current); +#ifndef VMS + { + /* + * On Unix, if it's not ftp and the directory cannot + * be read, don't put out a link. + * + * On VMS, this problem is dealt with internally by + * HTVMSBrowseDir(). + */ + extern BOOLEAN LYisLocalFile PARAMS((char *logical)); + DIR * dp=NULL; + + if (LYisLocalFile(logical)) { + if ((dp = opendir(relative)) == NULL) { + FREE(logical); + FREE(relative); + FREE(path); + return; + } + if (dp) + closedir(dp); + } + } +#endif /* !VMS */ + HTStartAnchor(target, "", relative); + FREE(relative); + +#ifdef DIRED_SUPPORT + if (dir_list_style != MIXED_STYLE) +#endif /* DIRED_SUPPORT */ + PUTS("Up to "); + if (parent) { +#ifdef DIRED_SUPPORT + if (dir_list_style == MIXED_STYLE) { + PUTS("../"); + } else { +#else + { +#endif /* DIRED_SUPPORT */ + char * printable = NULL; + StrAllocCopy(printable, parent + 1); + HTUnEscape(printable); + PUTS(printable); + FREE(printable); + } + } else { + PUTS("/"); + } + + END(HTML_A); + } +#endif /* NO_PARENT_DIR_REFERENCE */ + + FREE(logical); + FREE(path); + return; +} + + + +/* Load a document +** --------------- +** +** On entry, +** addr must point to the fully qualified hypertext reference. +** This is the physical address of the file +** +** On exit, +** returns <0 Error has occured. +** HTLOADED OK +** +*/ +PUBLIC int HTLoadFile ARGS4( + CONST char *, addr, + HTParentAnchor *, anchor, + HTFormat, format_out, + HTStream *, sink) +{ + char * filename = NULL; + char * access = NULL; + HTFormat format; + char * nodename = NULL; + char * newname = NULL; /* Simplified name of file */ + HTAtom * encoding; /* @@ not used yet */ +#ifdef VMS + struct stat stat_info; +#else + extern char *list_format; +#endif /* VMS */ + + /* + ** Reduce the filename to a basic form (hopefully unique!) + */ + StrAllocCopy(newname, addr); + filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION); + nodename=HTParse(newname, "", PARSE_HOST); + + /* + ** If access is ftp, or file is on another host, invoke ftp now. + */ + access = HTParse(newname, "", PARSE_ACCESS); + if (strcmp("ftp", access) == 0 || + (strcmp("localhost", nodename) != 0 && +#ifdef VMS + strcasecomp(nodename, HTHostName()) != 0)) +#else + strcmp(nodename, HTHostName()) != 0)) +#endif /* VMS */ + { + FREE(newname); + FREE(filename); + FREE(nodename); + FREE(access); + return HTFTPLoad(addr, anchor, format_out, sink); + } else { + FREE(newname); + FREE(access); + } +#ifdef VMS + HTUnEscape(filename); +#endif /* VMS */ + + /* + ** Determine the format and encoding mapped to any suffix. + */ + format = HTFileFormat(filename, &encoding); + + /* + ** Check the format for an extended MIME charset value, and + ** act on it if present. Otherwise, assume the ISO-8859-1 + ** character set for local files. If it's actually another + ** charset (e.g., ISO-8859-2 or KOI8-R) and the terminal is + ** using that, Lynx users should make the current character + ** set "ISO Latin 1" so that 8-bit characters are passed raw. + */ + format = HTCharsetFormat(format, anchor); + +#ifdef VMS + /* + ** Check to see if the 'filename' is in fact a directory. If it is + ** create a new hypertext object containing a list of files and + ** subdirectories contained in the directory. All of these are links + ** to the directories or files listed. + */ + if (HTStat(filename, &stat_info) == -1) { + if (TRACE) + fprintf(stderr, "HTLoadFile: Can't stat %s\n", filename); + } else { + if (((stat_info.st_mode) & S_IFMT) == S_IFDIR) { + if (HTDirAccess == HT_DIR_FORBID) { + FREE(filename); + FREE(nodename); + return HTLoadError(sink, 403, + "Directory browsing is not allowed."); + } + + if (HTDirAccess == HT_DIR_SELECTIVE) { + char * enable_file_name = + malloc(strlen(filename)+ 1 + + strlen(HT_DIR_ENABLE_FILE) + 1); + strcpy(enable_file_name, filename); + strcat(enable_file_name, "/"); + strcat(enable_file_name, HT_DIR_ENABLE_FILE); + if (HTStat(enable_file_name, &stat_info) == -1) { + FREE(filename); + FREE(nodename); + return HTLoadError(sink, 403, + "Selective access is not enabled for this directory"); + } + } + + FREE(filename); + FREE(nodename); + return HTVMSBrowseDir(addr, anchor, format_out, sink); + } + } + + /* + ** Assume that the file is in Unix-style syntax if it contains a '/' + ** after the leading one @@ + */ + { + FILE * fp; + char * vmsname = strchr(filename + 1, '/') ? + HTVMS_name(nodename, filename) : filename + 1; + fp = fopen(vmsname, "r", "shr=put", "shr=upd"); + + /* + ** If the file wasn't VMS syntax, then perhaps it is ultrix + */ + if (!fp) { + char ultrixname[INFINITY]; + if (TRACE) + fprintf(stderr, "HTLoadFile: Can't open as %s\n", vmsname); + sprintf(ultrixname, "%s::\"%s\"", nodename, filename); + fp = fopen(ultrixname, "r", "shr=put", "shr=upd"); + if (!fp) { + if (TRACE) + fprintf(stderr, "HTLoadFile: Can't open as %s\n", + ultrixname); + } + } + if (fp) { + int len; + char *cp = NULL; + char *semicolon = NULL; + + if (HTEditable(vmsname)) { + HTAtom * put = HTAtom_for("PUT"); + HTList * methods = HTAnchor_methods(anchor); + if (HTList_indexOf(methods, put) == (-1)) { + HTList_addObject(methods, put); + } + } + /* + * Trim vmsname at semicolon if a version number was + * included, so it doesn't interfere with the check + * for a compressed file. - FM + */ + if ((semicolon = strchr(vmsname, ';')) != NULL) + *semicolon = '\0'; + /* + ** Fake a Content-Encoding for compressed files. - FM + */ + if ((len = strlen(vmsname)) > 2) { + if ((vmsname[len - 1] == 'Z') && + (vmsname[len - 2] == '.' || + vmsname[len - 2] == '-' || + vmsname[len - 2] == '_') && + vmsname[len - 3] != ']' && + vmsname[len - 3] != ':') { + StrAllocCopy(cp, vmsname); + cp[len - 2] = '\0'; + format = HTFileFormat(cp, &encoding); + FREE(cp); + format = HTCharsetFormat(format, anchor); + StrAllocCopy(anchor->content_type, format->name); + StrAllocCopy(anchor->content_encoding, "x-compress"); + format = HTAtom_for("www/compressed"); + } else if ((len > 3) && + !strcasecomp((char *)&vmsname[len - 2], "gz")) { + if (vmsname[len - 3] == '.' || + vmsname[len - 3] == '-' || + vmsname[len - 3] == '_') { + StrAllocCopy(cp, vmsname); + cp[len - 3] = '\0'; + format = HTFileFormat(cp, &encoding); + FREE(cp); + format = HTCharsetFormat(format, anchor); + StrAllocCopy(anchor->content_type, format->name); + StrAllocCopy(anchor->content_encoding, "x-gzip"); + format = HTAtom_for("www/compressed"); + } + } + } + if (semicolon != NULL) + *semicolon = ';'; + FREE(filename); + FREE(nodename); + HTParseFile(format, format_out, anchor, fp, sink); + fclose(fp); + return HT_LOADED; + } /* If successfull open */ + FREE(filename); + } + +#else /* Unix: */ + + FREE(filename); + + /* + ** For unix, we try to translate the name into the name of a + ** transparently mounted file. + ** + ** Not allowed in secure (HTClienntHost) situations TBL 921019 + */ +#ifndef NO_UNIX_IO + /* Need protection here for telnet server but not httpd server */ + + if (!HTSecure) { /* try local file system */ + char * localname = HTLocalName(addr); + struct stat dir_info; + +#ifdef GOT_READ_DIR + /* Multiformat handling + ** + ** If needed, scan directory to find a good file. + ** Bug: we don't stat the file to find the length + */ + if ((strlen(localname) > strlen(MULTI_SUFFIX)) && + (0 == strcmp(localname + strlen(localname) - strlen(MULTI_SUFFIX), + MULTI_SUFFIX))) { + DIR *dp; + + STRUCT_DIRENT * dirbuf; + float best = NO_VALUE_FOUND; /* So far best is bad */ + HTFormat best_rep = NULL; /* Set when rep found */ + STRUCT_DIRENT best_dirbuf; /* Best dir entry so far */ + + char * base = strrchr(localname, '/'); + int baselen; + + if (!base || base == localname) + goto forget_multi; + *base++ = 0; /* Just got directory name */ + baselen = strlen(base)- strlen(MULTI_SUFFIX); + base[baselen] = 0; /* Chop off suffix */ + + dp = opendir(localname); + if (!dp) { +forget_multi: + FREE(localname); + FREE(nodename); + return HTLoadError(sink, 500, + "Multiformat: directory scan failed."); + } + + while ((dirbuf = readdir(dp))!=0) { + /* while there are directory entries to be read */ + if (dirbuf->d_ino == 0) + continue; /* if the entry is not being used, skip it */ + + if ((int)strlen(dirbuf->d_name) > baselen && /* Match? */ + !strncmp(dirbuf->d_name, base, baselen)) { + HTFormat rep = HTFileFormat(dirbuf->d_name, &encoding); + float value = HTStackValue(rep, format_out, + HTFileValue(dirbuf->d_name), + 0.0 /* @@@@@@ */); + if (value != NO_VALUE_FOUND) { + if (TRACE) + fprintf(stderr, + "HTLoadFile: value of presenting %s is %f\n", + HTAtom_name(rep), value); + if (value > best) { + best_rep = rep; + best = value; + best_dirbuf = *dirbuf; + } + } /* if best so far */ + } /* if match */ + + } /* end while directory entries left to read */ + closedir(dp); + + if (best_rep) { + format = best_rep; + base[-1] = '/'; /* Restore directory name */ + base[0] = 0; + StrAllocCat(localname, best_dirbuf.d_name); + goto open_file; + + } else { /* If not found suitable file */ + FREE(localname); + FREE(nodename); + return HTLoadError(sink, 403, /* List formats? */ + "Could not find suitable representation for transmission."); + } + /*NOTREACHED*/ + } /* if multi suffix */ + + /* + ** Check to see if the 'localname' is in fact a directory. If it + ** is create a new hypertext object containing a list of files and + ** subdirectories contained in the directory. All of these are + ** links to the directories or files listed. + ** NB This assumes the existance of a type 'STRUCT_DIRENT', which + ** will hold the directory entry, and a type 'DIR' which is used + ** to point to the current directory being read. + */ + if (stat(localname,&dir_info) == -1) { /* get file information */ + /* if can't read file information */ + if (TRACE) + fprintf(stderr, "HTLoadFile: can't stat %s\n", localname); + + } else { /* Stat was OK */ + + if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) { + /* if localname is a directory */ + + HTStructured* target; /* HTML object */ + HTStructuredClass targetClass; + + DIR *dp; + STRUCT_DIRENT * dirbuf; + + char * logical=0; + char * pathname=0; + char * tail=0; + + BOOL present[HTML_A_ATTRIBUTES]; + + char * tmpfilename = NULL; + struct stat file_info; + + if (TRACE) + fprintf(stderr,"%s is a directory\n",localname); + + /* + ** Check directory access. + ** Selective access means only those directories containing + ** a marker file can be browsed + */ + if (HTDirAccess == HT_DIR_FORBID) { + FREE(localname); + FREE(nodename); + return HTLoadError(sink, 403, + "Directory browsing is not allowed."); + } + + + if (HTDirAccess == HT_DIR_SELECTIVE) { + char * enable_file_name = + malloc(strlen(localname)+ 1 + + strlen(HT_DIR_ENABLE_FILE) + 1); + strcpy(enable_file_name, localname); + strcat(enable_file_name, "/"); + strcat(enable_file_name, HT_DIR_ENABLE_FILE); + if (stat(enable_file_name, &file_info) != 0) { + FREE(localname); + FREE(nodename); + return HTLoadError(sink, 403, + "Selective access is not enabled for this directory"); + } + } + + + dp = opendir(localname); + if (!dp) { + FREE(localname); + FREE(nodename); + return HTLoadError(sink, 403, "This directory is not readable."); + } + + + /* + ** Directory access is allowed and possible. + */ + logical = HTAnchor_address((HTAnchor*)anchor); + pathname = HTParse(logical, "", + PARSE_PATH + PARSE_PUNCTUATION); + + if (!strcmp(pathname,"/")) /* root path */ + StrAllocCopy (tail, "/foo/.."); + else + { + char * p = strrchr(pathname, '/'); /* find lastslash */ + StrAllocCopy(tail, p+1); /* take slash off the beginning */ + } + FREE(pathname); + + target = HTML_new(anchor, format_out, sink); + targetClass = *target->isa; /* Copy routine entry points */ + + { int i; + for (i = 0; i < HTML_A_ATTRIBUTES; i++) + present[i] = (i==HTML_A_HREF); + } + + HTDirTitles(target, (HTAnchor *)anchor); + +#ifdef DIRED_SUPPORT + HTAnchor_setFormat((HTParentAnchor *) anchor, WWW_DIRED); + lynx_edit_mode = TRUE; +#endif /* DIRED_SUPPORT */ + if (HTDirReadme == HT_DIR_README_TOP) + do_readme(target, localname); + { + HTBTree * bt = HTBTree_new((HTComparer)strcmp); + + while ((dirbuf = readdir(dp))!=0) + { + /* while there are directory entries to be read */ + HTBTElement * dirname = NULL; + extern BOOLEAN no_dotfiles, show_dotfiles; + + if (dirbuf->d_ino == 0) + /* if the entry is not being used, skip it */ + continue; + + if (strcmp(dirbuf->d_name, ".") == 0 /* skip self */ +#ifdef NO_PARENT_DIR_REFERENCE + || strcmp(dirbuf->d_name, "..") == 0 || +#else + || strcmp(dirbuf->d_name, "..") != 0 && +#endif + ((no_dotfiles || !show_dotfiles) && + dirbuf->d_name[0] == '.')) + /* skip those files whose name + * begins with '.' */ + continue; + + dirname = (HTBTElement *)malloc( + strlen(dirbuf->d_name) + 4); + if (dirname == NULL) + outofmem(__FILE__,"DirRead"); + StrAllocCopy(tmpfilename,localname); + if (strcmp(localname,"/")) + /* if filename is not root directory */ + StrAllocCat(tmpfilename,"/"); + + StrAllocCat(tmpfilename,dirbuf->d_name); + stat(tmpfilename, &file_info); + if (((file_info.st_mode) & S_IFMT) == S_IFDIR) +#ifndef DIRED_SUPPORT + sprintf((char *)dirname,"D%s",dirbuf->d_name); + else + sprintf((char *)dirname,"F%s",dirbuf->d_name); + /* D & F to have first directories, then files */ +#else + if (dir_list_style == MIXED_STYLE) + sprintf((char *)dirname," %s/",dirbuf->d_name); + else + sprintf((char *)dirname,"D%s",dirbuf->d_name); + else if (dir_list_style == MIXED_STYLE) + sprintf((char *)dirname," %s",dirbuf->d_name); + else if (dir_list_style == FILES_FIRST) + sprintf((char *)dirname,"C%s",dirbuf->d_name); + /* C & D to have first files, then directories */ + else + sprintf((char *)dirname,"F%s",dirbuf->d_name); +#endif /* !DIRED_SUPPORT */ + HTBTree_add(bt,dirname); /* Sort dirname in the tree bt */ + } + + /* + ** Run through tree printing out in order + */ + { + HTBTElement * next_element = HTBTree_next(bt,NULL); + /* pick up the first element of the list */ + char state; + /* I for initial (.. file), + D for directory file, + F for file */ + +#ifdef DIRED_SUPPORT + char test; +#endif /* DIRED_SUPPORT */ + state = 'I'; + + while (next_element != NULL) + { + char *entry, *file_extra; + + StrAllocCopy(tmpfilename,localname); + if (strcmp(localname,"/")) + + /* if filename is not root directory */ + StrAllocCat(tmpfilename,"/"); + + StrAllocCat(tmpfilename, + (char *)HTBTree_object(next_element)+1); + /* append the current entry's filename to the path */ + HTSimplify(tmpfilename); + /* Output the directory entry */ + if (strcmp((char *) + (HTBTree_object(next_element)),"D..")) + { +#ifdef DIRED_SUPPORT + test = *(char *)(HTBTree_object(next_element))=='D'?'D':'F'; + if (state != test) + { +#ifndef LONG_LIST + if (dir_list_style == FILES_FIRST) { + if (state == 'F') + END(HTML_DIR); + } else if (dir_list_style != MIXED_STYLE) + if (state == 'D') + END(HTML_DIR); +#endif /* !LONG_LIST */ + state = *(char *) + (HTBTree_object(next_element))=='D'?'D':'F'; + START(HTML_H2); + if (dir_list_style != MIXED_STYLE) + PUTS(state == 'D'?"Directories:":"Files"); +#else + if (state != *(char *)(HTBTree_object(next_element))) + { +#ifndef LONG_LIST + if (state == 'D') + END(HTML_DIR); +#endif /* !LONG_LIST */ + state = *(char *) + (HTBTree_object(next_element))=='D'?'D':'F'; + START(HTML_H2); + PUTS(state == 'D'?"Subdirectories:":"Files"); +#endif /* DIRED_SUPPORT */ + END(HTML_H2); +#ifndef LONG_LIST + START(HTML_DIR); +#endif /* !LONG_LIST */ + } +#ifndef LONG_LIST + START(HTML_LI); +#endif /* !LONG_LIST */ + } + entry = (char*)HTBTree_object(next_element)+1; + file_extra = NULL; + +#ifdef LONG_LIST + LYListFmtParse(list_format, tmpfilename, target, + entry, tail); +#else + HTDirEntry(target, tail, entry); + PUTS(entry); + END(HTML_A); + if (file_extra) { + PUTS(file_extra); + FREE(file_extra); + } +#endif /* LONG_LIST */ + + next_element = HTBTree_next(bt,next_element); + /* pick up the next element of the list; + if none, return NULL*/ + } + if (state == 'I') + { + START(HTML_P); + PUTS("Empty Directory"); + } +#ifndef LONG_LIST + else + END(HTML_DIR); +#endif /* !LONG_LIST */ + } + + /* end while directory entries left to read */ + closedir(dp); + FREE(logical); + FREE(tmpfilename); + FREE(tail); + HTBTreeAndObject_free(bt); + + if (HTDirReadme == HT_DIR_README_BOTTOM) + do_readme(target, localname); + FREE_TARGET; + FREE(localname); + FREE(nodename); + return HT_LOADED; /* document loaded */ + } + + } /* end if localname is directory */ + + } /* end if file stat worked */ + +/* End of directory reading section +*/ +#endif /* GOT_READ_DIR */ +open_file: + { + FILE * fp = fopen(localname,"r"); + + if (TRACE) + fprintf (stderr, "HTLoadFile: Opening `%s' gives %p\n", + localname, (void*)fp); + if (fp) { /* Good! */ + int len; + char *cp = NULL; + + if (HTEditable(localname)) { + HTAtom * put = HTAtom_for("PUT"); + HTList * methods = HTAnchor_methods(anchor); + if (HTList_indexOf(methods, put) == (-1)) { + HTList_addObject(methods, put); + } + } + /* + ** Fake a Content-Encoding for compressed files. - FM + */ + if ((len = strlen(localname)) > 2) { + if (localname[len - 1] == 'Z' && + localname[len - 2] == '.') { + StrAllocCopy(cp, localname); + cp[len - 2] = '\0'; + format = HTFileFormat(cp, &encoding); + FREE(cp); + format = HTCharsetFormat(format, anchor); + StrAllocCopy(anchor->content_type, format->name); + StrAllocCopy(anchor->content_encoding, "x-compress"); + format = HTAtom_for("www/compressed"); + } else if ((len > 3) && + !strcasecomp((char *)&localname[len - 2], + "gz") && + localname[len - 3] == '.') { + StrAllocCopy(cp, localname); + cp[len - 3] = '\0'; + format = HTFileFormat(cp, &encoding); + FREE(cp); + format = HTCharsetFormat(format, anchor); + StrAllocCopy(anchor->content_type, format->name); + StrAllocCopy(anchor->content_encoding, "x-gzip"); + format = HTAtom_for("www/compressed"); + } + } + FREE(localname); + FREE(nodename); + HTParseFile(format, format_out, anchor, fp, sink); + fclose(fp); + return HT_LOADED; + } /* If succesfull open */ + } /* scope of fp */ + } /* local unix file system */ +#endif /* !NO_UNIX_IO */ +#endif /* VMS */ + +#ifndef DECNET +/* Now, as transparently mounted access has failed, we try FTP. +*/ + { + /** Deal with case-sensitivity differences on VMS verus Unix **/ +#ifdef VMS + if (strcasecomp(nodename, HTHostName()) != 0) +#else + if (strcmp(nodename, HTHostName()) != 0) +#endif /* VMS */ + { + FREE(nodename); + if (!strncmp(addr, "file://localhost", 16)) { + return -1; /* never go to ftp site when URL + * is file://localhost + */ + } else { + return HTFTPLoad(addr, anchor, format_out, sink); + } + } + FREE(nodename); + } +#endif /* !DECNET */ + +/* All attempts have failed. +*/ + { + if (TRACE) + fprintf(stderr, "Can't open `%s', errno=%d\n", addr, SOCKET_ERRNO); + + return HTLoadError(sink, 403, "Can't access requested file."); + } + + +} + +/* Protocol descriptors +*/ +#ifdef GLOBALDEF_IS_MACRO +#define _HTFILE_C_1_INIT { "ftp", HTLoadFile, 0 } +GLOBALDEF (HTProtocol,HTFTP,_HTFILE_C_1_INIT); +#define _HTFILE_C_2_INIT { "file", HTLoadFile, HTFileSaveStream } +GLOBALDEF (HTProtocol,HTFile,_HTFILE_C_2_INIT); +#else +GLOBALDEF PUBLIC HTProtocol HTFTP = { "ftp", HTLoadFile, 0 }; +GLOBALDEF PUBLIC HTProtocol HTFile = { "file", HTLoadFile, HTFileSaveStream }; +#endif /* GLOBALDEF_IS_MACRO */ diff --git a/WWW/Library/Implementation/HTFile.h b/WWW/Library/Implementation/HTFile.h new file mode 100644 index 00000000..466ea8c3 --- /dev/null +++ b/WWW/Library/Implementation/HTFile.h @@ -0,0 +1,173 @@ +/* File access in libwww +** FILE ACCESS +** +** These are routines for local file access used by WWW browsers and servers. +** Implemented by HTFile.c. +** +** If the file is not a local file, then we pass it on to HTFTP in case it +** can be reached by FTP. +*/ +#ifndef HTFILE_H +#define HTFILE_H + +#include "HTFormat.h" +#include "HTAccess.h" +#include "HTML.h" /* SCW */ + +/* +** Controlling globals +** +** These flags control how directories and files are represented as +** hypertext, and are typically set by the application from command +** line options, etc. +*/ +extern int HTDirAccess; /* Directory access level */ + +#define HT_DIR_FORBID 0 /* Altogether forbidden */ +#define HT_DIR_SELECTIVE 1 /* If HT_DIR_ENABLE_FILE exists */ +#define HT_DIR_OK 2 /* Any accesible directory */ + +#define HT_DIR_ENABLE_FILE ".www_browsable" /* If exists, can browse */ + +extern int HTDirReadme; /* Include readme files in listing? */ + /* Values: */ +#define HT_DIR_README_NONE 0 /* No */ +#define HT_DIR_README_TOP 1 /* Yes, first */ +#define HT_DIR_README_BOTTOM 2 /* Yes, at the end */ + +#define HT_DIR_README_FILE "README" + +/* +** Convert filenames between local and WWW formats +*/ +extern char * HTLocalName PARAMS((CONST char * name)); + +/* +** Make a WWW name from a full local path name +*/ +extern char * WWW_nameOfFile PARAMS((const char * name)); + +/* +** Generate the name of a cache file +*/ +extern char * HTCacheFileName PARAMS((CONST char * name)); + +/* +** Output directory titles +** +** This is (like the next one) used by HTFTP. It is common code to generate +** the title and heading 1 and the parent directory link for any anchor. +*/ +extern void HTDirTitles PARAMS(( + HTStructured * target, + HTAnchor * anchor)); + +/* +** Output a directory entry +** +** This is used by HTFTP.c for example -- it is a common routine for +** generating a linked directory entry. +*/ +extern void HTDirEntry PARAMS(( + HTStructured * target, /* in which to put the linked text */ + CONST char * tail, /* last part of directory name */ + CONST char * entry)); /* name of this entry */ + +/* +** HTSetSuffix: Define the representation for a file suffix +** +** This defines a mapping between local file suffixes and file content +** types and encodings. +** +** ON ENTRY, +** +** suffix includes the "." if that is important (normally, yes!) +** +** representation is MIME-style content-type +** +** encoding is MIME-style content-transfer-encoding +** (8bit, 7bit, etc) +** +** quality an a priori judgement of the quality of such files +** (0.0..1.0) +** +** Example: HTSetSuffix(".ps", "application/postscript", "8bit", 1.0); +*/ +extern void HTSetSuffix PARAMS(( + CONST char * suffix, + CONST char * representation, + CONST char * encoding, + float quality)); + + +/* +** HTFileFormat: Get Representation and Encoding from file name. +** +** ON EXIT, +** +** return The represntation it imagines the file is in. +** +** *pEncoding The encoding (binary, 7bit, etc). See HTSetSuffix. +*/ +extern HTFormat HTFileFormat PARAMS(( + CONST char * filename, + HTAtom ** pEncoding)); + +/* +** HTCharsetFormat: Revise the file format in relation to the Lynx charset. +** +** This checks the format associated with an anchor for +** for an extended MIME Content-Type, and if a charset is +** indicated, sets Lynx up for proper handling in relation +** to the currently selected character set. - FM +*/ +extern HTFormat HTCharsetFormat PARAMS(( + HTFormat format, + HTParentAnchor * anchor)); + +/* +** Determine file value from file name. +*/ +extern float HTFileValue PARAMS(( + CONST char * filename)); + +/* +** Determine write access to a file. +** +** ON EXIT, +** +** return value YES if file can be accessed and can be written to. +** +** BUGS +** +** Isn't there a quicker way? +*/ +extern BOOL HTEditable PARAMS((CONST char * filename)); + +/* +** Determine a suitable suffix, given the representation. +** +** ON ENTRY, +** +** rep is the atomized MIME style representation +** +** ON EXIT, +** +** returns a pointer to a suitable suffix string if one has +** been found, else NULL. +*/ +extern CONST char * HTFileSuffix PARAMS(( + HTAtom* rep)); + +/* +** The Protocols +*/ +#ifdef GLOBALREF_IS_MACRO +extern GLOBALREF (HTProtocol,HTFTP); +extern GLOBALREF (HTProtocol,HTFile); +#else +GLOBALREF HTProtocol HTFTP, HTFile; +#endif /* GLOBALREF_IS_MACRO */ +#endif /* HTFILE_H */ + +/* end of HTFile */ diff --git a/WWW/Library/Implementation/HTFinger.c b/WWW/Library/Implementation/HTFinger.c new file mode 100644 index 00000000..3e37049c --- /dev/null +++ b/WWW/Library/Implementation/HTFinger.c @@ -0,0 +1,438 @@ +/* FINGER ACCESS HTFinger.c +** ============= +** Authors: +** ARB Andrew Brooks +** +** History: +** 21 Apr 94 First version (ARB, from HTNews.c by TBL) +** 12 Mar 96 Made the URL and command buffering secure from +** stack modifications, beautified the HTLoadFinger() +** and response() functions, and added support for the +** following URL formats for sending a "", "/w", +** "username[@host]", or "/w username[@host]" command +** to the server: +** finger://host +** finger://host/ +** finger://host/%2fw +** finger://host/%2fw%20username[@host] +** finger://host/w/username[@host] +** finger://host/username[@host] +** finger://host/username[@host]/w +** finger://username@host +** finger://username@host/ +** finger://username@host/w +** 15 Mar 96 Added support for port 79 gtype 0 gopher URLs +** relayed from HTLoadGopher. - FM +*/ + +#include "HTUtils.h" +#include "tcp.h" +#include "HTAlert.h" +#include "HTML.h" +#include "HTParse.h" +#include "HTFormat.h" +#include "HTTCP.h" +#include "HTString.h" +#include "HTFinger.h" + +#include "LYLeaks.h" + +/* #define TRACE 1 */ + +#define FINGER_PORT 79 /* See rfc742 */ +#define BIG 1024 /* Bug */ + +#define FREE(x) if (x) {free(x); x = NULL;} + +#define PUTC(c) (*targetClass.put_character)(target, c) +#define PUTS(s) (*targetClass.put_string)(target, s) +#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0) +#define END(e) (*targetClass.end_element)(target, e, 0) +#define FREE_TARGET (*targetClass._free)(target) +#define NEXT_CHAR HTGetCharacter() + + +/* Module-wide variables +*/ +PRIVATE int s; /* Socket for FingerHost */ + +struct _HTStructured { + CONST HTStructuredClass * isa; /* For gopher streams */ + /* ... */ +}; + +PRIVATE HTStructured * target; /* The output sink */ +PRIVATE HTStructuredClass targetClass; /* Copy of fn addresses */ + +/* Initialisation for this module +** ------------------------------ +*/ +PRIVATE BOOL initialized = NO; +PRIVATE BOOL initialize NOARGS +{ + s = -1; /* Disconnected */ + return YES; +} + + + +/* Start anchor element +** -------------------- +*/ +PRIVATE void start_anchor ARGS1(CONST char *, href) +{ + BOOL present[HTML_A_ATTRIBUTES]; + CONST char* value[HTML_A_ATTRIBUTES]; + + { + int i; + for(i=0; i<HTML_A_ATTRIBUTES; i++) + present[i] = (i==HTML_A_HREF); + } + ((CONST char **)value)[HTML_A_HREF] = href; + (*targetClass.start_element)(target, HTML_A, present, + (CONST char **)value, 0); + +} + +/* Send Finger Command line to remote host & Check Response +** -------------------------------------------------------- +** +** On entry, +** command points to the command to be sent, including CRLF, or is null +** pointer if no command to be sent. +** On exit, +** Negative status indicates transmission error, socket closed. +** Positive status is a Finger status. +*/ + + +PRIVATE int response ARGS5( + CONST char *, command, + char *, sitename, + HTParentAnchor *, anAnchor, + HTFormat, format_out, + HTStream*, sink) +{ + int status; + int length = strlen(command); + int ch, i; + char line[BIG], *l, *cmd=NULL; + char *p = line, *href=NULL; + extern int interrupted_in_htgetcharacter; + + if (length == 0) + return(-1); + + /* Set up buffering. + */ + HTInitInput(s); + + /* Send the command. + */ + if (TRACE) + fprintf(stderr, "HTFinger command to be sent: %s", command); + status = NETWRITE(s, (char *)command, length); + if (status < 0) { + if (TRACE) + fprintf(stderr, + "HTFinger: Unable to send command. Disconnecting.\n"); + NETCLOSE(s); + s = -1; + return status; + } /* if bad status */ + + /* Make a hypertext object with an anchor list. + */ + target = HTML_new(anAnchor, format_out, sink); + targetClass = *target->isa; /* Copy routine entry points */ + + /* Create the results report. + */ + if (TRACE) + fprintf(stderr,"HTFinger: Reading finger information\n"); + START(HTML_HTML); + PUTS("\n"); + START(HTML_HEAD); + PUTS("\n"); + START(HTML_TITLE); + PUTS("Finger server on "); + PUTS(sitename); + END(HTML_TITLE); + PUTS("\n"); + END(HTML_HEAD); + PUTS("\n"); + START(HTML_BODY); + PUTS("\n"); + START(HTML_H1); + PUTS("Finger server on "); + START(HTML_EM); + PUTS(sitename); + END(HTML_EM); + PUTS(": "); + if (command) { + StrAllocCopy(cmd, command); + } else { + StrAllocCopy(cmd, ""); + } + for (i = (strlen(cmd) - 1); i >= 0; i--) { + if (cmd[i] == LF || cmd[i] == CR) { + cmd[i] = '\0'; + } else { + break; + } + } + PUTS(cmd); + FREE(cmd); + END(HTML_H1); + PUTS("\n"); + START(HTML_PRE); + + while ((ch=NEXT_CHAR) != (char)EOF) { + + if (interrupted_in_htgetcharacter) { + if (TRACE) { + fprintf(stderr, + "HTFinger: Interrupted in HTGetCharacter, apparently.\n"); + } + _HTProgress ("Connection interrupted."); + goto end_html; + } + + if (ch != LF) { + *p = ch; /* Put character in line */ + if (p < &line[BIG-1]) { + p++; + } + } else { + *p = '\0'; /* Terminate line */ + /* + * OK we now have a line. + * Load it as 'l' and parse it. + */ + p = l = line; + while (*l) { + if (strncmp (l, "news:", 5) && + strncmp (l, "snews://", 8) && + strncmp (l, "nntp://", 7) && + strncmp (l, "ftp://", 6) && + strncmp (l, "file:/", 6) && + strncmp (l, "finger://", 9) && + strncmp (l, "http://", 7) && + strncmp (l, "https://", 8) && + strncmp (l, "wais://", 7) && + strncmp (l, "mailto:", 7) && + strncmp (l, "cso://", 6) && + strncmp (l, "gopher://", 9)) + PUTC(*l++); + else { + StrAllocCopy(href, l); + start_anchor(strtok(href, " \r\n\t,>)\"")); + while (*l && !strchr(" \r\n\t,>)\"", *l)) + PUTC(*l++); + END(HTML_A); + FREE(href); + } + } + PUTC('\n'); + } + } + NETCLOSE(s); + s = -1; + +end_html: + END(HTML_PRE); + PUTS("\n"); + END(HTML_BODY); + PUTS("\n"); + END(HTML_HTML); + PUTS("\n"); + FREE_TARGET; + return(0); +} + + +/* Load by name HTLoadFinger +** ============ +*/ +PUBLIC int HTLoadFinger ARGS4( + CONST char *, arg, + HTParentAnchor *, anAnchor, + HTFormat, format_out, + HTStream*, stream) +{ + char *username, *sitename, *colon; /* Fields extracted from URL */ + char *slash, *at_sign; /* Fields extracted from URL */ + char *command, *str; /* Buffers */ + int port; /* Port number from URL */ + int status; /* tcp return */ + + if (TRACE) { + fprintf(stderr, "HTFinger: Looking for %s\n", (arg ? arg : "NULL")); + } + + if (!(arg && *arg)) { + HTAlert("Could not load data."); + return HT_NOT_LOADED; /* Ignore if no name */ + } + + if (!initialized) + initialized = initialize(); + if (!initialized) { + HTAlert ("Could not set up finger connection."); + return HT_NOT_LOADED; /* FAIL */ + } + + { + CONST char * p1=arg; + BOOL IsGopherURL = FALSE; + + /* Set up the host and command fields. + */ + if (!strncasecomp(arg, "finger://", 9)) { + p1 = arg + 9; /* Skip "finger://" prefix */ + } else if (!strncasecomp(arg, "gopher://", 9)) { + p1 = arg + 9; /* Skip "gopher://" prefix */ + IsGopherURL = TRUE; + } + sitename = (char *)p1; + + if ((slash = strchr(sitename, '/')) != NULL) { + *slash++ = '\0'; + HTUnEscape(slash); + if (IsGopherURL) { + if (*slash != '0') { + HTAlert("Could not load data."); + return HT_NOT_LOADED; /* FAIL */ + } + *slash++ = '\0'; + } + } + if ((at_sign = strchr(sitename, '@')) != NULL) { + if (IsGopherURL) { + HTAlert("Could not load data."); + return HT_NOT_LOADED; /* FAIL */ + } + *at_sign++ = '\0'; + username = sitename; + sitename = at_sign; + HTUnEscape(username); + } else if (slash) { + username = slash; + } else { + username = ""; + } + + if (*sitename == '\0') { + HTAlert("Could not load data (no sitename in finger URL)"); + return HT_NOT_LOADED; /* Ignore if no name */ + } + + if ((colon = strchr(sitename, ':')) != NULL) { + *colon++ = '\0'; + port = atoi(colon); + if (port != 79) { + HTAlert("Invalid port number - will only use port 79!"); + return HT_NOT_LOADED; /* Ignore if wrong port */ + } + } + + /* Load the string for making a connection/ + */ + str = (char *)calloc(1, (strlen(sitename) + 10)); + if (str == NULL) + outofmem(__FILE__, "HTLoadFinger"); + sprintf(str, "lose://%s/", sitename); + + /* Load the command for the finger server. + */ + command = (char *)calloc(1, (strlen(username) + 10)); + if (command == NULL) + outofmem(__FILE__, "HTLoadFinger"); + if (at_sign && slash) { + if (*slash == 'w' || *slash == 'W') { + sprintf(command, "/w %s%c%c", username, CR, LF); + } else { + sprintf(command, "%s%c%c", username, CR, LF); + } + } else if (at_sign) { + sprintf(command, "%s%c%c", username, CR, LF); + } else if (*username == '/') { + if ((slash = strchr((username+1), '/')) != NULL) { + *slash = ' '; + } + sprintf(command, "%s%c%c", username, CR, LF); + } else if ((*username == 'w' || *username == 'W') && + *(username+1) == '/') { + if (*username+2 != '\0') { + *(username+1) = ' '; + } else { + *(username+1) = '\0'; + } + sprintf(command, "/%s%c%c", username, CR, LF); + } else if ((*username == 'w' || *username == 'W') && + *(username+1) == '\0') { + sprintf(command, "/%s%c%c", username, CR, LF); + } else if ((slash = strchr(username, '/')) != NULL) { + *slash++ = '\0'; + if (*slash == 'w' || *slash == 'W') { + sprintf(command, "/w %s%c%c", username, CR, LF); + } else { + sprintf(command, "%s%c%c", username, CR, LF); + } + } else { + sprintf(command, "%s%c%c", username, CR, LF); + } + } /* scope of p1 */ + + /* Now, let's get a stream setup up from the FingerHost: + ** CONNECTING to finger host + */ + if (TRACE) + fprintf(stderr, "HTFinger: doing HTDoConnect on '%s'\n", str); + status = HTDoConnect(str, "finger", FINGER_PORT, &s); + if (TRACE) + fprintf(stderr, "HTFinger: Done DoConnect; status %d\n", status); + + if (status == HT_INTERRUPTED) { + /* Interrupt cleanly */ + if (TRACE) + fprintf(stderr, + "HTFinger: Interrupted on connect; recovering cleanly.\n"); + HTProgress ("Connection interrupted."); + FREE(str); + FREE(command); + return HT_INTERRUPTED; + } + if (status < 0) { + NETCLOSE(s); + s = -1; + if (TRACE) + fprintf(stderr, "HTFinger: Unable to connect to finger host.\n"); + HTAlert("Could not access finger host."); + FREE(str); + FREE(command); + return HT_NOT_LOADED; /* FAIL */ + } + if (TRACE) + fprintf(stderr, "HTFinger: Connected to finger host '%s'.\n", str); + FREE(str); + + /* Send the command, and process response if successful. + */ + if (response(command, sitename, anAnchor, format_out, stream) != 0) { + HTAlert("No response from finger server."); + FREE(command); + return HT_NOT_LOADED; + } + + FREE(command); + return HT_LOADED; +} + +#ifdef GLOBALDEF_IS_MACRO +#define _HTFINGER_C_1_INIT { "finger", HTLoadFinger, NULL } +GLOBALDEF (HTProtocol, HTFinger, _HTFINGER_C_1_INIT); +#else +GLOBALDEF PUBLIC HTProtocol HTFinger = { "finger", HTLoadFinger, NULL }; +#endif /* GLOBALDEF_IS_MACRO */ diff --git a/WWW/Library/Implementation/HTFinger.h b/WWW/Library/Implementation/HTFinger.h new file mode 100644 index 00000000..98d98070 --- /dev/null +++ b/WWW/Library/Implementation/HTFinger.h @@ -0,0 +1,24 @@ +/* Finger protocol module for the WWW library */ +/* History: +** 21 Apr 94 Andrew Brooks +*/ + +#ifndef HTFINGER_H +#define HTFINGER_H + +#include "HTAccess.h" +#include "HTAnchor.h" + +#ifdef GLOBALREF_IS_MACRO +extern GLOBALREF (HTProtocol, HTFinger); +#else +GLOBALREF HTProtocol HTFinger; +#endif /* GLOBALREF_IS_MACRO */ + +extern int HTLoadFinger PARAMS(( + CONST char * arg, + HTParentAnchor * anAnchor, + HTFormat format_out, + HTStream * stream)); + +#endif /* HTFINGER_H */ diff --git a/WWW/Library/Implementation/HTFormat.c b/WWW/Library/Implementation/HTFormat.c new file mode 100644 index 00000000..f25d36b3 --- /dev/null +++ b/WWW/Library/Implementation/HTFormat.c @@ -0,0 +1,836 @@ + +/* Manage different file formats HTFormat.c +** ============================= +** +** Bugs: +** Not reentrant. +** +** Assumes the incoming stream is ASCII, rather than a local file +** format, and so ALWAYS converts from ASCII on non-ASCII machines. +** Therefore, non-ASCII machines can't read local files. +** +*/ + + +#include "HTUtils.h" +#include "tcp.h" + +/* Implements: +*/ +#include "HTFormat.h" + +PUBLIC float HTMaxSecs = 1e10; /* No effective limit */ +PUBLIC float HTMaxLength = 1e10; /* No effective limit */ +PUBLIC long int HTMaxBytes = 0; /* No effective limit */ + +PUBLIC int loading_length= -1; + +#ifdef unix +#ifdef NeXT +#define PRESENT_POSTSCRIPT "open %s; /bin/rm -f %s\n" +#else +#define PRESENT_POSTSCRIPT "(ghostview %s ; /bin/rm -f %s)&\n" + /* Full pathname would be better! */ +#endif +#endif + + +#include "HTML.h" +#include "HTMLDTD.h" +#include "HText.h" +#include "HTAlert.h" +#include "HTList.h" +#include "HTInit.h" +#include "HTTCP.h" +/* Streams and structured streams which we use: +*/ +#include "HTFWriter.h" +#include "HTPlain.h" +#include "SGML.h" +#include "HTML.h" +#include "HTMLGen.h" + +#include "LYexit.h" +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +extern int HTCheckForInterrupt NOPARAMS; + +PUBLIC BOOL HTOutputSource = NO; /* Flag: shortcut parser to stdout */ +/* extern BOOL interactive; LJM */ + +#ifdef ORIGINAL +struct _HTStream { + CONST HTStreamClass* isa; + /* ... */ +}; +#endif + +/* this version used by the NetToText stream */ +struct _HTStream { + CONST HTStreamClass * isa; + BOOL had_cr; + HTStream * sink; +}; + + +/* Presentation methods +** -------------------- +*/ + +PUBLIC HTList * HTPresentations = NULL; +PUBLIC HTPresentation * default_presentation = NULL; + +/* + * To free off the presentation list. + */ +PRIVATE void HTFreePresentations NOPARAMS; + +/* Define a presentation system command for a content-type +** ------------------------------------------------------- +*/ +PUBLIC void HTSetPresentation ARGS6( + CONST char *, representation, + CONST char *, command, + float, quality, + float, secs, + float, secs_per_byte, + long int, maxbytes) +{ + HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation)); + if (pres == NULL) + outofmem(__FILE__, "HTSetPresentation"); + + pres->rep = HTAtom_for(representation); + pres->rep_out = WWW_PRESENT; /* Fixed for now ... :-) */ + pres->converter = HTSaveAndExecute; /* Fixed for now ... */ + pres->quality = quality; + pres->secs = secs; + pres->secs_per_byte = secs_per_byte; + pres->maxbytes = maxbytes; + pres->command = NULL; + StrAllocCopy(pres->command, command); + + /* + * Memory leak fixed. + * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe + */ + if (!HTPresentations) { + HTPresentations = HTList_new(); + atexit(HTFreePresentations); + } + + if (strcmp(representation, "*")==0) { + FREE(default_presentation); + default_presentation = pres; + } else { + HTList_addObject(HTPresentations, pres); + } +} + + +/* Define a built-in function for a content-type +** --------------------------------------------- +*/ +PUBLIC void HTSetConversion ARGS7( + CONST char *, representation_in, + CONST char *, representation_out, + HTConverter*, converter, + float, quality, + float, secs, + float, secs_per_byte, + long int, maxbytes) +{ + HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation)); + if (pres == NULL) + outofmem(__FILE__, "HTSetConversion"); + + pres->rep = HTAtom_for(representation_in); + pres->rep_out = HTAtom_for(representation_out); + pres->converter = converter; + pres->command = NULL; /* Fixed */ + pres->quality = quality; + pres->secs = secs; + pres->secs_per_byte = secs_per_byte; + pres->maxbytes = maxbytes; + pres->command = NULL; + + /* + * Memory Leak fixed. + * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe + */ + if (!HTPresentations) { + HTPresentations = HTList_new(); + atexit(HTFreePresentations); + } + + HTList_addObject(HTPresentations, pres); +} + +/* +** Purpose: Free the presentation list. +** Arguments: void +** Return Value: void +** Remarks/Portability/Dependencies/Restrictions: +** Made to clean up Lynx's bad leakage. +** Revision History: +** 05-28-94 created Lynx 2-3-1 Garrett Arch Blythe +*/ +PRIVATE void HTFreePresentations NOARGS +{ + HTPresentation * pres = NULL; + + /* + * Loop through the list. + */ + while (!HTList_isEmpty(HTPresentations)) { + /* + * Free off each item. + * May also need to free off it's items, but not sure + * as of yet. + */ + pres = (HTPresentation *)HTList_removeLastObject(HTPresentations); + FREE(pres->command); + FREE(pres); + } + /* + * Free the list itself. + */ + HTList_delete(HTPresentations); + HTPresentations = NULL; +} + + +/* File buffering +** -------------- +** +** The input file is read using the macro which can read from +** a socket or a file. +** The input buffer size, if large will give greater efficiency and +** release the server faster, and if small will save space on PCs etc. +*/ +#define INPUT_BUFFER_SIZE 4096 /* Tradeoff */ +PRIVATE char input_buffer[INPUT_BUFFER_SIZE]; +PRIVATE char * input_pointer; +PRIVATE char * input_limit; +PRIVATE int input_file_number; + + +/* Set up the buffering +** +** These routines are public because they are in fact needed by +** many parsers, and on PCs and Macs we should not duplicate +** the static buffer area. +*/ +PUBLIC void HTInitInput ARGS1 (int,file_number) +{ + input_file_number = file_number; + input_pointer = input_limit = input_buffer; +} + +PUBLIC int interrupted_in_htgetcharacter = 0; +PUBLIC char HTGetCharacter NOARGS +{ + char ch; + interrupted_in_htgetcharacter = 0; + do { + if (input_pointer >= input_limit) { + int status = NETREAD( + input_file_number, input_buffer, INPUT_BUFFER_SIZE); + if (status <= 0) { + if (status == 0) return (char)EOF; + if (status == HT_INTERRUPTED) + { + if (TRACE) + fprintf (stderr, + "HTFormat: Interrupted in HTGetCharacter\n"); + interrupted_in_htgetcharacter = 1; + return (char)EOF; + } + if (TRACE) fprintf(stderr, + "HTFormat: File read error %d\n", status); + return (char)EOF; /* -1 is returned by UCX at end of HTTP link */ + } + input_pointer = input_buffer; + input_limit = input_buffer + status; + } + ch = *input_pointer++; + } while (ch == (char) 13); /* Ignore ASCII carriage return */ + + return FROMASCII(ch); +} + +/* Stream the data to an ouput file as binary +*/ +PUBLIC int HTOutputBinary ARGS2( int, input, + FILE *, output) +{ + do { + int status = NETREAD( + input, input_buffer, INPUT_BUFFER_SIZE); + if (status <= 0) { + if (status == 0) return 0; + if (TRACE) fprintf(stderr, + "HTFormat: File read error %d\n", status); + return 2; /* Error */ + } + fwrite(input_buffer, sizeof(char), status, output); + } while (YES); +} + +/* Match maintype to any MIME type starting with maintype, + * for example: image/gif should match image + */ +PRIVATE int half_match ARGS2(char *,trial_type, char *,target) +{ + char *cp=strchr(trial_type,'/'); + + /* if no '/' or no '*' */ + if(!cp || *(cp+1) != '*') + return 0; + + if(TRACE) + fprintf(stderr,"HTFormat: comparing %s and %s for half match\n", + trial_type, target); + + /* main type matches */ + if(!strncmp(trial_type, target, (cp-trial_type)-1)) + return 1; + + return 0; +} + + +/* Create a filter stack +** --------------------- +** +** If a wildcard match is made, a temporary HTPresentation +** structure is made to hold the destination format while the +** new stack is generated. This is just to pass the out format to +** MIME so far. Storing the format of a stream in the stream might +** be a lot neater. +** +*/ +PUBLIC HTStream * HTStreamStack ARGS4( + HTFormat, rep_in, + HTFormat, rep_out, + HTStream*, sink, + HTParentAnchor*, anchor) +{ + HTAtom * wildcard = HTAtom_for("*"); + + if (TRACE) fprintf(stderr, + "HTFormat: Constructing stream stack for %s to %s\n", + HTAtom_name(rep_in), + HTAtom_name(rep_out)); + + /* don't return on WWW_SOURCE some people might like + * to make use of the source!!!! LJM + */ + /* if (rep_out == WWW_SOURCE || + rep_out == rep_in) return sink; LJM */ + + if(rep_out == rep_in) return sink; + + /* don't do anymore do it in the Lynx code at startup LJM */ + /* if (!HTPresentations) HTFormatInit(); */ /* set up the list */ + + { + int n = HTList_count(HTPresentations); + int i; + HTPresentation * pres, *match, + *strong_wildcard_match=0, + *weak_wildcard_match=0, + *last_default_match=0, + *strong_subtype_wildcard_match=0; + + for(i=0; i<n; i++) { + pres = (HTPresentation *)HTList_objectAt(HTPresentations, i); + if (pres->rep == rep_in) { + if (pres->rep_out == rep_out) { + if(TRACE) + fprintf(stderr,"StreamStack: found exact match: %s\n",HTAtom_name(pres->rep)); + return (*pres->converter)(pres, anchor, sink); + + } else if (pres->rep_out == wildcard) { + if(!strong_wildcard_match) + strong_wildcard_match = pres; + /* otherwise use the first one */ + if(TRACE) + fprintf(stderr,"StreamStack: found strong wildcard match: %s\n",HTAtom_name(pres->rep)); + } + + } else if(half_match(HTAtom_name(pres->rep), + HTAtom_name(rep_in))) { + + if (pres->rep_out == rep_out) { + if(!strong_subtype_wildcard_match) + strong_subtype_wildcard_match = pres; + /* otherwise use the first one */ + if(TRACE) + fprintf(stderr,"StreamStack: found strong subtype wildcard match: %s\n",HTAtom_name(pres->rep)); + } + } + + if (pres->rep == WWW_SOURCE) { + if(pres->rep_out == rep_out) { + if(!weak_wildcard_match) + weak_wildcard_match = pres; + /* otherwise use the first one */ + if(TRACE) + fprintf(stderr,"StreamStack: found weak wildcard match: %s\n",HTAtom_name(pres->rep_out)); + + } + if(pres->rep_out == wildcard) { + if(!last_default_match) + last_default_match = pres; + /* otherwise use the first one */ + } + } + } + + match = strong_subtype_wildcard_match ? strong_subtype_wildcard_match : + strong_wildcard_match ? strong_wildcard_match : + weak_wildcard_match ? weak_wildcard_match : + last_default_match; + + if (match) { + HTPresentation temp; + temp = *match; /* Specific instance */ + temp.rep = rep_in; /* yuk */ + temp.rep_out = rep_out; /* yuk */ + if(TRACE) + fprintf(stderr,"StreamStack: Using %s\n",HTAtom_name(temp.rep_out)); + return (*match->converter)(&temp, anchor, sink); + } + } + + return NULL; +} + + +/* Find the cost of a filter stack +** ------------------------------- +** +** Must return the cost of the same stack which StreamStack would set up. +** +** On entry, +** length The size of the data to be converted +*/ +PUBLIC float HTStackValue ARGS4( + HTFormat, rep_in, + HTFormat, rep_out, + float, initial_value, + long int, length) +{ + HTAtom * wildcard = HTAtom_for("*"); + + if (TRACE) fprintf(stderr, + "HTFormat: Evaluating stream stack for %s worth %.3f to %s\n", + HTAtom_name(rep_in), initial_value, + HTAtom_name(rep_out)); + + if (rep_out == WWW_SOURCE || + rep_out == rep_in) return 0.0; + + /* don't do anymore do it in the Lynx code at startup LJM */ + /* if (!HTPresentations) HTFormatInit(); */ /* set up the list */ + + { + int n = HTList_count(HTPresentations); + int i; + HTPresentation * pres; + for(i=0; i<n; i++) { + pres = (HTPresentation *)HTList_objectAt(HTPresentations, i); + if (pres->rep == rep_in && ( + pres->rep_out == rep_out || + pres->rep_out == wildcard)) { + float value = initial_value * pres->quality; + if (HTMaxSecs != 0.0) + value = value - (length*pres->secs_per_byte + pres->secs) + /HTMaxSecs; + return value; + } + } + } + + return -1e30; /* Really bad */ + +} + + +/* Push data from a socket down a stream +** ------------------------------------- +** +** This routine is responsible for creating and PRESENTING any +** graphic (or other) objects described by the file. +** +** The file number given is assumed to be a TELNET stream ie containing +** CRLF at the end of lines which need to be stripped to LF for unix +** when the format is textual. +** +*/ + +PUBLIC int HTCopy ARGS3( + int, file_number, + void*, handle, + HTStream*, sink) +{ + HTStreamClass targetClass; + char line[256]; + int bytes=0; + int rv = 0; + char * msg; + + if (loading_length == -1) + msg = "Read %d bytes of data."; + else + /* We have a loading_length. */ + msg = "Read %d of %d bytes of data."; + + +/* Push the data down the stream +** +*/ + targetClass = *(sink->isa); /* Copy pointers to procedures */ + + /* Push binary from socket down sink + ** + ** This operation could be put into a main event loop + */ + for(;;) { + int status; + extern char LYCancelDownload; + + if (LYCancelDownload) { + LYCancelDownload = FALSE; + (*targetClass._abort)(sink, NULL); + rv = -1; + goto finished; + } + + if (HTCheckForInterrupt()) + { + _HTProgress ("Data transfer interrupted."); + (*targetClass._abort)(sink, NULL); + if(bytes) + rv = HT_INTERRUPTED; + else + rv = -1; + goto finished; + } + + + status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE); + + if (status <= 0) { + if (status == 0) + break; + else if (status == HT_INTERRUPTED) + { + _HTProgress ("Data transfer interrupted."); + (*targetClass._abort)(sink, NULL); + if(bytes) + rv = HT_INTERRUPTED; + else + rv = -1; + goto finished; + } + else if (SOCKET_ERRNO == ENOTCONN || SOCKET_ERRNO == ECONNRESET + || SOCKET_ERRNO == EPIPE) + { + /* Arrrrgh, HTTP 0/1 compability problem, maybe. */ + rv = -2; + goto finished; + } + break; + } + +#ifdef NOT_ASCII + { + char * p; + for(p = input_buffer; p < input_buffer+status; p++) { + *p = FROMASCII(*p); + } + } +#endif + + (*targetClass.put_block)(sink, input_buffer, status); + + bytes += status; + sprintf(line, msg, bytes, loading_length); + HTProgress(line); + + } /* next bufferload */ + + _HTProgress("Data transfer complete"); + NETCLOSE(file_number); + rv = HT_LOADED; + +finished: + loading_length = -1; + return(rv); + +} + + + +/* Push data from a file pointer down a stream +** ------------------------------------- +** +** This routine is responsible for creating and PRESENTING any +** graphic (or other) objects described by the file. +** +** +*/ +PUBLIC void HTFileCopy ARGS2( + FILE *, fp, + HTStream*, sink) +{ + HTStreamClass targetClass; + +/* Push the data down the stream +** +*/ + targetClass = *(sink->isa); /* Copy pointers to procedures */ + + /* Push binary from socket down sink + */ + for(;;) { + int status = fread( + input_buffer, 1, INPUT_BUFFER_SIZE, fp); + if (status == 0) { /* EOF or error */ + if (ferror(fp) == 0) break; + if (TRACE) fprintf(stderr, + "HTFormat: Read error, read returns %d\n", ferror(fp)); + break; + } + (*targetClass.put_block)(sink, input_buffer, status); + } /* next bufferload */ + +} + + + + +/* Push data from a socket down a stream STRIPPING CR +** -------------------------------------------------- +** +** This routine is responsible for creating and PRESENTING any +** graphic (or other) objects described by the socket. +** +** The file number given is assumed to be a TELNET stream ie containing +** CRLF at the end of lines which need to be stripped to LF for unix +** when the format is textual. +** +*/ +PUBLIC void HTCopyNoCR ARGS2( + int, file_number, + HTStream*, sink) +{ + HTStreamClass targetClass; + +/* Push the data, ignoring CRLF, down the stream +** +*/ + targetClass = *(sink->isa); /* Copy pointers to procedures */ + +/* Push text from telnet socket down sink +** +** @@@@@ To push strings could be faster? (especially is we +** cheat and don't ignore CR! :-} +*/ + HTInitInput(file_number); + for(;;) { + char character; + character = HTGetCharacter(); + if (character == (char)EOF) break; + (*targetClass.put_character)(sink, character); + } +} + + + +/* Parse a socket given format and file number +** +** This routine is responsible for creating and PRESENTING any +** graphic (or other) objects described by the file. +** +** The file number given is assumed to be a TELNET stream ie containing +** CRLF at the end of lines which need to be stripped to LF for unix +** when the format is textual. +** +*/ +PUBLIC int HTParseSocket ARGS5( + HTFormat, rep_in, + HTFormat, format_out, + HTParentAnchor *, anchor, + int, file_number, + HTStream*, sink) +{ + HTStream * stream; + HTStreamClass targetClass; + int rv; + extern char LYCancelDownload; + + stream = HTStreamStack(rep_in, + format_out, + sink , anchor); + + if (!stream) { + char buffer[1024]; /* @@@@@@@@ */ + if (LYCancelDownload) { + LYCancelDownload = FALSE; + return -1; + } + sprintf(buffer, "Sorry, can't convert from %s to %s.", + HTAtom_name(rep_in), HTAtom_name(format_out)); + if (TRACE) fprintf(stderr, "HTFormat: %s\n", buffer); + return HTLoadError(sink, 501, buffer); /* returns -501 */ + } + +/* +** Push the data, don't worry about CRLF we can strip them later. +*/ + targetClass = *(stream->isa); /* Copy pointers to procedures */ + rv = HTCopy(file_number, NULL, stream); + if (rv != -1 && rv != HT_INTERRUPTED) + (*targetClass._free)(stream); + + return rv; /* full: HT_LOADED; partial: HT_INTERRUPTED; no bytes: -1 */ +} + + + +/* Parse a file given format and file pointer +** +** This routine is responsible for creating and PRESENTING any +** graphic (or other) objects described by the file. +** +** The file number given is assumed to be a TELNET stream ie containing +** CRLF at the end of lines which need to be stripped to \n for unix +** when the format is textual. +** +*/ +PUBLIC int HTParseFile ARGS5( + HTFormat, rep_in, + HTFormat, format_out, + HTParentAnchor *, anchor, + FILE *, fp, + HTStream*, sink) +{ + HTStream * stream; + HTStreamClass targetClass; + + stream = HTStreamStack(rep_in, + format_out, + sink , anchor); + + if (!stream) { + char buffer[1024]; /* @@@@@@@@ */ + extern char LYCancelDownload; + if (LYCancelDownload) { + LYCancelDownload = FALSE; + return -1; + } + sprintf(buffer, "Sorry, can't convert from %s to %s.", + HTAtom_name(rep_in), HTAtom_name(format_out)); + if (TRACE) fprintf(stderr, "HTFormat(in HTParseFile): %s\n", buffer); + return HTLoadError(sink, 501, buffer); + } + +/* Push the data down the stream +** +** +** @@ Bug: This decision ought to be made based on "encoding" +** rather than on content-type. @@@ When we handle encoding. +** The current method smells anyway. +*/ + targetClass = *(stream->isa); /* Copy pointers to procedures */ + HTFileCopy(fp, stream); + (*targetClass._free)(stream); + + return HT_LOADED; +} + + +/* Converter stream: Network Telnet to internal character text +** ----------------------------------------------------------- +** +** The input is assumed to be in ASCII, with lines delimited +** by (13,10) pairs, These pairs are converted into (CR,LF) +** pairs in the local representation. The (CR,LF) sequence +** when found is changed to a '\n' character, the internal +** C representation of a new line. +*/ + + +PRIVATE void NetToText_put_character ARGS2(HTStream *, me, char, net_char) +{ + char c = FROMASCII(net_char); + if (me->had_cr) { + if (c==LF) { + me->sink->isa->put_character(me->sink, '\n'); /* Newline */ + me->had_cr = NO; + return; + } else { + me->sink->isa->put_character(me->sink, CR); /* leftover */ + } + } + me->had_cr = (c==CR); + if (!me->had_cr) + me->sink->isa->put_character(me->sink, c); /* normal */ +} + +PRIVATE void NetToText_put_string ARGS2(HTStream *, me, CONST char *, s) +{ + CONST char * p; + + for (p=s; *p; p++) + NetToText_put_character(me, *p); +} + +PRIVATE void NetToText_put_block ARGS3(HTStream *, me, CONST char*, s, int, l) +{ + CONST char * p; + for(p=s; p<(s+l); p++) NetToText_put_character(me, *p); +} + +PRIVATE void NetToText_free ARGS1(HTStream *, me) +{ + (me->sink->isa->_free)(me->sink); /* Close rest of pipe */ + FREE(me); +} + +PRIVATE void NetToText_abort ARGS2(HTStream *, me, HTError, e) +{ + me->sink->isa->_abort(me->sink,e); /* Abort rest of pipe */ + FREE(me); +} + +/* The class structure +*/ +PRIVATE HTStreamClass NetToTextClass = { + "NetToText", + NetToText_free, + NetToText_abort, + NetToText_put_character, + NetToText_put_string, + NetToText_put_block +}; + +/* The creation method +*/ +PUBLIC HTStream * HTNetToText ARGS1(HTStream *, sink) +{ + HTStream* me = (HTStream*)malloc(sizeof(*me)); + if (me == NULL) + outofmem(__FILE__, "NetToText"); + me->isa = &NetToTextClass; + + me->had_cr = NO; + me->sink = sink; + return me; +} + diff --git a/WWW/Library/Implementation/HTFormat.h b/WWW/Library/Implementation/HTFormat.h new file mode 100644 index 00000000..5b513fbb --- /dev/null +++ b/WWW/Library/Implementation/HTFormat.h @@ -0,0 +1,394 @@ +/* HTFormat: The format manager in the WWW Library + MANAGE DIFFERENT DOCUMENT FORMATS + + Here we describe the functions of the HTFormat module which handles conversion between + different data representations. (In MIME parlance, a representation is known as a + content-type. In WWW the term "format" is often used as it is shorter). + + This module is implemented by HTFormat.c . This hypertext document is used to generate + the HTFormat.h include file. Part of the WWW library . + +Preamble + + */ +#ifndef HTFORMAT_H +#define HTFORMAT_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTStream.h" +#include "HTAtom.h" +#include "HTList.h" + +#ifdef SHORT_NAMES +#define HTOutputSource HTOuSour +#define HTOutputBinary HTOuBina +#endif + +/* + +The HTFormat type + + We use the HTAtom object for holding representations. This allows faster manipulation + (comparison and copying) that if we stayed with strings. + + */ +typedef HTAtom * HTFormat; + +/* + + These macros (which used to be constants) define some basic internally referenced + representations. The www/xxx ones are of course not MIME standard. + + www/source is an output format which leaves the input untouched. It is useful for + diagnostics, and for users who want to see the original, whatever it is. + + */ + /* Internal ones */ +#define WWW_SOURCE HTAtom_for("www/source") /* Whatever it was originally*/ + +/* + + www/present represents the user's perception of the document. If you convert to + www/present, you present the material to the user. + + */ +#define WWW_PRESENT HTAtom_for("www/present") /* The user's perception */ + +/* + + The message/rfc822 format means a MIME message or a plain text message with no MIME + header. This is what is returned by an HTTP server. + + */ +#define WWW_MIME HTAtom_for("www/mime") /* A MIME message */ + +/* + + www/print is like www/present except it represents a printed copy. + + */ +#define WWW_PRINT HTAtom_for("www/print") /* A printed copy */ + +/* + + www/unknown is a really unknown type. Some default action is appropriate. + + */ +#define WWW_UNKNOWN HTAtom_for("www/unknown") + +#ifdef DIRED_SUPPORT +/* + www/dired signals directory edit mode. +*/ +#define WWW_DIRED HTAtom_for("www/dired") +#endif + +/* + + These are regular MIME types. HTML is assumed to be added by the W3 code. + application/octet-stream was mistakenly application/binary in earlier libwww versions + (pre 2.11). + + */ +#define WWW_PLAINTEXT HTAtom_for("text/plain") +#define WWW_POSTSCRIPT HTAtom_for("application/postscript") +#define WWW_RICHTEXT HTAtom_for("application/rtf") +#define WWW_AUDIO HTAtom_for("audio/basic") +#define WWW_HTML HTAtom_for("text/html") +#define WWW_BINARY HTAtom_for("application/octet-stream") + +/* + + We must include the following file after defining HTFormat, to which it makes + reference. + +The HTEncoding type + + */ +typedef HTAtom* HTEncoding; + +/* + + The following are values for the MIME types: + + */ +#define WWW_ENC_7BIT HTAtom_for("7bit") +#define WWW_ENC_8BIT HTAtom_for("8bit") +#define WWW_ENC_BINARY HTAtom_for("binary") + +/* + + We also add + + */ +#define WWW_ENC_COMPRESS HTAtom_for("compress") + +#include "HTAnchor.h" + +/* + +The HTPresentation and HTConverter types + + This HTPresentation structure represents a possible conversion algorithm from one + format to annother. It includes a pointer to a conversion routine. The conversion + routine returns a stream to which data should be fed. See also HTStreamStack which + scans the list of registered converters and calls one. See the initialisation module + for a list of conversion routines. + + */ +typedef struct _HTPresentation HTPresentation; + +typedef HTStream * HTConverter PARAMS(( + HTPresentation * pres, + HTParentAnchor * anchor, + HTStream * sink)); + +struct _HTPresentation { + HTAtom * rep; /* representation name atmoized */ + HTAtom * rep_out; /* resulting representation */ + HTConverter * converter; /* The routine to gen the stream stack */ + char * command; /* MIME-format string */ + float quality; /* Between 0 (bad) and 1 (good) */ + float secs; + float secs_per_byte; + long int maxbytes; +}; + +/* + + The list of presentations is kept by this module. It is also scanned by modules which + want to know the set of formats supported. for example. + + */ +extern HTList * HTPresentations; + +/* + + The default presentation is used when no other is appriporate + + */ +extern HTPresentation* default_presentation; + +/* + +HTSetPresentation: Register a system command to present a format + + ON ENTRY, + + rep is the MIME - style format name + + command is the MAILCAP - style command template + + quality A degradation faction 0..1.0 + + secs A limit on the time user will wait (0.0 for infinity) + secs_per_byte + + maxbytes A limit on the length acceptable as input (0 infinite) + + */ +extern void HTSetPresentation PARAMS(( + CONST char * representation, + CONST char * command, + float quality, + float secs, + float secs_per_byte, + long int maxbytes +)); + + +/* + +HTSetConversion: Register a converstion routine + + ON ENTRY, + + rep_in is the content-type input + + rep_out is the resulting content-type + + converter is the routine to make the stream to do it + + */ + +extern void HTSetConversion PARAMS(( + CONST char * rep_in, + CONST char * rep_out, + HTConverter * converter, + float quality, + float secs, + float secs_per_byte, + long int maxbytes +)); + + +/* + +HTStreamStack: Create a stack of streams + + This is the routine which actually sets up the conversion. It currently checks only for + direct conversions, but multi-stage conversions are forseen. It takes a stream into + which the output should be sent in the final format, builds the conversion stack, and + returns a stream into which the data in the input format should be fed. The anchor is + passed because hypertxet objects load information into the anchor object which + represents them. + + */ +extern HTStream * HTStreamStack PARAMS(( + HTFormat format_in, + HTFormat format_out, + HTStream* stream_out, + HTParentAnchor* anchor)); + +/* + +HTStackValue: Find the cost of a filter stack + + Must return the cost of the same stack which HTStreamStack would set up. + + ON ENTRY, + + format_in The fomat of the data to be converted + + format_out The format required + + initial_value The intrinsic "value" of the data before conversion on a scale + from 0 to 1 + + length The number of bytes expected in the input format + + */ +extern float HTStackValue PARAMS(( + HTFormat format_in, + HTFormat rep_out, + float initial_value, + long int length)); + +#define NO_VALUE_FOUND -1e20 /* returned if none found */ + +/* + +HTCopy: Copy a socket to a stream + + This is used by the protocol engines to send data down a stream, typically one which + has been generated by HTStreamStack. + + */ +extern int HTCopy PARAMS(( + int file_number, + void* handle, + HTStream* sink)); + + +/* + +HTFileCopy: Copy a file to a stream + + This is used by the protocol engines to send data down a stream, typically one which + has been generated by HTStreamStack. It is currently called by HTParseFile + + */ +extern void HTFileCopy PARAMS(( + FILE* fp, + HTStream* sink)); + + +/* + +HTCopyNoCR: Copy a socket to a stream, stripping CR characters. + + It is slower than HTCopy . + + */ + +extern void HTCopyNoCR PARAMS(( + int file_number, + HTStream* sink)); + + +/* + +Clear input buffer and set file number + + This routine and the one below provide simple character input from sockets. (They are + left over from the older architecure and may not be used very much.) The existence of + a common routine and buffer saves memory space in small implementations. + + */ +extern void HTInitInput PARAMS((int file_number)); + +/* + +Get next character from buffer + + */ +extern char HTGetCharacter NOPARAMS; + + +/* + +HTParseSocket: Parse a socket given its format + + This routine is called by protocol modules to load an object. uses HTStreamStack and + the copy routines above. Returns HT_LOADED if succesful, <0 if not. + + */ +extern int HTParseSocket PARAMS(( + HTFormat format_in, + HTFormat format_out, + HTParentAnchor *anchor, + int file_number, + HTStream* sink)); + +/* + +HTParseFile: Parse a File through a file pointer + + This routine is called by protocols modules to load an object. uses HTStreamStack and + HTFileCopy . Returns HT_LOADED if succesful, <0 if not. + + */ +extern int HTParseFile PARAMS(( + HTFormat format_in, + HTFormat format_out, + HTParentAnchor *anchor, + FILE *fp, + HTStream* sink)); + +/* + +HTNetToText: Convert Net ASCII to local representation + + This is a filter stream suitable for taking text from a socket and passing it into a + stream which expects text in the local C representation. It does ASCII and newline + conversion. As usual, pass its output stream to it when creating it. + + */ +extern HTStream * HTNetToText PARAMS ((HTStream * sink)); + +/* + +HTFormatInit: Set up default presentations and conversions + + These are defined in HTInit.c or HTSInit.c if these have been replaced. If you don't + call this routine, and you don't define any presentations, then this routine will + automatically be called the first time a conversion is needed. However, if you + explicitly add some conversions (eg using HTLoadRules) then you may want also to + explicitly call this to get the defaults as well. + + */ +extern void HTFormatInit NOPARAMS; + +/* + +Epilogue + + */ +extern BOOL HTOutputSource; /* Flag: shortcut parser */ +#endif + +/* + + end */ diff --git a/WWW/Library/Implementation/HTGopher.c b/WWW/Library/Implementation/HTGopher.c new file mode 100644 index 00000000..f7ce00c4 --- /dev/null +++ b/WWW/Library/Implementation/HTGopher.c @@ -0,0 +1,2008 @@ +/* GOPHER ACCESS HTGopher.c +** ============= +** +** History: +** 26 Sep 90 Adapted from other accesses (News, HTTP) TBL +** 29 Nov 91 Downgraded to C, for portable implementation. +** 10 Mar 96 Foteos Macrides (macrides@sci.wfbr.edu). Added a +** form-based CSO/PH gateway. Can be invoked via a +** "cso://host[:port]/" or "gopher://host:105/2" +** URL. If a gopher URL is used with a query token +** ('?'), the old ISINDEX procedure will be used +** instead of the form-based gateway. +** 15 Mar 96 Foteos Macrides (macrides@sci.wfbr.edu). Pass +** port 79, gtype 0 gopher URLs to the finger +** gateway. +*/ + +#include "HTUtils.h" /* Coding convention macros */ +#include "tcp.h" +#include "HTAlert.h" +#include "HTParse.h" +#include "HTTCP.h" +#include "HTFinger.h" + +/* Implements: +*/ +#include "HTGopher.h" + +#define HT_EM_SPACE ((char)2) /* For now */ + +#define GOPHER_PORT 70 /* See protocol spec */ +#define CSO_PORT 105 /* See protocol spec */ +#define BIG 1024 /* Bug */ +#define LINE_LENGTH 256 /* Bug */ + +/* Gopher entity types: +*/ +#define GOPHER_TEXT '0' +#define GOPHER_MENU '1' +#define GOPHER_CSO '2' +#define GOPHER_ERROR '3' +#define GOPHER_MACBINHEX '4' +#define GOPHER_PCBINARY '5' +#define GOPHER_UUENCODED '6' +#define GOPHER_INDEX '7' +#define GOPHER_TELNET '8' +#define GOPHER_BINARY '9' +#define GOPHER_GIF 'g' +#define GOPHER_HTML 'h' /* HTML */ +#define GOPHER_CHTML 'H' /* HTML */ +#define GOPHER_SOUND 's' +#define GOPHER_WWW 'w' /* W3 address */ +#define GOPHER_IMAGE 'I' +#define GOPHER_TN3270 'T' +#define GOPHER_INFO 'i' +#define GOPHER_DUPLICATE '+' +#define GOPHER_PLUS_IMAGE ':' /* Addition from Gopher Plus */ +#define GOPHER_PLUS_MOVIE ';' +#define GOPHER_PLUS_SOUND '<' +#define GOPHER_PLUS_PDF 'P' + +#include <ctype.h> + +#include "HTParse.h" +#include "HTFormat.h" +#include "HTTCP.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +/* Hypertext object building machinery +*/ +#include "HTML.h" + +#include "LYLeaks.h" + +#define PUTC(c) (*targetClass.put_character)(target, c) +#define PUTS(s) (*targetClass.put_string)(target, s) +#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0) +#define END(e) (*targetClass.end_element)(target, e, 0) +#define FREE_TARGET (*targetClass._free)(target) + +#define GOPHER_PROGRESS(foo) HTAlert(foo) + +#define NEXT_CHAR HTGetCharacter() + + +/* Module-wide variables +*/ +PRIVATE int s; /* Socket for gopher or CSO host */ + +struct _HTStructured { + CONST HTStructuredClass * isa; /* For gopher streams */ + /* ... */ +}; + +PRIVATE HTStructured *target; /* the new gopher hypertext */ +PRIVATE HTStructuredClass targetClass; /* Its action routines */ + +struct _HTStream +{ + HTStreamClass * isa; /* For form-based CSO gateway - FM */ +}; + +typedef struct _CSOfield_info { /* For form-based CSO gateway - FM */ + struct _CSOfield_info * next; + char * name; + char * attributes; + char * description; + int id; + int lookup; + int indexed; + int url; + int max_size; + int defreturn; + int explicit_return; + int reserved; + int public; + char name_buf[16]; /* Avoid malloc if we can */ + char desc_buf[32]; /* Avoid malloc if we can */ + char attr_buf[80]; /* Avoid malloc if we can */ +} CSOfield_info; + +PRIVATE CSOfield_info *CSOfields = NULL; /* For form-based CSO gateway - FM */ + +typedef struct _CSOformgen_context { /* For form-based CSO gateway - FM */ + char * host; + char * seek; + CSOfield_info * fld; + int port; + int cur_line; + int cur_off; + int rep_line; + int rep_off; + int public_override; + int field_select; +} CSOformgen_context; + + +/* Matrix of allowed characters in filenames +** ========================================= +*/ +PRIVATE BOOL acceptable[256]; +PRIVATE BOOL acceptable_inited = NO; + +PRIVATE void init_acceptable NOARGS +{ + unsigned int i; + char * good = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$"; + for(i = 0; i < 256; i++) + acceptable[i] = NO; + for(; *good; good++) + acceptable[(unsigned int)*good] = YES; + acceptable_inited = YES; +} + + +/* Decode one hex character +** ======================== +*/ +PRIVATE CONST char hex[17] = "0123456789abcdef"; + +PRIVATE char from_hex ARGS1(char, c) +{ + return (c>='0')&&(c<='9') ? c-'0' + : (c>='A')&&(c<='F') ? c-'A'+10 + : (c>='a')&&(c<='f') ? c-'a'+10 + : 0; +} + + +/* Paste in an Anchor +** ================== +** +** The title of the destination is set, as there is no way +** of knowing what the title is when we arrive. +** +** On entry, +** HT is in append mode. +** text points to the text to be put into the file, 0 terminated. +** addr points to the hypertext refernce address 0 terminated. +*/ +PUBLIC BOOLEAN HT_Is_Gopher_URL=FALSE; + +PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr) +{ + BOOL present[HTML_A_ATTRIBUTES]; + CONST char * value[HTML_A_ATTRIBUTES]; + + int i; + + for (i = 0; i < HTML_A_ATTRIBUTES; i++) + present[i] = 0; + present[HTML_A_HREF] = YES; + ((CONST char **)value)[HTML_A_HREF] = addr; + present[HTML_A_TITLE] = YES; + ((CONST char **)value)[HTML_A_TITLE] = text; + + if(TRACE) + fprintf(stderr,"HTGopher: adding URL: %s\n",addr); + + HT_Is_Gopher_URL = TRUE; /* tell HTML.c that this is a Gopher URL */ + (*targetClass.start_element)(target, HTML_A, present, + (CONST char **)value, 0); + + PUTS(text); + END(HTML_A); +} + + +/* Parse a Gopher Menu document +** ============================ +*/ +PRIVATE void parse_menu ARGS2( + CONST char *, arg, + HTParentAnchor *, anAnchor) +{ + char gtype; + char ch; + char line[BIG]; + char address[BIG]; + char *name = NULL, *selector = NULL; /* Gopher menu fields */ + char *host = NULL; + char *port; + char *p = line; + CONST char *title; + int bytes = 0; + int BytesReported = 0; + char buffer[128]; + extern int interrupted_in_htgetcharacter; + +#define TAB '\t' +#define HEX_ESCAPE '%' + + + START(HTML_HTML); + PUTS("\n"); + START(HTML_HEAD); + PUTS("\n"); + START(HTML_TITLE); + if ((title = HTAnchor_title(anAnchor))) + PUTS(title); + else + PUTS("Gopher Menu"); + END(HTML_TITLE); + PUTS("\n"); + END(HTML_HEAD); + PUTS("\n"); + + START(HTML_BODY); + PUTS("\n"); + START(HTML_H1); + if ((title = HTAnchor_title(anAnchor))) + PUTS(title); + else + PUTS("Gopher Menu"); + END(HTML_H1); + PUTS("\n"); + START(HTML_PRE); + while ((ch=NEXT_CHAR) != (char)EOF) { + + if (interrupted_in_htgetcharacter) { + if (TRACE) + fprintf(stderr, + "HTGopher: Interrupted in HTGetCharacter, apparently.\n"); + goto end_html; + } + + if (ch != LF) { + *p = ch; /* Put character in line */ + if (p< &line[BIG-1]) p++; + + } else { + *p++ = '\0'; /* Terminate line */ + bytes += p-line; /* add size */ + p = line; /* Scan it to parse it */ + port = 0; /* Flag "not parsed" */ + if (TRACE) + fprintf(stderr, "HTGopher: Menu item: %s\n", line); + gtype = *p++; + + if (bytes > BytesReported + 1024) { + sprintf(buffer, "Transferred %d bytes", bytes); + HTProgress(buffer); + BytesReported = bytes; + } + + /* Break on line with a dot by itself */ + if ((gtype=='.') && ((*p=='\r') || (*p==0))) + break; + + if (gtype && *p) { + name = p; + selector = strchr(name, TAB); + if (selector) { + *selector++ = '\0'; /* Terminate name */ + /* + * Gopher+ Type=0+ objects can be binary, and will + * have 9 or 5 beginning their selector. Make sure + * we don't trash the terminal by treating them as + * text. - FM + */ + if (gtype == GOPHER_TEXT && (*selector == GOPHER_BINARY || + *selector == GOPHER_PCBINARY)) + gtype = *selector; + host = strchr(selector, TAB); + if (host) { + *host++ = '\0'; /* Terminate selector */ + port = strchr(host, TAB); + if (port) { + char *junk; + port[0] = ':'; /* delimit host a la W3 */ + junk = strchr(port, TAB); + if (junk) *junk++ = '\0'; /* Chop port */ + if ((port[1]=='0') && (!port[2])) + port[0] = '\0'; /* 0 means none */ + } /* no port */ + } /* host ok */ + } /* selector ok */ + } /* gtype and name ok */ + + /* Nameless files are a separator line */ + if (gtype == GOPHER_TEXT) { + int i = strlen(name)-1; + while (name[i] == ' ' && i >= 0) + name[i--] = '\0'; + if (i < 0) + gtype = GOPHER_INFO; + } + + if (gtype == GOPHER_WWW) { /* Gopher pointer to W3 */ + PUTS("(HTML) "); + write_anchor(name, selector); + + } else if (gtype == GOPHER_INFO) { + /* Information or separator line */ + PUTS(" "); + PUTS(name); + + } else if (port) { /* Other types need port */ + if (gtype == GOPHER_TELNET) { + PUTS(" (TEL) "); + if (*selector) sprintf(address, "telnet://%s@%s/", + selector, host); + else sprintf(address, "telnet://%s/", host); + } + else if (gtype == GOPHER_TN3270) + { + PUTS("(3270) "); + if (*selector) + sprintf(address, "tn3270://%s@%s/", + selector, host); + else + sprintf(address, "tn3270://%s/", host); + } + else { /* If parsed ok */ + char *q; + char *p; + + switch(gtype) { + case GOPHER_TEXT: + PUTS("(FILE) "); + break; + case GOPHER_MENU: + PUTS(" (DIR) "); + break; + case GOPHER_CSO: + PUTS(" (CSO) "); + break; + case GOPHER_PCBINARY: + PUTS(" (BIN) "); + break; + case GOPHER_UUENCODED: + PUTS(" (UUE) "); + break; + case GOPHER_INDEX: + PUTS(" (?) "); + break; + case GOPHER_BINARY: + PUTS(" (BIN) "); + break; + case GOPHER_GIF: + case GOPHER_IMAGE: + case GOPHER_PLUS_IMAGE: + PUTS(" (IMG) "); + break; + case GOPHER_SOUND: + case GOPHER_PLUS_SOUND: + PUTS(" (SND) "); + break; + case GOPHER_MACBINHEX: + PUTS(" (HQX) "); + break; + case GOPHER_HTML: + case GOPHER_CHTML: + PUTS("(HTML) "); + break; + case 'm': + PUTS("(MIME) "); + break; + case GOPHER_PLUS_MOVIE: + PUTS(" (MOV) "); + break; + case GOPHER_PLUS_PDF: + PUTS(" (PDF) "); + break; + default: + PUTS("(UNKN) "); + break; + } + + sprintf(address, "//%s/%c", host, gtype); + + q = address+ strlen(address); + for(p=selector; *p; p++) { /* Encode selector string */ + if (acceptable[(unsigned char)*p]) *q++ = *p; + else { + *q++ = HEX_ESCAPE; /* Means hex coming */ + *q++ = hex[(TOASCII(*p)) >> 4]; + *q++ = hex[(TOASCII(*p)) & 15]; + } + } + + *q++ = '\0'; /* terminate address */ + } + /* Error response from Gopher doesn't deserve to + be a hyperlink. */ + if (strcmp (address, "gopher://error.host:1/0")) + write_anchor(name, address); + else + PUTS(name); + } else { /* parse error */ + if (TRACE) + fprintf(stderr, "HTGopher: Bad menu item.\n"); + PUTS(line); + + } /* parse error */ + + PUTS("\n"); + p = line; /* Start again at beginning of line */ + + } /* if end of line */ + + } /* Loop over characters */ + +end_html: + END(HTML_PRE); + PUTS("\n"); + END(HTML_BODY); + PUTS("\n"); + END(HTML_HTML); + PUTS("\n"); + FREE_TARGET; + + return; +} + + +/* Parse a Gopher CSO document from and ISINDEX query. +** =================================================== +** +** Accepts an open socket to a CSO server waiting to send us +** data and puts it on the screen in a reasonable manner. +** +** Perhaps this data can be automatically linked to some +** other source as well??? +** +** Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu +** on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret, +** secret@dxcern.cern.ch . +*/ +PRIVATE void parse_cso ARGS2( + CONST char *, arg, + HTParentAnchor *, anAnchor) +{ + char ch; + char line[BIG]; + char *p = line; + char *second_colon, last_char='\0'; + CONST char *title; + + START(HTML_HEAD); + PUTS("\n"); + START(HTML_TITLE); + if ((title = HTAnchor_title(anAnchor))) + PUTS(title); + else + PUTS("CSO Search Results"); + END(HTML_TITLE); + PUTS("\n"); + END(HTML_HEAD); + PUTS("\n"); + START(HTML_H1); + if ((title = HTAnchor_title(anAnchor))) + PUTS(title); + else { + PUTS(arg); + PUTS(" Search Results"); + } + END(HTML_H1); + PUTS("\n"); + START(HTML_PRE); + + /* start grabbing chars from the network */ + while ((ch=NEXT_CHAR) != (char)EOF) + { + if (ch != LF) + { + *p = ch; /* Put character in line */ + if (p< &line[BIG-1]) p++; + } + else + { + *p = '\0'; /* Terminate line */ + p = line; /* Scan it to parse it */ + + /* OK we now have a line in 'p' lets parse it and + print it */ + + /* Break on line that begins with a 2. It's the end of + * data. + */ + if (*p == '2') + break; + + /* lines beginning with 5 are errors, + * print them and quit + */ + if (*p == '5') { + START(HTML_H2); + PUTS(p+4); + END(HTML_H2); + break; + } + + if(*p == '-') { + /* data lines look like -200:#: + * where # is the search result number and can be + * multiple digits (infinate?) + * find the second colon and check the digit to the + * left of it to see if they are diferent + * if they are then a different person is starting. + * make this line an <h2> + */ + + /* find the second_colon */ + second_colon = strchr( strchr(p,':')+1, ':'); + + if(second_colon != NULL) { /* error check */ + + if (*(second_colon-1) != last_char) + /* print seperator */ + { + END(HTML_PRE); + START(HTML_H2); + } + + + /* right now the record appears with the alias + * (first line) + * as the header and the rest as <pre> text + * It might look better with the name as the + * header and the rest as a <ul> with <li> tags + * I'm not sure whether the name field comes in any + * special order or if its even required in a + * record, + * so for now the first line is the header no + * matter + * what it is (it's almost always the alias) + * A <dl> with the first line as the <DT> and + * the rest as some form of <DD> might good also? + */ + + /* print data */ + PUTS(second_colon+1); + PUTS("\n"); + + if (*(second_colon-1) != last_char) + /* end seperator */ + { + END(HTML_H2); + START(HTML_PRE); + } + + /* save the char before the second colon + * for comparison on the next pass + */ + last_char = *(second_colon-1) ; + + } /* end if second_colon */ + } /* end if *p == '-' */ + } /* if end of line */ + + } /* Loop over characters */ + + /* end the text block */ + PUTS("\n"); + END(HTML_PRE); + PUTS("\n"); + FREE_TARGET; + + return; /* all done */ +} /* end of procedure */ + + +/* Display a Gopher CSO ISINDEX cover page +** ======================================= +*/ +PRIVATE void display_cso ARGS2( + CONST char *, arg, + HTParentAnchor *, anAnchor) +{ + CONST char * title; + + START(HTML_HEAD); + PUTS("\n"); + START(HTML_TITLE); + if ((title = HTAnchor_title(anAnchor))) + PUTS(title); + else + PUTS("CSO index"); + END(HTML_TITLE); + PUTS("\n"); + START(HTML_ISINDEX); + PUTS("\n"); + END(HTML_HEAD); + PUTS("\n"); + START(HTML_H1); + if ((title = HTAnchor_title(anAnchor))) + PUTS(title); + else { + PUTS(arg); + PUTS(" index"); + } + END(HTML_H1); + PUTS("\nThis is a searchable index of a CSO database.\n"); + START(HTML_P); + PUTS("\nPress the 's' key and enter search keywords.\n"); + START(HTML_P); + PUTS("\nThe keywords that you enter will allow you to search on a"); + PUTS(" person's name in the database.\n"); + + if (!HTAnchor_title(anAnchor)) + HTAnchor_setTitle(anAnchor, arg); + + FREE_TARGET; + return; +} + + +/* Display a Gopher Index document +** =============================== +*/ +PRIVATE void display_index ARGS2( + CONST char *, arg, + HTParentAnchor *,anAnchor) +{ + CONST char * title; + + START(HTML_HEAD); + PUTS("\n"); + PUTS("\n"); + START(HTML_TITLE); + if ((title = HTAnchor_title(anAnchor))) + PUTS(title); + else + PUTS("Gopher index"); + END(HTML_TITLE); + PUTS("\n"); + START(HTML_ISINDEX); + PUTS("\n"); + END(HTML_HEAD); + PUTS("\n"); + START(HTML_H1); + if ((title = HTAnchor_title(anAnchor))) + PUTS(title); + else { + PUTS(arg); + PUTS(" index"); + } + END(HTML_H1); + PUTS("\nThis is a searchable Gopher index.\n"); + START(HTML_P); + PUTS("\nPlease enter search keywords.\n"); + + if (!HTAnchor_title(anAnchor)) + HTAnchor_setTitle(anAnchor, arg); + + FREE_TARGET; + return; +} + + +/* De-escape a selector into a command +** =================================== +** +** The % hex escapes are converted. Otheriwse, the string is copied. +*/ +PRIVATE void de_escape ARGS2(char *, command, CONST char *, selector) +{ + CONST char * p = selector; + char * q = command; + if (command == NULL) + outofmem(__FILE__, "HTLoadGopher"); + while (*p) { /* Decode hex */ + if (*p == HEX_ESCAPE) { + char c; + unsigned int b; + p++; + c = *p++; + b = from_hex(c); + c = *p++; + if (!c) break; /* Odd number of chars! */ + *q++ = FROMASCII((b<<4) + from_hex(c)); + } else { + *q++ = *p++; /* Record */ + } + } + *q++ = '\0'; /* Terminate command */ +} + + +/* Free the CSOfields structures. - FM +** =================================== +*/ +PRIVATE void free_CSOfields NOPARAMS +{ + CSOfield_info *cur = CSOfields; + CSOfield_info *prev; + + while (cur) { + if (cur->name != cur->name_buf) + FREE(cur->name); + if (cur->attributes != cur->attr_buf) + FREE(cur->attributes); + if (cur->description != cur->desc_buf) + FREE(cur->description); + prev = cur; + cur = cur->next; + FREE(prev); + } + + return; +} + +/* Interpret CSO/PH form template keys. - FM +** ========================================= +*/ +PRIVATE int interpret_cso_key ARGS5( + char *, key, + char *, buf, + int *, length, + CSOformgen_context *, ctx, + HTStream *, Target) +{ + CSOfield_info *fld; + + if (fld = ctx->fld) { + /* + * Most substitutions only recognized inside of loops. + */ + int error = 0; + if (0 == strncmp(key, "$(FID)", 6)) { + sprintf(buf, "%d", fld->id); + } else if (0 == strncmp(key, "$(FDESC)", 8)) { + sprintf(buf, "%s%s%s", fld->description, + ctx->public_override ? /***" "***/"" : "", + ctx->public_override ? /***fld->attributes***/"" : ""); + } else if (0 == strncmp(key, "$(FDEF)", 7)) { + strcpy(buf, fld->defreturn ? " checked" : ""); + } else if (0 == strncmp(key, "$(FNDX)", 7)) { + strcpy(buf, fld->indexed ? "*" : ""); + } else if (0 == strncmp(key, "$(FSIZE)", 8)) { + sprintf(buf, " size=%d maxlength=%d", + fld->max_size > 55 ? 55 : fld->max_size, + fld->max_size); + } else if (0 == strncmp(key, "$(FSIZE2)", 9)) { + sprintf(buf, " maxlength=%d", fld->max_size); + } else { + error = 1; + } + if (!error) { + *length = strlen(buf); + return -1; + } + } + /* + */ + buf[0] = '\0'; + if (0 == strncmp(key, "$(NEXTFLD)", 10)) { + if (!ctx->fld) + fld = CSOfields; + else + fld = ctx->fld->next; + switch (ctx->field_select) { + case 0: + /* 'Query' fields, public and lookup attributes */ + for (; fld; fld = fld->next) + if (fld->public && (fld->lookup==1)) + break; + break; + case 1: + /* 'Query' fields, accept lookup attribute */ + for (; fld; fld = fld->next) + if (fld->lookup == 1) + break; + break; + case 2: + /* 'Return' fields, public only */ + for (; fld; fld = fld->next) + if (fld->public) + break; + break; + case 3: + /* all fields */ + break; + } + if (fld) { + ctx->cur_line = ctx->rep_line; + ctx->cur_off = ctx->rep_off; + } + ctx->fld = fld; + + } else if ((0 == strncmp(key, "$(QFIELDS)", 10)) || + (0 == strncmp(key, "$(RFIELDS)", 10))) { + /* + * Begin interation sequence. + */ + ctx->rep_line = ctx->cur_line; + ctx->rep_off = ctx->cur_off; + ctx->fld = (CSOfield_info *) 0; + ctx->seek = "$(NEXTFLD)"; + ctx->field_select = (key[2] == 'Q') ? 0 : 2; + if (ctx->public_override) + ctx->field_select++; + + } else if (0 == strncmp(key, "$(NAMEFLD)", 10)) { + /* + * Special, locate name field. Flag lookup so QFIELDS will skip it. + */ + for (fld = CSOfields; fld; fld = fld->next) + if (strcmp(fld->name, "name") == 0 || + strcmp(fld->name, "Name") == 0) { + if (fld->lookup) + fld->lookup = 2; + break; + } + ctx->fld = fld; + } else if (0 == strncmp (key, "$(HOST)", 7)) { + strcpy (buf, ctx->host); + } else if (0 == strncmp (key, "$(PORT)", 7)) { + sprintf(buf, "%d", ctx->port); + } else { + /* + * No match, dump key to buffer so client sees it for debugging. + */ + int out = 0; + while (*key && (*key != ')')) { + buf[out++] = (*key++); + if (out > sizeof(buf)-2) { + buf[out] = '\0'; + (*Target->isa->put_block)(Target, buf, strlen(buf)); + out = 0; + } + } + buf[out++] = ')'; + buf[out] = '\0'; + *length = strlen(buf); + return -1; + } + *length = strlen(buf); + return 0; +} + + +/* Parse the elements in a CSO/PH fields structure. - FM +** ===================================================== +*/ +PRIVATE int parse_cso_field_info ARGS1( + CSOfield_info *, blk) +{ + int i; + char *info, *max_spec; + + /* + * initialize all fields to default values. + */ + blk->indexed = blk->lookup = blk->reserved = blk->max_size = blk->url = 0; + blk->defreturn = blk->explicit_return = blk->public = 0; + + /* + * Search for keywords in info string and set values. Attributes + * are converted to all lower-case for comparison. + */ + info = blk->attributes; + for (i = 0; info[i]; i++) + info[i] = TOLOWER(info[i]); + if (strstr(info, "indexed ")) + blk->indexed = 1; + if (strstr(info, "default ")) + blk->defreturn = 1; + if (strstr(info, "public ")) + blk->public = 1; + if (strstr(info, "lookup ")) + blk->lookup = 1; + if (strstr(info, "url ")) { + blk->url = 1; + blk->defreturn = 1; + } + max_spec = strstr(info, "max "); + if (max_spec) { + sscanf(&max_spec[4], "%d", &blk->max_size); + } else { + blk->max_size = 32; + } + + return 0; +} + + +/* Parse a reply from a CSO/PH fields request. - FM +** ================================================ +*/ +PRIVATE int parse_cso_fields ARGS2( + char *, buf, + int, size) +{ + char ch; + char *p = buf; + int i, code, prev_code, alen; + char *index, *name; + CSOfield_info *last, *new; + extern int interrupted_in_htgetcharacter; + + last = CSOfields = (CSOfield_info *) 0; + prev_code = -2555; + buf[0] = '\0'; + + /* start grabbing chars from the network */ + while ((ch=NEXT_CHAR) != (char)EOF) { + + if (interrupted_in_htgetcharacter) { + if (TRACE) { + fprintf(stderr, + "HTLoadCSO: Interrupted in HTGetCharacter, apparently.\n"); + } + free_CSOfields(); + buf[0] = '\0'; + return HT_INTERRUPTED; + } + + if (ch != LF) { + *p = ch; /* Put character in buffer */ + if (p < &buf[size-1]) { + p++; + } + } else { + *p = '\0'; /* Terminate line */ + p = buf; /* Scan it to parse it */ + + /* OK we now have a line in 'p' lets parse it. + */ + + /* Break on line that begins with a 2. + * It's the end of data. + */ + if (*p == '2') + break; + + /* lines beginning with 5 are errors, + * print them and quit + */ + if (*p == '5') { + strcpy (buf, p); + return 5; + } + + if (*p == '-') { + /* data lines look like -200:#: + * where # is the search result number and can be + * multiple digits (infinite?). + */ + + /* + * Check status, ignore any non-success. + */ + if (p[1] != '2' ) + continue; + + /* + * Parse fields within returned line into status, ndx, name, data. + */ + index = NULL; + name = NULL; + for (i = 0; p[i]; i++) + if (p[i] == ':' ) { + p[i] = '\0'; + if (!index) { + index = (char *)&p[i+1]; + code = atoi (index); + } else if (!name) { + name = (char *)&p[i+1]; + } else { + i++; + break; + } + } + /* + * Add data to field structure. + */ + if (name) { + if (code == prev_code) { + /* + * Remaining data is description, + * save in current info block. + */ + alen = strlen((char *)&p[i]) + 1; + if (alen > sizeof(last->desc_buf)) { + if (last->description != last->desc_buf) + FREE(last->description); + if (!(last->description = (char *)malloc(alen))) { + outofmem(__FILE__, "HTLoadCSO"); + } + } + strcpy(last->description, (char *)&p[i]); + } else { + /* + * Initialize new block, append to end of list + * to preserve order. + */ + new = (CSOfield_info *)calloc(1, sizeof(CSOfield_info)); + if (!new) { + outofmem(__FILE__, "HTLoadCSO"); + } + if (last) + last->next = new; + else + CSOfields = new; + last = new; + + new->next = (CSOfield_info *) 0; + new->name = new->name_buf; + alen = strlen(name) + 1; + if (alen > sizeof(new->name_buf)) { + if (!(new->name = (char *)malloc(alen))) { + outofmem(__FILE__, "HTLoadCSO"); + } + } + strcpy (new->name, name); + + new->attributes = new->attr_buf; + alen = strlen((char *)&p[i]) + 2; + if (alen > sizeof(new->attr_buf)) { + if (!(new->attributes = (char *)malloc(alen))) { + outofmem(__FILE__, "HTLoadCSO"); + } + } + strcpy(new->attributes, (char *)&p[i]); + strcpy((char *)&new->attributes[alen-2], " "); + new->description = new->desc_buf; + new->desc_buf[0] = '\0'; + new->id = atoi(index); + /* + * Scan for keywords. + */ + parse_cso_field_info(new); + } + prev_code = code; + } else + break; + } /* end if *p == '-' */ + } /* if end of line */ + + } /* Loop over characters */ + + /* end the text block */ + + if (buf[0] == '\0') { + return -1; /* no response */ + } + buf[0] = '\0'; + return 0; /* all done */ +} /* end of procedure */ + + +/* Generate a form for submitting CSO/PH searches. - FM +** ==================================================== +*/ +PRIVATE int generate_cso_form ARGS4( + char *, host, + int, port, + char *, buf, + HTStream *, Target) +{ + int i, j, length, out; + int full_flag = 1; + char *key, *line; + CSOformgen_context ctx; + static char *template[] = { + "<HEAD>\n<TITLE>CSO/PH Query Form for $(HOST)</TITLE>\n</HEAD>\n<BODY>", + "<H2><I>CSO/PH Query Form</I> for <EM>$(HOST)</EM></H2>", + "To search the database for a name, fill in one or more of the fields", + "in the form below and activate the 'Submit query' button. At least", + "one of the entered fields must be flagged as indexed.", + "<HR><FORM method=\"POST\" action=\"cso://$(HOST)/\">", + "[ <input type=\"submit\" value=\"Submit query\"> | ", + "<input type=\"reset\" value=\"Clear fields\"> ]", + "<P><DL>", + " <DT>Search parameters (* indicates indexed field):", + " <DD>", "$(NAMEFLD) <DL COMPACT>\n <DT><I>$(FDESC)</I>$(FNDX)", + " <DD>Last: <input name=\"q_$(FID)\" type=\"text\" size=49$(FSIZE2)>", + " <DD>First: <input name=\"q_$(FID)\" type=\"text\" size=48$(FSIZE2)>", + "$(QFIELDS) <DT><I>$(FDESC)</I>$(FNDX)", + " <DD><input name=\"q_$(FID)\" type=\"text\" $(FSIZE)>\n$(NEXTFLD)", + " </DL>", + " </DL>\n<P><DL>", + " <DT>Output format:", + " <DD>Returned data option: <select name=\"return\">", + " <option>default<option selected>all<option>selected</select><BR>", + "$(RFIELDS) <input type=\"checkbox\" name=\"r_$(FID)\"$(FDEF)> $(FDESC)<BR>", + "$(NEXTFLD) ", + " </DL></FORM><HR>\n</BODY>\n</HTML>", + (char *) 0 + }; + + out = 0; + ctx.host = host; + ctx.seek = (char *) 0; + ctx.port = port; + ctx.fld = (CSOfield_info *) 0; + ctx.public_override = full_flag; + /* + * Parse the strings in the template array to produce HTML document + * to send to client. First line is skipped for 'full' lists. + */ + out = 0; + buf[out] = '\0'; + for (i = full_flag ? /***1***/ 0 : 0; template[i]; i++) { + /* + * Search the current string for substitution, flagged by $( + */ + for (line=template[i], j = 0; line[j]; j++) { + if ((line[j] == '$') && (line[j+1] == '(')) { + /* + * Command detected, flush output buffer and find closing ')' + * that delimits the command. + */ + buf[out] = '\0'; + if (out > 0) + (*Target->isa->put_block)(Target, buf, strlen(buf)); + out = 0; + for (key = &line[j]; line[j+1] && (line[j] != ')'); j++) + ; + /* + * Save context, interpet command and restore updated context. + */ + ctx.cur_line = i; + ctx.cur_off = j; + interpret_cso_key(key, buf, &length, &ctx, Target); + i = ctx.cur_line; + j = ctx.cur_off; + line = template[i]; + out = length; + + if (ctx.seek) { + /* + * command wants us to skip (forward) to indicated token. + * Start at current position. + */ + int slen = strlen(ctx.seek); + for (; template[i]; i++) { + for (line = template[i]; line[j]; j++) { + if (line[j] == '$') + if (0 == strncmp(ctx.seek, &line[j], slen)) { + if (j == 0) + j = strlen(template[--i])-1; + else + --j; + line = template[i]; + ctx.seek = (char *) 0; + break; + } + } + if (!ctx.seek) + break; + j = 0; + } + if (ctx.seek) { + char *temp = (char *)malloc(strlen(ctx.seek) + 20); + if (temp) { + outofmem(__FILE__, "HTLoadCSO"); + } + sprintf(temp, "Seek fail on %s\n", ctx.seek); + (*Target->isa->put_block)(Target, temp, strlen(temp)); + FREE(temp); + } + } + } else { + /* + * Non-command text, add to output buffer. + */ + buf[out++] = line[j]; + if (out > (sizeof(buf)-3)) { + buf[out] = '\0'; + (*Target->isa->put_block)(Target, buf, strlen(buf)); + out = 0; + } + } + } + buf[out++] = '\n'; + buf[out] = '\0'; + } + if (out > 0) + (*Target->isa->put_block)(Target, buf, strlen(buf)); + + return 0; +} + + +/* Generate a results report for CSO/PH form-based searches. - FM +** ============================================================== +*/ +PRIVATE int generate_cso_report ARGS2( + char *, buf, + HTStream *, Target) +{ + char ch; + char line[BIG]; + char *p = line, *href = NULL; + int len, i, prev_ndx, ndx; + char *rcode, *ndx_str, *fname, *fvalue, *l; + CSOfield_info *fld; + BOOL stop = FALSE; + extern int interrupted_in_htgetcharacter; + + /* + * Read lines until non-negative status. + */ + prev_ndx = -100; + /* start grabbing chars from the network */ + while (!stop && (ch=NEXT_CHAR) != (char)EOF) { + + if (interrupted_in_htgetcharacter) { + buf[0] = '\0'; + if (TRACE) { + fprintf(stderr, + "HTLoadCSO: Interrupted in HTGetCharacter, apparently.\n"); + } + _HTProgress ("Connection interrupted."); + goto end_CSOreport; + } + + if (ch != LF) { + *p = ch; /* Put character in line */ + if (p < &line[BIG-1]) { + p++; + } + } else { + *p = '\0'; /* Terminate line */ + /* + * OK we now have a line. + * Load it as 'p' and parse it. + */ + p = line; + if (p[0] != '-' && p[0] != '1') { + stop = TRUE; + } + rcode = (p[0] == '-') ? &p[1] : p; + ndx_str = fname = NULL; + len = strlen(p); + for (i = 0; i < len; i++) { + if (p[i] == ':') { + p[i] = '\0'; + if (!ndx_str) { + fname = ndx_str = &p[i+1]; + } else { + fname = &p[i+1]; + break; + } + } + } + if (ndx_str) { + ndx = atoi(ndx_str); + if (prev_ndx != ndx) { + if (prev_ndx != -100) { + strcpy(buf, "</DL></DL>\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } + if (ndx == 0) { + strcpy(buf, + "<HR><DL><DT>Information/status<DD><DL><DT>\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } else { + sprintf(buf, + "<HR><DL><DT>Entry %d:<DD><DL COMPACT><DT>\n", ndx); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } + prev_ndx = ndx; + } + } else { + sprintf(buf, "<DD>%s\n", rcode); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + continue; + } + if ((*rcode >= '2') && (*rcode <= '5') && (fname != ndx_str)) { + while (*fname == ' ') { + fname++; /* trim leading spaces */ + } + for (fvalue = fname; *fvalue; fvalue++) { + if (*fvalue == ':') { + *fvalue++ = '\0'; + i = strlen(fname) - 1; + while (i >= 0 && fname[i] == ' ') { + fname[i--] = '\0'; /* trim trailing */ + } + break; + } + } + if (fvalue) { + while (*fvalue == ' ') { + fvalue++; /* trim leading spaces */ + } + } + if (*fname) { + for (fld = CSOfields; fld; fld = fld->next) { + if (!strcmp(fld->name, fname)) { + if (fld->description) { + fname = fld->description; + } + break; + } + } + if (fld && fld->url) { + sprintf(buf, + "<DT><I>%s</I><DD><A HREF=\"%s\">%s</A>\n", + fname, fvalue, fvalue); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } else { + sprintf(buf, "<DT><I>%s</I><DD>", fname); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + i = 0; + buf[i] = '\0'; + l = fvalue; + while (*l) { + if (*l == '<') { + strcat(buf, "<"); + l++; + i += 4; + buf[i] = '\0'; + } else if (*l == '>') { + strcat(buf, ">"); + l++; + i += 4; + buf[i] = '\0'; + } else if (strncmp (l, "news:", 5) && + strncmp (l, "snews://", 8) && + strncmp (l, "nntp://", 7) && + strncmp (l, "ftp://", 6) && + strncmp (l, "file:/", 6) && + strncmp (l, "finger://", 9) && + strncmp (l, "http://", 7) && + strncmp (l, "https://", 8) && + strncmp (l, "wais://", 7) && + strncmp (l, "mailto:", 7) && + strncmp (l, "cso://", 6) && + strncmp (l, "gopher://", 9)) { + buf[i++] = *l++; + buf[i] = '\0'; + } else { + strcat(buf, "<a href=\""); + i += 9; + buf[i] = '\0'; + StrAllocCopy(href, l); + strcat(buf, strtok(href, " \r\n\t,>)\"")); + strcat(buf, "\">"); + i = strlen(buf); + while (*l && !strchr(" \r\n\t,>)\"", *l)) { + buf[i++] = *l++; + } + buf[i] = '\0'; + strcat(buf, "</a>"); + i += 4; + FREE(href); + } + } + strcat(buf, "\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } + } else { + sprintf(buf, "<DD>"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + i = 0; + buf[i] = '\0'; + l = fvalue; + while (*l) { + if (*l == '<') { + strcat(buf, "<"); + l++; + i += 4; + buf[i] = '\0'; + } else if (*l == '>') { + strcat(buf, ">"); + l++; + i += 4; + buf[i] = '\0'; + } else if (strncmp (l, "news:", 5) && + strncmp (l, "snews://", 8) && + strncmp (l, "nntp://", 7) && + strncmp (l, "ftp://", 6) && + strncmp (l, "file:/", 6) && + strncmp (l, "finger://", 9) && + strncmp (l, "http://", 7) && + strncmp (l, "https://", 8) && + strncmp (l, "wais://", 7) && + strncmp (l, "mailto:", 7) && + strncmp (l, "cso://", 6) && + strncmp (l, "gopher://", 9)) { + buf[i++] = *l++; + buf[i] = '\0'; + } else { + strcat(buf, "<a href=\""); + i += 9; + buf[i] = '\0'; + StrAllocCopy(href, l); + strcat(buf, strtok(href, " \r\n\t,>)\"")); + strcat(buf, "\">"); + i = strlen(buf); + while (*l && !strchr(" \r\n\t,>)\"", *l)) { + buf[i++] = *l++; + } + buf[i] = '\0'; + strcat(buf, "</a>"); + i += 4; + FREE(href); + } + } + strcat(buf, "\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } + } else { + sprintf(buf, "<DD>%s\n", fname ? fname : rcode ); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } + } + } +end_CSOreport: + if (prev_ndx != -100) { + sprintf(buf, "</DL></DL>\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } + return 0; +} + + +/* CSO/PH form-based search gateway - FM HTLoadCSO +** ===================================== +*/ +PUBLIC int HTLoadCSO ARGS4( + CONST char *, arg, + HTParentAnchor *, anAnchor, + HTFormat, format_out, + HTStream*, sink) +{ + char *host, *cp; + int port = CSO_PORT; + int status; /* tcp return */ + char *command = NULL; + char *content = NULL; + int len, i, j, start, finish, flen, ndx, clen; + int return_type, has_indexed; + CSOfield_info *fld; + char buf[2048]; + HTFormat format_in = WWW_HTML; + HTStream *Target = NULL; + + struct sockaddr_in soc_address; /* Binary network address */ + struct sockaddr_in* sin = &soc_address; + + if (!acceptable_inited) + init_acceptable(); + + if (!arg) + return -3; /* Bad if no name sepcified */ + if (!*arg) + return -2; /* Bad if name had zero length */ + if (TRACE) + fprintf(stderr, "HTLoadCSO: Looking for %s\n", arg); + + /* Set up defaults: + */ + sin->sin_family = AF_INET; /* Family, host order */ + sin->sin_port = htons(CSO_PORT); /* Default: new port, */ + + /* Get node name and optional port number: + */ + { + char *p1 = HTParse(arg, "", PARSE_HOST); + int status = HTParseInet(sin, p1); + FREE(p1); + if (status) { + return status; /* Bad */ + } + } + + /* Set up a socket to the server for the data: + */ + status = HTDoConnect (arg, "cso", CSO_PORT, &s); + if (status == HT_INTERRUPTED) { + /* Interrupt cleanly. */ + if (TRACE) + fprintf(stderr, + "HTLoadCSO: Interrupted on connect; recovering cleanly.\n"); + _HTProgress ("Connection interrupted."); + return HT_INTERRUPTED; + } + if (status < 0) { + if (TRACE) + fprintf(stderr, + "HTLoadCSO: Unable to connect to remote host for `%s'.\n", + arg); + return HTInetStatus("connect"); + } + + HTInitInput(s); /* Set up input buffering */ + + if ((command = (char *)malloc(12)) == NULL) + outofmem(__FILE__, "HTLoadCSO"); + sprintf(command, "fields%c%c", CR, LF); + if (TRACE) + fprintf(stderr, + "HTLoadCSO: Connected, writing command `%s' to socket %d\n", + command, s); + _HTProgress ("Sending CSO/PH request."); + status = NETWRITE(s, command, (int)strlen(command)); + FREE(command); + if (status < 0) { + if (TRACE) + fprintf(stderr, "HTLoadCSO: Unable to send command.\n"); + return HTInetStatus("send"); + } + _HTProgress ("CSO/PH request sent; waiting for response."); + + /* Now read the data from the socket: + */ + status = parse_cso_fields(buf, sizeof(buf)); + if (status) { + NETCLOSE(s); + if (status = HT_INTERRUPTED) { + _HTProgress ("Connection interrupted."); + } else if (buf[0] != '\0') { + HTAlert(buf); + } else { + HTAlert("No response from server!"); + } + return HT_NOT_LOADED; + } + Target = HTStreamStack(format_in, + format_out, + sink, anAnchor); + if (!Target || Target == NULL) { + char *temp = (char *)malloc(256); + if (!temp) { + outofmem(__FILE__, "HTLoadCSO"); + } + sprintf(temp, "Sorry, no known way of converting %s to %s.", + HTAtom_name(format_in), HTAtom_name(format_out)); + HTAlert(temp); + FREE(temp); + NETCLOSE(s); + return HT_NOT_LOADED; + } + host = HTParse(arg, "", PARSE_HOST); + if ((cp=strchr(host, ':')) != NULL) { + if (cp[1] >= '0' && cp[1] <= '9') { + port = atoi((cp+1)); + if (port == CSO_PORT) { + *cp = '\0'; + } + } + } + if (!(anAnchor->post_data && *anAnchor->post_data)) { + generate_cso_form(host, port, buf, Target); + (*Target->isa->_free)(Target); + FREE(host); + NETCLOSE(s); + free_CSOfields(); + return HT_LOADED; + } + sprintf(buf, + "<HTML>\n<HEAD>\n<TITLE>CSO/PH Results on %s</TITLE>\n</HEAD>\n<BODY>\n", + host); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + FREE(host); + StrAllocCopy(content, anAnchor->post_data); + if (content[strlen(content)-1] != '&') + StrAllocCat(content, "&"); + len = strlen(content); + for (i = 0; i < len; i++) { + if (content[i] == '+') { + content[i] = ' '; + } + } + HTUnEscape(content); + len = strlen(content); + return_type = 0; + has_indexed = 0; + start = finish = clen = 0; + for (i = 0; i < len; i++) { + if (!content[i] || content[i] == '&') { + /* + * Value parsed. Unescape characters and look for first '=' + * to delimit field name from value. + */ + flen = i - start; + finish = start + flen; + content[finish] = '\0'; + for (j = start; j < finish; j++) { + if (content[j] == '=') { + /* + * content[start..j-1] is field name, + * [j+1..finish-1] is value. + */ + if ((content[start+1] == '_') && + ((content[start] == 'r') || (content[start] == 'q'))) { + /* + * Decode fields number and lookup field info. + */ + sscanf (&content[start+2], "%d=", &ndx); + for (fld = CSOfields; fld; fld = fld->next) { + if (ndx==fld->id) { + if ((j+1) >= finish) + break; /* ignore nulls */ + if (content[start] == 'q') { + /* + * Append field to query line. + */ + if (fld->lookup) { + if (fld->indexed) + has_indexed = 1; + if (clen == 0) { + StrAllocCopy(command, "query "); + clen = 6; + } else { + StrAllocCat(command, " "); + clen++; + } + sprintf(buf, "%s=\"%s\"", + fld->name, &content[j+1]); + StrAllocCat(command, buf); + clen += strlen(buf); + } else { + strcpy(buf, + "Warning: non-lookup field ignored<BR>\n"); + (*Target->isa->put_block)(Target, + buf, + strlen(buf)); + } + } else if (content[start] == 'r') { + fld->explicit_return = 1; + } + break; + } + } + } else if (!strncmp(&content[start],"return=",7)) { + if (!strcmp(&content[start+7],"all")) { + return_type = 1; + } else if (!strcmp(&content[start+7],"selected")) { + return_type = 2; + } + } + } + } + start = i + 1; + } + } + FREE(content); + if ((clen == 0) || !has_indexed) { + NETCLOSE(s); + strcpy(buf, + "<EM>Error:</EM> At least one indexed field value must be specified!\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + strcpy(buf, "</BODY>\n</HTML>\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + (*Target->isa->_free)(Target); + free_CSOfields(); + return HT_LOADED; + } + /* + * Append return fields. + */ + if (return_type == 1) { + StrAllocCat(command, " return all"); + clen += 11; + } else if (return_type == 2) { + StrAllocCat(command, " return"); + clen += 7; + for (fld = CSOfields; fld; fld = fld->next) { + if (fld->explicit_return) { + sprintf(buf, " %s", fld->name); + StrAllocCat(command, buf); + clen += strlen(buf); + } + } + } + sprintf(buf, "%c%c", CR, LF); + StrAllocCat(command, buf); + clen += strlen(buf); + strcpy(buf, "<H2>\n<EM>CSO/PH command:</EM> "); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + (*Target->isa->put_block)(Target, command, clen); + strcpy(buf, "</H2>\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + if (TRACE) + fprintf(stderr, + "HTLoadCSO: Writing command `%s' to socket %d\n", + command, s); + status = NETWRITE(s, command, clen); + FREE(command); + if (status < 0) { + if (TRACE) + fprintf(stderr, "HTLoadCSO: Unable to send command.\n"); + free_CSOfields(); + return HTInetStatus("send"); + } + generate_cso_report(buf, Target); + NETCLOSE(s); + strcpy(buf, "</BODY>\n</HTML>\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + (*Target->isa->_free)(Target); + FREE(host); + free_CSOfields(); + return HT_LOADED; +} + + +/* Load by name HTLoadGopher +** ============ +** +** Bug: No decoding of strange data types as yet. +** +*/ +PUBLIC int HTLoadGopher ARGS4( + CONST char *, arg, + HTParentAnchor *, anAnchor, + HTFormat, format_out, + HTStream*, sink) +{ + char *command; /* The whole command */ + int status; /* tcp return */ + char gtype; /* Gopher Node type */ + char * selector; /* Selector string */ + + struct sockaddr_in soc_address; /* Binary network address */ + struct sockaddr_in* sin = &soc_address; + + if (!acceptable_inited) + init_acceptable(); + + if (!arg) + return -3; /* Bad if no name sepcified */ + if (!*arg) + return -2; /* Bad if name had zero length */ + if (TRACE) + fprintf(stderr, "HTGopher: Looking for %s\n", arg); + + /* If it's a port 105 GOPHER_CSO gtype with no ISINDEX token ('?'), + ** use the form-based CSO gateway (otherwise, return an ISINDEX + ** cover page or do the ISINDEX search). - FM + */ + { + int len; + + if ((len = strlen(arg)) > 5) { + if (0 == strcmp((char *)&arg[len-6], ":105/2")) { + /* Use CSO gateway. */ + if (TRACE) + fprintf(stderr, "HTGopher: Passing to CSO/PH gateway.\n"); + return HTLoadCSO(arg, anAnchor, format_out, sink); + } + } + } + + /* If it's a port 79/0[/...] URL, use the finger gateway. - FM + */ + if (strstr(arg, ":79/0") != NULL) { + if (TRACE) + fprintf(stderr, "HTGopher: Passing to finger gateway.\n"); + return HTLoadFinger(arg, anAnchor, format_out, sink); + } + +/* Set up defaults: +*/ + sin->sin_family = AF_INET; /* Family, host order */ + sin->sin_port = htons(GOPHER_PORT); /* Default: new port, */ + +/* Get node name and optional port number: +*/ + { + char *p1 = HTParse(arg, "", PARSE_HOST); + int status = HTParseInet(sin, p1); + FREE(p1); + if (status) + return status; /* Bad */ + } + +/* Get entity type, and selector string. +*/ + { + char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION); + gtype = '1'; /* Default = menu */ + selector = p1; + if ((*selector++=='/') && (*selector)) { /* Skip first slash */ + gtype = *selector++; /* Pick up gtype */ + } + if (gtype == GOPHER_INDEX) { + char * query; + /* Search is allowed */ + HTAnchor_setIndex(anAnchor, anAnchor->address); + query = strchr(selector, '?'); /* Look for search string */ + if (!query || !query[1]) { /* No search required */ + target = HTML_new(anAnchor, format_out, sink); + targetClass = *target->isa; + display_index(arg, anAnchor); /* Display "cover page" */ + return HT_LOADED; /* Local function only */ + } + *query++ = '\0'; /* Skip '?' */ + command = + (char *)malloc(strlen(selector)+ 1 + strlen(query)+ 2 + 1); + if (command == NULL) + outofmem(__FILE__, "HTLoadGopher"); + + de_escape(command, selector); /* Bug fix TBL 921208 */ + + strcat(command, "\t"); + + { /* Remove plus signs 921006 */ + char *p; + for (p=query; *p; p++) { + if (*p == '+') *p = ' '; + } + } + + de_escape(&command[strlen(command)], query);/* bug fix LJM 940415 */ + } else if (gtype == GOPHER_CSO) { + char * query; + /* Search is allowed */ + query = strchr(selector, '?'); /* Look for search string */ + if (!query || !query[1]) { /* No search required */ + target = HTML_new(anAnchor, format_out, sink); + targetClass = *target->isa; + display_cso(arg, anAnchor); /* Display "cover page" */ + return HT_LOADED; /* Local function only */ + } + HTAnchor_setIndex(anAnchor, anAnchor->address); + *query++ = '\0'; /* Skip '?' */ + command = (char *)malloc(strlen("query")+1 + strlen(query)+2+1); + if (command == NULL) + outofmem(__FILE__, "HTLoadGopher"); + + de_escape(command, selector); /* Bug fix TBL 921208 */ + + strcpy(command, "query "); + + { /* Remove plus signs 921006 */ + char *p; + for (p=query; *p; p++) { + if (*p == '+') *p = ' '; + } + } + de_escape(&command[strlen(command)], query);/* bug fix LJM 940415 */ + + + } else { /* Not index */ + command = (char *)malloc(strlen(selector)+2+1); + de_escape(command, selector); + } + FREE(p1); + } + + { + char * p = command + strlen(command); + *p++ = CR; /* Macros to be correct on Mac */ + *p++ = LF; + *p++ = '\0'; + /* strcat(command, "\r\n"); */ /* CR LF, as in rfc 977 */ + } + +/* Set up a socket to the server for the data: +*/ + + status = HTDoConnect (arg, "gopher", GOPHER_PORT, &s); + if (status == HT_INTERRUPTED) + { + /* Interrupt cleanly. */ + if (TRACE) + fprintf(stderr, + "HTGopher: Interrupted on connect; recovering cleanly.\n"); + _HTProgress ("Connection interrupted."); + return HT_INTERRUPTED; + } + if (status < 0) { + if (TRACE) + fprintf(stderr, + "HTGopher: Unable to connect to remote host for `%s'.\n", + arg); + FREE(command); + return HTInetStatus("connect"); + } + + HTInitInput(s); /* Set up input buffering */ + + if (TRACE) + fprintf(stderr, + "HTGopher: Connected, writing command `%s' to socket %d\n", + command, s); + +#ifdef NOT_ASCII + { + char * p; + for(p = command; *p; p++) { + *p = TOASCII(*p); + } + } +#endif + + _HTProgress ("Sending Gopher request."); + + status = NETWRITE(s, command, (int)strlen(command)); + FREE(command); + if (status < 0) { + if (TRACE) + fprintf(stderr, "HTGopher: Unable to send command.\n"); + return HTInetStatus("send"); + } + + _HTProgress ("Gopher request sent; waiting for response."); + +/* Now read the data from the socket: +*/ + switch (gtype) { + + case GOPHER_TEXT : + HTParseSocket(WWW_PLAINTEXT, format_out, anAnchor, s, sink); + break; + + case GOPHER_HTML : + case GOPHER_CHTML : + HTParseSocket(WWW_HTML, format_out, anAnchor, s, sink); + break; + + case GOPHER_GIF: + case GOPHER_IMAGE: + case GOPHER_PLUS_IMAGE: + HTParseSocket(HTAtom_for("image/gif"), + format_out, anAnchor, s, sink); + break; + + case GOPHER_MENU : + case GOPHER_INDEX : + target = HTML_new(anAnchor, format_out, sink); + targetClass = *target->isa; + parse_menu(arg, anAnchor); + break; + + case GOPHER_CSO: + target = HTML_new(anAnchor, format_out, sink); + targetClass = *target->isa; + parse_cso(arg, anAnchor); + break; + + case GOPHER_SOUND : + case GOPHER_PLUS_SOUND : + HTParseSocket(WWW_AUDIO, format_out, anAnchor, s, sink); + break; + + case GOPHER_PLUS_MOVIE: + HTParseSocket(HTAtom_for("video/mpeg"), format_out, anAnchor, s, sink); + break; + + case GOPHER_PLUS_PDF: + HTParseSocket(HTAtom_for("application/pdf"), format_out, anAnchor, + s, sink); + break; + + case GOPHER_MACBINHEX: + case GOPHER_PCBINARY: + case GOPHER_UUENCODED: + case GOPHER_BINARY: + default: + /* Specifying WWW_UNKNOWN forces dump to local disk. */ + HTParseSocket (WWW_UNKNOWN, format_out, anAnchor, s, sink); + break; + + } /* switch(gtype) */ + + NETCLOSE(s); + return HT_LOADED; +} + +#ifdef GLOBALDEF_IS_MACRO +#define _HTGOPHER_C_1_INIT { "gopher", HTLoadGopher, NULL } +GLOBALDEF (HTProtocol, HTGopher, _HTGOPHER_C_1_INIT); +#define _HTCSO_C_1_INIT { "cso", HTLoadCSO, NULL } +GLOBALDEF (HTProtocol, HTCSO, _HTCSO_C_1_INIT); +#else +GLOBALDEF PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL }; +GLOBALDEF PUBLIC HTProtocol HTCSO = { "cso", HTLoadCSO, NULL }; +#endif /* GLOBALDEF_IS_MACRO */ diff --git a/WWW/Library/Implementation/HTGopher.h b/WWW/Library/Implementation/HTGopher.h new file mode 100644 index 00000000..947bd3e9 --- /dev/null +++ b/WWW/Library/Implementation/HTGopher.h @@ -0,0 +1,27 @@ +/* Gopher protocol module for libwww + GOPHER ACCESS + + HISTORY: + + 8 Jan 92 Adapted from HTTP TBL + + */ + + +#ifndef HTGOPHER_H +#define HTGOPHER_H + +#include "HTAccess.h" +#include "HTAnchor.h" + +#ifdef GLOBALREF_IS_MACRO +extern GLOBALREF (HTProtocol, HTGopher); +#else +GLOBALREF HTProtocol HTGopher; +#endif /* GLOBALREF_IS_MACRO */ + +#endif /* HTGOPHER_H */ + +/* + + end of gopher module */ diff --git a/WWW/Library/Implementation/HTGroup.c b/WWW/Library/Implementation/HTGroup.c new file mode 100644 index 00000000..e281a578 --- /dev/null +++ b/WWW/Library/Implementation/HTGroup.c @@ -0,0 +1,772 @@ + +/* MODULE HTGroup.c +** GROUP FILE ROUTINES +** +** Contains group file parser and routines to match IP +** address templates and to find out group membership. +** +** +** AUTHORS: +** AL Ari Luotonen luotonen@dxcern.cern.ch +** +** HISTORY: +** +** +** BUGS: +** +** +** +** GROUP DEFINITION GRAMMAR: +** +** string = "sequence of alphanumeric characters" +** user_name ::= string +** group_name ::= string +** group_ref ::= group_name +** user_def ::= user_name | group_ref +** user_def_list ::= user_def { ',' user_def } +** user_part = user_def | '(' user_def_list ')' +** +** templ = "sequence of alphanumeric characters and '*'s" +** ip_number_mask ::= templ '.' templ '.' templ '.' templ +** domain_name_mask ::= templ { '.' templ } +** address ::= ip_number_mask | domain_name_mask +** address_def ::= address +** address_def_list ::= address_def { ',' address_def } +** address_part = address_def | '(' address_def_list ')' +** +** item ::= [user_part] ['@' address_part] +** item_list ::= item { ',' item } +** group_def ::= item_list +** group_decl ::= group_name ':' group_def +** +*/ + + + +#include "HTUtils.h" +#include <string.h> +#include "HTAAUtil.h" +#include "HTLex.h" /* Lexical analysor */ +#include "HTGroup.h" /* Implemented here */ + +#include "LYLeaks.h" + +/* +** Group file parser +*/ + +typedef HTList UserDefList; +typedef HTList AddressDefList; + +typedef struct { + UserDefList * user_def_list; + AddressDefList * address_def_list; +} Item; + +typedef struct { + char * name; + GroupDef * translation; +} Ref; + + + +PRIVATE void syntax_error ARGS3(FILE *, fp, + char *, msg, + LexItem, lex_item) +{ + char buffer[41]; + int cnt = 0; + char ch; + + while ((ch = getc(fp)) != EOF && ch != '\n') + if (cnt < 40) buffer[cnt++] = ch; + buffer[cnt] = (char)0; + + if (TRACE) + fprintf(stderr, "%s %d before: '%s'\nHTGroup.c: %s (got %s)\n", + "HTGroup.c: Syntax error in rule file at line", + HTlex_line, buffer, msg, lex_verbose(lex_item)); + HTlex_line++; +} + + +PRIVATE AddressDefList *parse_address_part ARGS1(FILE *, fp) +{ + AddressDefList *address_def_list = NULL; + LexItem lex_item; + BOOL only_one = NO; + + lex_item = lex(fp); + if (lex_item == LEX_ALPH_STR || lex_item == LEX_TMPL_STR) + only_one = YES; + else if (lex_item != LEX_OPEN_PAREN || + ((lex_item = lex(fp)) != LEX_ALPH_STR && + lex_item != LEX_TMPL_STR)) { + syntax_error(fp, "Expecting a single address or '(' beginning list", + lex_item); + return NULL; + } + address_def_list = HTList_new(); + + for(;;) { + Ref *ref = (Ref*)calloc(1, sizeof(Ref)); + ref->name = NULL; + ref->translation = NULL; + StrAllocCopy(ref->name, HTlex_buffer); + + HTList_addObject(address_def_list, (void*)ref); + + if (only_one || (lex_item = lex(fp)) != LEX_ITEM_SEP) + break; + /* + ** Here lex_item == LEX_ITEM_SEP; after item separator it + ** is ok to have one or more newlines (LEX_REC_SEP) and + ** they are ignored (continuation line). + */ + do { + lex_item = lex(fp); + } while (lex_item == LEX_REC_SEP); + + if (lex_item != LEX_ALPH_STR && lex_item != LEX_TMPL_STR) { + syntax_error(fp, "Expecting an address template", lex_item); + HTList_delete(address_def_list); + address_def_list = NULL; + return NULL; + } + } + + if (!only_one && lex_item != LEX_CLOSE_PAREN) { + HTList_delete(address_def_list); + address_def_list = NULL; + syntax_error(fp, "Expecting ')' closing address list", lex_item); + return NULL; + } + return address_def_list; +} + + +PRIVATE UserDefList *parse_user_part ARGS1(FILE *, fp) +{ + UserDefList *user_def_list = NULL; + LexItem lex_item; + BOOL only_one = NO; + + lex_item = lex(fp); + if (lex_item == LEX_ALPH_STR) + only_one = YES; + else if (lex_item != LEX_OPEN_PAREN || + (lex_item = lex(fp)) != LEX_ALPH_STR) { + syntax_error(fp, "Expecting a single name or '(' beginning list", + lex_item); + return NULL; + } + user_def_list = HTList_new(); + + for (;;) { + Ref *ref = (Ref*)calloc(1, sizeof(Ref)); + ref->name = NULL; + ref->translation = NULL; + StrAllocCopy(ref->name, HTlex_buffer); + + HTList_addObject(user_def_list, (void*)ref); + + if (only_one || (lex_item = lex(fp)) != LEX_ITEM_SEP) + break; + /* + ** Here lex_item == LEX_ITEM_SEP; after item separator it + ** is ok to have one or more newlines (LEX_REC_SEP) and + ** they are ignored (continuation line). + */ + do { + lex_item = lex(fp); + } while (lex_item == LEX_REC_SEP); + + if (lex_item != LEX_ALPH_STR) { + syntax_error(fp, "Expecting user or group name", lex_item); + HTList_delete(user_def_list); + user_def_list = NULL; + return NULL; + } + } + + if (!only_one && lex_item != LEX_CLOSE_PAREN) { + HTList_delete(user_def_list); + user_def_list = NULL; + syntax_error(fp, "Expecting ')' closing user/group list", lex_item); + return NULL; + } + return user_def_list; +} + + +PRIVATE Item *parse_item ARGS1(FILE *, fp) +{ + Item *item = NULL; + UserDefList *user_def_list = NULL; + AddressDefList *address_def_list = NULL; + LexItem lex_item; + + lex_item = lex(fp); + if (lex_item == LEX_ALPH_STR || lex_item == LEX_OPEN_PAREN) { + unlex(lex_item); + user_def_list = parse_user_part(fp); + lex_item = lex(fp); + } + + if (lex_item == LEX_AT_SIGN) { + lex_item = lex(fp); + if (lex_item == LEX_ALPH_STR || lex_item == LEX_TMPL_STR || + lex_item == LEX_OPEN_PAREN) { + unlex(lex_item); + address_def_list = parse_address_part(fp); + } + else { + if (user_def_list) { + HTList_delete(user_def_list); /* @@@@ */ + user_def_list = NULL; + } + syntax_error(fp, "Expected address part (single address or list)", + lex_item); + return NULL; + } + } + else unlex(lex_item); + + if (!user_def_list && !address_def_list) { + syntax_error(fp, "Empty item not allowed", lex_item); + return NULL; + } + item = (Item*)calloc(1, sizeof(Item)); + item->user_def_list = user_def_list; + item->address_def_list = address_def_list; + return item; +} + + +PRIVATE ItemList *parse_item_list ARGS1(FILE *, fp) +{ + ItemList *item_list = HTList_new(); + Item *item; + LexItem lex_item; + + for(;;) { + if (!(item = parse_item(fp))) { + HTList_delete(item_list); /* @@@@ */ + item_list = NULL; + return NULL; + } + HTList_addObject(item_list, (void*)item); + lex_item = lex(fp); + if (lex_item != LEX_ITEM_SEP) { + unlex(lex_item); + return item_list; + } + /* + ** Here lex_item == LEX_ITEM_SEP; after item separator it + ** is ok to have one or more newlines (LEX_REC_SEP) and + ** they are ignored (continuation line). + */ + do { + lex_item = lex(fp); + } while (lex_item == LEX_REC_SEP); + unlex(lex_item); + } +} + + +PUBLIC GroupDef *HTAA_parseGroupDef ARGS1(FILE *, fp) +{ + ItemList *item_list = NULL; + GroupDef *group_def = NULL; + LexItem lex_item; + + if (!(item_list = parse_item_list(fp))) { + return NULL; + } + group_def = (GroupDef*)calloc(1, sizeof(GroupDef)); + group_def->group_name = NULL; + group_def->item_list = item_list; + + if ((lex_item = lex(fp)) != LEX_REC_SEP) { + syntax_error(fp, "Garbage after group definition", lex_item); + } + + return group_def; +} + + +PRIVATE GroupDef *parse_group_decl ARGS1(FILE *, fp) +{ + char *group_name = NULL; + GroupDef *group_def = NULL; + LexItem lex_item; + + do { + lex_item = lex(fp); + } while (lex_item == LEX_REC_SEP); /* Ignore empty lines */ + + if (lex_item != LEX_ALPH_STR) { + if (lex_item != LEX_EOF) + syntax_error(fp, "Expecting group name", lex_item); + return NULL; + } + StrAllocCopy(group_name, HTlex_buffer); + + if (LEX_FIELD_SEP != (lex_item = lex(fp))) { + syntax_error(fp, "Expecting field separator", lex_item); + FREE(group_name); + return NULL; + } + + if (!(group_def = HTAA_parseGroupDef(fp))) { + FREE(group_name); + return NULL; + } + group_def->group_name = group_name; + + return group_def; +} + + + +/* +** Group manipulation routines +*/ + +PRIVATE GroupDef *find_group_def ARGS2(GroupDefList *, group_list, + CONST char *, group_name) +{ + if (group_list && group_name) { + GroupDefList *cur = group_list; + GroupDef *group_def; + + while (NULL != (group_def = (GroupDef*)HTList_nextObject(cur))) { + if (!strcmp(group_name, group_def->group_name)) { + return group_def; + } + } + } + return NULL; +} + + +PUBLIC void HTAA_resolveGroupReferences ARGS2(GroupDef *, group_def, + GroupDefList *, group_def_list) +{ + if (group_def && group_def->item_list && group_def_list) { + ItemList *cur1 = group_def->item_list; + Item *item; + + while (NULL != (item = (Item*)HTList_nextObject(cur1))) { + UserDefList *cur2 = item->user_def_list; + Ref *ref; + + while (NULL != (ref = (Ref*)HTList_nextObject(cur2))) + ref->translation = find_group_def(group_def_list, ref->name); + + /* Does NOT translate address_def_list */ + } + } +} + + +PRIVATE void add_group_def ARGS2(GroupDefList *, group_def_list, + GroupDef *, group_def) +{ + HTAA_resolveGroupReferences(group_def, group_def_list); + HTList_addObject(group_def_list, (void*)group_def); +} + + +PRIVATE GroupDefList *parse_group_file ARGS1(FILE *, fp) +{ + GroupDefList *group_def_list = HTList_new(); + GroupDef *group_def; + + while (NULL != (group_def = parse_group_decl(fp))) + add_group_def(group_def_list, group_def); + + return group_def_list; +} + + +/* +** Trace functions +*/ + +PRIVATE void print_item ARGS1(Item *, item) +{ + if (!item) + fprintf(stderr, "\tNULL-ITEM\n"); + else { + UserDefList *cur1 = item->user_def_list; + AddressDefList *cur2 = item->address_def_list; + Ref *user_ref = (Ref*)HTList_nextObject(cur1); + Ref *addr_ref = (Ref*)HTList_nextObject(cur2); + + if (user_ref) { + fprintf(stderr, "\t[%s%s", user_ref->name, + (user_ref->translation ? "*REF*" : "")); + while (NULL != (user_ref = (Ref*)HTList_nextObject(cur1))) + fprintf(stderr, "; %s%s", user_ref->name, + (user_ref->translation ? "*REF*" : "")); + fprintf(stderr, "] "); + } else fprintf(stderr, "\tANYBODY "); + + if (addr_ref) { + fprintf(stderr, "@ [%s", addr_ref->name); + while (NULL != (addr_ref = (Ref*)HTList_nextObject(cur2))) + fprintf(stderr, "; %s", addr_ref->name); + fprintf(stderr, "]\n"); + } else fprintf(stderr, "@ ANYADDRESS\n"); + } +} + + +PRIVATE void print_item_list ARGS1(ItemList *, item_list) +{ + ItemList *cur = item_list; + Item *item; + + if (!item_list) + fprintf(stderr, "EMPTY"); + else while (NULL != (item = (Item*)HTList_nextObject(cur))) + print_item(item); +} + + +PUBLIC void HTAA_printGroupDef ARGS1(GroupDef *, group_def) +{ + if (!group_def) { + fprintf(stderr, "\nNULL RECORD\n"); + return; + } + + fprintf(stderr, "\nGroup %s:\n", + (group_def->group_name ? group_def->group_name : "NULL")); + + print_item_list(group_def->item_list); + fprintf(stderr, "\n"); +} + + +PRIVATE void print_group_def_list ARGS1(GroupDefList *, group_list) +{ + GroupDefList *cur = group_list; + GroupDef *group_def; + + while (NULL != (group_def = (GroupDef*)HTList_nextObject(cur))) + HTAA_printGroupDef(group_def); +} + + + +/* +** IP address template matching +*/ + +/* PRIVATE part_match() +** MATCH ONE PART OF INET ADDRESS AGAIST +** A PART OF MASK (inet address has 4 parts) +** ON ENTRY: +** tcur pointer to the beginning of template part. +** icur pointer to the beginning of actual inet +** number part. +** +** ON EXIT: +** returns YES, if match. +*/ +PRIVATE BOOL part_match ARGS2(CONST char *, tcur, + CONST char *, icur) +{ + char required[4]; + char actual[4]; + CONST char *cur; + int cnt; + + if (!tcur || !icur) return NO; + + cur=tcur; + cnt=0; + while (cnt < 3 && *cur && *cur != '.') + required[cnt++] = *(cur++); + required[cnt] = (char)0; + + cur=icur; + cnt=0; + while (cnt < 3 && *cur && *cur != '.') + actual[cnt++] = *(cur++); + actual[cnt] = (char)0; + + if (TRACE) { + BOOL status = HTAA_templateMatch(required, actual); + fprintf(stderr, "part_match: req: '%s' act: '%s' match: %s\n", + required, actual, (status ? "yes" : "no")); + return status; + } + + return HTAA_templateMatch(required, actual); +} + + + +/* PRIVATE ip_number_match() +** MATCH INET NUMBER AGAINST AN INET NUMBER MASK +** ON ENTRY: +** template mask to match agaist, e.g. 128.141.*.* +** inet_addr actual inet address, e.g. 128.141.201.74 +** +** ON EXIT: +** returns YES, if match; NO, if not. +*/ +PRIVATE BOOL ip_number_match ARGS2(CONST char *, template, + CONST char *, inet_addr) +{ + CONST char *tcur = template; + CONST char *icur = inet_addr; + int cnt; + + for (cnt=0; cnt<4; cnt++) { + if (!tcur || !icur || !part_match(tcur, icur)) + return NO; + if (NULL != (tcur = strchr(tcur, '.'))) tcur++; + if (NULL != (icur = strchr(icur, '.'))) icur++; + } + return YES; +} + + + +/* PRIVATE is_domain_mask() +** DETERMINE IF A GIVEN MASK IS A +** DOMAIN NAME MASK OR AN INET NUMBER MASK +** ON ENTRY: +** mask either a domain name mask, +** e.g. +** *.cern.ch +** +** or an inet number mask, +** e.g. +** 128.141.*.* +** +** ON EXIT: +** returns YES, if mask is a domain name mask. +** NO, if it is an inet number mask. +*/ +PRIVATE BOOL is_domain_mask ARGS1(CONST char *, mask) +{ + CONST char *cur = mask; + + if (!mask) return NO; + + while (*cur) { + if (*cur != '.' && *cur != '*' && (*cur < '0' || *cur > '9')) + return YES; /* Even one non-digit makes it a domain name mask */ + cur++; + } + return NO; /* All digits and dots, so it is an inet number mask */ +} + + + +/* PRIVATE ip_mask_match() +** MATCH AN IP NUMBER MASK OR IP NAME MASK +** AGAINST ACTUAL IP NUMBER OR IP NAME +** +** ON ENTRY: +** mask mask. Mask may be either an inet number +** mask or a domain name mask, +** e.g. +** 128.141.*.* +** or +** *.cern.ch +** +** ip_number IP number of connecting host. +** ip_name IP name of the connecting host. +** +** ON EXIT: +** returns YES, if hostname/internet number +** matches the mask. +** NO, if no match (no fire). +*/ +PRIVATE BOOL ip_mask_match ARGS3(CONST char *, mask, + CONST char *, ip_number, + CONST char *, ip_name) +{ + if (mask && (ip_number || ip_name)) { + if (is_domain_mask(mask)) { + if (HTAA_templateMatch(mask, ip_name)) + return YES; + } + else { + if (ip_number_match(mask, ip_number)) + return YES; + } + } + return NO; +} + + + + +PRIVATE BOOL ip_in_def_list ARGS3(AddressDefList *, address_def_list, + char *, ip_number, + char *, ip_name) +{ + if (address_def_list && (ip_number || ip_name)) { + AddressDefList *cur = address_def_list; + Ref *ref; + + while (NULL != (ref = (Ref*)HTList_nextObject(cur))) { + /* Value of ref->translation is ignored, i.e. */ + /* no recursion for ip address tamplates. */ + if (ip_mask_match(ref->name, ip_number, ip_name)) + return YES; + } + } + return NO; +} + + +/* +** Group file cached reading +*/ + +typedef struct { + char * group_filename; + GroupDefList * group_list; +} GroupCache; + +typedef HTList GroupCacheList; + +PRIVATE GroupCacheList *group_cache_list = NULL; + + +PUBLIC GroupDefList *HTAA_readGroupFile ARGS1(CONST char *, filename) +{ + FILE *fp; + GroupCache *group_cache; + + if (!filename || !*filename) return NULL; + + if (!group_cache_list) + group_cache_list = HTList_new(); + else { + GroupCacheList *cur = group_cache_list; + + while (NULL != (group_cache = (GroupCache*)HTList_nextObject(cur))) { + if (!strcmp(filename, group_cache->group_filename)) { + if (TRACE) fprintf(stderr, "%s '%s' %s\n", + "HTAA_readGroupFile: group file", + filename, "already found in cache"); + return group_cache->group_list; + } /* if cache match */ + } /* while cached files remain */ + } /* cache exists */ + + if (TRACE) fprintf(stderr, "HTAA_readGroupFile: reading group file `%s'\n", + filename); + + if (!(fp = fopen(filename, "r"))) { + if (TRACE) fprintf(stderr, "%s '%s'\n", + "HTAA_readGroupFile: unable to open group file", + filename); + return NULL; + } + + if (!(group_cache = (GroupCache*)calloc(1, sizeof(GroupCache)))) + outofmem(__FILE__, "HTAA_readGroupFile"); + + group_cache->group_filename = NULL; + StrAllocCopy(group_cache->group_filename, filename); + group_cache->group_list = parse_group_file(fp); + HTList_addObject(group_cache_list, (void*)group_cache); + fclose(fp); + + if (TRACE) { + fprintf(stderr, "Read group file '%s', results follow:\n", filename); + print_group_def_list(group_cache->group_list); + } + + return group_cache->group_list; +} + + +/* PUBLIC HTAA_userAndInetInGroup() +** CHECK IF USER BELONGS TO TO A GIVEN GROUP +** AND THAT THE CONNECTION COMES FROM AN +** ADDRESS THAT IS ALLOWED BY THAT GROUP +** ON ENTRY: +** group the group definition structure. +** username connecting user. +** ip_number browser host IP number, optional. +** ip_name browser host IP name, optional. +** However, one of ip_number or ip_name +** must be given. +** ON EXIT: +** returns HTAA_IP_MASK, if IP address mask was +** reason for failing. +** HTAA_NOT_MEMBER, if user does not belong +** to the group. +** HTAA_OK if both IP address and user are ok. +*/ +PUBLIC HTAAFailReasonType HTAA_userAndInetInGroup ARGS4(GroupDef *, group, + char *, username, + char *, ip_number, + char *, ip_name) +{ + HTAAFailReasonType reason = HTAA_NOT_MEMBER; + + if (group && username) { + ItemList *cur1 = group->item_list; + Item *item; + + while (NULL != (item = (Item*)HTList_nextObject(cur1))) { + if (!item->address_def_list || /* Any address allowed */ + ip_in_def_list(item->address_def_list, ip_number, ip_name)) { + + if (!item->user_def_list) /* Any user allowed */ + return HTAA_OK; + else { + UserDefList *cur2 = item->user_def_list; + Ref *ref; + + while (NULL != (ref = (Ref*)HTList_nextObject(cur2))) { + + if (ref->translation) { /* Group, check recursively */ + reason = HTAA_userAndInetInGroup(ref->translation, + username, + ip_number,ip_name); + if (reason == HTAA_OK) + return HTAA_OK; + } + else { /* Username, check directly */ + if (username && *username && + 0==strcmp(ref->name, username)) + return HTAA_OK; + } + } /* Every user/group name in this group */ + } /* search for username */ + } /* IP address ok */ + else { + reason = HTAA_IP_MASK; + } + } /* while items in group */ + } /* valid parameters */ + + return reason; /* No match, or invalid parameters */ +} + + +PUBLIC void GroupDef_delete ARGS1(GroupDef *, group_def) +{ + if (group_def) { + FREE(group_def->group_name); + if (group_def->item_list) { + HTList_delete(group_def->item_list); /* @@@@ */ + group_def->item_list = NULL; + } + FREE(group_def); + } +} + diff --git a/WWW/Library/Implementation/HTGroup.h b/WWW/Library/Implementation/HTGroup.h new file mode 100644 index 00000000..496a5077 --- /dev/null +++ b/WWW/Library/Implementation/HTGroup.h @@ -0,0 +1,189 @@ +/* GROUP FILE ROUTINES + + */ + +#ifndef HTGROUP_H +#define HTGROUP_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTList.h" + +#ifdef SHORT_NAMES +#define HTAApGrD HTAA_parseGroupDef +#define HTAArGrR HTAA_resolveGroupReferences +#define HTAApGrD HTAA_printGroupDef +#define HTAAGD_d GroupDef_delete +#define HTAAuIIG HTAA_userAndInetInGroup +#endif /* SHORT_NAMES */ + +typedef HTList GroupDefList; +typedef HTList ItemList; + +typedef struct { + char * group_name; + ItemList * item_list; +} GroupDef; + + +/* +** Access Authorization failure reasons +*/ +typedef enum { + HTAA_OK, /* 200 OK */ + HTAA_OK_GATEWAY, /* 200 OK, acting as a gateway */ + HTAA_NO_AUTH, /* 401 Unauthorized, not authenticated */ + HTAA_NOT_MEMBER, /* 401 Unauthorized, not authorized */ + HTAA_IP_MASK, /* 403 Forbidden by IP mask */ + HTAA_BY_RULE, /* 403 Forbidden by rule */ + HTAA_NO_ACL, /* 403 Forbidden, ACL non-existent */ + HTAA_NO_ENTRY, /* 403 Forbidden, no ACL entry */ + HTAA_SETUP_ERROR, /* 403 Forbidden, server setup error */ + HTAA_DOTDOT, /* 403 Forbidden, URL with /../ illegal */ + HTAA_HTBIN, /* 403 Forbidden, /htbin not enabled */ + HTAA_NOT_FOUND /* 404 Not found, or read protected */ +} HTAAFailReasonType; + +/* + +Group definition grammar + + string + "sequence of alphanumeric characters" + + user_name + string + + group_name + string + + group_ref + group_name + + user_def + user_name | group_ref + + user_def_list + user_def { ',' user_def } + + user_part + user_def | '(' user_def_list ')' + + templ + + "sequence of alphanumeric characters and '*'s" + + ip_number_mask + templ '.' templ '.' templ '.' templ + + domain_name_mask + templ { '.' templ } + + address + + ip_number_mask | domain_name_mask + + address_def + + address + + address_def_list + address_def { ',' address_def } + + address_part + address_def | '(' address_def_list ')' + + item + [user_part] ['@' address_part] + + item_list + item { ',' item } + + group_def + item_list + + group_decl + group_name ':' group_def + + PARSE GROUP DEFINITION + + */ + +PUBLIC GroupDef *HTAA_parseGroupDef PARAMS((FILE * fp)); +/* + +Fill in Pointers to referenced Group Definitions in a Group Definition + + References to groups (by their name) are resolved from group_def_list and pointers to + those structures are added to group_def. + + */ + +PUBLIC void HTAA_resolveGroupReferences PARAMS((GroupDef * group_def, + GroupDefList * group_def_list)); +/* + +Read Group File (and do caching) + + If group file is already in cache returns a pointer to previously read group definition + list. + + */ + +PUBLIC GroupDefList *HTAA_readGroupFile PARAMS((CONST char * filename)); +/* + +Delete Group Definition + + Groups in cache should never be freed by this function. This should only be used to + free group definitions read by HTAA_parseGroupDef. + + */ + +PUBLIC void GroupDef_delete PARAMS((GroupDef * group_def)); +/* + +Print Out Group Definition (for trace purposes) + + */ + +PUBLIC void HTAA_printGroupDef PARAMS((GroupDef * group_def)); +/* + +Does a User Belong to a Given Set of Groups + + This function checks both the username and the internet address. + + */ + +/* PUBLIC HTAA_userAndInetInGroup() +** CHECK IF USER BELONGS TO TO A GIVEN GROUP +** AND THAT THE CONNECTION COMES FROM AN +** ADDRESS THAT IS ALLOWED BY THAT GROUP +** ON ENTRY: +** group the group definition structure. +** username connecting user. +** ip_number browser host IP number, optional. +** ip_name browser host IP name, optional. +** However, one of ip_number or ip_name +** must be given. +** ON EXIT: +** returns HTAA_IP_MASK, if IP address mask was +** reason for failing. +** HTAA_NOT_MEMBER, if user does not belong +** to the group. +** HTAA_OK if both IP address and user are ok. +*/ +PUBLIC HTAAFailReasonType HTAA_userAndInetInGroup PARAMS((GroupDef * group, + char * username, + char * ip_number, + char * ip_name)); +/* + + */ + +#endif /* not HTGROUP_H */ +/* + + End of file HTGroup.h. */ diff --git a/WWW/Library/Implementation/HTHistory.c b/WWW/Library/Implementation/HTHistory.c new file mode 100644 index 00000000..726380a6 --- /dev/null +++ b/WWW/Library/Implementation/HTHistory.c @@ -0,0 +1,157 @@ +#include "HTUtils.h" +#include "tcp.h" /* for standard io */ + +#include "HTHistory.h" + +#include "LYLeaks.h" + +static HTList * history; /* List of visited anchors */ + + +/* Navigation +** ========== +*/ + +/* Record the jump to an anchor +** ---------------------------- +*/ + +void HTHistory_record + ARGS1 (HTAnchor *,destination) +{ + if (destination) { + if (! history) + history = HTList_new(); + HTList_addObject (history, destination); + } +} + +/* Go back in history (find the last visited node) +** ------------------ +*/ + +HTAnchor * HTHistory_backtrack + NOARGS /* FIXME: Should we add a `sticky' option ? */ +{ + if (HTHistory_canBacktrack()) + HTList_removeLastObject(history); + return(HTAnchor *)HTList_lastObject(history); /* is Home if can't backtrack */ +} + +BOOL HTHistory_canBacktrack + NOARGS +{ + return (HTList_objectAt (history, 1) != NULL); +} + +/* Browse through references in the same parent node +** ------------------------------------------------- +** +** Take the n-th child's link after or before the one we took to get here. +** Positive offset means go towards most recently added children. +*/ + +HTAnchor * HTHistory_moveBy + ARGS1 (int,offset) +{ + HTAnchor * last = (HTAnchor *)HTList_objectAt (history, 1); + if (! last) + return NULL; /* No last visited node */ + if (last != (HTAnchor *) last->parent) { /* Was a child */ + HTList * kids = last->parent->children; + int i = HTList_indexOf (kids, last); + HTAnchor * nextOne = (HTAnchor *)HTList_objectAt (kids, i - offset); + if (nextOne) { + HTAnchor * destination = HTAnchor_followMainLink (nextOne); + if (destination) { + HTList_removeLastObject (history); + HTList_removeLastObject (history); + HTList_addObject (history, nextOne); + HTList_addObject (history, destination); + } + return destination; + } else { + if (TRACE) fprintf(stderr, + "HTHistory_moveBy: offset by %+d goes out of list %p.\n", + offset, (void*)kids); + return NULL; + } + } else { /* Was a parent */ + return NULL; /* FIXME we could possibly follow the next link... */ + } +} + +BOOL HTHistory_canMoveBy + ARGS1 (int,offset) +{ + HTAnchor * last = (HTAnchor *)HTList_objectAt (history, 1); + if (! last) + return NO; /* No last visited node */ + if (last != (HTAnchor *) last->parent) { /* Was a child */ + HTList * kids = last->parent->children; + int i = HTList_indexOf (kids, last); + return (HTList_objectAt (kids, i - offset) != NULL); + } else { /* Was a parent */ + return NO; /* FIXME we could possibly follow the next link... */ + } +} + + +/* Retrieval +** ========= +*/ + +/* Read numbered visited anchor (1 is the oldest) +** ---------------------------- +*/ + +HTAnchor * HTHistory_read + ARGS1 (int,number) +{ + return (HTAnchor *)HTList_objectAt(history, HTList_count (history) - number); +} + + +/* Recall numbered visited anchor (1 is the oldest) +** ------------------------------ +** This reads the anchor and stores it again in the list, except if last. +*/ + +HTAnchor * HTHistory_recall + ARGS1 (int,number) +{ + HTAnchor * destination = + (HTAnchor *)HTList_objectAt (history, HTList_count (history) - number); + if (destination && destination != (HTAnchor *)HTList_lastObject (history)) + HTList_addObject (history, destination); + return destination; +} + +/* Number of Anchors stored +** ------------------------ +** +** This is needed in order to check the validity of certain commands +** for menus, etc. +(not needed for now. Use canBacktrack, etc.) +int HTHistory_count + NOARGS +{ + return HTList_count (history); +} +*/ + +/* Change last history entry +** ------------------------- +** +** Sometimes we load a node by one anchor but leave by a different +** one, and it is the one we left from which we want to remember. +*/ + +void HTHistory_leavingFrom + ARGS1 (HTAnchor *,anchor) +{ + if (HTList_removeLastObject (history)) + HTList_addObject (history, anchor); + else + if (TRACE) fprintf(stderr, "HTHistory_leavingFrom: empty history !\n"); +} diff --git a/WWW/Library/Implementation/HTHistory.h b/WWW/Library/Implementation/HTHistory.h new file mode 100644 index 00000000..a93781e9 --- /dev/null +++ b/WWW/Library/Implementation/HTHistory.h @@ -0,0 +1,112 @@ +/* */ + +#ifndef HTHISTORY_H +#define HTHISTORY_H + +#include "HTAnchor.h" + +#ifdef SHORT_NAMES +#define HTHistory_record HTHiReco +#define HTHistory_backtrack HTHiBack +#define HTHistory_canBacktrack HTHiCaBa +#define HTHistory_moveBy HTHiMoBy +#define HTHistory_canMoveBy HTHiCaMo +#define HTHistory_read HTHiRead +#define HTHistory_recall HTHiReca +#define HTHistory_count HTHiCoun +#define HTHistory_leavingFrom HTHiLeFr +#endif + +/* Navigation +** ========== +*/ + +/* Record the jump to an anchor +** ---------------------------- +*/ + +extern void HTHistory_record + PARAMS( + (HTAnchor * destination) + ); + +/* Go back in history (find the last visited node) +** ------------------ +*/ + +extern HTAnchor * HTHistory_backtrack + NOPARAMS; /* FIXME: Should we add a `sticky' option ? */ + +extern BOOL HTHistory_canBacktrack + NOPARAMS; + +/* Browse through references in the same parent node +** ------------------------------------------------- +** +** Take the n-th child's link after or before the one we took to get here. +** Positive offset means go towards most recently added children. +*/ + +extern HTAnchor * HTHistory_moveBy + PARAMS( + (int offset) + ); + +extern BOOL HTHistory_canMoveBy + PARAMS( + (int offset) + ); + +#define HTHistory_next (HTHistory_moveBy (+1)) +#define HTHistory_canNext (HTHistory_canMoveBy (+1)) +#define HTHistory_previous (HTHistory_moveBy (-1)) +#define HTHistory_canPrevious (HTHistory_canMoveBy (-1)) + + +/* Retrieval +** ========= +*/ + +/* Read numbered visited anchor (1 is the oldest) +** ---------------------------- +*/ + +extern HTAnchor * HTHistory_read + PARAMS( + (int number) + ); + +/* Recall numbered visited anchor (1 is the oldest) +** ------------------------------ +** This reads the anchor and stores it again in the list, except if last. +*/ + +extern HTAnchor * HTHistory_recall + PARAMS( + (int number) + ); + +/* Number of Anchors stored +** ------------------------ +** +** This is needed in order to check the validity of certain commands +** for menus, etc. +(not needed for now. Use canBacktrack, etc.) +extern int HTHistory_count NOPARAMS; +*/ + +/* Change last history entry +** ------------------------- +** +** Sometimes we load a node by one anchor but leave by a different +** one, and it is the one we left from which we want to remember. +*/ +extern void HTHistory_leavingFrom + PARAMS( + (HTAnchor * anchor) + ); + +#endif /* HTHISTORY_H */ +/* + + */ diff --git a/WWW/Library/Implementation/HTInit.c b/WWW/Library/Implementation/HTInit.c new file mode 100644 index 00000000..764da7b5 --- /dev/null +++ b/WWW/Library/Implementation/HTInit.c @@ -0,0 +1,176 @@ +/* Configuration-specific Initialialization HTInit.c +** ---------------------------------------- +*/ + +/* Define a basic set of suffixes and presentations +** ------------------------------------------------ +** +*/ + +#include "HTUtils.h" + +/* Implements: +*/ +#include "HTInit.h" + +#include "HTML.h" +#include "HTPlain.h" +#include "HTMLGen.h" +#include "HTFile.h" +#include "HTFormat.h" +#include "HTMIME.h" +#include "HTWSRC.h" +#include "HTFWriter.h" + +#include "LYLeaks.h" + +PUBLIC void HTFormatInit NOARGS +{ +#ifdef NeXT + HTSetPresentation("application/postscript", "open %s", 1.0, 2.0, 0.0, 0); + /* The following needs the GIF previewer -- you might not have it. */ + HTSetPresentation("image/gif", "open %s", 0.3, 2.0, 0.0, 0); + HTSetPresentation("image/x-tiff", "open %s", 1.0, 2.0, 0.0, 0); + HTSetPresentation("audio/basic", "open %s", 1.0, 2.0, 0.0, 0); + HTSetPresentation("*", "open %s", 1.0, 0.0, 0.0, 0); +#else + if (getenv("DISPLAY")) { /* Must have X11 */ + HTSetPresentation("application/postscript", "ghostview %s", + 1.0, 3.0, 0.0, 0); + HTSetPresentation("image/gif", "xv %s", 1.0, 3.0, 0.0, 0); + HTSetPresentation("image/x-tiff", "xv %s", 1.0, 3.0, 0.0, 0); + HTSetPresentation("image/jpeg", "xv %s", 1.0, 3.0, 0.0, 0); + } +#endif + HTSetConversion("www/mime", "*", HTMIMEConvert, + 1.0, 0.0, 0.0, 0); + HTSetConversion("application/x-wais-source","*", HTWSRCConvert, + 1.0, 0.0, 0.0, 0); + HTSetConversion("text/html", "text/x-c", HTMLToC, + 0.5, 0.0, 0.0, 0); + HTSetConversion("text/html", "text/plain", HTMLToPlain, + 0.5, 0.0, 0.0, 0); + HTSetConversion("text/html", "www/present", HTMLPresent, + 1.0, 0.0, 0.0, 0); + HTSetConversion("text/plain", "text/html", HTPlainToHTML, + 1.0, 0.0, 0.0, 0); + HTSetConversion("text/plain", "www/present", HTPlainPresent, + 1.0, 0.0, 0.0, 0); + HTSetConversion("application/octet-stream", "www/present", HTSaveLocally, + 0.1, 0.0, 0.0, 0); + HTSetConversion("www/unknown", "www/present", HTSaveLocally, + 0.3, 0.0, 0.0, 0); + HTSetConversion("www/source", "www/present", HTSaveLocally, + 0.3, 0.0, 0.0, 0); +} + + + +/* Define a basic set of suffixes +** ------------------------------ +** +** The LAST suffix for a type is that used for temporary files +** of that type. +** The quality is an apriori bias as to whether the file should be +** used. Not that different suffixes can be used to represent files +** which are of the same format but are originals or regenerated, +** with different values. +*/ + +#ifndef NO_INIT +PUBLIC void HTFileInit NOARGS +{ + /* Suffix Contenet-Type Content-Encoding Quality */ + + HTSetSuffix(".mime", "www/mime", "8bit", 1.0); /* Internal -- MIME is */ + /* not recursive */ + HTSetSuffix(".bin", "application/octet-stream", "binary", 1.0); /* Uninterpreted binary */ + HTSetSuffix(".oda", "application/oda", "binary", 1.0); + HTSetSuffix(".pdf", "application/pdf", "binary", 1.0); + HTSetSuffix(".ai", "application/postscript", "8bit", 0.5); /* Adobe Illustrator */ + HTSetSuffix(".PS", "application/postscript", "8bit", 0.8); /* PostScript */ + HTSetSuffix(".eps", "application/postscript", "8bit", 0.8); + HTSetSuffix(".ps", "application/postscript", "8bit", 0.8); + HTSetSuffix(".rtf", "application/x-rtf", "7bit", 1.0); /* RTF */ + HTSetSuffix(".Z", "application/x-compressed", "binary", 1.0); /* Compressed data */ + HTSetSuffix(".csh", "application/x-csh", "7bit", 0.5); /* C-shell script */ + HTSetSuffix(".dvi", "application/x-dvi", "binary", 1.0); /* TeX DVI */ + HTSetSuffix(".hdf", "application/x-hdf", "binary", 1.0); /* NCSA HDF data file */ + HTSetSuffix(".latex", "application/x-latex", "8bit", 1.0); /* LaTeX source */ + HTSetSuffix(".nc", "application/x-netcdf", "binary", 1.0); /* Unidata netCDF data */ + HTSetSuffix(".cdf", "application/x-netcdf", "binary", 1.0); + HTSetSuffix(".sh", "application/x-sh", "7bit", 0.5); /* Shell-script */ + HTSetSuffix(".tcl", "application/x-tcl", "7bit", 0.5); /* TCL-script */ + HTSetSuffix(".tex", "application/x-tex", "8bit", 1.0); /* TeX source */ + HTSetSuffix(".texi", "application/x-texinfo", "7bit", 1.0); /* Texinfo */ + HTSetSuffix(".texinfo","application/x-texinfo", "7bit", 1.0); + HTSetSuffix(".t", "application/x-troff", "7bit", 0.5); /* Troff */ + HTSetSuffix(".roff", "application/x-troff", "7bit", 0.5); + HTSetSuffix(".tr", "application/x-troff", "7bit", 0.5); + HTSetSuffix(".man", "application/x-troff-man", "7bit", 0.5); /* Troff with man macros*/ + HTSetSuffix(".me", "application/x-troff-me", "7bit", 0.5); /* Troff with me macros */ + HTSetSuffix(".ms", "application/x-troff-ms", "7bit", 0.5); /* Troff with ms macros */ + HTSetSuffix(".src", "application/x-wais-source", "7bit", 1.0); /* WAIS source */ + HTSetSuffix(".zip", "application/zip", "binary", 1.0); /* PKZIP */ + HTSetSuffix(".bcpio", "application/x-bcpio", "binary", 1.0); /* Old binary CPIO */ + HTSetSuffix(".cpio", "application/x-cpio", "binary", 1.0); /* POSIX CPIO */ + HTSetSuffix(".gtar", "application/x-gtar", "binary", 1.0); /* Gnu tar */ + HTSetSuffix(".shar", "application/x-shar", "8bit", 1.0); /* Shell archive */ + HTSetSuffix(".sv4cpio","application/x-sv4cpio", "binary", 1.0); /* SVR4 CPIO */ + HTSetSuffix(".sv4crc", "application/x-sv4crc", "binary", 1.0); /* SVR4 CPIO with CRC */ + HTSetSuffix(".tar", "application/x-tar", "binary", 1.0); /* 4.3BSD tar */ + HTSetSuffix(".ustar", "application/x-ustar", "binary", 1.0); /* POSIX tar */ + HTSetSuffix(".snd", "audio/basic", "binary", 1.0); /* Audio */ + HTSetSuffix(".au", "audio/basic", "binary", 1.0); + HTSetSuffix(".aiff", "audio/x-aiff", "binary", 1.0); + HTSetSuffix(".aifc", "audio/x-aiff", "binary", 1.0); + HTSetSuffix(".aif", "audio/x-aiff", "binary", 1.0); + HTSetSuffix(".wav", "audio/x-wav", "binary", 1.0); /* Windows+ WAVE format */ + HTSetSuffix(".gif", "image/gif", "binary", 1.0); /* GIF */ + HTSetSuffix(".ief", "image/ief", "binary", 1.0); /* Image Exchange fmt */ + HTSetSuffix(".jpg", "image/jpeg", "binary", 1.0); /* JPEG */ + HTSetSuffix(".JPG", "image/jpeg", "binary", 1.0); + HTSetSuffix(".JPE", "image/jpeg", "binary", 1.0); + HTSetSuffix(".jpe", "image/jpeg", "binary", 1.0); + HTSetSuffix(".JPEG", "image/jpeg", "binary", 1.0); + HTSetSuffix(".jpeg", "image/jpeg", "binary", 1.0); + HTSetSuffix(".tif", "image/tiff", "binary", 1.0); /* TIFF */ + HTSetSuffix(".tiff", "image/tiff", "binary", 1.0); + HTSetSuffix(".ras", "image/cmu-raster", "binary", 1.0); + HTSetSuffix(".pnm", "image/x-portable-anymap", "binary", 1.0); /* PBM Anymap format */ + HTSetSuffix(".pbm", "image/x-portable-bitmap", "binary", 1.0); /* PBM Bitmap format */ + HTSetSuffix(".pgm", "image/x-portable-graymap", "binary", 1.0); /* PBM Graymap format */ + HTSetSuffix(".ppm", "image/x-portable-pixmap", "binary", 1.0); /* PBM Pixmap format */ + HTSetSuffix(".rgb", "image/x-rgb", "binary", 1.0); + HTSetSuffix(".xbm", "image/x-xbitmap", "binary", 1.0); /* X bitmap */ + HTSetSuffix(".xpm", "image/x-xpixmap", "binary", 1.0); /* X pixmap format */ + HTSetSuffix(".xwd", "image/x-xwindowdump", "binary", 1.0); /* X window dump (xwd) */ + HTSetSuffix(".html", "text/html", "8bit", 1.0); /* HTML */ + HTSetSuffix(".c", "text/plain", "7bit", 0.5); /* C source */ + HTSetSuffix(".h", "text/plain", "7bit", 0.5); /* C headers */ + HTSetSuffix(".C", "text/plain", "7bit", 0.5); /* C++ source */ + HTSetSuffix(".cc", "text/plain", "7bit", 0.5); /* C++ source */ + HTSetSuffix(".hh", "text/plain", "7bit", 0.5); /* C++ headers */ + HTSetSuffix(".m", "text/plain", "7bit", 0.5); /* Objective-C source */ + HTSetSuffix(".f90", "text/plain", "7bit", 0.5); /* Fortran 90 source */ + HTSetSuffix(".txt", "text/plain", "7bit", 0.5); /* Plain text */ + HTSetSuffix(".rtx", "text/richtext", "7bit", 1.0); /* MIME Richtext format */ + HTSetSuffix(".tsv", "text/tab-separated-values", "7bit", 1.0); /* Tab-separated values */ + HTSetSuffix(".etx", "text/x-setext", "7bit", 0.9); /* Struct Enchanced Txt */ + HTSetSuffix(".MPG", "video/mpeg", "binary", 1.0); /* MPEG */ + HTSetSuffix(".mpg", "video/mpeg", "binary", 1.0); + HTSetSuffix(".MPE", "video/mpeg", "binary", 1.0); + HTSetSuffix(".mpe", "video/mpeg", "binary", 1.0); + HTSetSuffix(".MPEG", "video/mpeg", "binary", 1.0); + HTSetSuffix(".mpeg", "video/mpeg", "binary", 1.0); + HTSetSuffix(".qt", "video/quicktime", "binary", 1.0); /* QuickTime */ + HTSetSuffix(".mov", "video/quicktime", "binary", 1.0); + HTSetSuffix(".avi", "video/x-msvideo", "binary", 1.0); /* MS Video for Windows */ + HTSetSuffix(".movie", "video/x-sgi-movie", "binary", 1.0); /* SGI "moviepalyer" */ + + HTSetSuffix("*.*", "application/octet-stream", "binary", 0.1); + HTSetSuffix("*", "text/plain", "7bit", 0.5); + +} +#endif /* NO_INIT */ + diff --git a/WWW/Library/Implementation/HTInit.h b/WWW/Library/Implementation/HTInit.h new file mode 100644 index 00000000..b11e7238 --- /dev/null +++ b/WWW/Library/Implementation/HTInit.h @@ -0,0 +1,22 @@ +/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTInit.html + INITIALISATION MODULE + + This module resisters all the plug & play software modules which will be used in the + program. This is for a browser. + + To override this, just copy it and link in your version befoe you link with the + library. + + Implemented by HTInit.c by default. + + */ +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ + +extern void HTFormatInit NOPARAMS; +extern void HTFileInit NOPARAMS; + +/* + + */ diff --git a/WWW/Library/Implementation/HTLex.c b/WWW/Library/Implementation/HTLex.c new file mode 100644 index 00000000..26870170 --- /dev/null +++ b/WWW/Library/Implementation/HTLex.c @@ -0,0 +1,142 @@ + +/* MODULE HTLex.c +** LEXICAL ANALYSOR +** +** AUTHORS: +** AL Ari Luotonen luotonen@dxcern.cern.ch +** +** HISTORY: +** +** +** BUGS: +** +** +*/ + +#include "HTUtils.h" +#include "HTAAUtil.h" +#include "HTLex.h" /* Implemented here */ + +#include "LYLeaks.h" + +/* +** Global variables +*/ +PUBLIC char HTlex_buffer[40]; /* Read lexical string */ +PUBLIC int HTlex_line = 1; /* Line number in source file */ + + +/* +** Module-wide variables +*/ +PRIVATE int lex_cnt; +PRIVATE BOOL lex_template; +PRIVATE LexItem lex_pushed_back = LEX_NONE; +PRIVATE FILE *cache = NULL; + + +PUBLIC void unlex ARGS1(LexItem, lex_item) +{ + lex_pushed_back = lex_item; +} + + +PUBLIC LexItem lex ARGS1(FILE *, fp) +{ + int ch; + + if (fp != cache) { /* This cache doesn't work ok because the system */ + cache = fp; /* often assign same FILE structure the next open */ + HTlex_line = 1; /* file. So, if there are syntax errors in setup */ + } /* files it may confuse things later on. */ + + if (lex_pushed_back != LEX_NONE) { + LexItem ret = lex_pushed_back; + lex_pushed_back = LEX_NONE; + return ret; + } + + lex_cnt = 0; + lex_template = NO; + + for(;;) { + switch (ch = getc(fp)) { + case EOF: + case ' ': + case '\t': + case '\r': + case '\n': + case ':': + case ',': + case '(': + case ')': + case '@': + if (lex_cnt > 0) { + if (ch != EOF) ungetc(ch,fp); + if (lex_template) return LEX_TMPL_STR; + else return LEX_ALPH_STR; + } + else switch(ch) { + case EOF: return LEX_EOF; break; + case '\n': + HTlex_line++; return LEX_REC_SEP; break; + case ':': return LEX_FIELD_SEP; break; + case ',': return LEX_ITEM_SEP; break; + case '(': return LEX_OPEN_PAREN; break; + case ')': return LEX_CLOSE_PAREN; break; + case '@': return LEX_AT_SIGN; break; + default: ; /* Leading white space ignored (SP,TAB,CR) */ + } + break; + default: + HTlex_buffer[lex_cnt++] = ch; + HTlex_buffer[lex_cnt] = '\0'; + if ('*' == ch) lex_template = YES; + } /* switch ch */ + } /* forever */ +} + + +PUBLIC char *lex_verbose ARGS1(LexItem, lex_item) +{ + static char msg[100]; + + switch (lex_item) { + case LEX_NONE: /* Internally used */ + return "NO-LEX-ITEM"; + break; + case LEX_EOF: /* End of file */ + return "end-of-file"; + break; + case LEX_REC_SEP: /* Record separator */ + return "record separator (newline)"; + break; + case LEX_FIELD_SEP: /* Field separator */ + return "field separator ':'"; + break; + case LEX_ITEM_SEP: /* List item separator */ + return "item separator ','"; + break; + case LEX_OPEN_PAREN: /* Group start tag */ + return "'('"; + break; + case LEX_CLOSE_PAREN: /* Group end tag */ + return "')'"; + break; + case LEX_AT_SIGN: /* Address qualifier */ + return "address qualifier '@'"; + break; + case LEX_ALPH_STR: /* Alphanumeric string */ + sprintf(msg, "alphanumeric string '%s'", HTlex_buffer); + return msg; + break; + case LEX_TMPL_STR: /* Template string */ + sprintf(msg, "template string '%s'", HTlex_buffer); + return msg; + break; + default: + return "UNKNOWN-LEX-ITEM"; + break; + } +} + diff --git a/WWW/Library/Implementation/HTLex.h b/WWW/Library/Implementation/HTLex.h new file mode 100644 index 00000000..5895579b --- /dev/null +++ b/WWW/Library/Implementation/HTLex.h @@ -0,0 +1,64 @@ +/* LEXICAL ANALYSOR (MAINLY FOR CONFIG FILES) + + */ + +#ifndef HTLEX_H +#define HTLEX_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ + + +#ifdef SHORT_NAMES +#define lex_verb lex_verbose +#endif /*SHORT_NAMES*/ + + +typedef enum { + LEX_NONE, /* Internally used */ + LEX_EOF, /* End of file */ + LEX_REC_SEP, /* Record separator */ + LEX_FIELD_SEP, /* Field separator */ + LEX_ITEM_SEP, /* List item separator */ + LEX_OPEN_PAREN, /* Group start tag */ + LEX_CLOSE_PAREN, /* Group end tag */ + LEX_AT_SIGN, /* Address qualifier */ + LEX_ALPH_STR, /* Alphanumeric string */ + LEX_TMPL_STR /* Template string */ +} LexItem; + +extern char HTlex_buffer[]; /* Read lexical string */ +extern int HTlex_line; /* Line number in source file */ + +/* + +Get Next Lexical Item + + If returns LEX_ALPH_STR or LEX_TMPL_STR the string is in global buffer lex_buffer. + + */ + +PUBLIC LexItem lex PARAMS((FILE * fp)); +/* + +Push Back Latest Item + + */ + +PUBLIC void unlex PARAMS((LexItem lex_item)); +/* + +Get the Name for Lexical Item + + */ + +PUBLIC char *lex_verbose PARAMS((LexItem lex_item)); +/* + + */ + +#endif /* not HTLEX_H */ +/* + + End of file HTLex.h. */ diff --git a/WWW/Library/Implementation/HTList.c b/WWW/Library/Implementation/HTList.c new file mode 100644 index 00000000..7a41e83e --- /dev/null +++ b/WWW/Library/Implementation/HTList.c @@ -0,0 +1,262 @@ +/* A small List class HTList.c +** ================== +** +** A list is represented as a sequence of linked nodes of type HTList. +** The first node is a header which contains no object. +** New nodes are inserted between the header and the rest of the list. +*/ + +#include "HTUtils.h" +#include "HTList.h" + +/*#include <stdio.h> included by HTUtils.h -- FM *//* joe@athena, TBL 921019 */ + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + + +/* Create list. +*/ +PUBLIC HTList * HTList_new NOARGS +{ + HTList *newList; + + if ((newList = (HTList *)calloc(1, sizeof(HTList))) == NULL) + outofmem(__FILE__, "HTList_new"); + + newList->object = NULL; + newList->next = NULL; + + return newList; +} + + +/* Delete list. +*/ +PUBLIC void HTList_delete ARGS1( + HTList *, me) +{ + HTList *current; + + while ((current = me)) { + me = me->next; + FREE (current); + } + + return; +} + + +/* Add object to START of list (so it is pointed to by the head). +*/ +PUBLIC void HTList_addObject ARGS2( + HTList *, me, + void *, newObject) +{ + HTList *newNode; + + if (me) { + if ((newNode = (HTList *)calloc(1, sizeof(HTList))) == NULL) + outofmem(__FILE__, "HTList_addObject"); + newNode->object = newObject; + newNode->next = me->next; + me->next = newNode; + + } else if (TRACE) { + fprintf(stderr, + "HTList: Trying to add object %p to a nonexisting list\n", + newObject); + } + + return; +} + + +/* Append object to END of list (furthest from the head). +*/ +PUBLIC void HTList_appendObject ARGS2( + HTList *, me, + void *, newObject) +{ + HTList *temp = me; + + if (temp && newObject) { + while (temp->next) + temp = temp->next; + HTList_addObject(temp, newObject); + } + + return; +} + + +/* Remove specified object from list. +*/ +PUBLIC BOOL HTList_removeObject ARGS2( + HTList *, me, + void *, oldObject) +{ + HTList *temp = me; + HTList *prevNode; + + if (temp && oldObject) { + while (temp->next) { + prevNode = temp; + temp = temp->next; + if (temp->object == oldObject) { + prevNode->next = temp->next; + FREE (temp); + return YES; /* Success */ + } + } + } + return NO; /* object not found or NULL list */ +} + + +/* Remove object at a given position in the list, where 0 is the +** object pointed to by the head (returns a pointer to the element +** (->object) for the object, and NULL if the list is empty, or +** if it doesn't exist - Yuk!). +*/ +PUBLIC void * HTList_removeObjectAt ARGS2( + HTList *, me, + int, position) +{ + HTList * temp = me; + HTList * prevNode; + int pos = position; + + if (!temp || pos < 0) + return NULL; + + prevNode = temp; + while ((temp = temp->next)) { + if (pos == 0) { + prevNode->next = temp->next; + prevNode = temp; + FREE(temp); + return prevNode->object; + } + prevNode = temp; + pos--; + } + + return NULL; /* Reached the end of the list */ +} + + +/* Remove object from START of list (the Last one inserted +** via HTList_addObject(), and pointed to by the head). +*/ +PUBLIC void * HTList_removeLastObject ARGS1( + HTList *, me) +{ + HTList * lastNode; + void * lastObject; + + if (me && me->next) { + lastNode = me->next; + lastObject = lastNode->object; + me->next = lastNode->next; + FREE (lastNode); + return lastObject; + + } else { /* Empty list */ + return NULL; + } +} + + +/* Remove object from END of list (the First one inserted +** via HTList_addObject(), and furthest from the head). +*/ +PUBLIC void * HTList_removeFirstObject ARGS1( + HTList *, me) +{ + HTList * temp = me; + HTList * prevNode; + void *firstObject; + + if (!temp) + return NULL; + + prevNode = temp; + if (temp->next) { + while (temp->next) { + prevNode = temp; + temp = temp->next; + } + firstObject = temp->object; + prevNode->next = NULL; + FREE (temp); + return firstObject; + + } else { /* Empty list */ + return NULL; + } +} + + +/* Determine total number of objects in the list, +** not counting the head. +*/ +PUBLIC int HTList_count ARGS1( + HTList *, me) +{ + HTList * temp = me; + int count = 0; + + if (temp) + while ((temp = temp->next)) + count++; + + return count; +} + + +/* Determine position of an object in the list (a value of 0 +** means it is pointed to by the head; returns -1 if not found). +*/ +PUBLIC int HTList_indexOf ARGS2( + HTList *, me, + void *, object) +{ + HTList * temp = me; + int position = 0; + + if (temp) { + while ((temp = temp->next)) { + if (temp->object == object) + return position; + position++; + } + } + + return -1; /* Object not in the list */ +} + + +/* Return pointer to the object at a specified position in the list, +** where 0 is the object pointed to by the head (returns NULL if +** the list is empty, or if it doesn't exist - Yuk!). +*/ +PUBLIC void * HTList_objectAt ARGS2( + HTList *, me, + int, position) +{ + HTList * temp = me; + int pos = position; + + if (!temp || pos < 0) + return NULL; + + while ((temp = temp->next)) { + if (pos == 0) + return temp->object; + pos--; + } + + return NULL; /* Reached the end of the list */ +} diff --git a/WWW/Library/Implementation/HTList.h b/WWW/Library/Implementation/HTList.h new file mode 100644 index 00000000..ddab85ca --- /dev/null +++ b/WWW/Library/Implementation/HTList.h @@ -0,0 +1,136 @@ + +/* List object +** +** The list object is a generic container for storing collections +** of things in order. +*/ +#ifndef HTLIST_H +#define HTLIST_H + +#ifndef HTUTILS_H +#include "HTUtils.h" /* for BOOL type and PARAMS and ARGS*/ +#endif /* HTUTILS_H */ + +typedef struct _HTList HTList; + +struct _HTList { + void * object; + HTList * next; +}; + +#ifdef SHORT_NAMES +#define HTList_new HTLiNew +#define HTList_delete HTLiDele +#define HTList_addObject HTLiAdOb +#define HTList_removeObject HTLiReOb +#define HTList_removeObjectAt HTLiReAt +#define HTList_removeLastObject HTLiReLa +#define HTList_removeFirstObject HTLiReFi +#define HTList_count HTLiCoun +#define HTList_indexOf HTLiInOf +#define HTList_objectAt HTLiObAt +#endif /* SHORT_NAMES */ + + +/* Fast macro to traverse a list. Call it first with copy of the list +** header. It returns the first object and increments the passed list +** pointer. Call it with the same variable until it returns NULL. +*/ +#define HTList_nextObject(me) \ + ((me) && ((me) = (me)->next) ? (me)->object : NULL) + + +/* Macro to find object pointed to by the head (returns NULL +** if list is empty, OR if it doesn't exist - Yuk!) +*/ +#define HTList_lastObject(me) \ + ((me) && (me)->next ? (me)->next->object : NULL) + + +/* Macro to check if a list is empty (or doesn't exist - Yuk!) +*/ +#define HTList_isEmpty(me) ((me) ? ((me)->next == NULL) : YES) + + +/* Create list. +*/ +extern HTList * HTList_new NOPARAMS; + + +/* Delete list. +*/ +extern void HTList_delete PARAMS(( + HTList * me)); + + +/* Add object to START of list (so it is pointed to by the head). +*/ +extern void HTList_addObject PARAMS(( + HTList * me, + void * newObject)); + + +/* Append object to END of list (furthest from the head). +*/ +extern void HTList_appendObject PARAMS(( + HTList * me, + void * newObject)); + + +/* Remove specified object from list. +*/ +extern BOOL HTList_removeObject PARAMS(( + HTList * me, + void * oldObject)); + + +/* Remove object at a given position in the list, where 0 is the +** object pointed to by the head (returns a pointer to the element +** (->object) for the object, and NULL if the list is empty, or +** if it doesn't exist - Yuk!). +*/ +extern void * HTList_removeObjectAt PARAMS(( + HTList * me, + int position)); + + +/* Remove object from START of list (the Last one inserted +** via HTList_addObject(), and pointed to by the head). +*/ +extern void * HTList_removeLastObject PARAMS(( + HTList * me)); + + +/* Remove object from END of list (the First one inserted +** via HTList_addObject(), and furthest from the head). +*/ +extern void * HTList_removeFirstObject PARAMS(( + HTList * me)); + + +/* Determine total number of objects in the list, +** not counting the head. +*/ +extern int HTList_count PARAMS(( + HTList * me)); + + +/* Determine position of an object in the list (a value of 0 +** means it is pointed to by the head; returns -1 if not found). +*/ +extern int HTList_indexOf PARAMS(( + HTList * me, + void * object)); + + +/* Return pointer to the object at a specified position in the list, +** where 0 is the object pointed to by the head (returns NULL if +** the list is empty, or if it doesn't exist - Yuk!). +*/ +extern void * HTList_objectAt PARAMS(( + HTList * me, + int position)); + + +#endif /* HTLIST_H */ + diff --git a/WWW/Library/Implementation/HTMIME.c b/WWW/Library/Implementation/HTMIME.c new file mode 100644 index 00000000..313c34b9 --- /dev/null +++ b/WWW/Library/Implementation/HTMIME.c @@ -0,0 +1,2009 @@ +/* MIME Message Parse HTMIME.c +** ================== +** +** This is RFC 1341-specific code. +** The input stream pushed into this parser is assumed to be +** stripped on CRs, ie lines end with LF, not CR LF. +** (It is easy to change this except for the body part where +** conversion can be slow.) +** +** History: +** Feb 92 Written Tim Berners-Lee, CERN +** +*/ +#include "HTUtils.h" +#include "HTMIME.h" /* Implemented here */ +#include "HTAlert.h" +#include "HTCJK.h" + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +extern int current_char_set; +extern char *LYchar_set_names[]; +extern BOOL HTPassEightBitRaw; +extern HTCJKlang HTCJK; + + +/* MIME Object +** ----------- +*/ + +extern int loading_length; /* from HTFormat.c (for HTCopy) */ + +typedef enum _MIME_state { + MIME_TRANSPARENT, /* put straight through to target ASAP! */ + miBEGINNING_OF_LINE, /* first character and not a continuation */ + miA, + miACCEPT_RANGES, + miAGE, + miAL, + miALLOW, + miALTERNATES, + miC, + miCACHE_CONTROL, + miCO, + miCOOKIE, + miCON, + miCONNECTION, + miCONTENT_, + miCONTENT_BASE, + miCONTENT_ENCODING, + miCONTENT_FEATURES, + miCONTENT_L, + miCONTENT_LANGUAGE, + miCONTENT_LENGTH, + miCONTENT_LOCATION, + miCONTENT_MD5, + miCONTENT_RANGE, + miCONTENT_T, + miCONTENT_TRANSFER_ENCODING, + miCONTENT_TYPE, + miDATE, + miE, + miETAG, + miEXPIRES, + miKEEP_ALIVE, + miL, + miLAST_MODIFIED, + miLINK, + miLOCATION, + miP, + miPR, + miPRAGMA, + miPROXY_AUTHENTICATE, + miPUBLIC, + miRETRY_AFTER, + miSERVER, + miT, + miTITLE, + miTRANSFER_ENCODING, + miU, + miUPGRADE, + miURI, + miV, + miVARY, + miVIA, + miW, + miWARNING, + miWWW_AUTHENTICATE, + miSKIP_GET_VALUE, /* Skip space then get value */ + miGET_VALUE, /* Get value till white space */ + miJUNK_LINE, /* Ignore the rest of this folded line */ + miNEWLINE, /* Just found a LF .. maybe continuation */ + miCHECK, /* check against check_pointer */ + MIME_NET_ASCII, /* Translate from net ascii */ + MIME_IGNORE /* Ignore entire file */ + /* TRANSPARENT and IGNORE are defined as stg else in _WINDOWS */ +} MIME_state; + +#define VALUE_SIZE 1024 /* @@@@@@@ Arbitrary? */ +struct _HTStream { + CONST HTStreamClass * isa; + + BOOL net_ascii; /* Is input net ascii? */ + MIME_state state; /* current state */ + MIME_state if_ok; /* got this state if match */ + MIME_state field; /* remember which field */ + MIME_state fold_state; /* state on a fold */ + CONST char * check_pointer; /* checking input */ + + char * value_pointer; /* storing values */ + char value[VALUE_SIZE]; + + HTParentAnchor * anchor; /* Given on creation */ + HTStream * sink; /* Given on creation */ + + char * boundary; /* For multipart */ + + HTFormat encoding; /* Content-Transfer-Encoding */ + char * compression_encoding; + int content_length; + HTFormat format; /* Content-Type */ + HTStream * target; /* While writing out */ + HTStreamClass targetClass; + + HTAtom * targetRep; /* Converting into? */ +}; + + +/*_________________________________________________________________________ +** +** A C T I O N R O U T I N E S +*/ + +/* Character handling +** ------------------ +** +** This is a FSM parser which is tolerant as it can be of all +** syntax errors. It ignores field names it does not understand, +** and resynchronises on line beginnings. +*/ + +PRIVATE void HTMIME_put_character ARGS2(HTStream *, me, char, c) +{ + int i, j; + + if (me->state == MIME_TRANSPARENT) { + (*me->targetClass.put_character)(me->target, c);/* MUST BE FAST */ + return; + } + + /* This slightly simple conversion just strips CR and turns LF to + ** newline. On unix LF is \n but on Mac \n is CR for example. + ** See NetToText for an implementation which preserves single CR or LF. + */ + if (me->net_ascii) { + c = FROMASCII(c); + if (c == CR) + return; + else if (c == LF) + c = '\n'; + } + + switch(me->state) { + + case MIME_IGNORE: + return; + + case MIME_TRANSPARENT: /* Not reached see above */ + (*me->targetClass.put_character)(me->target, c); + return; + + case MIME_NET_ASCII: + (*me->targetClass.put_character)(me->target, c); /* MUST BE FAST */ + return; + + case miNEWLINE: + if (c != '\n' && WHITE(c)) { /* Folded line */ + me->state = me->fold_state; /* pop state before newline */ + break; + } + + /* else Falls through */ + + case miBEGINNING_OF_LINE: + me->net_ascii = YES; + switch (c) { + case 'a': + case 'A': + me->state = miA; + if (TRACE) + fprintf(stderr, + "HTMIME: Got 'A' at beginning of line, state now A\n"); + break; + + case 'c': + case 'C': + me->state = miC; + if (TRACE) + fprintf (stderr, + "HTMIME: Got 'C' at beginning of line, state now C\n"); + break; + + case 'd': + case 'D': + me->check_pointer = "ate:"; + me->if_ok = miDATE; + me->state = miCHECK; + if (TRACE) + fprintf (stderr, + "HTMIME: Got 'D' at beginning of line, checking for 'ate:'\n"); + break; + + case 'e': + case 'E': + me->state = miE; + if (TRACE) + fprintf (stderr, + "HTMIME: Got 'E' at beginning of line, state now E\n"); + break; + + case 'k': + case 'K': + me->check_pointer = "eep-alive:"; + me->if_ok = miKEEP_ALIVE; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Got 'S' at beginning of line, checking for 'eep-alive:'\n"); + break; + + case 'l': + case 'L': + me->state = miL; + if (TRACE) + fprintf (stderr, + "HTMIME: Got 'L' at beginning of line, state now L\n"); + break; + + case 'p': + case 'P': + me->state = miP; + if (TRACE) + fprintf (stderr, + "HTMIME: Got 'P' at beginning of line, state now P\n"); + break; + + case 'r': + case 'R': + me->check_pointer = "etry-after:"; + me->if_ok = miRETRY_AFTER; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Got 'R' at beginning of line, checking for 'etry-after'\n"); + break; + + case 's': + case 'S': + me->check_pointer = "erver:"; + me->if_ok = miSERVER; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Got 'S' at beginning of line, checking for 'erver'\n"); + break; + + case 't': + case 'T': + me->state = miT; + if (TRACE) + fprintf (stderr, + "HTMIME: Got 'T' at beginning of line, state now T\n"); + break; + + case 'u': + case 'U': + me->state = miU; + if (TRACE) + fprintf (stderr, + "HTMIME: Got 'U' at beginning of line, state now U\n"); + break; + + case 'v': + case 'V': + me->state = miV; + if (TRACE) + fprintf (stderr, + "HTMIME: Got 'V' at beginning of line, state now V\n"); + break; + + case 'w': + case 'W': + me->state = miW; + if (TRACE) + fprintf (stderr, + "HTMIME: Got 'W' at beginning of line, state now W\n"); + break; + + case '\n': /* Blank line: End of Header! */ + { + me->net_ascii = NO; + if (strchr(HTAtom_name(me->format), ';') != NULL) { + char *cp = NULL, *cp1, *cp2; + + if (TRACE) + fprintf(stderr, + "HTMIME: Extended MIME Content-Type is %s\n", + HTAtom_name(me->format)); + StrAllocCopy(cp, HTAtom_name(me->format)); + /* + ** Note that the Content-Type value was converted + ** to lower case when we loaded into me->format, + ** but there may have been a mixed or upper-case + ** atom, so we'll force lower-casing again. - FM + */ + for (i = 0; cp[i]; i++) + cp[i] = TOLOWER(cp[i]); + if ((cp1=strchr(cp, ';')) != NULL) { + if ((cp2=strstr(cp1, "charset")) != NULL) { + cp2 += 7; + while (*cp2 == ' ' || *cp2 == '=') + cp2++; + if (!strncmp(cp2, "us-ascii", 8) || + !strncmp(cp2, "iso-8859-1", 10)) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "iso-8859-1"); + HTCJK = NOCJK; + } else if + (!strncmp(cp2, "iso-8859-2", 10) && + !strncmp(LYchar_set_names[current_char_set], + "ISO Latin 2", 11)) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "iso-8859-2"); + HTPassEightBitRaw = TRUE; + } else if + (!strncmp(cp2, "iso-8859-", 9) && + !strncmp(LYchar_set_names[current_char_set], + "Other ISO Latin", 15)) { + /* + ** Hope it's a match, for now. - FM + */ + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "iso-8859- "); + me->anchor->charset[9] = cp2[9]; + HTPassEightBitRaw = TRUE; + HTAlert(me->anchor->charset); + } else if + (!strncmp(cp2, "koi8-r", 6) && + !strncmp(LYchar_set_names[current_char_set], + "KOI8-R character set", 20)) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "koi8-r"); + HTPassEightBitRaw = TRUE; + } else if + (!strncmp(cp2, "euc-jp", 6) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "euc-jp"); + } else if + (!strncmp(cp2, "shift_jis", 9) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "shift_jis"); + } else if + (!strncmp(cp2, "iso-2022-jp", 11) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "iso-2022-jp"); + } else if + (!strncmp(cp2, "iso-2022-jp-2", 13) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "iso-2022-jp-2"); + } else if + (!strncmp(cp2, "euc-kr", 6) && + HTCJK == KOREAN) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "euc-kr"); + } else if + (!strncmp(cp2, "iso-2022-kr", 11) && + HTCJK == KOREAN) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "iso-2022-kr"); + } else if + ((!strncmp(cp2, "big5", 4) || + !strncmp(cp2, "cn-big5", 7)) && + HTCJK == TAIPEI) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "big5"); + } else if + (!strncmp(cp2, "euc-cn", 6) && + HTCJK == CHINESE) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "euc-cn"); + } else if + ((!strncmp(cp2, "gb2312", 6) || + !strncmp(cp2, "cn-gb", 5)) && + HTCJK == CHINESE) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "gb2312"); + } else if + (!strncmp(cp2, "iso-2022-cn", 11) && + HTCJK == CHINESE) { + *cp1 = '\0'; + me->format = HTAtom_for(cp); + StrAllocCopy(me->anchor->charset, + "iso-2022-cn"); + } + } + } + FREE(cp); + } + StrAllocCopy(me->anchor->content_type, + HTAtom_name(me->format)); + if (!me->compression_encoding) { + if (TRACE) { + fprintf(stderr, + "HTMIME: MIME Content-Type is '%s', converting to '%s'\n", + HTAtom_name(me->format), HTAtom_name(me->targetRep)); + } + } else { + /* + ** Change the format to that for "www/compressed" + ** and set up a stream to deal with it. - FM + */ + if (TRACE) { + fprintf(stderr, + "HTMIME: MIME Content-Type is '%s',\n", HTAtom_name(me->format)); + } + me->format = HTAtom_for("www/compressed"); + if (TRACE) { + fprintf(stderr, + " Treating as '%s'. Converting to '%s'\n", + HTAtom_name(me->format), HTAtom_name(me->targetRep)); + } + } + me->target = HTStreamStack(me->format, me->targetRep, + me->sink , me->anchor); + if (!me->target) { + if (TRACE) + fprintf(stderr, "HTMIME: Can't translate! ** \n"); + me->target = me->sink; /* Cheat */ + } + if (me->target) { + me->targetClass = *me->target->isa; + /* + ** Check for encoding and select state from there, + ** someday, but until we have the relevant code, + ** from now push straight through. - FM + */ + me->state = MIME_TRANSPARENT; + } else { + me->state = MIME_IGNORE; /* What else to do? */ + } + FREE(me->compression_encoding); + } + break; + + default: + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miA: /* Check for 'c','g' or 'l' */ + switch (c) { + case 'c': + case 'C': + me->check_pointer = "cept-ranges:"; + me->if_ok = miACCEPT_RANGES; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was A, found C, checking for 'cept-ranges:'\n"); + break; + + case 'g': + case 'G': + me->check_pointer = "e:"; + me->if_ok = miAGE; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was A, found G, checking for 'e:'\n"); + break; + + case 'l': + case 'L': + me->state = miAL; + if (TRACE) + fprintf(stderr, "HTMIME: Was A, found L, state now AL'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'g' or 'l'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miAL: /* Check for 'l' or 't' */ + switch (c) { + case 'l': + case 'L': + me->check_pointer = "low:"; + me->if_ok = miALLOW; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was AL, found L, checking for 'low:'\n"); + break; + + case 't': + case 'T': + me->check_pointer = "ernates:"; + me->if_ok = miALTERNATES; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was AL, found T, checking for 'ernates:'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'l' or 't'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miC: /* Check for 'a' or 'o' */ + switch (c) { + case 'a': + case 'A': + me->check_pointer = "che-control:"; + me->if_ok = miCACHE_CONTROL; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was C, found A, checking for 'che-control:'\n"); + break; + + case 'o': + case 'O': + me->state = miCO; + if (TRACE) + fprintf(stderr, "HTMIME: Was C, found O, state now CO'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'a' or 'o'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miCO: /* Check for 'n' or 'o' */ + switch (c) { + case 'n': + case 'N': + me->state = miCON; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CO, found N, state now CON\n"); + break; + + case 'o': + case 'O': + me->check_pointer = "kie:"; + me->if_ok = miCOOKIE; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CO, found O, checking for 'kie:'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'n' or 'o'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miCON: /* Check for 'n' or 't' */ + switch (c) { + case 'n': + case 'N': + me->check_pointer = "nection:"; + me->if_ok = miCONNECTION; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CON, found N, checking for 'nection:'\n"); + break; + + case 't': + case 'T': + me->check_pointer = "ent-"; + me->if_ok = miCONTENT_; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CON, found T, checking for 'ent-'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'n' or 't'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miE: /* Check for 't' or 'x' */ + switch (c) { + case 't': + case 'T': + me->check_pointer = "ag:"; + me->if_ok = miETAG; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was E, found T, checking for 'ag:'\n"); + break; + + case 'x': + case 'X': + me->check_pointer = "pires:"; + me->if_ok = miEXPIRES; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was E, found X, checking for 'pires:'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'t' or 'x'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miL: /* Check for 'a', 'i' or 'o' */ + switch (c) { + case 'a': + case 'A': + me->check_pointer = "st-modified:"; + me->if_ok = miLAST_MODIFIED; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was L, found A, checking for 'st-modified:'\n"); + break; + + case 'i': + case 'I': + me->check_pointer = "nk:"; + me->if_ok = miLINK; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was L, found I, checking for 'nk:'\n"); + break; + + case 'o': + case 'O': + me->check_pointer = "cation:"; + me->if_ok = miLOCATION; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was L, found O, checking for 'cation:'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'a', 'i' or 'o'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miP: /* Check for 'r' or 'u' */ + switch (c) { + case 'r': + case 'R': + me->state = miPR; + if (TRACE) + fprintf(stderr, "HTMIME: Was P, found O, state now PR'\n"); + break; + + case 'u': + case 'U': + me->check_pointer = "lic:"; + me->if_ok = miPUBLIC; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was P, found U, checking for 'blic:'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'r' or 'u'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miPR: /* Check for 'a' or 'o' */ + switch (c) { + case 'a': + case 'A': + me->check_pointer = "agma:"; + me->if_ok = miPRAGMA; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was PR, found A, checking for 'agma'\n"); + break; + + case 'o': + case 'O': + me->check_pointer = "xy-authenticate:"; + me->if_ok = miPROXY_AUTHENTICATE; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was PR, found O, checking for 'xy-authenticate'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'a' or 'o'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miT: /* Check for 'i' or 'r' */ + switch (c) { + case 'i': + case 'I': + me->check_pointer = "tle:"; + me->if_ok = miTITLE; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was T, found I, checking for 'tle:'\n"); + break; + + case 'r': + case 'R': + me->check_pointer = "ansfer-encoding:"; + me->if_ok = miTRANSFER_ENCODING; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was T, found R, checking for 'ansfer-encoding'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'i' or 'r'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miU: /* Check for 'p' or 'r' */ + switch (c) { + case 'p': + case 'P': + me->check_pointer = "grade:"; + me->if_ok = miUPGRADE; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was U, found P, checking for 'grade:'\n"); + break; + + case 'r': + case 'R': + me->check_pointer = "i:"; + me->if_ok = miURI; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was U, found R, checking for 'i:'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'p' or 'r'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miV: /* Check for 'a' or 'i' */ + switch (c) { + case 'a': + case 'A': + me->check_pointer = "ry:"; + me->if_ok = miVARY; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was V, found A, checking for 'ry:'\n"); + break; + + case 'i': + case 'I': + me->check_pointer = "a:"; + me->if_ok = miVIA; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was V, found I, checking for 'a:'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'a' or 'i'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miW: /* Check for 'a' or 'w' */ + switch (c) { + case 'a': + case 'A': + me->check_pointer = "rning:"; + me->if_ok = miWARNING; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was W, found A, checking for 'rning:'\n"); + break; + + case 'w': + case 'W': + me->check_pointer = "w-authenticate:"; + me->if_ok = miWWW_AUTHENTICATE; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was W, found W, checking for 'w-authenticate:'\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, "'a' or 'w'"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miCHECK: /* Check against string */ + if (TOLOWER(c) == *(me->check_pointer)++) { + if (!*me->check_pointer) + me->state = me->if_ok; + } else { /* Error */ + if (TRACE) + fprintf(stderr, + "HTMIME: Bad character `%c' found where `%s' expected\n", + c, me->check_pointer - 1); + goto bad_field_name; + } + break; + + case miCONTENT_: + if (TRACE) + fprintf (stderr, + "HTMIME: in case CONTENT_\n"); + switch(c) { + case 'b': + case 'B': + me->check_pointer = "ase:"; + me->if_ok = miCONTENT_BASE; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CONTENT_, found B, checking for 'ase:'\n"); + break; + + case 'e': + case 'E': + me->check_pointer = "ncoding:"; + me->if_ok = miCONTENT_ENCODING; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CONTENT_, found E, checking for 'ncoding:'\n"); + break; + + case 'f': + case 'F': + me->check_pointer = "eatures:"; + me->if_ok = miCONTENT_FEATURES; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CONTENT_, found F, checking for 'eatures:'\n"); + break; + + case 'l': + case 'L': + me->state = miCONTENT_L; + if (TRACE) + fprintf (stderr, + "HTMIME: Was CONTENT_, found L, state now CONTENT_L\n"); + break; + + case 'm': + case 'M': + me->check_pointer = "d5:"; + me->if_ok = miCONTENT_MD5; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CONTENT_, found M, checking for 'd5:'\n"); + break; + + case 'r': + case 'R': + me->check_pointer = "ange:"; + me->if_ok = miCONTENT_RANGE; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CONTENT_, found R, checking for 'ange:'\n"); + break; + + case 't': + case 'T': + me->state = miCONTENT_T; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CONTENT_, found T, state now CONTENT_T\n"); + break; + + default: + if (TRACE) + fprintf(stderr, + "HTMIME: Was CONTENT_, found nothing; bleah\n"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miCONTENT_L: + if (TRACE) + fprintf (stderr, + "HTMIME: in case CONTENT_L\n"); + switch(c) { + case 'a': + case 'A': + me->check_pointer = "nguage:"; + me->if_ok = miCONTENT_LANGUAGE; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CONTENT_L, found A, checking for 'nguage:'\n"); + break; + + case 'e': + case 'E': + me->check_pointer = "ngth:"; + me->if_ok = miCONTENT_LENGTH; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CONTENT_L, found E, checking for 'ngth:'\n"); + break; + + default: + if (TRACE) + fprintf (stderr, + "HTMIME: Was CONTENT_L, found nothing; bleah\n"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miCONTENT_T: + if (TRACE) + fprintf (stderr, + "HTMIME: in case CONTENT_T\n"); + switch(c) { + case 'r': + case 'R': + me->check_pointer = "ansfer-encoding:"; + me->if_ok = miCONTENT_TRANSFER_ENCODING; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CONTENT_T, found R, checking for 'ansfer-encoding:'\n"); + break; + + case 'y': + case 'Y': + me->check_pointer = "pe:"; + me->if_ok = miCONTENT_TYPE; + me->state = miCHECK; + if (TRACE) + fprintf(stderr, + "HTMIME: Was CONTENT_T, found Y, checking for 'pe:'\n"); + break; + + default: + if (TRACE) + fprintf (stderr, + "HTMIME: Was CONTENT_T, found nothing; bleah\n"); + goto bad_field_name; + break; + + } /* switch on character */ + break; + + case miACCEPT_RANGES: + case miAGE: + case miALLOW: + case miALTERNATES: + case miCACHE_CONTROL: + case miCOOKIE: + case miCONNECTION: + case miCONTENT_BASE: + case miCONTENT_ENCODING: + case miCONTENT_FEATURES: + case miCONTENT_LANGUAGE: + case miCONTENT_LENGTH: + case miCONTENT_LOCATION: + case miCONTENT_MD5: + case miCONTENT_RANGE: + case miCONTENT_TRANSFER_ENCODING: + case miCONTENT_TYPE: + case miDATE: + case miETAG: + case miEXPIRES: + case miKEEP_ALIVE: + case miLAST_MODIFIED: + case miLINK: + case miLOCATION: + case miPRAGMA: + case miPROXY_AUTHENTICATE: + case miPUBLIC: + case miRETRY_AFTER: + case miSERVER: + case miTITLE: + case miTRANSFER_ENCODING: + case miUPGRADE: + case miURI: + case miVARY: + case miVIA: + case miWARNING: + case miWWW_AUTHENTICATE: + me->field = me->state; /* remember it */ + me->state = miSKIP_GET_VALUE; + /* Fall through! */ + case miSKIP_GET_VALUE: + if (c == '\n') { + me->fold_state = me->state; + me->state = miNEWLINE; + break; + } + if (WHITE(c)) + break; /* Skip white space */ + + me->value_pointer = me->value; + me->state = miGET_VALUE; + /* Fall through to store first character */ + + case miGET_VALUE: + if (WHITE(c) && c != 32) { /* End of field */ + char *cp; + *me->value_pointer = '\0'; + cp = (me->value_pointer - 1); + while ((cp >= me->value) && *cp == 32) + *cp = '\0'; /* Trim trailing spaces. - FM */ + switch (me->field) { + case miACCEPT_RANGES: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Accept-Ranges: '%s'\n", + me->value); + break; + case miAGE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Age: '%s'\n", + me->value); + break; + case miALLOW: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Allow: '%s'\n", + me->value); + break; + case miALTERNATES: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Alternates: '%s'\n", + me->value); + break; + case miCACHE_CONTROL: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Cache-Control: '%s'\n", + me->value); + /* + ** Convert to lowercase and indicate in anchor. - FM + */ + for (i = 0; me->value[i]; i++) + me->value[i] = TOLOWER(me->value[i]); + StrAllocCopy(me->anchor->cache_control, me->value); + /* + * Check whether to set no_cache for the anchor. - FM + */ + if (!strcmp(me->value, "no-cache")) + me->anchor->no_cache = TRUE; + break; + case miCOOKIE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Cookie: '%s'\n", + me->value); + break; + case miCONNECTION: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Connection: '%s'\n", + me->value); + break; + case miCONTENT_BASE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Content-Base: '%s'\n", + me->value); + break; + case miCONTENT_ENCODING: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Content-Encoding: '%s'\n", + me->value); + if (!(me->value && *me->value)) + break; + /* + ** Convert to lowercase and indicate in anchor. - FM + */ + for (i = 0; me->value[i]; i++) + me->value[i] = TOLOWER(me->value[i]); + StrAllocCopy(me->anchor->content_encoding, me->value); + FREE(me->compression_encoding); + if (!strcmp(me->value, "8bit") || + !strcmp(me->value, "7bit") || + !strcmp(me->value, "binary")) { + /* + ** Some server indicated "8bit", "7bit" or "binary" + ** inappropriately. We'll ignore it. - FM + */ + if (TRACE) + fprintf(stderr, + " Ignoring it!\n"); + } else { + /* + ** Save it to use as a flag for setting + ** up a "www/compressed" target. - FM + */ + StrAllocCopy(me->compression_encoding, me->value); + } + break; + case miCONTENT_FEATURES: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Content-Base: '%s'\n", + me->value); + break; + case miCONTENT_LANGUAGE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Content-Language: '%s'\n", + me->value); + /* + ** Convert to lowercase and indicate in anchor. - FM + */ + for (i = 0; me->value[i]; i++) + me->value[i] = TOLOWER(me->value[i]); + StrAllocCopy(me->anchor->content_language, me->value); + break; + case miCONTENT_LENGTH: + me->content_length = atoi(me->value); + /* This is TEMPORARY. */ + loading_length = me->content_length; + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Content-Length: '%d'\n", + me->content_length); + break; + case miCONTENT_LOCATION: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Content-Location: '%s'\n", + me->value); + break; + case miCONTENT_MD5: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Content-MD5: '%s'\n", + me->value); + break; + case miCONTENT_RANGE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Content-Range: '%s'\n", + me->value); + break; + case miCONTENT_TRANSFER_ENCODING: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Content-Transfer-Encoding: '%s'\n", + me->value); + /* + ** Force the Content-Transfer-Encoding value + ** to all lower case. - FM + */ + for (i = 0; me->value[i]; i++) + me->value[i] = TOLOWER(me->value[i]); + me->encoding = HTAtom_for(me->value); + break; + case miCONTENT_TYPE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Content-Type: '%s'\n", + me->value); + /* + ** Force the Content-Type value to all + ** lower case and strip spaces. - FM + */ + for (i = 0, j = 0; me->value[i]; i++) { + if (me->value[i] != ' ') { + me->value[j++] = TOLOWER(me->value[i]); + } + } + me->value[j] = '\0'; + me->format = HTAtom_for(me->value); + break; + case miDATE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Date: '%s'\n", + me->value); + break; + case miETAG: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP ETag: '%s'\n", + me->value); + break; + case miEXPIRES: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Expires: '%s'\n", + me->value); + break; + case miKEEP_ALIVE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Keep-Alive: '%s'\n", + me->value); + break; + case miLAST_MODIFIED: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Last-Modified: '%s'\n", + me->value); + break; + case miLINK: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Link: '%s'\n", + me->value); + break; + case miLOCATION: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Location: '%s'\n", + me->value); + break; + case miPRAGMA: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Pragma: '%s'\n", + me->value); + /* + * Check whether to set no_cache for the anchor. - FM + */ + if (!strcmp(me->value, "no-cache")) + me->anchor->no_cache = TRUE; + break; + case miPROXY_AUTHENTICATE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Proxy-Authenticate: '%s'\n", + me->value); + break; + case miPUBLIC: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Public: '%s'\n", + me->value); + break; + case miRETRY_AFTER: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Retry-After: '%s'\n", + me->value); + break; + case miSERVER: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Server: '%s'\n", + me->value); + break; + case miTITLE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Title: '%s'\n", + me->value); + break; + case miTRANSFER_ENCODING: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Transfer-Encoding: '%s'\n", + me->value); + break; + case miUPGRADE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Upgrade: '%s'\n", + me->value); + break; + case miURI: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP URI: '%s'\n", + me->value); + break; + case miVARY: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Vary: '%s'\n", + me->value); + break; + case miVIA: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Via: '%s'\n", + me->value); + break; + case miWARNING: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP Warning: '%s'\n", + me->value); + break; + case miWWW_AUTHENTICATE: + if (TRACE) + fprintf(stderr, + "HTMIME: PICKED UP WWW-Authenticate: '%s'\n", + me->value); + break; + default: /* Should never get here */ + break; + } + } else { + if (me->value_pointer < me->value + VALUE_SIZE - 1) { + *me->value_pointer++ = c; + break; + } else { + goto value_too_long; + } + } + /* Fall through */ + + case miJUNK_LINE: + if (c == '\n') { + me->state = miNEWLINE; + me->fold_state = me->state; + } + break; + + + } /* switch on state*/ + + return; + +value_too_long: + if (TRACE) + fprintf(stderr, "HTMIME: *** Syntax error. (string too long)\n"); + +bad_field_name: /* Ignore it */ + me->state = miJUNK_LINE; + return; + +} + + + +/* String handling +** --------------- +** +** Strings must be smaller than this buffer size. +*/ +PRIVATE void HTMIME_put_string ARGS2(HTStream *, me, CONST char*, s) +{ + CONST char * p; + + if (me->state == MIME_TRANSPARENT) { /* Optimisation */ + (*me->targetClass.put_string)(me->target,s); + + } else if (me->state != MIME_IGNORE) { + if (TRACE) + fprintf(stderr, "HTMIME: %s\n", s); + + for (p=s; *p; p++) + HTMIME_put_character(me, *p); + } +} + + +/* Buffer write. Buffers can (and should!) be big. +** ------------ +*/ +PRIVATE void HTMIME_write ARGS3(HTStream *, me, CONST char*, s, int, l) +{ + CONST char * p; + + if (me->state == MIME_TRANSPARENT) { /* Optimisation */ + (*me->targetClass.put_block)(me->target, s, l); + + } else { + if (TRACE) + fprintf(stderr, "HTMIME: %s\n", s); + + for (p = s; p < s+l; p++) + HTMIME_put_character(me, *p); + } +} + + +/* Free an HTML object +** ------------------- +** +*/ +PRIVATE void HTMIME_free ARGS1(HTStream *, me) +{ + if (me->target) + (*me->targetClass._free)(me->target); + FREE(me); +} + +/* End writing +*/ + +PRIVATE void HTMIME_abort ARGS2(HTStream *, me, HTError, e) +{ + if (me->target) + (*me->targetClass._abort)(me->target, e); + FREE(me); +} + + +/* Structured Object Class +** ----------------------- +*/ +PRIVATE CONST HTStreamClass HTMIME = +{ + "MIMEParser", + HTMIME_free, + HTMIME_abort, + HTMIME_put_character, + HTMIME_put_string, + HTMIME_write +}; + + +/* Subclass-specific Methods +** ------------------------- +*/ + +PUBLIC HTStream* HTMIMEConvert ARGS3( + HTPresentation *, pres, + HTParentAnchor *, anchor, + HTStream *, sink) +{ + HTStream* me; + + me = (HTStream *)calloc(1, sizeof(*me)); + if (me == NULL) + outofmem(__FILE__, "HTML_new"); + me->isa = &HTMIME; + me->sink = sink; + me->anchor = anchor; + me->anchor->no_cache = FALSE; + FREE(me->anchor->cache_control); + FREE(me->anchor->charset); + FREE(me->anchor->content_language); + FREE(me->anchor->content_encoding); + me->target = NULL; + me->state = miBEGINNING_OF_LINE; + /* + * Sadly enough, change this to always default to WWW_HTML + * to parse all text as HTML for the users. + * GAB 06-30-94 + * Thanks to Robert Rowland robert@cyclops.pei.edu + * + * After discussion of the correct handline, should be application/octet- + * stream or unknown; causing servers to send a correct content + * type. + * + * The consequence of using WWW_UNKNOWN is that you end up downloading + * as a binary file what 99.9% of the time is an HTML file, which should + * have been rendered or displayed. So sadly enough, I'm changing it + * back to WWW_HTML, and it will handle the situation like Mosaic does, + * and as Robert Rowland suggested, because being functionally correct + * 99.9% of the time is better than being technically correct but + * functionally nonsensical. - FM + *//*** + me->format = WWW_UNKNOWN; + ***/ + me->format = WWW_HTML; + me->targetRep = pres->rep_out; + me->boundary = NULL; /* Not set yet */ + me->encoding = 0; /* Not set yet */ + me->compression_encoding = NULL; /* Not set yet */ + me->net_ascii = NO; /* Local character set */ + return me; +} + +PUBLIC HTStream* HTNetMIME ARGS3( + HTPresentation *, pres, + HTParentAnchor *, anchor, + HTStream *, sink) +{ + HTStream* me = HTMIMEConvert(pres,anchor, sink); + if (!me) + return NULL; + + me->net_ascii = YES; + return me; +} + +/* Japanese header handling functions +** ================================== +** +** K&Rized and added 07-Jun-96 by FM, based on: +** +//////////////////////////////////////////////////////////////////////// +** +** ISO-2022-JP handling routines +** & +** MIME decode routines (quick hack just for ISO-2022-JP) +** +** Thu Jan 25 10:11:42 JST 1996 +** +** Copyright (C) 1994, 1995, 1996 +** Shuichi Ichikawa (ichikawa@nuee.nagoya-u.ac.jp) +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either versions 2, or (at your option) +** any later version. +** +** This program is distributed in the hope that it will be useful +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with SKK, see the file COPYING. If not, write to the Free +** Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* #include <stdio.h> */ /* Included via previous headers. - FM */ +/* #include <string.h> */ /* Included via previous headers. - FM */ + +/* + * MIME decoding routines + * + * Written by S. Ichikawa, + * partially inspired by encdec.c of <jh@efd.lth.se>. + */ +#define BUFLEN 1024 +#ifdef ESC +#undef ESC +#endif /* ESC */ +#define ESC '\033' + +PRIVATE char HTmm64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" ; +PRIVATE char HTmmquote[] = "0123456789ABCDEF"; +PRIVATE int HTmmcont = 0; + +PUBLIC void HTmmdec_base64 ARGS2( + char *, t, + char *, s) +{ + int d, count, j, val; + char buf[BUFLEN], *bp, nw[4], *p; + + for (bp = buf; *s; s += 4) { + val = 0; + if (s[2] == '=') + count = 1; + else if (s[3] == '=') + count = 2; + else + count = 3; + + for (j = 0; j <= count; j++) { + if (!(p = strchr(HTmm64, s[j]))) { + return; + } + d = p - HTmm64; + d <<= (3-j)*6; + val += d; + } + for (j = 2; j >= 0; j--) { + nw[j] = val & 255; + val >>= 8; + } + if (count--) + *bp++ = nw[0]; + if (count--) + *bp++ = nw[1]; + if (count) + *bp++ = nw[2]; + } + *bp = '\0'; + strcpy(t, buf); +} + +PUBLIC void HTmmdec_quote ARGS2( + char *, t, + char *, s) +{ + char buf[BUFLEN], cval, *bp, *p; + + for (bp = buf; *s; ) { + if (*s == '=') { + cval = 0; + if (s[1] && (p = strchr(HTmmquote, s[1]))) { + cval += (p - HTmmquote); + } else { + *bp++ = *s++; + continue; + } + if (s[2] && (p = strchr(HTmmquote, s[2]))) { + cval <<= 4; + cval += (p - HTmmquote); + *bp++ = cval; + s += 3; + } else { + *bp++ = *s++; + } + } else if (*s == '_') { + *bp++ = 0x20; + s++; + } else { + *bp++ = *s++; + } + } + *bp = '\0'; + strcpy(t, buf); +} + +PUBLIC void HTmmdecode ARGS2( + char *, trg, + char *, str) +{ + char buf[BUFLEN], mmbuf[BUFLEN]; + char *s, *t, *u; + int base64, quote; + + buf[0] = '\0'; + + for (s = str, u = buf; *s; ) { + if (!strncasecomp(s, "=?ISO-2022-JP?B?", 16)) { + base64 = 1; + } else { + base64 = 0; + } + if (!strncasecomp(s, "=?ISO-2022-JP?Q?", 16)) { + quote = 1; + } else { + quote = 0; + } + if (base64 || quote) { + if (HTmmcont) { + for (t = s - 1; + t >= str && (*t == ' ' || *t == '\t'); t--) { + u--; + } + } + for (s += 16, t = mmbuf; *s; ) { + if (s[0] == '?' && s[1] == '=') { + break; + } else { + *t++ = *s++; + } + } + if (s[0] != '?' || s[1] != '=') { + goto end; + } else { + s += 2; + *t = '\0'; + } + if (base64) + HTmmdec_base64(mmbuf, mmbuf); + if (quote) + HTmmdec_quote(mmbuf, mmbuf); + for (t = mmbuf; *t; ) + *u++ = *t++; + HTmmcont = 1; + /* if (*s == ' ' || *s == '\t') *u++ = *s; */ + /* for ( ; *s == ' ' || *s == '\t'; s++) ; */ + } else { + if (*s != ' ' && *s != '\t') + HTmmcont = 0; + *u++ = *s++; + } + } + *u = '\0'; +end: + strcpy(trg, buf); +} + +/* +** Modified for Lynx-jp by Takuya ASADA (and K&Rized by FM). +*/ +#if NOTDEFINED +PUBLIC int main ARGS2( + int, ac, + char **, av) +{ + FILE *fp; + char buf[BUFLEN]; + char header = 1, body = 0, r_jis = 0; + int i, c; + + for (i = 1; i < ac; i++) { + if (strcmp(av[i], "-B") == NULL) + body = 1; + else if (strcmp(av[i], "-r") == NULL) + r_jis = 1; + else + break; + } + + if (i >= ac) { + fp = stdin; + } else { + if ((fp = fopen(av[i], "r")) == NULL) { + fprintf(stderr, "%s: cannot open %s\n", av[0], av[i]); + exit(1); + } + } + + while (fgets(buf, BUFLEN, fp)) { + if (buf[0] == '\n' && buf[1] == '\0') + header = 0; + if (header) { + c = fgetc(fp); + if (c == ' ' || c == '\t') { + buf[strlen(buf)-1] = '\0'; + ungetc(c, fp); + } else { + ungetc(c, fp); + } + } + if (header || body) + HTmmdecode(buf, buf); + if (r_jis) + HTrjis(buf, buf); + fprintf(stdout, "%s", buf); + } + + close(fp); + exit(0); +} +#endif /* NOTDEFINED */ + +/* +** Insert ESC where it seems lost. +** (The author of this function "rjis" is S. Ichikawa.) +*/ +PUBLIC int HTrjis ARGS2( + char *, t, + char *, s) +{ + char *p, buf[BUFLEN]; + int kanji = 0; + + if (strchr(s, ESC) || !strchr(s, '$')) { + if (s != t) + strcpy(t, s); + return 1; + } + for (p = buf; *s; ) { + if (!kanji && s[0] == '$' && (s[1] == '@' || s[1] == 'B')) { + if (HTmaybekanji((int)s[2], (int)s[3])) { + kanji = 1; + *p++ = ESC; + *p++ = *s++; + *p++ = *s++; + *p++ = *s++; + *p++ = *s++; + continue; + } + *p++ = *s++; + continue; + } + if (kanji && s[0] == '(' && (s[1] == 'J' || s[1] == 'B')) { + kanji = 0; + *p++ = ESC; + *p++ = *s++; + *p++ = *s++; + continue; + } + *p++ = *s++; + } + *p = *s; /* terminate string */ + + strcpy(t, buf); + return 0; +} + +/* +** The following function "maybekanji" is derived from +** RJIS-1.0 by Mr. Hironobu Takahashi. +** Maybekanji() is included here under the courtesy of the author. +** The original comment of rjis.c is also included here. +*/ +/* + * RJIS ( Recover JIS code from broken file ) + * $Header: /usr/build/VCS/lynx/WWW/Library/Implementation/RCS/HTMIME.c,v 1.1 1996/09/02 00:21:10 tom Exp $ + * Copyright (C) 1992 1994 + * Hironobu Takahashi (takahasi@tiny.or.jp) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either versions 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with SKK, see the file COPYING. If not, write to the Free + * Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +PUBLIC int HTmaybekanji ARGS2( + int, c1, + int, c2) +{ + + if ((c2 < 33) || (c2 > 126)) + return 0; + if ((c1 < 33) || ((40 < c1) && (c1 < 48)) || (116 < c1)) + return 0; + c2 -= 32; + switch(c1-32) { + case 2: + if ((14 < c2) && ( c2 < 26)) + return 0; + if ((33 < c2) && ( c2 < 42)) + return 0; + if ((48 < c2) && ( c2 < 60)) + return 0; + if ((74 < c2) && ( c2 < 82)) + return 0; + if ((89 < c2) && ( c2 < 94)) + return 0; + break; + case 3: + if (c2 < 16) + return 0; + if ((25 < c2) && ( c2 < 33)) + return 0; + if ((58 < c2) && ( c2 < 65)) + return 0; + if (90 < c2) + return 0; + break; + case 4: + if (83 < c2) + return 0; + break; + case 5: + if (86 < c2) + return 0; + break; + case 6: + if ((24 < c2) && ( c2 < 33)) + return 0; + if (56 < c2) + return 0; + break; + case 7: + if ((33 < c2) && ( c2 < 49)) + return 0; + if (81 < c2) + return 0; + break; + case 8: + if (32 < c2) + return 0; + break; + case 47: + if (51 < c2) + return 0; + break; + case 84: + if (6 < c2) + return 0; + break; + } + return 1; +} + diff --git a/WWW/Library/Implementation/HTMIME.h b/WWW/Library/Implementation/HTMIME.h new file mode 100644 index 00000000..94b9665c --- /dev/null +++ b/WWW/Library/Implementation/HTMIME.h @@ -0,0 +1,77 @@ +/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTMIME.html + MIME PARSER + + The MIME parser stream presents a MIME document. It recursively invokes the format + manager to handle embedded formats. + + As well as stripping off and parsing the headers, the MIME parser has to parse any + weirld MIME encodings it may meet within the body parts of messages, and must deal with + multipart messages. + + This module is implemented to the level necessary for operation with WWW, but is not + currently complete for any arbitrary MIME message. + + Check the source for latest additions to functionality. + + The MIME parser is complicated by the fact that WWW allows real binary to be sent, not + ASCII encoded. Therefore the netascii decoding is included in this module. One cannot + layer it by converting first from Net to local text, then decoding it. Of course, for + local files, the net ascii decoding is not needed. There are therefore two creation + routines. + + */ +#ifndef HTMIME_H +#define HTMIME_H + +#include "HTStream.h" +#include "HTAnchor.h" + +/* + + INPUT: LOCAL TEXT + + */ +extern HTStream * HTMIMEConvert PARAMS((HTPresentation * pres, + HTParentAnchor * anchor, + HTStream * sink)); +/* + + INPUT: NET ASCII + + */ +extern HTStream * HTNetMIME PARAMS((HTPresentation * pres, + HTParentAnchor * anchor, + HTStream * sink)); + + +/* + + For handling Japanese headers. + +*/ +extern void HTmmdec_base64 PARAMS(( + char * t, + char * s)); + +extern void HTmmdec_quote PARAMS(( + char * t, + char * s)); + +extern void HTmmdecode PARAMS(( + char * trg, + char * str)); + +extern int HTrjis PARAMS(( + char * t, + char * s)); + +PUBLIC int HTmaybekanji PARAMS(( + int c1, + int c2)); + + +#endif /* !HTMIME_H */ + +/* + + end of HTMIME */ diff --git a/WWW/Library/Implementation/HTML.h b/WWW/Library/Implementation/HTML.h new file mode 100644 index 00000000..aada006f --- /dev/null +++ b/WWW/Library/Implementation/HTML.h @@ -0,0 +1,79 @@ +/* HTML to rich text converter for libwww + THE HTML TO RTF OBJECT CONVERTER + + This interprets the HTML semantics. + + */ +#ifndef HTML_H +#define HTML_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTAnchor.h" +#include "HTMLDTD.h" + +#ifdef SHORT_NAMES +#define HTMLPresentation HTMLPren +#define HTMLPresent HTMLPres +#endif /* SHORT_NAMES */ + +extern CONST HTStructuredClass HTMLPresentation; + +/* + +HTConverter to present HTML + + */ +PUBLIC HTStream* HTMLToPlain PARAMS(( + HTPresentation * pres, + HTParentAnchor * anchor, + HTStream * sink)); + +PUBLIC HTStream* HTMLToC PARAMS(( + HTPresentation * pres, + HTParentAnchor * anchor, + HTStream * sink)); + +PUBLIC HTStream* HTMLPresent PARAMS(( + HTPresentation * pres, + HTParentAnchor * anchor, + HTStream * sink)); + +extern HTStructured* HTML_new PARAMS(( + HTParentAnchor * anchor, + HTFormat format_out, + HTStream * target)); + +/* Names for selected internal representations: +*/ +typedef enum _HTMLCharacterSet { + HTML_ISO_LATIN1, + HTML_NEXT_CHARS, + HTML_PC_CP950 +} HTMLCharacterSet; + + +/* + +Record error message as a hypertext object + + The error message should be marked as an error so that it can be reloaded later. This + implementation just throws up an error message and leaves the document unloaded. + + */ +/* On entry, +** sink is a stream to the output device if any +** number is the HTTP error number +** message is the human readable message. +** On exit, +** a retrun code like HT_LOADED if object exists else 60; 0 +*/ + +PUBLIC int HTLoadError PARAMS(( + HTStream * sink, + int number, + CONST char * message)); + +#endif /* HTML_H */ + diff --git a/WWW/Library/Implementation/HTMLDTD.c b/WWW/Library/Implementation/HTMLDTD.c new file mode 100644 index 00000000..3a982b2f --- /dev/null +++ b/WWW/Library/Implementation/HTMLDTD.c @@ -0,0 +1,1122 @@ +/* Our Static DTD for HTML +** ----------------------- +*/ + +/* Implements: +*/ + +#include "HTUtils.h" +#include "HTMLDTD.h" +#include "LYLeaks.h" + +/* Entity Names +** ------------ +** +** This table must be matched exactly with ALL the translation tables +*/ +static CONST char* entities[] = { + "AElig", /* capital AE diphthong (ligature) */ + "Aacute", /* capital A, acute accent */ + "Acirc", /* capital A, circumflex accent */ + "Agrave", /* capital A, grave accent */ + "Aring", /* capital A, ring */ + "Atilde", /* capital A, tilde */ + "Auml", /* capital A, dieresis or umlaut mark */ + "Ccedil", /* capital C, cedilla */ + "Dstrok", /* capital Eth, Icelandic */ + "ETH", /* capital Eth, Icelandic */ + "Eacute", /* capital E, acute accent */ + "Ecirc", /* capital E, circumflex accent */ + "Egrave", /* capital E, grave accent */ + "Euml", /* capital E, dieresis or umlaut mark */ + "Iacute", /* capital I, acute accent */ + "Icirc", /* capital I, circumflex accent */ + "Igrave", /* capital I, grave accent */ + "Iuml", /* capital I, dieresis or umlaut mark */ + "Ntilde", /* capital N, tilde */ + "Oacute", /* capital O, acute accent */ + "Ocirc", /* capital O, circumflex accent */ + "Ograve", /* capital O, grave accent */ + "Oslash", /* capital O, slash */ + "Otilde", /* capital O, tilde */ + "Ouml", /* capital O, dieresis or umlaut mark */ + "THORN", /* capital THORN, Icelandic */ + "Uacute", /* capital U, acute accent */ + "Ucirc", /* capital U, circumflex accent */ + "Ugrave", /* capital U, grave accent */ + "Uuml", /* capital U, dieresis or umlaut mark */ + "Yacute", /* capital Y, acute accent */ + "aacute", /* small a, acute accent */ + "acirc", /* small a, circumflex accent */ + "acute", /* spacing acute */ + "aelig", /* small ae diphthong (ligature) */ + "agrave", /* small a, grave accent */ + "amp", /* ampersand */ + "aring", /* small a, ring */ + "atilde", /* small a, tilde */ + "auml", /* small a, dieresis or umlaut mark */ + "brkbar", /* broken vertical bar */ + "brvbar", /* broken vertical bar */ + "ccedil", /* small c, cedilla */ + "cedil", /* spacing cedilla */ + "cent", /* cent sign */ + "copy", /* copyright sign */ + "curren", /* currency sign */ + "deg", /* degree sign */ + "die", /* spacing diaresis */ + "divide", /* division sign */ + "eacute", /* small e, acute accent */ + "ecirc", /* small e, circumflex accent */ + "egrave", /* small e, grave accent */ + "emdash", /* dash the width of emsp */ + "emsp", /* em space - not collapsed */ + "endash", /* dash the width of ensp */ + "ensp", /* en space - not collapsed */ + "eth", /* small eth, Icelandic */ + "euml", /* small e, dieresis or umlaut mark */ + "frac12", /* fraction 1/2 */ + "frac14", /* fraction 1/4 */ + "frac34", /* fraction 3/4 */ + "gt", /* greater than */ + "hibar", /* spacing macron */ + "iacute", /* small i, acute accent */ + "icirc", /* small i, circumflex accent */ + "iexcl", /* inverted exclamation mark */ + "igrave", /* small i, grave accent */ + "iquest", /* inverted question mark */ + "iuml", /* small i, dieresis or umlaut mark */ + "laquo", /* angle quotation mark, left */ + "lt", /* less than */ + "macr", /* spacing macron */ + "mdash", /* dash the width of emsp */ + "micro", /* micro sign */ + "middot", /* middle dot */ + "nbsp", /* non breaking space */ + "ndash", /* dash the width of ensp */ + "not", /* negation sign */ + "ntilde", /* small n, tilde */ + "oacute", /* small o, acute accent */ + "ocirc", /* small o, circumflex accent */ + "ograve", /* small o, grave accent */ + "ordf", /* feminine ordinal indicator */ + "ordm", /* masculine ordinal indicator */ + "oslash", /* small o, slash */ + "otilde", /* small o, tilde */ + "ouml", /* small o, dieresis or umlaut mark */ + "para", /* paragraph sign */ + "plusmn", /* plus-or-minus sign */ + "pound", /* pound sign */ + "quot", /* quot '"' */ + "raquo", /* angle quotation mark, right */ + "reg", /* circled R registered sign */ + "sect", /* section sign */ + "shy", /* soft hyphen */ + "sup1", /* superscript 1 */ + "sup2", /* superscript 2 */ + "sup3", /* superscript 3 */ + "szlig", /* small sharp s, German (sz ligature) */ + "thinsp", /* thin space (not collapsed) */ + "thorn", /* small thorn, Icelandic */ + "times", /* multiplication sign */ + "trade", /* registerd trademark */ + "uacute", /* small u, acute accent */ + "ucirc", /* small u, circumflex accent */ + "ugrave", /* small u, grave accent */ + "uml", /* spacing diaresis */ + "uuml", /* small u, dieresis or umlaut mark */ + "yacute", /* small y, acute accent */ + "yen", /* yen sign */ + "yuml", /* small y, dieresis or umlaut mark */ +}; + +#define HTML_ENTITIES 112 + + +/* Attribute Lists +** --------------- +** +** Lists must be in alphatbetical order by attribute name +** The tag elements contain the number of attributes +*/ + +static attr a_attr[] = { /* Anchor attributes */ + { "ACCESSKEY" }, + { "CLASS" }, + { "CLEAR" }, + { "COORDS" }, + { "DIR" }, + { "HREF" }, + { "ID" }, + { "ISMAP" }, + { "LANG" }, + { "MD" }, + { "NAME" }, + { "NOTAB" }, + { "ONCLICK" }, + { "ONMOUSEOUT" }, + { "ONMOUSEOVER" }, + { "REL" }, + { "REV" }, + { "SHAPE" }, + { "STYLE" }, + { "TABINDEX" }, + { "TARGET" }, + { "TITLE" }, + { "TYPE" }, + { "URN" }, + { 0 } /* Terminate list */ +}; + +static attr address_attr[] = { /* ADDRESS attributes */ + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "NOWRAP" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr applet_attr[] = { /* APPLET attributes */ + { "ALIGN" }, + { "ALT" }, + { "CLASS" }, + { "CLEAR" }, + { "CODE" }, + { "CODEBASE" }, + { "DIR" }, + { "DOWNLOAD" }, + { "HEIGHT" }, + { "HSPACE" }, + { "ID" }, + { "LANG" }, + { "NAME" }, + { "STYLE" }, + { "TITLE" }, + { "VSPACE" }, + { "WIDTH" }, + { 0 } /* Terminate list */ +}; + +static attr area_attr[] = { /* AREA attributes */ + { "ALT" }, + { "CLASS" }, + { "CLEAR" }, + { "COORDS" }, + { "DIR" }, + { "HREF" }, + { "ID" }, + { "LANG" }, + { "NOHREF" }, + { "NOTAB" }, + { "ONCLICK" }, + { "ONMOUSEOUT" }, + { "ONMOUSEOVER" }, + { "SHAPE" }, + { "STYLE" }, + { "TABINDEX" }, + { "TARGET" }, + { "TITLE" }, + { 0 } /* Terminate list */ +}; + +static attr base_attr[] = { /* BASE attributes */ + { "HREF" }, + { "TARGET" }, + { 0 } /* Terminate list */ +}; + +static attr bgsound_attr[] = { /* BGSOUND attributes */ + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "LOOP" }, + { "SRC" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr body_attr[] = { /* BODY attributes */ + { "ALINK" }, + { "BACKGROUND" }, + { "BGCOLOR" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "LINK" }, + { "STYLE" }, + { "ONLOAD" }, + { "ONUNLOAD" }, + { "TEXT" }, + { "VLINK" }, + { 0 } /* Terminate list */ +}; + +static attr bodytext_attr[] = { /* BODYTEXT attributes */ + { "CLASS" }, + { "CLEAR" }, + { "DATA" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "NAME" }, + { "OBJECT" }, + { "REF" }, + { "STYLE" }, + { "TYPE" }, + { "VALUE" }, + { "VALUETYPE" }, + { 0 } /* Terminate list */ +}; + +static attr bq_attr[] = { /* BQ (BLOCKQUOTE) attributes */ + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "NOWRAP" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr caption_attr[] = { /* CAPTION attributes */ + { "ACCESSKEY" }, + { "ALIGN" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr col_attr[] = { /* COL and COLGROUP attributes */ + { "ALIGN" }, + { "CHAR" }, + { "CHAROFF" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "SPAN" }, + { "STYLE" }, + { "VALIGN" }, + { "WIDTH" }, + { 0 } /* Terminate list */ +}; + +static attr credit_attr[] = { /* CREDIT attributes */ + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr div_attr[] = { /* DIV attribures */ + { "ALIGN" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr embed_attr[] = { /* EMBED attributes */ + { "ALIGN" }, /* (including, for now, those from FIG and IMG) */ + { "ALT" }, + { "BORDER" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "HEIGHT" }, + { "ID" }, + { "IMAGEMAP" }, + { "ISMAP" }, + { "LANG" }, + { "MD" }, + { "NAME" }, + { "NOFLOW" }, + { "PARAMS" }, + { "SRC" }, + { "STYLE" }, + { "UNITS" }, + { "USEMAP" }, + { "WIDTH" }, + { 0 } /* Terminate list */ +}; + +static attr fig_attr[] = { /* FIG attributes */ + { "ALIGN" }, + { "BORDER" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "HEIGHT" }, + { "ID" }, + { "IMAGEMAP" }, + { "ISOBJECT" }, + { "LANG" }, + { "MD" }, + { "NOFLOW" }, + { "SRC" }, + { "STYLE" }, + { "UNITS" }, + { "WIDTH" }, + { 0 } /* Terminate list */ +}; + +static attr fieldset_attr[] = { /* FIELDSET attributes */ + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "STYLE" }, + { "TITLE" }, + { 0 } /* Terminate list */ +}; + +static attr fn_attr[] = { /* FN attributes */ + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr font_attr[] = { /* FONT attributes */ + { "CLASS" }, + { "CLEAR" }, + { "COLOR" }, + { "DIR" }, + { "END" }, + { "FACE" }, + { "ID" }, + { "LANG" }, + { "SIZE" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr form_attr[] = { /* FORM attributes */ + { "ACTION"}, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ENCTYPE" }, + { "ID" }, + { "LANG" }, + { "METHOD" }, + { "ONSUBMIT" }, + { "SCRIPT" }, + { "STYLE" }, + { "SUBJECT" }, + { "TARGET" }, + { "TITLE" }, + { 0 } /* Terminate list */ +}; + +static attr frame_attr[] = { /* FRAME attributes */ + { "MARGINHEIGHT"}, + { "MARGINWIDTH" }, + { "NAME" }, + { "NORESIZE" }, + { "SCROLLING" }, + { "SRC" }, + { 0 } /* Terminate list */ +}; + +static attr frameset_attr[] = { /* FRAMESET attributes */ + { "COLS"}, + { "ROWS" }, + { 0 } /* Terminate list */ +}; + +static attr gen_attr[] = { /* Minimum HTML 3.0 */ + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr glossary_attr[] = { /* DL (and DLC) attributes */ + { "CLASS" }, + { "CLEAR" }, + { "COMPACT" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr h_attr[] = { /* H1 - H6 attributes */ + { "ALIGN" }, + { "CLASS" }, + { "CLEAR" }, + { "DINGBAT" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "MD" }, + { "NOWRAP" }, + { "SEQNUM" }, + { "SKIP" }, + { "SRC" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr hr_attr[] = { /* HR attributes */ + { "ALIGN" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "MD" }, + { "NOSHADE" }, + { "SIZE" }, + { "SRC" }, + { "STYLE" }, + { "WIDTH" }, + { 0 } /* Terminate list */ +}; + +static attr img_attr[] = { /* IMG attributes */ + { "ALIGN" }, + { "ALT" }, + { "BORDER" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "HEIGHT" }, + { "ID" }, + { "ISMAP" }, + { "ISOBJECT" }, + { "LANG" }, + { "MD" }, + { "SRC" }, + { "STYLE" }, + { "TITLE" }, + { "UNITS" }, + { "USEMAP" }, + { "WIDTH" }, + { 0 } /* Terminate list */ +}; + +static attr input_attr[] = { /* INPUT attributes */ + { "ACCEPT" }, + { "ALIGN" }, + { "CHECKED" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "DISABLED" }, + { "ERROR" }, + { "HEIGHT" }, + { "ID" }, + { "LANG" }, + { "MAX" }, + { "MAXLENGTH" }, + { "MD" }, + { "MIN" }, + { "NAME" }, + { "NOTAB" }, + { "ONBLUR" }, + { "ONCHANGE" }, + { "ONCLICK" }, + { "ONFOCUS" }, + { "ONSELECT" }, + { "SIZE" }, + { "SRC" }, + { "STYLE" }, + { "TABINDEX" }, + { "TITLE" }, + { "TYPE" }, + { "VALUE" }, + { "WIDTH" }, + { 0 } /* Terminate list */ +}; + +static attr isindex_attr[] = { /* ISINDEX attributes */ + { "ACTION" }, /* Not in spec. Lynx treats it as HREF. - FM */ + { "DIR" }, + { "HREF" }, /* HTML 3.0 attritute for search action. - FM */ + { "LANG" }, + { "PROMPT" }, /* HTML 3.0 attribute for prompt string. - FM */ + { 0 } /* Terminate list */ +}; + +static attr label_attr[] = { /* LABEL attributes */ + { "ACCESSKEY" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "FOR" }, + { "ID" }, + { "LANG" }, + { "ONCLICK" }, + { "STYLE" }, + { "TITLE" }, + { 0 } /* Terminate list */ +}; + +static attr link_attr[] = { /* LINK attributes */ + { "CLASS" }, + { "HREF" }, + { "ID" }, + { "REL" }, + { "REV" }, + { "STYLE" }, + { "TARGET" }, + { "TITLE" }, + { "TYPE" }, + { 0 } /* Terminate list */ +}; + +static attr list_attr[] = { /* LI attributes */ + { "CLASS" }, + { "CLEAR" }, + { "DINGBAT" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "MD" }, + { "SRC" }, + { "SKIP" }, + { "STYLE" }, + { "TYPE" }, + { "VALUE" }, + { 0 } /* Terminate list */ +}; + +static attr map_attr[] = { /* MAP attributes */ + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "NAME" }, + { "STYLE" }, + { "TITLE" }, + { 0 } /* Terminate list */ +}; + +static attr math_attr[] = { /* MATH attributes */ + { "BOX" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr meta_attr[] = { /* META attributes */ + { "CONTENT" }, + { "HTTP-EQUIV" }, + { "NAME" }, + { 0 } /* Terminate list */ +}; + +static attr nextid_attr[] = { /* NEXTID attribures */ + { "N" } +}; + +static attr note_attr[] = { /* NOTE attributes */ + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "MD" }, + { "ROLE" }, + { "SRC" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr object_attr[] = { /* OBJECT attributes */ + { "ALIGN" }, + { "BORDER" }, + { "CLASS" }, + { "CLASSID" }, + { "CODEBASE" }, + { "CODETYPE" }, + { "DATA" }, + { "DECLARE" }, + { "DIR" }, + { "HEIGHT" }, + { "HSPACE" }, + { "ID" }, + { "ISMAP" }, + { "LANG" }, + { "NAME" }, + { "NOTAB" }, + { "SHAPES" }, + { "STANDBY" }, + { "STYLE" }, + { "TABINDEX" }, + { "TITLE" }, + { "TYPE" }, + { "USEMAP" }, + { "VSPACE" }, + { "WIDTH" }, + { 0 } /* Terminate list */ +}; + +static attr olist_attr[] = { /* OL attributes */ + { "CLASS" }, + { "CLEAR" }, + { "COMPACT" }, + { "CONTINUE" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "SEQNUM" }, + { "START" }, + { "STYLE" }, + { "TYPE" }, + { 0 } /* Terminate list */ +}; + +static attr option_attr[] = { /* OPTION attributes */ + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "DISABLED" }, + { "ERROR" }, + { "ID" }, + { "LANG" }, + { "SELECTED" }, + { "SHAPE" }, + { "STYLE" }, + { "VALUE" }, + { 0 } /* Terminate list */ +}; + +static attr overlay_attr[] = { /* OVERLAY attributes */ + { "CLASS" }, + { "HEIGHT" }, + { "ID" }, + { "IMAGEMAP" }, + { "MD" }, + { "SRC" }, + { "STYLE" }, + { "UNITS" }, + { "WIDTH" }, + { "X" }, + { "Y" }, + { 0 } /* Terminate list */ +}; + +static attr p_attr[] = { /* P attributes */ + { "ALIGN" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "NOWRAP" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr param_attr[] = { /* PARAM attribures */ + { "ACCEPT" }, + { "ACCEPT-CHARSET" }, + { "ACCEPT-ENCODING" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "DATA" }, + { "NAME" }, + { "OBJECT" }, + { "REF" }, + { "STYLE" }, + { "TYPE" }, + { "VALUE" }, + { "VALUEREF" }, + { "VALUETYPE" }, + { 0 } /* Terminate list */ +}; + +static attr script_attr[] = { /* SCRIPT attribures */ + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "EVENT" }, + { "FOR" }, + { "ID" }, + { "LANG" }, + { "LANGUAGE" }, + { "NAME" }, + { "SCRIPTENGINE" }, + { "SRC" }, + { "TYPE" }, + { "STYLE" }, + { 0 } /* Terminate list */ +}; + +static attr select_attr[] = { /* SELECT attributes */ + { "ALIGN" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "DISABLED" }, + { "ERROR" }, + { "HEIGHT" }, + { "ID" }, + { "LANG" }, + { "MD" }, + { "MULTIPLE" }, + { "NAME" }, + { "NOTAB" }, + { "ONBLUR" }, + { "ONCHANGE" }, + { "ONFOCUS" }, + { "SIZE" }, + { "STYLE" }, + { "TABINDEX" }, + { "TITLE" }, + { "UNITS" }, + { "WIDTH" }, + { 0 } /* Terminate list */ +}; + +static attr style_attr[] = { /* STYLE attributes */ + { "DIR" }, + { "LANG" }, + { "NOTATION" }, + { "TITLE" }, + { 0 } /* Terminate list */ +}; + +static attr tab_attr[] = { /* TAB attributes */ + { "ALIGN" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "DP" }, + { "ID" }, + { "INDENT" }, + { "LANG" }, + { "STYLE" }, + { "TO" }, + { 0 } /* Terminate list */ +}; + +static attr table_attr[] = { /* TABLE attributes */ + { "ALIGN" }, + { "BORDER" }, + { "CELLPADDING" }, + { "CELLSPACING" }, + { "CLASS" }, + { "CLEAR" }, + { "COLS" }, + { "COLSPEC" }, + { "DIR" }, + { "DP" }, + { "FRAME" }, + { "ID" }, + { "LANG" }, + { "NOFLOW" }, + { "NOWRAP" }, + { "RULES" }, + { "STYLE" }, + { "UNITS" }, + { "WIDTH" }, + { 0 } /* Terminate list */ +}; + +static attr td_attr[] = { /* TD and TH attributes */ + { "ALIGN" }, + { "AXES" }, + { "AXIS" }, + { "CHAR" }, + { "CHAROFF" }, + { "CLASS" }, + { "CLEAR" }, + { "COLSPAN" }, + { "DIR" }, + { "DP" }, + { "ID" }, + { "LANG" }, + { "NOWRAP" }, + { "ROWSPAN" }, + { "STYLE" }, + { "VALIGN" }, + { 0 } /* Terminate list */ +}; + +static attr textarea_attr[] = { /* TEXTAREA attributes */ + { "ALIGN" }, + { "CLASS" }, + { "CLEAR" }, + { "COLS" }, + { "DIR" }, + { "DISABLED" }, + { "ERROR" }, + { "ID" }, + { "LANG" }, + { "NAME" }, + { "NOTAB" }, + { "ONBLUR" }, + { "ONCHANGE" }, + { "ONFOCUS" }, + { "ONSELECT" }, + { "ROWS" }, + { "STYLE" }, + { "TABINDEX" }, + { "TITLE" }, + { 0 } /* Terminate list */ +}; + +static attr tr_attr[] = { /* TR, THEAD, TFOOT, and TBODY attributes */ + { "ALIGN" }, + { "CHAR" }, + { "CHAROFF" }, + { "CLASS" }, + { "CLEAR" }, + { "DIR" }, + { "DP" }, + { "ID" }, + { "LANG" }, + { "NOWRAP" }, + { "STYLE" }, + { "VALIGN" }, + { 0 } /* Terminate list */ +}; + +static attr ulist_attr[] = { /* UL attributes */ + { "CLASS" }, + { "CLEAR" }, + { "COMPACT" }, + { "DINGBAT" }, + { "DIR" }, + { "ID" }, + { "LANG" }, + { "MD" }, + { "PLAIN" }, + { "SRC" }, + { "STYLE" }, + { "TYPE" }, + { "WRAP" }, + { 0 } /* Terminate list */ +}; + +/* Elements +** -------- +** +** Must match definitions in HTMLDTD.html! +** Must be in alphabetical order. +** +** Name, Attributes, content +*/ +static HTTag tags[HTML_ELEMENTS] = { + { "A" , a_attr, HTML_A_ATTRIBUTES, SGML_MIXED }, + { "ABBREV" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "ACRONYM" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "ADDRESS" , address_attr, HTML_ADDRESS_ATTRIBUTES, SGML_MIXED }, + { "APPLET" , applet_attr, HTML_APPLET_ATTRIBUTES, SGML_MIXED }, + { "AREA" , area_attr, HTML_AREA_ATTRIBUTES, SGML_EMPTY }, + { "AU" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "AUTHOR" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "B" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "BANNER" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "BASE" , base_attr, HTML_BASE_ATTRIBUTES, SGML_EMPTY }, + { "BASEFONT", font_attr, HTML_FONT_ATTRIBUTES, SGML_EMPTY }, + { "BDO" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "BGSOUND" , bgsound_attr, HTML_BGSOUND_ATTRIBUTES, SGML_EMPTY }, + { "BIG" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "BLINK" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "BLOCKQUOTE", bq_attr, HTML_BQ_ATTRIBUTES, SGML_MIXED }, + { "BODY" , body_attr, HTML_BODY_ATTRIBUTES, SGML_MIXED }, + { "BODYTEXT", bodytext_attr,HTML_BODYTEXT_ATTRIBUTES, SGML_MIXED }, + { "BQ" , bq_attr, HTML_BQ_ATTRIBUTES, SGML_MIXED }, + { "BR" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_EMPTY }, + { "CAPTION" , caption_attr, HTML_CAPTION_ATTRIBUTES, SGML_MIXED }, + { "CENTER" , div_attr, HTML_DIV_ATTRIBUTES, SGML_MIXED }, + { "CITE" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "CODE" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "COL" , col_attr, HTML_COL_ATTRIBUTES, SGML_EMPTY }, + { "COLGROUP", col_attr, HTML_COL_ATTRIBUTES, SGML_EMPTY }, + { "COMMENT" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "CREDIT" , credit_attr, HTML_CREDIT_ATTRIBUTES, SGML_MIXED }, + { "DD" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_EMPTY }, + { "DEL" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "DFN" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "DIR" , ulist_attr, HTML_UL_ATTRIBUTES, SGML_MIXED }, + { "DIV" , div_attr, HTML_DIV_ATTRIBUTES, SGML_MIXED }, + { "DL" , glossary_attr, HTML_DL_ATTRIBUTES, SGML_MIXED }, + { "DLC" , glossary_attr, HTML_DL_ATTRIBUTES, SGML_MIXED }, + { "DT" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_EMPTY }, + { "EM" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "EMBED" , embed_attr, HTML_EMBED_ATTRIBUTES, SGML_EMPTY }, + { "FIELDSET", fieldset_attr,HTML_FIELDSET_ATTRIBUTES, SGML_MIXED }, + { "FIG" , fig_attr, HTML_FIG_ATTRIBUTES, SGML_MIXED }, + { "FN" , fn_attr, HTML_FN_ATTRIBUTES, SGML_MIXED }, + { "FONT" , font_attr, HTML_FONT_ATTRIBUTES, SGML_EMPTY }, + { "FORM" , form_attr, HTML_FORM_ATTRIBUTES, SGML_MIXED }, + { "FRAME" , frame_attr, HTML_FRAME_ATTRIBUTES, SGML_EMPTY }, + { "FRAMESET", frameset_attr,HTML_FRAMESET_ATTRIBUTES, SGML_MIXED }, + { "H1" , h_attr, HTML_H_ATTRIBUTES, SGML_MIXED }, + { "H2" , h_attr, HTML_H_ATTRIBUTES, SGML_MIXED }, + { "H3" , h_attr, HTML_H_ATTRIBUTES, SGML_MIXED }, + { "H4" , h_attr, HTML_H_ATTRIBUTES, SGML_MIXED }, + { "H5" , h_attr, HTML_H_ATTRIBUTES, SGML_MIXED }, + { "H6" , h_attr, HTML_H_ATTRIBUTES, SGML_MIXED }, + { "HEAD" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "HR" , hr_attr, HTML_HR_ATTRIBUTES, SGML_EMPTY }, + { "HTML" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "I" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "IMG" , img_attr, HTML_IMG_ATTRIBUTES, SGML_EMPTY }, + { "INPUT" , input_attr, HTML_INPUT_ATTRIBUTES, SGML_EMPTY }, + { "INS" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "ISINDEX" , isindex_attr, HTML_ISINDEX_ATTRIBUTES,SGML_EMPTY }, + { "KBD" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "LABEL" , label_attr, HTML_LABEL_ATTRIBUTES, SGML_MIXED }, + { "LH" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_EMPTY }, + { "LI" , list_attr, HTML_LI_ATTRIBUTES, SGML_EMPTY }, + { "LINK" , link_attr, HTML_LINK_ATTRIBUTES, SGML_EMPTY }, + { "LISTING" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_LITTERAL }, + { "MAP" , map_attr, HTML_MAP_ATTRIBUTES, SGML_MIXED }, + { "MARQUEE" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "MATH" , math_attr, HTML_MATH_ATTRIBUTES, SGML_LITTERAL }, + { "MENU" , ulist_attr, HTML_UL_ATTRIBUTES, SGML_MIXED }, + { "META" , meta_attr, HTML_META_ATTRIBUTES, SGML_EMPTY }, + { "NEXTID" , nextid_attr, 1, SGML_EMPTY }, + { "NOFRAMES", gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "NOTE" , note_attr, HTML_NOTE_ATTRIBUTES, SGML_MIXED }, + { "OBJECT" , object_attr, HTML_OBJECT_ATTRIBUTES, SGML_LITTERAL }, + { "OL" , olist_attr, HTML_OL_ATTRIBUTES, SGML_MIXED }, + { "OPTION" , option_attr, HTML_OPTION_ATTRIBUTES, SGML_EMPTY }, + { "OVERLAY" , overlay_attr, HTML_OVERLAY_ATTRIBUTES, SGML_EMPTY }, + { "P" , p_attr, HTML_P_ATTRIBUTES, SGML_EMPTY }, + { "PARAM" , param_attr, HTML_PARAM_ATTRIBUTES, SGML_EMPTY }, + { "PLAINTEXT", gen_attr, HTML_GEN_ATTRIBUTES, SGML_LITTERAL }, + { "PRE" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "Q" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "S" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "SAMP" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "SCRIPT" , script_attr, HTML_SCRIPT_ATTRIBUTES, SGML_LITTERAL }, + { "SELECT" , select_attr, HTML_SELECT_ATTRIBUTES, SGML_MIXED }, + { "SMALL" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "SPAN" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "SPOT" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_EMPTY }, + { "STRIKE" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "STRONG" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "STYLE" , style_attr, HTML_STYLE_ATTRIBUTES, SGML_LITTERAL }, + { "SUB" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "SUP" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "TAB" , tab_attr, HTML_TAB_ATTRIBUTES, SGML_EMPTY }, + { "TABLE" , table_attr, HTML_TABLE_ATTRIBUTES, SGML_MIXED }, + { "TBODY" , tr_attr, HTML_TR_ATTRIBUTES, SGML_EMPTY }, + { "TD" , td_attr, HTML_TD_ATTRIBUTES, SGML_EMPTY }, + { "TEXTAREA", textarea_attr,HTML_TEXTAREA_ATTRIBUTES, SGML_LITTERAL }, + { "TEXTFLOW", bodytext_attr,HTML_BODYTEXT_ATTRIBUTES, SGML_MIXED }, + { "TFOOT" , tr_attr, HTML_TR_ATTRIBUTES, SGML_EMPTY }, + { "TH" , td_attr, HTML_TD_ATTRIBUTES, SGML_EMPTY }, + { "THEAD" , tr_attr, HTML_TR_ATTRIBUTES, SGML_EMPTY }, + { "TITLE", gen_attr, HTML_GEN_ATTRIBUTES, SGML_RCDATA }, + { "TR" , tr_attr, HTML_TR_ATTRIBUTES, SGML_EMPTY }, + { "TT" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "U" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "UL" , ulist_attr, HTML_UL_ATTRIBUTES, SGML_MIXED }, + { "VAR" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_MIXED }, + { "XMP" , gen_attr, HTML_GEN_ATTRIBUTES, SGML_LITTERAL }, +}; + + +PUBLIC CONST SGML_dtd HTML_dtd = { + tags, + HTML_ELEMENTS, + entities, + sizeof(entities)/sizeof(char**) +}; + +/* Utility Routine: useful for people building HTML objects */ + +/* Start anchor element +** -------------------- +** +** It is kinda convenient to have a particulr routine for +** starting an anchor element, as everything else for HTML is +** simple anyway. +*/ +struct _HTStructured { + HTStructuredClass * isa; + /* ... */ +}; + +PUBLIC void HTStartAnchor ARGS3(HTStructured *, obj, + CONST char *, name, + CONST char *, href) +{ + BOOL present[HTML_A_ATTRIBUTES]; + CONST char * value[HTML_A_ATTRIBUTES]; + + { + int i; + for(i=0; i<HTML_A_ATTRIBUTES; i++) + present[i] = NO; + } + if (name) { + present[HTML_A_NAME] = YES; + value[HTML_A_NAME] = (CONST char *)name; + } + if (href) { + present[HTML_A_HREF] = YES; + value[HTML_A_HREF] = (CONST char *)href; + } + + (*obj->isa->start_element)(obj, HTML_A , present, value, 0); + +} + +PUBLIC void HTStartIsIndex ARGS3(HTStructured *, obj, + CONST char *, prompt, + CONST char *, href) +{ + BOOL present[HTML_ISINDEX_ATTRIBUTES]; + CONST char * value[HTML_ISINDEX_ATTRIBUTES]; + + { + int i; + for(i=0; i<HTML_ISINDEX_ATTRIBUTES; i++) + present[i] = NO; + } + if (prompt) { + present[HTML_ISINDEX_PROMPT] = YES; + value[HTML_ISINDEX_PROMPT] = (CONST char *)prompt; + } + if (href) { + present[HTML_ISINDEX_HREF] = YES; + value[HTML_ISINDEX_HREF] = (CONST char *)href; + } + + (*obj->isa->start_element)(obj, HTML_ISINDEX , present, value, 0); + +} + diff --git a/WWW/Library/Implementation/HTMLDTD.h b/WWW/Library/Implementation/HTMLDTD.h new file mode 100644 index 00000000..a176e59b --- /dev/null +++ b/WWW/Library/Implementation/HTMLDTD.h @@ -0,0 +1,890 @@ +/* The HTML DTD -- software interface in libwww + HTML DTD - SOFTWARE INTERFACE + + SGML purists should excuse the use of the term "DTD" in this file to represent + DTD-related information which is not exactly a DTD itself. + + The C modular structure doesn't work very well here, as the dtd is partly in the .h and + partly in the .c which are not very independent. Tant pis. + + */ +#ifndef HTMLDTD_H +#define HTMLDTD_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "SGML.h" + +/* + +Element Numbers + + */ + +/* + + Must Match all tables by element! + These include tables in HTMLDTD.c and code in HTML.c. + + */ +typedef enum _HTMLElement { + HTML_A, + HTML_ABBREV, + HTML_ACRONYM, + HTML_ADDRESS, + HTML_APPLET, + HTML_AREA, + HTML_AU, + HTML_AUTHOR, + HTML_B, + HTML_BANNER, + HTML_BASE, + HTML_BASEFONT, + HTML_BDO, + HTML_BGSOUND, + HTML_BIG, + HTML_BLINK, + HTML_BLOCKQUOTE, + HTML_BODY, + HTML_BODYTEXT, + HTML_BQ, + HTML_BR, + HTML_CAPTION, + HTML_CENTER, + HTML_CITE, + HTML_CODE, + HTML_COL, + HTML_COLGROUP, + HTML_COMMENT, + HTML_CREDIT, + HTML_DD, + HTML_DEL, + HTML_DFN, + HTML_DIR, + HTML_DIV, + HTML_DL, + HTML_DLC, + HTML_DT, + HTML_EM, + HTML_EMBED, + HTML_FIELDSET, + HTML_FIG, + HTML_FN, + HTML_FONT, + HTML_FORM, + HTML_FRAME, + HTML_FRAMESET, + HTML_H1, + HTML_H2, + HTML_H3, + HTML_H4, + HTML_H5, + HTML_H6, + HTML_HEAD, + HTML_HR, + HTML_HTML, + HTML_I, + HTML_IMG, + HTML_INPUT, + HTML_INS, + HTML_ISINDEX, + HTML_KBD, + HTML_LABEL, + HTML_LH, + HTML_LI, + HTML_LINK, + HTML_LISTING, + HTML_MAP, + HTML_MARQUEE, + HTML_MATH, + HTML_MENU, + HTML_META, + HTML_NEXTID, + HTML_NOFRAMES, + HTML_NOTE, + HTML_OBJECT, + HTML_OL, + HTML_OPTION, + HTML_OVERLAY, + HTML_P, + HTML_PARAM, + HTML_PLAINTEXT, + HTML_PRE, + HTML_Q, + HTML_S, + HTML_SAMP, + HTML_SCRIPT, + HTML_SELECT, + HTML_SMALL, + HTML_SPAN, + HTML_SPOT, + HTML_STRIKE, + HTML_STRONG, + HTML_STYLE, + HTML_SUB, + HTML_SUP, + HTML_TAB, + HTML_TABLE, + HTML_TBODY, + HTML_TD, + HTML_TEXTAREA, + HTML_TEXTFLOW, + HTML_TFOOT, + HTML_TH, + HTML_THEAD, + HTML_TITLE, + HTML_TR, + HTML_TT, + HTML_U, + HTML_UL, + HTML_VAR, + HTML_XMP } HTMLElement; + +#define HTML_ELEMENTS 111 + +/* + +Attribute numbers + + */ + +/* + + Identifier is HTML_<element>_<attribute>. + These must match the tables in HTML.c! + + */ +#define HTML_A_ACCESSKEY 0 +#define HTML_A_CLASS 1 +#define HTML_A_CLEAR 2 +#define HTML_A_COORDS 3 +#define HTML_A_DIR 4 +#define HTML_A_HREF 5 +#define HTML_A_ID 6 +#define HTML_A_ISMAP 7 +#define HTML_A_LANG 8 +#define HTML_A_MD 9 +#define HTML_A_NAME 10 +#define HTML_A_NOTAB 11 +#define HTML_A_ONCLICK 12 +#define HTML_A_ONMOUSEOUT 13 +#define HTML_A_ONMOUSEOVER 14 +#define HTML_A_REL 15 +#define HTML_A_REV 16 +#define HTML_A_SHAPE 17 +#define HTML_A_STYLE 18 +#define HTML_A_TABINDEX 19 +#define HTML_A_TARGET 20 +#define HTML_A_TITLE 21 +#define HTML_A_TYPE 22 +#define HTML_A_URN 23 +#define HTML_A_ATTRIBUTES 24 + +#define HTML_ADDRESS_CLASS 0 +#define HTML_ADDRESS_CLEAR 1 +#define HTML_ADDRESS_DIR 2 +#define HTML_ADDRESS_ID 3 +#define HTML_ADDRESS_LANG 4 +#define HTML_ADDRESS_NOWRAP 5 +#define HTML_ADDRESS_STYLE 6 +#define HTML_ADDRESS_ATTRIBUTES 7 + +#define HTML_APPLET_ALIGN 0 +#define HTML_APPLET_ALT 1 +#define HTML_APPLET_CLASS 2 +#define HTML_APPLET_CLEAR 3 +#define HTML_APPLET_CODE 4 +#define HTML_APPLET_CODEBASE 5 +#define HTML_APPLET_DIR 6 +#define HTML_APPLET_DOWNLOAD 7 +#define HTML_APPLET_HEIGHT 8 +#define HTML_APPLET_HSPACE 9 +#define HTML_APPLET_ID 10 +#define HTML_APPLET_LANG 11 +#define HTML_APPLET_NAME 12 +#define HTML_APPLET_STYLE 13 +#define HTML_APPLET_TITLE 14 +#define HTML_APPLET_VSPACE 15 +#define HTML_APPLET_WIDTH 16 +#define HTML_APPLET_ATTRIBUTES 17 + +#define HTML_AREA_ALT 0 +#define HTML_AREA_CLASS 1 +#define HTML_AREA_CLEAR 2 +#define HTML_AREA_COORDS 3 +#define HTML_AREA_DIR 4 +#define HTML_AREA_HREF 5 +#define HTML_AREA_ID 6 +#define HTML_AREA_LANG 7 +#define HTML_AREA_NOHREF 8 +#define HTML_AREA_NONOTAB 9 +#define HTML_AREA_ONCLICK 10 +#define HTML_AREA_ONMOUSEOUT 11 +#define HTML_AREA_ONMOUSEOVER 12 +#define HTML_AREA_SHAPE 13 +#define HTML_AREA_STYLE 14 +#define HTML_AREA_TABINDEX 15 +#define HTML_AREA_TARGET 16 +#define HTML_AREA_TITLE 17 +#define HTML_AREA_ATTRIBUTES 18 + +#define HTML_BASE_HREF 0 +#define HTML_BASE_TARGET 1 +#define HTML_BASE_ATTRIBUTES 2 + +#define HTML_BGSOUND_CLASS 0 +#define HTML_BGSOUND_CLEAR 1 +#define HTML_BGSOUND_DIR 2 +#define HTML_BGSOUND_ID 3 +#define HTML_BGSOUND_LANG 4 +#define HTML_BGSOUND_LOOP 5 +#define HTML_BGSOUND_SRC 6 +#define HTML_BGSOUND_STYLE 7 +#define HTML_BGSOUND_ATTRIBUTES 8 + +#define HTML_BQ_CLASS 0 +#define HTML_BQ_CLEAR 1 +#define HTML_BQ_DIR 2 +#define HTML_BQ_ID 3 +#define HTML_BQ_LANG 4 +#define HTML_BQ_NOWRAP 5 +#define HTML_BQ_STYLE 6 +#define HTML_BQ_ATTRIBUTES 7 + +#define HTML_BODYTEXT_CLASS 1 +#define HTML_BODYTEXT_CLEAR 2 +#define HTML_BODYTEXT_DATA 3 +#define HTML_BODYTEXT_DIR 4 +#define HTML_BODYTEXT_ID 5 +#define HTML_BODYTEXT_LANG 6 +#define HTML_BODYTEXT_NAME 7 +#define HTML_BODYTEXT_OBJECT 8 +#define HTML_BODYTEXT_REF 9 +#define HTML_BODYTEXT_STYLE 10 +#define HTML_BODYTEXT_TYPE 11 +#define HTML_BODYTEXT_VALUE 12 +#define HTML_BODYTEXT_VALUETYPE 13 +#define HTML_BODYTEXT_ATTRIBUTES 14 + +#define HTML_BODY_ALINK 0 +#define HTML_BODY_BACKGROUND 1 +#define HTML_BODY_BGCOLOR 2 +#define HTML_BODY_CLASS 3 +#define HTML_BODY_CLEAR 4 +#define HTML_BODY_DIR 5 +#define HTML_BODY_ID 6 +#define HTML_BODY_LANG 7 +#define HTML_BODY_LINK 8 +#define HTML_BODY_ONLOAD 9 +#define HTML_BODY_ONUNLOAD 10 +#define HTML_BODY_STYLE 11 +#define HTML_BODY_TEXT 12 +#define HTML_BODY_VLINK 13 +#define HTML_BODY_ATTRIBUTES 14 + +#define HTML_CAPTION_ACCESSKEY 0 +#define HTML_CAPTION_ALIGN 1 +#define HTML_CAPTION_CLASS 2 +#define HTML_CAPTION_CLEAR 3 +#define HTML_CAPTION_DIR 4 +#define HTML_CAPTION_ID 5 +#define HTML_CAPTION_LANG 6 +#define HTML_CAPTION_STYLE 7 +#define HTML_CAPTION_ATTRIBUTES 8 + +#define HTML_COL_ALIGN 0 +#define HTML_COL_CHAR 1 +#define HTML_COL_CHAROFF 2 +#define HTML_COL_CLASS 3 +#define HTML_COL_CLEAR 4 +#define HTML_COL_DIR 5 +#define HTML_COL_ID 6 +#define HTML_COL_LANG 7 +#define HTML_COL_SPAN 8 +#define HTML_COL_STYLE 9 +#define HTML_COL_VALIGN 10 +#define HTML_COL_WIDTH 11 +#define HTML_COL_ATTRIBUTES 12 + +#define HTML_CREDIT_CLASS 0 +#define HTML_CREDIT_CLEAR 1 +#define HTML_CREDIT_DIR 2 +#define HTML_CREDIT_ID 3 +#define HTML_CREDIT_LANG 4 +#define HTML_CREDIT_STYLE 5 +#define HTML_CREDIT_ATTRIBUTES 6 + +#define HTML_DIV_ALIGN 0 +#define HTML_DIV_CLASS 1 +#define HTML_DIV_CLEAR 2 +#define HTML_DIV_DIR 3 +#define HTML_DIV_ID 4 +#define HTML_DIV_LANG 5 +#define HTML_DIV_STYLE 6 +#define HTML_DIV_ATTRIBUTES 7 + +#define HTML_DL_CLASS 0 +#define HTML_DL_CLEAR 1 +#define HTML_DL_COMPACT 2 +#define HTML_DL_DIR 3 +#define HTML_DL_ID 4 +#define HTML_DL_LANG 5 +#define HTML_DL_STYLE 6 +#define HTML_DL_ATTRIBUTES 7 + +#define HTML_EMBED_ALIGN 0 +#define HTML_EMBED_ALT 1 +#define HTML_EMBED_BORDER 2 +#define HTML_EMBED_CLASS 3 +#define HTML_EMBED_CLEAR 4 +#define HTML_EMBED_DIR 5 +#define HTML_EMBED_HEIGHT 6 +#define HTML_EMBED_ID 7 +#define HTML_EMBED_IMAGEMAP 8 +#define HTML_EMBED_ISMAP 9 +#define HTML_EMBED_LANG 10 +#define HTML_EMBED_MD 11 +#define HTML_EMBED_NAME 12 +#define HTML_EMBED_NOFLOW 13 +#define HTML_EMBED_PARAMS 14 +#define HTML_EMBED_SRC 15 +#define HTML_EMBED_STYLE 16 +#define HTML_EMBED_UNITS 17 +#define HTML_EMBED_USEMAP 18 +#define HTML_EMBED_WIDTH 19 +#define HTML_EMBED_ATTRIBUTES 20 + +#define HTML_FIELDSET_CLASS 0 +#define HTML_FIELDSET_CLEAR 1 +#define HTML_FIELDSET_DIR 2 +#define HTML_FIELDSET_ID 3 +#define HTML_FIELDSET_LANG 4 +#define HTML_FIELDSET_STYLE 5 +#define HTML_FIELDSET_TITLE 6 +#define HTML_FIELDSET_ATTRIBUTES 7 + +#define HTML_FIG_ALIGN 0 +#define HTML_FIG_BORDER 1 +#define HTML_FIG_CLASS 2 +#define HTML_FIG_CLEAR 3 +#define HTML_FIG_DIR 4 +#define HTML_FIG_HEIGHT 5 +#define HTML_FIG_ID 6 +#define HTML_FIG_IMAGEMAP 7 +#define HTML_FIG_ISOBJECT 8 +#define HTML_FIG_LANG 9 +#define HTML_FIG_MD 10 +#define HTML_FIG_NOFLOW 11 +#define HTML_FIG_SRC 12 +#define HTML_FIG_STYLE 13 +#define HTML_FIG_UNITS 14 +#define HTML_FIG_WIDTH 15 +#define HTML_FIG_ATTRIBUTES 16 + +#define HTML_FN_CLASS 0 +#define HTML_FN_CLEAR 1 +#define HTML_FN_DIR 2 +#define HTML_FN_ID 3 +#define HTML_FN_LANG 4 +#define HTML_FN_STYLE 5 +#define HTML_FN_ATTRIBUTES 6 + +#define HTML_FONT_CLASS 0 +#define HTML_FONT_CLEAR 1 +#define HTML_FONT_COLOR 2 +#define HTML_FONT_DIR 3 +#define HTML_FONT_END 4 +#define HTML_FONT_FACE 5 +#define HTML_FONT_ID 6 +#define HTML_FONT_LANG 7 +#define HTML_FONT_SIZE 8 +#define HTML_FONT_STYLE 9 +#define HTML_FONT_ATTRIBUTES 10 + +#define HTML_FORM_ACTION 0 +#define HTML_FORM_CLASS 1 +#define HTML_FORM_CLEAR 2 +#define HTML_FORM_DIR 3 +#define HTML_FORM_ENCTYPE 4 +#define HTML_FORM_ID 5 +#define HTML_FORM_LANG 6 +#define HTML_FORM_METHOD 7 +#define HTML_FORM_ONSUBMIT 8 +#define HTML_FORM_SCRIPT 9 +#define HTML_FORM_STYLE 10 +#define HTML_FORM_SUBJECT 11 +#define HTML_FORM_TARGET 12 +#define HTML_FORM_TITLE 13 +#define HTML_FORM_ATTRIBUTES 14 + +#define HTML_FRAME_MARGINHEIGHT 0 +#define HTML_FRAME_MARGINWIDTH 1 +#define HTML_FRAME_NAME 2 +#define HTML_FRAME_NORESIZE 3 +#define HTML_FRAME_SCROLLING 4 +#define HTML_FRAME_SRC 5 +#define HTML_FRAME_ATTRIBUTES 6 + +#define HTML_FRAMESET_COLS 0 +#define HTML_FRAMESET_ROWS 1 +#define HTML_FRAMESET_ATTRIBUTES 2 + +#define HTML_GEN_CLASS 0 +#define HTML_GEN_CLEAR 1 +#define HTML_GEN_DIR 2 +#define HTML_GEN_ID 3 +#define HTML_GEN_LANG 4 +#define HTML_GEN_STYLE 5 +#define HTML_GEN_ATTRIBUTES 6 + +#define HTML_H_ALIGN 0 +#define HTML_H_CLASS 1 +#define HTML_H_CLEAR 2 +#define HTML_H_DINGBAT 3 +#define HTML_H_DIR 4 +#define HTML_H_ID 5 +#define HTML_H_LANG 6 +#define HTML_H_MD 7 +#define HTML_H_NOWRAP 8 +#define HTML_H_SEQNUM 9 +#define HTML_H_SKIP 10 +#define HTML_H_SRC 11 +#define HTML_H_STYLE 12 +#define HTML_H_ATTRIBUTES 13 + +#define HTML_HR_ALIGN 0 +#define HTML_HR_CLASS 1 +#define HTML_HR_CLEAR 2 +#define HTML_HR_DIR 3 +#define HTML_HR_ID 4 +#define HTML_HR_MD 5 +#define HTML_HR_NOSHADE 6 +#define HTML_HR_SIZE 7 +#define HTML_HR_SRC 8 +#define HTML_HR_STYLE 9 +#define HTML_HR_WIDTH 10 +#define HTML_HR_ATTRIBUTES 11 + +#define HTML_IMG_ALIGN 0 +#define HTML_IMG_ALT 1 +#define HTML_IMG_BORDER 2 +#define HTML_IMG_CLASS 3 +#define HTML_IMG_CLEAR 4 +#define HTML_IMG_DIR 5 +#define HTML_IMG_HEIGHT 6 +#define HTML_IMG_ID 7 +#define HTML_IMG_ISMAP 8 +#define HTML_IMG_ISOBJECT 9 +#define HTML_IMG_LANG 10 +#define HTML_IMG_MD 11 +#define HTML_IMG_SRC 12 +#define HTML_IMG_STYLE 13 +#define HTML_IMG_TITLE 14 +#define HTML_IMG_UNITS 15 +#define HTML_IMG_USEMAP 16 +#define HTML_IMG_WIDTH 17 +#define HTML_IMG_ATTRIBUTES 18 + +#define HTML_INPUT_ACCEPT 0 +#define HTML_INPUT_ALIGN 1 +#define HTML_INPUT_CHECKED 2 +#define HTML_INPUT_CLASS 3 +#define HTML_INPUT_CLEAR 4 +#define HTML_INPUT_DIR 5 +#define HTML_INPUT_DISABLED 6 +#define HTML_INPUT_ERROR 7 +#define HTML_INPUT_HEIGHT 8 +#define HTML_INPUT_ID 9 +#define HTML_INPUT_LANG 10 +#define HTML_INPUT_MAX 11 +#define HTML_INPUT_MAXLENGTH 12 +#define HTML_INPUT_MD 13 +#define HTML_INPUT_MIN 14 +#define HTML_INPUT_NAME 15 +#define HTML_INPUT_NOTAB 16 +#define HTML_INPUT_ONBLUR 17 +#define HTML_INPUT_ONCHANGE 18 +#define HTML_INPUT_ONCLICK 19 +#define HTML_INPUT_ONFOCUS 20 +#define HTML_INPUT_ONSELECT 21 +#define HTML_INPUT_SIZE 22 +#define HTML_INPUT_SRC 23 +#define HTML_INPUT_STYLE 24 +#define HTML_INPUT_TABINDEX 25 +#define HTML_INPUT_TITLE 26 +#define HTML_INPUT_TYPE 27 +#define HTML_INPUT_VALUE 28 +#define HTML_INPUT_WIDTH 29 +#define HTML_INPUT_ATTRIBUTES 30 + +#define HTML_ISINDEX_ACTION 0 /* Treat as synonym for HREF. - FM */ +#define HTML_ISINDEX_DIR 1 +#define HTML_ISINDEX_HREF 2 /* HTML 3.0 "action". - FM */ +#define HTML_ISINDEX_LANG 3 +#define HTML_ISINDEX_PROMPT 4 /* HTML 3.0 "prompt". - FM */ +#define HTML_ISINDEX_ATTRIBUTES 5 + +#define HTML_LINK_CLASS 0 +#define HTML_LINK_HREF 1 +#define HTML_LINK_ID 2 +#define HTML_LINK_REL 3 +#define HTML_LINK_REV 4 +#define HTML_LINK_STYLE 5 +#define HTML_LINK_TARGET 6 +#define HTML_LINK_TITLE 7 +#define HTML_LINK_TYPE 8 +#define HTML_LINK_ATTRIBUTES 9 + +#define HTML_LABEL_ACCESSKEY 0 +#define HTML_LABEL_CLASS 1 +#define HTML_LABEL_CLEAR 2 +#define HTML_LABEL_DIR 3 +#define HTML_LABEL_FOR 4 +#define HTML_LABEL_ID 5 +#define HTML_LABEL_LANG 6 +#define HTML_LABEL_ONCLICK 7 +#define HTML_LABEL_STYLE 8 +#define HTML_LABEL_TITLE 9 +#define HTML_LABEL_ATTRIBUTES 10 + +#define HTML_LI_CLASS 0 +#define HTML_LI_CLEAR 1 +#define HTML_LI_DINGBAT 2 +#define HTML_LI_DIR 3 +#define HTML_LI_ID 4 +#define HTML_LI_LANG 5 +#define HTML_LI_MD 6 +#define HTML_LI_SRC 7 +#define HTML_LI_SKIP 8 +#define HTML_LI_STYLE 9 +#define HTML_LI_TYPE 10 +#define HTML_LI_VALUE 11 +#define HTML_LI_ATTRIBUTES 12 + +#define HTML_MAP_CLASS 0 +#define HTML_MAP_CLEAR 1 +#define HTML_MAP_DIR 2 +#define HTML_MAP_ID 3 +#define HTML_MAP_LANG 4 +#define HTML_MAP_NAME 5 +#define HTML_MAP_STYLE 6 +#define HTML_MAP_TITLE 7 +#define HTML_MAP_ATTRIBUTES 8 + +#define HTML_MATH_BOX 0 +#define HTML_MATH_CLASS 1 +#define HTML_MATH_CLEAR 2 +#define HTML_MATH_DIR 3 +#define HTML_MATH_ID 4 +#define HTML_MATH_LANG 5 +#define HTML_MATH_STYLE 6 +#define HTML_MATH_ATTRIBUTES 7 + +#define HTML_META_CONTENT 0 +#define HTML_META_HTTP_EQUIV 1 /* For parsing in HTML.c - FM */ +#define HTML_META_NAME 2 +#define HTML_META_ATTRIBUTES 3 + +#define NEXTID_N 0 + +#define HTML_NOTE_CLASS 0 +#define HTML_NOTE_CLEAR 1 +#define HTML_NOTE_DIR 2 +#define HTML_NOTE_ID 3 +#define HTML_NOTE_LANG 4 +#define HTML_NOTE_MD 5 +#define HTML_NOTE_ROLE 6 /* Old name for CLASS - FM */ +#define HTML_NOTE_SRC 7 +#define HTML_NOTE_STYLE 8 +#define HTML_NOTE_ATTRIBUTES 9 + +#define HTML_OBJECT_ALIGN 0 +#define HTML_OBJECT_BORDER 1 +#define HTML_OBJECT_CLASS 2 +#define HTML_OBJECT_CLASSID 3 +#define HTML_OBJECT_CODEBASE 4 +#define HTML_OBJECT_CODETYPE 5 +#define HTML_OBJECT_DATA 6 +#define HTML_OBJECT_DECLARE 7 +#define HTML_OBJECT_DIR 8 +#define HTML_OBJECT_HEIGHT 9 +#define HTML_OBJECT_HSPACE 10 +#define HTML_OBJECT_ID 11 +#define HTML_OBJECT_ISMAP 12 +#define HTML_OBJECT_LANG 13 +#define HTML_OBJECT_NAME 14 +#define HTML_OBJECT_NOTAB 15 +#define HTML_OBJECT_SHAPES 16 +#define HTML_OBJECT_STANDBY 17 +#define HTML_OBJECT_STYLE 18 +#define HTML_OBJECT_TABINDEX 19 +#define HTML_OBJECT_TITLE 20 +#define HTML_OBJECT_TYPE 21 +#define HTML_OBJECT_USEMAP 22 +#define HTML_OBJECT_VSPACE 23 +#define HTML_OBJECT_WIDTH 24 +#define HTML_OBJECT_ATTRIBUTES 25 + +#define HTML_OL_CLASS 0 +#define HTML_OL_CLEAR 1 +#define HTML_OL_COMPACT 2 +#define HTML_OL_CONTINUE 3 +#define HTML_OL_DIR 4 +#define HTML_OL_ID 5 +#define HTML_OL_LANG 6 +#define HTML_OL_SEQNUM 7 +#define HTML_OL_START 8 +#define HTML_OL_STYLE 9 +#define HTML_OL_TYPE 10 +#define HTML_OL_ATTRIBUTES 11 + +#define HTML_OPTION_CLASS 0 +#define HTML_OPTION_CLEAR 1 +#define HTML_OPTION_DIR 2 +#define HTML_OPTION_DISABLED 3 +#define HTML_OPTION_ERROR 4 +#define HTML_OPTION_ID 5 +#define HTML_OPTION_LANG 6 +#define HTML_OPTION_SELECTED 7 +#define HTML_OPTION_SHAPE 8 +#define HTML_OPTION_STYLE 9 +#define HTML_OPTION_VALUE 10 +#define HTML_OPTION_ATTRIBUTES 11 + +#define HTML_OVERLAY_CLASS 0 +#define HTML_OVERLAY_HEIGHT 1 +#define HTML_OVERLAY_ID 2 +#define HTML_OVERLAY_IMAGEMAP 3 +#define HTML_OVERLAY_MD 4 +#define HTML_OVERLAY_SRC 5 +#define HTML_OVERLAY_STYLE 6 +#define HTML_OVERLAY_UNITS 7 +#define HTML_OVERLAY_WIDTH 8 +#define HTML_OVERLAY_X 9 +#define HTML_OVERLAY_Y 10 +#define HTML_OVERLAY_ATTRIBUTES 11 + +#define HTML_P_ALIGN 0 +#define HTML_P_CLASS 1 +#define HTML_P_CLEAR 2 +#define HTML_P_DIR 3 +#define HTML_P_ID 4 +#define HTML_P_LANG 5 +#define HTML_P_STYLE 6 +#define HTML_P_NOWRAP 7 +#define HTML_P_ATTRIBUTES 8 + +#define HTML_PARAM_ACCEPT 0 +#define HTML_PARAM_ACCEPT_CHARSET 1 +#define HTML_PARAM_ACCEPT_ENCODING 2 +#define HTML_PARAM_CLASS 3 +#define HTML_PARAM_CLEAR 4 +#define HTML_PARAM_DATA 5 +#define HTML_PARAM_DIR 6 +#define HTML_PARAM_ID 7 +#define HTML_PARAM_LANG 8 +#define HTML_PARAM_NAME 9 +#define HTML_PARAM_OBJECT 10 +#define HTML_PARAM_REF 11 +#define HTML_PARAM_STYLE 12 +#define HTML_PARAM_TYPE 13 +#define HTML_PARAM_VALUE 14 +#define HTML_PARAM_VALUEREF 15 /* Use VALUETYPE (DATA|REF|OBJECT). - FM */ +#define HTML_PARAM_VALUETYPE 16 +#define HTML_PARAM_ATTRIBUTES 17 + +#define HTML_SCRIPT_CLASS 0 +#define HTML_SCRIPT_CLEAR 1 +#define HTML_SCRIPT_DIR 2 +#define HTML_SCRIPT_EVENT 3 +#define HTML_SCRIPT_FOR 4 +#define HTML_SCRIPT_ID 5 +#define HTML_SCRIPT_LANG 6 +#define HTML_SCRIPT_LANGUAGE 7 +#define HTML_SCRIPT_NAME 8 +#define HTML_SCRIPT_SCRIPTENGINE 9 +#define HTML_SCRIPT_SRC 10 +#define HTML_SCRIPT_STYLE 11 +#define HTML_SCRIPT_TYPE 12 +#define HTML_SCRIPT_ATTRIBUTES 13 + +#define HTML_SELECT_ALIGN 0 +#define HTML_SELECT_CLASS 1 +#define HTML_SELECT_CLEAR 2 +#define HTML_SELECT_DIR 3 +#define HTML_SELECT_DISABLED 4 +#define HTML_SELECT_ERROR 5 +#define HTML_SELECT_HEIGHT 6 +#define HTML_SELECT_ID 7 +#define HTML_SELECT_LANG 8 +#define HTML_SELECT_MD 9 +#define HTML_SELECT_MULTIPLE 10 +#define HTML_SELECT_NAME 11 +#define HTML_SELECT_NOTAB 12 +#define HTML_SELECT_ONBLUR 13 +#define HTML_SELECT_ONCHANGE 14 +#define HTML_SELECT_ONFOCUS 15 +#define HTML_SELECT_SIZE 16 +#define HTML_SELECT_STYLE 17 +#define HTML_SELECT_TABINDEX 18 +#define HTML_SELECT_TITLE 19 +#define HTML_SELECT_UNITS 20 +#define HTML_SELECT_WIDTH 21 +#define HTML_SELECT_ATTRIBUTES 22 + +#define HTML_STYLE_DIR 0 +#define HTML_STYLE_LANG 1 +#define HTML_STYLE_NOTATION 2 +#define HTML_STYLE_TITLE 3 +#define HTML_STYLE_ATTRIBUTES 4 + +#define HTML_TAB_ALIGN 0 +#define HTML_TAB_CLASS 1 +#define HTML_TAB_CLEAR 2 +#define HTML_TAB_DIR 3 +#define HTML_TAB_DP 4 +#define HTML_TAB_ID 5 +#define HTML_TAB_INDENT 6 +#define HTML_TAB_LANG 7 +#define HTML_TAB_STYLE 8 +#define HTML_TAB_TO 9 +#define HTML_TAB_ATTRIBUTES 10 + +#define HTML_TABLE_ALIGN 0 +#define HTML_TABLE_BORDER 1 +#define HTML_TABLE_CELLPADDING 2 +#define HTML_TABLE_CELLSPACING 3 +#define HTML_TABLE_CLASS 4 +#define HTML_TABLE_CLEAR 5 +#define HTML_TABLE_COLS 6 +#define HTML_TABLE_COLSPEC 7 +#define HTML_TABLE_DIR 8 +#define HTML_TABLE_DP 9 +#define HTML_TABLE_FRAME 10 +#define HTML_TABLE_ID 11 +#define HTML_TABLE_LANG 12 +#define HTML_TABLE_NOFLOW 13 +#define HTML_TABLE_NOWRAP 14 +#define HTML_TABLE_RULES 15 +#define HTML_TABLE_STYLE 16 +#define HTML_TABLE_UNITS 17 +#define HTML_TABLE_WIDTH 18 +#define HTML_TABLE_ATTRIBUTES 19 + +#define HTML_TD_ALIGN 0 +#define HTML_TD_AXES 1 +#define HTML_TD_AXIS 2 +#define HTML_TD_CHAR 3 +#define HTML_TD_CHAROFF 4 +#define HTML_TD_CLASS 5 +#define HTML_TD_CLEAR 6 +#define HTML_TD_COLSPAN 7 +#define HTML_TD_DIR 8 +#define HTML_TD_DP 9 +#define HTML_TD_ID 10 +#define HTML_TD_LANG 11 +#define HTML_TD_NOWRAP 12 +#define HTML_TD_ROWSPAN 13 +#define HTML_TD_STYLE 14 +#define HTML_TD_VALIGN 15 +#define HTML_TD_ATTRIBUTES 16 + +#define HTML_TEXTAREA_ALIGN 0 +#define HTML_TEXTAREA_CLASS 1 +#define HTML_TEXTAREA_CLEAR 2 +#define HTML_TEXTAREA_COLS 3 +#define HTML_TEXTAREA_DIR 4 +#define HTML_TEXTAREA_DISABLED 5 +#define HTML_TEXTAREA_ERROR 6 +#define HTML_TEXTAREA_ID 7 +#define HTML_TEXTAREA_LANG 8 +#define HTML_TEXTAREA_NAME 9 +#define HTML_TEXTAREA_NOTAB 10 +#define HTML_TEXTAREA_ONBLUR 11 +#define HTML_TEXTAREA_ONCHANGE 12 +#define HTML_TEXTAREA_ONFOCUS 13 +#define HTML_TEXTAREA_ONSELECT 14 +#define HTML_TEXTAREA_ROWS 15 +#define HTML_TEXTAREA_STYLE 16 +#define HTML_TEXTAREA_TABINDEX 17 +#define HTML_TEXTAREA_TITLE 18 +#define HTML_TEXTAREA_ATTRIBUTES 19 + +#define HTML_TR_ALIGN 0 +#define HTML_TR_CHAR 1 +#define HTML_TR_CHAROFF 2 +#define HTML_TR_CLASS 3 +#define HTML_TR_CLEAR 4 +#define HTML_TR_DIR 5 +#define HTML_TR_DP 6 +#define HTML_TR_ID 7 +#define HTML_TR_LANG 8 +#define HTML_TR_NOWRAP 9 +#define HTML_TR_STYLE 10 +#define HTML_TR_VALIGN 11 +#define HTML_TR_ATTRIBUTES 12 + +#define HTML_UL_CLASS 0 +#define HTML_UL_CLEAR 1 +#define HTML_UL_COMPACT 2 +#define HTML_UL_DINGBAT 3 +#define HTML_UL_DIR 4 +#define HTML_UL_ID 5 +#define HTML_UL_LANG 6 +#define HTML_UL_MD 7 +#define HTML_UL_PLAIN 8 +#define HTML_UL_SRC 9 +#define HTML_UL_STYLE 10 +#define HTML_UL_TYPE 11 +#define HTML_UL_WRAP 12 +#define HTML_UL_ATTRIBUTES 13 + +extern CONST SGML_dtd HTML_dtd; + +/* + +Start anchor element + + It is kinda convenient to have a particular routine for starting an anchor + element, as everything else for HTML is simple anyway. + + ON ENTRY + + targetstream points to a structured stream object. + + name and href point to attribute strings or are NULL if the attribute is + to be omitted. + + */ +extern void HTStartAnchor PARAMS(( + HTStructured * targetstream, + CONST char * name, + CONST char * href)); + +/* + +Start IsIndex element - FM + + It is kinda convenient to have a particular routine for starting an IsIndex + element with the prompt and/or href (action) attributes specified. + + ON ENTRY + + targetstream points to a structured stream object. + + prompt and href point to attribute strings or are NULL if the attribute is + to be omitted. + + */ +extern void HTStartIsIndex PARAMS(( + HTStructured * targetstream, + CONST char * prompt, + CONST char * href)); + + +#endif /* HTMLDTD_H */ + +/* + + End of module definition */ diff --git a/WWW/Library/Implementation/HTMLGen.c b/WWW/Library/Implementation/HTMLGen.c new file mode 100644 index 00000000..bea63bac --- /dev/null +++ b/WWW/Library/Implementation/HTMLGen.c @@ -0,0 +1,367 @@ +/* HTML Generator +** ============== +** +** This version of the HTML object sends HTML markup to the output stream. +** +** Bugs: Line wrapping is not done at all. +** All data handled as PCDATA. +** Should convert old XMP, LISTING and PLAINTEXT to PRE. +** +** It is not obvious to me right now whether the HEAD should be generated +** from the incomming data or the anchor. Currently it is from the former +** which is cleanest. +*/ + +#include "HTUtils.h" +#include "tcp.h" + +#define BUFFER_SIZE 80 /* Line buffer attempts to make neat breaks */ + +/* Implements: +*/ +#include "HTMLGen.h" + +#include <stdio.h> +#include "HTMLDTD.h" +#include "HTStream.h" +#include "SGML.h" +#include "HTFormat.h" + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +#define PUTC(c) (*me->targetClass.put_character)(me->target, c) +/* #define PUTS(s) (*me->targetClass.put_string)(me->target, s) */ +#define PUTB(s,l) (*me->targetClass.put_block)(me->target, s, l) + +/* HTML Object +** ----------- +*/ + +struct _HTStream { + CONST HTStreamClass * isa; + HTStream * target; + HTStreamClass targetClass; /* COPY for speed */ +}; + +struct _HTStructured { + CONST HTStructuredClass * isa; + HTStream * target; + HTStreamClass targetClass; /* COPY for speed */ + + char buffer[BUFFER_SIZE+1]; /* 1for NL */ + char * write_pointer; + char * line_break; + int cleanness; + BOOL delete_line_break_char; + BOOL preformatted; +}; + + +/* Flush Buffer +** ------------ +*/ +PRIVATE void HTMLGen_flush ARGS1(HTStructured *, me) +{ + (*me->targetClass.put_block)(me->target, + me->buffer, + me->write_pointer - me->buffer); + me->write_pointer = me->buffer; + me->line_break = me->buffer; + me->cleanness = 0; + me->delete_line_break_char = NO; +} + + +/* Character handling +** ------------------ +** +** The tricky bits are the line break handling. This attempts +** to synchrononise line breaks on sentence or phrase ends. This +** is important if one stores SGML files in a line-oriented code +** repository, so that if a small change is made, line ends don't +** shift in a ripple-through to apparently change a large part of the +** file. We give extra "cleanness" to spaces appearing directly +** after periods (full stops), [semi]colons and commas. +** This should make the source files easier to read and modify +** by hand, too, though this is not a primary design consideration. +*/ +PRIVATE void HTMLGen_put_character ARGS2(HTStructured *, me, char, c) +{ + + *me->write_pointer++ = c; + + if (c=='\n') { + HTMLGen_flush(me); + return; + } + + if ((!me->preformatted && c==' ')) { + int new_cleanness = 1; + if (me->write_pointer > (me->buffer + 1)) { + char delims[5]; + char * p; + strcpy(delims, ",;:."); /* @@ english bias */ + p = strchr(delims, me->write_pointer[-2]); + if (p) new_cleanness = p - delims + 2; + } + if (new_cleanness >= me->cleanness) { + me->line_break = me->write_pointer - 1; /* Point to space */ + me->cleanness = new_cleanness; + me->delete_line_break_char = YES; + } + } + + /* Flush buffer out when full */ + if (me->write_pointer == me->buffer + BUFFER_SIZE) { + if (me->cleanness) { + char line_break_char = me->line_break[0]; + char * saved = me->line_break; + + if (me->delete_line_break_char) saved++; + me->line_break[0] = '\n'; + (*me->targetClass.put_block)(me->target, + me->buffer, + me->line_break - me->buffer + 1); + me->line_break[0] = line_break_char; + { /* move next line in */ + char * p=saved; + char *q; + for(q=me->buffer; p < me->write_pointer; ) + *q++ = *p++; + } + me->cleanness = 0; + me->delete_line_break_char = 0; + me->write_pointer = me->write_pointer - (saved-me->buffer); + + } else { + (*me->targetClass.put_block)(me->target, + me->buffer, + BUFFER_SIZE); + me->write_pointer = me->buffer; + } + me->line_break = me->buffer; + } +} + + + +/* String handling +** --------------- +*/ +PRIVATE void HTMLGen_put_string ARGS2(HTStructured *, me, CONST char*, s) +{ + CONST char * p; + for(p=s; *p; p++) HTMLGen_put_character(me, *p); +} + +PRIVATE void HTMLGen_write ARGS3(HTStructured *, me, CONST char*, s, int, l) +{ + CONST char * p; + for(p=s; p<s+l; p++) HTMLGen_put_character(me, *p); +} + + +/* Start Element +** ------------- +** +** Within the opening tag, there may be spaces +** and the line may be broken at these spaces. +*/ +PRIVATE void HTMLGen_start_element ARGS5( + HTStructured *, me, + int, element_number, + CONST BOOL*, present, + CONST char **, value, + char **, insert) +{ + int i; + + BOOL was_preformatted = me->preformatted; + HTTag * tag = &HTML_dtd.tags[element_number]; + + me->preformatted = NO; /* free text within tags */ + HTMLGen_put_character(me, '<'); + HTMLGen_put_string(me, tag->name); + if (present) for (i=0; i< tag->number_of_attributes; i++) { + if (present[i]) { + HTMLGen_put_character(me, ' '); + HTMLGen_put_string(me, tag->attributes[i].name); + if (value[i]) { + HTMLGen_put_string(me, "=\""); + HTMLGen_put_string(me, value[i]); + HTMLGen_put_character(me, '"'); + } + } + } + HTMLGen_put_string(me, ">"); /* got rid of \n LJM */ + + /* Make very specific HTML assumption that PRE can't be + nested! */ + me->preformatted = (element_number == HTML_PRE) ? YES : was_preformatted; + + /* can break after element start */ + if (!me->preformatted && tag->contents != SGML_EMPTY) { + me->line_break = me->write_pointer; /* Don't you hate SGML? */ + me->cleanness = 1; + me->delete_line_break_char = NO; + } +} + + +/* End Element +** ----------- +** +*/ +/* When we end an element, the style must be returned to that +** in effect before that element. Note that anchors (etc?) +** don't have an associated style, so that we must scan down the +** stack for an element with a defined style. (In fact, the styles +** should be linked to the whole stack not just the top one.) +** TBL 921119 +*/ +PRIVATE void HTMLGen_end_element ARGS3(HTStructured *, me, + int , element_number, char **, insert) +{ + if (!me->preformatted && + HTML_dtd.tags[element_number].contents != SGML_EMPTY) { + /* can break before element end */ + me->line_break = me->write_pointer; /* Don't you hate SGML? */ + me->cleanness = 1; + me->delete_line_break_char = NO; + } + HTMLGen_put_string(me, "</"); + HTMLGen_put_string(me, HTML_dtd.tags[element_number].name); + HTMLGen_put_character(me, '>'); + if (element_number == HTML_PRE) me->preformatted = NO; +} + + +/* Expanding entities +** ------------------ +** +*/ + +PRIVATE void HTMLGen_put_entity ARGS2(HTStructured *, me, int, entity_number) +{ + HTMLGen_put_character(me, '&'); + HTMLGen_put_string(me, HTML_dtd.entity_names[entity_number]); + HTMLGen_put_character(me, ';'); +} + + + +/* Free an HTML object +** ------------------- +** +*/ +PRIVATE void HTMLGen_free ARGS1(HTStructured *, me) +{ + (*me->targetClass.put_character)(me->target, '\n'); + HTMLGen_flush(me); + (*me->targetClass._free)(me->target); /* ripple through */ + FREE(me); +} + + +PRIVATE void PlainToHTML_free ARGS1(HTStructured *, me) +{ + HTMLGen_end_element(me, HTML_PRE, 0); + HTMLGen_free(me); +} + + + +PRIVATE void HTMLGen_abort ARGS2(HTStructured *, me, HTError, e) +{ + HTMLGen_free(me); +} + + +PRIVATE void PlainToHTML_abort ARGS2(HTStructured *, me, HTError, e) +{ + PlainToHTML_free(me); +} + + + +/* Structured Object Class +** ----------------------- +*/ +PRIVATE CONST HTStructuredClass HTMLGeneration = /* As opposed to print etc */ +{ + "text/html", + HTMLGen_free, + HTMLGen_abort, + HTMLGen_put_character, HTMLGen_put_string, HTMLGen_write, + HTMLGen_start_element, HTMLGen_end_element, + HTMLGen_put_entity +}; + + +/* Subclass-specific Methods +** ------------------------- +*/ + +PUBLIC HTStructured * HTMLGenerator ARGS1(HTStream *, output) +{ + HTStructured* me = (HTStructured*)malloc(sizeof(*me)); + if (me == NULL) + outofmem(__FILE__, "HTMLGenerator"); + me->isa = &HTMLGeneration; + + me->target = output; + me->targetClass = *me->target->isa; /* Copy pointers to routines for speed*/ + + me->write_pointer = me->buffer; + me->line_break = me->buffer; + me->cleanness = 0; + me->delete_line_break_char = NO; + me->preformatted = NO; + return me; +} + +/* Stream Object Class +** ------------------- +** +** This object just converts a plain text stream into HTML +** It is officially a structured strem but only the stream bits exist. +** This is just the easiest way of typecasting all the routines. +*/ +PRIVATE CONST HTStructuredClass PlainToHTMLConversion = +{ + "plaintexttoHTML", + HTMLGen_free, + PlainToHTML_abort, + HTMLGen_put_character, + HTMLGen_put_string, + HTMLGen_write, + NULL, /* Structured stuff */ + NULL, + NULL +}; + + +/* HTConverter from plain text to HTML Stream +** ------------------------------------------ +*/ + +PUBLIC HTStream* HTPlainToHTML ARGS3( + HTPresentation *, pres, + HTParentAnchor *, anchor, + HTStream *, sink) +{ + HTStructured* me = (HTStructured*)malloc(sizeof(*me)); + if (me == NULL) + outofmem(__FILE__, "PlainToHTML"); + me->isa = (HTStructuredClass*) &PlainToHTMLConversion; + + me->target = sink; + me->targetClass = *me->target->isa; + /* Copy pointers to routines for speed*/ + + HTMLGen_put_string(me, "<HTML>\n<BODY>\n<PRE>\n"); + me->preformatted = YES; + return (HTStream*) me; +} diff --git a/WWW/Library/Implementation/HTMLGen.h b/WWW/Library/Implementation/HTMLGen.h new file mode 100644 index 00000000..ac814ad8 --- /dev/null +++ b/WWW/Library/Implementation/HTMLGen.h @@ -0,0 +1,32 @@ +/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTMLGen.html + HTML GENERATOR + + This module converts structed stream into stream. That is, given a stream to write to, + it will give you a structured stream to + + */ +#ifndef HTMLGEN_H +#define HTMLGEN_H + +#include "HTML.h" +#include "HTStream.h" + +/* Subclass: +*/ +/* extern CONST HTStructuredClass HTMLGeneration; */ + +/* Special Creation: +*/ +extern HTStructured * HTMLGenerator PARAMS((HTStream * output)); + +extern HTStream * HTPlainToHTML PARAMS(( + HTPresentation * pres, + HTParentAnchor * anchor, + HTStream * sink)); + + +#endif + +/* + + */ diff --git a/WWW/Library/Implementation/HTNews.c b/WWW/Library/Implementation/HTNews.c new file mode 100644 index 00000000..7eeb7c2a --- /dev/null +++ b/WWW/Library/Implementation/HTNews.c @@ -0,0 +1,1646 @@ +/* NEWS ACCESS HTNews.c +** =========== +** +** History: +** 26 Sep 90 Written TBL +** 29 Nov 91 Downgraded to C, for portable implementation. +*/ + +#include "HTUtils.h" /* Coding convention macros */ +#include "tcp.h" + +/* Implements: +*/ +#include "HTNews.h" + +#include "HTCJK.h" +#include "HTMIME.h" +#include "HTTCP.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +/* this define should be in HTFont.h :( */ +#define HT_NON_BREAK_SPACE ((char)1) /* For now */ + +#define CR FROMASCII('\015') /* Must be converted to ^M for transmission */ +#define LF FROMASCII('\012') /* Must be converted to ^J for transmission */ + +#define NEWS_PORT 119 /* See rfc977 */ +#define SNEWS_PORT 563 /* See Lou Montulli */ +#define APPEND /* Use append methods */ +PUBLIC int HTNewsChunkSize = 30;/* Number of articles for quick display */ +PUBLIC int HTNewsMaxChunk = 40; /* Largest number of articles in one window */ + +#ifndef DEFAULT_NEWS_HOST +#define DEFAULT_NEWS_HOST "news" +#endif +#ifndef SERVER_FILE +#define SERVER_FILE "/usr/local/lib/rn/server" +#endif + +#define NEWS_NETREAD NETREAD +#define NEWS_NETWRITE NETWRITE + +#include <ctype.h> + +#include "HTML.h" +#include "HTParse.h" +#include "HTFormat.h" +#include "HTAlert.h" + +#include "LYLeaks.h" + +#define BIG 1024 /* @@@ */ + +struct _HTStructured { + CONST HTStructuredClass * isa; + /* ... */ +}; + +#define NEXT_CHAR HTGetCharacter() +#define LINE_LENGTH 512 /* Maximum length of line of ARTICLE etc */ +#define GROUP_NAME_LENGTH 256 /* Maximum length of group name */ +extern BOOLEAN scan_for_buried_news_references; +extern BOOLEAN LYListNewsNumbers; +extern BOOLEAN LYListNewsDates; +extern HTCJKlang HTCJK; + +/* +** Module-wide variables. +*/ +PUBLIC char * HTNewsHost=NULL; +PRIVATE char * NewsHost=NULL; +PRIVATE char * NewsHREF=NULL; +PRIVATE int s; /* Socket for NewsHost */ +PRIVATE char response_text[LINE_LENGTH+1]; /* Last response */ +/* PRIVATE HText * HT; */ /* the new hypertext */ +PRIVATE HTStructured * target; /* The output sink */ +PRIVATE HTStructuredClass targetClass; /* Copy of fn addresses */ +PRIVATE HTParentAnchor *node_anchor; /* Its anchor */ +PRIVATE int diagnostic; /* level: 0=none 2=source */ + +#define PUTC(c) (*targetClass.put_character)(target, c) +#define PUTS(s) (*targetClass.put_string)(target, s) +#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0) +#define END(e) (*targetClass.end_element)(target, e, 0) + +PUBLIC CONST char * HTGetNewsHost NOARGS +{ + return HTNewsHost; +} + +PUBLIC void HTSetNewsHost ARGS1(CONST char *, value) +{ + StrAllocCopy(HTNewsHost, value); +} + +/* Initialisation for this module +** ------------------------------ +** +** Except on the NeXT, we pick up the NewsHost name from +** +** 1. Environment variable NNTPSERVER +** 2. File SERVER_FILE +** 3. Compilation time macro DEFAULT_NEWS_HOST +** 4. Default to "news" +** +** On the NeXT, we pick up the NewsHost name from, in order: +** +** 1. WorldWideWeb default "NewsHost" +** 2. Global default "NewsHost" +** 3. News default "NewsHost" +** 4. Compilation time macro DEFAULT_NEWS_HOST +** 5. Default to "news" +*/ +PRIVATE BOOL initialized = NO; +PRIVATE BOOL initialize NOARGS +{ + +/* Get name of Host +*/ +#ifdef NeXTStep + if ((HTNewsHost = NXGetDefaultValue("WorldWideWeb","NewsHost"))==0) + if ((HTNewsHost = NXGetDefaultValue("News","NewsHost")) == 0) + HTNewsHost = DEFAULT_NEWS_HOST; +#else + if (getenv("NNTPSERVER")) { + StrAllocCopy(HTNewsHost, (char *)getenv("NNTPSERVER")); + if (TRACE) fprintf(stderr, "HTNews: NNTPSERVER defined as `%s'\n", + HTNewsHost); + } else { + char server_name[256]; + FILE* fp = fopen(SERVER_FILE, "r"); + if (fp) { + if (fscanf(fp, "%s", server_name)==1) { + StrAllocCopy(HTNewsHost, server_name); + if (TRACE) fprintf(stderr, + "HTNews: File %s defines news host as `%s'\n", + SERVER_FILE, HTNewsHost); + } + fclose(fp); + } + } + if (!HTNewsHost) + HTNewsHost = DEFAULT_NEWS_HOST; +#endif /* NeXTStep */ + + s = -1; /* Disconnected */ + + return YES; +} + +/* Send NNTP Command line to remote host & Check Response +** ------------------------------------------------------ +** +** On entry, +** command points to the command to be sent, including CRLF, or is null +** pointer if no command to be sent. +** On exit, +** Negative status indicates transmission error, socket closed. +** Positive status is an NNTP status. +*/ +PRIVATE int response ARGS1(CONST char *,command) +{ + int result; + char * p = response_text; + char ch; + if (command) { + int status; + int length = strlen(command); + if (TRACE) fprintf(stderr, "NNTP command to be sent: %s", command); +#ifdef NOT_ASCII + { + CONST char * p; + char * q; + char ascii[LINE_LENGTH+1]; + for(p = command, q=ascii; *p; p++, q++) { + *q = TOASCII(*p); + } + status = NEWS_NETWRITE(s, ascii, length); + } +#else + status = NEWS_NETWRITE(s, (char *)command, length); +#endif + if (status < 0){ + if (TRACE) fprintf(stderr, + "HTNews: Unable to send command. Disconnecting.\n"); + NETCLOSE(s); + s = -1; + return status; + } /* if bad status */ + } /* if command to be sent */ + + for(;;) { + if (((*p++ = NEXT_CHAR) == LF) || + (p == &response_text[LINE_LENGTH])) { + *p++ = '\0'; /* Terminate the string */ + if (TRACE) fprintf(stderr, "NNTP Response: %s\n", response_text); + sscanf(response_text, "%d", &result); + return result; + } /* if end of line */ + + if ((ch = *(p-1)) == (char)EOF) { + if (TRACE) fprintf(stderr, + "HTNews: EOF on read, closing socket %d\n", s); + NETCLOSE(s); /* End of file, close socket */ + return s = -1; /* End of file on response */ + } + } /* Loop over characters */ +} + +/* Case insensitive string comparisons +** ----------------------------------- +** +** On entry, +** template must be already un upper case. +** unknown may be in upper or lower or mixed case to match. +*/ +PRIVATE BOOL match ARGS2 (CONST char *,unknown, CONST char *,template) +{ + CONST char * u = unknown; + CONST char * t = template; + for (; *u && *t && (TOUPPER(*u) == *t); u++, t++) + ; /* Find mismatch or end */ + return (BOOL)(*t == 0); /* OK if end of template */ +} + +/* Find Author's name in mail address +** ---------------------------------- +** +** On exit, +** Returns allocated string which cannot be freed by the +** calling function, and is reallocated on subsequent calls +** to this function. +** +** For example, returns "Tim Berners-Lee" if given any of +** " Tim Berners-Lee <tim@online.cern.ch> " +** or " tim@online.cern.ch ( Tim Berners-Lee ) " +*/ +PRIVATE char * author_name ARGS1 (char *,email) +{ + static char *name = NULL; + char *s, *e; + + StrAllocCopy(name, email); + if (TRACE) + fprintf(stderr,"Trying to find name in: %s\n",name); + + if ((s = strchr(name, '(')) && (e = strchr(name, ')'))) { + if (e > s) { + *e = '\0'; /* Chop off everything after the ')' */ + return HTStrip(s+1); /* Remove leading and trailing spaces */ + } + } + + if ((s = strchr(name, '<')) && (e = strchr(name, '>'))) { + if (e > s) { + strcpy(s, e+1); /* Remove <...> */ + return HTStrip(name); /* Remove leading and trailing spaces */ + } + } + + return HTStrip(name); /* Default to the whole thing */ +} + +/* Find Author's mail address +** -------------------------- +** +** On exit, +** Returns allocated string which cannot be freed by the +** calling function, and is reallocated on subsequent calls +** to this function. +** +** For example, returns "montulli@spaced.out.galaxy.net" if given any of +** " Lou Montulli <montulli@spaced.out.galaxy.net> " +** or " montulli@spacedout.galaxy.net ( Lou "The Stud" Montulli ) " +*/ +PRIVATE char * author_address ARGS1(char *,email) +{ + static char *address = NULL; + char *s, *at, *e; + + StrAllocCopy(address, email); + if (TRACE) + fprintf(stderr,"Trying to find address in: %s\n",address); + + if ((s = strchr(address, '<'))) { + if ((e = strchr(s, '>')) && (at = strchr(s, '@'))) { + if (at < e) { + *e = '\0'; /* Remove > */ + return HTStrip(s+1); /* Remove leading and trailing spaces */ + } + } + } + + if ((s = strchr(address, '(')) && + (e = strchr(address, ')')) && (at = strchr(address, '@'))) { + if (e > s && at < e) { + *s = '\0'; /* Chop off everything after the ')' */ + return HTStrip(address); /* Remove leading and trailing spaces */ + } + } + + if ((at = strchr(address, '@')) && at > address) { + s = (at - 1); + e = (at + 1); + while (s > address && !isspace((unsigned char)*s)) + s--; + while (*e && !isspace((unsigned char)*e)) + e++; + *e = 0; + return HTStrip(s); + } + + /* Default to the first word */ + s = address; + while (isspace((unsigned char)*s)) + s++; /* find first non-space */ + e = s; + while (!isspace((unsigned char)*e) && *e != '\0') + e++; /* find next space or end */ + *e = '\0'; /* terminate space */ + + return(s); +} + +/* Start anchor element +** -------------------- +*/ +PRIVATE void start_anchor ARGS1(CONST char *, href) +{ + BOOL present[HTML_A_ATTRIBUTES]; + CONST char* value[HTML_A_ATTRIBUTES]; + + { + int i; + for(i=0; i < HTML_A_ATTRIBUTES; i++) + present[i] = (i == HTML_A_HREF); + } + ((CONST char **)value)[HTML_A_HREF] = href; + (*targetClass.start_element)(target, HTML_A , present, + (CONST char **)value, 0); +} + +/* Start link element +** ------------------ +*/ +PRIVATE void start_link ARGS2(CONST char *, href, CONST char *, rev) +{ + BOOL present[HTML_LINK_ATTRIBUTES]; + CONST char* value[HTML_LINK_ATTRIBUTES]; + + { + int i; + for(i=0; i < HTML_LINK_ATTRIBUTES; i++) + present[i] = (i == HTML_LINK_HREF || i == HTML_LINK_REV); + } + ((CONST char **)value)[HTML_LINK_HREF] = href; + ((CONST char **)value)[HTML_LINK_REV] = rev; + (*targetClass.start_element)(target, HTML_LINK, present, + (CONST char **)value, 0); +} + +/* Start list element +** ------------------ +*/ +PRIVATE void start_list ARGS1(int, seqnum) +{ + BOOL present[HTML_OL_ATTRIBUTES]; + CONST char* value[HTML_OL_ATTRIBUTES]; + char SeqNum[20]; + int i; + + for (i=0; i < HTML_OL_ATTRIBUTES; i++) + present[i] = (i == HTML_OL_SEQNUM || i == HTML_OL_START); + sprintf(SeqNum, "%d", seqnum); + ((CONST char **)value)[HTML_OL_SEQNUM] = SeqNum; + ((CONST char **)value)[HTML_OL_START] = SeqNum; + (*targetClass.start_element)(target, HTML_OL , present, + (CONST char **)value, 0); +} + +/* Paste in an Anchor +** ------------------ +** +** +** On entry, +** HT has a selection of zero length at the end. +** text points to the text to be put into the file, 0 terminated. +** addr points to the hypertext refernce address, +** terminated by white space, comma, NULL or '>' +*/ +PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr) +{ + char href[LINE_LENGTH+1]; + + { + CONST char * p; + strcpy(href, NewsHREF); + for (p = addr; *p && (*p != '>') && !WHITE(*p) && (*p!=','); p++) + ; + strncat(href, addr, p-addr); /* Make complete hypertext reference */ + } + + start_anchor(href); + PUTS(text); + END(HTML_A); +} + +/* Write list of anchors +** --------------------- +** +** We take a pointer to a list of objects, and write out each, +** generating an anchor for each. +** +** On entry, +** HT has a selection of zero length at the end. +** text points to a comma or space separated list of addresses. +** On exit, +** *text is NOT any more chopped up into substrings. +*/ +PRIVATE void write_anchors ARGS1 (char *,text) +{ + char * start = text; + char * end; + char c; + for (;;) { + for (; *start && (WHITE(*start)); start++) + ; /* Find start */ + if (!*start) + return; /* (Done) */ + for (end = start; + *end && (*end != ' ') && (*end != ','); end++) + ;/* Find end */ + if (*end) + end++; /* Include comma or space but not NULL */ + c = *end; + *end = '\0'; + if (*start == '<') + write_anchor(start, start+1); + else + write_anchor(start, start); + START(HTML_BR); + *end = c; + start = end; /* Point to next one */ + } +} + +/* Abort the connection abort_socket +** -------------------- +*/ +PRIVATE void abort_socket NOARGS +{ + if (TRACE) + fprintf(stderr, + "HTNews: EOF on read, closing socket %d\n", s); + NETCLOSE(s); /* End of file, close socket */ + PUTS("Network Error: connection lost"); + PUTC('\n'); + s = -1; /* End of file on response */ + return; +} + +/* Read in an Article read_article +** ------------------ +** +** +** Note the termination condition of a single dot on a line by itself. +** RFC 977 specifies that the line "folding" of RFC850 is not used, so we +** do not handle it here. +** +** On entry, +** s Global socket number is OK +** HT Global hypertext object is ready for appending text +*/ +PRIVATE void read_article NOARGS +{ + + char line[LINE_LENGTH+1]; + char *full_line = NULL; + char *subject=NULL; /* Subject string */ + char *from=NULL; /* From string */ + char *replyto=NULL; /* Reply-to string */ + char *date=NULL; /* Date string */ + char *organization=NULL; /* Organization string */ + char *references=NULL; /* Hrefs for other articles */ + char *newsgroups=NULL; /* Newsgroups list */ + char *followupto=NULL; /* Followup list */ + char *href=NULL; + char *p = line; + BOOL done = NO; + + /* + ** Read in the HEADer of the article. + ** + ** The header fields are either ignored, or formatted and put into the + ** Text. + */ + if (!diagnostic) { + while (!done) { + char ch = *p++ = NEXT_CHAR; + if (ch == (char)EOF) { + abort_socket(); /* End of file, close socket */ + return; /* End of file on response */ + } + if ((ch == LF) || (p == &line[LINE_LENGTH])) { + *--p = '\0'; /* Terminate the string */ + if (TRACE) + fprintf(stderr, "H %s\n", line); + + if (line[0] == '\t' || line[0] == ' ') { + int i = 0; + while (line[i]) { + if (line[i] == '\t') + line[i] = ' '; + i++; + } + if (full_line == NULL) { + StrAllocCopy(full_line, line); + } else { + StrAllocCat(full_line, line); + } + } else { + StrAllocCopy(full_line, line); + } + + if (full_line[0] == '.') { + if (full_line[1] < ' ') { /* End of article? */ + done = YES; + break; + } + } else if (full_line[0] < ' ') { + break; /* End of Header? */ + + } else if (match(full_line, "SUBJECT:")) { + StrAllocCopy(subject, HTStrip(strchr(full_line,':')+1)); + if (HTCJK == JAPANESE) { + HTmmdecode(subject, subject); + HTrjis(subject, subject); + } + + } else if (match(full_line, "DATE:")) { + StrAllocCopy(date, HTStrip(strchr(full_line,':')+1)); + + } else if (match(full_line, "ORGANIZATION:")) { + StrAllocCopy(organization, HTStrip(strchr(full_line,':')+1)); + if (HTCJK == JAPANESE) { + HTmmdecode(organization, organization); + HTrjis(organization, organization); + } + + } else if (match(full_line, "FROM:")) { + StrAllocCopy(from, HTStrip(strchr(full_line,':')+1)); + if (HTCJK == JAPANESE) { + HTmmdecode(from, from); + HTrjis(from, from); + } + + } else if (match(full_line, "REPLY-TO:")) { + StrAllocCopy(replyto, HTStrip(strchr(full_line,':')+1)); + if (HTCJK == JAPANESE) { + HTmmdecode(replyto, replyto); + HTrjis(replyto, replyto); + } + + } else if (match(full_line, "NEWSGROUPS:")) { + StrAllocCopy(newsgroups, HTStrip(strchr(full_line,':')+1)); + + } else if (match(full_line, "REFERENCES:")) { + StrAllocCopy(references, HTStrip(strchr(full_line,':')+1)); + + } else if (match(full_line, "FOLLOWUP-TO:")) { + StrAllocCopy(followupto, HTStrip(strchr(full_line,':')+1)); + + } /* end if match */ + p = line; /* Restart at beginning */ + } /* if end of line */ + } /* Loop over characters */ + FREE(full_line); + + START(HTML_HEAD); + PUTC('\n'); + START(HTML_TITLE); + if (subject && *subject != '\0') + PUTS(subject); + else + PUTS("No Subject"); + END(HTML_TITLE); + PUTC('\n'); + /* put in the owner as a link rel. */ + if (from || replyto) { + char *temp=NULL; + StrAllocCopy(temp, replyto ? replyto : from); + StrAllocCopy(href,"mailto:"); + StrAllocCat(href, author_address(temp)); + start_link(href, "made"); + PUTC('\n'); + FREE(temp); + } + END(HTML_HEAD); + PUTC('\n'); + + START(HTML_H1); + if (subject && *subject != '\0') + PUTS(subject); + else + PUTS("No Subject"); + END(HTML_H1); + PUTC('\n'); + + if (subject) + FREE(subject); + + START(HTML_DLC); + PUTC('\n'); + + if (from || replyto) { + START(HTML_DT); + START(HTML_B); + PUTS("From:"); + END(HTML_B); + PUTC(' '); + if (from) + PUTS(from); + else + PUTS(from); + PUTC('\n'); + + if (!replyto) + StrAllocCopy(replyto, from); + START(HTML_DT); + START(HTML_B); + PUTS("Reply to:"); + END(HTML_B); + PUTC(' '); + start_anchor(href); + if (*replyto != '<') + PUTS(author_name(replyto)); + else + PUTS(author_address(replyto)); + END(HTML_A); + START(HTML_BR); + PUTC('\n'); + + FREE(from); + FREE(replyto); + } + + if (date) { + START(HTML_DT); + START(HTML_B); + PUTS("Date:"); + END(HTML_B); + PUTC(' '); + PUTS(date); + PUTC('\n'); + FREE(date); + } + + if (organization) { + START(HTML_DT); + START(HTML_B); + PUTS("Organization:"); + END(HTML_B); + PUTC(' '); + PUTS(organization); + PUTC('\n'); + FREE(organization); + } + + if (newsgroups && !strncmp(NewsHREF, "news:", 5)) { + /* make posting possible */ + StrAllocCopy(href,"newsreply:"); + StrAllocCat(href, (followupto ? followupto : newsgroups)); + + START(HTML_DT); + START(HTML_B); + PUTS("Newsgroups:"); + END(HTML_B); + PUTC('\n'); + START(HTML_DD); + write_anchors(newsgroups); + PUTC('\n'); + + START(HTML_DT); + START(HTML_B); + PUTS("Followup to:"); + END(HTML_B); + PUTC(' '); + start_anchor(href); + PUTS("newsgroup(s)"); + END(HTML_A); + PUTC('\n'); + } + FREE(newsgroups); + FREE(followupto); + + if (references) { + START(HTML_DT); + START(HTML_B); + PUTS("References:"); + END(HTML_B); + PUTC('\n'); + START(HTML_DD); + write_anchors(references); + PUTC('\n'); + FREE(references); + } + + END(HTML_DLC); + PUTC('\n'); + FREE(href); + } + + /* + ** Read in the BODY of the Article. + */ + START(HTML_PRE); + PUTC('\n'); + + p = line; + while (!done) { + char ch = *p++ = NEXT_CHAR; + if (ch == (char)EOF) { + abort_socket(); /* End of file, close socket */ + return; /* End of file on response */ + } + if ((ch == LF) || (p == &line[LINE_LENGTH])) { + *p++ = '\0'; /* Terminate the string */ + if (TRACE) + fprintf(stderr, "B %s", line); + if (line[0] == '.') { + if (line[1] < ' ') { /* End of article? */ + done = YES; + break; + } else { /* Line starts with dot */ + PUTS(&line[1]); /* Ignore first dot */ + } + } else { + + if (diagnostic || !scan_for_buried_news_references) { + +/* All lines are passed as unmodified source. - FM +*/ + PUTS(line); + + } else { + +/* Normal lines are scanned for buried references to other articles. +** Unfortunately, it could pick up mail addresses as well! It also +** can corrupt uuencoded messages! So we don't do this when fetching +** articles as WWW_SOURCE or when downloading (diagnostic is TRUE) or +** if the client has set scan_for_buried_news_references to FALSE. +** Otherwise, we convert all "<...@...>" strings preceded by "rticle " +** to "news:...@..." links, and any strings that look like URLs to +** links. - FM +*/ + char *l = line; + char *p; + + while (p=strstr(l, "rticle <")) { + char *q = strchr(p,'>'); + char *at = strchr(p, '@'); + if (q && at && at<q) { + char c = q[1]; + q[1] = 0; /* chop up */ + p += 7; + *p = 0; + while (*l) { + if (strncmp (l, "news:", 5) && + strncmp (l, "snews://", 8) && + strncmp (l, "nntp://", 7) && + strncmp (l, "ftp://", 6) && + strncmp (l, "file:/", 6) && + strncmp (l, "finger://", 9) && + strncmp (l, "http://", 7) && + strncmp (l, "https://", 8) && + strncmp (l, "wais://", 7) && + strncmp (l, "mailto:", 7) && + strncmp (l, "cso://", 6) && + strncmp (l, "gopher://", 9)) + PUTC (*l++); + else { + StrAllocCopy(href, l); + start_anchor(strtok(href, " \r\n\t,>)\"")); + while (*l && !strchr(" \r\n\t,>)\"", *l)) + PUTC(*l++); + END(HTML_A); + FREE(href); + } + } + *p = '<'; /* again */ + *q = 0; + start_anchor(p+1); + *q = '>'; /* again */ + PUTS(p); + END(HTML_A); + q[1] = c; /* again */ + l=q+1; + } else { + break; /* line has unmatched <> */ + } + } + while (*l) { /* Last bit of the line */ + if (strncmp (l, "news:", 5) && + strncmp (l, "snews://", 8) && + strncmp (l, "nntp://", 7) && + strncmp (l, "ftp://", 6) && + strncmp (l, "file:/", 6) && + strncmp (l, "finger://", 9) && + strncmp (l, "http://", 7) && + strncmp (l, "https://", 8) && + strncmp (l, "wais://", 7) && + strncmp (l, "mailto:", 7) && + strncmp (l, "cso://", 6) && + strncmp (l, "gopher://", 9)) + PUTC (*l++); + else { + StrAllocCopy(href, l); + start_anchor(strtok(href, " \r\n\t,>)\"")); + while (*l && !strchr(" \r\n\t,>)\"", *l)) + PUTC(*l++); + END(HTML_A); + FREE(href); + } + } + } /* if diagnostic or not scan_for_buried_news_references */ + } /* if not dot */ + p = line; /* Restart at beginning */ + } /* if end of line */ + } /* Loop over characters */ + + END(HTML_PRE); + PUTC('\n'); +} + +/* Read in a List of Newsgroups +** ---------------------------- +** +** Note the termination condition of a single dot on a line by itself. +** RFC 977 specifies that the line "folding" of RFC850 is not used, so we +** do not handle it here. +*/ +PRIVATE void read_list ARGS1(char *, arg) +{ + + char line[LINE_LENGTH+1]; + char *p; + BOOL done = NO; + BOOL head = NO; + BOOL tail = NO; + int listing = 0; + char *pattern = NULL; + int len = 0; + + /* + * Support head or tail matches for groups to list. - FM + */ + if (arg && strlen(arg) > 1) { + if (*arg == '*') { + tail = YES; + StrAllocCopy(pattern, (arg+1)); + } else if (arg[strlen(arg)-1] == '*') { + head = YES; + StrAllocCopy(pattern, arg); + pattern[strlen(pattern)-1] = '\0'; + } + if (tail || head) { + len = strlen(pattern); + } + + } + + /* + ** Read in the HEADer of the article. + ** + ** The header fields are either ignored, or formatted and put into the + ** Text. + */ + START(HTML_HEAD); + PUTC('\n'); + START(HTML_TITLE); + PUTS("Newsgroups"); + END(HTML_TITLE); + PUTC('\n'); + END(HTML_HEAD); + PUTC('\n'); + START(HTML_H1); + PUTS( "Newsgroups"); + END(HTML_H1); + PUTC('\n'); + p = line; + START(HTML_DLC); + PUTC('\n'); + while (!done) { + char ch = *p++ = NEXT_CHAR; + if (ch == (char)EOF) { + abort_socket(); /* End of file, close socket */ + FREE(pattern); + return; /* End of file on response */ + } + if ((ch == LF) || (p == &line[LINE_LENGTH])) { + *p++ = '\0'; /* Terminate the string */ + if (TRACE) + fprintf(stderr, "B %s", line); + if (line[0] == '.') { + if (line[1] < ' ') { /* End of article? */ + done = YES; + break; + } else { /* Line starts with dot */ + START(HTML_DT); + PUTS(&line[1]); + } + } else { + /* + ** Normal lines are scanned for references to newsgroups. + */ + int i = 0; + + /* find whitespace if it exits */ + for (; line[i] != '\0' && !WHITE(line[i]); i++) + ; /* null body */ + + if (line[i] != '\0') { + line[i] = '\0'; + if ((head && strncasecomp(line, pattern, len)) || + (tail && (i < len || + strcasecomp((line + (i - len)), pattern)))) { + p = line; /* Restart at beginning */ + continue; + } + START(HTML_DT); + write_anchor(line, line); + listing++; + PUTC('\n'); + START(HTML_DD); + PUTS(&line[i+1]); /* put description */ + } else { + if ((head && strncasecomp(line, pattern, len)) || + (tail && (i < len || + strcasecomp((line + (i - len)), pattern)))) { + p = line; /* Restart at beginning */ + continue; + } + START(HTML_DT); + write_anchor(line, line); + listing++; + } + } /* if not dot */ + p = line; /* Restart at beginning */ + } /* if end of line */ + } /* Loop over characters */ + if (!listing) { + START(HTML_DT); + sprintf(line, "No matches for: %s", arg); + PUTS(line); + } + END(HTML_DLC); + PUTC('\n'); + FREE(pattern); + return; +} + +/* Read in a Newsgroup +** ------------------- +** Unfortunately, we have to ask for each article one by one if we +** want more than one field. +** +*/ +PRIVATE void read_group ARGS3( + CONST char *,groupName, + int,first_required, + int,last_required) +{ + char line[LINE_LENGTH+1]; + char author[LINE_LENGTH+1]; + char subject[LINE_LENGTH+1]; + char *date = NULL; + int i; + char *p; + BOOL done; + + char buffer[LINE_LENGTH]; + char *reference = NULL; /* Href for article */ + int art; /* Article number WITHIN GROUP */ + int status, count, first, last; /* Response fields */ + /* count is only an upper limit */ + + author[0] = '\0'; + START(HTML_HEAD); + PUTC('\n'); + START(HTML_TITLE); + PUTS("Newsgroup "); + PUTS(groupName); + END(HTML_TITLE); + PUTC('\n'); + END(HTML_HEAD); + PUTC('\n'); + + sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last); + if (TRACE) + fprintf(stderr, + "Newsgroup status=%d, count=%d, (%d-%d) required:(%d-%d)\n", + status, count, first, last, first_required, last_required); + if (last ==0 ) { + PUTS("\nNo articles in this group.\n"); + goto add_post; + } + +#define FAST_THRESHOLD 100 /* Above this, read IDs fast */ +#define CHOP_THRESHOLD 50 /* Above this, chop off the rest */ + + if (first_required < first) + first_required = first; /* clip */ + if ((last_required == 0) || (last_required > last)) + last_required = last; + + if (last_required < first_required) { + PUTS("\nNo articles in this range.\n"); + goto add_post; + } + + if (last_required-first_required+1 > HTNewsMaxChunk) { /* Trim this block */ + first_required = last_required-HTNewsChunkSize+1; + } + if (TRACE) + fprintf(stderr, " Chunk will be (%d-%d)\n", + first_required, last_required); + + /* + ** Set window title. + */ + sprintf(buffer, "%s, Articles %d-%d", + groupName, first_required, last_required); + START(HTML_H1); + PUTS(buffer); + END(HTML_H1); + PUTC('\n'); + + /* + ** Link to earlier articles. + */ + if (first_required > first) { + int before; /* Start of one before */ + if (first_required-HTNewsMaxChunk <= first) + before = first; + else + before = first_required-HTNewsChunkSize; + sprintf(buffer, "%s/%d-%d", groupName, before, first_required-1); + if (TRACE) + fprintf(stderr, " Block before is %s\n", buffer); + PUTC('('); + start_anchor(buffer); + PUTS("Earlier articles"); + END(HTML_A); + PUTS("...)\n"); + START(HTML_P); + PUTC('\n'); + } + + done = NO; + +/*#define USE_XHDR*/ +#ifdef USE_XHDR + if (count > FAST_THRESHOLD) { + sprintf(buffer, + "\nThere are about %d articles currently available in %s, IDs as follows:\n\n", + count, groupName); + PUTS(buffer); + sprintf(buffer, "XHDR Message-ID %d-%d%c%c", first, last, CR, LF); + status = response(buffer); + if (status == 221) { + + p = line; + while (!done) { + char ch = *p++ = NEXT_CHAR; + if (ch == (char)EOF) { + abort_socket(); /* End of file, close socket */ + return; /* End of file on response */ + } + if ((ch == '\n') || (p == &line[LINE_LENGTH])) { + *p++ = '\0'; /* Terminate the string */ + if (TRACE) + fprintf(stderr, "X %s", line); + if (line[0] == '.') { + if (line[1] < ' ') { /* End of article? */ + done = YES; + break; + } else { /* Line starts with dot */ + /* Ignore strange line */ + } + } else { + + /* Normal lines are scanned for references to articles. + */ + char * space = strchr(line, ' '); + if (space++) + write_anchor(space, space); + } /* if not dot */ + p = line; /* Restart at beginning */ + } /* if end of line */ + } /* Loop over characters */ + + /* leaving loop with "done" set */ + } /* Good status */ + } +#endif /* USE_XHDR */ + + /* + ** Read newsgroup using individual fields. + */ + if (!done) { + START(HTML_B); + if (first == first_required && last == last_required) + PUTS("All available articles in "); + else + PUTS("Articles in "); + PUTS(groupName); + END(HTML_B); + PUTC('\n'); + if (LYListNewsNumbers) + start_list(first_required); + else + START(HTML_UL); + for (art = first_required; art <= last_required; art++) { + +/*#define OVERLAP*/ +#ifdef OVERLAP +/* With this code we try to keep the server running flat out by queuing just +** one extra command ahead of time. We assume (1) that the server won't abort +** if it gets input during output, and (2) that TCP buffering is enough for the +** two commands. Both these assumptions seem very reasonable. However, we HAVE +** had a hangup with a loaded server. +*/ + if (art == first_required) { + if (art == last_required) { /* Only one */ + sprintf(buffer, + "HEAD %d%c%c", art, CR, LF); + status = response(buffer); + } else { /* First of many */ + sprintf(buffer, "HEAD %d%c%cHEAD %d%c%c", + art, CR, LF, art+1, CR, LF); + status = response(buffer); + } + } else if (art == last_required) { /* Last of many */ + status = response(NULL); + } else { /* Middle of many */ + sprintf(buffer, "HEAD %d%c%c", art+1, CR, LF); + status = response(buffer); + } + +#else /* NOT OVERLAP: */ + sprintf(buffer, "HEAD %d%c%c", art, CR, LF); + status = response(buffer); +#endif /* OVERLAP */ + + if (status == 221) { /* Head follows - parse it:*/ + + p = line; /* Write pointer */ + done = NO; + while( !done ) { + char ch = *p++ = NEXT_CHAR; + if (ch == (char)EOF) { + abort_socket(); /* End of file, close socket */ + return; /* End of file on response */ + } + if ((ch == LF) || + (p == &line[LINE_LENGTH])) { + + *--p = '\0'; /* Terminate & chop LF*/ + p = line; /* Restart at beginning */ + if (TRACE) + fprintf(stderr, "G %s\n", line); + switch(line[0]) { + + case '.': + done = (line[1] < ' '); /* End of article? */ + break; + + case 'S': + case 's': + if (match(line, "SUBJECT:")) + strcpy(subject, line+9);/* Save subject */ + break; + + case 'M': + case 'm': + if (match(line, "MESSAGE-ID:")) { + char * addr = HTStrip(line+11) +1; /* Chop < */ + addr[strlen(addr)-1] = '\0'; /* Chop > */ + StrAllocCopy(reference, addr); + } + break; + + case 'f': + case 'F': + if (match(line, "FROM:")) { + char * p; + strcpy(author, + author_name(strchr(line,':')+1)); + p = author + strlen(author) - 1; + if (*p==LF) + *p = '\0'; /* Chop off newline */ + } + break; + + case 'd': + case 'D': + if (LYListNewsDates && match(line, "DATE:")) { + StrAllocCopy(date, + HTStrip(strchr(line,':')+1)); + } + break; + + } /* end switch on first character */ + } /* if end of line */ + } /* Loop over characters */ + + PUTC('\n'); + START(HTML_LI); + sprintf(buffer, "\"%s\"", subject); + if (reference) { + write_anchor(buffer, reference); + FREE(reference); + } else { + PUTS(buffer); + } + if (author[0] != '\0') { + PUTS(" - "); + if (LYListNewsDates) + START(HTML_I); + PUTS(author); + if (LYListNewsDates) + END(HTML_I); + author[0] = '\0'; + } + if (date) { + if (!diagnostic) { + for (i = 0; date[i]; i++) { + if (date[i] == ' ') { + date[i] = HT_NON_BREAK_SPACE; + } + } + } + sprintf(buffer, " [%s]", date); + PUTS(buffer); + FREE(date); + } + + +/* indicate progress! @@@@@@ +*/ + + } /* If good response */ + } /* Loop over article */ + } /* If read headers */ + PUTC('\n'); + if (LYListNewsNumbers) + END(HTML_OL); + else + END(HTML_UL); + PUTC('\n'); + + /* + ** Link to later articles. + */ + if (last_required < last) { + int after; /* End of article after */ + after = last_required+HTNewsChunkSize; + if (after == last) + sprintf(buffer, "%s%s", NewsHREF, groupName); /* original group */ + else + sprintf(buffer, "%s%s/%d-%d", NewsHREF, groupName, + last_required+1, after); + if (TRACE) + fprintf(stderr, " Block after is %s\n", buffer); + PUTC('('); + start_anchor(buffer); + PUTS("Later articles"); + END(HTML_A); + PUTS("...)\n"); + } + +add_post: + if (!strncmp(NewsHREF, "news:", 5)) { + char *href = NULL; + START(HTML_HR); + PUTC('\n'); + StrAllocCopy(href,"newspost:"); + StrAllocCat(href,groupName); + start_anchor(href); + PUTS("Post to "); + PUTS(groupName); + END(HTML_A); + FREE(href); + } else { + START(HTML_HR); + } + PUTC('\n'); +} + +/* Load by name HTLoadNews +** ============ +*/ +PUBLIC int HTLoadNews ARGS4( + CONST char *, arg, + HTParentAnchor *, anAnchor, + HTFormat, format_out, + HTStream*, stream) +{ + char command[260]; /* The whole command */ + char groupName[GROUP_NAME_LENGTH]; /* Just the group name */ + int status; /* tcp return */ + int retries; /* A count of how hard we have tried */ + BOOL group_wanted; /* Flag: group was asked for, not article */ + BOOL list_wanted; /* Flag: group was asked for, not article */ + int first, last; /* First and last articles asked for */ + char *cp; + char *ListArg = NULL; + + diagnostic = (format_out == WWW_SOURCE || /* set global flag */ + format_out == HTAtom_for("www/download") || + format_out == HTAtom_for("www/dump")); + + if (TRACE) fprintf(stderr, "HTNews: Looking for %s\n", arg); + + if (!initialized) + initialized = initialize(); + if (!initialized) + return -1; /* FAIL */ + + FREE(NewsHREF); + + { + CONST char * p1 = arg; + + /* + ** We will ask for the document, omitting the host name & anchor. + ** + ** Syntax of address is + ** xxx@yyy Article + ** <xxx@yyy> Same article + ** xxxxx News group (no "@") + ** group/n1-n2 Articles n1 to n2 in group + */ + group_wanted = (strchr(arg, '@')==0) && (strchr(arg, '*')==0); + list_wanted = (strchr(arg, '@')==0) && (strchr(arg, '*')!=0); + + /* p1 = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION); */ + /* + * Don't use HTParse because news: access doesn't follow traditional + * rules. For instance, if the article reference contains a '#', + * the rest of it is lost -- JFG 10/7/92, from a bug report + */ + if (!strncasecomp (arg, "nntp://", 7)) { + if (!(cp = strchr(arg+7, '/')) || *(cp+1) == '\0') { + p1 = "*"; + group_wanted = FALSE; + list_wanted = TRUE; + } else { + p1 = (cp+1); + } + if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') { + if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) { + NETCLOSE(s); + s = -1; + } + StrAllocCopy(NewsHost, HTNewsHost); + } else { + if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) { + NETCLOSE(s); + s = -1; + } + StrAllocCopy(NewsHost, cp); + } + FREE(cp); + sprintf(command, "nntp://%s/", NewsHost); + StrAllocCopy(NewsHREF, command); + } + else if (!strncasecomp(arg, "snews:", 6)) { + HTAlert("This client does not contain support for SNEWS URLs."); + return HT_NO_DATA; + } + else if (!strncasecomp (arg, "news://", 7)) { + if (!(cp = strchr(arg+7, '/')) || *(cp+1) == '\0') { + p1 = "*"; + group_wanted = FALSE; + list_wanted = TRUE; + } else { + p1 = (cp+1); + } + if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') { + if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) { + NETCLOSE(s); + s = -1; + } + StrAllocCopy(NewsHost, HTNewsHost); + } else { + if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) { + NETCLOSE(s); + s = -1; + } + StrAllocCopy(NewsHost, cp); + } + FREE(cp); + sprintf(command, "news://%s/", NewsHost); + StrAllocCopy(NewsHREF, command); + } + else { + p1 = arg + 5; /* Skip "news:" prefix */ + if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) { + NETCLOSE(s); + s = -1; + } + StrAllocCopy(NewsHost, HTNewsHost); + StrAllocCopy(NewsHREF, "news:"); + } + if (list_wanted) { + strcpy(command, "LIST NEWSGROUPS"); + } else if (group_wanted) { + char * slash = strchr(p1, '/'); + strcpy(command, "GROUP "); + first = 0; + last = 0; + if (slash) { + *slash = '\0'; + strcpy(groupName, p1); + *slash = '/'; + (void)sscanf(slash+1, "%d-%d", &first, &last); + if ((first > 0) && (isdigit(*(slash+1))) && + (strchr(slash+1, '-') == NULL || first == last)) { + /* + * We got a number greater than 0, which will be + * loaded as first, and either no range or the + * range computes to zero, so make last negative, + * as a flag to select the group and then fetch + * an article by number (first) instead of by + * messageID. - FM + */ + last = -1; + } + } else { + strcpy(groupName, p1); + } + strcat(command, groupName); + } else { + strcpy(command, "ARTICLE "); + if (strchr(p1, '<') == 0) + strcat(command,"<"); + strcat(command, p1); + if (strchr(p1, '>') == 0) + strcat(command,">"); + } + + { + char * p = command + strlen(command); + *p++ = CR; /* Macros to be correct on Mac */ + *p++ = LF; + *p++ = 0; + /* strcat(command, "\r\n"); */ /* CR LF, as in rfc 977 */ + } + StrAllocCopy(ListArg, p1); + } /* scope of p1 */ + + if (!*arg) { + FREE(NewsHREF); + FREE(ListArg); + return NO; /* Ignore if no name */ + } + + /* + * Make a hypertext object with an anchor list. + */ + node_anchor = anAnchor; + target = HTML_new(anAnchor, format_out, stream); + targetClass = *target->isa; /* Copy routine entry points */ + + /* + * Now, let's get a stream setup up from the NewsHost. + */ + for (retries = 0; retries < 2; retries++) { + if (s < 0) { + /* CONNECTING to news host */ + char url[1024]; + if (!strncmp(arg, "news:", 5)) + sprintf (url, "lose://%s/", NewsHost); + else + strcpy (url, arg); + if (TRACE) + fprintf (stderr, "News: doing HTDoConnect on '%s'\n", url); + + _HTProgress("Connecting to NewsHost ..."); + + status = HTDoConnect (url, "NNTP", NEWS_PORT, &s); + if (status == HT_INTERRUPTED) { + /* + * Interrupt cleanly. + */ + if (TRACE) + fprintf(stderr, + "News: Interrupted on connect; recovering cleanly.\n"); + _HTProgress("Connection interrupted."); + + (*targetClass._abort)(target, NULL); + + FREE(NewsHost); + FREE(NewsHREF); + FREE(ListArg); + return HT_INTERRUPTED; + } + if (status < 0) { + char message[256]; + NETCLOSE(s); + s = -1; + if (TRACE) + fprintf(stderr, + "HTNews: Unable to connect to news host.\n"); + if (retries < 1) + continue; + sprintf(message, "Could not access %s.", NewsHost); + FREE(NewsHost); + FREE(NewsHREF); + FREE(ListArg); + return HTLoadError(stream, 500, message); + } else { + if (TRACE) + fprintf(stderr, "HTNews: Connected to news host %s.\n", + NewsHost); + HTInitInput(s); /* set up buffering */ + if ((response(NULL) / 100) != 2) { + char message[BIG]; + NETCLOSE(s); + s = -1; + if (retries < 1) + continue; + sprintf(message, + "Can't read news info. News host %.20s responded: %.200s", + NewsHost, response_text); + FREE(NewsHost); + FREE(NewsHREF); + FREE(ListArg); + return HTLoadError(stream, 500, message); + } + } + } /* If needed opening */ + + /* + * Ensure reader mode, but don't bother checking the + * status for anything but HT_INERRUPTED, because if + * if the reader mode command is not needed, the server + * probably return a 500, which is irrelevant at this + * point. - FM + */ + { + char buffer[20]; + sprintf(buffer, "mode reader%c%c", CR, LF); + if ((status = response(buffer)) == HT_INTERRUPTED) { + _HTProgress("Connection interrupted."); + break; + } + } + +Send_NNTP_command: + if ((status = response(command)) == HT_INTERRUPTED) { + _HTProgress("Connection interrupted."); + break; + } + if (status < 0) { + if (retries < 1) { + continue; + } else { + break; + } + } + if ((status/100) != 2) { + if (retries) + HTAlert(response_text); + else + _HTProgress(response_text); + NETCLOSE(s); + s = -1; + /* + * Message might be a leftover "Timeout-disconnected", + * so try again if retries is not exhausted. + */ + continue; + } + + /* + * Load a group, article, etc + */ + if (list_wanted) { + _HTProgress("Reading list of available newsgroups."); + read_list(ListArg); + } else if (group_wanted) { + if (last < 0) { + /* + * We got one article number rather than a range + * following the slash which followed the group + * name, or the range was zero, so now that we + * have selected that group, load ARTICLE and the + * the number (first) as the command and go back + * to send it and check the response. - FM + */ + sprintf(command, "ARTICLE %d%c%c", first, CR, LF); + group_wanted = FALSE; + retries = 2; + goto Send_NNTP_command; + } + _HTProgress("Reading list of articles in newsgroup."); + read_group(groupName, first, last); + } else { + _HTProgress("Reading news article."); + read_article(); + } + + (*targetClass._free)(target); + FREE(NewsHREF); + FREE(ListArg); + return HT_LOADED; + + } /* Retry loop */ + + + /* HTAlert("Sorry, could not load requested news."); */ + +/* NXRunAlertPanel(NULL, "Sorry, could not load `%s'.", + NULL,NULL,NULL, arg);No -- message earlier wil have covered it */ + + (*targetClass._abort)(target, NULL); + FREE(NewsHREF); + FREE(ListArg); + return HT_NO_DATA; +} + +#ifdef GLOBALDEF_IS_MACRO +#define _HTNEWS_C_1_INIT { "news", HTLoadNews, NULL } +GLOBALDEF (HTProtocol,HTNews,_HTNEWS_C_1_INIT); +#define _HTNEWS_C_2_INIT { "nntp", HTLoadNews, NULL } +GLOBALDEF (HTProtocol,HTNNTP,_HTNEWS_C_2_INIT); +#define _HTNEWS_C_3_INIT { "snews", HTLoadNews, NULL } +GLOBALDEF (HTProtocol,HTSNews,_HTNEWS_C_1_INIT); +#else +GLOBALDEF PUBLIC HTProtocol HTNews = { "news", HTLoadNews, NULL }; +GLOBALDEF PUBLIC HTProtocol HTNNTP = { "nntp", HTLoadNews, NULL }; +GLOBALDEF PUBLIC HTProtocol HTSNews = { "snews", HTLoadNews, NULL }; +#endif /* GLOBALDEF_IS_MACRO */ diff --git a/WWW/Library/Implementation/HTNews.h b/WWW/Library/Implementation/HTNews.h new file mode 100644 index 00000000..4bc998af --- /dev/null +++ b/WWW/Library/Implementation/HTNews.h @@ -0,0 +1,35 @@ +/* Network News Transfer protocol module for the WWW library + HTNEWS + + */ +/* History: +** 26 Sep 90 Written TBL in Objective-C +** 29 Nov 91 Downgraded to C, for portable implementation. +*/ + +#ifndef HTNEWS_H +#define HTNEWS_H + +#include "HTAccess.h" +#include "HTAnchor.h" + +#ifdef GLOBALREF_IS_MACRO +extern GLOBALREF(HTProtocol, HTNews); +extern GLOBALREF(HTProtocol, HTNNTP); +extern GLOBALREF(HTProtocol, HTSNews); +#else +GLOBALREF HTProtocol HTNews; +GLOBALREF HTProtocol HTNNTP; +GLOBALREF HTProtocol HTSNews; +#endif /* GLOBALREF_IS_MACRO */ + +extern void HTSetNewsHost PARAMS((CONST char *value)); +extern CONST char * HTGetNewsHost NOPARAMS; +extern char * HTNewsHost; + +#endif /* HTNEWS_H */ + + +/* + + tbl */ diff --git a/WWW/Library/Implementation/HTParse.c b/WWW/Library/Implementation/HTParse.c new file mode 100644 index 00000000..463488f7 --- /dev/null +++ b/WWW/Library/Implementation/HTParse.c @@ -0,0 +1,606 @@ +/* Parse HyperText Document Address HTParse.c +** ================================ +*/ + +#include "HTUtils.h" +#include "tcp.h" +#include "HTParse.h" + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +#define HEX_ESCAPE '%' + +struct struct_parts { + char * access; + char * host; + char * absolute; + char * relative; +/* char * search; no - treated as part of path */ + char * anchor; +}; + + +/* Strip white space off a string +** ------------------------------ +** +** On exit, +** Return value points to first non-white character, or to 0 if none. +** All trailing white space is OVERWRITTEN with zero. +*/ + +PUBLIC char * HTStrip ARGS1( + char *, s) +{ +#define SPACE(c) ((c==' ')||(c=='\t')||(c=='\n')) + char * p = s; + for (p = s; *p; p++) + ; /* Find end of string */ + for (p--; p >= s; p--) { + if (SPACE(*p)) + *p = '\0'; /* Zap trailing blanks */ + else + break; + } + while (SPACE(*s)) + s++; /* Strip leading blanks */ + return s; +} + + +/* Scan a filename for its consituents +** ----------------------------------- +** +** On entry, +** name points to a document name which may be incomplete. +** On exit, +** absolute or relative may be nonzero (but not both). +** host, anchor and access may be nonzero if they were specified. +** Any which are nonzero point to zero terminated strings. +*/ +PRIVATE void scan ARGS2( + char *, name, + struct struct_parts *, parts) +{ + char * after_access; + char * p; + int length = strlen(name); + + parts->access = NULL; + parts->host = NULL; + parts->absolute = NULL; + parts->relative = NULL; + parts->anchor = NULL; + + after_access = name; + for (p = name; *p; p++) { + if (*p==':') { + *p = '\0'; + parts->access = name; /* Access name has been specified */ + after_access = p+1; + break; + } + if (*p=='/') + break; + if (*p=='#') + break; + } + + for (p = (name+length-1); p >= name; p--) { + if (*p =='#') { + parts->anchor = p + 1; + *p = '\0'; /* terminate the rest */ + } + } + p = after_access; + if (*p == '/') { + if (p[1] == '/') { + parts->host = p+2; /* host has been specified */ + *p = '\0'; /* Terminate access */ + p = strchr(parts->host,'/'); /* look for end of host name if any */ + if (p) { + *p = '\0'; /* Terminate host */ + parts->absolute = p+1; /* Root has been found */ + } + } else { + parts->absolute = p+1; /* Root found but no host */ + } + } else { + parts->relative = (*after_access) ? after_access : 0; /* zero for "" */ + } + + if (parts->access && parts->anchor) { + if ((!parts->host && strcasecomp(parts->access, "lynxcgi")) || + !strcasecomp(parts->access, "nntp") || + !strcasecomp(parts->access, "snews") || + !strcasecomp(parts->access, "news") || + !strcasecomp(parts->access, "data")) { + /* + * Access specified but no host and not a lynxcgi URL, so the + * anchor may not really be one, e.g., news:j462#36487@foo.bar, + * or it's an nntp or snews URL, or news URL with a host. + * Restore the '#' in the address. + */ + *(parts->anchor - 1) = '#'; + parts->anchor = NULL; + } + } + +#ifdef NOT_DEFINED /* search is just treated as part of path */ + { + char *p = relative ? relative : absolute; + if (p) { + char * q = strchr(p, '?'); /* Any search string? */ + if (q) { + *q = '\0'; /* If so, chop that off. */ + parts->search = q+1; + } + } + } +#endif /* NOT_DEFINED */ +} /*scan */ + + +/* Parse a Name relative to another name +** ------------------------------------- +** +** This returns those parts of a name which are given (and requested) +** substituting bits from the related name where necessary. +** +** On entry, +** aName A filename given +** relatedName A name relative to which aName is to be parsed +** wanted A mask for the bits which are wanted. +** +** On exit, +** returns A pointer to a malloc'd string which MUST BE FREED +*/ +PUBLIC char * HTParse ARGS3( + CONST char *, aName, + CONST char *, relatedName, + int, wanted) +{ + char * result = NULL; + char * return_value = NULL; + int len; + char * name = NULL; + char * rel = NULL; + char * p; + char * access; + struct struct_parts given, related; + + /* Make working copies of input strings to cut up: + */ + len = strlen(aName)+strlen(relatedName)+10; + result = (char *)malloc(len); /* Lots of space: more than enough */ + if (result == NULL) + outofmem(__FILE__, "HTParse"); + + if (TRACE) + fprintf(stderr, + "HTParse: aName:%s relatedName:%s\n",aName,relatedName); + + StrAllocCopy(name, aName); + StrAllocCopy(rel, relatedName); + + scan(name, &given); + scan(rel, &related); + result[0] = '\0'; /* Clear string */ + if (given.access && given.host && !given.relative && !given.absolute) { + if (!strcmp(given.access, "http") || + !strcmp(given.access, "https") || + !strcmp(given.access, "ftp")) + given.absolute = ""; /* Assume root */ + } + access = given.access ? given.access : related.access; + if (wanted & PARSE_ACCESS) + if (access) { + strcat(result, access); + if (wanted & PARSE_PUNCTUATION) + strcat(result, ":"); + } + + /* If different, inherit nothing. */ + if (given.access && related.access && strcmp(given.access,related.access)) { + related.host = NULL; + related.absolute = NULL; + related.relative = NULL; + related.anchor = NULL; + } + + if (wanted & PARSE_HOST) + if (given.host || related.host) { + char * tail = result + strlen(result); + if (wanted & PARSE_PUNCTUATION) + strcat(result, "//"); + strcat(result, given.host ? given.host : related.host); +#define CLEAN_URLS +#ifdef CLEAN_URLS + /* Ignore default port numbers, and trailing dots on FQDNs + which will only cause identical addresses to look different */ + { + char * p, * h; + p = strchr(tail, ':'); + if (p && access) { /* Port specified */ + if ( ( strcmp(access, "http") == 0 + && strcmp(p, ":80") == 0 ) + || + ( strcmp(access, "gopher") == 0 + && strcmp(p, ":70") == 0 ) + || + ( strcmp(access, "ftp") == 0 + && strcmp(p, ":21") == 0 ) + || + ( strcmp(access, "wais") == 0 + && strcmp(p, ":210") == 0 ) + || + ( (strcmp(access, "nntp") == 0 || + strcmp(access, "news") == 0) + && strcmp(p, ":119") == 0 ) + || + ( strcmp(access, "snews") == 0 + && strcmp(p, ":563") == 0 ) + || + ( strcmp(access, "finger") == 0 + && strcmp(p, ":79") == 0 ) + || + ( strcmp(access, "cso") == 0 + && strcmp(p, ":105") == 0 ) + ) + *p = '\0'; /* It is the default: ignore it */ + } + if (!p) { + int len = strlen(tail); + + if (len > 0) { + h = tail + len - 1; /* last char of hostname */ + if (*h == '.') + *h = '\0'; /* chop final . */ + } + } else { + h = p; + h--; /* End of hostname */ + if (*h == '.') + strcpy(h, p); /* slide p over h */ + } + } +#endif /* CLEAN_URLS */ + } + + if (given.host && related.host) /* If different hosts, inherit no path. */ + if (strcmp(given.host, related.host) != 0) { + related.absolute = NULL; + related.relative = NULL; + related.anchor = NULL; + } + + if (wanted & PARSE_PATH) { + if (access && !given.absolute && given.relative) { + if (!strcasecomp(access, "nntp") || + !strcasecomp(access, "snews") || + (!strcasecomp(access, "news") && + !strncasecomp(result, "news://", 7))) { + /* + * Treat all given nntp or snews paths, + * or given paths for news URLs with a host, + * as absolute. + */ + given.absolute = given.relative; + given.relative = NULL; + } + } + if (given.absolute) { /* All is given */ + if (wanted & PARSE_PUNCTUATION) + strcat(result, "/"); + strcat(result, given.absolute); + if (TRACE) + fprintf(stderr,"1\n"); + } else if (related.absolute) { /* Adopt path not name */ + strcat(result, "/"); + strcat(result, related.absolute); + if (given.relative) { + p = strchr(result, '?'); /* Search part? */ + if (!p) + p = result+strlen(result)-1; + for (; *p!='/'; p--) + ; /* last / */ + p[1] = '\0'; /* Remove filename */ + strcat(result, given.relative); /* Add given one */ + HTSimplify (result); + } + if (TRACE) + fprintf(stderr,"2\n"); + } else if (given.relative) { + strcat(result, given.relative); /* what we've got */ + if (TRACE) + fprintf(stderr,"3\n"); + } else if (related.relative) { + strcat(result, related.relative); + if (TRACE) + fprintf(stderr,"4\n"); + } else { /* No inheritance */ + strcat(result, "/"); + if (TRACE) + fprintf(stderr,"5\n"); + } + } + + if (TRACE) + fprintf(stderr,"HTParse: result:%s\n",result); + + if (wanted & PARSE_ANCHOR) + if (given.anchor || related.anchor) { + if (wanted & PARSE_PUNCTUATION) + strcat(result, "#"); + strcat(result, given.anchor ? given.anchor : related.anchor); + } + FREE(rel); + FREE(name); + + StrAllocCopy(return_value, result); + FREE(result); + + return return_value; /* exactly the right length */ +} + + +/* Simplify a filename +** ------------------- +** +** A unix-style file is allowed to contain the seqeunce xxx/../ which may be +** replaced by "" , and the seqeunce "/./" which may be replaced by "/". +** Simplification helps us recognize duplicate filenames. +** +** Thus, /etc/junk/../fred becomes /etc/fred +** /etc/junk/./fred becomes /etc/junk/fred +** +** but we should NOT change +** http://fred.xxx.edu/../.. +** +** or ../../albert.html +*/ +PUBLIC void HTSimplify ARGS1( + char *, filename) +{ + char * p; + char * q; + + if (filename == NULL) + return; + + if (filename[0] && filename[1]) /* Bug fix 12 Mar 93 TBL */ + for (p = filename+2; *p; p++) { + if (*p == '/') { + if ((p[1] == '.') && (p[2]=='.') && (p[3] == '/' || !p[3])) { + for (q = (p-1); (q >= filename) && (*q != '/'); q--) + ; /* prev slash */ + if (q[0] == '/' && 0 != strncmp(q, "/../", 4) && + !(q-1 > filename && q[-1] == '/')) { + strcpy(q, p+3); /* Remove /xxx/.. */ + if (!(*filename)) + strcpy(filename, "/"); + p = q-1; /* Start again with prev slash */ + } else { /* xxx/.. leave it! */ +#ifdef BUG_CODE + strcpy(filename, p[3] ? p+4 : p+3); /* rm xxx/../ */ + p = filename; /* Start again */ +#endif /* BUG_CODE */ + } + } else if ((p[1] == '.') && (p[2] == '/' || !p[2])) { + strcpy(p, p+2); /* Remove a slash and a dot */ + } + } + } +} + + +/* Make Relative Name +** ------------------ +** +** This function creates and returns a string which gives an expression of +** one address as related to another. Where there is no relation, an absolute +** address is retured. +** +** On entry, +** Both names must be absolute, fully qualified names of nodes +** (no anchor bits) +** +** On exit, +** The return result points to a newly allocated name which, if +** parsed by HTParse relative to relatedName, will yield aName. +** The caller is responsible for freeing the resulting name later. +** +*/ +PUBLIC char * HTRelative ARGS2( + CONST char *, aName, + CONST char *, relatedName) +{ + char * result = NULL; + CONST char *p = aName; + CONST char *q = relatedName; + CONST char * after_access = NULL; + CONST char * path = NULL; + CONST char * last_slash = NULL; + int slashes = 0; + + for (; *p; p++, q++) { /* Find extent of match */ + if (*p != *q) + break; + if (*p == ':') + after_access = p+1; + if (*p == '/') { + last_slash = p; + slashes++; + if (slashes == 3) + path=p; + } + } + + /* q, p point to the first non-matching character or zero */ + + if (!after_access) { /* Different access */ + StrAllocCopy(result, aName); + } else if (slashes < 3){ /* Different nodes */ + StrAllocCopy(result, after_access); + } else if (slashes == 3){ /* Same node, different path */ + StrAllocCopy(result, path); + } else { /* Some path in common */ + int levels = 0; + for (; *q && (*q!='#'); q++) + if (*q=='/') + levels++; + result = (char *)malloc(3*levels + strlen(last_slash) + 1); + if (result == NULL) + outofmem(__FILE__, "HTRelative"); + result[0] = '\0'; + for (; levels; levels--) + strcat(result, "../"); + strcat(result, last_slash+1); + } + if (TRACE) + fprintf(stderr, "HT: `%s' expressed relative to\n `%s' is\n `%s'.", + aName, relatedName, result); + return result; +} + + +/* Escape undesirable characters using % HTEscape() +** ------------------------------------- +** +** This function takes a pointer to a string in which +** some characters may be unacceptable unescaped. +** It returns a string which has these characters +** represented by a '%' character followed by two hex digits. +** +** Unlike HTUnEscape(), this routine returns a malloced string. +*/ + +PRIVATE CONST unsigned char isAcceptable[96] = + +/* Bit 0 xalpha -- see HTFile.h +** Bit 1 xpalpha -- as xalpha but with plus. +** Bit 3 ... path -- as xpalphas but with / +*/ + /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + { 0,0,0,0,0,0,0,0,0,0,7,6,0,7,7,4, /* 2x !"#$%&'()*+,-./ */ + 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */ + 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */ + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0 }; /* 7X pqrstuvwxyz{\}~ DEL */ + +PRIVATE char *hex = "0123456789ABCDEF"; +#define ACCEPTABLE(a) ( a>=32 && a<128 && ((isAcceptable[a-32]) & mask)) + +PUBLIC char * HTEscape ARGS2( + CONST char *, str, + unsigned char, mask) +{ + CONST char * p; + char * q; + char * result; + int unacceptable = 0; + for (p = str; *p; p++) + if (!ACCEPTABLE((unsigned char)TOASCII(*p))) + unacceptable++; + result = (char *) malloc(p-str + unacceptable+ unacceptable + 1); + if (result == NULL) + outofmem(__FILE__, "HTEscape"); + for (q = result, p = str; *p; p++) { + unsigned char a = TOASCII(*p); + if (!ACCEPTABLE(a)) { + *q++ = HEX_ESCAPE; /* Means hex commming */ + *q++ = hex[a >> 4]; + *q++ = hex[a & 15]; + } + else *q++ = *p; + } + *q++ = '\0'; /* Terminate */ + return result; +} + + +/* Escape undesirable characters using % HTEscapeSP() +** ------------------------------------- +** +** This function takes a pointer to a string in which +** some characters may be unacceptable unescaped. +** It returns a string which has these characters +** represented by a '%' character followed by two hex digits, +** except that spaces are converted to '+'. +** +** Unlike HTUnEscape(), this routine returns a malloced string. +*/ +PUBLIC char * HTEscapeSP ARGS2( + CONST char *, str, + unsigned char, mask) +{ + CONST char * p; + char * q; + char * result; + int unacceptable = 0; + for (p = str; *p; p++) + if (!(*p == ' ' || ACCEPTABLE((unsigned char)TOASCII(*p)))) + unacceptable++; + result = (char *) malloc(p-str + unacceptable+ unacceptable + 1); + if (result == NULL) + outofmem(__FILE__, "HTEscape"); + for (q = result, p = str; *p; p++) { + unsigned char a = TOASCII(*p); + if (a == 32) { + *q++ = '+'; + } else if (!ACCEPTABLE(a)) { + *q++ = HEX_ESCAPE; /* Means hex commming */ + *q++ = hex[a >> 4]; + *q++ = hex[a & 15]; + } else { + *q++ = *p; + } + } + *q++ = '\0'; /* Terminate */ + return result; +} + + +/* Decode %xx escaped characters HTUnEscape() +** ----------------------------- +** +** This function takes a pointer to a string in which some +** characters may have been encoded in %xy form, where xy is +** the acsii hex code for character 16x+y. +** The string is converted in place, as it will never grow. +*/ + +PRIVATE char from_hex ARGS1( + char, c) +{ + return c >= '0' && c <= '9' ? c - '0' + : c >= 'A' && c <= 'F'? c - 'A' + 10 + : c - 'a' + 10; /* accept small letters just in case */ +} + +PUBLIC char * HTUnEscape ARGS1( + char *, str) +{ + char * p = str; + char * q = str; + while (*p) { + if (*p == HEX_ESCAPE) { + p++; + if (*p) + *q = from_hex(*p++) * 16; + if (*p) + *q = FROMASCII(*q + from_hex(*p++)); + q++; + } else { + *q++ = *p++; + } + } + + *q++ = '\0'; + return str; + +} /* HTUnEscape */ + + diff --git a/WWW/Library/Implementation/HTParse.h b/WWW/Library/Implementation/HTParse.h new file mode 100644 index 00000000..669837c2 --- /dev/null +++ b/WWW/Library/Implementation/HTParse.h @@ -0,0 +1,159 @@ +/* HTParse: URL parsing in the WWW Library + HTPARSE + + This module of the WWW library contains code to parse URLs and various related things. + Implemented by HTParse.c . + + */ +#ifndef HTPARSE_H +#define HTPARSE_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ + +/* + + The following are flag bits which may be ORed together to form a number to give the + 'wanted' argument to HTParse. + + */ +#define PARSE_ACCESS 16 +#define PARSE_HOST 8 +#define PARSE_PATH 4 +#define PARSE_ANCHOR 2 +#define PARSE_PUNCTUATION 1 +#define PARSE_ALL 31 + + +/* + +HTParse: Parse a URL relative to another URL + + This returns those parts of a name which are given (and requested) substituting bits + from the related name where necessary. + + ON ENTRY + + aName A filename given + + relatedName A name relative to which aName is to be parsed + + wanted A mask for the bits which are wanted. + + ON EXIT, + + returns A pointer to a malloc'd string which MUST BE FREED + + */ + +extern char * HTParse PARAMS(( + CONST char * aName, + CONST char * relatedName, + int wanted)); + + +/* + +HTStrip: Strip white space off a string + + ON EXIT + + Return value points to first non-white character, or to 0 if none. + + All trailing white space is OVERWRITTEN with zero. + + */ +extern char * HTStrip PARAMS(( + char * s)); + +/* + +HTSimplify: Simplify a UTL + + A URL is allowed to contain the seqeunce xxx/../ which may be replaced by "" , and the + seqeunce "/./" which may be replaced by "/". Simplification helps us recognize + duplicate filenames. It doesn't deal with soft links, though. The new (shorter) + filename overwrites the old. + + */ +/* +** Thus, /etc/junk/../fred becomes /etc/fred +** /etc/junk/./fred becomes /etc/junk/fred +*/ +extern void HTSimplify PARAMS(( + char * filename)); + + +/* + +HTRelative: Make Relative (Partial) URL + + This function creates and returns a string which gives an expression of one address as + related to another. Where there is no relation, an absolute address is retured. + + ON ENTRY, + + Both names must be absolute, fully qualified names of nodes (no anchor bits) + + ON EXIT, + + The return result points to a newly allocated name which, if parsed by HTParse relative + to relatedName, will yield aName. The caller is responsible for freeing the resulting + name later. + + */ +extern char * HTRelative PARAMS(( + CONST char * aName, + CONST char * relatedName)); + + +/* + +HTEscape: Encode unacceptable characters in string + + This funtion takes a string containing any sequence of ASCII characters, and returns a + malloced string containing the same infromation but with all "unacceptable" characters + represented in the form %xy where X and Y are two hex digits. + + */ +extern char * HTEscape PARAMS(( + CONST char * str, + unsigned char mask)); + +/* Convert space to plus instead of %2b - FM */ +extern char * HTEscapeSP PARAMS(( + CONST char * str, + unsigned char mask)); + +/* + + The following are valid mask values. The terms are the BNF names in the URL document. + + */ +#define URL_XALPHAS (unsigned char) 1 +#define URL_XPALPHAS (unsigned char) 2 +#define URL_PATH (unsigned char) 4 + + +/* + +HTUnEscape: Decode %xx escaped characters + + This function takes a pointer to a string in which character smay have been encoded in + %xy form, where xy is the acsii hex code for character 16x+y. The string is converted + in place, as it will never grow. + + */ +extern char * HTUnEscape PARAMS(( + char * str)); + + +#endif /* HTPARSE_H */ + + +/* + + end of HTParse + + */ diff --git a/WWW/Library/Implementation/HTPasswd.c b/WWW/Library/Implementation/HTPasswd.c new file mode 100644 index 00000000..58ae62c8 --- /dev/null +++ b/WWW/Library/Implementation/HTPasswd.c @@ -0,0 +1,301 @@ + +/* MODULE HTPasswd.c +** PASSWORD FILE ROUTINES +** +** AUTHORS: +** AL Ari Luotonen luotonen@dxcern.cern.ch +** MD Mark Donszelmann duns@vxdeop.cern.ch +** +** HISTORY: +** 7 Nov 93 MD free for crypt taken out (static data returned) +** +** +** BUGS: +** +** +*/ + + +#include "HTUtils.h" +#include "tcp.h" /* FROMASCII() */ +#include <string.h> +#include "HTAAUtil.h" /* Common parts of AA */ +#include "HTAAFile.h" /* File routines */ +#include "HTPasswd.h" /* Implemented here */ + +#include "LYLeaks.h" + +extern char *crypt(); + + +PRIVATE char salt_chars [65] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; + + +/* PRIVATE next_rec() +** GO TO THE BEGINNING OF THE NEXT RECORD +** Otherwise like HTAAFile_nextRec() but +** does not handle continuation lines +** (because password file has none). +** ON ENTRY: +** fp is the password file from which records are read from. +** +** ON EXIT: +** returns nothing. File read pointer is located at the beginning +** of the next record. +*/ +PRIVATE void next_rec ARGS1(FILE *, fp) +{ + int ch = getc(fp); + + while (ch != EOF && ch != CR && ch != LF) + ch = getc(fp); /* Skip until end-of-line */ + + while (ch != EOF && + (ch == CR || ch == LF)) /*Skip carriage returns and linefeeds*/ + ch = getc(fp); + + if (ch != EOF) + ungetc(ch, fp); +} + + +/* PUBLIC HTAA_encryptPasswd() +** ENCRYPT PASSWORD TO THE FORM THAT IT IS SAVED +** IN THE PASSWORD FILE. +** ON ENTRY: +** password is a string of arbitrary lenght. +** +** ON EXIT: +** returns password in one-way encrypted form. +** +** NOTE: +** Uses currently the C library function crypt(), which +** only accepts at most 8 characters long strings and produces +** always 13 characters long strings. This function is +** called repeatedly so that longer strings can be encrypted. +** This is of course not as safe as encrypting the entire +** string at once, but then again, we are not that paranoid +** about the security inside the machine. +** +*/ +PUBLIC char *HTAA_encryptPasswd ARGS1(CONST char *, password) +{ + char salt[3]; + char chunk[9]; + char *result; + char *tmp; + CONST char *cur = password; + int len = strlen(password); + extern time_t theTime; + int random = (int)theTime; /* This is random enough */ + + if (!(result = (char*)malloc(13*((strlen(password)+7)/8) + 1))) + outofmem(__FILE__, "HTAA_encryptPasswd"); + + *result = (char)0; + while (len > 0) { + salt[0] = salt_chars[random%64]; + salt[1] = salt_chars[(random/64)%64]; + salt[2] = (char)0; + + strncpy(chunk, cur, 8); + chunk[8] = (char)0; + + tmp = crypt((char*)password, salt); /*crypt() doesn't change its args*/ + strcat(result, tmp); + + cur += 8; + len -= 8; + } /* while */ + + return result; +} + + + +/* PUBLIC HTAA_passwdMatch() +** VERIFY THE CORRECTNESS OF A GIVEN PASSWORD +** AGAINST A ONE-WAY ENCRYPTED FORM OF PASSWORD. +** ON ENTRY: +** password is cleartext password. +** encrypted is one-way encrypted password, as returned +** by function HTAA_encryptPasswd(). +** This is typically read from the password +** file. +** +** ON EXIT: +** returns YES, if password matches the encrypted one. +** NO, if not, or if either parameter is NULL. +** FIX: +** Only the length of original encrypted password is +** checked -- longer given passwords are accepted if +** common length is correct (but not shorter). +** This is to allow interoperation of servers and clients +** who have a hard-coded limit of 8 to password. +*/ +PUBLIC BOOL HTAA_passwdMatch ARGS2(CONST char *, password, + CONST char *, encrypted) +{ + char *result; + int len; + int status; + + if (!password || !encrypted) + return NO; + + len = 13*((strlen(password)+7)/8); + if (len < strlen(encrypted)) + return NO; + + if (!(result = (char*)malloc(len + 1))) + outofmem(__FILE__, "HTAA_encryptPasswd"); + + *result = (char)0; + while (len > 0) { + char salt[3]; + char chunk[9]; + CONST char *cur1 = password; + CONST char *cur2 = encrypted; + char *tmp; + + salt[0] = *cur2; + salt[1] = *(cur2+1); + salt[2] = (char)0; + + strncpy(chunk, cur1, 8); + chunk[8] = (char)0; + + tmp = crypt((char*)password, salt); + strcat(result, tmp); + + cur1 += 8; + cur2 += 13; + len -= 13; + } /* while */ + + status = strncmp(result, encrypted, strlen(encrypted)); + + if (TRACE) + fprintf(stderr, + "%s `%s' (encrypted: `%s') with: `%s' => %s\n", + "HTAA_passwdMatch: Matching password:", + password, result, encrypted, + (status==0 ? "OK" : "INCORRECT")); + + FREE(result); + + if (status==0) + return YES; + else + return NO; +} + + +/* PUBLIC HTAAFile_readPasswdRec() +** READ A RECORD FROM THE PASSWORD FILE +** ON ENTRY: +** fp open password file +** out_username buffer to put the read username, must be at +** least MAX_USERNAME_LEN+1 characters long. +** out_passwd buffer to put the read password, must be at +** least MAX_PASSWORD_LEN+1 characters long. +** ON EXIT: +** returns EOF on end of file, +** otherwise the number of read fields +** (i.e. in a correct case returns 2). +** out_username contains the null-terminated read username. +** out_password contains the null-terminated read password. +** +** FORMAT OF PASSWORD FILE: +** username:password:maybe real name or other stuff +** (may include even colons) +** +** There may be whitespace (blanks or tabs) in the beginning and +** the end of each field. They are ignored. +*/ +PUBLIC int HTAAFile_readPasswdRec ARGS3(FILE *, fp, + char *, out_username, + char *, out_password) +{ + char terminator; + + terminator = HTAAFile_readField(fp, out_username, MAX_USERNAME_LEN); + + if (terminator == EOF) { /* End of file */ + return EOF; + } + else if (terminator == CR || terminator == LF) { /* End of line */ + next_rec(fp); + return 1; + } + else { + HTAAFile_readField(fp, out_password, MAX_PASSWORD_LEN); + next_rec(fp); + return 2; + } +} + + + +/* PUBLIC HTAA_checkPassword() +** CHECK A USERNAME-PASSWORD PAIR +** ON ENTRY: +** username is a null-terminated string containing +** the client's username. +** password is a null-terminated string containing +** the client's corresponding password. +** filename is a null-terminated absolute filename +** for password file. +** If NULL or empty, the value of +** PASSWD_FILE is used. +** ON EXIT: +** returns YES, if the username-password pair was correct. +** NO, otherwise; also, if open fails. +*/ +PUBLIC BOOL HTAA_checkPassword ARGS3(CONST char *, username, + CONST char *, password, + CONST char *, filename) +{ + FILE *fp = NULL; + char user[MAX_USERNAME_LEN+1]; + char pw[MAX_PASSWORD_LEN+1]; + int status; + + if (filename && *filename) fp = fopen(filename,"r"); + else fp = fopen(PASSWD_FILE,"r"); + + if (!fp) { + if (TRACE) fprintf(stderr, "%s `%s'\n", + "HTAA_checkPassword: Unable to open password file", + (filename && *filename ? filename : PASSWD_FILE)); + return NO; + } + do { + if (2 == (status = HTAAFile_readPasswdRec(fp,user,pw))) { + if (TRACE) + fprintf(stderr, + "HTAAFile_validateUser: %s \"%s\" %s \"%s:%s\"\n", + "Matching username:", username, + "against passwd record:", user, pw); + if (username && user && !strcmp(username,user)) { + /* User's record found */ + if (*pw != '\0') { /* So password is required for this user */ + if (!password || + !HTAA_passwdMatch(password,pw)) /* Check the password */ + status = EOF; /* If wrong, indicate it with EOF */ + } + break; /* exit loop */ + } /* if username found */ + } /* if record is ok */ + } while (status != EOF); + + fclose(fp); + + if (TRACE) fprintf(stderr, "HTAAFile_checkPassword: (%s,%s) %scorrect\n", + username, password, ((status != EOF) ? "" : "in")); + + if (status == EOF) return NO; /* We traversed to the end without luck */ + else return YES; /* The user was found */ +} + diff --git a/WWW/Library/Implementation/HTPasswd.h b/WWW/Library/Implementation/HTPasswd.h new file mode 100644 index 00000000..0c3b3eb6 --- /dev/null +++ b/WWW/Library/Implementation/HTPasswd.h @@ -0,0 +1,129 @@ +/* PASSWORD FILE ROUTINES + + */ + +#ifndef HTPASSWD_H +#define HTPASSWD_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTList.h" + +#ifdef SHORT_NAMES +#define HTAAenPw HTAA_encryptPasswd +#define HTAApwMa HTAA_passwdMatch +#define HTAAFrPR HTAAFile_readPasswdRec +#define HTAAchPw HTAA_checkPasswd +#endif /* SHORT_NAMES */ + +/* + +User Authentication + + HTAA_checkPassword(username,password,passwdfile)opens the password file, and checks if + the username-password pair is correct. Return value is YES, if and only if they are + correct. Otherwise, and also if the open fails, returns NO. + + If the given password file name is NULL or an empty string, the default password file + name is used (macro PASSWD_FILE). + + */ + +/* PUBLIC HTAA_checkPassword() +** VALIDATE A USERNAME-PASSWORD PAIR +** ON ENTRY: +** username is a null-terminated string containing +** the client's username. +** password is a null-terminated string containing +** the client's corresponding password. +** filename is a null-terminated absolute filename +** for password file. +** If NULL or empty, the value of +** PASSWD_FILE is used. +** ON EXIT: +** returns YES, if the username-password pair was correct. +** NO, otherwise; also, if open fails. +*/ +PUBLIC BOOL HTAA_checkPassword PARAMS((CONST char * username, + CONST char * password, + CONST char * filename)); +/* + +Password File Maintenance Routines + + */ + +/* PUBLIC HTAA_encryptPasswd() +** ENCRYPT PASSWORD TO THE FORM THAT IT IS SAVED +** IN THE PASSWORD FILE. +** ON ENTRY: +** password is a string of arbitrary lenght. +** +** ON EXIT: +** returns password in one-way encrypted form. +** +** NOTE: +** Uses currently the C library function crypt(), which +** only accepts at most 8 characters long strings and produces +** always 13 characters long strings. This function is +** called repeatedly so that longer strings can be encrypted. +** This is of course not as safe as encrypting the entire +** string at once, but then again, we are not that paranoid +** about the security inside the machine. +** +*/ +PUBLIC char *HTAA_encryptPasswd PARAMS((CONST char * password)); + + +/* PUBLIC HTAA_passwdMatch() +** VERIFY THE CORRECTNESS OF A GIVEN PASSWORD +** AGAINST A ONE-WAY ENCRYPTED FORM OF PASSWORD. +** ON ENTRY: +** password is cleartext password. +** encrypted is one-way encrypted password, as returned +** by function HTAA_encryptPasswd(). +** This is typically read from the password +** file. +** +** ON EXIT: +** returns YES, if password matches the encrypted one. +** NO, if not, or if either parameter is NULL. +*/ +PUBLIC BOOL HTAA_passwdMatch PARAMS((CONST char * password, + CONST char * encrypted)); + + +/* PUBLIC HTAAFile_readPasswdRec() +** READ A RECORD FROM THE PASSWORD FILE +** ON ENTRY: +** fp open password file +** out_username buffer to put the read username, must be at +** least MAX_USERNAME_LEN+1 characters long. +** out_passwd buffer to put the read password, must be at +** least MAX_PASSWORD_LEN+1 characters long. +** ON EXIT: +** returns EOF on end of file, +** otherwise the number of read fields +** (i.e. in a correct case returns 2). +** out_username contains the null-terminated read username. +** out_password contains the null-terminated read password. +** +** FORMAT OF PASSWORD FILE: +** username:password:maybe real name or other stuff +** (may include even colons) +** +** There may be whitespace (blanks or tabs) in the beginning and +** the end of each field. They are ignored. +*/ +PUBLIC int HTAAFile_readPasswdRec PARAMS((FILE * fp, + char * out_username, + char * out_password)); +/* + + */ + +#endif /* not HTPASSWD_H */ +/* + + End of file HTPasswd.h. */ diff --git a/WWW/Library/Implementation/HTPlain.c b/WWW/Library/Implementation/HTPlain.c new file mode 100644 index 00000000..009f24e7 --- /dev/null +++ b/WWW/Library/Implementation/HTPlain.c @@ -0,0 +1,311 @@ +/* Plain text object HTWrite.c +** ================= +** +** This version of the stream object just writes to a socket. +** The socket is assumed open and left open. +** +** Bugs: +** strings written must be less than buffer size. +*/ +#include "HTUtils.h" + +#include "HTPlain.h" + +#define BUFFER_SIZE 4096; /* Tradeoff */ + +#include "HText.h" +#include "HTStyle.h" +#include "HTMLDTD.h" +#include "HTCJK.h" + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +extern HTStyleSheet * styleSheet; + +extern int current_char_set; +extern char * LYchar_set_names[]; +extern CONST char **LYCharSets[]; +extern CONST char * HTMLGetEntityName PARAMS((int i)); +extern BOOL HTPassEightBitRaw; +extern BOOL HTPassHighCtrlRaw; +extern HTCJKlang HTCJK; + +PUBLIC int HTPlain_lastraw = -1; + +/* HTML Object +** ----------- +*/ +struct _HTStream { + CONST HTStreamClass * isa; + + HText * text; +}; + +/* Write the buffer out to the socket +** ---------------------------------- +*/ + + +/*_________________________________________________________________________ +** +** A C T I O N R O U T I N E S +*/ + +/* Character handling +** ------------------ +*/ +PRIVATE void HTPlain_put_character ARGS2(HTStream *, me, char, c) +{ +#ifdef REMOVE_CR_ONLY + /* throw away \r's */ + if (c != '\r') { + HText_appendCharacter(me->text, c); + } +#else + /* + ** See HTPlain_write() for explanations of the following code + ** (we've been called via HTPlain_put_string() to do for each + ** character of a terminated string what HTPlain_write() does + ** via a while loop for each character in a stream of given + ** length). - FM + */ + if ((HTPlain_lastraw == '\r') && c == '\n') { + HTPlain_lastraw = -1; + return; + } + HTPlain_lastraw = c; + if (c == '\r') { + HText_appendCharacter(me->text, '\n'); + } else if (HTCJK != NOCJK) { + HText_appendCharacter(me->text, c); + } else if ((unsigned char)c >= 127 && (unsigned char)c < 161 && + HTPassHighCtrlRaw) { + HText_appendCharacter(me->text, c); + } else if ((unsigned char)c == 160) { + HText_appendCharacter(me->text, ' '); + } else if ((unsigned char)c == 173) { + return; + } else if (((unsigned char)c >= 32 && (unsigned char)c < 127) || + c == '\n' || c == '\t') { + HText_appendCharacter(me->text, c); + } else if ((unsigned char)c > 160) { + if (!HTPassEightBitRaw && + strncmp(LYchar_set_names[current_char_set], "ISO Latin 1", 11)) { + int len, high, low, i, diff; + CONST char * name; + int value = (int)((unsigned char)c - 160); + name = HTMLGetEntityName(value); + len = strlen(name); + for(low=0, high = HTML_dtd.number_of_entities; + high > low; + diff < 0 ? (low = i+1) : (high = i)) { + /* Binary search */ + i = (low + (high-low)/2); + diff = strncmp(HTML_dtd.entity_names[i], name, len); + if (diff==0) { + HText_appendText(me->text, + LYCharSets[current_char_set][i]); + break; + } + } + if (diff) { + HText_appendCharacter(me->text, c); + } + } else { + HText_appendCharacter(me->text, c); + } + } +#endif /* REMOVE_CR_ONLY */ +} + + +/* String handling +** --------------- +** +*/ +PRIVATE void HTPlain_put_string ARGS2(HTStream *, me, CONST char*, s) +{ +#ifdef REMOVE_CR_ONLY + HText_appendText(me->text, s); +#else + CONST char * p; + + if (s == NULL) + return; + for (p = s; *p; p++) { + HTPlain_put_character(me, *p); + } +#endif /* REMOVE_CR_ONLY */ +} + + +/* +** Entry function for displayed text/plain and WWW_SOURCE strings. - FM +** --------------------------------------------------------------- +*/ +PRIVATE void HTPlain_write ARGS3(HTStream *, me, CONST char*, s, int, l) +{ + CONST char * p; + CONST char * e = s+l; + + for (p = s; p < e; p++) { +#ifdef REMOVE_CR_ONLY + /* + ** Append the whole string, but remove any \r's. - FM + */ + if (*p != '\r') { + HText_appendCharacter(me->text, *p); + } +#else + /* + ** Try to handle lone LFs, CRLFs and lone CRs + ** as newline, and to deal with control, ASCII, + ** and 8-bit characters based on best guesses + ** of what's appropriate. - FM + */ + if ((HTPlain_lastraw == '\r') && *p == '\n') { + HTPlain_lastraw = -1; + continue; + } + HTPlain_lastraw = *p; + if (*p == '\r') { + HText_appendCharacter(me->text, '\n'); + /* + ** If CJK mode is on, we'll assume the document matches + ** the user's selected character set, and if not, the + ** user should toggle off raw/CJK mode to reload. - FM + */ + } else if (HTCJK != NOCJK) { + HText_appendCharacter(me->text, *p); + /* + ** If HTPassHighCtrlRaw is set (e.g., for KOI8-R) assume the + ** document matches and pass 127-160 8-bit characters. If it + ** doesn't match, the user should toggle raw/CJK mode off. - FM + */ + } else if ((unsigned char)*p >= 127 && (unsigned char)*p < 161 && + HTPassHighCtrlRaw) { + HText_appendCharacter(me->text, *p); + /* + ** If neither HTPassHighCtrlRaw nor CJK is set, play it safe + ** and treat 160 (nbsp) as an ASCII space (32). - FM + */ + } else if ((unsigned char)*p == 160) { + HText_appendCharacter(me->text, ' '); + /* + ** If neither HTPassHighCtrlRaw nor CJK is set, play it safe + ** and ignore 173 (shy). - FM + */ + } else if ((unsigned char)*p == 173) { + continue; + /* + ** If we get to here, pass the displayable ASCII characters. - FM + */ + } else if (((unsigned char)*p >= 32 && (unsigned char)*p < 127) || + *p == '\n' || *p == '\t') { + HText_appendCharacter(me->text, *p); + /* + ** If we get to here and HTPassEightBitRaw or the + ** selected character set is not "ISO Latin 1", + ** use the translation tables for 161-255 8-bit + ** characters (173 was handled above). - FM + */ + } else if ((unsigned char)*p > 160) { + if (!HTPassEightBitRaw && + strncmp(LYchar_set_names[current_char_set], + "ISO Latin 1", 11)) { + /* + ** Attempt to translate. - FM + */ + int len, high, low, i, diff; + CONST char * name; + int value = (int)((unsigned char)*p - 160); + name = HTMLGetEntityName(value); + len = strlen(name); + for(low=0, high = HTML_dtd.number_of_entities; + high > low; + diff < 0 ? (low = i+1) : (high = i)) { + /* Binary search */ + i = (low + (high-low)/2); + diff = strncmp(HTML_dtd.entity_names[i], name, len); + if (diff==0) { + HText_appendText(me->text, + LYCharSets[current_char_set][i]); + break; + } + } + if (diff) { + /* + ** Something went wrong in the translation, so + ** pass the raw character and hope it's OK. - FM + */ + HText_appendCharacter(me->text, *p); + } + } else { + /* + ** Didn't attempt a translation. - FM + */ + HText_appendCharacter(me->text, *p); + } + } +#endif /* REMOVE_CR_ONLY */ + } +} + + +/* Free an HTML object +** ------------------- +** +** Note that the SGML parsing context is freed, but the created object is +** not, as it takes on an existence of its own unless explicitly freed. +*/ +PRIVATE void HTPlain_free ARGS1(HTStream *, me) +{ + FREE(me); +} + + +/* End writing +*/ +PRIVATE void HTPlain_abort ARGS2(HTStream *, me, HTError, e) +{ + HTPlain_free(me); +} + + +/* Structured Object Class +** ----------------------- +*/ +PUBLIC CONST HTStreamClass HTPlain = +{ + "SocketWriter", + HTPlain_free, + HTPlain_abort, + HTPlain_put_character, HTPlain_put_string, HTPlain_write, +}; + + +/* New object +** ---------- +*/ +PUBLIC HTStream* HTPlainPresent ARGS3( + HTPresentation *, pres, + HTParentAnchor *, anchor, + HTStream *, sink) +{ + + HTStream* me = (HTStream*)malloc(sizeof(*me)); + if (me == NULL) + outofmem(__FILE__, "HTPlain_new"); + me->isa = &HTPlain; + + HTPlain_lastraw = -1; + + me->text = HText_new(anchor); + HText_setStyle(me->text, HTStyleNamed(styleSheet, "Example")); + HText_beginAppend(me->text); + + return (HTStream*) me; +} + diff --git a/WWW/Library/Implementation/HTPlain.h b/WWW/Library/Implementation/HTPlain.h new file mode 100644 index 00000000..f4ce3e14 --- /dev/null +++ b/WWW/Library/Implementation/HTPlain.h @@ -0,0 +1,21 @@ +/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTPlain.html + PLAIN TEXT OBJECT + + */ +#ifndef HTPLAIN_H +#define HTPLAIN_H + +#include "HTStream.h" +#include "HTAnchor.h" + +extern HTStream* HTPlainPresent PARAMS(( + HTPresentation * pres, + HTParentAnchor * anchor, + HTStream * sink)); + + +#endif + +/* + + */ diff --git a/WWW/Library/Implementation/HTRules.c b/WWW/Library/Implementation/HTRules.c new file mode 100644 index 00000000..92de0fc7 --- /dev/null +++ b/WWW/Library/Implementation/HTRules.c @@ -0,0 +1,418 @@ +/* Configuration manager for Hypertext Daemon HTRules.c +** ========================================== +** +** +** History: +** 3 Jun 91 Written TBL +** 10 Aug 91 Authorisation added after Daniel Martin (pass, fail) +** Rule order in file changed +** Comments allowed with # on 1st char of rule line +** 17 Jun 92 Bug fix: pass and fail failed if didn't contain '*' TBL +** 1 Sep 93 Bug fix: no memory check - Nathan Torkington +** BYTE_ADDRESSING removed - Arthur Secret +** 11 Sep 93 MD Changed %i into %d in debug printf. +** VMS does not recognize %i. +** Bug Fix: in case of PASS, only one parameter to printf. +** 19 Sep 93 AL Added Access Authorization stuff. +** 1 Nov 93 AL Added htbin. +** +*/ + +/* (c) CERN WorldWideWeb project 1990,91. See Copyright.html for details */ +#include "HTRules.h" + +#include "HTUtils.h" +#include "tcp.h" +/*#include <stdio.h> included by HTUtils.h -- FM */ +#include "HTFile.h" +#include "HTAAServ.h" /* Access Authorization */ + +#include "LYLeaks.h" + +#define LINE_LENGTH 256 + + +typedef struct _rule { + struct _rule * next; + HTRuleOp op; + char * pattern; + char * equiv; +} rule; + +/* Global variables +** ---------------- +*/ +PUBLIC char *HTBinDir = NULL; /* Physical /htbin directory path. */ + /* In future this should not be global. */ +PUBLIC char *HTSearchScript = NULL; /* Search script name. */ + + +/* Module-wide variables +** --------------------- +*/ + +PRIVATE rule * rules = 0; /* Pointer to first on list */ +#ifndef PUT_ON_HEAD +PRIVATE rule * rule_tail = 0; /* Pointer to last on list */ +#endif + + +/* Add rule to the list HTAddRule() +** -------------------- +** +** On entry, +** pattern points to 0-terminated string containing a single "*" +** equiv points to the equivalent string with * for the +** place where the text matched by * goes. +** On exit, +** returns 0 if success, -1 if error. +*/ + +#ifdef __STDC__ +PUBLIC int HTAddRule (HTRuleOp op, const char * pattern, const char * equiv) +#else +int HTAddRule(op, pattern, equiv) + HTRuleOp op; + char * pattern; + char * equiv; +#endif +{ /* BYTE_ADDRESSING removed and memory check - AS - 1 Sep 93 */ + rule * temp; + char * pPattern; + + temp = (rule *)malloc(sizeof(*temp)); + if (temp==NULL) + outofmem(__FILE__, "HTAddRule"); + pPattern = (char *)malloc(strlen(pattern)+1); + if (pPattern==NULL) + outofmem(__FILE__, "HTAddRule"); + if (equiv) { /* Two operands */ + char * pEquiv = (char *)malloc(strlen(equiv)+1); + if (pEquiv==NULL) + outofmem(__FILE__, "HTAddRule"); + temp->equiv = pEquiv; + strcpy(pEquiv, equiv); + } else { + temp->equiv = 0; + } + temp->pattern = pPattern; + temp->op = op; + + strcpy(pPattern, pattern); + if (TRACE) { + if (equiv) + fprintf(stderr, "Rule: For `%s' op %d `%s'\n", pattern, op, equiv); + else + fprintf(stderr, "Rule: For `%s' op %d\n", pattern, op); + } + +#ifdef PUT_ON_HEAD + temp->next = rules; + rules = temp; +#else + temp->next = 0; + if (rule_tail) rule_tail->next = temp; + else rules = temp; + rule_tail = temp; +#endif + + + return 0; +} + + +/* Clear all rules HTClearRules() +** --------------- +** +** On exit, +** There are no rules +** returns 0 if success, -1 if error. +** +** See also +** HTAddRule() +*/ +#ifdef __STDC__ +int HTClearRules(void) +#else +int HTClearRules() +#endif +{ + while (rules) { + rule * temp = rules; + rules = temp->next; + FREE(temp->pattern); + FREE(temp->equiv); + FREE(temp); + } +#ifndef PUT_ON_HEAD + rule_tail = 0; +#endif + + return 0; +} + + +/* Translate by rules HTTranslate() +** ------------------ +** +** The most recently defined rules are applied first. +** +** On entry, +** required points to a string whose equivalent value is neeed +** On exit, +** returns the address of the equivalent string allocated from +** the heap which the CALLER MUST FREE. If no translation +** occured, then it is a copy of te original. +** NEW FEATURES: +** When a "protect" or "defprot" rule is mathed, +** a call to HTAA_setCurrentProtection() or +** HTAA_setDefaultProtection() is made to notify +** the Access Authorization module that the file is +** protected, and so it knows how to handle it. +** -- AL +*/ +#ifdef __STDC__ +char * HTTranslate(const char * required) +#else +char * HTTranslate(required) + char * required; +#endif +{ + rule * r; + char *current = NULL; + StrAllocCopy(current, required); + + HTAA_clearProtections(); /* Reset from previous call -- AL */ + + for(r = rules; r; r = r->next) { + char * p = r->pattern; + int m=0; /* Number of characters matched against wildcard */ + CONST char * q = current; + for(;*p && *q; p++, q++) { /* Find first mismatch */ + if (*p!=*q) break; + } + + if (*p == '*') { /* Match up to wildcard */ + m = strlen(q) - strlen(p+1); /* Amount to match to wildcard */ + if(m<0) continue; /* tail is too short to match */ + if (0!=strcmp(q+m, p+1)) continue; /* Tail mismatch */ + } else /* Not wildcard */ + if (*p != *q) continue; /* plain mismatch: go to next rule */ + + switch (r->op) { /* Perform operation */ + +#ifdef ACCESS_AUTH + case HT_DefProt: + case HT_Protect: + { + char *local_copy = NULL; + char *p; + char *eff_ids = NULL; + char *prot_file = NULL; + + if (TRACE) fprintf(stderr, + "HTRule: `%s' matched %s %s: `%s'\n", + current, + (r->op==HT_Protect ? "Protect" : "DefProt"), + "rule, setup", + (r->equiv ? r->equiv : + (r->op==HT_Protect ?"DEFAULT" :"NULL!!"))); + + if (r->equiv) { + StrAllocCopy(local_copy, r->equiv); + p = local_copy; + prot_file = HTNextField(&p); + eff_ids = HTNextField(&p); + } + + if (r->op == HT_Protect) + HTAA_setCurrentProtection(current, prot_file, eff_ids); + else + HTAA_setDefaultProtection(current, prot_file, eff_ids); + + FREE(local_copy); + + /* continue translating rules */ + } + break; +#endif /* ACCESS_AUTH */ + + case HT_Pass: /* Authorised */ + if (!r->equiv) { + if (TRACE) fprintf(stderr, "HTRule: Pass `%s'\n", current); + return current; + } + /* Else fall through ...to map and pass */ + + case HT_Map: + if (*p == *q) { /* End of both strings, no wildcard */ + if (TRACE) fprintf(stderr, + "For `%s' using `%s'\n", current, r->equiv); + StrAllocCopy(current, r->equiv); /* use entire translation */ + } else { + char * ins = strchr(r->equiv, '*'); /* Insertion point */ + if (ins) { /* Consistent rule!!! */ + char * temp = (char *)malloc( + strlen(r->equiv)-1 + m + 1); + if (temp==NULL) + outofmem(__FILE__, "HTTranslate"); /* NT & AS */ + strncpy(temp, r->equiv, ins-r->equiv); + /* Note: temp may be unterminated now! */ + strncpy(temp+(ins-r->equiv), q, m); /* Matched bit */ + strcpy (temp+(ins-r->equiv)+m, ins+1); /* Last bit */ + if (TRACE) fprintf(stderr, "For `%s' using `%s'\n", + current, temp); + FREE(current); + current = temp; /* Use this */ + + } else { /* No insertion point */ + char * temp = (char *)malloc(strlen(r->equiv)+1); + if (temp==NULL) + outofmem(__FILE__, "HTTranslate"); /* NT & AS */ + strcpy(temp, r->equiv); + if (TRACE) fprintf(stderr, "For `%s' using `%s'\n", + current, temp); + FREE(current); + current = temp; /* Use this */ + } /* If no insertion point exists */ + } + if (r->op == HT_Pass) { + if (TRACE) fprintf(stderr, "HTRule: ...and pass `%s'\n", + current); + return current; + } + break; + + case HT_Invalid: + case HT_Fail: /* Unauthorised */ + if (TRACE) fprintf(stderr, "HTRule: *** FAIL `%s'\n", + current); + return (char *)0; + + } /* if tail matches ... switch operation */ + + } /* loop over rules */ + + + return current; +} + +/* Load one line of configuration +** ------------------------------ +** +** Call this, for example, to load a X resource with config info. +** +** returns 0 OK, < 0 syntax error. +*/ +PUBLIC int HTSetConfiguration ARGS1(CONST char *, config) +{ + HTRuleOp op; + char * line = NULL; + char * pointer = line; + char *word1, *word2, *word3; + float quality, secs, secs_per_byte; + int maxbytes; + int status; + + StrAllocCopy(line, config); + { + char * p = strchr(line, '#'); /* Chop off comments */ + if (p) *p = 0; + } + pointer = line; + word1 = HTNextField(&pointer); + if (!word1) { + FREE(line); + return 0; + } ; /* Comment only or blank */ + + word2 = HTNextField(&pointer); + + if (0==strcasecomp(word1, "defprot") || + 0==strcasecomp(word1, "protect")) + word3 = pointer; /* The rest of the line to be parsed by AA module */ + else + word3 = HTNextField(&pointer); /* Just the next word */ + + if (!word2) { + fprintf(stderr, "HTRule: Insufficient operands: %s\n", line); + FREE(line); + return -2; /*syntax error */ + } + + if (0==strcasecomp(word1, "suffix")) { + char * encoding = HTNextField(&pointer); + if (pointer) status = sscanf(pointer, "%f", &quality); + else status = 0; + HTSetSuffix(word2, word3, + encoding ? encoding : "binary", + status >= 1? quality : 1.0); + + } else if (0==strcasecomp(word1, "presentation")) { + if (pointer) status = sscanf(pointer, "%f%f%f%d", + &quality, &secs, &secs_per_byte, &maxbytes); + else status = 0; + HTSetPresentation(word2, word3, + status >= 1? quality : 1.0, + status >= 2 ? secs : 0.0, + status >= 3 ? secs_per_byte : 0.0, + status >= 4 ? maxbytes : 0 ); + + } else if (0==strncasecomp(word1, "htbin", 5) || + 0==strncasecomp(word1, "bindir", 6)) { + StrAllocCopy(HTBinDir, word2); /* Physical /htbin location */ + + } else if (0==strncasecomp(word1, "search", 6)) { + StrAllocCopy(HTSearchScript, word2); /* Search script name */ + + } else { + op = 0==strcasecomp(word1, "map") ? HT_Map + : 0==strcasecomp(word1, "pass") ? HT_Pass + : 0==strcasecomp(word1, "fail") ? HT_Fail + : 0==strcasecomp(word1, "defprot") ? HT_DefProt + : 0==strcasecomp(word1, "protect") ? HT_Protect + : HT_Invalid; + if (op==HT_Invalid) { + fprintf(stderr, "HTRule: Bad rule `%s'\n", config); + } else { + HTAddRule(op, word2, word3); + } + } + FREE(line); + return 0; +} + + +/* Load the rules from a file HTLoadRules() +** -------------------------- +** +** On entry, +** Rules can be in any state +** On exit, +** Any existing rules will have been kept. +** Any new rules will have been loaded. +** Returns 0 if no error, 0 if error! +** +** Bugs: +** The strings may not contain spaces. +*/ + +int HTLoadRules ARGS1(CONST char *, filename) +{ + FILE * fp = fopen(filename, "r"); + char line[LINE_LENGTH+1]; + + if (!fp) { + if (TRACE) fprintf(stderr, + "HTRules: Can't open rules file %s\n", filename); + return -1; /* File open error */ + } + for(;;) { + if (!fgets(line, LINE_LENGTH+1, fp)) break; /* EOF or error */ + (void) HTSetConfiguration(line); + } + fclose(fp); + return 0; /* No error or syntax errors ignored */ +} + + diff --git a/WWW/Library/Implementation/HTRules.h b/WWW/Library/Implementation/HTRules.h new file mode 100644 index 00000000..84075244 --- /dev/null +++ b/WWW/Library/Implementation/HTRules.h @@ -0,0 +1,165 @@ +/* Configuration Manager for libwww + CONFIGURATION MANAGER + + Author Tim Berners-Lee/CERN. Public domain. Please mail changes to timbl@info.cern.ch. + + The configuration information loaded includes tables (file suffixes, presentation + methods) in other modules. The most likely routines needed by developers will be: + + HTSetConfiguration to load configuration information. + + HTLoadRules to load a whole file of configuration information + + HTTranslate to translate a URL using the rule table. + + */ +#ifndef HTRULE_H +#define HTRULE_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ + +typedef enum _HTRuleOp { + HT_Invalid, + HT_Map, + HT_Pass, + HT_Fail, + HT_DefProt, + HT_Protect +} HTRuleOp; + +#ifdef SHORT_NAMES +#define HTSearSc HTSearchScript +#endif /*SHORT_NAMES*/ + +/* + +Server Side Script Execution + + If a URL starts with /htbin/ it is understood to mean a script execution request on + server. This feature needs to be turned on by setting HTBinDir by the htbin rule. + Index searching is enabled by setting HTSearchScript into the name of script in BinDir + doing the actual search by search rule (BinDir must also be set in this case, of + course). + + */ + +extern char * HTBinDir; /* Physical /htbin location */ +extern char * HTSearchScript; /* Search script name */ + +/* + +HTAddRule: Add rule to the list + + ON ENTRY, + + pattern points to 0-terminated string containing a single "*" + + equiv points to the equivalent string with * for the place where the + text matched by * goes. + + ON EXIT, + + returns 0 if success, -1 if error. + + Note that if BYTE_ADDRESSING is set, the three blocks required are allocated and + deallocated as one. This will save time and storage, when malloc's allocation units are + large. + + */ +extern int HTAddRule PARAMS((HTRuleOp op, const char * pattern, const char * equiv)); + + +/* + +HTClearRules: Clear all rules + + ON EXIT, + + Rule file There are no rules + + returns + 0 if success, -1 if error. + + */ + +#ifdef __STDC__ +extern int HTClearRules(void); +#else +extern int HTClearRules(); +#endif + + +/* + +HTTranslate: Translate by rules + + */ + +/* + + ON ENTRY, + + required points to a string whose equivalent value is neeed + + ON EXIT, + + returns the address of the equivalent string allocated from the heap + which the CALLER MUST FREE. If no translation occured, then it is + a copy of the original. + + */ +#ifdef __STDC__ +extern char * HTTranslate(const char * required); +#else +extern char * HTTranslate(); +#endif + + +/* + +HTSetConfiguration: Load one line of configuration information + + ON ENTRY, + + config is a string in the syntax of a rule file line. + + This routine may be used for loading configuration information from sources other than + the rule file, for example INI files for X resources. + + */ +extern int HTSetConfiguration PARAMS((CONST char * config)); + + +/* + +HtLoadRules: Load the rules from a file + + ON ENTRY, + + Rule table Rules can be in any state + + ON EXIT, + + Rule table Any existing rules will have been kept. Any new rules will have + been loaded on top, so as to be tried first. + + Returns 0 if no error. + + */ + +#ifdef __STDC__ +extern int HTLoadRules(const char * filename); +#else +extern int HTLoadRules(); +#endif +/* + + */ + + +#endif /* HTUtils.h */ +/* + + end */ diff --git a/WWW/Library/Implementation/HTStream.h b/WWW/Library/Implementation/HTStream.h new file mode 100644 index 00000000..72344cd3 --- /dev/null +++ b/WWW/Library/Implementation/HTStream.h @@ -0,0 +1,61 @@ +/* The Stream class definition -- libwww + STREAM OBJECT DEFINITION + + A Stream object is something which accepts a stream of text. + + The creation methods will vary on the type of Stream Object. All creation methods + return a pointer to the stream type below. + + As you can see, but the methods used to write to the stream and close it are pointed to + be the object itself. + + */ +#ifndef HTSTREAM_H +#define HTSTREAM_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ + +typedef struct _HTStream HTStream; + +/* + + These are the common methods of all streams. They should be self-explanatory, except + for end_document which must be called before free. It should be merged with free in + fact: it should be dummy for new streams. + + The put_block method was write, but this upset systems whiuch had macros for write(). + + */ +typedef struct _HTStreamClass { + + char* name; /* Just for diagnostics */ + + void (*_free) PARAMS(( + HTStream* me)); + + void (*_abort) PARAMS(( + HTStream* me, + HTError e)); + + void (*put_character) PARAMS(( + HTStream* me, + char ch)); + + void (*put_string) PARAMS(( + HTStream* me, + CONST char * str)); + + void (*put_block) PARAMS(( + HTStream* me, + CONST char * str, + int len)); + +}HTStreamClass; + +#endif /* HTSTREAM_H */ + +/* + + end of HTStream.h */ diff --git a/WWW/Library/Implementation/HTString.c b/WWW/Library/Implementation/HTString.c new file mode 100644 index 00000000..9f693924 --- /dev/null +++ b/WWW/Library/Implementation/HTString.c @@ -0,0 +1,156 @@ +/* Case-independent string comparison HTString.c +** +** Original version came with listserv implementation. +** Version TBL Oct 91 replaces one which modified the strings. +** 02-Dec-91 (JFG) Added stralloccopy and stralloccat +** 23 Jan 92 (TBL) Changed strallocc* to 8 char HTSAC* for VM and suchlike +** 6 Oct 92 (TBL) Moved WWW_TraceFlag in here to be in library +*/ +#include <ctype.h> +#include "HTUtils.h" +#include "tcp.h" + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +PUBLIC int WWW_TraceFlag = 0; /* Global trace flag for ALL W3 code */ + +#ifndef VC +#define VC "unknown" +#endif /* !VC */ + +PUBLIC CONST char * HTLibraryVersion = VC; /* String for help screen etc */ + +#ifndef VM /* VM has these already it seems */ + +/* Strings of any length +** --------------------- +*/ +PUBLIC int strcasecomp ARGS2( + CONST char*, a, + CONST char *, b) +{ + CONST char *p = a; + CONST char *q = b; + + for (p = a, q = b; *p && *q; p++, q++) { + int diff = TOLOWER(*p) - TOLOWER(*q); + if (diff) return diff; + } + if (*p) + return 1; /* p was longer than q */ + if (*q) + return -1; /* p was shorter than q */ + return 0; /* Exact match */ +} + + +/* With count limit +** ---------------- +*/ +PUBLIC int strncasecomp ARGS3( + CONST char*, a, + CONST char *, b, + int, n) +{ + CONST char *p = a; + CONST char *q = b; + + for (p = a, q = b; ; p++, q++) { + int diff; + if (p == (a+n)) + return 0; /* Match up to n characters */ + if (!(*p && *q)) + return (*p - *q); + diff = TOLOWER(*p) - TOLOWER(*q); + if (diff) + return diff; + } + /*NOTREACHED*/ +} +#endif /* VM */ + +/* Allocate a new copy of a string, and returns it +*/ +PUBLIC char * HTSACopy ARGS2( + char **, dest, + CONST char *, src) +{ + FREE(*dest); + if (src) { + *dest = (char *) malloc (strlen(src) + 1); + if (*dest == NULL) + outofmem(__FILE__, "HTSACopy"); + strcpy (*dest, src); + } + return *dest; +} + +/* String Allocate and Concatenate +*/ +PUBLIC char * HTSACat ARGS2( + char **, dest, + CONST char *, src) +{ + if (src && *src) { + if (*dest) { + int length = strlen(*dest); + *dest = (char *)realloc(*dest, length + strlen(src) + 1); + if (*dest == NULL) + outofmem(__FILE__, "HTSACat"); + strcpy (*dest + length, src); + } else { + *dest = (char *)malloc(strlen(src) + 1); + if (*dest == NULL) + outofmem(__FILE__, "HTSACat"); + strcpy (*dest, src); + } + } + return *dest; +} + + +/* Find next Field +** --------------- +** +** On entry, +** *pstr points to a string containig white space separated +** field, optionlly quoted. +** +** On exit, +** *pstr has been moved to the first delimiter past the +** field +** THE STRING HAS BEEN MUTILATED by a 0 terminator +** +** returns a pointer to the first field +*/ +PUBLIC char * HTNextField ARGS1( + char **, pstr) +{ + char * p = *pstr; + char * start; /* start of field */ + + while (*p && WHITE(*p)) + p++; /* Strip white space */ + if (!*p) { + *pstr = p; + return NULL; /* No first field */ + } + if (*p == '"') { /* quoted field */ + p++; + start = p; + for (; *p && *p!='"'; p++) { + if (*p == '\\' && p[1]) + p++; /* Skip escaped chars */ + } + } else { + start = p; + while (*p && !WHITE(*p)) + p++; /* Skip first field */ + } + if (*p) + *p++ = '\0'; + *pstr = p; + return start; +} diff --git a/WWW/Library/Implementation/HTString.h b/WWW/Library/Implementation/HTString.h new file mode 100644 index 00000000..7fad84bb --- /dev/null +++ b/WWW/Library/Implementation/HTString.h @@ -0,0 +1,51 @@ +/* String handling for libwww + STRINGS + + Case-independent string comparison and allocations with copies etc + + */ +#ifndef HTSTRING_H +#define HTSTRING_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ + +extern int WWW_TraceFlag; /* Global flag for all W3 trace */ + +extern CONST char * HTLibraryVersion; /* String for help screen etc */ + +/* + +Case-insensitive string comparison + + The usual routines (comp instead of cmp) had some problem. + + */ +extern int strcasecomp PARAMS((CONST char *a, CONST char *b)); +extern int strncasecomp PARAMS((CONST char *a, CONST char *b, int n)); + +/* + +Malloced string manipulation + + */ +#define StrAllocCopy(dest, src) HTSACopy (&(dest), src) +#define StrAllocCat(dest, src) HTSACat (&(dest), src) +extern char * HTSACopy PARAMS ((char **dest, CONST char *src)); +extern char * HTSACat PARAMS ((char **dest, CONST char *src)); + +/* + +Next word or quoted string + + */ +extern char * HTNextField PARAMS ((char** pstr)); + + +#endif +/* + + end + + */ diff --git a/WWW/Library/Implementation/HTStyle.c b/WWW/Library/Implementation/HTStyle.c new file mode 100644 index 00000000..5b31e252 --- /dev/null +++ b/WWW/Library/Implementation/HTStyle.c @@ -0,0 +1,363 @@ +/* Style Implementation for Hypertext HTStyle.c +** ================================== +** +** Styles allow the translation between a logical property +** of a piece of text and its physical representation. +** +** A StyleSheet is a collection of styles, defining the +** translation necessary to +** represent a document. It is a linked list of styles. +*/ +#include "HTUtils.h" +#include "HTStyle.h" + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +/* Create a new style +*/ +PUBLIC HTStyle* HTStyleNew NOARGS +{ + HTStyle * self = (HTStyle *)malloc(sizeof(*self)); + memset((void *)self, 0, sizeof(*self)); + self->font = (HTFont) 0; + self->color = 0; + return self; +} + +/* Create a new style with a name +*/ +PUBLIC HTStyle* HTStyleNewNamed ARGS1 (CONST char *,name) +{ + HTStyle * self = HTStyleNew(); + StrAllocCopy(self->name, name); + return self; +} + + +/* Free a style +*/ +PUBLIC HTStyle * HTStyleFree ARGS1 (HTStyle *,self) +{ + FREE(self->name); + FREE(self->SGMLTag); + FREE(self); + return NULL; +} + + +#ifdef SUPPRESS /* Only on the NeXT */ +/* Read a style from a stream (without its name) +** -------------------------- +** +** Reads a style with paragraph information from a stream. +** The style name is not read or written by these routines. +*/ +#define NONE_STRING "(None)" +#define HTStream NXStream + +HTStyle * HTStyleRead (HTStyle * style, HTStream * stream) +{ + char myTag[STYLE_NAME_LENGTH]; + char fontName[STYLE_NAME_LENGTH]; + NXTextStyle *p; + int tab; + int gotpara; /* flag: have we got a paragraph definition? */ + + NXScanf(stream, "%s%s%f%d", + myTag, + fontName, + &style->fontSize, + &gotpara); + if (gotpara) { + if (!style->paragraph) { + style->paragraph = malloc(sizeof(*(style->paragraph))); + style->paragraph->tabs = 0; + } + p = style->paragraph; + NXScanf(stream, "%f%f%f%f%hd%f%f%hd", + &p->indent1st, + &p->indent2nd, + &p->lineHt, + &p->descentLine, + &p->alignment, + &style->spaceBefore, + &style->spaceAfter, + &p->numTabs); + FREE(p->tabs); + p->tabs = malloc(p->numTabs * sizeof(p->tabs[0])); + for (tab=0; tab < p->numTabs; tab++) { + NXScanf(stream, "%hd%f", + &p->tabs[tab].kind, + &p->tabs[tab].x); + } + } else { /* No paragraph */ + FREE(style->paragraph); + } /* if no paragraph */ + StrAllocCopy(style->SGMLTag, myTag); + if (strcmp(fontName, NONE_STRING)==0) + style->font = 0; + else + style->font = [Font newFont:fontName size:style->fontSize]; + return NULL; +} + + +/* Write a style to a stream in a compatible way +*/ +HTStyle * HTStyleWrite (HTStyle * style, NXStream * stream) +{ + int tab; + NXTextStyle *p = style->paragraph; + NXPrintf(stream, "%s %s %f %d\n", + style->SGMLTag, + style->font ? [style->font name] : NONE_STRING, + style->fontSize, + p!=0); + + if (p) { + NXPrintf(stream, "\t%f %f %f %f %d %f %f\t%d\n", + p->indent1st, + p->indent2nd, + p->lineHt, + p->descentLine, + p->alignment, + style->spaceBefore, + style->spaceAfter, + p->numTabs); + + for (tab=0; tab < p->numTabs; tab++) + NXPrintf(stream, "\t%d %f\n", + p->tabs[tab].kind, + p->tabs[tab].x); + } + return style; +} + + +/* Write a style to stdout for diagnostics +*/ +HTStyle * HTStyleDump (HTStyle * style) +{ + int tab; + NXTextStyle *p = style->paragraph; + printf("Style %d `%s' SGML:%s. Font %s %.1f point.\n", + style, + style->name, + style->SGMLTag, + [style->font name], + style->fontSize); + if (p) { + printf( + "\tIndents: first=%.0f others=%.0f, Height=%.1f Desc=%.1f\n" + "\tAlign=%d, %d tabs. (%.0f before, %.0f after)\n", + p->indent1st, + p->indent2nd, + p->lineHt, + p->descentLine, + p->alignment, + p->numTabs, + style->spaceBefore, + style->spaceAfter); + + for (tab=0; tab < p->numTabs; tab++) { + printf("\t\tTab kind=%d at %.0f\n", + p->tabs[tab].kind, + p->tabs[tab].x); + } + printf("\n"); + } /* if paragraph */ + return style; +} +#endif /* SUPPRESS */ + + +/* StyleSheet Functions +** ==================== +*/ + +/* Searching for styles: +*/ +HTStyle * HTStyleNamed ARGS2 (HTStyleSheet *,self, CONST char *,name) +{ + HTStyle * scan; + for (scan=self->styles; scan; scan=scan->next) + if (0==strcmp(scan->name, name)) return scan; + if (TRACE) fprintf(stderr, "StyleSheet: No style named `%s'\n", name); + return NULL; +} + +#ifdef NEXT_SUPRESS /* Not in general common code */ + +HTStyle * HTStyleMatching (HTStyleSheet * self, HTStyle *style) +{ + HTStyle * scan; + for (scan=self->styles; scan; scan=scan->next) + if (scan->paragraph == para) return scan; + return NULL; +} + +/* Find the style which best fits a given run +** ------------------------------------------ +** +** This heuristic is used for guessing the style for a run of +** text which has been pasted in. In order, we try: +** +** A style whose paragraph structure is actually used by the run. +** A style matching in font +** A style matching in paragraph style exactly +** A style matching in paragraph to a degree +*/ + +HTStyle * HTStyleForRun (HTStyleSheet *self, NXRun *run) +{ + HTStyle * scan; + HTStyle * best = 0; + int bestMatch = 0; + NXTextStyle * rp = run->paraStyle; + for (scan=self->styles; scan; scan=scan->next) + if (scan->paragraph == run->paraStyle) return scan; /* Exact */ + + for (scan=self->styles; scan; scan=scan->next){ + NXTextStyle * sp = scan->paragraph; + if (sp) { + int match = 0; + if (sp->indent1st == rp->indent1st) match = match+1; + if (sp->indent2nd == rp->indent2nd) match = match+2; + if (sp->lineHt == rp->lineHt) match = match+1; + if (sp->numTabs == rp->numTabs) match = match+1; + if (sp->alignment == rp->alignment) match = match+3; + if (scan->font == run->font) match = match+10; + if (match>bestMatch) { + best=scan; + bestMatch=match; + } + } + } + if (TRACE) fprintf(stderr, "HTStyleForRun: Best match for style is %d out of 18\n", + bestMatch); + return best; +} +#endif /* NEXT_SUPRESS */ + + +/* Add a style to a sheet +** ---------------------- +*/ +HTStyleSheet * HTStyleSheetAddStyle ARGS2 + (HTStyleSheet *,self, HTStyle *,style) +{ + style->next = 0; /* The style will go on the end */ + if (!self->styles) { + self->styles = style; + } else { + HTStyle * scan; + for(scan=self->styles; scan->next; scan=scan->next); /* Find end */ + scan->next=style; + } + return self; +} + + +/* Remove the given object from a style sheet if it exists +*/ +HTStyleSheet * HTStyleSheetRemoveStyle ARGS2 + (HTStyleSheet *,self, HTStyle *,style) +{ + if (self->styles = style) { + self->styles = style->next; + return self; + } else { + HTStyle * scan; + for(scan = self->styles; scan; scan = scan->next) { + if (scan->next = style) { + scan->next = style->next; + return self; + } + } + } + return NULL; +} + +/* Create new style sheet +*/ + +HTStyleSheet * HTStyleSheetNew NOARGS +{ + HTStyleSheet * self = (HTStyleSheet *)malloc(sizeof(*self)); + + memset((void*)self, 0, sizeof(*self)); /* ANSI */ +/* Harbison c ref man says (char*)self + but k&r ansii and abc books and Think_C say (void*) */ + +/* bzero(self, sizeof(*self)); */ /* BSD */ + return self; +} + + +/* Free off a style sheet pointer +*/ +HTStyleSheet * HTStyleSheetFree ARGS1 (HTStyleSheet *,self) +{ + HTStyle * style; + while((style=self->styles)!=0) { + self->styles = style->next; + HTStyleFree(style); + } + FREE(self); + return NULL; +} + + +/* Read a stylesheet from a typed stream +** ------------------------------------- +** +** Reads a style sheet from a stream. If new styles have the same names +** as existing styles, they replace the old ones without changing the ids. +*/ + +#ifdef NEXT_SUPRESS /* Only on the NeXT */ +HTStyleSheet * HTStyleSheetRead(HTStyleSheet * self, NXStream * stream) +{ + int numStyles; + int i; + HTStyle * style; + char styleName[80]; + NXScanf(stream, " %d ", &numStyles); + if (TRACE) fprintf(stderr, "Stylesheet: Reading %d styles\n", numStyles); + for (i=0; i<numStyles; i++) { + NXScanf(stream, "%s", styleName); + style = HTStyleNamed(self, styleName); + if (!style) { + style = HTStyleNewNamed(styleName); + (void) HTStyleSheetAddStyle(self, style); + } + (void) HTStyleRead(style, stream); + if (TRACE) HTStyleDump(style); + } + return self; +} + +/* Write a stylesheet to a typed stream +** ------------------------------------ +** +** Writes a style sheet to a stream. +*/ + +HTStyleSheet * HTStyleSheetWrite(HTStyleSheet * self, NXStream * stream) +{ + int numStyles = 0; + HTStyle * style; + + for(style=self->styles; style; style=style->next) numStyles++; + NXPrintf(stream, "%d\n", numStyles); + + if (TRACE) fprintf(stderr, "StyleSheet: Writing %d styles\n", numStyles); + for (style=self->styles; style; style=style->next) { + NXPrintf(stream, "%s ", style->name); + (void) HTStyleWrite(style, stream); + } + return self; +} +#endif /* NEXT_SUPRESS */ diff --git a/WWW/Library/Implementation/HTStyle.h b/WWW/Library/Implementation/HTStyle.h new file mode 100644 index 00000000..100162d2 --- /dev/null +++ b/WWW/Library/Implementation/HTStyle.h @@ -0,0 +1,183 @@ +/* HTStyle: Style management for libwww + STYLE DEFINITION FOR HYPERTEXT + + Styles allow the translation between a logical property of a piece of text and its + physical representation. + + A StyleSheet is a collection of styles, defining the translation necessary to represent + a document. It is a linked list of styles. + +Overriding this module + + Why is the style structure declared in the HTStyle.h module, instead of having the user + browser define the structure, and the HTStyle routines just use sizeof() for copying? + + It's not obvious whether HTStyle.c should be common code. It's useful to have common + code for loading style sheets, especially if the movement toward standard style sheets + gets going. + + If it IS common code, then both the hypertext object and HTStyle.c must know the + structure of a style, so HTStyle.h is a suitable place to put that. HTStyle.c has to + be compiled with a knowledge of the + + It we take it out of the library, then of course HTStyle could be declared as an + undefined structure. The only references to it are in the structure-flattening code + HTML.c and HTPlain.c, which only use HTStypeNamed(). + + You can in any case override this function in your own code, which will prevent the + HTStyle from being loaded. You will be able to redefine your style structure in this + case without problems, as no other moule needs to know it. + + */ +#ifndef HTStyle_H +#define HTStyle_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTAnchor.h" + +typedef long int HTFont; /* Dummy definition instead */ + +#ifdef SHORT_NAMES +#define HTStyleNew HTStNew +#define HTStyleFree HTStFree +#define HTStyleRead HTStRead +#define HTStyleWrite HTStWrite +#define HTStyleSheetNew HTStShNe +#define HTStyleSheetFree HTStShFr +#define HTStyleNamed HTStName +#define HTStyleForParagraph HTStFoPa +#define HTStyleMatching HTStMatc +#define HTStyleForRun HTStFoRu +#define HTStyleSheetAddStyle HTStShAd +#define HTStyleSheetRemoveStyle HTStShRm +#define HTStyleSheetRead HTStShRe +#define HTStyleSheetWrite HTStShWr +#endif + +#ifdef NeXT_suppressed +#include <appkit/appkit.h> +typedef NXCoord HTCoord; +#define HTParagraphStyle NXTextStyle +#define HTCoord NXCoord +typedef struct _color { + float grey; + int RGBColor; +} HTColor; +#else + +typedef float HTCoord; + +typedef struct _HTParagraphStyle { + HTCoord left_indent; /* @@@@ junk! etc etc*/ +} HTParagraphStyle; + +typedef int HTColor; /* Sorry about the US spelling! */ + +#endif + + + +#define STYLE_NAME_LENGTH 80 /* @@@@@@@@@@@ */ + +typedef struct { + short kind; /* only NX_LEFTTAB implemented*/ + HTCoord position; /* x coordinate for stop */ +} HTTabStop; + + +/* The Style Structure +** ------------------- +*/ + +typedef struct _HTStyle { + +/* Style management information +*/ + struct _HTStyle *next; /* Link for putting into stylesheet */ + char * name; /* Style name */ + char * SGMLTag; /* Tag name to start */ + + +/* Character attributes (a la NXRun) +*/ + HTFont font; /* Font id */ + HTCoord fontSize; /* The size of font, not independent */ + HTColor color; /* text gray of current run */ + int superscript; /* superscript (-sub) in points */ + + HTAnchor *anchor; /* Anchor id if any, else zero */ + +/* Paragraph Attribtes (a la NXTextStyle) +*/ + HTCoord indent1st; /* how far first line in paragraph is + * indented */ + HTCoord leftIndent; /* how far second line is indented */ + HTCoord rightIndent; /* (Missing from NeXT version */ + short alignment; /* quad justification */ + HTCoord lineHt; /* line height */ + HTCoord descentLine; /* descender bottom from baseline */ + HTTabStop *tabs; /* array of tab stops, 0 terminated */ + + BOOL wordWrap; /* Yes means wrap at space not char */ + BOOL freeFormat; /* Yes means \n is just white space */ + HTCoord spaceBefore; /* Omissions from NXTextStyle */ + HTCoord spaceAfter; + int paraFlags; /* Paragraph flags, bits as follows: */ + +#define PARA_KEEP 1 /* Do not break page within this paragraph */ +#define PARA_WITH_NEXT 2 /* Do not break page after this paragraph */ + +#define HT_JUSTIFY 0 /* For alignment */ +#define HT_LEFT 1 +#define HT_RIGHT 2 +#define HT_CENTER 3 + +} HTStyle; + + +/* Style functions: +*/ +extern HTStyle * HTStyleNew NOPARAMS; +extern HTStyle* HTStyleNewNamed PARAMS ((CONST char * name)); +extern HTStyle * HTStyleFree PARAMS((HTStyle * self)); +#ifdef SUPRESS +extern HTStyle * HTStyleRead PARAMS((HTStyle * self, HTStream * stream)); +extern HTStyle * HTStyleWrite PARAMS((HTStyle * self, HTStream * stream)); +#endif +/* Style Sheet +** ----------- +*/ +typedef struct _HTStyleSheet { + char * name; + HTStyle * styles; +} HTStyleSheet; + + +/* Stylesheet functions: +*/ +extern HTStyleSheet * HTStyleSheetNew NOPARAMS; +extern HTStyleSheet * HTStyleSheetFree PARAMS((HTStyleSheet * self)); +extern HTStyle * HTStyleNamed PARAMS((HTStyleSheet * self, CONST char * name)); +extern HTStyle * HTStyleForParagraph PARAMS((HTStyleSheet * self, + HTParagraphStyle * paraStyle)); +extern HTStyle * HTStyleMatching PARAMS((HTStyleSheet *self, HTStyle * style)); +/* extern HTStyle * HTStyleForRun PARAMS((HTStyleSheet *self, NXRun * run)); */ +extern HTStyleSheet * HTStyleSheetAddStyle PARAMS((HTStyleSheet * self, + HTStyle * style)); +extern HTStyleSheet * HTStyleSheetRemoveStyle PARAMS((HTStyleSheet * self, + HTStyle * style)); +#ifdef SUPPRESS +extern HTStyleSheet * HTStyleSheetRead PARAMS((HTStyleSheet * self, + HTStream * stream)); +extern HTStyleSheet * HTStyleSheetWrite PARAMS((HTStyleSheet * self, + HTStream * stream)); +#endif +#define CLEAR_POINTER ((void *)-1) /* Pointer value means "clear me" */ +#endif /* HTStyle_H */ + + +/* + + */ diff --git a/WWW/Library/Implementation/HTTCP.c b/WWW/Library/Implementation/HTTCP.c new file mode 100644 index 00000000..45d8a979 --- /dev/null +++ b/WWW/Library/Implementation/HTTCP.c @@ -0,0 +1,955 @@ +/* Generic Communication Code HTTCP.c +** ========================== +** +** This code is in common between client and server sides. +** +** 16 Jan 92 TBL Fix strtol() undefined on CMU Mach. +** 25 Jun 92 JFG Added DECNET option through TCP socket emulation. +** 13 Sep 93 MD Added correct return of vmserrorno for HTInetStatus. +** Added decoding of vms error message for MULTINET. +** 7-DEC-1993 Bjorn S. Nilsson, ALEPH, CERN, VMS UCX ioctl() changes +** (done of Mosaic) +** 19 Feb 94 Danny Mayer Added Bjorn Fixes to Lynx version +** 7 Mar 94 Danny Mayer Added Fix UCX version for full domain name +** 20 May 94 Andy Harper Added support for CMU TCP/IP transport +** 17 Nov 94 Andy Harper Added support for SOCKETSHR transport +** 16 Jul 95 S. Bjorndahl added kluge to deal with LIBCMU bug +*/ + + +#include "HTUtils.h" +#include "tcp.h" /* Defines SHORT_NAMES if necessary */ +#include "HTAccess.h" +#include "HTParse.h" +#include "HTAlert.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +extern int HTCheckForInterrupt NOPARAMS; + +#ifdef SVR4_BSDSELECT +PUBLIC int BSDselect PARAMS((int nfds, fd_set * readfds, fd_set * writefds, + fd_set * exceptfds, struct timeval * timeout)); +#ifdef select +#undef select +#endif /* select */ +#define select BSDselect +#ifdef SOCKS +#ifdef Rselect +#undef Rselect +#endif /* Rselect */ +#define Rselect BSDselect +#endif /* SOCKS */ +#endif /* SVR4_BSDSELECT */ + +#include "LYLeaks.h" + +#ifdef SHORT_NAMES +#define HTInetStatus HTInStat +#define HTInetString HTInStri +#define HTParseInet HTPaInet +#endif + +#ifndef FD_SETSIZE +#if defined(UCX) || defined(SOCKETSHR_TCP) || defined(CMU_TCP) +#define FD_SETSIZE 32 +#else +#define FD_SETSIZE 256 +#endif /* Limit # sockets to 32 for UCX, BSN - also SOCKETSHR and CMU, AH */ +#endif + +/* Module-Wide variables +*/ + +PRIVATE char *hostname = NULL; /* The name of this host */ + + +/* PUBLIC VARIABLES +*/ + +#ifdef SOCKS +extern BOOLEAN socks_flag; +PUBLIC unsigned long socks_bind_remoteAddr; /* for long Rbind */ +#endif /* SOCKS */ + +/* PUBLIC SockA HTHostAddress; */ /* The internet address of the host */ + /* Valid after call to HTHostName() */ + +/* Encode INET status (as in sys/errno.h) inet_status() +** ------------------ +** +** On entry, +** where gives a description of what caused the error +** global errno gives the error number in the unix way. +** +** On return, +** returns a negative status in the unix way. +*/ +#ifndef PCNFS + +#ifdef VMS +#include <perror.h> +#ifndef errno +extern int errno; +#endif /* !errno */ +#endif /* VMS */ + +#ifndef VM +#ifndef VMS +#ifndef NeXT +#ifndef THINK_C +#ifndef __NetBSD__ +#ifndef __FreeBSD__ +#ifndef BSDI +extern char *sys_errlist[]; /* see man perror on cernvax */ +extern int sys_nerr; +#endif /* BSDI */ +#endif /* !__FreeBSD__ */ +#endif /* !__NetBSD__ */ +#endif /* !THINK_C */ +#endif /* !NeXT */ +#endif /* !VMS */ +#endif /* !VM */ + +#endif /* !PCNFS */ + +#if defined(VMS) && defined(UCX) +/* + * A routine to mimick the ioctl function for UCX. + * Bjorn S. Nilsson, 25-Nov-1993. Based on an example in the UCX manual. + */ +#include <iodef.h> +#define IOC_OUT (int)0x40000000 +extern int vaxc$get_sdc(), sys$qiow(); + +PUBLIC int HTioctl ARGS3 + (int, d, + int, request, + int *, argp) +{ + int sdc, status; + unsigned short fun, iosb[4]; + char *p5, *p6; + struct comm + { + int command; + char *addr; + } ioctl_comm; + struct it2 + { + unsigned short len; + unsigned short opt; + struct comm *addr; + } ioctl_desc; + if ((sdc = vaxc$get_sdc (d)) == 0) + { + errno = EBADF; + return -1; + } + ioctl_desc.opt = UCX$C_IOCTL; + ioctl_desc.len = sizeof(struct comm); + ioctl_desc.addr = &ioctl_comm; + if (request & IOC_OUT) + { + fun = IO$_SENSEMODE; + p5 = 0; + p6 = (char *)&ioctl_desc; + } + else + { + fun = IO$_SETMODE; + p5 = (char *)&ioctl_desc; + p6 = 0; + } + ioctl_comm.command = request; + ioctl_comm.addr = (char *)argp; + status = sys$qiow (0, sdc, fun, iosb, 0, 0, + 0, 0, 0, 0, p5, p6); + if (!(status & 01)) + { + errno = status; + return -1; + } + if (!(iosb[0] & 01)) + { + errno = iosb[0]; + return -1; + } + return 0; +} +#endif /* VMS && UCX */ + + +/* Report Internet Error +** --------------------- +*/ +PUBLIC int HTInetStatus ARGS1( + char *, where) +{ +#ifdef VMS +#ifdef MULTINET + SOCKET_ERRNO = vmserrno; +#endif /* MULTINET */ +#endif /* VMS */ + + CTRACE(tfp, + "TCP: Error %d in `SOCKET_ERRNO' after call to %s() failed.\n\t%s\n", + SOCKET_ERRNO, where, /* third arg is transport/platform specific */ + +#ifdef VM + "(Error number not translated)"); /* What Is the VM equiv? */ +#define ER_NO_TRANS_DONE +#endif /* VM */ + +#ifdef VMS +#ifdef MULTINET + vms_errno_string()); +#else + ((SOCKET_ERRNO > 0 && SOCKET_ERRNO <= 65) ? + strerror(SOCKET_ERRNO) : "(Error number not translated)")); +#endif /* MULTINET */ +#define ER_NO_TRANS_DONE +#endif /* VMS */ + +#if defined(NeXT) || defined(THINK_C) + strerror(SOCKET_ERRNO)); +#define ER_NO_TRANS_DONE +#endif /* NeXT || THINK_C */ + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(BSDI) + strerror(SOCKET_ERRNO)); +#define ER_NO_TRANS_DONE +#endif /* __NetBSD__ || __FreeBSD__ || BSDI */ + +#ifndef ER_NO_TRANS_DONE + (SOCKET_ERRNO < sys_nerr ? + sys_errlist[SOCKET_ERRNO] : "Unknown error" )); +#endif /* !ER_NO_TRANS_DONE */ + +#ifdef VMS +#ifndef MULTINET + CTRACE(tfp, + " Unix error number (SOCKET_ERRNO) = %ld dec\n", SOCKET_ERRNO); + CTRACE(tfp, + " VMS error (vaxc$errno) = %lx hex\n", vaxc$errno); +#endif /* MULTINET */ +#endif /* VMS */ + +#ifdef VMS + /* uerrno and errno happen to be zero if vmserrno <> 0 */ +#ifdef MULTINET + return -vmserrno; +#else + return -vaxc$errno; +#endif /* MULTINET */ +#else + return -SOCKET_ERRNO; +#endif /* VMS */ +} + + +/* Parse a cardinal value parse_cardinal() +** ---------------------- +** +** On entry, +** *pp points to first character to be interpreted, terminated by +** non 0:9 character. +** *pstatus points to status already valid +** maxvalue gives the largest allowable value. +** +** On exit, +** *pp points to first unread character +** *pstatus points to status updated iff bad +*/ + +PUBLIC unsigned int HTCardinal ARGS3 + (int *, pstatus, + char **, pp, + unsigned int, max_value) +{ + int n; + if ( (**pp<'0') || (**pp>'9')) { /* Null string is error */ + *pstatus = -3; /* No number where one expeceted */ + return 0; + } + + n=0; + while ((**pp>='0') && (**pp<='9')) n = n*10 + *((*pp)++) - '0'; + + if (n>max_value) { + *pstatus = -4; /* Cardinal outside range */ + return 0; + } + + return n; +} + + +#ifndef DECNET /* Function only used below for a trace message */ + +/* Produce a string for an Internet address +** ---------------------------------------- +** +** On exit, +** returns a pointer to a static string which must be copied if +** it is to be kept. +*/ + +PUBLIC CONST char * HTInetString ARGS1(SockA*,sin) +{ + static char string[16]; + sprintf(string, "%d.%d.%d.%d", + (int)*((unsigned char *)(&sin->sin_addr)+0), + (int)*((unsigned char *)(&sin->sin_addr)+1), + (int)*((unsigned char *)(&sin->sin_addr)+2), + (int)*((unsigned char *)(&sin->sin_addr)+3)); + return string; +} +#endif /* Decnet */ + + +/* Parse a network node address and port +** ------------------------------------- +** +** On entry, +** str points to a string with a node name or number, +** with optional trailing colon and port number. +** sin points to the binary internet or decnet address field. +** +** On exit, +** *sin is filled in. If no port is specified in str, that +** field is left unchanged in *sin. +*/ +PUBLIC int HTParseInet ARGS2( + SockA *, sin, + CONST char *, str) +{ + char *port; + char *host = NULL; + struct hostent *phost; /* Pointer to host - See netdb.h */ + + if (!str) { + if (TRACE) + fprintf(stderr, + "HTParseInet: Can't parse `NULL'.\n"); + return -1; + } + StrAllocCopy(host, str); /* Make a copy we can mutilate */ + + +/* Parse port number if present +*/ + if ((port = strchr(host, ':')) != NULL) { + *port++ = 0; /* Chop off port */ + if (port[0] >= '0' && port[0] <= '9') { + +#ifdef unix + sin->sin_port = htons(atol(port)); +#else /* VMS */ +#ifdef DECNET + sin->sdn_objnum = (unsigned char) (strtol(port, (char**)0 , 10)); +#else + sin->sin_port = htons((unsigned short) strtol(port,(char**)0,10)); +#endif /* Decnet */ +#endif /* Unix vs. VMS */ + + } else { + +#ifdef SUPPRESS /* 1. crashes!?!. 2. Not recommended */ + struct servent * serv = getservbyname(port, (char*)0); + if (serv) sin->sin_port = serv->s_port; + else if (TRACE) fprintf(stderr, "TCP: Unknown service %s\n", port); +#endif + } + } + +#ifdef DECNET + /* read Decnet node name. @@ Should know about DECnet addresses, but it's + probably worth waiting until the Phase transition from IV to V. */ + + sin->sdn_nam.n_len = min(DN_MAXNAML, strlen(host)); /* <=6 in phase 4 */ + strncpy (sin->sdn_nam.n_name, host, sin->sdn_nam.n_len + 1); + + if (TRACE) + fprintf(stderr, + "DECnet: Parsed address as object number %d on host %.6s...\n", + sin->sdn_objnum, host); + +#else /* parse Internet host */ + +/* Parse host number if present. +*/ + if (*host >= '0' && *host <= '9') { /* Numeric node address: */ +#ifdef DGUX_OLD + sin->sin_addr.s_addr = inet_addr(host).s_addr; /* See arpa/inet.h */ +#else + sin->sin_addr.s_addr = inet_addr(host); /* See arpa/inet.h */ +#endif /* DGUX_OLD */ + FREE(host); + + } else { /* Alphanumeric node name: */ +#ifdef MVS /* Oustanding problem with crash in MVS gethostbyname */ + if(TRACE) + fprintf(stderr, "HTTCP: Calling gethostbyname(%s)\n", host); +#endif + phost=gethostbyname(host); /* See netdb.h */ +#ifdef MVS + if(TRACE) + fprintf(stderr, "HTTCP: gethostbyname() returned %d\n", phost); +#endif + if (!phost) { + if (TRACE) + fprintf(stderr, + "HTTPAccess: Can't find internet node name `%s'.\n",host); + FREE(host); + return -1; /* Fail? */ + } + FREE(host); +#if defined(VMS) && defined(CMU_TCP) + /* In LIBCMU, phost->h_length contains not the length of one address + * (four bytes) but the number of bytes in *h_addr, i.e. some multiple + * of four. Thus we need to hard code the value here, and remember to + * change it if/when IP addresses change in size. :-( LIBCMU is no + * longer supported, and CMU users are encouraged to obtain and use + * SOCKETSHR/NETLIB instead. + * S. Bjorndahl + */ + memcpy((void *)&sin->sin_addr, phost->h_addr, 4); +#else + memcpy((void *)&sin->sin_addr, phost->h_addr, phost->h_length); +#endif /* VMS && CMU_TCP */ + } + + if (TRACE) + fprintf(stderr, + "TCP: Parsed address as port %d, IP address %d.%d.%d.%d\n", + (int)ntohs(sin->sin_port), + (int)*((unsigned char *)(&sin->sin_addr)+0), + (int)*((unsigned char *)(&sin->sin_addr)+1), + (int)*((unsigned char *)(&sin->sin_addr)+2), + (int)*((unsigned char *)(&sin->sin_addr)+3)); + +#endif /* Internet vs. Decnet */ + + return 0; /* OK */ +} + + +/* Free our name for the host on which we are - FM +** ------------------------------------------- +** +*/ +PRIVATE void free_HTTCP_hostname NOARGS +{ + FREE(hostname); +} + +/* Derive the name of the host on which we are +** ------------------------------------------- +** +*/ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 /* Arbitrary limit */ +#endif /* MAXHOSTNAMELEN */ + +PRIVATE void get_host_details NOARGS +{ + char name[MAXHOSTNAMELEN+1]; /* The name of this host */ +#ifdef UCX + char *domain_name; /* The name of this host domain */ +#endif +#ifdef NEED_HOST_ADDRESS /* no -- needs name server! */ + struct hostent * phost; /* Pointer to host -- See netdb.h */ +#endif + int namelength = sizeof(name); + + if (hostname) + return; /* Already done */ + gethostname(name, namelength); /* Without domain */ + StrAllocCopy(hostname, name); + atexit(free_HTTCP_hostname); +#ifdef UCX + /* UCX doesn't give the complete domain name. get rest from UCX$BIND_DOM + ** Logical + */ + if(strchr(hostname,'.') == NULL) { /* Not full address */ + domain_name = getenv("UCX$BIND_DOMAIN"); + if(domain_name != NULL) { + StrAllocCat(hostname, "."); + StrAllocCat(hostname, domain_name); + } + } +#endif /* UCX */ + CTRACE(tfp, "TCP: Local host name is %s\n", hostname); + +#ifndef DECNET /* Decnet ain't got no damn name server 8#OO */ +#ifdef NEED_HOST_ADDRESS /* no -- needs name server! */ + phost=gethostbyname(name); /* See netdb.h */ + if (!phost) { + if (TRACE) fprintf(stderr, + "TCP: Can't find my own internet node address for `%s'!!\n", + name); + return; /* Fail! */ + } + StrAllocCopy(hostname, phost->h_name); + memcpy(&HTHostAddress, &phost->h_addr, phost->h_length); + if (TRACE) fprintf(stderr, " Name server says that I am `%s' = %s\n", + hostname, HTInetString(&HTHostAddress)); +#endif /* NEED_HOST_ADDRESS */ + +#endif /* !DECNET */ +} + +PUBLIC CONST char * HTHostName NOARGS +{ + get_host_details(); + return hostname; +} + +/* + * interruptable connect as implemented by Marc Andreesen and + * hacked in by Lou Montulli + */ + +PUBLIC int HTDoConnect ARGS4( + CONST char *, url, + char *, protocol, + int, default_port, + int *, s) +{ + struct sockaddr_in soc_address; + struct sockaddr_in *sin = &soc_address; + int status; + char *line = NULL; + + /* Set up defaults: */ + sin->sin_family = AF_INET; + sin->sin_port = htons(default_port); + + /* Get node name and optional port number: */ + { + char *p1 = HTParse(url, "", PARSE_HOST); + char *at_sign; + char *host = NULL; + int status; + + /* if there's an @ then use the stuff after it as a hostname */ + if((at_sign = strchr(p1,'@')) != NULL) + StrAllocCopy(host, at_sign+1); + else + StrAllocCopy(host, p1); + + line = (char *)malloc(strlen(host) + strlen(protocol) + 128); + if (line == NULL) + outofmem(__FILE__, "HTDoConnect"); + sprintf (line, "Looking up %s.", host); + _HTProgress (line); + + status = HTParseInet(sin, host); + if (status) + { + sprintf (line, "Unable to locate remote host %s.", host); + _HTProgress(line); + FREE(p1); + FREE(host); + FREE(line); + return HT_NO_DATA; + } + + sprintf (line, "Making %s connection to %s.", protocol, host); + _HTProgress (line); + FREE(p1); + FREE(host); + } + + /* Now, let's get a socket set up from the server for the data: */ + *s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (*s == -1) + { + HTAlert("socket failed."); + FREE(line); + return HT_NO_DATA; + } + + /* + * Make the socket non-blocking, so the connect can be canceled. + * This means that when we issue the connect we should NOT + * have to wait for the accept on the other end. + */ +#if !defined(NO_IOCTL) + { + int ret; + int val = 1; + + ret = IOCTL(*s, FIONBIO, &val); + if (ret == -1) + { + sprintf (line, "Could not make connection non-blocking."); + _HTProgress(line); + } + } +#endif /* not NO_IOCTL */ +#if defined(USE_FCNTL) + { + int ret; + + ret = fcntl(*s, F_SETFL, O_NONBLOCK); + if (ret == -1) + { + sprintf (line, "Could not make connection non-blocking."); + _HTProgress(line); + } + } +#endif /* USE_FCNTL */ + + /* + * Issue the connect. Since the server can't do an instantaneous accept + * and we are non-blocking, this will almost certainly return a negative + * status. + */ +#ifdef SOCKS + if (socks_flag) { + status = Rconnect(*s, (struct sockaddr*)&soc_address, + sizeof(soc_address)); + socks_bind_remoteAddr = soc_address.sin_addr.s_addr; /* for long Rbind */ + } + else +#endif /* SOCKS */ + status = connect(*s, (struct sockaddr*)&soc_address, sizeof(soc_address)); + + /* + * According to the Sun man page for connect: + * EINPROGRESS The socket is non-blocking and the con- + * nection cannot be completed immediately. + * It is possible to select(2) for comple- + * tion by selecting the socket for writ- + * ing. + * According to the Motorola SVR4 man page for connect: + * EAGAIN The socket is non-blocking and the con- + * nection cannot be completed immediately. + * It is possible to select for completion + * by selecting the socket for writing. + * However, this is only possible if the + * socket STREAMS module is the topmost + * module on the protocol stack with a + * write service procedure. This will be + * the normal case. + */ + if ((status < 0) && + (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EAGAIN)) + { + struct timeval timeout; + int ret; + int tries=0; + + ret = 0; + while (ret <= 0) + { + fd_set writefds; + + /* + * Protect against an infinite loop. + */ + if (tries++ >= 180000) { + HTAlert("Connection failed for 180,000 tries."); + FREE(line); + return HT_NO_DATA; + } + + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + FD_ZERO(&writefds); + FD_SET(*s, &writefds); +#ifdef SOCKS + if (socks_flag) + ret = Rselect(FD_SETSIZE, NULL, + (void *)&writefds, NULL, &timeout); + else +#endif /* SOCKS */ + ret = select(FD_SETSIZE, NULL, (void *)&writefds, NULL, &timeout); + /* + * Again according to the Sun and Motorola man pagse for connect: + * EALREADY The socket is non-blocking and a previ- + * ous connection attempt has not yet been + * completed. + * Thus if the SOCKET_ERRNO is NOT EALREADY we have a real error, and + * should break out here and return that error. + * Otherwise if it is EALREADY keep on trying to complete the + * connection. + */ + if ((ret < 0) && (SOCKET_ERRNO != EALREADY)) + { + status = ret; + break; + } + else if (ret > 0) + { + /* + * Extra check here for connection success, if we try to connect + * again, and get EISCONN, it means we have a successful + * connection. + */ +#ifdef SOCKS + if (socks_flag) + status = Rconnect(*s, (struct sockaddr*)&soc_address, + sizeof(soc_address)); + else +#endif /* SOCKS */ + status = connect(*s, (struct sockaddr*)&soc_address, + sizeof(soc_address)); +#ifndef UCX + if ((status < 0)&&(SOCKET_ERRNO == EISCONN)) +#else +/* + * A UCX feature: Instead of returning EISCONN UCX returns EADDRINUSE. + * Test for this status also. + */ + if ((status < 0)&&((SOCKET_ERRNO == EISCONN) || + (SOCKET_ERRNO == EADDRINUSE))) +#endif /* VMS, UCX, BSN */ + { + status = 0; + } + + if (status && (SOCKET_ERRNO == EALREADY)) /* new stuff LJM */ + ret=0; /* keep going */ + else + break; + } + /* + * The select says we aren't ready yet. Try to connect again to + * make sure. If we don't get EALREADY or EISCONN, something has + * gone wrong. Break out and report it. + * + * For some reason, SVR4 returns EAGAIN here instead of EALREADY, + * even though the man page says it should be EALREADY. + * + * For some reason, UCX pre 3 apparently returns errno = 18242 + * instead the EALREADY or EISCONN values. + */ + else + { +#ifdef SOCKS + if (socks_flag) + status = Rconnect(*s, (struct sockaddr*)&soc_address, + sizeof(soc_address)); + else +#endif /* SOCKS */ + status = connect(*s, (struct sockaddr*)&soc_address, + sizeof(soc_address)); + if ((status < 0) && + (SOCKET_ERRNO != EALREADY && SOCKET_ERRNO != EAGAIN) && +#ifdef UCX + (SOCKET_ERRNO != 18242) && +#endif /* UCX */ + (SOCKET_ERRNO != EISCONN)) + { + break; + } + } + if(HTCheckForInterrupt()) + { + if (TRACE) + fprintf (stderr, "*** INTERRUPTED in middle of connect.\n"); + status = HT_INTERRUPTED; + SOCKET_ERRNO = EINTR; + break; + } + } + } + + /* + * Make the socket blocking again on good connect + */ + if (status >= 0) + { +#if !defined(NO_IOCTL) + int ret; + int val = 0; + + ret = IOCTL(*s, FIONBIO, &val); + if (ret == -1) + { + sprintf (line, "Could not restore socket to blocking."); + _HTProgress(line); + } +#endif /* not NO_IOCTL */ +#if defined(USE_FCNTL) + { + int ret; + + ret = fcntl(*s, F_SETFL, 0); + if (ret == -1) + { + sprintf (line, "Could not restore socket to blocking."); + _HTProgress(line); + } + } +#endif /* USE_FCNTL */ + } + /* + * Else the connect attempt failed or was interrupted. + * so close up the socket. + */ + else + { + NETCLOSE(*s); + } + + FREE(line); + return status; +} + +/* This is so interruptible reads can be implemented cleanly. */ +PUBLIC int HTDoRead ARGS3( + int, fildes, + void *, buf, + unsigned, nbyte) +{ + int ready, ret; + fd_set readfds; + struct timeval timeout; + int tries=0; +#ifdef UCX + int nb; +#endif /* UCX, BSN */ + + if (fildes <= 0) + return -1; + + if (HTCheckForInterrupt()) + { + SOCKET_ERRNO = EINTR; + return (HT_INTERRUPTED); + } + +#if !defined(NO_IOCTL) + ready = 0; +#else + ready = 1; +#endif /* bypass for NO_IOCTL */ + while (!ready) + { + /* + * Protect against an infinite loop. + */ + if (tries++ >= 180000) { + HTAlert("Socket read failed for 180,000 tries."); + SOCKET_ERRNO = EINTR; + return HT_INTERRUPTED; + } + + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + FD_ZERO(&readfds); + FD_SET(fildes, &readfds); +#ifdef SOCKS + if (socks_flag) + ret = Rselect(FD_SETSIZE, (void *)&readfds, NULL, NULL, &timeout); + else +#endif /* SOCKS */ + ret = select(FD_SETSIZE, (void *)&readfds, NULL, NULL, &timeout); + if (ret < 0) + { + return -1; + } + else if (ret > 0) + { + ready = 1; + } + else if(HTCheckForInterrupt()) + { + SOCKET_ERRNO = EINTR; + return HT_INTERRUPTED; + } + } + +#if !defined(UCX) || !defined(VAXC) + return SOCKET_READ (fildes, buf, nbyte); +#else /* VAXC and UCX problem only */ + errno = vaxc$errno = 0; + nb = SOCKET_READ (fildes, buf, nbyte); + CTRACE(tfp, "Read - nb,errno,vaxc$errno: %d %d %d\n", nb,errno,vaxc$errno); + if ((nb <= 0) && TRACE) + perror ("HTTCP.C:HTDoRead:read"); /* RJF */ + /* + * An errno value of EPIPE and nb < 0 indicates end-of-file on VAXC + */ + if ((nb <= 0) && (errno == EPIPE)) { + nb = 0; + errno = 0; + } + return nb; +#endif /* UCX, BSN */ +} + +#ifdef SVR4_BSDSELECT +/* + * This is a fix for the difference between BSD's select() and + * SVR4's select(). SVR4's select() can never return a value larger + * than the total number of file descriptors being checked. So, if + * you select for read and write on one file descriptor, and both + * are true, SVR4 select() will only return 1. BSD select in the + * same situation will return 2. + * + * Additionally, BSD select() on timing out, will zero the masks, + * while SVR4 does not. This is fixed here as well. + * + * Set your tabstops to 4 characters to have this code nicely formatted. + * + * Jerry Whelan, guru@bradley.edu, June 12th, 1993 + */ +#ifdef select +#undef select +#endif /* select */ + +#ifdef SOCKS +#ifdef Rselect +#undef Rselect +#endif /* Rselect */ +#endif /* SOCKS */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/select.h> + + +PUBLIC int BSDselect ARGS5 (int,nfds, fd_set *,readfds,fd_set *,writefds, + fd_set *,exceptfds, struct timeval *,timeout) +{ + int rval, + i; + +#ifdef SOCKS + if (socks_flag) + rval = Rselect(nfds, readfds, writefds, exceptfds, timeout); + else +#endif /* SOCKS */ + rval = select(nfds, readfds, writefds, exceptfds, timeout); + + switch(rval) { + case -1: return(rval); + break; + + case 0: if(readfds != NULL) + FD_ZERO(readfds); + if(writefds != NULL) + FD_ZERO(writefds); + if(exceptfds != NULL) + FD_ZERO(exceptfds); + + return(rval); + break; + + default: for(i=0, rval=0; i < nfds; i++) { + if((readfds != NULL) && FD_ISSET(i, readfds)) rval++; + if((writefds != NULL) && FD_ISSET(i, writefds)) rval++; + if((exceptfds != NULL) && FD_ISSET(i, exceptfds)) rval++; + + } + return(rval); + } +/* Should never get here */ +} +#endif /* SVR4_BSDSELECT */ diff --git a/WWW/Library/Implementation/HTTCP.h b/WWW/Library/Implementation/HTTCP.h new file mode 100644 index 00000000..1187c459 --- /dev/null +++ b/WWW/Library/Implementation/HTTCP.h @@ -0,0 +1,118 @@ +/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/src/HTTCP.html + GENERIC TCP/IP COMMUNICATION + + This module has the common code for handling TCP/IP connections etc. + + */ +#ifndef HTTCP_H +#define HTTCP_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "tcp.h" + +#ifdef SHORT_NAMES +#define HTInetStatus HTInStat +#define HTInetString HTInStri +#define HTParseInet HTPaInet +#endif + + +/* Produce a string for an internet address +** --------------------------------------- +** +** On exit: +** returns a pointer to a static string which must be copied if +** it is to be kept. +*/ +#ifdef __STDC__ + extern const char * HTInetString(struct sockaddr_in* sin); +#else + extern char * HTInetString(); +#endif + + +/* Encode INET status (as in sys/errno.h) inet_status() +** ------------------ +** +** On entry: +** where gives a description of what caused the error +** global errno gives the error number in the unix way. +** +** On return: +** returns a negative status in the unix way. +*/ +#ifdef __STDC__ + extern int HTInetStatus(char *where); +#else + extern int HTInetStatus(); +#endif + +/* Publicly accessible variables +*/ +/* extern struct sockaddr_in HTHostAddress; */ + /* The internet address of the host */ + /* Valid after call to HTHostName() */ + + +/* Parse a cardinal value parse_cardinal() +** ---------------------- +** +** On entry: +** *pp points to first character to be interpreted, terminated by +** non 0..9 character. +** *pstatus points to status already valid, +** maxvalue gives the largest allowable value. +** +** On exit: +** *pp points to first unread character, +** *pstatus points to status updated iff bad +*/ + +extern unsigned int HTCardinal PARAMS((int *pstatus, + char **pp, + unsigned int max_value)); + + +/* Parse an internet node address and port +** --------------------------------------- +** +** On entry: +** str points to a string with a node name or number, +** with optional trailing colon and port number. +** sin points to the binary internet or decnet address field. +** +** On exit: +** *sin is filled in. If no port is specified in str, that +** field is left unchanged in *sin. +*/ +#ifdef __STDC__ + extern int HTParseInet(struct sockaddr_in * sin, CONST char * str); + /*!! had to change this to get it to compile. CTB */ +#else + extern int HTParseInet(); +#endif + +/* Get Name of This Machine +** ------------------------ +** +*/ + +extern CONST char * HTHostName NOPARAMS; + +extern int HTDoConnect PARAMS(( + CONST char * url, + char * protocol, + int default_port, + int * s)); + +extern int HTDoRead PARAMS(( + int fildes, + void * buf, + unsigned nbyte)); + +#endif /* HTTCP_H */ +/* + + End. */ diff --git a/WWW/Library/Implementation/HTTP.c b/WWW/Library/Implementation/HTTP.c new file mode 100644 index 00000000..10a484f6 --- /dev/null +++ b/WWW/Library/Implementation/HTTP.c @@ -0,0 +1,1309 @@ +/* HyperText Tranfer Protocol - Client implementation HTTP.c +** ========================== +** Modified: +** 27 Jan 1994 PDM Added Ari Luotonen's Fix for Reload when using proxy +** servers. +*/ + +#include "HTUtils.h" +#include "tcp.h" + +#include "HTTP.h" + +#define HTTP_VERSION "HTTP/1.0" + +#define HTTP_PORT 80 +#define HTTPS_PORT 443 +#define SNEWS_PORT 563 + +#define HTTP_NETREAD NETREAD +#define HTTP_NETWRITE NETWRITE + +#define INIT_LINE_SIZE 1024 /* Start with line buffer this big */ +#define LINE_EXTEND_THRESH 256 /* Minimum read size */ +#define VERSION_LENGTH 20 /* for returned protocol version */ + +#include "HTParse.h" +#include "HTTCP.h" +#include "HTFormat.h" +#include "HTFile.h" +#include <ctype.h> +#include "HTAlert.h" +#include "HTMIME.h" +#include "HTML.h" +#include "HTInit.h" +#include "HTAABrow.h" + +#include "LYLeaks.h" + +/* #define TRACE 1 */ + +struct _HTStream +{ + HTStreamClass * isa; +}; + +extern char * HTAppName; /* Application name: please supply */ +extern char * HTAppVersion; /* Application version: please supply */ +extern char * personal_mail_address; /* User's name/email address */ +extern char * LYUserAgent; /* Lynx User-Agent string */ +extern BOOL LYNoRefererHeader; /* Never send Referer header? */ +extern BOOL LYNoRefererForThis; /* No Referer header for this URL? */ +extern BOOL LYNoFromHeader; /* Never send From header? */ + +extern BOOL using_proxy; /* Are we using an HTTP gateway? */ +PUBLIC BOOL reloading = FALSE; /* Reloading => send no-cache pragma to proxy */ +PUBLIC char * redirecting_url = NULL; /* Location: value. */ +PUBLIC BOOL permanent_redirection = FALSE; /* Got 301 status? */ +PUBLIC BOOL redirect_post_content = FALSE; /* Don't convert to GET? */ + +extern char LYUserSpecifiedURL; /* Is the URL a goto? */ + +extern BOOL keep_mime_headers; /* Include mime headers and force source dump */ +extern BOOL no_url_redirection; /* Don't follow Location: URL for */ +extern char *http_error_file; /* Store HTTP status code in this file */ +extern BOOL traversal; /* TRUE if we are doing a traversal */ +extern BOOL dump_output_immediately; /* TRUE if no interactive user */ + +extern char * HTLoadedDocumentURL NOPARAMS; +extern int HTCheckForInterrupt NOPARAMS; + + +/* Load Document from HTTP Server HTLoadHTTP() +** ============================== +** +** Given a hypertext address, this routine loads a document. +** +** +** On entry, +** arg is the hypertext reference of the article to be loaded. +** +** On exit, +** returns >=0 If no error, a good socket number +** <0 Error. +** +** The socket must be closed by the caller after the document has been +** read. +** +*/ +PUBLIC int HTLoadHTTP ARGS4 ( + CONST char *, arg, + HTParentAnchor *, anAnchor, + HTFormat, format_out, + HTStream*, sink) +{ + int s; /* Socket number for returned data */ + char *url = (char *)arg; /* The URL which get_physical() returned */ + char *command=NULL; /* The whole command */ + char *eol; /* End of line if found */ + char *start_of_data; /* Start of body of reply */ + int status; /* tcp return */ + int bytes_already_read; + char crlf[3]; /* A CR LF equivalent string */ + HTStream *target; /* Unconverted data */ + HTFormat format_in; /* Format arriving in the message */ + int do_head = 0; /* Whether or not we should do a head */ + int do_post = 0; /* ARE WE posting ? */ + char *METHOD; + + BOOL had_header; /* Have we had at least one header? */ + char *line_buffer; + char *line_kept_clean; + BOOL extensions; /* Assume good HTTP server */ + int compressed; + char line[INIT_LINE_SIZE]; + char temp[80]; + BOOL first_Accept = TRUE; + + int length, doing_redirect, rv; + int already_retrying = 0; + int len = 0; + + if (anAnchor->isHEAD) + do_head = TRUE; + else if (anAnchor->post_data) + do_post = TRUE; + + if (!url) + { + status = -3; + _HTProgress ("Bad request."); + goto done; + } + if (!*url) + { + status = -2; + _HTProgress ("Bad request."); + goto done; + } + + sprintf(crlf, "%c%c", CR, LF); + + /* At this point, we're talking HTTP/1.0. */ + extensions = YES; + + try_again: + /* All initializations are moved down here from up above, + so we can start over here... */ + eol = 0; + bytes_already_read = 0; + had_header = NO; + length = 0; + doing_redirect = 0; + permanent_redirection = FALSE; + redirect_post_content = FALSE; + compressed = 0; + target = NULL; + line_buffer = NULL; + line_kept_clean = NULL; + + if (!strncmp(url, "https", 5)) + { + HTAlert("This client does not contain support for HTTPS URLs."); + status = HT_NO_DATA; + goto done; + } + status = HTDoConnect (arg, "HTTP", HTTP_PORT, &s); + if (status == HT_INTERRUPTED) + { + /* Interrupt cleanly. */ + if (TRACE) + fprintf (stderr, + "HTTP: Interrupted on connect; recovering cleanly.\n"); + _HTProgress ("Connection interrupted."); + /* status already == HT_INTERRUPTED */ + goto done; + } + if (status < 0) + { + if (TRACE) + fprintf(stderr, + "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", + url, SOCKET_ERRNO); + HTAlert("Unable to connect to remote host."); + status = HT_NO_DATA; + goto done; + } + + /* Ask that node for the document, + ** omitting the host name & anchor + */ + { + char * p1 = (HTParse(url, "", PARSE_PATH|PARSE_PUNCTUATION)); + + if (do_post) { + METHOD = "POST"; + StrAllocCopy(command, "POST "); + } else if (do_head) { + METHOD = "HEAD"; + StrAllocCopy(command, "HEAD "); + } else { + METHOD = "GET"; + StrAllocCopy(command, "GET "); + } + + /* if we are using a proxy gateway don't copy in the first slash + * of say: /gopher://a;lkdjfl;ajdf;lkj/;aldk/adflj + * so that just gopher://.... is sent. + */ + if (using_proxy) + StrAllocCat(command, p1+1); + else + StrAllocCat(command, p1); + FREE(p1); + } + if (extensions) + { + StrAllocCat(command, " "); + StrAllocCat(command, HTTP_VERSION); + } + + StrAllocCat(command, crlf); /* CR LF, as in rfc 977 */ + + if (extensions) + { + int n, i; + char * host = NULL; + extern char * language; /* Lynx's preferred language - FM */ + extern char * pref_charset; /* Lynx's preferred character set - MM */ + + if ((host = HTParse(anAnchor->address, "", PARSE_HOST)) != NULL) { + sprintf(line, "Host: %s%c%c", host, CR,LF); + StrAllocCat(command, line); + FREE(host); + } + + if (!HTPresentations) + HTFormatInit(); + n = HTList_count(HTPresentations); + + first_Accept = TRUE; + len = 0; + for (i = 0; i < n; i++) { + HTPresentation *pres = + (HTPresentation *)HTList_objectAt(HTPresentations, i); + if (pres->rep_out == WWW_PRESENT) { + if (pres->rep != WWW_SOURCE && + strcasecomp(HTAtom_name(pres->rep), "www/mime") && + strcasecomp(HTAtom_name(pres->rep), "www/compressed")) { + if (pres->quality < 1.0) { + if (pres->maxbytes > 0) { + sprintf(temp, ";q=%4.3f;mxb=%d", + pres->quality, pres->maxbytes); + } else { + sprintf(temp, ";q=%4.3f", pres->quality); + } + } else if (pres->maxbytes > 0) { + sprintf(temp, ";mxb=%d", pres->maxbytes); + } else { + temp[0] = '\0'; + } + sprintf(line, "%s%s%s", + (first_Accept ? + "Accept: " : ", "), + HTAtom_name(pres->rep), + temp); + len += strlen(line); + if (len > 1000 && !first_Accept) { + StrAllocCat(command, crlf); + sprintf(line, "Accept: %s%s", + HTAtom_name(pres->rep), + temp); + len = strlen(line); + } + StrAllocCat(command, line); + first_Accept = FALSE; + } + } + } + sprintf(line, "%s*/*;q=0.001%c%c", + (first_Accept ? + "Accept: " : ", "), CR, LF); + StrAllocCat(command, line); + first_Accept = FALSE; + len = 0; + + sprintf(line, "Accept-Encoding: %s, %s%c%c", + "gzip", "compress", CR, LF); + StrAllocCat(command, line); + + if (language && *language) { + sprintf(line, "Accept-Language: %s%c%c", language, CR, LF); + StrAllocCat(command, line); + } + + if (pref_charset && *pref_charset) { + StrAllocCat(command, "Accept-Charset: "); + strcpy(line, pref_charset); + if (line[strlen(line)-1] == ',') + line[strlen(line)-1] = '\0'; + for (i = 0; line[i]; i++) + line[i] = TOLOWER(line[i]); + if (strstr(line, "iso-8859-1") == NULL) + strcat(line, ", iso-8859-1;q=0.001"); + if (strstr(line, "us-ascii") == NULL) + strcat(line, ", us-ascii;q=0.001"); + StrAllocCat(command, line); + sprintf(line, "%c%c", CR, LF); + StrAllocCat(command, line); + } + + /* + * When reloading give no-cache pragma to proxy server to make + * it refresh its cache. -- Ari L. <luotonen@dxcern.cern.ch> + * + * Also send it as a Cache-Control header for HTTP/1.1. - FM + */ + if (reloading) { + sprintf(line, "Pragma: no-cache%c%c", CR, LF); + StrAllocCat(command, line); + sprintf(line, "Cache-Control: no-cache%c%c", CR, LF); + StrAllocCat(command, line); + } + reloading = FALSE; /* Now turn it off again if on */ + + if (LYUserAgent && *LYUserAgent) { + sprintf(line, "User-Agent: %s%c%c", LYUserAgent, CR, LF); + } else { + sprintf(line, "User-Agent: %s/%s libwww-FM/%s%c%c", + HTAppName ? HTAppName : "unknown", + HTAppVersion ? HTAppVersion : "0.0", + HTLibraryVersion, CR, LF); + } + StrAllocCat(command, line); + + if (personal_mail_address && !LYNoFromHeader) { + sprintf(line, "From: %s%c%c", personal_mail_address, CR,LF); + StrAllocCat(command, line); + } + + if (!(LYUserSpecifiedURL || + LYNoRefererHeader || LYNoRefererForThis) && + strcmp((char *)HTLoadedDocumentURL(), "")) { + StrAllocCat(command, "Referer: "); + StrAllocCat(command, (char *)HTLoadedDocumentURL()); + sprintf(line, "%c%c", CR, LF); + StrAllocCat(command, line); + } + + { + char *docname; + char *hostname; + char *colon; + int portnumber; + char *auth; + + docname = HTParse(url, "", PARSE_PATH); + hostname = HTParse(url, "", PARSE_HOST); + if (hostname && + NULL != (colon = strchr(hostname, ':'))) + { + *(colon++) = '\0'; /* Chop off port number */ + portnumber = atoi(colon); + } + else if (!strncmp(url, "https", 5)) + { + portnumber = HTTPS_PORT; + } + else + { + portnumber = HTTP_PORT; + } + + if ((auth=HTAA_composeAuth(hostname, portnumber, docname)) != NULL && + *auth != '\0') { + /* + * If auth is not NULL nor zero-length, it's + * an Authorization header to be included. - FM + */ + sprintf(line, "%s%c%c", auth, CR, LF); + StrAllocCat(command, line); + if (TRACE) + fprintf(stderr, "HTTP: Sending authorization: %s\n", auth); + } else if (auth && *auth == '\0') { + /* + * If auth is a zero-length string, the user either + * cancelled or goofed at the username and password + * prompt. - FM + */ + HTAlert("Can't proceed without a username and password."); + FREE(command); + FREE(hostname); + FREE(docname); + status = HT_NO_DATA; + goto done; + } else { + if (TRACE) + fprintf(stderr, "HTTP: Not sending authorization (yet)\n"); + } + FREE(hostname); + FREE(docname); + } + } + + if (do_post) + { + if (TRACE) + fprintf (stderr, "HTTP: Doing post, content-type '%s'\n", + anAnchor->post_content_type); + sprintf (line, "Content-type: %s%c%c", + anAnchor->post_content_type ? anAnchor->post_content_type + : "lose", CR, LF); + StrAllocCat(command, line); + { + int content_length; + if (!anAnchor->post_data) + content_length = 4; /* 4 == "lose" :-) */ + else + content_length = strlen (anAnchor->post_data); + sprintf (line, "Content-length: %d%c%c", + content_length, CR, LF); + StrAllocCat(command, line); + } + + StrAllocCat(command, crlf); /* Blank line means "end" */ + + StrAllocCat(command, anAnchor->post_data); + } + + StrAllocCat(command, crlf); /* Blank line means "end" */ + + if (TRACE) + fprintf (stderr, "Writing:\n%s----------------------------------\n", + command); + + _HTProgress ("Sending HTTP request."); + + status = HTTP_NETWRITE(s, command, (int)strlen(command)); + FREE(command); + if (status <= 0) + { + if (status == 0) + { + if (TRACE) + fprintf (stderr, "HTTP: Got status 0 in initial write\n"); + /* Do nothing. */ + } + else if + ((SOCKET_ERRNO == ENOTCONN || SOCKET_ERRNO == ECONNRESET || + SOCKET_ERRNO == EPIPE) && + !already_retrying && + /* Don't retry if we're posting. */ !do_post) + { + /* Arrrrgh, HTTP 0/1 compability problem, maybe. */ + if (TRACE) + fprintf (stderr, + "HTTP: BONZO ON WRITE Trying again with HTTP0 request.\n"); + _HTProgress ("Retrying as HTTP0 request."); + (void)NETCLOSE(s); + extensions = NO; + already_retrying = 1; + goto try_again; + } + else + { + if (TRACE) + fprintf (stderr, + "HTTP: Hit unexpected network WRITE error; aborting connection.\n"); + (void)NETCLOSE(s); + status = -1; + HTAlert("Unexpected network write error; connection aborted."); + goto done; + } + } + + if (TRACE) + fprintf (stderr, "HTTP: WRITE delivered OK\n"); + _HTProgress ("HTTP request sent; waiting for response."); + + /* Read the first line of the response + ** ----------------------------------- + */ + + { + /* Get numeric status etc */ + BOOL end_of_file = NO; + int buffer_length = INIT_LINE_SIZE; + + line_buffer = (char *) malloc(buffer_length * sizeof(char)); + + do + { /* Loop to read in the first line */ + /* Extend line buffer if necessary for those crazy WAIS URLs ;-) */ + if (buffer_length - length < LINE_EXTEND_THRESH) + { + buffer_length = buffer_length + buffer_length; + line_buffer = + (char *) realloc(line_buffer, buffer_length * sizeof(char)); + } + if (TRACE) + fprintf (stderr, "HTTP: Trying to read %d\n", + buffer_length - length - 1); + status = HTTP_NETREAD(s, line_buffer + length, + buffer_length - length - 1); + if (TRACE) + fprintf (stderr, "HTTP: Read %d\n", status); + if (status <= 0) + { + /* Retry if we get nothing back too; + bomb out if we get nothing twice. */ + if (status == HT_INTERRUPTED) + { + if (TRACE) + fprintf (stderr, "HTTP: Interrupted initial read.\n"); + _HTProgress ("Connection interrupted."); + status = HT_INTERRUPTED; + goto clean_up; + } + else if + (status < 0 && + (SOCKET_ERRNO == ENOTCONN || SOCKET_ERRNO == ECONNRESET || + SOCKET_ERRNO == EPIPE) && !already_retrying && !do_post) + { + /* Arrrrgh, HTTP 0/1 compability problem, maybe. */ + if (TRACE) + fprintf (stderr, + "HTTP: BONZO Trying again with HTTP0 request.\n"); + (void)NETCLOSE(s); + FREE(line_buffer); + FREE(line_kept_clean); + + extensions = NO; + already_retrying = 1; + _HTProgress ("Retrying as HTTP0 request."); + goto try_again; + } + else + { + if (TRACE) + fprintf (stderr, + "HTTP: Hit unexpected network read error; aborting connection; status %d.\n", + status); + HTAlert("Unexpected network read error; connection aborted."); + (void)NETCLOSE(s); + status = -1; + goto clean_up; + } + } + + bytes_already_read += status; + sprintf (line, "Read %d bytes of data.", bytes_already_read); + HTProgress (line); + +#ifdef UCX /* UCX returns -1 on EOF */ + if (status == 0 || status == -1) +#else + if (status == 0) +#endif + { + end_of_file = YES; + break; + } + line_buffer[length+status] = 0; + + if (line_buffer) + { + FREE(line_kept_clean); + line_kept_clean = (char *)malloc (buffer_length * sizeof (char)); + memcpy(line_kept_clean, line_buffer, buffer_length); + } + + eol = strchr(line_buffer + length, LF); + /* Do we *really* want to do this? */ + if (eol && eol != line_buffer && *(eol-1) == CR) + *(eol-1) = ' '; + + length = length + status; + + /* Do we really want to do *this*? */ + if (eol) + *eol = 0; /* Terminate the line */ + } + /* All we need is the first line of the response. If it's a HTTP/1.0 + response, then the first line will be absurdly short and therefore + we can safely gate the number of bytes read through this code + (as opposed to below) to ~1000. */ + /* Well, let's try 100. */ + while (!eol && !end_of_file && bytes_already_read < 100); + } /* Scope of loop variables */ + + + /* We now have a terminated unfolded line. Parse it. + ** ------------------------------------------------- + */ + if (TRACE) + fprintf(stderr, "HTTP: Rx: %s\n", line_buffer); + +/* Kludge to work with old buggy servers and the VMS Help gateway. +** They can't handle the third word, so we try again without it. +*/ + if (extensions && /* Old buggy server or Help gateway? */ + (0==strncmp(line_buffer,"<TITLE>Bad File Request</TITLE>",31) || + 0==strncmp(line_buffer,"Address should begin with",25) || + 0==strncmp(line_buffer,"<TITLE>Help ",12) || + 0==strcmp(line_buffer, + "Document address invalid or access not authorised"))) { + FREE(line_buffer); + FREE(line_kept_clean); + extensions = NO; + already_retrying = 1; + if (TRACE) + fprintf(stderr, "HTTP: close socket %d to retry with HTTP0\n", s); + (void)NETCLOSE(s); + /* print a progress message */ + _HTProgress ("Retrying as HTTP0 request."); + goto try_again; + } + + + { + int fields; + char server_version[VERSION_LENGTH+1]; + int server_status; + + server_version[0] = 0; + + fields = sscanf(line_buffer, "%20s %d", + server_version, + &server_status); + + if (TRACE) + fprintf (stderr, "HTTP: Scanned %d fields from line_buffer\n", fields); + + if (http_error_file) { /* Make the status code externally available */ + FILE *error_file; +#ifdef SERVER_STATUS_ONLY + error_file = fopen(http_error_file, "w"); + if (error_file) { /* Managed to open the file */ + fprintf(error_file, "error=%d\n", server_status); +#else + error_file = fopen(http_error_file, "a"); + if (error_file) { /* Managed to open the file */ + fprintf(error_file, " URL=%s (%s)\n", url, METHOD); + fprintf(error_file, "STATUS=%s\n", line_buffer); +#endif /* SERVER_STATUS_ONLY */ + fclose(error_file); + } + } + + /* + * Rule out a non-HTTP/1.n reply as best we can. + */ + if (fields < 2 || !server_version[0] || server_version[0] != 'H' || + server_version[1] != 'T' || server_version[2] != 'T' || + server_version[3] != 'P' || server_version[4] != '/' || + server_version[6] != '.') { + /* + * Ugh! An HTTP0 reply, + */ + HTAtom * encoding; + + if (TRACE) + fprintf (stderr, "--- Talking HTTP0.\n"); + + format_in = HTFileFormat(url, &encoding); + /* + * Treat all plain text as HTML. + * This sucks but its the only solution without + * without looking at content. + */ + if (!strncmp(HTAtom_name(format_in), "text/plain",10)) { + if (TRACE) + fprintf(stderr, + "HTTP: format_in being changed to text/HTML\n"); + format_in = WWW_HTML; + } + + start_of_data = line_kept_clean; + } else { + /* + * Set up to decode full HTTP/1.n response. - FM + */ + format_in = HTAtom_for("www/mime"); + if (TRACE) + fprintf (stderr, "--- Talking HTTP1.\n"); + + /* + * We set start_of_data to "" when !eol here because there + * will be a put_block done below; we do *not* use the value + * of start_of_data (as a pointer) in the computation of + * length (or anything else) when !eol. Otherwise, set the + * value of length to what we have beyond eol (i.e., beyond + * the status line). - FM + */ + start_of_data = eol ? eol + 1 : ""; + length = eol ? length - (start_of_data - line_buffer) : 0; + + /* + * Trim trailing spaces in line_buffer so that we can use + * it in messages which include the status line. - FM + */ + while (line_buffer[strlen(line_buffer)-1] == ' ') + line_buffer[strlen(line_buffer)-1] = '\0'; + + /* + * Take appropriate actions based on the status. - FM + */ + switch (server_status/100) { + case 1: + /* + * HTTP/1.1 Informational statuses. + * 100 Continue. + * 101 Switching Protocols. + * > 101 is unknown. + * We should never get these, and they have only + * the status line and possibly other headers, + * so we'll deal with them by showing the full + * header to the user as text/plain. - FM + */ + HTAlert("Got unexpected Informational Status."); + do_head = TRUE; + break; + + case 2: + /* + * Good: Got MIME object! (Successful) - FM + */ + switch (server_status) { + case 204: + /* + * No Content. + */ + HTAlert(line_buffer); + status = HT_NO_DATA; + goto done; + break; + + case 205: + /* + * Reset Content. The server has fulfilled the + * request but nothing is returned and we should + * reset any form content. We'll instruct the + * user to do that, and restore the current + * document. - FM + */ + HTAlert("Request fulfilled. Reset Content."); + status = HT_NO_DATA; + goto done; + break; + + case 206: + /* + * Partial Content. We didn't send a Range + * so something went wrong somewhere. Show + * the status message and restore the current + * document. - FM + */ + HTAlert(line_buffer); + (void)NETCLOSE(s); + status = HT_NO_DATA; + goto done; + break; + + default: + /* + * 200 OK. + * 201 Created. + * 202 Accepted. + * 203 Non-Authoritative Information. + * > 206 is unknown. + * All should return something to display. + */ + HTProgress(line_buffer); + } /* case 2 switch */ + break; + + case 3: + /* + * Various forms of Redirection. - FM + * 300 Multiple Choices. + * 301 Moved Permanently. + * 302 Moved Temporarily. + * 303 See Other. + * 304 Not Modified. + * 305 Use Proxy. + * > 305 is unknown. + */ + if (no_url_redirection || do_head || keep_mime_headers) { + /* + * If any of these flags are set, we do not redirect, + * but instead show what was returned to the user as + * text/plain. - FM + */ + HTProgress(line_buffer); + break; + } + + if (server_status == 300) { /* Multiple Choices */ + /* + * For client driven content negotiation. The server + * should be sending some way for the user-agent to + * make a selection, so we'll show the user whatever + * the server returns. There might be a Location: + * header with the server's preference present, but + * the choice should be up to the user, someday based + * on an Alternates: header, and a body always should + * be present with descriptions and links for the + * choices (i.e., we use the latter, for now). - FM + */ + HTAlert(line_buffer); + break; + } + + if (server_status == 304) { /* Not Modified */ + /* + * We didn't send an "If-Modified-Since" header, + * so this status is inappropriate. We'll deal + * with it by showing the full header to the user + * as text/plain. - FM + */ + HTAlert("Got unexpected 304 Not Modified status."); + do_head = TRUE; + break; + } + + if (server_status == 305) { /* Use Proxy */ + /* + * We don't want to compound proxying, so if we + * got this from a proxy, just show any message + * to the user. - FM + */ + if (using_proxy) { + HTAlert("Got redirection to a proxy from the proxy!"); + break; + } + } else if (server_status > 305) { + /* + * Show user the content, if any, for redirection + * statuses we don't know. - FM + */ + HTAlert(line_buffer); + break; + } + + /* + * We do not load the file, but read the headers for + * the "Location:", check out that redirecting_url + * and if it's acceptible (e.g., not a telnet URL + * when we have that disabled), initiate a new fetch. + * If that's another redirecting_url, we'll repeat the + * checks, and fetch initiations if acceptible, until + * we reach the actual URL, or the redirection limit + * set in HTAccess.c is exceeded. If the status was 301, + * indicating that the relocation is permanent, we set + * the permanent_redirection flag to make it permanent + * for the current anchor tree (i.e., will persist until + * the tree is freed or the client exits). If the + * redirection would include POST content, we seek + * confirmation from an interactive user, and otherwise + * refuse the redirection. We also don't allow permanent + * redirection if we keep POST content. If we don't find + * the Location header or it's value is zero-length, we + * display whatever the server returned, and the user + * should RELOAD that to try again, or make a selection + * from it if it contains links, or Left-Arrow to the + * previous document. - FM + */ + { + char *cp; + + if ((dump_output_immediately || traversal) && + do_post && server_status != 303) { + /* + * Don't redirect POST content without approval + * from an interactive user. - FM + */ + (void)NETCLOSE(s); + status = -1; + HTAlert( + "Redirection of POST content requires user approval."); + if (traversal) + HTProgress(line_buffer); + goto clean_up; + } + + /* + * Get the rest of the headers and data, if + * any, and then close the connection. - FM + */ + while ((status = HTTP_NETREAD(s, line_buffer, + INIT_LINE_SIZE)) > 0) { + line_buffer[status] = '\0'; + StrAllocCat(line_kept_clean, line_buffer); + } + (void)NETCLOSE(s); + if (status == HT_INTERRUPTED) { + /* + * Impatient user. - FM + */ + if (TRACE) + fprintf (stderr, "HTTP: Interrupted followup read.\n"); + _HTProgress ("Connection interrupted."); + status = HT_INTERRUPTED; + goto clean_up; + } + doing_redirect = 1; + if (server_status == 301) { /* Moved Permanently */ + HTProgress(line_buffer); + if (do_post) { + /* + * Don't make the redirection permanent + * if we have POST content. - FM + */ + if (TRACE) + fprintf(stderr, + "HTTP: Have POST content. Treating 301 (Permanent) as 302 (Temporary).\n"); + HTAlert( + "Have POST content. Treating Permanent Redirection as Temporary.\n"); + } else { + permanent_redirection = TRUE; + } + } + + /* + * Look for the "Location:" in the headers. - FM + */ + cp = line_kept_clean; + while (*cp) { + if (*cp != 'l' && *cp != 'L') { + cp++; + } else if (!strncasecomp(cp, "Location:", 9)) { + char *cp1 = NULL, *cp2 = NULL; + cp += 9; + /* + * Trim leading spaces. - FM + */ + while (*cp == ' ') + cp++; + /* + * Accept CRLF, LF, or CR as end of header. - FM + */ + if (((cp1 = strchr(cp, LF)) != NULL) || + (cp2 = strchr(cp, CR)) != NULL) { + if (*cp1) { + *cp1 = '\0'; + if ((cp2 = strchr(cp, CR)) != NULL) + *cp2 = '\0'; + } else { + *cp2 = '\0'; + } + if (*cp == '\0') { + /* + * The "Location:" value is zero-length, and + * thus is probably something in the body, so + * we'll show the user what was returned. - FM + */ + if (TRACE) + fprintf(stderr, + "HTTP: 'Location:' is zero-length!\n"); + if (cp1) + *cp1 = LF; + if (cp2) + *cp2 = CR; + doing_redirect = 0; + permanent_redirection = FALSE; + start_of_data = line_kept_clean; + length = strlen(start_of_data); + HTAlert( + "Got redirection with a bad Location header."); + HTProgress(line_buffer); + break; + } + + /* + * Load the new URL into redirecting_url, + * which will be checked in LYGetFile.c + * for restrictions before seeking the + * document at that Location. - FM + */ + StrAllocCopy(redirecting_url, cp); + HTProgress(line_buffer); + if (TRACE) + fprintf(stderr, + "HTTP: Picked up location '%s'\n", cp); + if (cp1) + *cp1 = LF; + if (cp2) + *cp2 = CR; + if (server_status == 305) { /* Use Proxy */ + /* + * Make sure the proxy field ends with + * a slash. - FM + */ + if (redirecting_url[strlen(redirecting_url)-1] + != '/') + StrAllocCat(redirecting_url, "/"); + /* + * Append our URL. - FM + */ + StrAllocCat(redirecting_url, anAnchor->address); + if (TRACE) + fprintf(stderr, + "HTTP: Proxy URL is '%s'\n", + redirecting_url); + } + if (!do_post || server_status == 303) { + /* + * We don't have POST content (nor support PUT + * or DELETE), or the status is "See Other" and + * we can convert to GET, so go back and check + * out the new URL. - FM + */ + status = HT_REDIRECTING; + goto clean_up; + } + /* + * Make sure the user wants to redirect + * the POST content. - FM + */ + if (!HTConfirm( + "Redirection for POST content. Proceed?")) { + doing_redirect = 0; + FREE(redirecting_url); + status = HT_NO_DATA; + goto clean_up; + } + /* + * Set the flag to retain the POST content + * and go back to check out the URL. - FM + */ + redirect_post_content = TRUE; + status = HT_REDIRECTING; + goto clean_up; + } + break; + } else { + /* + * Keep looking for the Location header. - FM + */ + cp++; + } + } + + /* + * If we get to here, we didn't find the Location + * header, so we'll show the user what we got, if + * anything. - FM + */ + if (TRACE) + fprintf (stderr, "HTTP: Failed to pick up location.\n"); + doing_redirect = 0; + permanent_redirection = FALSE; + start_of_data = line_kept_clean; + length = strlen(start_of_data); + HTAlert("Got redirection with no Location header."); + HTProgress(line_buffer); + break; + } + + case 4: + /* + * "I think I goofed!" (Client Error) - FM + */ + switch (server_status) { + case 401: /* Unauthorized */ + /* + * Authorization for orgin server required. + * If we can set up authorization based on + * the WWW-Authenticate header, and the user + * provides a username and password, try again. + * Otherwise, issue a statusline message and + * restore the current document. - FM + */ + if (HTAA_shouldRetryWithAuth(start_of_data, length, NULL, s)) + { + extern char *authentication_info[2]; + + (void)NETCLOSE(s); + if (dump_output_immediately && !authentication_info[0]) { + fprintf(stderr, + "HTTP: Access authorization required.\n"); + fprintf(stderr, + " Use the -auth=id:pw parameter.\n"); + status = HT_NO_DATA; + goto clean_up; + } + + if (TRACE) + fprintf(stderr, "%s %d %s\n", + "HTTP: close socket", s, + "to retry with Access Authorization"); + + _HTProgress ( + "Retrying with access authorization information."); + FREE(line_buffer); + FREE(line_kept_clean); + goto try_again; + break; + } + else + { + HTAlert( + "Can't retry with authorization! Contact the server's WebMaster."); + (void)NETCLOSE(s); + status = -1; + goto clean_up; + } + + case 407: + /* + * Proxy Authentication Required. We'll handle + * Proxy-Authenticate headers and retry with a + * Proxy-Authorization header, someday, but for + * now, apologized to the user and restore the + * current document. - FM + */ + HTAlert( + "Proxy Authentication Required. Sorry, not yet supported."); + (void)NETCLOSE(s); + status = HT_NO_DATA; + goto done; + break; + + case 408: + /* + * Request Timeout. Show the status message + * and restore the current document. - FM + */ + HTAlert(line_buffer); + (void)NETCLOSE(s); + status = HT_NO_DATA; + goto done; + break; + + default: + /* + * 400 Bad Request. + * 402 Payment Required. + * 403 Forbidden. + * 404 Not Found. + * 405 Method Not Allowed. + * 406 Not Acceptable. + * 409 Conflict. + * 410 Gone. + * 411 Length Required. + * 412 Precondition Failed. + * 413 Request Entity Too Large. + * 414 Request-URI Too Long. + * 415 Unsupported Media Type. + * 416 List Response (for content negotiation). + * > 416 is unknown. + * Show the status message, and display + * the returned text if we are not doing + * a traversal. - FM + */ + HTAlert(line_buffer); + if (traversal) { + (void)NETCLOSE(s); + status = -1; + goto clean_up; + } + break; + } /* case 4 switch */ + break; + + case 5: + /* + * "I think YOU goofed!" (server error) + * 500 Internal Server Error + * 501 Not Implemented + * 502 Bad Gateway + * 503 Service Unavailable + * 504 Gateway Timeout + * 505 HTTP Version Not Supported + * > 505 is unknown. + * Should always include a message, which + * we always should display. - FM + */ + HTAlert(line_buffer); + break; + + default: + /* + * Bad or unknown server_status number. + * Take a chance and hope there is + * something to display. - FM + */ + HTAlert("Unknown status reply from server!"); + HTAlert(line_buffer); + if (traversal) { + (void)NETCLOSE(s); + status = -1; + goto clean_up; + } + break; + } /* Switch on server_status/100 */ + + } /* Full HTTP reply */ + } /* scope of fields */ + + /* Set up the stream stack to handle the body of the message */ + if (do_head || keep_mime_headers) { + /* It was a HEAD request, or we want the headers and source */ + start_of_data = line_kept_clean; + length = strlen(start_of_data); + format_in = HTAtom_for("text/plain"); + } + + target = HTStreamStack(format_in, + format_out, + sink, anAnchor); + + if (!target || target == NULL) + { + char buffer[1024]; /* @@@@@@@@ */ + + (void)NETCLOSE(s); + sprintf(buffer, "Sorry, no known way of converting %s to %s.", + HTAtom_name(format_in), HTAtom_name(format_out)); + _HTProgress (buffer); + status = -1; + goto clean_up; + } + + /* Recycle the first chunk of data, in all cases. */ + (*target->isa->put_block)(target, start_of_data, length); + + /* Go pull the bulk of the data down. */ + rv = HTCopy(s, NULL, target); + + if (rv == -1) + { + /* Intentional interrupt before data were received, not an error */ + /* (*target->isa->_abort)(target, NULL); */ /* already done in HTCopy */ + status = HT_INTERRUPTED; + (void)NETCLOSE(s); + goto clean_up; + } + if (rv == -2 && !already_retrying && !do_post) + { + /* Aw hell, a REAL error, maybe cuz it's a dumb HTTP0 server */ + if (TRACE) + fprintf (stderr, "HTTP: Trying again with HTTP0 request.\n"); + /* May as well consider it an interrupt -- right? */ + (*target->isa->_abort)(target, NULL); + (void)NETCLOSE(s); + FREE(line_buffer); + FREE(line_kept_clean); + extensions = NO; + already_retrying = 1; + _HTProgress ("Retrying as HTTP0 request."); + goto try_again; + } + + /* + * Close socket if partial transmission (was freed on abort) + * Free if complete transmission (socket was closed before return) + */ + if (rv == HT_INTERRUPTED) + { + (void)NETCLOSE(s); + } + else + (*target->isa->_free)(target); + + if (doing_redirect) + /* + * We already jumped over all this if the "case 3:" code worked + * above, but we'll check here as a backup in case it fails. - FM + */ + { + /* Lou's old comment: - FM */ + /* OK, now we've got the redirection URL temporarily stored + in external variable redirecting_url, exported from HTMIME.c, + since there's no straightforward way to do this in the library + currently. Do the right thing. */ + status = HT_REDIRECTING; + } + else + { + /* If any data were received, treat as a complete transmission */ + status = HT_LOADED; + } + + /* Clean up + */ +clean_up: + FREE(line_buffer); + FREE(line_kept_clean); + +done: + /* Clear out on exit, just in case. */ + do_head = 0; + do_post = 0; + return status; +} + + +/* Protocol descriptor +*/ + +#ifdef GLOBALDEF_IS_MACRO +#define _HTTP_C_GLOBALDEF_1_INIT { "http", HTLoadHTTP, 0} +GLOBALDEF (HTProtocol,HTTP,_HTTP_C_GLOBALDEF_1_INIT); +#define _HTTP_C_GLOBALDEF_2_INIT { "https", HTLoadHTTP, 0} +GLOBALDEF (HTProtocol,HTTPS,_HTTP_C_GLOBALDEF_2_INIT); +#else +GLOBALDEF PUBLIC HTProtocol HTTP = { "http", HTLoadHTTP, 0 }; +GLOBALDEF PUBLIC HTProtocol HTTPS = { "https", HTLoadHTTP, 0 }; +#endif /* GLOBALDEF_IS_MACRO */ diff --git a/WWW/Library/Implementation/HTTP.h b/WWW/Library/Implementation/HTTP.h new file mode 100644 index 00000000..c97369fb --- /dev/null +++ b/WWW/Library/Implementation/HTTP.h @@ -0,0 +1,28 @@ +/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTTP.html + HYPERTEXT TRANFER PROTOCOL + + */ +#ifndef HTTP_H +#define HTTP_H + +#include "HTAccess.h" + +#ifdef GLOBALREF_IS_MACRO +extern GLOBALREF (HTProtocol,HTTP); +extern GLOBALREF (HTProtocol,HTTPS); +#else +GLOBALREF HTProtocol HTTP; +GLOBALREF HTProtocol HTTPS; +#endif /* GLOBALREF_IS_MACRO */ + +#define URL_GET_METHOD 1 +#define URL_POST_METHOD 2 +#define URL_MAIL_METHOD 3 + +#endif /* HTTP_H */ + +/* + + end of HTTP module definition + + */ diff --git a/WWW/Library/Implementation/HTTelnet.c b/WWW/Library/Implementation/HTTelnet.c new file mode 100644 index 00000000..a9bf8527 --- /dev/null +++ b/WWW/Library/Implementation/HTTelnet.c @@ -0,0 +1,579 @@ +/* Telnet Acees, Roligin, etc HTTelnet.c +** ========================== +** +** Authors +** TBL Tim Berners-Lee timbl@info.cern.ch +** JFG Jean-Francois Groff jgh@next.com +** DD Denis DeLaRoca (310) 825-4580 <CSP1DWD@mvs.oac.ucla.edu> +** History +** 8 Jun 92 Telnet hopping prohibited as telnet is not secure (TBL) +** 26 Jun 92 When over DECnet, suppressed FTP, Gopher and News. (JFG) +** 6 Oct 92 Moved HTClientHost and logfile into here. (TBL) +** 17 Dec 92 Tn3270 added, bug fix. (DD) +** 2 Feb 93 Split from HTAccess.c. Registration.(TBL) +*/ + +#include "HTUtils.h" +#include "tcp.h" + +/* Implements: +*/ +#include "HTTelnet.h" + +#include "HTParse.h" +#include "HTAnchor.h" +#include "HTTP.h" +#include "HTFile.h" +/*#include <errno.h> included by tcp.h -- FM */ +/*#include <stdio.h> included by HTUtils.h -- FM */ + +#include "HText.h" + +#include "HTAccess.h" +#include "HTAlert.h" +#ifndef VMS +#include "../../../userdefs.h" /* for TELNET_COMMAND and RLOGIN_COMMAND */ +#endif /* not VMS */ + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +#define HT_NO_DATA -9999 + + +/* Telnet or "rlogin" access +** ------------------------- +*/ +PRIVATE int remote_session ARGS2(char *, access, char *, host) +{ + char * user = host; + char * password = NULL; + char * cp; + char * hostname; + char * port; + char command[256]; + enum _login_protocol { telnet, rlogin, tn3270 } login_protocol = + strcmp(access, "rlogin") == 0 ? rlogin : + strcmp(access, "tn3270") == 0 ? tn3270 : telnet; +#ifdef VMS + extern int DCLsystem PARAMS((char *command)); +#define system(a) DCLsystem(a) /* use LYCurses.c routines for spawns */ +#endif /* VMS */ + + /* + * Modified to allow for odd chars in a username only if exists. + * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe + */ + /* prevent telnet://hostname;rm -rf * URL's (VERY BAD) + * *cp=0; / * terminate at any ;,<,>,`,|,",' or space or return + * or tab to prevent security whole + */ + for(cp = (strchr(host, '@') ? strchr(host, '@') : host); *cp != '\0'; + cp++) { + if(!isalnum(*cp) && *cp != '_' && *cp != '-' && + *cp != ':' && *cp != '.' && *cp != '@') { + *cp = '\0'; + break; + } + } + + hostname = strchr(host, '@'); + + if (hostname) { + *hostname++ = '\0'; /* Split */ + } else { + hostname = host; + user = NULL; /* No user specified */ + } + + port = strchr(hostname, ':'); + if (port) + *port++ = '\0'; /* Split */ + + if (!hostname || *hostname == '\0') { + if (TRACE) + fprintf(stderr, "HTTelnet: No host specified!\n"); + return HT_NO_DATA; + } + + if (user) { + password = strchr(user, ':'); + if (password) { + *password++ = '\0'; + } + } + +/* If the person is already telnetting etc, forbid hopping */ +/* This is a security precaution, for us and remote site */ + + if (HTSecure) { + +#ifdef TELNETHOPPER_MAIL + sprintf(command, + "finger @%s | mail -s \"**telnethopper %s\" tbl@dxcern.cern.ch", + HTClientHost, HTClientHost); + system(command); +#endif + printf("\n\nSorry, but the service you have selected is one\n"); + printf("to which you have to log in. If you were running www\n"); + printf("on your own computer, you would be automatically connected.\n"); + printf("For security reasons, this is not allowed when\n"); + printf("you log in to this information service remotely.\n\n"); + + printf("You can manually connect to this service using %s\n", + access); + printf("to host %s", hostname); + if (user) printf(", user name %s", user); + if (password) printf(", password %s", password); + if (port) printf(", port %s", port); + printf(".\n\n"); + return HT_NO_DATA; + } + +/* Not all telnet servers get it even if user name is specified +** so we always tell the guy what to log in as +*/ + if (user && login_protocol != rlogin) + printf("When you are connected, log in as: %s\n", user); + if (password && login_protocol != rlogin) + printf(" The password is: %s\n", password); + +/* + * NeXTSTEP is the implied version of the NeXT operating system. + * You may need to define this yourself. + */ +#if defined(NeXT) && defined(NeXTSTEP) && NeXTSTEP<=20100 + sprintf(command, "%s%s%s %s %s", TELNET_COMMAND, + user ? " -l " : "", + user ? user : "", + hostname, + port ? port : ""); + + if (TRACE) + fprintf(stderr, "HTTelnet: Command is: %s\n\n", command); + system(command); + return HT_NO_DATA; /* Ok - it was done but no data */ +#define TELNET_DONE +#endif + +/* Most unix machines suppport username only with rlogin */ +#if defined(unix) +#ifndef TELNET_DONE + if (login_protocol == rlogin) { + sprintf(command, "%s %s%s%s", RLOGIN_COMMAND, + hostname, + user ? " -l " : "", + user ? user : ""); + + } else if (login_protocol == tn3270) { + sprintf(command, "%s %s %s", TN3270_COMMAND, + hostname, + port ? port : ""); + + } else { /* TELNET */ + sprintf(command, "%s %s %s", TELNET_COMMAND, + hostname, + port ? port : ""); + } + + if (TRACE) + fprintf(stderr, "HTTelnet: Normal: Command is: %s\n\n", command); + system(command); + return HT_NO_DATA; /* Ok - it was done but no data */ +#define TELNET_DONE +#endif /* !TELNET_DONE */ +#endif /* unix */ + +/* VMS varieties */ +#if defined(MULTINET) + if (login_protocol == rlogin) { + sprintf(command, "RLOGIN%s%s%s%s%s %s", /*lm 930713 */ + user ? "/USERNAME=\"" : "", + user ? user : "", + user ? "\"" : "", + port ? "/PORT=" : "", + port ? port : "", + hostname); + + } else if (login_protocol == tn3270) { + sprintf(command, "TELNET/TN3270 %s%s %s", + port ? "/PORT=" : "", + port ? port : "", + hostname); + + } else { /* TELNET */ + sprintf(command, "TELNET %s%s %s", + port ? "/PORT=" : "", + port ? port : "", + hostname); + } + + if (TRACE) + fprintf(stderr, "HTTelnet: Command is: %s\n\n", command); + system(command); + return HT_NO_DATA; /* Ok - it was done but no data */ +#define TELNET_DONE +#endif /* MULTINET */ + +#if defined(WIN_TCP) + { + char *cp; + + if ((cp=getenv("WINTCP_COMMAND_STYLE")) != NULL && + 0==strncasecomp(cp, "VMS", 3)) { /* VMS command syntax */ + if (login_protocol == rlogin) { + sprintf(command, "RLOGIN%s%s%s%s%s %s", /*lm 930713 */ + user ? "/USERNAME=\"" : "", + user ? user : "", + user ? "\"" : "", + port ? "/PORT=" : "", + port ? port : "", + hostname); + + } else if (login_protocol == tn3270) { + sprintf(command, "TELNET/TN3270 %s%s %s", + port ? "/PORT=" : "", + port ? port : "", + hostname); + + } else { /* TELNET */ + sprintf(command, "TELNET %s%s %s", + port ? "/PORT=" : "", + port ? port : "", + hostname); + } + + } else { /* UNIX command syntax */ + if (login_protocol == rlogin) { + sprintf(command, "RLOGIN %s%s%s%s%s", + hostname, + user ? " -l " : "", + user ? "\"" : "", + user ? user : "", + user ? "\"" : ""); + + } else if (login_protocol == tn3270) { + sprintf(command, "TN3270 %s %s", + hostname, + port ? port : ""); + + } else { /* TELNET */ + sprintf(command, "TELNET %s %s", + hostname, + port ? port : ""); + } + } + + if (TRACE) + fprintf(stderr, "HTTelnet: Command is: %s\n\n", command); + system(command); + return HT_NO_DATA; /* Ok - it was done but no data */ + } +#define TELNET_DONE +#endif /* WIN_TCP */ + +#ifdef UCX + if (login_protocol == rlogin) { + sprintf(command, "RLOGIN%s%s%s %s %s", + user ? "/USERNAME=\"" : "", + user ? user : "", + user ? "\"" : "", + hostname, + port ? port : ""); + + } else if (login_protocol == tn3270) { + sprintf(command, "TN3270 %s %s", + hostname, + port ? port : ""); + + } else { /* TELNET */ + sprintf(command, "TELNET %s %s", + hostname, + port ? port : ""); + } + + if (TRACE) + fprintf(stderr, "HTTelnet: Command is: %s\n\n", command); + system(command); + return HT_NO_DATA; /* Ok - it was done but no data */ +#define TELNET_DONE +#endif /* UCX */ + +#ifdef CMU_TCP + if (login_protocol == telnet) { + sprintf(command, "TELNET %s%s %s", + port ? "/PORT=" : "", + port ? port : "", + hostname); + if (TRACE) + fprintf(stderr, "HTTelnet: Command is: %s\n\n", command); + system(command); + } + else { + extern int LYgetch NOPARAMS; + extern BOOLEAN HadVMSInterrupt; + + printf( + "\nSorry, this browser was compiled without the %s access option.\n", + access); + printf("\nPress <return> to return to Lynx."); + LYgetch(); + HadVMSInterrupt = FALSE; + } + return HT_NO_DATA; /* Ok - it was done but no data */ +#define TELNET_DONE +#endif /* CMU_TCP */ + +#ifdef SOCKETSHR_TCP + { + char *cp; + + if (getenv("MULTINET_SOCKET_LIBRARY") != NULL) { + if (login_protocol == rlogin) { + sprintf(command, "MULTINET RLOGIN%s%s%s%s %s", /*lm 930713 */ + user ? "/USERNAME=" : "", + user ? user : "", + port ? "/PORT=" : "", + port ? port : "", + hostname); + + } else if (login_protocol == tn3270) { + sprintf(command, "MULTINET TELNET/TN3270 %s%s %s", + port ? "/PORT=" : "", + port ? port : "", + hostname); + + } else { /* TELNET */ + sprintf(command, "MULTINET TELNET %s%s %s", + port ? "/PORT=" : "", + port ? port : "", + hostname); + } + + if (TRACE) + fprintf(stderr, "HTTelnet: Command is: %s\n\n", command); + system(command); + return HT_NO_DATA; /* Ok - it was done but no data */ + } + else if ((cp=getenv("WINTCP_COMMAND_STYLE")) != NULL) { + if (0==strncasecomp(cp, "VMS", 3)) { /* VMS command syntax */ + if (login_protocol == rlogin) { + sprintf(command, "RLOGIN%s%s%s%s %s", /*lm 930713 */ + user ? "/USERNAME=" : "", + user ? user : "", + port ? "/PORT=" : "", + port ? port : "", + hostname); + } else if (login_protocol == tn3270) { + sprintf(command, "TELNET/TN3270 %s%s %s", + port ? "/PORT=" : "", + port ? port : "", + hostname); + } else { /* TELNET */ + sprintf(command, "TELNET %s%s %s", + port ? "/PORT=" : "", + port ? port : "", + hostname); + } + } else { /* UNIX command syntax */ + if (login_protocol == rlogin) { + sprintf(command, "RLOGIN %s%s%s", + hostname, + user ? " -l " : "", + user ? user : ""); + } else if (login_protocol == tn3270) { + sprintf(command, "TN3270 %s %s", + hostname, + port ? port : ""); + } else { /* TELNET */ + sprintf(command, "TELNET %s %s", + hostname, + port ? port : ""); + } + } + + if (TRACE) + fprintf(stderr, "HTTelnet: Command is: %s\n\n", command); + system(command); + return HT_NO_DATA; /* Ok - it was done but no data */ + } + else if (getenv("UCX$DEVICE") != NULL) { + if (login_protocol == rlogin) { + sprintf(command, "RLOGIN%s%s %s %s", + user ? "/USERNAME=" : "", + user ? user : "", + hostname, + port ? port : ""); + + } else if (login_protocol == tn3270) { + sprintf(command, "TN3270 %s %s", + hostname, + port ? port : ""); + + } else { /* TELNET */ + sprintf(command, "TELNET %s %s", + hostname, + port ? port : ""); + } + + if (TRACE) + fprintf(stderr, "HTTelnet: Command is: %s\n\n", command); + system(command); + return HT_NO_DATA; /* Ok - it was done but no data */ + } + else if (getenv("CMUTEK_ROOT") != NULL) { + if (login_protocol == telnet) { + sprintf(command, "TELNET %s%s %s", + port ? "/PORT=" : "", + port ? port : "", + hostname); + if (TRACE) + fprintf(stderr, "HTTelnet: Command is: %s\n\n", command); + system(command); + } + else { + extern int LYgetch NOPARAMS; + extern BOOLEAN HadVMSInterrupt; + + printf( + "\nSorry, this browser was compiled without the %s access option.\n", + access); + printf("\nPress <return> to return to Lynx."); + LYgetch(); + HadVMSInterrupt = FALSE; + } + return HT_NO_DATA; /* Ok - it was done but no data */ + } + else { + if (login_protocol == telnet) { + sprintf(command, "TELNET %s%s %s", + port ? "/PORT=" : "", + port ? port : "", + hostname); + if (TRACE) + fprintf(stderr, "HTTelnet: Command is: %s\n\n", command); + system(command); + } + else { + extern int LYgetch NOPARAMS; + extern BOOLEAN HadVMSInterrupt; + + printf( + "\nSorry, this browser was compiled without the %s access option.\n", + access); + printf("\nPress <return> to return to Lynx."); + LYgetch(); + HadVMSInterrupt = FALSE; + } + return HT_NO_DATA; /* Ok - it was done but no data */ + } + } +#define TELNET_DONE +#endif /* SOCKETSHR_TCP */ + +#ifdef VM +#define SIMPLE_TELNET +#endif +#ifdef SIMPLE_TELNET + if (login_protocol == telnet) { /* telnet only */ + sprintf(command, "TELNET %s", /* @@ Bug: port ignored */ + hostname); + if (TRACE) + fprintf(stderr, "HTTelnet: Command is: %s\n\n", command); + system(command); + return HT_NO_DATA; /* Ok - it was done but no data */ + } +#endif + +#ifndef TELNET_DONE + printf( + "\nSorry, this browser was compiled without the %s access option.\n", + access); + printf( + "\nTo access the information you must %s to %s", access, hostname); + if (port) + printf(" (port %s)", port); + if (user) + printf("\nlogging in with username %s", user); + printf(".\n"); + { + extern int LYgetch NOPARAMS; + + printf("\nPress <return> to return to Lynx."); + fflush(stdout); + LYgetch(); +#ifdef VMS + { + extern BOOLEAN HadVMSInterrupt; + HadVMSInterrupt = FALSE; + } +#endif /* VMS */ + } + return HT_NO_DATA; +#endif /* !TELNET_DONE */ +} + +/* "Load a document" -- establishes a session +** ------------------------------------------ +** +** On entry, +** addr must point to the fully qualified hypertext reference. +** +** On exit, +** returns <0 Error has occured. +** >=0 Value of file descriptor or socket to be used +** to read data. +** *pFormat Set to the format of the file, if known. +** (See WWW.h) +** +*/ +PRIVATE int HTLoadTelnet +ARGS4 +( + CONST char *, addr, + HTParentAnchor *, anchor, + HTFormat, format_out, + HTStream *, sink /* Ignored */ +) +{ + char * access; + + char * host; + int status; + + if (sink) { + if (TRACE) + fprintf(stderr, + "HTTelnet: Can't output a live session -- must be interactive!\n"); + return HT_NO_DATA; + } + access = HTParse(addr, "file:", PARSE_ACCESS); + + host = HTParse(addr, "", PARSE_HOST); + if (!host || *host == '\0') { + status = HT_NO_DATA; + if (TRACE) + fprintf(stderr, "HTTelnet: No host specified!\n"); + } else { + status = remote_session(access, host); + } + + FREE(host); + FREE(access); + return status; +} + + +#ifdef GLOBALDEF_IS_MACRO +#define _HTTELNET_C_1_INIT { "telnet", HTLoadTelnet, NULL } +#define _HTTELNET_C_2_INIT { "rlogin", HTLoadTelnet, NULL } +#define _HTTELNET_C_3_INIT { "tn3270", HTLoadTelnet, NULL } +GLOBALDEF (HTProtocol, HTTelnet, _HTTELNET_C_1_INIT ); +GLOBALDEF (HTProtocol, HTRlogin, _HTTELNET_C_2_INIT ); +GLOBALDEF (HTProtocol, HTTn3270, _HTTELNET_C_3_INIT ); +#else +GLOBALDEF PUBLIC HTProtocol HTTelnet = { "telnet", HTLoadTelnet, NULL }; +GLOBALDEF PUBLIC HTProtocol HTRlogin = { "rlogin", HTLoadTelnet, NULL }; +GLOBALDEF PUBLIC HTProtocol HTTn3270 = { "tn3270", HTLoadTelnet, NULL }; +#endif /* GLOBALDEF_IS_MACRO */ diff --git a/WWW/Library/Implementation/HTTelnet.h b/WWW/Library/Implementation/HTTelnet.h new file mode 100644 index 00000000..4c5de744 --- /dev/null +++ b/WWW/Library/Implementation/HTTelnet.h @@ -0,0 +1,24 @@ +/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTTelnet.html + TELNET AND SIMILAR ACCESS METHODS + + */ + +#ifndef HTTELNET_H +#define HTTELNET_H + +#include "HTAccess.h" + +#ifdef GLOBALREF_IS_MACRO +extern GLOBALREF(HTProtocol,HTTelnet); +extern GLOBALREF(HTProtocol,HTRlogin); +extern GLOBALREF(HTProtocol,HTTn3270); +#else +GLOBALREF HTProtocol HTTelnet; +GLOBALREF HTProtocol HTRlogin; +GLOBALREF HTProtocol HTTn3270; +#endif /* GLOBALREF_IS_MACRO */ +#endif /* HTTELNET_H */ + +/* + + end */ diff --git a/WWW/Library/Implementation/HTUU.c b/WWW/Library/Implementation/HTUU.c new file mode 100644 index 00000000..68cad23b --- /dev/null +++ b/WWW/Library/Implementation/HTUU.c @@ -0,0 +1,207 @@ + +/* MODULE HTUU.c +** UUENCODE AND UUDECODE +** +** ACKNOWLEDGEMENT: +** This code is taken from rpem distribution, and was originally +** written by Mark Riordan. +** +** AUTHORS: +** MR Mark Riordan riordanmr@clvax1.cl.msu.edu +** AL Ari Luotonen luotonen@dxcern.cern.ch +** +** HISTORY: +** Added as part of the WWW library and edited to conform +** with the WWW project coding standards by: AL 5 Aug 1993 +** Originally written by: MR 12 Aug 1990 +** Original header text: +** ------------------------------------------------------------- +** File containing routines to convert a buffer +** of bytes to/from RFC 1113 printable encoding format. +** +** This technique is similar to the familiar Unix uuencode +** format in that it maps 6 binary bits to one ASCII +** character (or more aptly, 3 binary bytes to 4 ASCII +** characters). However, RFC 1113 does not use the same +** mapping to printable characters as uuencode. +** +** Mark Riordan 12 August 1990 and 17 Feb 1991. +** This code is hereby placed in the public domain. +** ------------------------------------------------------------- +** +** BUGS: +** +** +*/ + +#include "HTUtils.h" +#include "HTUU.h" + +#include "LYLeaks.h" + +PRIVATE char six2pr[64] = { + 'A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z', + '0','1','2','3','4','5','6','7','8','9','+','/' +}; + +PRIVATE unsigned char pr2six[256]; + + +/*--- function HTUU_encode ----------------------------------------------- + * + * Encode a single line of binary data to a standard format that + * uses only printing ASCII characters (but takes up 33% more bytes). + * + * Entry bufin points to a buffer of bytes. If nbytes is not + * a multiple of three, then the byte just beyond + * the last byte in the buffer must be 0. + * nbytes is the number of bytes in that buffer. + * This cannot be more than 48. + * bufcoded points to an output buffer. Be sure that this + * can hold at least 1 + (4*nbytes)/3 characters. + * + * Exit bufcoded contains the coded line. The first 4*nbytes/3 bytes + * contain printing ASCII characters representing + * those binary bytes. This may include one or + * two '=' characters used as padding at the end. + * The last byte is a zero byte. + * Returns the number of ASCII characters in "bufcoded". + */ +PUBLIC int HTUU_encode ARGS3(unsigned char *, bufin, + unsigned int, nbytes, + char *, bufcoded) +{ +/* ENC is the basic 1 character encoding function to make a char printing */ +#define ENC(c) six2pr[c] + + register char *outptr = bufcoded; + unsigned int i; + /* This doesn't seem to be needed (AL): register unsigned char *inptr = bufin; */ + + for (i=0; i<nbytes; i += 3) { + *(outptr++) = ENC(*bufin >> 2); /* c1 */ + *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /*c2*/ + *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03));/*c3*/ + *(outptr++) = ENC(bufin[2] & 077); /* c4 */ + + bufin += 3; + } + + /* If nbytes was not a multiple of 3, then we have encoded too + * many characters. Adjust appropriately. + */ + if(i == nbytes+1) { + /* There were only 2 bytes in that last group */ + outptr[-1] = '='; + } else if(i == nbytes+2) { + /* There was only 1 byte in that last group */ + outptr[-1] = '='; + outptr[-2] = '='; + } + *outptr = '\0'; + return(outptr - bufcoded); +} + + +/*--- function HTUU_decode ------------------------------------------------ + * + * Decode an ASCII-encoded buffer back to its original binary form. + * + * Entry bufcoded points to a uuencoded string. It is + * terminated by any character not in + * the printable character table six2pr, but + * leading whitespace is stripped. + * bufplain points to the output buffer; must be big + * enough to hold the decoded string (generally + * shorter than the encoded string) plus + * as many as two extra bytes used during + * the decoding process. + * outbufsize is the maximum number of bytes that + * can fit in bufplain. + * + * Exit Returns the number of binary bytes decoded. + * bufplain contains these bytes. + */ +PUBLIC int HTUU_decode ARGS3(char *, bufcoded, + unsigned char *, bufplain, + int, outbufsize) +{ +/* single character decode */ +#define DEC(c) pr2six[c] +#define MAXVAL 63 + + static int first = 1; + + int nbytesdecoded, j; + register char *bufin = bufcoded; + register unsigned char *bufout = bufplain; + register int nprbytes; + + /* If this is the first call, initialize the mapping table. + * This code should work even on non-ASCII machines. + */ + if(first) { + first = 0; + for(j=0; j<256; j++) pr2six[j] = MAXVAL+1; + + for(j=0; j<64; j++) pr2six[(unsigned char)six2pr[j]] = (unsigned char)j; +#if 0 + pr2six['A']= 0; pr2six['B']= 1; pr2six['C']= 2; pr2six['D']= 3; + pr2six['E']= 4; pr2six['F']= 5; pr2six['G']= 6; pr2six['H']= 7; + pr2six['I']= 8; pr2six['J']= 9; pr2six['K']=10; pr2six['L']=11; + pr2six['M']=12; pr2six['N']=13; pr2six['O']=14; pr2six['P']=15; + pr2six['Q']=16; pr2six['R']=17; pr2six['S']=18; pr2six['T']=19; + pr2six['U']=20; pr2six['V']=21; pr2six['W']=22; pr2six['X']=23; + pr2six['Y']=24; pr2six['Z']=25; pr2six['a']=26; pr2six['b']=27; + pr2six['c']=28; pr2six['d']=29; pr2six['e']=30; pr2six['f']=31; + pr2six['g']=32; pr2six['h']=33; pr2six['i']=34; pr2six['j']=35; + pr2six['k']=36; pr2six['l']=37; pr2six['m']=38; pr2six['n']=39; + pr2six['o']=40; pr2six['p']=41; pr2six['q']=42; pr2six['r']=43; + pr2six['s']=44; pr2six['t']=45; pr2six['u']=46; pr2six['v']=47; + pr2six['w']=48; pr2six['x']=49; pr2six['y']=50; pr2six['z']=51; + pr2six['0']=52; pr2six['1']=53; pr2six['2']=54; pr2six['3']=55; + pr2six['4']=56; pr2six['5']=57; pr2six['6']=58; pr2six['7']=59; + pr2six['8']=60; pr2six['9']=61; pr2six['+']=62; pr2six['/']=63; +#endif + } + + /* Strip leading whitespace. */ + + while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++; + + /* Figure out how many characters are in the input buffer. + * If this would decode into more bytes than would fit into + * the output buffer, adjust the number of input bytes downwards. + */ + bufin = bufcoded; + while(pr2six[(unsigned char)*(bufin++)] <= MAXVAL); + nprbytes = bufin - bufcoded - 1; + nbytesdecoded = ((nprbytes+3)/4) * 3; + if(nbytesdecoded > outbufsize) { + nprbytes = (outbufsize*4)/3; + } + + bufin = bufcoded; + + while (nprbytes > 0) { + *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4); + *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2); + *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3])); + bufin += 4; + nprbytes -= 4; + } + + if(nprbytes & 03) { + if(pr2six[bufin[-2]] > MAXVAL) { + nbytesdecoded -= 2; + } else { + nbytesdecoded -= 1; + } + } + + return(nbytesdecoded); +} + diff --git a/WWW/Library/Implementation/HTUU.h b/WWW/Library/Implementation/HTUU.h new file mode 100644 index 00000000..05874e24 --- /dev/null +++ b/WWW/Library/Implementation/HTUU.h @@ -0,0 +1,29 @@ +/* ENCODING TO PRINTABLE CHARACTERS + + File module provides functions HTUU_encode() and HTUU_decode() which convert a buffer + of bytes to/from RFC 1113 printable encoding format. This technique is similar to the + familiar Unix uuencode format in that it maps 6 binary bits to one ASCII character (or + more aptly, 3 binary bytes to 4 ASCII characters). However, RFC 1113 does not use the + same mapping to printable characters as uuencode. + + */ + +#ifndef HTUU_H +#define HTUU_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ + +PUBLIC int HTUU_encode PARAMS((unsigned char *bufin, + unsigned int nbytes, + char *bufcoded)); + +PUBLIC int HTUU_decode PARAMS((char *bufcoded, + unsigned char *bufplain, + int outbufsize)); + +#endif +/* + + End of file. */ diff --git a/WWW/Library/Implementation/HTUtils.h b/WWW/Library/Implementation/HTUtils.h new file mode 100644 index 00000000..3eb54ea0 --- /dev/null +++ b/WWW/Library/Implementation/HTUtils.h @@ -0,0 +1,311 @@ +/* Utitlity macros for the W3 code library + MACROS FOR GENERAL USE + + Generates: HTUtils.h + + See also: the system dependent file "tcp.h" + + */ + +#ifndef DEBUG +#define DEBUG /* Noone ever turns this off as trace is too important */ +#endif /* Keeep option for really small memory applications tho */ + +#ifndef HTUTILS_H +#define HTUTILS_H + +#ifdef _WINDOWS /* SCW */ +#include "windef.h" +#define BOOLEAN_DEFINED +#endif + +#ifdef SHORT_NAMES +#define WWW_TraceFlag HTTrFlag +#endif + +/* + +Debug message control. + + */ +#ifndef STDIO_H +#include <stdio.h> +#define STDIO_H +#endif + +#ifdef DEBUG +#define TRACE (WWW_TraceFlag) +#define PROGRESS(str) printf(str) + extern int WWW_TraceFlag; +#else +#define TRACE 0 +#define PROGRESS(str) /* nothing for now */ +#endif + +#define CTRACE if(TRACE)fprintf +#define tfp stderr + +/* + + ERROR TYPE + + This is passed back when streams are aborted. It might be nice to have some structure + of error messages, numbers, and recursive pointers to reasons. Curently this is a + placeholder for something more sophisticated. + + */ +typedef void * HTError; /* Unused at present -- best definition? */ + +/* + +Standard C library for malloc() etc + + */ +#ifdef DGUX +#include <stdlib.h> +#endif /* DGUX */ + +#ifdef vax +#ifdef unix +#define ultrix /* Assume vax+unix=ultrix */ +#endif /* unix */ +#endif /* vax */ + +#ifndef VMS +#ifndef ultrix + +#ifdef NeXT +#include <libc.h> /* NeXT */ +#endif /* NeXT */ +#ifndef MACH /* Vincent.Cate@furmint.nectar.cs.cmu.edu */ +#ifndef __STRICT_BSD__ +#include <stdlib.h> +#endif /* !__STRICT_BSD__ */ +#endif /* !MACH */ + +#else /* ultrix: */ + +#include <malloc.h> +#include <memory.h> +#include <stdio.h> +#include <stdlib.h> /* ANSI */ /* BSN */ + +#endif /* !ultrix */ +#else /* VMS: */ + +#include <stdlib.h> +#include <unixlib.h> +#include <ctype.h> +#if defined(VAXC) && !defined(__DECC) +#define malloc VAXC$MALLOC_OPT +#define calloc VAXC$CALLOC_OPT +#define free VAXC$FREE_OPT +#define cfree VAXC$CFREE_OPT +#define realloc VAXC$REALLOC_OPT +#endif /* VAXC && !__DECC */ + +#endif /* !VMS */ + +/* + +Macros for declarations + + */ +#define PUBLIC /* Accessible outside this module */ +#define PRIVATE static /* Accessible only within this module */ + +#ifdef __STDC__ +#define CONST const /* "const" only exists in STDC */ +#define NOPARAMS (void) +#define PARAMS(parameter_list) parameter_list +#define NOARGS (void) +#define ARGS1(t,a) \ + (t a) +#define ARGS2(t,a,u,b) \ + (t a, u b) +#define ARGS3(t,a,u,b,v,c) \ + (t a, u b, v c) +#define ARGS4(t,a,u,b,v,c,w,d) \ + (t a, u b, v c, w d) +#define ARGS5(t,a,u,b,v,c,w,d,x,e) \ + (t a, u b, v c, w d, x e) +#define ARGS6(t,a,u,b,v,c,w,d,x,e,y,f) \ + (t a, u b, v c, w d, x e, y f) +#define ARGS7(t,a,u,b,v,c,w,d,x,e,y,f,z,g) \ + (t a, u b, v c, w d, x e, y f, z g) +#define ARGS8(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h) \ + (t a, u b, v c, w d, x e, y f, z g, s h) +#define ARGS9(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h,r,i) \ + (t a, u b, v c, w d, x e, y f, z g, s h, r i) +#define ARGS10(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h,r,i,q,j) \ + (t a, u b, v c, w d, x e, y f, z g, s h, r i, q j) + +#else /* not ANSI */ + +#ifndef _WINDOWS +#define CONST +#endif +#define NOPARAMS () +#define PARAMS(parameter_list) () +#define NOARGS () +#define ARGS1(t,a) (a) \ + t a; +#define ARGS2(t,a,u,b) (a,b) \ + t a; u b; +#define ARGS3(t,a,u,b,v,c) (a,b,c) \ + t a; u b; v c; +#define ARGS4(t,a,u,b,v,c,w,d) (a,b,c,d) \ + t a; u b; v c; w d; +#define ARGS5(t,a,u,b,v,c,w,d,x,e) (a,b,c,d,e) \ + t a; u b; v c; w d; x e; +#define ARGS6(t,a,u,b,v,c,w,d,x,e,y,f) (a,b,c,d,e,f) \ + t a; u b; v c; w d; x e; y f; +#define ARGS7(t,a,u,b,v,c,w,d,x,e,y,f,z,g) (a,b,c,d,e,f,g) \ + t a; u b; v c; w d; x e; y f; z g; +#define ARGS8(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h) (a,b,c,d,e,f,g,h) \ + t a; u b; v c; w d; x e; y f; z g; s h; +#define ARGS9(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h,r,i) (a,b,c,d,e,f,g,h,i) \ + t a; u b; v c; w d; x e; y f; z g; s h; r i; +#define ARGS10(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h,r,i,q,j) (a,b,c,d,e,f,g,h,i,j) \ + t a; u b; v c; w d; x e; y f; z g; s h; r i; q j; + + +#endif /* __STDC__ (ANSI) */ + +#ifndef NULL +#define NULL ((void *)0) +#endif + +/* + +Booleans + + */ +/* Note: GOOD and BAD are already defined (differently) on RS6000 aix */ +/* #define GOOD(status) ((status)38;1) VMS style status: test bit 0 */ +/* #define BAD(status) (!GOOD(status)) Bit 0 set if OK, otherwise clear */ + +#ifndef _WINDOWS +#ifndef BOOLEAN_DEFINED + typedef char BOOLEAN; /* Logical value */ +#ifndef CURSES +#ifndef TRUE +#define TRUE (BOOLEAN)1 +#define FALSE (BOOLEAN)0 +#endif +#endif /* CURSES */ +#endif /* _WINDOWS */ +#define BOOLEAN_DEFINED +#endif + +#ifndef BOOL +#define BOOL BOOLEAN +#endif +#ifndef YES +#define YES (BOOLEAN)1 +#define NO (BOOLEAN)0 +#endif + +#define TCP_PORT 80 /* Allocated to http by Jon Postel/ISI 24-Jan-92 */ +#define OLD_TCP_PORT 2784 /* Try the old one if no answer on 80 */ +#define DNP_OBJ 80 /* This one doesn't look busy, but we must check */ + /* That one was for decnet */ + +/* Inline Function WHITE: Is character c white space? */ +/* For speed, include all control characters */ + +#define WHITE(c) (((unsigned char)(TOASCII(c))) <= 32) + + +/* + +Sucess (>=0) and failure (<0) codes + + */ + +#define HT_REDIRECTING 29996 +#define HT_LOADED 29997 /* Instead of a socket */ +#define HT_INTERRUPTED -29998 +#define HT_NOT_LOADED -29999 +#define HT_OK 0 /* Generic success*/ + +#define HT_NO_ACCESS -10 /* Access not available */ +#define HT_FORBIDDEN -11 /* Access forbidden */ +#define HT_INTERNAL -12 /* Weird -- should never happen. */ +#define HT_BAD_EOF -12 /* Premature EOF */ + + +#include "HTString.h" /* String utilities */ + +#ifndef va_arg +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif +#endif + +/* + +Out Of Memory checking for malloc() return: + + */ +#ifndef __FILE__ +#define __FILE__ "" +#define __LINE__ "" +#endif + +#include "LYexit.h" + +#define outofmem(file, func) \ + { fprintf(stderr, "%s %s: out of memory.\nProgram aborted.\n", file, func); \ + exit(-1);} +/* extern void outofmem PARAMS((const char *fname, const char *func)); */ + + +/* + + WHO PUT THESE IN AND WHAT ARE THEY ANYWAY? + + */ +#ifdef THEY_WILL_BE_REMOVED +extern void msg_init PARAMS((int height)); +extern void msg_printf PARAMS((int y, const char *fmt, ...)); +extern void msg_exit PARAMS((int wait_for_key)); +#endif + +/* + +Upper- and Lowercase macros + + The problem here is that toupper(x) is not defined officially unless isupper(x) is. + These macros are CERTAINLY needed on #if defined(pyr) || define(mips) or BDSI + platforms. For safefy, we make them mandatory. + + */ +#include <ctype.h> +#include <string.h> + +#ifndef TOLOWER + /* Pyramid and Mips can't uppercase non-alpha */ +#define TOLOWER(c) (isupper((unsigned char)c) ? tolower((unsigned char)c) : (c)) +#define TOUPPER(c) (islower((unsigned char)c) ? toupper((unsigned char)c) : (c)) +#endif /* ndef TOLOWER */ + +/* + +The local equivalents of CR and LF + + We can check for these after net ascii text has been converted to the local + representation. Similarly, we include them in strings to be sent as net ascii after + translation. + + */ +#define LF FROMASCII('\012') /* ASCII line feed LOCAL EQUIVALENT */ +#define CR FROMASCII('\015') /* Will be converted to ^M for transmission */ + +#endif /* HTUTILS_H */ + +/* + + end of utilities */ diff --git a/WWW/Library/Implementation/HTVMSUtils.c b/WWW/Library/Implementation/HTVMSUtils.c new file mode 100644 index 00000000..3d97115f --- /dev/null +++ b/WWW/Library/Implementation/HTVMSUtils.c @@ -0,0 +1,1220 @@ + +/* MODULE HTVMSUtil.c +** VMS Utility Routines +** +** AUTHORS: +** MD Mark Donszelmann duns@vxdeop.cern.ch +** +** HISTORY: +** 14 Nov 93 MD Written +** +** BUGS: +** +** +*/ + +#include "HTUtils.h" +#include "tcp.h" +#include "HTFormat.h" +#include "HTStream.h" +#include "HTVMSUtils.h" +/*#include <stdio.h> included by HTUtils.h -- FM */ +/*#include <unixlib.h> included by HTUtils.h -- FM */ +#include <ssdef.h> +#include <jpidef.h> +#include <prvdef.h> +#include <acldef.h> +#include <chpdef.h> +#include <descrip.h> +#include <lib$routines.h> +#include <starlet.h> +#include <rmsdef.h> + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +#define INFINITY 512 /* File name length @@ FIXME */ + +PUBLIC BOOL HTVMSFileVersions=FALSE; /* Include version numbers in listing? */ + +typedef struct { + unsigned long BufferLength : 16; + unsigned long ItemCode : 16; + unsigned long BufferAddress : 32; + unsigned long ReturnLengthAddress : 32; +} ItemStruct; + +extern CONST char * HTHostName NOPARAMS; + +/* PUBLIC HTVMS_authSysPrv() +** CHECKS IF THIS PROCESS IS AUTHORIZED TO ENABLE SYSPRV +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns YES if SYSPRV is authorized +*/ +PUBLIC BOOL HTVMS_authSysPrv NOARGS +{ +unsigned long Result; +ItemStruct ItemList[2]; +unsigned long Length; +unsigned long Buffer[2]; + + /* fill Item */ + ItemList[0].BufferLength = sizeof(Buffer); + ItemList[0].BufferAddress = (unsigned long)Buffer; + ItemList[0].ReturnLengthAddress = (unsigned long)&Length; + ItemList[0].ItemCode = JPI$_AUTHPRIV; + + /* terminate list */ + ItemList[1].ItemCode = 0; + ItemList[1].BufferLength = 0; + + /* call system */ + Result = sys$getjpiw(0, 0, 0, ItemList, 0, 0, 0); + + if (Result != SS$_NORMAL) + return(NO); + + if (Buffer[0] & PRV$M_SYSPRV) + return(YES); + + return(NO); +} + + + +/* PUBLIC HTVMS_enableSysPrv() +** ENABLES SYSPRV +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** +*/ +PUBLIC void HTVMS_enableSysPrv NOARGS +{ +unsigned long Result; +unsigned long Prv[2], PreviousPrv[2]; + + Prv[0] = PRV$M_SYSPRV; + Prv[1] = 0; + Result = sys$setprv(1,&Prv,0,&PreviousPrv); + + if (TRACE) { + if (Result == SS$_NORMAL) { + if (!(PreviousPrv[0] & PRV$M_SYSPRV)) { + fprintf(stderr, "HTVMS_enableSysPrv: Enabled SYSPRV\n"); + } + } + } +} + + + +/* PUBLIC HTVMS_disableSysPrv() +** DISABLES SYSPRV +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** +*/ +PUBLIC void HTVMS_disableSysPrv NOARGS +{ +unsigned long Result; +unsigned long Prv[2], PreviousPrv[2]; + + Prv[0] = PRV$M_SYSPRV; + Prv[1] = 0; + Result = sys$setprv(0,&Prv,0,&PreviousPrv); + + if (TRACE) { + if (Result == SS$_NORMAL) { + if (PreviousPrv[0] & PRV$M_SYSPRV) { + fprintf(stderr, "HTVMS_disableSysPrv: Disabled SYSPRV\n"); + } + } + } +} + + + +/* PUBLIC HTVMS_checkAccess() +** CHECKS ACCESS TO FILE FOR CERTAIN USER +** ON ENTRY: +** FileName The file to be accessed +** UserName Name of the user to check access for. +** User nobody, represented by "" is given NO for an answer +** Method Name of the method to be chceked +** +** ON EXIT: +** returns YES if access is allowed +** +*/ +PUBLIC BOOL HTVMS_checkAccess ARGS3( + CONST char *, FileName, + CONST char *, UserName, + CONST char *, Method) +{ +unsigned long Result; +ItemStruct ItemList[2]; +unsigned long Length; +unsigned long Buffer; +unsigned long ObjType; + +char *VmsName; + +struct dsc$descriptor_s FileNameDesc; +struct dsc$descriptor_s UserNameDesc; + +char *colon; + + /* user nobody should access as from account under which server is running */ + if (0 == strcmp(UserName,"")) + return(NO); + + /* check Filename and convert */ + colon = strchr(FileName,':'); + if (colon) + VmsName = HTVMS_name("",colon+1); + else + VmsName = HTVMS_name("",FileName); + + /* check for GET */ + if (0 == strcmp(Method,"GET")) + { + /* fill Item */ + ItemList[0].BufferLength = sizeof(Buffer); + ItemList[0].BufferAddress = (unsigned long)&Buffer; + ItemList[0].ReturnLengthAddress = (unsigned long)&Length; + ItemList[0].ItemCode = CHP$_FLAGS; + + /* terminate list */ + ItemList[1].ItemCode = 0; + ItemList[1].BufferLength = 0; + + /* fill input */ + ObjType = ACL$C_FILE; + Buffer = CHP$M_READ; + UserNameDesc.dsc$w_length = strlen(UserName); + UserNameDesc.dsc$b_dtype = DSC$K_DTYPE_T; + UserNameDesc.dsc$b_class = DSC$K_CLASS_S; + UserNameDesc.dsc$a_pointer = (char *)UserName; + FileNameDesc.dsc$w_length = strlen(VmsName); + FileNameDesc.dsc$b_dtype = DSC$K_DTYPE_T; + FileNameDesc.dsc$b_class = DSC$K_CLASS_S; + FileNameDesc.dsc$a_pointer = VmsName; + + /* call system */ + Result = sys$check_access(&ObjType,&FileNameDesc,&UserNameDesc,ItemList); + + if (Result == SS$_NORMAL) + return(YES); + else + return(NO); + } + + return(NO); +} + + + +/* PUBLIC HTVMS_wwwName() +** CONVERTS VMS Name into WWW Name +** ON ENTRY: +** vmsname VMS file specification (NO NODE) +** +** ON EXIT: +** returns www file specification +** +** EXAMPLES: +** vmsname wwwname +** DISK$USER disk$user +** DISK$USER: /disk$user/ +** DISK$USER:[DUNS] /disk$user/duns +** DISK$USER:[DUNS.ECHO] /disk$user/duns/echo +** [DUNS] duns +** [DUNS.ECHO] duns/echo +** [DUNS.ECHO.-.TRANS] duns/echo/../trans +** [DUNS.ECHO.--.TRANS] duns/echo/../../trans +** [.DUNS] duns +** [.DUNS.ECHO] duns/echo +** [.DUNS.ECHO]TEST.COM duns/echo/test.com +** TEST.COM test.com +** +** +*/ +PUBLIC char * HTVMS_wwwName ARGS1( + char *, vmsname) +{ +static char wwwname[256]; +char *src, *dst; +int dir; + dst = wwwname; + src = vmsname; + dir = 0; + if (strchr(src,':')) *(dst++) = '/'; + for ( ; *src != '\0' ; src++) + { + switch(*src) + { + case ':': *(dst++) = '/'; break; + case '-': if (dir) + { + if ((*(src-1)=='[' || *(src-1)=='.' || *(src-1)=='-') && + (*(src+1)=='.' || *(src+1)=='-')) + { + *(dst++) = '/'; + *(dst++) = '.'; + *(dst++) = '.'; + } + else + *(dst++) = '-'; + } + else + { + if (*(src-1) == ']') *(dst++) = '/'; + *(dst++) = '-'; + } + break; + case '.': if (dir) + { + if (*(src-1) != '[') *(dst++) = '/'; + } + else + { + if (*(src-1) == ']') *(dst++) = '/'; + *(dst++) = '.'; + } + break; + case '[': dir = 1; break; + case ']': dir = 0; break; + default: if (*(src-1) == ']') *(dst++) = '/'; + *(dst++) = *src; + break; + } + } + *(dst++) = '\0'; + return(wwwname); +} + + +/* PUBLIC HTVMS_name() +** CONVERTS WWW name into a VMS name +** ON ENTRY: +** nn Node Name (optional) +** fn WWW file name +** +** ON EXIT: +** returns vms file specification +** +** Bug: Returns pointer to static -- non-reentrant +*/ +PUBLIC char * HTVMS_name ARGS2( + CONST char *, nn, + CONST char *, fn) +{ + +/* We try converting the filename into Files-11 syntax. That is, we assume +** first that the file is, like us, on a VMS node. We try remote +** (or local) DECnet access. Files-11, VMS, VAX and DECnet +** are trademarks of Digital Equipment Corporation. +** The node is assumed to be local if the hostname WITHOUT DOMAIN +** matches the local one. @@@ +*/ + static char vmsname[INFINITY]; /* returned */ + char * filename = (char*)malloc(strlen(fn)+1); + char * nodename = (char*)malloc(strlen(nn)+2+1); /* Copies to hack */ + char *second; /* 2nd slash */ + char *last; /* last slash */ + + char * hostname = (char *)HTHostName(); + + if (!filename || !nodename) outofmem(__FILE__, "HTVMSname"); + strcpy(filename, fn); + strcpy(nodename, ""); /* On same node? Yes if node names match */ + if (strncmp(nn,"localhost",9)) { + char *p, *q; + for (p=hostname, q=(char *)nn; + *p && *p!='.' && *q && *q!='.'; p++, q++){ + if (TOUPPER(*p)!=TOUPPER(*q)) { + strcpy(nodename, nn); + q = strchr(nodename, '.'); /* Mismatch */ + if (q) *q=0; /* Chop domain */ + strcat(nodename, "::"); /* Try decnet anyway */ + break; + } + } + } + + second = strchr(filename+1, '/'); /* 2nd slash */ + last = strrchr(filename, '/'); /* last slash */ + + if (!second) { /* Only one slash */ + sprintf(vmsname, "%s%s", nodename, filename + 1); + } else if(second==last) { /* Exactly two slashes */ + *second = 0; /* Split filename from disk */ + sprintf(vmsname, "%s%s:%s", nodename, filename+1, second+1); + *second = '/'; /* restore */ + } else { /* More than two slashes */ + char * p; + *second = 0; /* Split disk from directories */ + *last = 0; /* Split dir from filename */ + sprintf(vmsname, "%s%s:[%s]%s", + nodename, filename+1, second+1, last+1); + *second = *last = '/'; /* restore filename */ + for (p=strchr(vmsname, '['); *p!=']'; p++) + if (*p=='/') *p='.'; /* Convert dir sep. to dots */ + } + FREE(nodename); + FREE(filename); + return vmsname; +} + +/* +** The code below is for directory browsing by VMS Curses clients. +** It is based on the newer WWWLib's HTDirBrw.c. - Foteos Macrides +*/ +PUBLIC int HTStat ARGS2( + CONST char *, filename, + stat_t *, info) +{ + /* + the following stuff does not work in VMS with a normal stat... + --> /disk$user/duns/www if www is a directory + is statted like: /disk$user/duns/www.dir + after a normal stat has failed + --> /disk$user/duns if duns is a toplevel directory + is statted like: /disk$user/000000/duns.dir + --> /disk$user since disk$user is a device + is statted like: /disk$user/000000/000000.dir + --> / + searches all devices, no solution yet... + --> /vxcern!/disk$cr/wwwteam/login.com + is not statted but granted with fake information... + */ +int Result; +int Len; +char *Ptr, *Ptr2; +char Name[256]; + + /* try normal stat... */ + Result = stat((char *)filename,info); + if (Result == 0) + return(Result); + + /* make local copy */ + strcpy(Name,filename); + +#ifdef NOT_USED + /* if filename contains a node specification (! or ::), we will try to access + the file via DECNET, but we do not stat it..., just return success + with some fake information... */ + if (HTVMS_checkDecnet(Name)) + { + /* set up fake info, only the one we use... */ + info->st_dev = NULL; + info->st_ino[0] = 0; + info->st_ino[1] = 0; + info->st_ino[2] = 0; + info->st_mode = S_IFREG | S_IREAD; /* assume it is a regular Readable file */ + info->st_nlink = NULL; + info->st_uid = 0; + info->st_gid = 0; + info->st_rdev = 0; + info->st_size = 0; + info->st_atime = time(NULL); + info->st_mtime = time(NULL); + info->st_ctime = time(NULL); + + return(0); + } +#endif /* NOT_USED */ + + /* failed,so do device search in case root is requested */ + if (!strcmp(Name,"/")) + { /* root requested */ + return(-1); + } + + /* failed so this might be a directory, add '.dir' */ + Len = strlen(Name); + if (Name[Len-1] == '/') + Name[Len-1] = '\0'; + + /* fail in case of device */ + Ptr = strchr(Name+1,'/'); + if ((Ptr == NULL) && (Name[0] == '/')) + { /* device only... */ + strcat(Name,"/000000/000000"); + } + + if (Ptr != NULL) + { /* correct filename in case of toplevel dir */ + Ptr2 = strchr(Ptr+1,'/'); + if ((Ptr2 == NULL) && (Name[0] == '/')) + { + char End[256]; + strcpy(End,Ptr); + *(Ptr+1) = '\0'; + strcat(Name,"000000"); + strcat(Name,End); + } + } + + /* try in case a file on toplevel directory or .DIR was alreadyt specified */ + Result = stat(Name,info); + if (Result == 0) + return(Result); + + /* add .DIR and try again */ + strcat(Name,".dir"); + Result = stat(Name,info); + return(Result); +} + +/*** "dirent.h" ***/ +/* #include <types.h> already in tcp.h */ + +#ifndef _POSIX_SOURCE +#define d_ino d_fileno /* compatability */ +#ifndef NULL +#define NULL 0 +#endif +#endif /* !_POSIX_SOURCE */ + +typedef struct __dirdesc { +#if 0 + int dd_fd; /* file descriptor */ + long dd_loc; /* buf offset of entry from last readddir() */ + long dd_size; /* amount of valid data in buffer */ + long dd_bsize; /* amount of entries read at a time */ + long dd_off; /* Current offset in dir (for telldir) */ + char *dd_buf; /* directory data buffer */ +#endif + long context; /* context descriptor for LIB$FIND_FILE calls */ + char dirname[255+1]; /* keeps the directory name, including *.* */ + struct dsc$descriptor_s dirname_desc; /* descriptor of dirname */ +} DIR; + +PRIVATE DIR *HTVMSopendir(char *dirname); +PRIVATE struct dirent *HTVMSreaddir(DIR *dirp); +PRIVATE int HTVMSclosedir(DIR *dirp); +#if 0 +#ifndef _POSIX_SOURCE +extern void seekdir(/* DIR *dirp, int loc */); +extern long telldir(/* DIR *dirp */); +#endif /* POSIX_SOURCE */ +extern void rewinddir(/* DIR *dirp */); + +#ifndef lint +#define rewinddir(dirp) seekdir((dirp), (long)0) +#endif +#endif /* not defined for VMS */ + +/*** #include "sys_dirent.h" ***/ +/*** "sys_dirent.h" ***/ +struct dirent { +#if 0 + off_t d_off; /* offset of next disk dir entry */ +#endif + unsigned long d_fileno; /* file number of entry */ +#if 0 + unsigned short d_reclen; /* length of this record */ +#endif + unsigned short d_namlen; /* length of string in d_name */ + char d_name[255+1]; /* name (up to MAXNAMLEN + 1) */ +}; + +#ifndef _POSIX_SOURCE +/* + * It's unlikely to change, but make sure that sizeof d_name above is + * at least MAXNAMLEN + 1 (more may be added for padding). + */ +#define MAXNAMLEN 255 +/* + * The macro DIRSIZ(dp) gives the minimum amount of space required to represent + * a directory entry. For any directory entry dp->d_reclen >= DIRSIZ(dp). + * Specific filesystem types may use this macro to construct the value + * for d_reclen. + */ +#undef DIRSIZ +#define DIRSIZ(dp) \ + (((sizeof(struct dirent) - (MAXNAMLEN+1) + ((dp)->d_namlen+1)) +3) & ~3) + +#endif /* !_POSIX_SOURCE */ + + +PRIVATE DIR *HTVMSopendir(char *dirname) +{ +static DIR dir; +char *closebracket; +long status; +struct dsc$descriptor_s entryname_desc; +struct dsc$descriptor_s dirname_desc; +char DirEntry[256]; +char VMSentry[256]; +char UnixEntry[256]; +int index; +char *dot; + + /* check if directory exists */ + /* dirname can look like /disk$user/duns/www/test/multi */ + /* or like /disk$user/duns/www/test/multi/ */ + /* DirEntry should look like disk$user:[duns.www.test]multi in both cases */ + /* dir.dirname should look like disk$user:[duns.www.test.multi] */ + strcpy(UnixEntry,dirname); + if (UnixEntry[strlen(UnixEntry)-1] != '/') + strcat(UnixEntry,"/"); + + strcpy(DirEntry, HTVMS_name("",UnixEntry)); + strcpy(dir.dirname, DirEntry); + index = strlen(DirEntry) - 1; + + if (DirEntry[index] == ']') + DirEntry[index] = '\0'; + + if ((dot = strrchr(DirEntry,'.')) == NULL) + { /* convert disk$user:[duns] into disk$user:[000000]duns.dir */ + char *openbr = strrchr(DirEntry,'['); + if (!openbr) + { /* convert disk$user: into disk$user:[000000]000000.dir */ + strcpy(dir.dirname, DirEntry); + strcat(dir.dirname, "[000000]"); + strcat(DirEntry,"[000000]000000.dir"); + } + else + { + char End[256]; + strcpy(End,openbr+1); + *(openbr+1) = '\0'; + strcat(DirEntry,"000000]"); + strcat(DirEntry,End); + strcat(DirEntry,".dir"); + } + } + else + { + *dot = ']'; + strcat(DirEntry,".dir"); + } + + dir.context = 0; + dirname_desc.dsc$w_length = strlen(DirEntry); + dirname_desc.dsc$b_dtype = DSC$K_DTYPE_T; + dirname_desc.dsc$b_class = DSC$K_CLASS_S; + dirname_desc.dsc$a_pointer = (char *)&(DirEntry); + + /* look for the directory */ + entryname_desc.dsc$w_length = 255; + entryname_desc.dsc$b_dtype = DSC$K_DTYPE_T; + entryname_desc.dsc$b_class = DSC$K_CLASS_S; + entryname_desc.dsc$a_pointer = VMSentry; + + status = lib$find_file(&(dirname_desc), + &entryname_desc, + &(dir.context), + 0,0,0,0); + if (!(status & 0x01)) + { /* directory not found */ + return(NULL); + } + +#if 0 + /* now correct dirname, which looks like disk$user:[duns.www.test]multi */ + /* and should look like disk$user:[duns.www.test.multi] */ + closebracket = strchr(dir.dirname,']'); + *closebracket = '.'; + closebracket = strstr(dir.dirname,".dir"); + *closebracket = '\0'; + strcat(dir.dirname,"]"); +#endif + + if (HTVMSFileVersions) + strcat(dir.dirname,"*.*;*"); + else + strcat(dir.dirname,"*.*"); + dir.context = 0; + dir.dirname_desc.dsc$w_length = strlen(dir.dirname); + dir.dirname_desc.dsc$b_dtype = DSC$K_DTYPE_T; + dir.dirname_desc.dsc$b_class = DSC$K_CLASS_S; + dir.dirname_desc.dsc$a_pointer = (char *)&(dir.dirname); + return(&dir); +} + +PRIVATE struct dirent *HTVMSreaddir(DIR *dirp) +{ +static struct dirent entry; +long status; +struct dsc$descriptor_s entryname_desc; +char *space, *slash; +char VMSentry[256]; +char *UnixEntry; + + entryname_desc.dsc$w_length = 255; + entryname_desc.dsc$b_dtype = DSC$K_DTYPE_T; + entryname_desc.dsc$b_class = DSC$K_CLASS_S; + entryname_desc.dsc$a_pointer = VMSentry; + + status = lib$find_file(&(dirp->dirname_desc), + &entryname_desc, + &(dirp->context), + 0,0,0,0); + if (status == RMS$_NMF) + { /* no more files */ + return(NULL); + } + else + { /* ok */ + if (!(status & 0x01)) return(0); + if (HTVMSFileVersions) + space = strchr(VMSentry,' '); + else + space = strchr(VMSentry,';'); + if (space) + *space = '\0'; + + /* convert to unix style... */ + UnixEntry = HTVMS_wwwName(VMSentry); + slash = strrchr(UnixEntry,'/') + 1; + strcpy(entry.d_name,slash); + entry.d_namlen = strlen(entry.d_name); + entry.d_fileno = 1; + return(&entry); + } +} + +PRIVATE int HTVMSclosedir(DIR *dirp) +{ +long status; + + status = lib$find_file_end(&(dirp->context)); + if (!(status & 0x01)) exit(status); + dirp->context = 0; + return(0); +} + +#include "HTAnchor.h" +#include "HTParse.h" +#include "HTBTree.h" +#include "HTFile.h" /* For HTFileFormat() */ +#include "HTAlert.h" +/* +** Hypertext object building machinery. +*/ +#include "HTML.h" +#define PUTC(c) (*targetClass.put_character)(target, c) +#define PUTS(s) (*targetClass.put_string)(target, s) +#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0) +#define END(e) (*targetClass.end_element)(target, e, 0) +#define FREE_TARGET (*targetClass._free)(target) +#define ABORT_TARGET (*targetClass._free)(target) +struct _HTStructured { + CONST HTStructuredClass * isa; + /* ... */ +}; + +#define STRUCT_DIRENT struct dirent + +PRIVATE char * months[12] = { + "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" +}; + +typedef struct _VMSEntryInfo { + char * filename; + char * type; + char * date; + unsigned int size; + BOOLEAN display; /* show this entry? */ +} VMSEntryInfo; + +PRIVATE void free_VMSEntryInfo_struct_contents ARGS1(VMSEntryInfo *,entry_info) +{ + if (entry_info) { + FREE(entry_info->filename); + FREE(entry_info->type); + FREE(entry_info->date); + } + /* dont free the struct */ +} + +#define FILE_BY_NAME 0 +#define FILE_BY_TYPE 1 +#define FILE_BY_SIZE 2 +#define FILE_BY_DATE 3 +extern BOOLEAN HTfileSortMethod; /* specifies the method of sorting */ + +PUBLIC int compare_VMSEntryInfo_structs ARGS2(VMSEntryInfo *,entry1, + VMSEntryInfo *,entry2) +{ + int i, status; + char date1[16], date2[16], time1[8], time2[8], month[4]; + + switch(HTfileSortMethod) + { + case FILE_BY_SIZE: + /* both equal or both 0 */ + if(entry1->size == entry2->size) + return(strcasecomp(entry1->filename, + entry2->filename)); + else + if(entry1->size > entry2->size) + return(1); + else + return(-1); + break; + case FILE_BY_TYPE: + if(entry1->type && entry2->type) { + status = strcasecomp(entry1->type, entry2->type); + if(status) + return(status); + /* else fall to filename comparison */ + } + return (strcasecomp(entry1->filename, + entry2->filename)); + break; + case FILE_BY_DATE: + if(entry1->date && entry2->date) { + /* + ** Make sure we have the correct length. - FM + */ + if (strlen(entry1->date) != 12 || + strlen(entry2->date) != 12) { + return (strcasecomp(entry1->filename, + entry2->filename)); + } + /* + ** Set up for sorting in reverse + ** chronological order. - FM + */ + if (entry1->date[7] != ' ') { + strcpy(date1, "9999"); + strcpy(time1, (char *)&entry1->date[7]); + } else { + strcpy(date1, (char *)&entry1->date[8]); + strcpy(time1, "00:00"); + } + strncpy(month, entry1->date, 3); + month[3] = '\0'; + for (i = 0; i < 12; i++) { + if (!strcasecomp(month, months[i])) { + break; + } + } + i++; + sprintf(month, "%s%d", (i < 10 ? "0" : ""), i); + strcat(date1, month); + strncat(date1, (char *)&entry1->date[4], 2); + date1[8] = '\0'; + if (date1[6] == ' ') { + date1[6] = '0'; + } + strcat(date1, time1); + if (entry2->date[7] != ' ') { + strcpy(date2, "9999"); + strcpy(time2, (char *)&entry2->date[7]); + } else { + strcpy(date2, (char *)&entry2->date[8]); + strcpy(time2, "00:00"); + } + strncpy(month, entry2->date, 3); + month[3] = '\0'; + for (i = 0; i < 12; i++) { + if (!strcasecomp(month, months[i])) { + break; + } + } + i++; + sprintf(month, "%s%d", (i < 10 ? "0" : ""), i); + strcat(date2, month); + strncat(date2, (char *)&entry2->date[4], 2); + date2[8] = '\0'; + if (date2[6] == ' ') { + date2[6] = '0'; + } + strcat(date2, time2); + /* + ** Do the comparison. - FM + */ + status = strcasecomp(date2, date1); + if(status) + return(status); + /* else fall to filename comparison */ + } + return (strcasecomp(entry1->filename, + entry2->filename)); + break; + case FILE_BY_NAME: + default: + return (strcmp(entry1->filename, + entry2->filename)); + } +} + + +/* HTVMSBrowseDir() +** +** This function generates a directory listing as an HTML-object +** for local file URL's. It assumes the first two elements of +** of the path are a device followed by a directory: +** +** file://localhost/device/directory[/[foo]] +** +** Will not accept 000000 as a directory name. +** Will offer links to parent through the top directory, unless +** a terminal slash was included in the calling URL. +** +** Returns HT_LOADED on success, HTLoadError() messages on error. +** +** Developed for Lynx by Foteos Macrides (macrides@sci.wfeb.edu). +*/ +PUBLIC int HTVMSBrowseDir ARGS4( + CONST char *, address, + HTParentAnchor *, anchor, + HTFormat, format_out, + HTStream *, sink +) +{ + HTStructured* target; + HTStructuredClass targetClass; + char *pathname = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION); + char *tail = NULL; + char *title = NULL; + char *header = NULL; + char *parent = NULL; + char *relative = NULL; + char *cp, *cp1; + int pathend; + DIR *dp; + struct stat file_info; + time_t NowTime; + static char ThisYear[8]; + VMSEntryInfo *entry_info=0; + char string_buffer[64]; + extern BOOLEAN no_dotfiles, show_dotfiles; + + HTUnEscape(pathname); + CTRACE(stderr,"HTVMSBrowseDir: Browsing `%s\'\n", pathname); + + /* + * Require at least two elements (presumably a device and directory) + * and disallow the device root (000000 directory). Symbolic paths + * (e.g., sys$help) should have been translated and expanded (e.g., + * to /sys$sysroot/syshlp) before calling this routine. + */ + if (((*pathname != '/') || + (cp=strchr(pathname+1, '/')) == NULL || + *(cp+1) == '\0' || + 0==strncmp((cp+1), "000000", 6)) || + (dp=HTVMSopendir(pathname)) == NULL) { + FREE(pathname); + return HTLoadError(sink, 403, "Could not access directory."); + } + + /* + * Set up the output stream. + */ + _HTProgress ("Building directory listing..."); + target = HTML_new(anchor, format_out, sink); + targetClass = *(target->isa); + + /* + * Set up the offset string of the anchor reference, + * and strings for the title and header. + */ + cp = strrchr(pathname, '/'); /* find lastslash */ + StrAllocCopy(tail, (cp+1)); /* take slash off the beginning */ + if (*tail != '\0') { + StrAllocCopy(title, tail); + *cp = '\0'; + if ((cp1=strrchr(pathname, '/')) != NULL && + cp1 != pathname && + strncmp((cp1+1), "000000", 6)) + StrAllocCopy(parent, (cp1+1)); + *cp = '/'; + } else { + pathname[strlen(pathname)-1] = '\0'; + cp = strrchr(pathname, '/'); + StrAllocCopy(title, (cp+1)); + pathname[strlen(pathname)] = '/'; + } + StrAllocCopy(header, pathname); + + /* + * Initialize path name for HTStat(). + */ + pathend = strlen(pathname); + if (*(pathname+pathend-1) != '/') { + StrAllocCat(pathname, "/"); + pathend++; + } + + /* + * Output the title and header. + */ + START(HTML_HTML); + PUTS("\n"); + START(HTML_HEAD); + PUTS("\n"); + HTUnEscape(title); + START(HTML_TITLE); + PUTS(title); + PUTS(" directory"); + END(HTML_TITLE); + PUTS("\n"); + FREE(title); + END(HTML_HEAD); + PUTS("\n"); + START(HTML_BODY); + PUTS("\n"); + HTUnEscape(header); + START(HTML_H1); + PUTS(header); + END(HTML_H1); + PUTS("\n"); + if (HTDirReadme == HT_DIR_README_TOP) { + FILE * fp; + if (header[strlen(header)-1] != '/') + StrAllocCat(header, "/"); + StrAllocCat(header, HT_DIR_README_FILE); + if ((fp = fopen(header, "r")) != NULL) { + START(HTML_PRE); + for(;;) { + char c = fgetc(fp); + if (c == (char)EOF) + break; +#ifdef NOTDEFINED + switch (c) { + case '&': + case '<': + case '>': + PUTC('&'); + PUTC('#'); + PUTC((char)(c / 10)); + PUTC((char) (c % 10)); + PUTC(';'); + break; + default: + PUTC(c); + } +#else + PUTC(c); +#endif /* NOTDEFINED */ + } + END(HTML_PRE); + fclose(fp); + } + } + FREE(header); + if (parent) { + relative = (char*) malloc(strlen(tail) + 4); + if (relative == NULL) + outofmem(__FILE__, "HTVMSBrowseDir"); + sprintf(relative, "%s/..", tail); + HTStartAnchor(target, "", relative); + PUTS("Up to "); + HTUnEscape(parent); + PUTS(parent); + END(HTML_A); + START(HTML_P); + PUTS("\n"); + FREE(relative); + FREE(parent); + } + + /* + * Set up the date comparison. + */ + NowTime = time(NULL); + strcpy(ThisYear, (char *)ctime(&NowTime)+20); + ThisYear[4] = '\0'; + + /* + * Now, generate the Btree and put it out to the output stream. + */ + { + char dottest = 2; /* To avoid two strcmp() each time */ + STRUCT_DIRENT *dirbuf; + HTBTree *bt; + + /* Set up sort key and initialize BTree */ + bt = HTBTree_new((HTComparer) compare_VMSEntryInfo_structs); + + /* Build tree */ + while ((dirbuf = HTVMSreaddir(dp))) { + HTAtom *encoding = NULL; + HTFormat format; + + /* Skip if not used */ + if (!dirbuf->d_ino) { + continue; + } + + /* Current and parent directories are never shown in list */ + if (dottest && (!strcmp(dirbuf->d_name, ".") || + !strcmp(dirbuf->d_name, ".."))) { + dottest--; + continue; + } + + /* Don't show the selective enabling file + * unless version numbers are included */ + if (!strcasecomp(dirbuf->d_name, HT_DIR_ENABLE_FILE)) { + continue; + } + + /* Skip files beginning with a dot? */ + if ((no_dotfiles || !show_dotfiles) && *dirbuf->d_name == '.') { + continue; + } + + /* OK, make an lstat() and get a key ready. */ + *(pathname+pathend) = '\0'; + StrAllocCat(pathname, dirbuf->d_name); + if (HTStat(pathname, &file_info)) { + /* for VMS the failure here means the file is not readable... + we however continue to browse through the directory... */ + continue; + } + entry_info = (VMSEntryInfo *)malloc(sizeof(VMSEntryInfo)); + if (entry_info == NULL) + outofmem(__FILE__, "HTVMSBrowseDir"); + entry_info->type = 0; + entry_info->size = 0; + entry_info->date = 0; + entry_info->filename = 0; + entry_info->display = TRUE; + + /* Get the type */ + format = HTFileFormat(dirbuf->d_name, &encoding); + if(!strncmp(HTAtom_name(format), "application",11)) + { + cp = HTAtom_name(format) + 12; + if(!strncmp(cp,"x-",2)) + cp+=2; + } + else + cp = HTAtom_name(format); + StrAllocCopy(entry_info->type, cp); + + StrAllocCopy(entry_info->filename, dirbuf->d_name); + if ((file_info.st_mode & S_IFMT) == S_IFDIR) { + /* strip .DIR part... */ + char *dot; + dot = strstr(entry_info->filename, ".DIR"); + if (dot) + *dot = '\0'; + StrAllocCopy(entry_info->type, "Directory"); + } + + /* Get the date */ + { + char *t = (char *)ctime((CONST time_t *)&file_info.st_ctime); + *(t+24) = '\0'; + + StrAllocCopy(entry_info->date, (t+4)); + *((entry_info->date)+7) = '\0'; + if ((atoi((t+19))) < atoi(ThisYear)) + StrAllocCat(entry_info->date, (t+19)); + else { + StrAllocCat(entry_info->date, (t+11)); + *((entry_info->date)+12) = '\0'; + } + } + + /* Get the size */ + if ((file_info.st_mode & S_IFMT) != S_IFDIR) + entry_info->size = (unsigned int)file_info.st_size; + else + entry_info->size = 0; + + /* Now, update the BTree etc. */ + if(entry_info->display) + { + CTRACE(stderr,"Adding file to BTree: %s\n", + entry_info->filename); + HTBTree_add(bt, (VMSEntryInfo *)entry_info); + } + + } /* End while HTVMSreaddir() */ + + FREE(pathname); + HTVMSclosedir(dp); + + START(HTML_PRE); + /* + * Run through the BTree printing out in order + */ + { + HTBTElement * ele; + int i; + for (ele = HTBTree_next(bt, NULL); + ele != NULL; + ele = HTBTree_next(bt, ele)) + { + entry_info = (VMSEntryInfo *)HTBTree_object(ele); + + /* Output the date */ + if(entry_info->date) + { + PUTS(entry_info->date); + PUTS(" "); + } + else + PUTS(" * "); + + /* Output the type */ + if(entry_info->type) + { + for(i = 0; entry_info->type[i] != '\0' && i < 15; i++) + PUTC(entry_info->type[i]); + for(; i < 17; i++) + PUTC(' '); + + } + + /* Output the link for the name */ + HTDirEntry(target, tail, entry_info->filename); + PUTS(entry_info->filename); + END(HTML_A); + + /* Output the size */ + if(entry_info->size) + { + if(entry_info->size < 1024) + sprintf(string_buffer," %d bytes", + entry_info->size); + else + sprintf(string_buffer," %dKb", + entry_info->size/1024); + PUTS(string_buffer); + } + + PUTC('\n'); /* end of this entry */ + + free_VMSEntryInfo_struct_contents(entry_info); + } + } + + HTBTreeAndObject_free(bt); + + } /* End of both BTree loops */ + + /* + * Complete the output stream. + */ + END(HTML_PRE); + PUTS("\n"); + END(HTML_BODY); + PUTS("\n"); + END(HTML_HTML); + PUTS("\n"); + FREE(tail); + FREE_TARGET; + + return HT_LOADED; + +} /* End of directory reading section */ diff --git a/WWW/Library/Implementation/HTVMSUtils.h b/WWW/Library/Implementation/HTVMSUtils.h new file mode 100644 index 00000000..e055d672 --- /dev/null +++ b/WWW/Library/Implementation/HTVMSUtils.h @@ -0,0 +1,116 @@ +/* VMS specific routines + + */ + +#ifndef HTVMSUTIL_H +#define HTVMSUTIL_H + +#include <stat.h> + +extern BOOL HTVMSFileVersions; /* Include version numbers in listing? */ + +/* PUBLIC HTVMS_authSysPrv() +** CHECKS IF THIS PROCESS IS AUTHORIZED TO ENABLE SYSPRV +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** returns YES if SYSPRV is authorized +*/ +PUBLIC BOOL HTVMS_authSysPrv NOPARAMS; + + +/* PUBLIC HTVMS_enableSysPrv() +** ENABLES SYSPRV +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** +*/ +PUBLIC void HTVMS_enableSysPrv NOPARAMS; + + +/* PUBLIC HTVMS_disableSysPrv() +** DISABLES SYSPRV +** ON ENTRY: +** No arguments. +** +** ON EXIT: +** +*/ +PUBLIC void HTVMS_disableSysPrv NOPARAMS; + +/* PUBLIC HTVMS_checkAccess() +** CHECKS ACCESS TO FILE FOR CERTAIN USER +** ON ENTRY: +** FileName The file to be accessed +** UserName Name of the user to check access for +** +** ON EXIT: +** returns YES if access is allowed +** +*/ +PUBLIC BOOL HTVMS_checkAccess PARAMS(( + CONST char * FileName, + CONST char * UserName, + CONST char * Method)); + + +/* PUBLIC HTVMS_wwwName() +** CONVERTS VMS Name into WWW Name +** ON ENTRY: +** vmsname VMS file specification (NO NODE) +** +** ON EXIT: +** returns www file specification +** +** EXAMPLES: +** vmsname wwwname +** DISK$USER disk$user +** DISK$USER: /disk$user/ +** DISK$USER:[DUNS] /disk$user/duns +** DISK$USER:[DUNS.ECHO] /disk$user/duns/echo +** [DUNS] duns +** [DUNS.ECHO] duns/echo +** [DUNS.ECHO.-.TRANS] duns/echo/../trans +** [DUNS.ECHO.--.TRANS] duns/echo/../../trans +** [.DUNS] duns +** [.DUNS.ECHO] duns/echo +** [.DUNS.ECHO]TEST.COM duns/echo/test.com +** TEST.COM test.com +** +** +*/ +PUBLIC char * HTVMS_wwwName PARAMS(( + char * vmsname)); + +/* PUBLIC HTVMS_name() +** CONVERTS WWW name into a VMS name +** ON ENTRY: +** nn Node Name (optional) +** fn WWW file name +** +** ON EXIT: +** returns vms file specification +** +** Bug: Returns pointer to static -- non-reentrant +*/ +PUBLIC char * HTVMS_name PARAMS(( + CONST char * nn, + CONST char * fn)); + +PUBLIC int HTStat PARAMS(( + CONST char * filename, + stat_t * info)); + +PUBLIC int HTVMSBrowseDir PARAMS(( + CONST char * address, + HTParentAnchor * anchor, + HTFormat format_out, + HTStream * sink)); + +#endif /* not HTVMSUTIL_H */ +/* + + End of file HTVMSUtil.h. */ diff --git a/WWW/Library/Implementation/HTVMS_WaisProt.c b/WWW/Library/Implementation/HTVMS_WaisProt.c new file mode 100644 index 00000000..ee3a51c0 --- /dev/null +++ b/WWW/Library/Implementation/HTVMS_WaisProt.c @@ -0,0 +1,2501 @@ +/* HTVMS_WAISProt.c +** +** Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu) +** +** 31-May-1994 FM Initial version. +** +**----------------------------------------------------------------------*/ + +/* +** Routines originally from WProt.c -- FM +** +**----------------------------------------------------------------------*/ +/* WIDE AREA INFORMATION SERVER SOFTWARE: + No guarantees or restrictions. See the readme file for the full standard + disclaimer. + + 3.26.90 Harry Morris, morris@think.com + 3.30.90 Harry Morris + - removed chunk code from WAISSearchAPDU, + - added makeWAISQueryType1Query() and readWAISType1Query() which replace + makeWAISQueryTerms() and makeWAISQueryDocs(). + 4.11.90 HWM - generalized conditional includes (see c-dialect.h) + - renamed makeWAISType1Query() to makeWAISTextQuery() + renamed readWAISType1Query() to readWAISTextQuery() + 5.29.90 TS - fixed bug in makeWAISQueryDocs + added CSTFreeWAISFoo functions +*/ + +#define _C_WAIS_protocol_ + +/* This file implements the Z39.50 extensions required for WAIS +*/ + +#include "HTUtils.h" +#include "tcp.h" +#include "HTVMS_WaisUI.h" +#include "HTVMS_WaisProt.h" + +#include "LYLeaks.h" + + +/* very rough estimates of the size of an object */ +#define DefWAISInitResponseSize (size_t)200 +#define DefWAISSearchSize (size_t)3000 +#define DefWAISSearchResponseSize (size_t)6000 +#define DefWAISPresentSize (size_t)1000 +#define DefWAISPresentResponseSize (size_t)6000 +#define DefWAISDocHeaderSize (size_t)500 +#define DefWAISShortHeaderSize (size_t)200 +#define DefWAISLongHeaderSize (size_t)800 +#define DefWAISDocTextSize (size_t)6000 +#define DefWAISDocHeadlineSize (size_t)500 +#define DefWAISDocCodeSize (size_t)500 + +#define RESERVE_SPACE_FOR_WAIS_HEADER(len) \ + if (*len > 0) \ + *len -= header_len; + +/*----------------------------------------------------------------------*/ + +static unsigned long userInfoTagSize _AP((data_tag tag, + unsigned long length)); + +static unsigned long +userInfoTagSize(tag,length) +data_tag tag; +unsigned long length; +/* return the number of bytes required to write the user info tag and + length + */ +{ + unsigned long size; + + /* calculate bytes required to represent tag. max tag is 16K */ + size = writtenCompressedIntSize(tag); + size += writtenCompressedIntSize(length); + + return(size); +} + +/*----------------------------------------------------------------------*/ + +static char* writeUserInfoHeader _AP((data_tag tag,long infoSize, + long estHeaderSize,char* buffer, + long* len)); + +static char* +writeUserInfoHeader(tag,infoSize,estHeaderSize,buffer,len) +data_tag tag; +long infoSize; +long estHeaderSize; +char* buffer; +long* len; +/* write the tag and size, making sure the info fits. return the true end + of the info (after adjustment) note that the argument infoSize includes + estHeaderSize. Note that the argument len is the number of bytes remaining + in the buffer. Since we write the tag and size at the begining of the + buffer (in space that we reserved) we don't want to pass len the calls which + do that writing. + */ +{ + long dummyLen = 100; /* plenty of space for a tag and size */ + char* buf = buffer; + long realSize = infoSize - estHeaderSize; + long realHeaderSize = userInfoTagSize(tag,realSize); + + if (buffer == NULL || *len == 0) + return(NULL); + + /* write the tag */ + buf = writeTag(tag,buf,&dummyLen); + + /* see if the if the header size was correct. if not, + we have to shift the info to fit the real header size */ + if (estHeaderSize != realHeaderSize) + { /* make sure there is enough space */ + CHECK_FOR_SPACE_LEFT(realHeaderSize - estHeaderSize,len); + memmove(buffer + realHeaderSize,buffer + estHeaderSize,(size_t)(realSize)); + } + + /* write the size */ + writeCompressedInteger(realSize,buf,&dummyLen); + + /* return the true end of buffer */ + return(buffer + realHeaderSize + realSize); +} + +/*----------------------------------------------------------------------*/ + +static char* readUserInfoHeader _AP((data_tag* tag,unsigned long* num, + char* buffer)); + +static char* +readUserInfoHeader(tag,num,buffer) +data_tag* tag; +unsigned long* num; +char* buffer; +/* read the tag and size */ +{ + char* buf = buffer; + buf = readTag(tag,buf); + buf = readCompressedInteger(num,buf); + return(buf); +} + +/*----------------------------------------------------------------------*/ + +WAISInitResponse* +makeWAISInitResponse(chunkCode, + chunkIDLen, + chunkMarker, + highlightMarker, + deHighlightMarker, + newLineChars) +long chunkCode; +long chunkIDLen; +char* chunkMarker; +char* highlightMarker; +char* deHighlightMarker; +char* newLineChars; +/* create a WAIS init response object */ +{ + WAISInitResponse* init = (WAISInitResponse*)s_malloc((size_t)sizeof(WAISInitResponse)); + + init->ChunkCode = chunkCode; /* note: none are copied! */ + init->ChunkIDLength = chunkIDLen; + init->ChunkMarker = chunkMarker; + init->HighlightMarker = highlightMarker; + init->DeHighlightMarker = deHighlightMarker; + init->NewlineCharacters = newLineChars; + + return(init); +} + +/*----------------------------------------------------------------------*/ + +void +freeWAISInitResponse(init) +WAISInitResponse* init; +/* free an object made with makeWAISInitResponse */ +{ + s_free(init->ChunkMarker); + s_free(init->HighlightMarker); + s_free(init->DeHighlightMarker); + s_free(init->NewlineCharacters); + s_free(init); +} + +/*----------------------------------------------------------------------*/ + +char* +writeInitResponseInfo(init,buffer,len) +InitResponseAPDU* init; +char* buffer; +long* len; +/* write an init response object */ +{ + unsigned long header_len = userInfoTagSize(DT_UserInformationLength, + DefWAISInitResponseSize); + char* buf = buffer + header_len; + WAISInitResponse* info = (WAISInitResponse*)init->UserInformationField; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeNum(info->ChunkCode,DT_ChunkCode,buf,len); + buf = writeNum(info->ChunkIDLength,DT_ChunkIDLength,buf,len); + buf = writeString(info->ChunkMarker,DT_ChunkMarker,buf,len); + buf = writeString(info->HighlightMarker,DT_HighlightMarker,buf,len); + buf = writeString(info->DeHighlightMarker,DT_DeHighlightMarker,buf,len); + buf = writeString(info->NewlineCharacters,DT_NewlineCharacters,buf,len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_UserInformationLength,size,header_len,buffer,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readInitResponseInfo(info,buffer) +void** info; +char* buffer; +/* read an init response object */ +{ + char* buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag; + long chunkCode,chunkIDLen; + char* chunkMarker = NULL; + char* highlightMarker = NULL; + char* deHighlightMarker = NULL; + char* newLineChars = NULL; + + chunkCode = chunkIDLen = UNUSED; + + buf = readUserInfoHeader(&tag,&size,buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) + { data_tag tag = peekTag(buf); + switch (tag) + { case DT_ChunkCode: + buf = readNum(&chunkCode,buf); + break; + case DT_ChunkIDLength: + buf = readNum(&chunkIDLen,buf); + break; + case DT_ChunkMarker: + buf = readString(&chunkMarker,buf); + break; + case DT_HighlightMarker: + buf = readString(&highlightMarker,buf); + break; + case DT_DeHighlightMarker: + buf = readString(&deHighlightMarker,buf); + break; + case DT_NewlineCharacters: + buf = readString(&newLineChars,buf); + break; + default: + s_free(highlightMarker); + s_free(deHighlightMarker); + s_free(newLineChars); + REPORT_READ_ERROR(buf); + break; + } + } + + *info = (void *)makeWAISInitResponse(chunkCode,chunkIDLen,chunkMarker, + highlightMarker,deHighlightMarker, + newLineChars); + return(buf); +} + +/*----------------------------------------------------------------------*/ + +WAISSearch* +makeWAISSearch(seedWords, + docs, + textList, + dateFactor, + beginDateRange, + endDateRange, + maxDocsRetrieved) +char* seedWords; +DocObj** docs; +char** textList; +long dateFactor; +char* beginDateRange; +char* endDateRange; +long maxDocsRetrieved; + +/* create a type 3 query object */ +{ + WAISSearch* query = (WAISSearch*)s_malloc((size_t)sizeof(WAISSearch)); + + query->SeedWords = seedWords; /* not copied! */ + query->Docs = docs; /* not copied! */ + query->TextList = textList; /* not copied! */ + query->DateFactor = dateFactor; + query->BeginDateRange = beginDateRange; + query->EndDateRange = endDateRange; + query->MaxDocumentsRetrieved = maxDocsRetrieved; + + return(query); +} + +/*----------------------------------------------------------------------*/ + +void +freeWAISSearch(query) +WAISSearch* query; + +/* destroy an object made with makeWAISSearch() */ +{ + void* ptr = NULL; + long i; + + s_free(query->SeedWords); + + if (query->Docs != NULL) + for (i = 0,ptr = (void *)query->Docs[i]; ptr != NULL; ptr = (void *)query->Docs[++i]) + freeDocObj((DocObj*)ptr); + s_free(query->Docs); + + if (query->TextList != NULL) /* XXX revisit when textlist is fully defined */ + for (i = 0,ptr = (void *)query->TextList[i]; ptr != NULL; ptr = (void *)query->TextList[++i]) + s_free(ptr); + s_free(query->TextList); + + s_free(query->BeginDateRange); + s_free(query->EndDateRange); + s_free(query); +} + +/*----------------------------------------------------------------------*/ + +DocObj* +makeDocObjUsingWholeDocument(docID,type) +any* docID; +char* type; + +/* construct a document object using byte chunks - only for use by + servers */ +{ + DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(DocObj)); + doc->DocumentID = docID; /* not copied! */ + doc->Type = type; /* not copied! */ + doc->ChunkCode = CT_document; + return(doc); +} + +/*----------------------------------------------------------------------*/ + +DocObj* +makeDocObjUsingLines(docID,type,start,end) +any* docID; +char* type; +long start; +long end; + +/* construct a document object using line chunks - only for use by + servers */ +{ + DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(DocObj)); + doc->ChunkCode = CT_line; + doc->DocumentID = docID; /* not copied */ + doc->Type = type; /* not copied! */ + doc->ChunkStart.Pos = start; + doc->ChunkEnd.Pos = end; + return(doc); +} + +/*----------------------------------------------------------------------*/ + +DocObj* +makeDocObjUsingBytes(docID,type,start,end) +any* docID; +char* type; +long start; +long end; + +/* construct a document object using byte chunks - only for use by + servers */ +{ + DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(DocObj)); + doc->ChunkCode = CT_byte; + doc->DocumentID = docID; /* not copied */ + doc->Type = type; /* not copied! */ + doc->ChunkStart.Pos = start; + doc->ChunkEnd.Pos = end; + return(doc); +} + +/*----------------------------------------------------------------------*/ + +DocObj* +makeDocObjUsingParagraphs(docID,type,start,end) +any* docID; +char* type; +any* start; +any* end; + +/* construct a document object using byte chunks - only for use by + servers */ +{ + DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(DocObj)); + doc->ChunkCode = CT_paragraph; + doc->DocumentID = docID; /* not copied */ + doc->Type = type; + doc->ChunkStart.ID = start; + doc->ChunkEnd.ID = end; + return(doc); +} + +/*----------------------------------------------------------------------*/ + +void +freeDocObj(doc) +DocObj* doc; + +/* free a docObj */ +{ + freeAny(doc->DocumentID); + s_free(doc->Type); + if (doc->ChunkCode == CT_paragraph) + { freeAny(doc->ChunkStart.ID); + freeAny(doc->ChunkEnd.ID); + } + s_free(doc); +} + +/*----------------------------------------------------------------------*/ + +static char* writeDocObj _AP((DocObj* doc,char* buffer,long* len)); + +static char* +writeDocObj(doc,buffer,len) +DocObj* doc; +char* buffer; +long* len; + +/* write as little as we can about the doc obj */ +{ + char* buf = buffer; + + /* we alwasy have to write the id, but its tag depends on if its a chunk */ + if (doc->ChunkCode == CT_document) + buf = writeAny(doc->DocumentID,DT_DocumentID,buf,len); + else + buf = writeAny(doc->DocumentID,DT_DocumentIDChunk,buf,len); + + if (doc->Type != NULL) + buf = writeString(doc->Type,DT_TYPE,buf,len); + + switch (doc->ChunkCode) + { case CT_document: + /* do nothing - there is no chunk data */ + break; + case CT_byte: + case CT_line: + buf = writeNum(doc->ChunkCode,DT_ChunkCode,buf,len); + buf = writeNum(doc->ChunkStart.Pos,DT_ChunkStartID,buf,len); + buf = writeNum(doc->ChunkEnd.Pos,DT_ChunkEndID,buf,len); + break; + case CT_paragraph: + buf = writeNum(doc->ChunkCode,DT_ChunkCode,buf,len); + buf = writeAny(doc->ChunkStart.ID,DT_ChunkStartID,buf,len); + buf = writeAny(doc->ChunkEnd.ID,DT_ChunkEndID,buf,len); + break; + default: + panic("Implementation error: unknown chuck type %ld", + doc->ChunkCode); + break; + } + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +static char* readDocObj _AP((DocObj** doc,char* buffer)); + +static char* +readDocObj(doc,buffer) +DocObj** doc; +char* buffer; + +/* read whatever we have about the new document */ +{ + char* buf = buffer; + data_tag tag; + + *doc = (DocObj*)s_malloc((size_t)sizeof(DocObj)); + + tag = peekTag(buf); + buf = readAny(&((*doc)->DocumentID),buf); + + if (tag == DT_DocumentID) + { (*doc)->ChunkCode = CT_document; + tag = peekTag(buf); + if (tag == DT_TYPE) /* XXX depends on DT_TYPE != what comes next */ + buf = readString(&((*doc)->Type),buf); + /* ChunkStart and ChunkEnd are undefined */ + } + else if (tag == DT_DocumentIDChunk) + { boolean readParagraphs = false; /* for cleanup */ + tag = peekTag(buf); + if (tag == DT_TYPE) /* XXX depends on DT_TYPE != CT_FOO */ + buf = readString(&((*doc)->Type),buf); + buf = readNum(&((*doc)->ChunkCode),buf); + switch ((*doc)->ChunkCode) + { case CT_byte: + case CT_line: + buf = readNum(&((*doc)->ChunkStart.Pos),buf); + buf = readNum(&((*doc)->ChunkEnd.Pos),buf); + break; + case CT_paragraph: + readParagraphs = true; + buf = readAny(&((*doc)->ChunkStart.ID),buf); + buf = readAny(&((*doc)->ChunkEnd.ID),buf); + break; + default: + freeAny((*doc)->DocumentID); + if (readParagraphs) + { freeAny((*doc)->ChunkStart.ID); + freeAny((*doc)->ChunkEnd.ID); + } + s_free(doc); + REPORT_READ_ERROR(buf); + break; + } + } + else + { freeAny((*doc)->DocumentID); + s_free(*doc); + REPORT_READ_ERROR(buf); + } + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +writeSearchInfo(query,buffer,len) +SearchAPDU* query; +char* buffer; +long* len; + +/* write out a WAIS query (type 1 or 3) */ +{ + if (strcmp(query->QueryType,QT_TextRetrievalQuery) == 0) + { return(writeAny((any*)query->Query,DT_Query,buffer,len)); + } + else + { unsigned long header_len = userInfoTagSize(DT_UserInformationLength, + DefWAISSearchSize); + char* buf = buffer + header_len; + WAISSearch* info = (WAISSearch*)query->Query; + unsigned long size; + long i; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeString(info->SeedWords,DT_SeedWords,buf,len); + + if (info->Docs != NULL) + { for (i = 0; info->Docs[i] != NULL; i++) + { buf = writeDocObj(info->Docs[i],buf,len); + } + } + + /* XXX text list */ + + buf = writeNum(info->DateFactor,DT_DateFactor,buf,len); + buf = writeString(info->BeginDateRange,DT_BeginDateRange,buf,len); + buf = writeString(info->EndDateRange,DT_EndDateRange,buf,len); + buf = writeNum(info->MaxDocumentsRetrieved,DT_MaxDocumentsRetrieved,buf,len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_UserInformationLength,size,header_len,buffer,len); + + return(buf); + } +} + +/*----------------------------------------------------------------------*/ + +char* +readSearchInfo(info,buffer) +void** info; +char* buffer; + +/* read a WAIS query (type 1 or 3) */ +{ + data_tag type = peekTag(buffer); + if (type == DT_Query) /* this is a type 1 query */ + { char* buf = buffer; + any* query = NULL; + buf = readAny(&query,buf); + *info = (void *)query; + return(buf); + } + else /* a type 3 query */ + { char* buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag; + char* seedWords = NULL; + char* beginDateRange = NULL; + char* endDateRange = NULL; + long dateFactor,maxDocsRetrieved; + char** textList = NULL; + DocObj** docIDs = NULL; + DocObj* doc = NULL; + long docs = 0; + long i; + void* ptr = NULL; + + dateFactor = maxDocsRetrieved = UNUSED; + + buf = readUserInfoHeader(&tag,&size,buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) + { data_tag tag = peekTag(buf); + switch (tag) + { case DT_SeedWords: + buf = readString(&seedWords,buf); + break; + case DT_DocumentID: + case DT_DocumentIDChunk: + if (docIDs == NULL) /* create a new doc list */ + { docIDs = (DocObj**)s_malloc((size_t)sizeof(DocObj*) * 2); + } + else /* grow the doc list */ + { docIDs = (DocObj**)s_realloc((char*)docIDs,(size_t)(sizeof(DocObj*) * (docs + 2))); + } + buf = readDocObj(&doc,buf); + if (buf == NULL) + { s_free(seedWords); + s_free(beginDateRange); + s_free(endDateRange); + if (docIDs != NULL) + for (i = 0,ptr = (void *)docIDs[i]; ptr != NULL; ptr = (void *)docIDs[++i]) + freeDocObj((DocObj*)ptr); + s_free(docIDs); + /* XXX should also free textlist when it is fully defined */ + } + RETURN_ON_NULL(buf); + docIDs[docs++] = doc; /* put it in the list */ + docIDs[docs] = NULL; + break; + case DT_TextList: + /* XXX */ + break; + case DT_DateFactor: + buf = readNum(&dateFactor,buf); + break; + case DT_BeginDateRange: + buf = readString(&beginDateRange,buf); + break; + case DT_EndDateRange: + buf = readString(&endDateRange,buf); + break; + case DT_MaxDocumentsRetrieved: + buf = readNum(&maxDocsRetrieved,buf); + break; + default: + s_free(seedWords); + s_free(beginDateRange); + s_free(endDateRange); + if (docIDs != NULL) + for (i = 0,ptr = (void *)docIDs[i]; ptr != NULL; ptr = (void *)docIDs[++i]) + freeDocObj((DocObj*)ptr); + s_free(docIDs); + /* XXX should also free textlist when it is fully defined */ + REPORT_READ_ERROR(buf); + break; + } + } + + *info = (void *)makeWAISSearch(seedWords,docIDs,textList, + dateFactor,beginDateRange,endDateRange, + maxDocsRetrieved); + return(buf); + } +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentHeader* +makeWAISDocumentHeader(docID, + versionNumber, + score, + bestMatch, + docLen, + lines, + types, + source, + date, + headline, + originCity) +any* docID; +long versionNumber; +long score; +long bestMatch; +long docLen; +long lines; +char** types; +char* source; +char* date; +char* headline; +char* originCity; + +/* construct a standard document header, note that no fields are copied! + if the application needs to save these fields, it should copy them, + or set the field in this object to NULL before freeing it. + */ +{ + WAISDocumentHeader* header = + (WAISDocumentHeader*)s_malloc((size_t)sizeof(WAISDocumentHeader)); + + header->DocumentID = docID; + header->VersionNumber = versionNumber; + header->Score = score; + header->BestMatch = bestMatch; + header->DocumentLength = docLen; + header->Lines = lines; + header->Types = types; + header->Source = source; + header->Date = date; + header->Headline = headline; + header->OriginCity = originCity; + + return(header); +} + +/*----------------------------------------------------------------------*/ + +void +freeWAISDocumentHeader(header) +WAISDocumentHeader* header; + +{ + freeAny(header->DocumentID); + doList((void**)header->Types,fs_free); /* can't use the macro here ! */ + s_free(header->Types); + s_free(header->Source); + s_free(header->Date); + s_free(header->Headline); + s_free(header->OriginCity); + s_free(header); +} + +/*----------------------------------------------------------------------*/ + +char* +writeWAISDocumentHeader(header,buffer,len) +WAISDocumentHeader* header; +char* buffer; +long* len; +{ + unsigned long header_len = userInfoTagSize(DT_DocumentHeaderGroup , + DefWAISDocHeaderSize); + char* buf = buffer + header_len; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(header->DocumentID,DT_DocumentID,buf,len); + buf = writeNum(header->VersionNumber,DT_VersionNumber,buf,len); + buf = writeNum(header->Score,DT_Score,buf,len); + buf = writeNum(header->BestMatch,DT_BestMatch,buf,len); + buf = writeNum(header->DocumentLength,DT_DocumentLength,buf,len); + buf = writeNum(header->Lines,DT_Lines,buf,len); + if (header->Types != NULL) + { long size; + char* ptr = NULL; + long i; + buf = writeTag(DT_TYPE_BLOCK,buf,len); + for (i = 0,size = 0,ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i]) + { long typeSize = strlen(ptr); + size += writtenTagSize(DT_TYPE); + size += writtenCompressedIntSize(typeSize); + size += typeSize; + } + buf = writeCompressedInteger((unsigned long)size,buf,len); + for (i = 0,ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i]) + buf = writeString(ptr,DT_TYPE,buf,len); + } + buf = writeString(header->Source,DT_Source,buf,len); + buf = writeString(header->Date,DT_Date,buf,len); + buf = writeString(header->Headline,DT_Headline,buf,len); + buf = writeString(header->OriginCity,DT_OriginCity,buf,len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentHeaderGroup,size,header_len,buffer,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readWAISDocumentHeader(header,buffer) +WAISDocumentHeader** header; +char* buffer; +{ + char* buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag; + any* docID = NULL; + long versionNumber,score,bestMatch,docLength,lines; + char** types = NULL; + char *source = NULL; + char *date = NULL; + char *headline = NULL; + char *originCity = NULL; + + versionNumber = score = bestMatch = docLength = lines = UNUSED; + + buf = readUserInfoHeader(&tag,&size,buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) + { data_tag tag = peekTag(buf); + switch (tag) + { case DT_DocumentID: + buf = readAny(&docID,buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber,buf); + break; + case DT_Score: + buf = readNum(&score,buf); + break; + case DT_BestMatch: + buf = readNum(&bestMatch,buf); + break; + case DT_DocumentLength: + buf = readNum(&docLength,buf); + break; + case DT_Lines: + buf = readNum(&lines,buf); + break; + case DT_TYPE_BLOCK: + { unsigned long size = -1; + long numTypes = 0; + buf = readTag(&tag,buf); + buf = readCompressedInteger(&size,buf); + while (size > 0) + { char* type = NULL; + char* originalBuf = buf; + buf = readString(&type,buf); + types = (char**)s_realloc(types,(size_t)(sizeof(char*) * (numTypes + 2))); + types[numTypes++] = type; + types[numTypes] = NULL; + size -= (buf - originalBuf); + } + } + case DT_Source: + buf = readString(&source,buf); + break; + case DT_Date: + buf = readString(&date,buf); + break; + case DT_Headline: + buf = readString(&headline,buf); + break; + case DT_OriginCity: + buf = readString(&originCity,buf); + break; + default: + freeAny(docID); + s_free(source); + s_free(date); + s_free(headline); + s_free(originCity); + REPORT_READ_ERROR(buf); + break; + } + } + + *header = makeWAISDocumentHeader(docID,versionNumber,score,bestMatch, + docLength,lines,types,source,date,headline, + originCity); + return(buf); +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentShortHeader* +makeWAISDocumentShortHeader(docID, + versionNumber, + score, + bestMatch, + docLen, + lines) +any* docID; +long versionNumber; +long score; +long bestMatch; +long docLen; +long lines; +/* construct a short document header, note that no fields are copied! + if the application needs to save these fields, it should copy them, + or set the field in this object to NULL before freeing it. + */ +{ + WAISDocumentShortHeader* header = + (WAISDocumentShortHeader*)s_malloc((size_t)sizeof(WAISDocumentShortHeader)); + + header->DocumentID = docID; + header->VersionNumber = versionNumber; + header->Score = score; + header->BestMatch = bestMatch; + header->DocumentLength = docLen; + header->Lines = lines; + + return(header); +} + +/*----------------------------------------------------------------------*/ + +void +freeWAISDocumentShortHeader(header) +WAISDocumentShortHeader* header; +{ + freeAny(header->DocumentID); + s_free(header); +} + +/*----------------------------------------------------------------------*/ + +char* +writeWAISDocumentShortHeader(header,buffer,len) +WAISDocumentShortHeader* header; +char* buffer; +long* len; +{ + unsigned long header_len = userInfoTagSize(DT_DocumentShortHeaderGroup , + DefWAISShortHeaderSize); + char* buf = buffer + header_len; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(header->DocumentID,DT_DocumentID,buf,len); + buf = writeNum(header->VersionNumber,DT_VersionNumber,buf,len); + buf = writeNum(header->Score,DT_Score,buf,len); + buf = writeNum(header->BestMatch,DT_BestMatch,buf,len); + buf = writeNum(header->DocumentLength,DT_DocumentLength,buf,len); + buf = writeNum(header->Lines,DT_Lines,buf,len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentShortHeaderGroup,size,header_len,buffer,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readWAISDocumentShortHeader(header,buffer) +WAISDocumentShortHeader** header; +char* buffer; +{ + char* buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag; + any* docID = NULL; + long versionNumber,score,bestMatch,docLength,lines; + + versionNumber = score = bestMatch = docLength = lines = UNUSED; + + buf = readUserInfoHeader(&tag,&size,buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) + { data_tag tag = peekTag(buf); + switch (tag) + { case DT_DocumentID: + buf = readAny(&docID,buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber,buf); + break; + case DT_Score: + buf = readNum(&score,buf); + break; + case DT_BestMatch: + buf = readNum(&bestMatch,buf); + break; + case DT_DocumentLength: + buf = readNum(&docLength,buf); + break; + case DT_Lines: + buf = readNum(&lines,buf); + break; + default: + freeAny(docID); + REPORT_READ_ERROR(buf); + break; + } + } + + *header = makeWAISDocumentShortHeader(docID,versionNumber,score,bestMatch, + docLength,lines); + return(buf); +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentLongHeader* +makeWAISDocumentLongHeader(docID, + versionNumber, + score, + bestMatch, + docLen, + lines, + types, + source, + date, + headline, + originCity, + stockCodes, + companyCodes, + industryCodes) +any* docID; +long versionNumber; +long score; +long bestMatch; +long docLen; +long lines; +char** types; +char* source; +char* date; +char* headline; +char* originCity; +char* stockCodes; +char* companyCodes; +char* industryCodes; +/* construct a long document header, note that no fields are copied! + if the application needs to save these fields, it should copy them, + or set the field in this object to NULL before freeing it. + */ +{ + WAISDocumentLongHeader* header = + (WAISDocumentLongHeader*)s_malloc((size_t)sizeof(WAISDocumentLongHeader)); + + header->DocumentID = docID; + header->VersionNumber = versionNumber; + header->Score = score; + header->BestMatch = bestMatch; + header->DocumentLength = docLen; + header->Lines = lines; + header->Types = types; + header->Source = source; + header->Date = date; + header->Headline = headline; + header->OriginCity = originCity; + header->StockCodes = stockCodes; + header->CompanyCodes = companyCodes; + header->IndustryCodes = industryCodes; + + return(header); +} + +/*----------------------------------------------------------------------*/ + +void +freeWAISDocumentLongHeader(header) +WAISDocumentLongHeader* header; +{ + freeAny(header->DocumentID); + doList((void**)header->Types,fs_free); /* can't use the macro here! */ + s_free(header->Source); + s_free(header->Date); + s_free(header->Headline); + s_free(header->OriginCity); + s_free(header->StockCodes); + s_free(header->CompanyCodes); + s_free(header->IndustryCodes); + s_free(header); +} + +/*----------------------------------------------------------------------*/ + +char* +writeWAISDocumentLongHeader(header,buffer,len) +WAISDocumentLongHeader* header; +char* buffer; +long* len; +{ + unsigned long header_len = userInfoTagSize(DT_DocumentLongHeaderGroup , + DefWAISLongHeaderSize); + char* buf = buffer + header_len; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(header->DocumentID,DT_DocumentID,buf,len); + buf = writeNum(header->VersionNumber,DT_VersionNumber,buf,len); + buf = writeNum(header->Score,DT_Score,buf,len); + buf = writeNum(header->BestMatch,DT_BestMatch,buf,len); + buf = writeNum(header->DocumentLength,DT_DocumentLength,buf,len); + buf = writeNum(header->Lines,DT_Lines,buf,len); + if (header->Types != NULL) + { long size; + char* ptr = NULL; + long i; + buf = writeTag(DT_TYPE_BLOCK,buf,len); + for (i = 0,size = 0,ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i]) + { long typeSize = strlen(ptr); + size += writtenTagSize(DT_TYPE); + size += writtenCompressedIntSize(typeSize); + size += typeSize; + } + buf = writeCompressedInteger((unsigned long)size,buf,len); + for (i = 0,ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i]) + buf = writeString(ptr,DT_TYPE,buf,len); + } + buf = writeString(header->Source,DT_Source,buf,len); + buf = writeString(header->Date,DT_Date,buf,len); + buf = writeString(header->Headline,DT_Headline,buf,len); + buf = writeString(header->OriginCity,DT_OriginCity,buf,len); + buf = writeString(header->StockCodes,DT_StockCodes,buf,len); + buf = writeString(header->CompanyCodes,DT_CompanyCodes,buf,len); + buf = writeString(header->IndustryCodes,DT_IndustryCodes,buf,len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentLongHeaderGroup,size,header_len,buffer,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readWAISDocumentLongHeader(header,buffer) +WAISDocumentLongHeader** header; +char* buffer; +{ + char* buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag; + any* docID; + long versionNumber,score,bestMatch,docLength,lines; + char **types; + char *source,*date,*headline,*originCity,*stockCodes,*companyCodes,*industryCodes; + + docID = NULL; + versionNumber = score = bestMatch = docLength = lines = UNUSED; + types = NULL; + source = date = headline = originCity = stockCodes = companyCodes = industryCodes = NULL; + + buf = readUserInfoHeader(&tag,&size,buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) + { data_tag tag = peekTag(buf); + switch (tag) + { case DT_DocumentID: + buf = readAny(&docID,buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber,buf); + break; + case DT_Score: + buf = readNum(&score,buf); + break; + case DT_BestMatch: + buf = readNum(&bestMatch,buf); + break; + case DT_DocumentLength: + buf = readNum(&docLength,buf); + break; + case DT_Lines: + buf = readNum(&lines,buf); + break; + case DT_TYPE_BLOCK: + { unsigned long size = -1; + long numTypes = 0; + buf = readTag(&tag,buf); + readCompressedInteger(&size,buf); + while (size > 0) + { char* type = NULL; + char* originalBuf = buf; + buf = readString(&type,buf); + types = (char**)s_realloc(types,(size_t)(sizeof(char*) * (numTypes + 2))); + types[numTypes++] = type; + types[numTypes] = NULL; + size -= (buf - originalBuf); + } + } + case DT_Source: + buf = readString(&source,buf); + break; + case DT_Date: + buf = readString(&date,buf); + break; + case DT_Headline: + buf = readString(&headline,buf); + break; + case DT_OriginCity: + buf = readString(&originCity,buf); + break; + case DT_StockCodes: + buf = readString(&stockCodes,buf); + break; + case DT_CompanyCodes: + buf = readString(&companyCodes,buf); + break; + case DT_IndustryCodes: + buf = readString(&industryCodes,buf); + break; + default: + freeAny(docID); + s_free(source); + s_free(date); + s_free(headline); + s_free(originCity); + s_free(stockCodes); + s_free(companyCodes); + s_free(industryCodes); + REPORT_READ_ERROR(buf); + break; + } + } + + *header = makeWAISDocumentLongHeader(docID,versionNumber,score,bestMatch, + docLength,lines,types,source,date,headline, + originCity,stockCodes,companyCodes, + industryCodes); + return(buf); +} + +/*----------------------------------------------------------------------*/ + +WAISSearchResponse* +makeWAISSearchResponse(seedWordsUsed, + docHeaders, + shortHeaders, + longHeaders, + text, + headlines, + codes, + diagnostics) +char* seedWordsUsed; +WAISDocumentHeader** docHeaders; +WAISDocumentShortHeader** shortHeaders; +WAISDocumentLongHeader** longHeaders; +WAISDocumentText** text; +WAISDocumentHeadlines** headlines; +WAISDocumentCodes** codes; +diagnosticRecord** diagnostics; +{ + WAISSearchResponse* response = (WAISSearchResponse*)s_malloc((size_t)sizeof(WAISSearchResponse)); + + response->SeedWordsUsed = seedWordsUsed; + response->DocHeaders = docHeaders; + response->ShortHeaders = shortHeaders; + response->LongHeaders = longHeaders; + response->Text = text; + response->Headlines = headlines; + response->Codes = codes; + response->Diagnostics = diagnostics; + + return(response); +} + +/*----------------------------------------------------------------------*/ + +void +freeWAISSearchResponse(response) +WAISSearchResponse* response; +{ + void* ptr = NULL; + long i; + + s_free(response->SeedWordsUsed); + + if (response->DocHeaders != NULL) + for (i = 0,ptr = (void *)response->DocHeaders[i]; ptr != NULL; ptr = (void *)response->DocHeaders[++i]) + freeWAISDocumentHeader((WAISDocumentHeader*)ptr); + s_free(response->DocHeaders); + + if (response->ShortHeaders != NULL) + for (i = 0,ptr = (void *)response->ShortHeaders[i]; ptr != NULL; ptr = (void *)response->ShortHeaders[++i]) + freeWAISDocumentShortHeader((WAISDocumentShortHeader*)ptr); + s_free(response->ShortHeaders); + + if (response->LongHeaders != NULL) + for (i = 0,ptr = (void *)response->LongHeaders[i]; ptr != NULL; ptr = (void *)response->LongHeaders[++i]) + freeWAISDocumentLongHeader((WAISDocumentLongHeader*)ptr); + s_free(response->LongHeaders); + + if (response->Text != NULL) + for (i = 0,ptr = (void *)response->Text[i]; ptr != NULL; ptr = (void *)response->Text[++i]) + freeWAISDocumentText((WAISDocumentText*)ptr); + s_free(response->Text); + + if (response->Headlines != NULL) + for (i = 0,ptr = (void *)response->Headlines[i]; ptr != NULL; ptr = (void *)response->Headlines[++i]) + freeWAISDocumentHeadlines((WAISDocumentHeadlines*)ptr); + s_free(response->Headlines); + + if (response->Codes != NULL) + for (i = 0,ptr = (void *)response->Codes[i]; ptr != NULL; ptr = (void *)response->Codes[++i]) + freeWAISDocumentCodes((WAISDocumentCodes*)ptr); + s_free(response->Codes); + + if (response->Diagnostics != NULL) + for (i = 0,ptr = (void *)response->Diagnostics[i]; ptr != NULL; ptr = (void *)response->Diagnostics[++i]) + freeDiag((diagnosticRecord*)ptr); + s_free(response->Diagnostics); + + s_free(response); +} + +/*----------------------------------------------------------------------*/ + +char* +writeSearchResponseInfo(query,buffer,len) +SearchResponseAPDU* query; +char* buffer; +long* len; +{ + unsigned long header_len = userInfoTagSize(DT_UserInformationLength, + DefWAISSearchResponseSize); + char* buf = buffer + header_len; + WAISSearchResponse* info = (WAISSearchResponse*)query->DatabaseDiagnosticRecords; + unsigned long size; + void* header = NULL; + long i; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeString(info->SeedWordsUsed,DT_SeedWordsUsed,buf,len); + + /* write out all the headers */ + if (info->DocHeaders != NULL) + { for (i = 0,header = (void *)info->DocHeaders[i]; header != NULL; header = (void *)info->DocHeaders[++i]) + buf = writeWAISDocumentHeader((WAISDocumentHeader*)header,buf,len); + } + + if (info->ShortHeaders != NULL) + { for (i = 0,header = (void *)info->ShortHeaders[i]; header != NULL; header = (void *)info->ShortHeaders[++i]) + buf = writeWAISDocumentShortHeader((WAISDocumentShortHeader*)header,buf,len); + } + + if (info->LongHeaders != NULL) + { for (i = 0,header = (void *)info->LongHeaders[i]; header != NULL; header = (void *)info->LongHeaders[++i]) + buf = writeWAISDocumentLongHeader((WAISDocumentLongHeader*)header,buf,len); + } + + if (info->Text != NULL) + { for (i = 0,header = (void *)info->Text[i]; header != NULL; header = (void *)info->Text[++i]) + buf = writeWAISDocumentText((WAISDocumentText*)header,buf,len); + } + + if (info->Headlines != NULL) + { for (i = 0,header = (void *)info->Headlines[i]; header != NULL; header = (void *)info->Headlines[++i]) + buf = writeWAISDocumentHeadlines((WAISDocumentHeadlines*)header,buf,len); + } + + if (info->Codes != NULL) + { for (i = 0,header = (void *)info->Codes[i]; header != NULL;header = (void *)info->Codes[++i]) + buf = writeWAISDocumentCodes((WAISDocumentCodes*)header,buf,len); + } + + if (info->Diagnostics != NULL) + { for (i = 0, header = (void *)info->Diagnostics[i]; header != NULL; header = (void *)info->Diagnostics[++i]) + buf = writeDiag((diagnosticRecord*)header,buf,len); + } + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_UserInformationLength,size,header_len,buffer,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +static void +cleanUpWaisSearchResponse _AP((char* buf,char* seedWordsUsed, + WAISDocumentHeader** docHeaders, + WAISDocumentShortHeader** shortHeaders, + WAISDocumentLongHeader** longHeaders, + WAISDocumentText** text, + WAISDocumentHeadlines** headlines, + WAISDocumentCodes** codes, + diagnosticRecord**diags)); + +static void +cleanUpWaisSearchResponse (buf,seedWordsUsed,docHeaders,shortHeaders, + longHeaders,text,headlines,codes,diags) +char* buf; +char* seedWordsUsed; +WAISDocumentHeader** docHeaders; +WAISDocumentShortHeader** shortHeaders; +WAISDocumentLongHeader** longHeaders; +WAISDocumentText** text; +WAISDocumentHeadlines** headlines; +WAISDocumentCodes** codes; +diagnosticRecord** diags; +/* if buf is NULL, we have just gotten a read error, and need to clean up + any state we have built. If not, then everything is going fine, and + we should just hang loose + */ +{ + void* ptr = NULL; + long i; + + if (buf == NULL) + { s_free(seedWordsUsed); + if (docHeaders != NULL) + for (i = 0,ptr = (void *)docHeaders[i]; ptr != NULL; + ptr = (void *)docHeaders[++i]) + freeWAISDocumentHeader((WAISDocumentHeader*)ptr); + s_free(docHeaders); + if (shortHeaders != NULL) + for (i = 0,ptr = (void *)shortHeaders[i]; ptr != NULL; + ptr = (void *)shortHeaders[++i]) + freeWAISDocumentShortHeader((WAISDocumentShortHeader*)ptr); + s_free(shortHeaders); + if (longHeaders != NULL) + for (i = 0,ptr = (void *)longHeaders[i]; ptr != NULL; + ptr = (void *)longHeaders[++i]) + freeWAISDocumentLongHeader((WAISDocumentLongHeader*)ptr); + s_free(longHeaders); + if (text != NULL) + for (i = 0,ptr = (void *)text[i]; ptr != NULL; ptr = (void *)text[++i]) + freeWAISDocumentText((WAISDocumentText*)ptr); + s_free(text); + if (headlines != NULL) + for (i = 0,ptr = (void *)headlines[i]; ptr != NULL; + ptr = (void *)headlines[++i]) + freeWAISDocumentHeadlines((WAISDocumentHeadlines*)ptr); + s_free(headlines); + if (codes != NULL) + for (i = 0,ptr = (void *)codes[i]; ptr != NULL; + ptr = (void *)codes[++i]) + freeWAISDocumentCodes((WAISDocumentCodes*)ptr); + s_free(codes); + if (diags != NULL) + for (i = 0,ptr = (void *)diags[i]; ptr != NULL; + ptr = (void *)diags[++i]) + freeDiag((diagnosticRecord*)ptr); + s_free(diags); + } +} + +/*----------------------------------------------------------------------*/ + +char* +readSearchResponseInfo(info,buffer) +void** info; +char* buffer; +{ + char* buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag; + void* header = NULL; + WAISDocumentHeader** docHeaders = NULL; + WAISDocumentShortHeader** shortHeaders = NULL; + WAISDocumentLongHeader** longHeaders = NULL; + WAISDocumentText** text = NULL; + WAISDocumentHeadlines** headlines = NULL; + WAISDocumentCodes** codes = NULL; + long numDocHeaders,numLongHeaders,numShortHeaders,numText,numHeadlines; + long numCodes; + char* seedWordsUsed = NULL; + diagnosticRecord** diags = NULL; + diagnosticRecord* diag = NULL; + long numDiags = 0; + + numDocHeaders = numLongHeaders = numShortHeaders = numText = numHeadlines = numCodes = 0; + + buf = readUserInfoHeader(&tag,&size,buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) + { data_tag tag = peekTag(buf); + switch (tag) + { case DT_SeedWordsUsed: + buf = readString(&seedWordsUsed,buf); + break; + case DT_DatabaseDiagnosticRecords: + if (diags == NULL) /* create a new diag list */ + { diags = (diagnosticRecord**)s_malloc((size_t)sizeof(diagnosticRecord*) * 2); + } + else /* grow the diag list */ + { diags = (diagnosticRecord**)s_realloc((char*)diags,(size_t)(sizeof(diagnosticRecord*) * (numDiags + 2))); + } + buf = readDiag(&diag,buf); + diags[numDiags++] = diag; /* put it in the list */ + diags[numDiags] = NULL; + break; + case DT_DocumentHeaderGroup: + if (docHeaders == NULL) /* create a new header list */ + { docHeaders = (WAISDocumentHeader**)s_malloc((size_t)sizeof(WAISDocumentHeader*) * 2); + } + else /* grow the doc list */ + { docHeaders = (WAISDocumentHeader**)s_realloc((char*)docHeaders,(size_t)(sizeof(WAISDocumentHeader*) * (numDocHeaders + 2))); + } + buf = readWAISDocumentHeader((WAISDocumentHeader**)&header,buf); + cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags); + RETURN_ON_NULL(buf); + docHeaders[numDocHeaders++] = + (WAISDocumentHeader*)header; /* put it in the list */ + docHeaders[numDocHeaders] = NULL; + break; + case DT_DocumentShortHeaderGroup: + if (shortHeaders == NULL) /* create a new header list */ + { shortHeaders = (WAISDocumentShortHeader**)s_malloc((size_t)sizeof(WAISDocumentShortHeader*) * 2); + } + else /* grow the doc list */ + { shortHeaders = (WAISDocumentShortHeader**)s_realloc((char*)shortHeaders,(size_t)(sizeof(WAISDocumentShortHeader*) * (numShortHeaders + 2))); + } + buf = readWAISDocumentShortHeader((WAISDocumentShortHeader**)&header,buf); + cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags); + RETURN_ON_NULL(buf); + shortHeaders[numShortHeaders++] = + (WAISDocumentShortHeader*)header; /* put it in the list */ + shortHeaders[numShortHeaders] = NULL; + break; + case DT_DocumentLongHeaderGroup: + if (longHeaders == NULL) /* create a new header list */ + { longHeaders = (WAISDocumentLongHeader**)s_malloc((size_t)sizeof(WAISDocumentLongHeader*) * 2); + } + else /* grow the doc list */ + { longHeaders = (WAISDocumentLongHeader**)s_realloc((char*)longHeaders,(size_t)(sizeof(WAISDocumentLongHeader*) * (numLongHeaders + 2))); + } + buf = readWAISDocumentLongHeader((WAISDocumentLongHeader**)&header,buf); + cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags); + RETURN_ON_NULL(buf); + longHeaders[numLongHeaders++] = + (WAISDocumentLongHeader*)header; /* put it in the list */ + longHeaders[numLongHeaders] = NULL; + break; + case DT_DocumentTextGroup: + if (text == NULL) /* create a new list */ + { text = (WAISDocumentText**)s_malloc((size_t)sizeof(WAISDocumentText*) * 2); + } + else /* grow the list */ + { text = (WAISDocumentText**)s_realloc((char*)text,(size_t)(sizeof(WAISDocumentText*) * (numText + 2))); + } + buf = readWAISDocumentText((WAISDocumentText**)&header,buf); + cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags); + RETURN_ON_NULL(buf); + text[numText++] = + (WAISDocumentText*)header; /* put it in the list */ + text[numText] = NULL; + break; + case DT_DocumentHeadlineGroup: + if (headlines == NULL) /* create a new list */ + { headlines = (WAISDocumentHeadlines**)s_malloc((size_t)sizeof(WAISDocumentHeadlines*) * 2); + } + else /* grow the list */ + { headlines = (WAISDocumentHeadlines**)s_realloc((char*)headlines,(size_t)(sizeof(WAISDocumentHeadlines*) * (numHeadlines + 2))); + } + buf = readWAISDocumentHeadlines((WAISDocumentHeadlines**)&header,buf); + cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags); + RETURN_ON_NULL(buf); + headlines[numHeadlines++] = + (WAISDocumentHeadlines*)header; /* put it in the list */ + headlines[numHeadlines] = NULL; + break; + case DT_DocumentCodeGroup: + if (codes == NULL) /* create a new list */ + { codes = (WAISDocumentCodes**)s_malloc((size_t)sizeof(WAISDocumentCodes*) * 2); + } + else /* grow the list */ + { codes = (WAISDocumentCodes**)s_realloc((char*)codes,(size_t)(sizeof(WAISDocumentCodes*) * (numCodes + 2))); + } + buf = readWAISDocumentCodes((WAISDocumentCodes**)&header,buf); + cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags); + RETURN_ON_NULL(buf); + codes[numCodes++] = + (WAISDocumentCodes*)header; /* put it in the list */ + codes[numCodes] = NULL; + break; + default: + cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags); + REPORT_READ_ERROR(buf); + break; + } + } + + *info = (void *)makeWAISSearchResponse(seedWordsUsed,docHeaders,shortHeaders, + longHeaders,text,headlines,codes,diags); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentText* +makeWAISDocumentText(docID,versionNumber,documentText) +any* docID; +long versionNumber; +any* documentText; +{ + WAISDocumentText* docText = (WAISDocumentText*)s_malloc((size_t)sizeof(WAISDocumentText)); + + docText->DocumentID = docID; + docText->VersionNumber = versionNumber; + docText->DocumentText = documentText; + + return(docText); +} + +/*----------------------------------------------------------------------*/ + +void +freeWAISDocumentText(docText) +WAISDocumentText* docText; +{ + freeAny(docText->DocumentID); + freeAny(docText->DocumentText); + s_free(docText); +} + +/*----------------------------------------------------------------------*/ + +char* +writeWAISDocumentText(docText,buffer,len) +WAISDocumentText* docText; +char* buffer; +long* len; +{ + unsigned long header_len = userInfoTagSize(DT_DocumentTextGroup, + DefWAISDocTextSize); + char* buf = buffer + header_len; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(docText->DocumentID,DT_DocumentID,buf,len); + buf = writeNum(docText->VersionNumber,DT_VersionNumber,buf,len); + buf = writeAny(docText->DocumentText,DT_DocumentText,buf,len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentTextGroup,size,header_len,buffer,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readWAISDocumentText(docText,buffer) +WAISDocumentText** docText; +char* buffer; +{ + char* buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag; + any *docID,*documentText; + long versionNumber; + + docID = documentText = NULL; + versionNumber = UNUSED; + + buf = readUserInfoHeader(&tag,&size,buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) + { data_tag tag = peekTag(buf); + switch (tag) + { case DT_DocumentID: + buf = readAny(&docID,buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber,buf); + break; + case DT_DocumentText: + buf = readAny(&documentText,buf); + break; + default: + freeAny(docID); + freeAny(documentText); + REPORT_READ_ERROR(buf); + break; + } + } + + *docText = makeWAISDocumentText(docID,versionNumber,documentText); + return(buf); +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentHeadlines* +makeWAISDocumentHeadlines(docID, + versionNumber, + source, + date, + headline, + originCity) +any* docID; +long versionNumber; +char* source; +char* date; +char* headline; +char* originCity; +{ + WAISDocumentHeadlines* docHeadline = + (WAISDocumentHeadlines*)s_malloc((size_t)sizeof(WAISDocumentHeadlines)); + + docHeadline->DocumentID = docID; + docHeadline->VersionNumber = versionNumber; + docHeadline->Source = source; + docHeadline->Date = date; + docHeadline->Headline = headline; + docHeadline->OriginCity = originCity; + + return(docHeadline); +} + +/*----------------------------------------------------------------------*/ + +void +freeWAISDocumentHeadlines(docHeadline) +WAISDocumentHeadlines* docHeadline; +{ + freeAny(docHeadline->DocumentID); + s_free(docHeadline->Source); + s_free(docHeadline->Date); + s_free(docHeadline->Headline); + s_free(docHeadline->OriginCity); + s_free(docHeadline); +} + +/*----------------------------------------------------------------------*/ + +char* +writeWAISDocumentHeadlines(docHeadline,buffer,len) +WAISDocumentHeadlines* docHeadline; +char* buffer; +long* len; +{ + unsigned long header_len = userInfoTagSize(DT_DocumentHeadlineGroup, + DefWAISDocHeadlineSize); + char* buf = buffer + header_len; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(docHeadline->DocumentID,DT_DocumentID,buf,len); + buf = writeNum(docHeadline->VersionNumber,DT_VersionNumber,buf,len); + buf = writeString(docHeadline->Source,DT_Source,buf,len); + buf = writeString(docHeadline->Date,DT_Date,buf,len); + buf = writeString(docHeadline->Headline,DT_Headline,buf,len); + buf = writeString(docHeadline->OriginCity,DT_OriginCity,buf,len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentHeadlineGroup,size,header_len,buffer,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readWAISDocumentHeadlines(docHeadline,buffer) +WAISDocumentHeadlines** docHeadline; +char* buffer; +{ + char* buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag; + any* docID; + long versionNumber; + char *source,*date,*headline,*originCity; + + docID = NULL; + versionNumber = UNUSED; + source = date = headline = originCity = NULL; + + buf = readUserInfoHeader(&tag,&size,buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) + { data_tag tag = peekTag(buf); + switch (tag) + { case DT_DocumentID: + buf = readAny(&docID,buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber,buf); + break; + case DT_Source: + buf = readString(&source,buf); + break; + case DT_Date: + buf = readString(&date,buf); + break; + case DT_Headline: + buf = readString(&headline,buf); + break; + case DT_OriginCity: + buf = readString(&originCity,buf); + break; + default: + freeAny(docID); + s_free(source); + s_free(date); + s_free(headline); + s_free(originCity); + REPORT_READ_ERROR(buf); + break; + } + } + + *docHeadline = makeWAISDocumentHeadlines(docID,versionNumber,source,date, + headline,originCity); + return(buf); +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentCodes* +makeWAISDocumentCodes(docID, + versionNumber, + stockCodes, + companyCodes, + industryCodes) +any* docID; +long versionNumber; +char* stockCodes; +char* companyCodes; +char* industryCodes; +{ + WAISDocumentCodes* docCodes = (WAISDocumentCodes*)s_malloc((size_t)sizeof(WAISDocumentCodes)); + + docCodes->DocumentID = docID; + docCodes->VersionNumber = versionNumber; + docCodes->StockCodes = stockCodes; + docCodes->CompanyCodes = companyCodes; + docCodes->IndustryCodes = industryCodes; + + return(docCodes); +} + +/*----------------------------------------------------------------------*/ + +void +freeWAISDocumentCodes(docCodes) +WAISDocumentCodes* docCodes; +{ + freeAny(docCodes->DocumentID); + s_free(docCodes->StockCodes); + s_free(docCodes->CompanyCodes); + s_free(docCodes->IndustryCodes); + s_free(docCodes); +} + +/*----------------------------------------------------------------------*/ + +char* +writeWAISDocumentCodes(docCodes,buffer,len) +WAISDocumentCodes* docCodes; +char* buffer; +long* len; +{ + unsigned long header_len = userInfoTagSize(DT_DocumentCodeGroup , + DefWAISDocCodeSize); + char* buf = buffer + header_len; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(docCodes->DocumentID,DT_DocumentID,buf,len); + buf = writeNum(docCodes->VersionNumber,DT_VersionNumber,buf,len); + buf = writeString(docCodes->StockCodes,DT_StockCodes,buf,len); + buf = writeString(docCodes->CompanyCodes,DT_CompanyCodes,buf,len); + buf = writeString(docCodes->IndustryCodes,DT_IndustryCodes,buf,len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentCodeGroup,size,header_len,buffer,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readWAISDocumentCodes(docCodes,buffer) +WAISDocumentCodes** docCodes; +char* buffer; +{ + char* buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag; + any* docID; + long versionNumber; + char *stockCodes,*companyCodes,*industryCodes; + + docID = NULL; + versionNumber = UNUSED; + stockCodes = companyCodes = industryCodes = NULL; + + buf = readUserInfoHeader(&tag,&size,buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) + { data_tag tag = peekTag(buf); + switch (tag) + { case DT_DocumentID: + buf = readAny(&docID,buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber,buf); + break; + case DT_StockCodes: + buf = readString(&stockCodes,buf); + break; + case DT_CompanyCodes: + buf = readString(&companyCodes,buf); + break; + case DT_IndustryCodes: + buf = readString(&industryCodes,buf); + break; + default: + freeAny(docID); + s_free(stockCodes); + s_free(companyCodes); + s_free(industryCodes); + REPORT_READ_ERROR(buf); + break; + } + } + + *docCodes = makeWAISDocumentCodes(docID,versionNumber,stockCodes, + companyCodes,industryCodes); + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +writePresentInfo(present,buffer,len) +PresentAPDU* present; +char* buffer; +long* len; +{ + /* The WAIS protocol doesn't use present info */ + return(buffer); +} + +/*----------------------------------------------------------------------*/ + +char* +readPresentInfo(info,buffer) +void** info; +char* buffer; +{ + /* The WAIS protocol doesn't use present info */ + *info = NULL; + return(buffer); +} + +/*----------------------------------------------------------------------*/ + +char* +writePresentResponseInfo(response,buffer,len) +PresentResponseAPDU* response; +char* buffer; +long* len; +{ + /* The WAIS protocol doesn't use presentResponse info */ + return(buffer); +} + +/*----------------------------------------------------------------------*/ + +char* +readPresentResponseInfo(info,buffer) +void** info; +char* buffer; +{ + /* The WAIS protocol doesn't use presentResponse info */ + *info = NULL; + return(buffer); +} + +/*----------------------------------------------------------------------*/ + +/* support for type 1 queries */ + +/* new use values (for the chunk types) */ +#define BYTE "wb" +#define LINE "wl" +#define PARAGRAPH "wp" +#define DATA_TYPE "wt" + +/* WAIS supports the following semantics for type 1 queries: + + 1. retrieve the header/codes from a document: + + System_Control_Number = docID + Data Type = type (optional) + And + + 2. retrieve a fragment of the text of a document: + + System_Control_Number = docID + Data Type = type (optional) + And + Chunk >= start + And + Chunk < end + And + + Information from multiple documents may be requested by using + groups of the above joined by: + + OR + + ( XXX does an OR come after every group but the first, or do they + all come at the end? ) + + ( XXX return type could be in the element set) +*/ + +static query_term** makeWAISQueryTerms _AP((DocObj** docs)); + +static query_term** +makeWAISQueryTerms(docs) +DocObj** docs; +/* given a null terminated list of docObjs, construct the appropriate + query of the form given above + */ +{ + query_term** terms = NULL; + long numTerms = 0; + DocObj* doc = NULL; + long i; + + if (docs == NULL) + return((query_term**)NULL); + + terms = (query_term**)s_malloc((size_t)(sizeof(query_term*) * 1)); + terms[numTerms] = NULL; + + /* loop through the docs making terms for them all */ + for (i = 0,doc = docs[i]; doc != NULL; doc = docs[++i]) + { any* type = NULL; + + if (doc->Type != NULL) + type = stringToAny(doc->Type); + + if (doc->ChunkCode == CT_document) /* a whole document */ + { terms = (query_term**)s_realloc((char*)terms, + (size_t)(sizeof(query_term*) * + (numTerms + 3 + 1))); + terms[numTerms++] = makeAttributeTerm(SYSTEM_CONTROL_NUMBER, + EQUAL,IGNORE,IGNORE, + IGNORE,IGNORE,doc->DocumentID); + if (type != NULL) + { terms[numTerms++] = makeAttributeTerm(DATA_TYPE,EQUAL, + IGNORE,IGNORE,IGNORE, + IGNORE,type); + terms[numTerms++] = makeOperatorTerm(AND); + } + terms[numTerms] = NULL; + } + else /* a document fragment */ + { char chunk_att[ATTRIBUTE_SIZE]; + any* startChunk = NULL; + any* endChunk = NULL; + + terms = (query_term**)s_realloc((char*)terms, + (size_t)(sizeof(query_term*) * + (numTerms + 7 + 1))); + + switch (doc->ChunkCode) + { case CT_byte: + case CT_line: + { char start[20],end[20]; + (doc->ChunkCode == CT_byte) ? + strncpy(chunk_att,BYTE,ATTRIBUTE_SIZE) : + strncpy(chunk_att,LINE,ATTRIBUTE_SIZE); + sprintf(start,"%ld",doc->ChunkStart.Pos); + startChunk = stringToAny(start); + sprintf(end,"%ld",doc->ChunkEnd.Pos); + endChunk = stringToAny(end); + } + break; + case CT_paragraph: + strncpy(chunk_att,PARAGRAPH,ATTRIBUTE_SIZE); + startChunk = doc->ChunkStart.ID; + endChunk = doc->ChunkEnd.ID; + break; + default: + /* error */ + break; + } + + terms[numTerms++] = makeAttributeTerm(SYSTEM_CONTROL_NUMBER, + EQUAL,IGNORE,IGNORE, + IGNORE, + IGNORE,doc->DocumentID); + if (type != NULL) + { terms[numTerms++] = makeAttributeTerm(DATA_TYPE,EQUAL,IGNORE, + IGNORE,IGNORE,IGNORE, + type); + terms[numTerms++] = makeOperatorTerm(AND); + } + terms[numTerms++] = makeAttributeTerm(chunk_att, + GREATER_THAN_OR_EQUAL, + IGNORE,IGNORE,IGNORE, + IGNORE, + startChunk); + terms[numTerms++] = makeOperatorTerm(AND); + terms[numTerms++] = makeAttributeTerm(chunk_att,LESS_THAN, + IGNORE,IGNORE,IGNORE, + IGNORE, + endChunk); + terms[numTerms++] = makeOperatorTerm(AND); + terms[numTerms] = NULL; + + if (doc->ChunkCode == CT_byte || doc->ChunkCode == CT_line) + { freeAny(startChunk); + freeAny(endChunk); + } + } + + freeAny(type); + + if (i != 0) /* multiple independent queries, need a disjunction */ + { terms = (query_term**)s_realloc((char*)terms, + (size_t)(sizeof(query_term*) * + (numTerms + 1 + 1))); + terms[numTerms++] = makeOperatorTerm(OR); + terms[numTerms] = NULL; + } + } + + return(terms); +} + +/*----------------------------------------------------------------------*/ + +static DocObj** makeWAISQueryDocs _AP((query_term** terms)); + +static DocObj** +makeWAISQueryDocs(terms) +query_term** terms; +/* given a list of terms in the form given above, convert them to + DocObjs. + */ +{ + query_term* docTerm = NULL; + query_term* fragmentTerm = NULL; + DocObj** docs = NULL; + DocObj* doc = NULL; + long docNum,termNum; + + docNum = termNum = 0; + + docs = (DocObj**)s_malloc((size_t)(sizeof(DocObj*) * 1)); + docs[docNum] = NULL; + + /* translate the terms into DocObjs */ + while (true) + { + query_term* typeTerm = NULL; + char* type = NULL; + long startTermOffset; + + docTerm = terms[termNum]; + + if (docTerm == NULL) + break; /* we're done converting */; + + typeTerm = terms[termNum + 1]; /* get the lead Term if it exists */ + + if (strcmp(typeTerm->Use,DATA_TYPE) == 0) /* we do have a type */ + { startTermOffset = 3; + type = anyToString(typeTerm->Term); + } + else /* no type */ + { startTermOffset = 1; + typeTerm = NULL; + type = NULL; + } + + /* grow the doc list */ + docs = (DocObj**)s_realloc((char*)docs,(size_t)(sizeof(DocObj*) * + (docNum + 1 + 1))); + + /* figure out what kind of docObj to build - and build it */ + fragmentTerm = terms[termNum + startTermOffset]; + if (fragmentTerm != NULL && fragmentTerm->TermType == TT_Attribute) + { /* build a document fragment */ + query_term* startTerm = fragmentTerm; + query_term* endTerm = terms[termNum + startTermOffset + 2]; + + if (strcmp(startTerm->Use,BYTE) == 0){ /* a byte chunk */ + doc = makeDocObjUsingBytes(duplicateAny(docTerm->Term), + type, + anyToLong(startTerm->Term), + anyToLong(endTerm->Term)); + log_write("byte"); + }else if (strcmp(startTerm->Use,LINE) == 0){ /* a line chunk */ + doc = makeDocObjUsingLines(duplicateAny(docTerm->Term), + type, + anyToLong(startTerm->Term), + anyToLong(endTerm->Term)); + log_write("line"); + }else{ + log_write("chunk"); /* a paragraph chunk */ + doc = makeDocObjUsingParagraphs(duplicateAny(docTerm->Term), + type, + duplicateAny(startTerm->Term), + duplicateAny(endTerm->Term)); +} + termNum += (startTermOffset + 4); /* point to next term */ + } + else /* build a full document */ + { + doc = makeDocObjUsingWholeDocument(duplicateAny(docTerm->Term), + type); +log_write("whole doc"); + termNum += startTermOffset; /* point to next term */ + } + + docs[docNum++] = doc; /* insert the new document */ + + docs[docNum] = NULL; /* keep the doc list terminated */ + + + if (terms[termNum] != NULL) + termNum++; /* skip the OR operator it necessary */ + else + break; /* we are done */ + } + + return(docs); +} + +/*----------------------------------------------------------------------*/ + +any* +makeWAISTextQuery(docs) +DocObj** docs; +/* given a list of DocObjs, return an any whose contents is the corresponding + type 1 query + */ +{ + any *buf = NULL; + query_term** terms = NULL; + + terms = makeWAISQueryTerms(docs); + buf = writeQuery(terms); + + doList((void**)terms,freeTerm); + s_free(terms); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +DocObj** +readWAISTextQuery(buf) +any* buf; +/* given an any whose contents are type 1 queries of the WAIS sort, + construct a list of the corresponding DocObjs + */ +{ + query_term** terms = NULL; + DocObj** docs = NULL; + + terms = readQuery(buf); + docs = makeWAISQueryDocs(terms); + + doList((void**)terms,freeTerm); + s_free(terms); + + return(docs); +} + +/*----------------------------------------------------------------------*/ +/* Customized free WAIS object routines: */ +/* */ +/* This set of procedures is for applications to free a WAIS object */ +/* which was made with makeWAISFOO. */ +/* Each procedure frees only the memory that was allocated in its */ +/* associated makeWAISFOO routine, thus it's not necessary for the */ +/* caller to assign nulls to the pointer fields of the WAIS object. */ +/*----------------------------------------------------------------------*/ + +void +CSTFreeWAISInitResponse(init) +WAISInitResponse* init; +/* free an object made with makeWAISInitResponse */ +{ + s_free(init); +} + +/*----------------------------------------------------------------------*/ + +void +CSTFreeWAISSearch(query) +WAISSearch* query; +/* destroy an object made with makeWAISSearch() */ +{ + s_free(query); +} + +/*----------------------------------------------------------------------*/ + +void +CSTFreeDocObj(doc) +DocObj* doc; +/* free a docObj */ +{ + s_free(doc); +} + +/*----------------------------------------------------------------------*/ + +void +CSTFreeWAISDocumentHeader(header) +WAISDocumentHeader* header; +{ + s_free(header); +} + +/*----------------------------------------------------------------------*/ + +void +CSTFreeWAISDocumentShortHeader(header) +WAISDocumentShortHeader* header; +{ + s_free(header); +} +/*----------------------------------------------------------------------*/ + +void +CSTFreeWAISDocumentLongHeader(header) +WAISDocumentLongHeader* header; +{ + s_free(header); +} + +/*----------------------------------------------------------------------*/ + +void +CSTFreeWAISSearchResponse(response) +WAISSearchResponse* response; +{ + s_free(response); +} + +/*----------------------------------------------------------------------*/ + +void +CSTFreeWAISDocumentText(docText) +WAISDocumentText* docText; +{ + s_free(docText); +} + +/*----------------------------------------------------------------------*/ + +void +CSTFreeWAISDocHeadlines(docHeadline) +WAISDocumentHeadlines* docHeadline; +{ + s_free(docHeadline); +} + +/*----------------------------------------------------------------------*/ + +void +CSTFreeWAISDocumentCodes(docCodes) +WAISDocumentCodes* docCodes; +{ + s_free(docCodes); +} + +/*----------------------------------------------------------------------*/ + +void +CSTFreeWAISTextQuery(query) +any* query; +{ + freeAny(query); +} + +/*----------------------------------------------------------------------*/ + + +/* +** Routines originally from WMessage.c -- FM +** +**----------------------------------------------------------------------*/ +/* WIDE AREA INFORMATION SERVER SOFTWARE + No guarantees or restrictions. See the readme file for the full standard + disclaimer. + 3.26.90 +*/ + +/* This file is for reading and writing the wais packet header. + * Morris@think.com + */ + +/* to do: + * add check sum + * what do you do when checksum is wrong? + */ + +/*---------------------------------------------------------------------*/ + +void +readWAISPacketHeader(msgBuffer,header_struct) +char* msgBuffer; +WAISMessage *header_struct; +{ + /* msgBuffer is a string containing at least HEADER_LENGTH bytes. */ + + memmove(header_struct->msg_len,msgBuffer,(size_t)10); + header_struct->msg_type = char_downcase((unsigned long)msgBuffer[10]); + header_struct->hdr_vers = char_downcase((unsigned long)msgBuffer[11]); + memmove(header_struct->server,(void*)(msgBuffer + 12),(size_t)10); + header_struct->compression = char_downcase((unsigned long)msgBuffer[22]); + header_struct->encoding = char_downcase((unsigned long)msgBuffer[23]); + header_struct->msg_checksum = char_downcase((unsigned long)msgBuffer[24]); +} + +/*---------------------------------------------------------------------*/ + +/* this modifies the header argument. See wais-message.h for the different + * options for the arguments. + */ + +void +writeWAISPacketHeader(header, + dataLen, + type, + server, + compression, + encoding, + version) +char* header; +long dataLen; +long type; +char* server; +long compression; +long encoding; +long version; +/* Puts together the new wais before-the-z39-packet header. */ +{ + char lengthBuf[11]; + char serverBuf[11]; + + long serverLen = strlen(server); + if (serverLen > 10) + serverLen = 10; + + sprintf(lengthBuf, "%010ld", dataLen); + strncpy(header,lengthBuf,10); + + header[10] = type & 0xFF; + header[11] = version & 0xFF; + + strncpy(serverBuf,server,serverLen); + strncpy((char*)(header + 12),serverBuf,serverLen); + + header[22] = compression & 0xFF; + header[23] = encoding & 0xFF; + header[24] = '0'; /* checkSum(header + HEADER_LENGTH,dataLen); XXX the result must be ascii */ +} + +/*---------------------------------------------------------------------*/ + diff --git a/WWW/Library/Implementation/HTVMS_WaisProt.h b/WWW/Library/Implementation/HTVMS_WaisProt.h new file mode 100644 index 00000000..1a4e83d5 --- /dev/null +++ b/WWW/Library/Implementation/HTVMS_WaisProt.h @@ -0,0 +1,376 @@ +/* HTVMS_WAISProt.h +** +** Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu) +** +** 31-May-1994 FM Initial version. +** +**----------------------------------------------------------------------*/ + +/* +** Routines originally from WProt.h -- FM +** +**----------------------------------------------------------------------*/ +/* WIDE AREA INFORMATION SERVER SOFTWARE: + No guarantees or restrictions. See the readme file for the full standard + disclaimer. + + 3.26.90 Harry Morris, morris@think.com + 3.30.90 Harry Morris + - removed chunk code from WAISSearchAPDU, + - added makeWAISQueryType1Query() and readWAISType1Query() which + replace makeWAISQueryTerms() and makeWAISQueryDocs(). + 4.11.90 HWM - added definitions of wais element set names + 4.14.90 HWM - changed symbol for relevance feedback query from QT_3 to + QT_RelevanceFeedbackQuery added QT_TextRetrievalQuery as a + synonym for QT_BooleanQuery + - renamed makeWAISType1Query() to makeWAISTextQuery() + renamed readWAISType1Query() to readWAISTextQuery() + 5.29.90 TS - added CSTFreeWAISFoo functions +*/ + +#ifndef _H_WAIS_protocol_ + +#define _H_WAIS_protocol_ + +/*----------------------------------------------------------------------*/ +/* Data types / constants */ + +/* date factor constants */ +#define DF_INDEPENDENT 1 +#define DF_LATER 2 +#define DF_EARLIER 3 +#define DF_SPECIFIED_RANGE 4 + +/* chunk types */ +#define CT_document 0 +#define CT_byte 1 +#define CT_line 2 +#define CT_paragraph 3 + +/* relevance feedback query */ +#define QT_RelevanceFeedbackQuery "3" +#define QT_TextRetrievalQuery QT_BooleanQuery + +/* new data tags */ +#define DT_UserInformationLength (data_tag)99 +#define DT_ChunkCode (data_tag)100 +#define DT_ChunkIDLength (data_tag)101 +#define DT_ChunkMarker (data_tag)102 +#define DT_HighlightMarker (data_tag)103 +#define DT_DeHighlightMarker (data_tag)104 +#define DT_NewlineCharacters (data_tag)105 +#define DT_SeedWords (data_tag)106 +#define DT_DocumentIDChunk (data_tag)107 +#define DT_ChunkStartID (data_tag)108 +#define DT_ChunkEndID (data_tag)109 +#define DT_TextList (data_tag)110 +#define DT_DateFactor (data_tag)111 +#define DT_BeginDateRange (data_tag)112 +#define DT_EndDateRange (data_tag)113 +#define DT_MaxDocumentsRetrieved (data_tag)114 +#define DT_SeedWordsUsed (data_tag)115 +#define DT_DocumentID (data_tag)116 +#define DT_VersionNumber (data_tag)117 +#define DT_Score (data_tag)118 +#define DT_BestMatch (data_tag)119 +#define DT_DocumentLength (data_tag)120 +#define DT_Source (data_tag)121 +#define DT_Date (data_tag)122 +#define DT_Headline (data_tag)123 +#define DT_OriginCity (data_tag)124 +#define DT_PresentStartByte (data_tag)125 +#define DT_TextLength (data_tag)126 +#define DT_DocumentText (data_tag)127 +#define DT_StockCodes (data_tag)128 +#define DT_CompanyCodes (data_tag)129 +#define DT_IndustryCodes (data_tag)130 + +/* added by harry */ +#define DT_DocumentHeaderGroup (data_tag)150 +#define DT_DocumentShortHeaderGroup (data_tag)151 +#define DT_DocumentLongHeaderGroup (data_tag)152 +#define DT_DocumentTextGroup (data_tag)153 +#define DT_DocumentHeadlineGroup (data_tag)154 +#define DT_DocumentCodeGroup (data_tag)155 +#define DT_Lines (data_tag)131 +#define DT_TYPE_BLOCK (data_tag)132 +#define DT_TYPE (data_tag)133 + +/* wais element sets */ +#define ES_DocumentHeader "Document Header" +#define ES_DocumentShortHeader "Document Short Header" +#define ES_DocumentLongHeader "Document Long Header" +#define ES_DocumentText "Document Text" +#define ES_DocumentHeadline "Document Headline" +#define ES_DocumentCodes "Document Codes" + +typedef struct DocObj { /* specifies a section of a document */ + any* DocumentID; + char* Type; + long ChunkCode; + union { + long Pos; + any* ID; + } ChunkStart; + union { + long Pos; + any* ID; + } ChunkEnd; + } DocObj; + +/*----------------------------------------------------------------------*/ +/* WAIS APDU extensions */ + +typedef struct WAISInitResponse { + long ChunkCode; + long ChunkIDLength; + char* ChunkMarker; + char* HighlightMarker; + char* DeHighlightMarker; + char* NewlineCharacters; + /* XXX need to add UpdateFrequency and Update Time */ + } WAISInitResponse; + +typedef struct WAISSearch { + char* SeedWords; + DocObj** Docs; + char** TextList; + long DateFactor; + char* BeginDateRange; + char* EndDateRange; + long MaxDocumentsRetrieved; + } WAISSearch; + +typedef struct WAISDocumentHeader { + any* DocumentID; + long VersionNumber; + long Score; + long BestMatch; + long DocumentLength; + long Lines; + char** Types; + char* Source; + char* Date; + char* Headline; + char* OriginCity; + } WAISDocumentHeader; + +typedef struct WAISDocumentShortHeader { + any* DocumentID; + long VersionNumber; + long Score; + long BestMatch; + long DocumentLength; + long Lines; + } WAISDocumentShortHeader; + +typedef struct WAISDocumentLongHeader { + any* DocumentID; + long VersionNumber; + long Score; + long BestMatch; + long DocumentLength; + long Lines; + char** Types; + char* Source; + char* Date; + char* Headline; + char* OriginCity; + char* StockCodes; + char* CompanyCodes; + char* IndustryCodes; + } WAISDocumentLongHeader; + +typedef struct WAISDocumentText { + any* DocumentID; + long VersionNumber; + any* DocumentText; + } WAISDocumentText; + +typedef struct WAISDocumentHeadlines { + any* DocumentID; + long VersionNumber; + char* Source; + char* Date; + char* Headline; + char* OriginCity; + } WAISDocumentHeadlines; + +typedef struct WAISDocumentCodes { + any* DocumentID; + long VersionNumber; + char* StockCodes; + char* CompanyCodes; + char* IndustryCodes; + } WAISDocumentCodes; + +typedef struct WAISSearchResponse { + char* SeedWordsUsed; + WAISDocumentHeader** DocHeaders; + WAISDocumentShortHeader** ShortHeaders; + WAISDocumentLongHeader** LongHeaders; + WAISDocumentText** Text; + WAISDocumentHeadlines** Headlines; + WAISDocumentCodes** Codes; + diagnosticRecord** Diagnostics; + } WAISSearchResponse; + +/*----------------------------------------------------------------------*/ +/* Functions */ + +DocObj* makeDocObjUsingWholeDocument _AP((any* aDocID,char* type)); +DocObj* makeDocObjUsingBytes _AP((any* aDocID,char* type,long start,long end)); +DocObj* makeDocObjUsingLines _AP((any* aDocID,char* type,long start,long end)); +DocObj* makeDocObjUsingParagraphs _AP((any* aDocID,char* type,any* start,any* end)); +void freeDocObj _AP((DocObj* doc)); + +WAISInitResponse* makeWAISInitResponse _AP((long chunkCode,long chunkIDLen, + char* chunkMarker,char* highlightMarker, + char* deHighlightMarker,char* newLineChars)); +void freeWAISInitResponse _AP((WAISInitResponse* init)); + +WAISSearch* makeWAISSearch _AP(( + char* seedWords,DocObj** docs,char** textList, + long dateFactor,char* beginDateRange,char* endDateRange, + long maxDocsRetrieved)); +void freeWAISSearch _AP((WAISSearch* query)); + +WAISDocumentHeader* makeWAISDocumentHeader _AP(( + any* aDocID,long versionNumber,long score,long bestMatch,long docLen, + long lines,char** types,char* source,char* date,char* headline,char* originCity)); +void freeWAISDocumentHeader _AP((WAISDocumentHeader* header)); +char* writeWAISDocumentHeader _AP((WAISDocumentHeader* header,char* buffer,long* len)); +char* readWAISDocumentHeader _AP((WAISDocumentHeader** header,char* buffer)); + +WAISDocumentShortHeader* makeWAISDocumentShortHeader _AP(( + any* aDocID,long versionNumber,long score,long bestMatch,long docLen,long lines)); +void freeWAISDocumentShortHeader _AP((WAISDocumentShortHeader* header)); +char* writeWAISDocumentShortHeader _AP((WAISDocumentShortHeader* header, + char* buffer,long* len)); +char* readWAISDocumentShortHeader _AP((WAISDocumentShortHeader** header,char* buffer)); + +WAISDocumentLongHeader* makeWAISDocumentLongHeader _AP(( + any* aDocID,long versionNumber,long score,long bestMatch,long docLen, + long lines,char** types,char* source,char* date, char* headline,char* originCity, + char* stockCodes,char* companyCodes,char* industryCodes)); +void freeWAISDocumentLongHeader _AP((WAISDocumentLongHeader* header)); +char* writeWAISDocumentLongHeader _AP((WAISDocumentLongHeader* header,char* buffer,long* len)); +char* readWAISDocumentLongHeader _AP((WAISDocumentLongHeader** header,char* buffer)); + +WAISSearchResponse* makeWAISSearchResponse _AP(( + char* seedWordsUsed,WAISDocumentHeader** docHeaders, + WAISDocumentShortHeader** shortHeaders, + WAISDocumentLongHeader** longHeaders, + WAISDocumentText** text,WAISDocumentHeadlines** headlines, + WAISDocumentCodes** codes, + diagnosticRecord** diagnostics)); +void freeWAISSearchResponse _AP((WAISSearchResponse* response)); + +WAISDocumentText* makeWAISDocumentText _AP((any* aDocID,long versionNumber, + any* documentText)); +void freeWAISDocumentText _AP((WAISDocumentText* docText)); +char* writeWAISDocumentText _AP((WAISDocumentText* docText,char* buffer,long* len)); +char* readWAISDocumentText _AP((WAISDocumentText** docText,char* buffer)); + +WAISDocumentHeadlines* makeWAISDocumentHeadlines _AP(( + any* aDocID,long versionNumber,char* source,char* date,char* headline, + char* originCity)); +void freeWAISDocumentHeadlines _AP((WAISDocumentHeadlines* docHeadline)); +char* writeWAISDocumentHeadlines _AP((WAISDocumentHeadlines* docHeadline,char* buffer,long* len)); +char* readWAISDocumentHeadlines _AP((WAISDocumentHeadlines** docHeadline,char* buffer)); + +WAISDocumentCodes* makeWAISDocumentCodes _AP(( + any* aDocID,long versionNumber,char* stockCodes,char* companyCodes, + char* industryCodes)); +void freeWAISDocumentCodes _AP((WAISDocumentCodes* docCodes)); +char* writeWAISDocumentCodes _AP((WAISDocumentCodes* docCodes,char* buffer,long* len)); +char* readWAISDocumentCodes _AP((WAISDocumentCodes** docCodes,char* buffer)); + +any* makeWAISTextQuery _AP((DocObj** docs)); +DocObj** readWAISTextQuery _AP((any* terms)); + +void CSTFreeWAISInitResponse _AP((WAISInitResponse* init)); +void CSTFreeWAISSearch _AP((WAISSearch* query)); +void CSTFreeDocObj _AP((DocObj* doc)); +void CSTFreeWAISDocumentHeader _AP((WAISDocumentHeader* header)); +void CSTFreeWAISDocumentShortHeader _AP((WAISDocumentShortHeader* header)); +void CSTFreeWAISDocumentLongHeader _AP((WAISDocumentLongHeader* header)); +void CSTFreeWAISSearchResponse _AP((WAISSearchResponse* response)); +void CSTFreeWAISDocumentText _AP((WAISDocumentText* docText)); +void CSTFreeWAISDocHeadlines _AP((WAISDocumentHeadlines* docHeadline)); +void CSTFreeWAISDocumentCodes _AP((WAISDocumentCodes* docCodes)); +void CSTFreeWAISTextQuery _AP(( any* query)); + +/*----------------------------------------------------------------------*/ + +#endif /* ndef _H_WAIS_protocol_ */ + + +/* +** Routines originally from WMessage.h -- FM +** +**----------------------------------------------------------------------*/ +/* WIDE AREA INFORMATION SERVER SOFTWARE + No guarantees or restrictions. See the readme file for the full standard + disclaimer. + 3.26.90 +*/ + +/* wais-message.h + * + * This is the header outside of WAIS Z39.50 messages. The header will be + * printable ascii, so as to be transportable. This header will precede each + * Z39.50 APDU, or zero-length message if it is an ACK or NACK. Be sure to + * change hdr_vers current value if you change the structure of the header. + * + * The characters in the header are case insensitive so that the systems from + * the past that only handle one case can at least read the header. + * + * 7.5.90 HWM - added constants + * 7/5/90 brewster added funtion prototypes and comments + * 11/30/90 HWM - went to version 2 (inits and typed retrieval) + */ + +#ifndef WMESSAGE_H +#define WMESSAGE_H + +typedef struct wais_header { + char msg_len[10]; /* length in bytes of following message */ + char msg_type; /* type of message: 'z'=Z39.50 APDU, + 'a'=ACK, 'n'=NACK */ + char hdr_vers; /* version of this header, currently = '2' */ + char server[10]; /* name or address of server */ + char compression; /* <sp>=no compression, 'u'=unix compress */ + char encoding; /* <sp>=no encoding, 'h'=hexize, + 'u'=uuencode */ + char msg_checksum; /* XOR of every byte of message */ + } WAISMessage; + +#define HEADER_LENGTH 25 /* number of bytes needed to write a + wais-header (not sizeof(wais_header)) */ + +#define HEADER_VERSION (long)'2' + +/* message type */ +#define Z3950 'z' +#define ACK 'a' +#define NAK 'n' + +/* compression */ +#define NO_COMPRESSION ' ' +#define UNIX_COMPRESSION 'u' + +/* encoding */ +#define NO_ENCODING ' ' +#define HEX_ENCODING 'h' /* Swartz 4/3 encoding */ +#define IBM_HEXCODING 'i' /* same as h but uses characters acceptable for IBM mainframes */ +#define UUENCODE 'u' + + +void readWAISPacketHeader _AP((char* msgBuffer,WAISMessage *header_struct)); +long getWAISPacketLength _AP((WAISMessage* header)); +void writeWAISPacketHeader _AP((char* header,long dataLen,long type, + char* server,long compression, + long encoding,long version)); + +#endif /* ndef WMESSAGE_H */ diff --git a/WWW/Library/Implementation/HTVMS_WaisUI.c b/WWW/Library/Implementation/HTVMS_WaisUI.c new file mode 100644 index 00000000..39f2bd0b --- /dev/null +++ b/WWW/Library/Implementation/HTVMS_WaisUI.c @@ -0,0 +1,2448 @@ +/* HTVMS_WAISUI.c +** +** Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu) +** +** 30-May-1994 FM Initial version. +** +/*----------------------------------------------------------------------*/ + +/* +** Routines originally from UI.c -- FM +** +**----------------------------------------------------------------------*/ +/* WIDE AREA INFORMATION SERVER SOFTWARE: + No guarantees or restrictions. See the readme file for the full standard + disclaimer. + + Brewster@think.com +*/ + +/* + * this is a simple ui toolkit for building other ui's on top. + * -brewster + * + * top level functions: + * generate_search_apdu + * generate_retrieval_apdu + * interpret_message + * + */ + +/* to do: + * generate multiple queries for long documents. + * this will crash if the file being retrieved is larger than 100k. + * do log_write() + * + */ + +#include "HTUtils.h" +#include "tcp.h" +#include "HTVMS_WaisUI.h" +#include "HTVMS_WaisProt.h" +#include "HTTCP.h" +/*#include <stdio> included by HTUtils.h -- FM */ +#include <string.h> +#include <ctype.h> +#include <math.h> +#include <stdarg.h> + +#include "LYexit.h" +#include "LYLeaks.h" + +void +log_write(s) +char *s; +{ + return; +} + +/*----------------------------------------------------------------------*/ + +/* returns a pointer in the buffer of the first free byte. + if it overflows, then NULL is returned + */ +char * +generate_search_apdu(buff, + buff_len, + seed_words, + database_name, + docobjs, + maxDocsRetrieved) +char* buff; /* buffer to hold the apdu */ +long *buff_len; /* length of the buffer changed to reflect new data written */ +char *seed_words; /* string of the seed words */ +char *database_name; +DocObj** docobjs; +long maxDocsRetrieved; +{ + /* local variables */ + + SearchAPDU *search3; + char *end_ptr; + static char *database_names[2] = {"", 0}; + any refID; + WAISSearch *query; + refID.size = 1; + refID.bytes = "3"; + + database_names[0] = database_name; + query = makeWAISSearch(seed_words, + docobjs, /* DocObjsPtr */ + 0, + 1, /* DateFactor */ + 0, /* BeginDateRange */ + 0, /* EndDateRange */ + maxDocsRetrieved + ); + + search3 = makeSearchAPDU(30, + 5000, /* should be large */ + 30, + 1, /* replace indicator */ + "", /* result set name */ + database_names, /* database name */ + QT_RelevanceFeedbackQuery, /* query_type */ + 0, /* element name */ + NULL, /* reference ID */ + query); + + end_ptr = writeSearchAPDU(search3, buff, buff_len); + + CSTFreeWAISSearch(query); + freeSearchAPDU(search3); + return(end_ptr); +} + +/*----------------------------------------------------------------------*/ + +/* returns a pointer into the buffer of the next free byte. + if it overflowed, then NULL is returned + */ + +char * +generate_retrieval_apdu(buff, + buff_len, + docID, + chunk_type, + start, + end, + type, + database_name) +char *buff; +long *buff_len; /* length of the buffer changed to reflect new data written */ +any *docID; +long chunk_type; +long start; +long end; +char *type; +char *database_name; +{ + SearchAPDU *search; + char *end_ptr; + + static char *database_names[2]; + static char *element_names[3]; + any refID; + + DocObj *DocObjs[2]; + any *query; /* changed from char* by brewster */ + + if(NULL == type) + type = s_strdup("TEXT"); + + database_names[0] = database_name; + database_names[1] = NULL; + + element_names[0] = " "; + element_names[1] = ES_DocumentText; + element_names[2] = NULL; + + refID.size = 1; + refID.bytes = "3"; + + switch(chunk_type){ + case CT_line: + DocObjs[0] = makeDocObjUsingLines(docID, type, start, end); + break; + case CT_byte: + DocObjs[0] = makeDocObjUsingBytes(docID, type, start, end); + break; + } + DocObjs[1] = NULL; + + query = makeWAISTextQuery(DocObjs); + search = makeSearchAPDU( 10, 16, 15, + 1, /* replace indicator */ + "FOO", /* result set name */ + database_names, /* database name */ + QT_TextRetrievalQuery, /* query_type */ + element_names, /* element name */ + &refID, /* reference ID */ + query); + end_ptr = writeSearchAPDU(search, buff, buff_len); + CSTFreeWAISTextQuery(query); + freeSearchAPDU(search); + return(end_ptr); +} + +/*----------------------------------------------------------------------*/ + +/* this is a safe version of unix 'read' it does all the checking + * and looping necessary + * to those trying to modify the transport code to use non-UNIX streams: + * This is the function to modify! + */ +long read_from_stream(d,buf,nbytes) +int d; /* this is the stream */ +char *buf; +long nbytes; +{ + long didRead; + long toRead = nbytes; + long totalRead = 0; /* paranoia */ + + while (toRead > 0){ + didRead = NETREAD (d, buf, (int)toRead); + if(didRead == HT_INTERRUPTED) + return(HT_INTERRUPTED); + if(didRead == -1) /* error*/ + return(-1); + if(didRead == 0) /* eof */ + return(-2); /* maybe this should return 0? */ + toRead -= didRead; + buf += didRead; + totalRead += didRead; + } + if(totalRead != nbytes) /* we overread for some reason */ + return(- totalRead); /* bad news */ + return(totalRead); +} + +/*----------------------------------------------------------------------*/ + +/* returns the length of the response, 0 if an error */ + +long +transport_message(connection, + request_message, + request_length, + response_message, + response_buffer_length) +int connection; +char *request_message; +long request_length; +char *response_message; +long response_buffer_length; +{ + WAISMessage header; + long response_length; + int rv; + + + /* Write out message. Read back header. Figure out response length. */ + + if( request_length + HEADER_LENGTH != + NETWRITE(connection,request_message, + (int)( request_length +HEADER_LENGTH)) ) + return 0; + + /* read for the first '0' */ + + while(1){ + rv = read_from_stream(connection, response_message, 1); + if (rv == HT_INTERRUPTED) + return HT_INTERRUPTED; + if (rv < 0) + return 0; + if('0' == response_message[0]) + break; + } + + rv = read_from_stream(connection, response_message + 1, HEADER_LENGTH -1); + if (rv == HT_INTERRUPTED) + return HT_INTERRUPTED; + if (rv < 0) + return 0; + + readWAISPacketHeader(response_message, &header); + { + char length_array[11]; + strncpy(length_array, header.msg_len, 10); + length_array[10] = '\0'; + response_length = atol(length_array); + /* + if(verbose){ + printf("WAIS header: '%s' length_array: '%s'\n", + response_message, length_array); + } + */ + if(response_length > response_buffer_length){ + /* we got a message that is too long, therefore empty the message out, + and return 0 */ + long i; + for(i = 0; i < response_length; i++){ + rv = read_from_stream(connection, + response_message + HEADER_LENGTH, + 1); + if (rv == HT_INTERRUPTED) + return HT_INTERRUPTED; + if (rv < 0) + return 0; + } + return(0); + } + } + rv = read_from_stream(connection, + response_message + HEADER_LENGTH, + response_length); + if (rv == HT_INTERRUPTED) + return HT_INTERRUPTED; + if (rv < 0) + return 0; + return(response_length); +} + +/*----------------------------------------------------------------------*/ + +/* returns the number of bytes writen. 0 if an error */ +long +interpret_message(request_message,request_length, + response_message, + response_buffer_length, + connection, + verbose) +char *request_message; +long request_length; /* length of the buffer */ +char *response_message; +long response_buffer_length; +int connection; +boolean verbose; +{ + long response_length; + + /* ? + if(verbose){ + printf ("sending"); + if(hostname_internal && strlen(hostname_internal) > 0) + printf(" to host %s", hostname_internal); + if(service_name && strlen(service_name) > 0) + printf(" for service %s", service_name); + printf("\n"); + twais_dsply_rsp_apdu(request_message + HEADER_LENGTH, + request_length); + } + + */ + + writeWAISPacketHeader(request_message, + request_length, + (long)'z', /* Z39.50 */ + "wais ", /* server name */ + (long)NO_COMPRESSION, /* no compression */ + (long)NO_ENCODING,(long)HEADER_VERSION); + if(connection != 0) { + response_length = transport_message(connection, request_message, + request_length, + response_message, + response_buffer_length); + if (response_length == HT_INTERRUPTED) + return(HT_INTERRUPTED); + } + else + return(0); + + return(response_length); +} + +/*----------------------------------------------------------------------*/ + +/* modifies the string to exclude all seeker codes. sets length to + the new length. */ +char *delete_seeker_codes(string,length) +char *string; +long *length; +{ + long original_count; /* index into the original string */ + long new_count = 0; /* index into the collapsed string */ + for(original_count = 0; original_count < *length; original_count++){ + if(27 == string[original_count]){ + /* then we have an escape code */ + /* if the next letter is '(' or ')', then ignore two letters */ + if('(' == string[original_count + 1] || + ')' == string[original_count + 1]) + original_count += 1; /* it is a term marker */ + else original_count += 4; /* it is a paragraph marker */ + } + else string[new_count++] = string[original_count]; + } + *length = new_count; + return(string); +} + +/*----------------------------------------------------------------------*/ + +#if defined(VMS) && defined(__GNUC__) /* 10-AUG-1995 [pr] */ +/* + Workaround for an obscure bug in gcc's 2.6.[123] and 2.7.0 vax/vms port; + sometimes global variables will end up not being defined properly, + causing first gas to assume they're routines, then the linker to complain + about unresolved symbols, and finally the program to reference the wrong + objects (provoking ACCVIO). It's triggered by the specific ordering of + variable usage in the source code, hence rarely appears. This bug is + fixed in gcc 2.7.1, and was not present in 2.6.0 and earlier. + + Make a reference to VAXCRTL's _ctype_[], and also one to this dummy + variable itself to prevent any "defined but not used" warning. + */ +static __const void *__const ctype_dummy[] = { &_ctype_, &ctype_dummy }; +#endif /* VMS && __GNUC__ */ + +/* returns a pointer to a string with good stuff */ +char *trim_junk(headline) +char *headline; +{ + long length = strlen(headline) + 1; /* include the trailing null */ + long i; + headline = delete_seeker_codes(headline, &length); + /* delete leading spaces */ + for(i=0; i < strlen(headline); i++){ + if(isprint(headline[i])){ + break; + } + } + headline = headline + i; + /* delete trailing stuff */ + for(i=strlen(headline) - 1 ; i > 0; i--){ + if(isprint(headline[i])){ + break; + } + headline[i] = '\0'; + } + return(headline); +} + +/*----------------------------------------------------------------------*/ + + +/* +** Routines originally from ZProt.c -- FM +** +**----------------------------------------------------------------------*/ +/* WIDE AREA INFORMATION SERVER SOFTWARE:` + No guarantees or restrictions. See the readme file for the full standard + disclaimer. + + 3.26.90 Harry Morris, morris@think.com + 3.30.90 Harry Morris - Changed any->bits to any->bytes + 4.11.90 HWM - generalized conditional includes (see c-dialect.h) +*/ + +#define RESERVE_SPACE_FOR_HEADER(spaceLeft) \ + *spaceLeft -= HEADER_LEN; + +#define RELEASE_HEADER_SPACE(spaceLeft) \ + if (*spaceLeft > 0) \ + *spaceLeft += HEADER_LEN; + +/*----------------------------------------------------------------------*/ + +InitResponseAPDU* +makeInitResponseAPDU(result, + search, + present, + deleteIt, + accessControl, + resourceControl, + prefSize, + maxMsgSize, + auth, + id, + name, + version, + refID, + userInfo) +boolean result; +boolean search; +boolean present; +boolean deleteIt; +boolean accessControl; +boolean resourceControl; +long prefSize; +long maxMsgSize; +char* auth; +char* id; +char* name; +char* version; +any* refID; +void* userInfo; +/* build an initResponse APDU with user specified information */ +{ + InitResponseAPDU* init = (InitResponseAPDU*)s_malloc((size_t)sizeof(InitResponseAPDU)); + + init->PDUType = initResponseAPDU; + init->Result = result; + init->willSearch = search; + init->willPresent = present; + init->willDelete = deleteIt; + init->supportAccessControl = accessControl; + init->supportResourceControl = resourceControl; + init->PreferredMessageSize = prefSize; + init->MaximumRecordSize = maxMsgSize; + init->IDAuthentication = s_strdup(auth); + init->ImplementationID = s_strdup(id); + init->ImplementationName = s_strdup(name); + init->ImplementationVersion = s_strdup(version); + init->ReferenceID = duplicateAny(refID); + init->UserInformationField = userInfo; /* not copied! */ + + return(init); +} + +/*----------------------------------------------------------------------*/ + +void +freeInitResponseAPDU(init) +InitResponseAPDU* init; +/* free an initAPDU */ +{ + s_free(init->IDAuthentication); + s_free(init->ImplementationID); + s_free(init->ImplementationName); + s_free(init->ImplementationVersion); + freeAny(init->ReferenceID); + s_free(init); +} + +/*----------------------------------------------------------------------*/ + +char* +writeInitResponseAPDU(init,buffer,len) +InitResponseAPDU* init; +char* buffer; +long* len; +/* write the initResponse to a buffer, adding system information */ +{ + char* buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */ + long size; + bit_map* optionsBM = NULL; + + RESERVE_SPACE_FOR_HEADER(len); + + buf = writePDUType(init->PDUType,buf,len); + buf = writeBoolean(init->Result,buf,len); + buf = writeProtocolVersion(buf,len); + + optionsBM = makeBitMap((unsigned long)5,init->willSearch,init->willPresent, + init->willDelete,init->supportAccessControl, + init->supportResourceControl); + buf = writeBitMap(optionsBM,DT_Options,buf,len); + freeBitMap(optionsBM); + + buf = writeNum(init->PreferredMessageSize,DT_PreferredMessageSize,buf,len); + buf = writeNum(init->MaximumRecordSize,DT_MaximumRecordSize,buf,len); + buf = writeString(init->IDAuthentication,DT_IDAuthentication,buf,len); + buf = writeString(init->ImplementationID,DT_ImplementationID,buf,len); + buf = writeString(init->ImplementationName,DT_ImplementationName,buf,len); + buf = writeString(init->ImplementationVersion,DT_ImplementationVersion,buf,len); + buf = writeAny(init->ReferenceID,DT_ReferenceID,buf,len); + + /* go back and write the header-length-indicator */ + RELEASE_HEADER_SPACE(len); + size = buf - buffer - HEADER_LEN; + writeBinaryInteger(size,HEADER_LEN,buffer,len); + + if (init->UserInformationField != NULL) + buf = writeInitResponseInfo(init,buf,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readInitResponseAPDU(init,buffer) +InitResponseAPDU** init; +char* buffer; +{ + char* buf = buffer; + boolean search,present,delete,accessControl,resourceControl; + long prefSize,maxMsgSize; + char *auth,*id,*name,*version; + long size; + pdu_type pduType; + bit_map* versionBM = NULL; + bit_map* optionsBM = NULL; + boolean result; + any *refID = NULL; + void* userInfo = NULL; + + auth = id = name = version = NULL; + refID = NULL; + + /* read required part */ + buf = readBinaryInteger(&size,HEADER_LEN,buf); + buf = readPDUType(&pduType,buf); + buf = readBoolean(&result,buf); + buf = readBitMap(&versionBM,buf); + buf = readBitMap(&optionsBM,buf); + buf = readNum(&prefSize,buf); + buf = readNum(&maxMsgSize,buf); + + /* decode optionsBM */ + search = bitAtPos(0,optionsBM); + present = bitAtPos(1,optionsBM); + delete = bitAtPos(2,optionsBM); + accessControl = bitAtPos(3,optionsBM); + resourceControl = bitAtPos(4,optionsBM); + + /* read optional part */ + while (buf < (buffer + size + HEADER_LEN)) + { data_tag tag = peekTag(buf); + switch (tag) + { case DT_IDAuthentication: + buf = readString(&auth,buf); + break; + case DT_ImplementationID: + buf = readString(&id,buf); + break; + case DT_ImplementationName: + buf = readString(&name,buf); + break; + case DT_ImplementationVersion: + buf = readString(&version,buf); + break; + case DT_ReferenceID: + buf = readAny(&refID,buf); + break; + default: + freeBitMap(versionBM); + freeBitMap(optionsBM); + s_free(auth); + s_free(id); + s_free(name); + s_free(version); + freeAny(refID); + REPORT_READ_ERROR(buf); + break; + } + } + + buf = readInitResponseInfo(&userInfo,buf); + if (buf == NULL) + { freeBitMap(versionBM); + freeBitMap(optionsBM); + s_free(auth); + s_free(id); + s_free(name); + s_free(version); + freeAny(refID); + } + RETURN_ON_NULL(buf); + + /* construct the basic init object */ + *init = makeInitResponseAPDU(result, + search,present,delete,accessControl,resourceControl, + prefSize,maxMsgSize,auth,id,name,version,refID,userInfo); + + freeBitMap(versionBM); + freeBitMap(optionsBM); + s_free(auth); + s_free(id); + s_free(name); + s_free(version); + freeAny(refID); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +InitResponseAPDU* +replyToInitAPDU(init,result,userInfo) +InitAPDU* init; +boolean result; +void* userInfo; +/* respond to an init message in the default way - echoing back + the init info + */ +{ + InitResponseAPDU* initResp; + initResp = makeInitResponseAPDU(result, + init->willSearch,init->willPresent,init->willDelete, + init->supportAccessControl,init->supportResourceControl, + init->PreferredMessageSize,init->MaximumRecordSize, + init->IDAuthentication,defaultImplementationID(),defaultImplementationName(), + defaultImplementationVersion(), + init->ReferenceID,userInfo); + return(initResp); +} + +/*----------------------------------------------------------------------*/ + +SearchAPDU* +makeSearchAPDU(small, + large, + medium, + replace, + name, + databases, + type, + elements, + refID, + queryInfo) +long small; +long large; +long medium; +boolean replace; +char* name; +char** databases; +char* type; +char** elements; +any* refID; +void* queryInfo; +{ + char* ptr = NULL; + long i; + SearchAPDU* query = (SearchAPDU*)s_malloc((size_t)sizeof(SearchAPDU)); + query->PDUType = searchAPDU; + query->SmallSetUpperBound = small; + query->LargeSetLowerBound = large; + query->MediumSetPresentNumber = medium; + query->ReplaceIndicator = replace; + query->ResultSetName = s_strdup(name); + query->DatabaseNames = NULL; + if (databases != NULL) + { for (i = 0, ptr = databases[i]; ptr != NULL; ptr = databases[++i]) + { if (query->DatabaseNames == NULL) + query->DatabaseNames = (char**)s_malloc((size_t)(sizeof(char*) * 2)); + else + query->DatabaseNames = (char**)s_realloc((char*)query->DatabaseNames, + (size_t)(sizeof(char*) * (i + 2))); + query->DatabaseNames[i] = s_strdup(ptr); + query->DatabaseNames[i+1] = NULL; + } + } + query->QueryType = s_strdup(type); + query->ElementSetNames = NULL; + if (elements != NULL) + { for (i = 0, ptr = elements[i]; ptr != NULL; ptr = elements[++i]) + { if (query->ElementSetNames == NULL) + query->ElementSetNames = (char**)s_malloc((size_t)(sizeof(char*) * 2)); + else + query->ElementSetNames = (char**)s_realloc((char*)query->ElementSetNames, + (size_t)(sizeof(char*) * (i + 2))); + query->ElementSetNames[i] = s_strdup(ptr); + query->ElementSetNames[i+1] = NULL; + } + } + query->ReferenceID = duplicateAny(refID); + query->Query = queryInfo; /* not copied! */ + return(query); +} + +/*----------------------------------------------------------------------*/ + +void +freeSearchAPDU(query) +SearchAPDU* query; +{ + s_free(query->ResultSetName); + s_free(query->QueryType); + doList((void**)query->DatabaseNames,fs_free); /* can't use the macro here ! */ + s_free(query->DatabaseNames); + doList((void**)query->ElementSetNames,fs_free); /* can't use the macro here ! */ + s_free(query->ElementSetNames); + freeAny(query->ReferenceID); + s_free(query); +} + +/*----------------------------------------------------------------------*/ + +#define DB_DELIMITER "\037" /* hex 1F occurs between each database name */ +#define ES_DELIMITER_1 "\037" /* separates database name from element name */ +#define ES_DELIMITER_2 "\036" /* hex 1E separates <db,es> groups from one another */ + +char* +writeSearchAPDU(query,buffer,len) +SearchAPDU* query; +char* buffer; +long* len; +{ + char* buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */ + long size,i; + char* ptr = NULL; + char* scratch = NULL; + + RESERVE_SPACE_FOR_HEADER(len); + + buf = writePDUType(query->PDUType,buf,len); + buf = writeBinaryInteger(query->SmallSetUpperBound,(size_t)3,buf,len); + buf = writeBinaryInteger(query->LargeSetLowerBound,(size_t)3,buf,len); + buf = writeBinaryInteger(query->MediumSetPresentNumber,(size_t)3,buf,len); + buf = writeBoolean(query->ReplaceIndicator,buf,len); + buf = writeString(query->ResultSetName,DT_ResultSetName,buf,len); + /* write database names */ + if (query->DatabaseNames != NULL) + { for (i = 0,scratch = NULL, ptr = query->DatabaseNames[i]; ptr != NULL; ptr = query->DatabaseNames[++i]) + { if (scratch == NULL) + scratch = s_strdup(ptr); + else + { size_t newScratchSize = (size_t)(strlen(scratch) + strlen(ptr) + 2); + scratch = (char*)s_realloc(scratch,newScratchSize); + s_strncat(scratch,DB_DELIMITER,2,newScratchSize); + s_strncat(scratch,ptr,strlen(ptr) + 1,newScratchSize); + } + } + buf = writeString(scratch,DT_DatabaseNames,buf,len); + s_free(scratch); + } + buf = writeString(query->QueryType,DT_QueryType,buf,len); + /* write element set names */ + if (query->ElementSetNames != NULL) + { for (i = 0,scratch = NULL, ptr = query->ElementSetNames[i]; ptr != NULL; ptr = query->ElementSetNames[++i]) + { if (scratch == NULL) + { if (query->ElementSetNames[i+1] == NULL) /* there is a single element set name */ + { scratch = (char*)s_malloc((size_t)strlen(ptr) + 2); + strncpy(scratch,ES_DELIMITER_1,2); + s_strncat(scratch,ptr,strlen(ptr) + 1,strlen(ptr) + 2); + } + else /* this is the first of a series of element set names */ + { size_t newScratchSize = (size_t)(strlen(ptr) + strlen(query->ElementSetNames[i + 1]) + 2); + scratch = s_strdup(ptr); /* the database name */ + ptr = query->ElementSetNames[++i]; /* the element set name */ + scratch = (char*)s_realloc(scratch,newScratchSize); + s_strncat(scratch,ES_DELIMITER_1,2,newScratchSize); + s_strncat(scratch,ptr,strlen(ptr) + 1,newScratchSize); + } + } + else + { char* esPtr = query->ElementSetNames[++i]; /* the element set name */ + size_t newScratchSize = (size_t)(strlen(scratch) + strlen(ptr) + strlen(esPtr) + 3); + scratch = (char*)s_realloc(scratch,newScratchSize); + s_strncat(scratch,ES_DELIMITER_2,2,newScratchSize); + s_strncat(scratch,ptr,strlen(ptr) + 1,newScratchSize); + s_strncat(scratch,ES_DELIMITER_1,2,newScratchSize); + s_strncat(scratch,esPtr,strlen(esPtr) + 1,newScratchSize); + } + } + buf = writeString(scratch,DT_ElementSetNames,buf,len); + s_free(scratch); + } + buf = writeAny(query->ReferenceID,DT_ReferenceID,buf,len); + + /* go back and write the header-length-indicator */ + RELEASE_HEADER_SPACE(len); + size = buf - buffer - HEADER_LEN; + writeBinaryInteger(size,HEADER_LEN,buffer,len); + + if (query->Query != NULL) + buf = writeSearchInfo(query,buf,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +SearchResponseAPDU* +makeSearchResponseAPDU(result,count,recordsReturned,nextPos,resultStatus, + presentStatus,refID,records) +long result; +long count; +long recordsReturned; +long nextPos; +long resultStatus; +long presentStatus; +any* refID; +void* records; +{ + SearchResponseAPDU* query = (SearchResponseAPDU*)s_malloc((size_t)sizeof(SearchResponseAPDU)); + query->PDUType = searchResponseAPDU; + query->SearchStatus = result; + query->ResultCount = count; + query->NumberOfRecordsReturned = recordsReturned; + query->NextResultSetPosition = nextPos; + query->ResultSetStatus = resultStatus; + query->PresentStatus = presentStatus; + query->ReferenceID = duplicateAny(refID); + query->DatabaseDiagnosticRecords = records; + return(query); +} + +/*----------------------------------------------------------------------*/ + +void +freeSearchResponseAPDU(queryResponse) +SearchResponseAPDU* queryResponse; +{ + freeAny(queryResponse->ReferenceID); + s_free(queryResponse); +} + +/*----------------------------------------------------------------------*/ + +char* +writeSearchResponseAPDU(queryResponse,buffer,len) +SearchResponseAPDU* queryResponse; +char* buffer; +long* len; +{ + char* buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */ + long size; + + RESERVE_SPACE_FOR_HEADER(len); + + buf = writePDUType(queryResponse->PDUType,buf,len); + buf = writeBinaryInteger(queryResponse->SearchStatus,(size_t)1,buf,len); + buf = writeBinaryInteger(queryResponse->ResultCount,(size_t)3,buf,len); + buf = writeBinaryInteger(queryResponse->NumberOfRecordsReturned,(size_t)3,buf,len); + buf = writeBinaryInteger(queryResponse->NextResultSetPosition,(size_t)3,buf,len); + buf = writeNum(queryResponse->ResultSetStatus,DT_ResultSetStatus,buf,len); + buf = writeNum(queryResponse->PresentStatus,DT_PresentStatus,buf,len); + buf = writeAny(queryResponse->ReferenceID,DT_ReferenceID,buf,len); + + /* go back and write the header-length-indicator */ + RELEASE_HEADER_SPACE(len); + size = buf - buffer - HEADER_LEN; + writeBinaryInteger(size,HEADER_LEN,buffer,len); + + if (queryResponse->DatabaseDiagnosticRecords != NULL) + buf = writeSearchResponseInfo(queryResponse,buf,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readSearchResponseAPDU(queryResponse,buffer) +SearchResponseAPDU** queryResponse; +char* buffer; +{ + char* buf = buffer; + long size; + pdu_type pduType; + long result,count,recordsReturned,nextPos; + long resultStatus,presentStatus; + any *refID = NULL; + void* userInfo = NULL; + + /* read required part */ + buf = readBinaryInteger(&size,HEADER_LEN,buf); + buf = readPDUType(&pduType,buf); + buf = readBinaryInteger(&result,(size_t)1,buf); + buf = readBinaryInteger(&count,(size_t)3,buf); + buf = readBinaryInteger(&recordsReturned,(size_t)3,buf); + buf = readBinaryInteger(&nextPos,(size_t)3,buf); + + resultStatus = presentStatus = UNUSED; + refID = NULL; + + /* read optional part */ + while (buf < (buffer + size + HEADER_LEN)) + { data_tag tag = peekTag(buf); + switch (tag) + { case DT_ResultSetStatus: + buf = readNum(&resultStatus,buf); + break; + case DT_PresentStatus: + buf = readNum(&presentStatus,buf); + break; + case DT_ReferenceID: + buf = readAny(&refID,buf); + break; + default: + freeAny(refID); + REPORT_READ_ERROR(buf); + break; + } + } + + buf = readSearchResponseInfo(&userInfo,buf); + if (buf == NULL) + freeAny(refID); + RETURN_ON_NULL(buf); + + /* construct the search object */ + *queryResponse = makeSearchResponseAPDU(result,count,recordsReturned,nextPos, + (long)resultStatus,(long)presentStatus,refID,userInfo); + + freeAny(refID); + + return(buf); +} + + +/* +** Routines originally from ZUtil.c -- FM +** +**----------------------------------------------------------------------*/ +/* WIDE AREA INFORMATION SERVER SOFTWARE: + No guarantees or restrictions. See the readme file for the full standard + disclaimer. + + 3.26.90 Harry Morris, morris@think.com + 3.30.90 Harry Morris - Changed any->bits to any->bytes + 4.11.90 HWM - fixed include file names, changed + - writeCompressedIntegerWithPadding() to + writeCompressedIntWithPadding() + - generalized conditional includes (see c-dialect.h) + 3.7.91 Jonny Goldman. Replaced "short" in makeBitMap with "int" line 632. +*/ + +char* readErrorPosition = NULL; /* pos where buf stoped making sense */ + +/*----------------------------------------------------------------------*/ +/* A note on error handling + read - these are low level routines, they do not check the type tags + which (sometimes) preceed the data (this is done by the higher + level functions which call these functions). There is no + attempt made to check that the reading does not exceed the read + buffer. Such cases should be very rare and usually will be + caught by the calling functions. (note - it is unlikely that + a series of low level reads will go far off the edge without + triggering a type error. However, it is possible for a single + bad read in an array function (eg. readAny) to attempt to read a + large ammount, possibly causing a segmentation violation or out + of memory condition. + */ +/*----------------------------------------------------------------------*/ + +diagnosticRecord* +makeDiag(surrogate,code,addInfo) +boolean surrogate; +char* code; +char* addInfo; +{ + diagnosticRecord* diag = + (diagnosticRecord*)s_malloc((size_t)sizeof(diagnosticRecord)); + + diag->SURROGATE = surrogate; + memcpy(diag->DIAG,code,DIAGNOSTIC_CODE_SIZE); + diag->ADDINFO = s_strdup(addInfo); + + return(diag); +} + +/*----------------------------------------------------------------------*/ + +void +freeDiag(diag) +diagnosticRecord* diag; +{ + if (diag != NULL) + { if (diag->ADDINFO != NULL) + s_free(diag->ADDINFO); + s_free(diag); + } +} + +/*----------------------------------------------------------------------*/ + +#define END_OF_RECORD 0x1D + +char* +writeDiag(diag,buffer,len) +diagnosticRecord* diag; +char* buffer; +long* len; +/* diagnostics (as per Appendix D) have a very weird format - this changes + in SR-1 + */ +{ + char* buf = buffer; + long length; + + if (diag == NULL) /* handle unspecified optional args */ + return(buf); + + buf = writeTag(DT_DatabaseDiagnosticRecords,buf,len); + CHECK_FOR_SPACE_LEFT(0,len); + + length = 3; + if (diag->ADDINFO != NULL) + length += strlen(diag->ADDINFO); + + if (length >= 0xFFFF ) /* make sure the length is reasonable */ + { length = 0xFFFF - 1; + diag->ADDINFO[0xFFFF - 3 - 1] = '\0'; + } + + buf = writeBinaryInteger(length,2,buf,len); + + CHECK_FOR_SPACE_LEFT(1,len); + buf[0] = diag->DIAG[0]; + buf++; + + CHECK_FOR_SPACE_LEFT(1,len); + buf[0] = diag->DIAG[1]; + buf++; + + if (length > 3) + { CHECK_FOR_SPACE_LEFT(3,len); + memcpy(buf,diag->ADDINFO,(size_t)length - 3); + buf += length - 3; + } + + CHECK_FOR_SPACE_LEFT(1,len); + buf[0] = diag->SURROGATE; + buf++; + + CHECK_FOR_SPACE_LEFT(1,len); + buf[0] = END_OF_RECORD; + buf++; + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readDiag(diag,buffer) +diagnosticRecord** diag; +char* buffer; +{ + char* buf = buffer; + diagnosticRecord* d + = (diagnosticRecord*)s_malloc((size_t)sizeof(diagnosticRecord)); + data_tag tag; + long len; + + buf = readTag(&tag,buf); + + buf = readBinaryInteger(&len,2,buf); + + d->DIAG[0] = buf[0]; + d->DIAG[1] = buf[1]; + d->DIAG[2] = '\0'; + + if (len > 3) + { d->ADDINFO = (char*)s_malloc((size_t)(len - 3 + 1)); + memcpy(d->ADDINFO,(char*)(buf + 2),(size_t)(len - 3)); + d->ADDINFO[len - 3] = '\0'; + } + else + d->ADDINFO = NULL; + + d->SURROGATE = buf[len - 1]; + + *diag = d; + + return(buf + len + 1); +} + +/*----------------------------------------------------------------------*/ + +#define continueBit 0x80 +#define dataMask 0x7F +#define dataBits 7 + +char* +writeCompressedInteger(num,buf,len) +unsigned long num; +char* buf; +long* len; +/* write a binary integer in the format described on p. 40. + this might be sped up +*/ +{ + char byte; + long i; + unsigned long size; + + size = writtenCompressedIntSize(num); + CHECK_FOR_SPACE_LEFT(size,len); + + for (i = size - 1; i >= 0; i--) + { byte = num & dataMask; + if (i != (size-1)) /* turn on continue bit */ + byte = (char)(byte | continueBit); + buf[i] = byte; + num = num >> dataBits; /* don't and here */ + } + + return(buf + size); +} + +/*----------------------------------------------------------------------*/ + +char* +readCompressedInteger(num,buf) +unsigned long *num; +char* buf; +/* read a binary integer in the format described on p. 40. + this might be sped up +*/ +{ + long i = 0; + unsigned char byte; + + *num = 0; + + do + { byte = buf[i++]; + *num = *num << dataBits; + *num += (byte & dataMask); + } + while (byte & continueBit); + + return(buf + i); +} + +/*----------------------------------------------------------------------*/ + +#define pad 128 /* high bit is set */ + +char* +writeCompressedIntWithPadding(num,size,buffer,len) +unsigned long num; +unsigned long size; +char* buffer; +long* len; +/* Like writeCompressedInteger, except writes padding (128) to make + sure that size bytes are used. This can be read correctly by + readCompressedInteger() +*/ +{ + char* buf = buffer; + unsigned long needed,padding; + long i; + + CHECK_FOR_SPACE_LEFT(size,len); + + needed = writtenCompressedIntSize(num); + padding = size - needed; + i = padding - 1; + + for (i = padding - 1;i >= 0;i--) + { buf[i] = pad; + } + + buf = writeCompressedInteger(num,buf + padding,len); + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +unsigned long +writtenCompressedIntSize(num) +unsigned long num; +/* return the number of bytes needed to represnet the value num in + compressed format. curently limited to 4 bytes + */ +{ + if (num < CompressedInt1Byte) + return(1); + else if (num < CompressedInt2Byte) + return(2); + else if (num < CompressedInt3Byte) + return(3); + else + return(4); +} + +/*----------------------------------------------------------------------*/ + +char* +writeTag(tag,buf,len) +data_tag tag; +char* buf; +long* len; +/* write out a data tag */ +{ + return(writeCompressedInteger(tag,buf,len)); +} + +/*----------------------------------------------------------------------*/ + +char* +readTag(tag,buf) +data_tag* tag; +char* buf; +/* read a data tag */ +{ + return(readCompressedInteger(tag,buf)); +} + +/*----------------------------------------------------------------------*/ + +unsigned long +writtenTagSize(tag) +data_tag tag; +{ + return(writtenCompressedIntSize(tag)); +} + +/*----------------------------------------------------------------------*/ + +data_tag +peekTag(buf) +char* buf; +/* read a data tag without advancing the buffer */ +{ + data_tag tag; + readTag(&tag,buf); + return(tag); +} + +/*----------------------------------------------------------------------*/ + +any* +makeAny(size,data) +unsigned long size; +char* data; +{ + any* a = (any*)s_malloc((size_t)sizeof(any)); + a->size = size; + a->bytes = data; + return(a); +} + +/*----------------------------------------------------------------------*/ + +void +freeAny(a) +any* a; +/* destroy an any and its associated data. Assumes a->bytes was + allocated using the s_malloc family of libraries + */ +{ + if (a != NULL) + { if (a->bytes != NULL) + s_free(a->bytes); + s_free(a); + } +} + +/*----------------------------------------------------------------------*/ + +any* +duplicateAny(a) +any* a; +{ + any* copy = NULL; + + if (a == NULL) + return(NULL); + + copy = (any*)s_malloc((size_t)sizeof(any)); + copy->size = a->size; + if (a->bytes == NULL) + copy->bytes = NULL; + else + { copy->bytes = (char*)s_malloc((size_t)copy->size); + memcpy(copy->bytes,a->bytes,(size_t)copy->size); + } + return(copy); +} + +/*----------------------------------------------------------------------*/ + +char* +writeAny(a,tag,buffer,len) +any* a; +data_tag tag; +char* buffer; +long* len; +/* write an any + tag and size info */ +{ + char* buf = buffer; + + if (a == NULL) /* handle unspecified optional args */ + return(buf); + + /* write the tags */ + buf = writeTag(tag,buf,len); + buf = writeCompressedInteger(a->size,buf,len); + + /* write the bytes */ + CHECK_FOR_SPACE_LEFT(a->size,len); + memcpy(buf,a->bytes,(size_t)a->size); + + return(buf+a->size); +} + +/*----------------------------------------------------------------------*/ + + +char *readAny(anAny,buffer) +any** anAny; +char* buffer; +/* read an any + tag and size info */ +{ + char *buf; + any* a; + data_tag tag; + + + +a=(any*)s_malloc((size_t)sizeof(any)); + + buf=buffer; + + buf = readTag(&tag,buf); + + buf = readCompressedInteger(&a->size,buf); + + /* now simply copy the bytes */ + a->bytes = (char*)s_malloc((size_t)a->size); + memcpy(a->bytes,buf,(size_t)a->size); + *anAny = a; + + return(buf+a->size); +} + +/*----------------------------------------------------------------------*/ + +unsigned long +writtenAnySize(tag,a) +data_tag tag; +any* a; +{ + unsigned long size; + + if (a == NULL) + return(0); + + size = writtenTagSize(tag); + size += writtenCompressedIntSize(a->size); + size += a->size; + return(size); +} + +/*----------------------------------------------------------------------*/ + +any* +stringToAny(s) +char* s; +{ + any* a = NULL; + + if (s == NULL) + return(NULL); + + a = (any*)s_malloc((size_t)sizeof(any)); + a->size = strlen(s); + a->bytes = (char*)s_malloc((size_t)a->size); + memcpy(a->bytes,s,(size_t)a->size); + return(a); +} + +/*----------------------------------------------------------------------*/ + +char* +anyToString(a) +any* a; +{ + char* s = NULL; + + if (a == NULL) + return(NULL); + + s = s_malloc((size_t)(a->size + 1)); + memcpy(s,a->bytes,(size_t)a->size); + s[a->size] = '\0'; + return(s); +} + +/*----------------------------------------------------------------------*/ + +char* +writeString(s,tag,buffer,len) +char* s; +data_tag tag; +char* buffer; +long* len; +/* Write a C style string. The terminating null is not written. + This function is not part of the Z39.50 spec. It is provided + for the convienience of those wishing to pass C strings in + the place of an any. + */ +{ + char* buf = buffer; + any* data = NULL; + if (s == NULL) + return(buffer); /* handle unused optional item before making an any */ + data = (any*)s_malloc((size_t)sizeof(any)); + data->size = strlen(s); + data->bytes = s; /* save a copy here by not using stringToAny() */ + buf = writeAny(data,tag,buf,len); + s_free(data); /* don't use freeAny() since it will free s too */ + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readString(s ,buffer) +char** s ; +char* buffer; +/* Read an any and convert it into a C style string. + This function is not part of the Z39.50 spec. It is provided + for the convienience of those wishing to pass C strings in + the place of an any. + */ +{ + any* data = NULL; + char* buf = readAny(&data,buffer); + *s = anyToString(data); + freeAny(data); + return(buf); +} + +/*----------------------------------------------------------------------*/ + +unsigned long +writtenStringSize(tag,s) +data_tag tag; +char* s; +{ + unsigned long size; + + if (s == NULL) + return(0); + + size = writtenTagSize(tag); + size += writtenCompressedIntSize(size); + size += strlen(s); + return(size); +} + +/*----------------------------------------------------------------------*/ + +any* +longToAny(num) +long num; +/* a convienience function */ +{ + char s[40]; + + sprintf(s,"%ld",num); + + return(stringToAny(s)); +} + +/*----------------------------------------------------------------------*/ + +long +anyToLong(a) +any* a; +/* a convienience function */ +{ + long num; + char* str = NULL; + str = anyToString(a); + sscanf(str,"%ld",&num); /* could check the result and return + an error */ + s_free(str); + return(num); +} + +/*----------------------------------------------------------------------*/ + +#define bitsPerByte 8 + +bit_map* +makeBitMap(unsigned long numBits, ...) +/* construct and return a bitmap with numBits elements */ +{ + va_list ap; + long i,j; + bit_map* bm = NULL; + + va_start(ap,numBits); + + bm = (bit_map*)s_malloc((size_t)sizeof(bit_map)); + bm->size = (unsigned long)ceil((double)numBits / bitsPerByte); + bm->bytes = (char*)s_malloc((size_t)bm->size); + + /* fill up the bits */ + for (i = 0; i < bm->size; i++) /* iterate over bytes */ + { char byte = 0; + for (j = 0; j < bitsPerByte; j++) /* iterate over bits */ + { if ((i * bitsPerByte + j) < numBits) + { boolean bit = false; + bit = (boolean)va_arg(ap,boolean); + if (bit) + { byte = byte | (1 << (bitsPerByte - j - 1)); + } + } + } + bm->bytes[i] = byte; + } + + va_end(ap); + return(bm); +} + + +/*----------------------------------------------------------------------*/ + +void +freeBitMap(bm) +bit_map* bm; +/* destroy a bit map created by makeBitMap() */ +{ + s_free(bm->bytes); + s_free(bm); +} + +/*----------------------------------------------------------------------*/ + +/* use this routine to interpret a bit map. pos specifies the bit + number. bit 0 is the Leftmost bit of the first byte. + Could do bounds checking. + */ + +boolean +bitAtPos(pos,bm) +long pos; +bit_map* bm; +{ + if (pos > bm->size*bitsPerByte) + return false; + else + return((bm->bytes[(pos / bitsPerByte)] & + (0x80>>(pos % bitsPerByte))) ? + true : false); +} + +/*----------------------------------------------------------------------*/ + +char* +writeBitMap(bm,tag,buffer,len) +bit_map* bm; +data_tag tag; +char* buffer; +long* len; +/* write a bitmap + type and size info */ +{ + return(writeAny((any*)bm,tag,buffer,len)); +} + +/*----------------------------------------------------------------------*/ + +char* +readBitMap(bm,buffer) +bit_map** bm; +char* buffer; +/* read a bitmap + type and size info */ +{ + char *c; + + + +c=readAny((any**)bm,buffer); + + return(c); +} + +/*----------------------------------------------------------------------*/ + +char* +writeByte(byte,buf,len) +unsigned long byte; +char* buf; +long* len; +{ + CHECK_FOR_SPACE_LEFT(1,len); + buf[0] = byte & 0xFF; /* we really only want the first byte */ + return(buf + 1); +} + +/*----------------------------------------------------------------------*/ + +char* +readByte(byte,buf) +unsigned char* byte; +char* buf; +{ + *byte = buf[0]; + return(buf + 1); +} + +/*----------------------------------------------------------------------*/ + +char* +writeBoolean(flag,buf,len) +boolean flag; +char* buf; +long* len; +{ + return(writeByte(flag,buf,len)); +} + +/*----------------------------------------------------------------------*/ + +char* +readBoolean(flag,buffer) +boolean* flag; +char* buffer; +{ + unsigned char byte; + char* buf = readByte(&byte,buffer); + *flag = (byte == true) ? true : false; + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +writePDUType(pduType,buf,len) +pdu_type pduType; +char* buf; +long* len; +/* PDUType is a single byte */ +{ + return(writeBinaryInteger((long)pduType,(unsigned long)1,buf,len)); +} + +/*----------------------------------------------------------------------*/ + +char* +readPDUType(pduType,buf) +pdu_type* pduType; +char* buf; +/* PDUType is a single byte */ +{ + return(readBinaryInteger((long*)pduType,(unsigned long)1,buf)); +} + +/*----------------------------------------------------------------------*/ + +pdu_type +peekPDUType(buf) +char* buf; +/* read the next pdu without advancing the buffer, Note that this + function is to be used on a buffer that is known to contain an + APDU. The pdu_type is written HEADER_LEN bytes into the buffer + */ +{ + pdu_type pdu; + readPDUType(&pdu,buf + HEADER_LEN); + return(pdu); +} + +/*----------------------------------------------------------------------*/ + +#define BINARY_INTEGER_BYTES sizeof(long) /* the number of bytes used by + a "binary integer" */ +char* +writeBinaryInteger(num,size,buf,len) +long num; +unsigned long size; +char* buf; +long* len; +/* write out first size bytes of num - no type info + XXX should this take unsigned longs instead ??? */ +{ + long i; + char byte; + + if (size < 1 || size > BINARY_INTEGER_BYTES) + return(NULL); /* error */ + + CHECK_FOR_SPACE_LEFT(size,len); + + for (i = size - 1; i >= 0; i--) + { byte = (char)(num & 255); + buf[i] = byte; + num = num >> bitsPerByte; /* don't and here */ + } + + return(buf + size); +} + +/*----------------------------------------------------------------------*/ + +char* +readBinaryInteger(num,size,buf) +long* num; +unsigned long size; +char* buf; +/* read in first size bytes of num - no type info + XXX this should take unsigned longs instead !!! */ +{ + long i; + unsigned char byte; + + if (size < 1 || size > BINARY_INTEGER_BYTES) + return(buf); /* error */ + *num = 0; + + for (i = 0; i < size; i++) + { byte = buf[i]; + *num = *num << bitsPerByte; + *num += byte; + } + + return(buf + size); +} + +/*----------------------------------------------------------------------*/ + +unsigned long +writtenCompressedBinIntSize(num) +long num; +/* return the number of bytes needed to represent the value num. + currently limited to max of 4 bytes + Only compresses for positive nums - negatives get whole 4 bytes + */ +{ + if (num < 0L) + return(4); + else if (num < 256L) /* 2**8 */ + return(1); + else if (num < 65536L) /* 2**16 */ + return(2); + else if (num < 16777216L) /* 2**24 */ + return(3); + else + return(4); +} + +/*----------------------------------------------------------------------*/ + +char* +writeNum(num,tag,buffer,len) +long num; +data_tag tag; +char* buffer; +long* len; +/* write a binary integer + size and tag info */ +{ + char* buf = buffer; + long size = writtenCompressedBinIntSize(num); + + if (num == UNUSED) + return(buffer); + + buf = writeTag(tag,buf,len); + buf = writeCompressedInteger(size,buf,len); + buf = writeBinaryInteger(num,(unsigned long)size,buf,len); + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readNum(num,buffer) +long* num; +char* buffer; +/* read a binary integer + size and tag info */ +{ + char* buf = buffer; + data_tag tag; + unsigned long size; + unsigned long val; + + buf = readTag(&tag,buf); + buf = readCompressedInteger(&val,buf); + size = (unsigned long)val; + buf = readBinaryInteger(num,size,buf); + return(buf); +} + +/*----------------------------------------------------------------------*/ + +unsigned long +writtenNumSize(tag,num) +data_tag tag; +long num; +{ + long dataSize = writtenCompressedBinIntSize(num); + long size; + + size = writtenTagSize(tag); /* space for the tag */ + size += writtenCompressedIntSize(dataSize); /* space for the size */ + size += dataSize; /* space for the data */ + + return(size); +} + +/*----------------------------------------------------------------------*/ + +typedef void (voidfunc)(); + +void +doList(list,func) +void** list; +voidfunc *func; +/* call func on each element of the NULL terminated list of pointers */ +{ + register long i; + register void* ptr = NULL; + if (list == NULL) + return; + for (i = 0,ptr = list[i]; ptr != NULL; ptr = list[++i]) + (*func)(ptr); +} + +/*----------------------------------------------------------------------*/ + +char* +writeProtocolVersion(buf,len) +char* buf; +long* len; +/* write a bitmap describing the protocols available */ +{ + static bit_map* version = NULL; + + if (version == NULL) + { version = makeBitMap((unsigned long)1,true); /* version 1! */ + } + + return(writeBitMap(version,DT_ProtocolVersion,buf,len)); +} + +/*----------------------------------------------------------------------*/ + +char* +defaultImplementationID() +{ + static char ImplementationID[] = "TMC"; + return(ImplementationID); +} + +/*----------------------------------------------------------------------*/ + +char* +defaultImplementationName() +{ + static char ImplementationName[] = "Thinking Machines Corporation Z39.50"; + return(ImplementationName); +} + +/*----------------------------------------------------------------------*/ + +char* +defaultImplementationVersion() +{ + static char ImplementationVersion[] = "2.0A"; + return(ImplementationVersion); +} + +/*----------------------------------------------------------------------*/ + + +/* +** Routines originally from ZType1.c -- FM +** +**----------------------------------------------------------------------*/ +/* WIDE AREA INFORMATION SERVER SOFTWARE: + No guarantees or restrictions. See the readme file for the full standard + disclaimer. + + 3.26.90 Harry Morris, morris@think.com + 4.11.90 HWM - generalized conditional includes (see c-dialect.h) +*/ +/*----------------------------------------------------------------------*/ + +query_term* +makeAttributeTerm(use, + relation, + position, + structure, + truncation, + completeness, + term) +char* use; +char* relation; +char* position; +char* structure; +char* truncation; +char* completeness; +any* term; +{ + query_term* qt = (query_term*)s_malloc((size_t)sizeof(query_term)); + + qt->TermType = TT_Attribute; + + /* copy in the attributes */ + strncpy(qt->Use,use,ATTRIBUTE_SIZE); + strncpy(qt->Relation,relation,ATTRIBUTE_SIZE); + strncpy(qt->Position,position,ATTRIBUTE_SIZE); + strncpy(qt->Structure,structure,ATTRIBUTE_SIZE); + strncpy(qt->Truncation,truncation,ATTRIBUTE_SIZE); + strncpy(qt->Completeness,completeness,ATTRIBUTE_SIZE); + + qt->Term = duplicateAny(term); + + qt->ResultSetID = NULL; + + return(qt); +} + +/*----------------------------------------------------------------------*/ + +query_term* +makeResultSetTerm(resultSet) +any* resultSet; +{ + query_term* qt = (query_term*)s_malloc((size_t)sizeof(query_term)); + + qt->TermType = TT_ResultSetID; + + qt->ResultSetID = duplicateAny(resultSet); + + qt->Term = NULL; + + return(qt); +} + +/*----------------------------------------------------------------------*/ + +query_term* +makeOperatorTerm(operatorCode) +char* operatorCode; +{ + query_term* qt = (query_term*)s_malloc((size_t)sizeof(query_term)); + + qt->TermType = TT_Operator; + + strncpy(qt->Operator,operatorCode,OPERATOR_SIZE); + + qt->Term = NULL; + qt->ResultSetID = NULL; + + return(qt); +} + +/*----------------------------------------------------------------------*/ + +void +freeTerm(qt) +query_term* qt; +{ + switch (qt->TermType) + { case TT_Attribute: + freeAny(qt->Term); + break; + case TT_ResultSetID: + freeAny(qt->ResultSetID); + break; + case TT_Operator: + /* do nothing */ + break; + default: + panic("Implementation error: Unknown term type %ld", + qt->TermType); + break; + } + s_free(qt); +} + +/*----------------------------------------------------------------------*/ + +#define ATTRIBUTE_LIST_SIZE ATTRIBUTE_SIZE * 6 +#define AT_DELIMITER " " + +char* +writeQueryTerm(qt,buffer,len) +query_term* qt; +char* buffer; +long* len; +{ + char* buf = buffer; + char attributes[ATTRIBUTE_LIST_SIZE]; + + switch (qt->TermType) + { case TT_Attribute: + strncpy(attributes,qt->Use,ATTRIBUTE_LIST_SIZE); + s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE); + s_strncat(attributes,qt->Relation,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE); + s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE); + s_strncat(attributes,qt->Position,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE); + s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE); + s_strncat(attributes,qt->Structure,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE); + s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE); + s_strncat(attributes,qt->Truncation,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE); + s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE); + s_strncat(attributes,qt->Completeness,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE); + buf = writeString(attributes,DT_AttributeList,buf,len); + buf = writeAny(qt->Term,DT_Term,buf,len); + break; + case TT_ResultSetID: + buf = writeAny(qt->ResultSetID,DT_ResultSetID,buf,len); + break; + case TT_Operator: + buf = writeString(qt->Operator,DT_Operator,buf,len); + break; + default: + panic("Implementation error: Unknown term type %ld", + qt->TermType); + break; + } + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +char* +readQueryTerm(qt,buffer) +query_term** qt; +char* buffer; +{ + char* buf = buffer; + char *attributeList = NULL; + char* operator = NULL; + any* term; + char* use = NULL; + char* relation = NULL; + char* position = NULL; + char* structure = NULL; + char* truncation = NULL; + char* completeness; + any* resultSetID = NULL; + data_tag tag; + + + tag = peekTag(buffer); + + switch(tag) + { case DT_AttributeList: + buf = readString(&attributeList,buf); + buf = readAny(&term,buf); + use = strtok(attributeList,AT_DELIMITER); + relation = strtok(NULL,AT_DELIMITER); + position = strtok(NULL,AT_DELIMITER); + structure = strtok(NULL,AT_DELIMITER); + truncation = strtok(NULL,AT_DELIMITER); + completeness = strtok(NULL,AT_DELIMITER); + *qt = makeAttributeTerm(use,relation,position,structure, + truncation,completeness,term); + s_free(attributeList); + freeAny(term); + break; + case DT_ResultSetID: + buf = readAny(&resultSetID,buf); + *qt = makeResultSetTerm(resultSetID); + freeAny(resultSetID); + break; + case DT_Operator: + buf = readString(&operator,buf); + *qt = makeOperatorTerm(operator); + s_free(operator); + break; + default: + REPORT_READ_ERROR(buf); + break; + } + + return(buf); +} + +/*----------------------------------------------------------------------*/ + +static unsigned long getQueryTermSize _AP((query_term* qt)); + +static unsigned long +getQueryTermSize(qt) +query_term* qt; +/* figure out how many bytes it will take to write this query */ +{ + unsigned long size; + static char attributes[] = "11 22 33 44 55 66"; /* we just need this to + calculate its written + size */ + + switch (qt->TermType) + { case TT_Attribute: + size = writtenStringSize(DT_AttributeList,attributes); + size += writtenAnySize(DT_Term,qt->Term); + break; + case TT_ResultSetID: + size = writtenAnySize(DT_ResultSetID,qt->ResultSetID); + break; + case TT_Operator: + size = writtenStringSize(DT_Operator,qt->Operator); + break; + default: + panic("Implementation error: Unknown term type %ld", + qt->TermType); + break; + } + + return(size); +} + +/*----------------------------------------------------------------------*/ + +/* A query is simply a null terminated list of query terms. For + transmission, a query is written into an any which is sent as + the user information field. */ + +any* +writeQuery(terms) +query_term** terms; +{ + any* info = NULL; + char* writePos = NULL; + char* data = NULL; + unsigned long size = 0; + long remaining = 0; + long i; + query_term* qt = NULL; + + if (terms == NULL) + return(NULL); + + /* calculate the size of write buffer */ + for (i = 0,qt = terms[i]; qt != NULL; qt = terms[++i]) + size += getQueryTermSize(qt); + + data = (char*)s_malloc((size_t)size); + + /* write the terms */ + writePos = data; + remaining = size; + for (i = 0,qt = terms[i]; qt != NULL; qt = terms[++i]) + writePos = writeQueryTerm(qt,writePos,&remaining); + + info = makeAny(size,data); + + return(info); +} + +/*----------------------------------------------------------------------*/ + +query_term** +readQuery(info) +any *info; +{ + char* readPos = info->bytes; + query_term** terms = NULL; + query_term* qt = NULL; + long numTerms = 0L; +char tmp[100]; + +sprintf(tmp,"readquery: bytes: %ld",info->size); +log_write(tmp); + + while (readPos < info->bytes + info->size) + { readPos = readQueryTerm(&qt,readPos); + + if (terms == NULL) + { terms = (query_term**)s_malloc((size_t)(sizeof(query_term*)*2)); + } + else + { terms = + (query_term**)s_realloc((char*)terms, + (size_t)(sizeof(query_term*)*(numTerms+2))); + } +if(qt==NULL) + log_write("qt = null"); + terms[numTerms++] = qt; + terms[numTerms] = NULL; + } + + return(terms); +} + +/*----------------------------------------------------------------------*/ + + +/* +** Routines originally from panic.c -- FM +** +**----------------------------------------------------------------------*/ +/* WIDE AREA INFORMATION SERVER SOFTWARE: + No guarantees or restrictions. See the readme file for the full standard + disclaimer. + + Morris@think.com +*/ + +/* panic is an error system interface. On the Mac, it will pop + * up a little window to explain the problem. + * On a unix box, it will print out the error and call perror() + */ + +/*----------------------------------------------------------------------*/ + +static void exitAction _AP((long error)); + +static void +exitAction(error) +long error; +{ + long i; + for (i = 0; i < 100000; i++) + ; + exit(0); +} + +/*----------------------------------------------------------------------*/ + +#define PANIC_HEADER "Fatal Error: " + +void +panic(char *format, ...) +{ + va_list ap; /* the variable arguments */ + + fprintf(stderr,PANIC_HEADER); + va_start(ap, format); /* init ap */ + vfprintf(stderr,format,ap); /* print the contents */ + va_end(ap); /* free ap */ + fflush(stderr); + + exitAction(0); +} + +/*----------------------------------------------------------------------*/ + + +/* +** Routines originally from cutil.c -- FM +** +**----------------------------------------------------------------------*/ +/* Wide AREA INFORMATION SERVER SOFTWARE + No guarantees or restrictions. See the readme file for the full standard + disclaimer. + + 3.26.90 Harry Morris, morris@think.com + 4.11.90 HWM - generalized conditional includes (see c-dialect.h) +*/ + +#include <varargs.h> + + +/*----------------------------------------------------------------------*/ + +void +fs_checkPtr(ptr) +void* ptr; +/* If the ptr is NULL, give an error */ +{ + if (ptr == NULL) + panic("checkPtr found a NULL pointer"); +} + +/*----------------------------------------------------------------------*/ + +void* +fs_malloc(size) +size_t size; +/* does safety checks and optional accounting */ +{ + register void* ptr = NULL; + + ptr = (void*)calloc((size_t)size,(size_t)1); + s_checkPtr(ptr); + + return(ptr); +} + +/*----------------------------------------------------------------------*/ + +void* +fs_realloc(ptr,size) +void* ptr; +size_t size; +/* does safety checks and optional accounting + note - we don't know how big ptr's memory is, so we can't ensure + that any new memory allocated is NULLed! + */ +{ + register void* nptr = NULL; + + if (ptr == NULL) /* this is really a malloc */ + return(s_malloc(size)); + + nptr = (void*)realloc(ptr,size); + s_checkPtr(ptr); + + return(nptr); +} + +/*----------------------------------------------------------------------*/ + +void +fs_free(ptr) +void* ptr; +/* does safety checks and optional accounting */ +{ + if (ptr != NULL) /* some non-ansi compilers/os's cant handle freeing null */ + { /* if we knew the size of this block of memory, we could clear it - oh well */ + free(ptr); + ptr = NULL; + } +} + +/*----------------------------------------------------------------------*/ + +char* +s_strdup(s) +char* s; + +/* return a copy of s. This is identical to the standard library routine + strdup(), except that it is safe. If s == NULL or malloc fails, + appropriate action is taken. + */ +{ + unsigned long len; + char* copy = NULL; + + if (s == NULL) /* saftey check to postpone stupid errors */ + return(NULL); + + len = strlen(s); /* length of string - terminator */ + copy = (char*)s_malloc((size_t)(sizeof(char)*(len + 1))); + strncpy(copy,s,len + 1); + return(copy); +} + +/*----------------------------------------------------------------------*/ + +char* +fs_strncat(dst,src,maxToAdd,maxTotal) +char* dst; + char* src; + size_t maxToAdd; + size_t maxTotal; + +/* like strncat, except the fourth argument limits the maximum total + length of the resulting string + */ +{ + size_t dstSize = strlen(dst); + size_t srcSize = strlen(src); + + if (dstSize + srcSize < maxTotal) /* use regular old strncat */ + return(strncat(dst,src,maxToAdd)); + else + { size_t truncateTo = maxTotal - dstSize - 1; + char saveChar = src[truncateTo]; + char* result = NULL; + src[truncateTo] = '\0'; + result = strncat(dst,src,maxToAdd); + src[truncateTo] = saveChar; + return(result); + } +} + +/*----------------------------------------------------------------------*/ + +char char_downcase(long_ch) +unsigned long long_ch; +{ + unsigned char ch = long_ch & 0xFF; /* just want one byte */ + /* when ansi is the way of the world, this can be tolower */ + return (((ch >= 'A') && (ch <= 'Z')) ? (ch + 'a' -'A') : ch); +} + +char *string_downcase(word) +char *word; +{ + long i = 0; + while(word[i] != '\0'){ + word[i] = char_downcase((unsigned long)word[i]); + i++; + } + return(word); +} + +/*----------------------------------------------------------------------*/ + diff --git a/WWW/Library/Implementation/HTVMS_WaisUI.h b/WWW/Library/Implementation/HTVMS_WaisUI.h new file mode 100644 index 00000000..6f492b87 --- /dev/null +++ b/WWW/Library/Implementation/HTVMS_WaisUI.h @@ -0,0 +1,674 @@ +/* HTVMS_WAISUI.h +** +** Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu) +** +** 31-May-1994 FM Initial version. +*/ + +#ifndef HTVMSWAIS_H +#define HTVMSWAIS_H + +#ifndef __STDLIB_LOADED +#include <stdlib.h> +#endif /* __STDLIB_LOADED */ + +#define _AP(args) () + +void log_write _AP((char *)); + +/* +** Routines originally from Panic.h -- FM +** +**----------------------------------------------------------------------*/ + +void panic (char* format,...); + +/*----------------------------------------------------------------------*/ + + +/* +** Routines originally from CUtil.h -- FM +** +**----------------------------------------------------------------------*/ + +/* types and constants */ + +#ifndef boolean +#define boolean unsigned long +#endif /* boolean */ + +#ifndef true +#define true (boolean)1L +#endif /* true */ + +#ifndef false +#define false (boolean)0L /* used to be (!true), but broke + some compilers */ +#endif /* false */ + +#ifndef TRUE +#define TRUE true +#endif /* TRUE */ + +#ifndef FALSE +#define FALSE false +#endif /* FALSE */ + +/*----------------------------------------------------------------------*/ +/* functions */ + +/* enhanced memory handling functions - don't call them directly, use the + macros below */ +void fs_checkPtr _AP((void* ptr)); +void* fs_malloc _AP((size_t size)); +void* fs_realloc _AP((void* ptr,size_t size)); +void fs_free _AP((void* ptr)); +char* fs_strncat _AP((char* dst,char* src,size_t maxToAdd,size_t maxTotal)); + +/* macros for memory functions. call these in your program. */ +#define s_checkPtr(ptr) fs_checkPtr(ptr) +#define s_malloc(size) fs_malloc(size) +#define s_realloc(ptr,size) fs_realloc((ptr),(size)) +#define s_free(ptr) { fs_free((char*)ptr); ptr = NULL; } +#define s_strncat(dst,src,maxToAdd,maxTotal) fs_strncat((dst),(src),(maxToAdd),(maxTotal)) + +char* s_strdup _AP((char* s)); + +#define IS_DELIMITER 1 +#define NOT_DELIMITER !IS_DELIMITER + +char char_downcase _AP((unsigned long ch)); +char *string_downcase _AP((char* word)); + +/*----------------------------------------------------------------------*/ + + + +/* +** Routines originally from ZUtil.c -- FM +** +**----------------------------------------------------------------------*/ + +/* Data types / constants */ + +/* bytes to leave for the header size info */ +#define HEADER_LEN (size_t)2 + +typedef long pdu_type; + +#define initAPDU (pdu_type)20 +#define initResponseAPDU (pdu_type)21 +#define searchAPDU (pdu_type)22 +#define searchResponseAPDU (pdu_type)23 +#define presentAPDU (pdu_type)24 +#define presentResponseAPDU (pdu_type)25 +#define deteteAPDU (pdu_type)26 +#define deleteResponseAPDU (pdu_type)27 +#define accessControlAPDU (pdu_type)28 +#define accessControlResponseAPDU (pdu_type)29 +#define resourceControlAPDU (pdu_type)30 +#define resourceControlResponseAPDU (pdu_type)31 + +typedef struct any { /* an any is a non-ascii string of characters */ + unsigned long size; + char* bytes; + } any; + +typedef any bit_map; /* a bit_map is a group of packed bits */ + +typedef unsigned long data_tag; + +#define DT_PDUType (data_tag)1 +#define DT_ReferenceID (data_tag)2 +#define DT_ProtocolVersion (data_tag)3 +#define DT_Options (data_tag)4 +#define DT_PreferredMessageSize (data_tag)5 +#define DT_MaximumRecordSize (data_tag)6 +#define DT_IDAuthentication (data_tag)7 +#define DT_ImplementationID (data_tag)8 +#define DT_ImplementationName (data_tag)9 +#define DT_ImplementationVersion (data_tag)10 +#define DT_UserInformationField (data_tag)11 +#define DT_Result (data_tag)12 +#define DT_SmallSetUpperBound (data_tag)13 +#define DT_LargeSetLowerBound (data_tag)14 +#define DT_MediumSetPresentNumber (data_tag)15 +#define DT_ReplaceIndicator (data_tag)16 +#define DT_ResultSetName (data_tag)17 +#define DT_DatabaseNames (data_tag)18 +#define DT_ElementSetNames (data_tag)19 +#define DT_QueryType (data_tag)20 +#define DT_Query (data_tag)21 +#define DT_SearchStatus (data_tag)22 +#define DT_ResultCount (data_tag)23 +#define DT_NumberOfRecordsReturned (data_tag)24 +#define DT_NextResultSetPosition (data_tag)25 +#define DT_ResultSetStatus (data_tag)26 +#define DT_PresentStatus (data_tag)27 +#define DT_DatabaseDiagnosticRecords (data_tag)28 +#define DT_NumberOfRecordsRequested (data_tag)29 +#define DT_ResultSetStartPosition (data_tag)30 +#define DT_ResultSetID (data_tag)31 +#define DT_DeleteOperation (data_tag)32 +#define DT_DeleteStatus (data_tag)33 +#define DT_NumberNotDeleted (data_tag)34 +#define DT_BulkStatuses (data_tag)35 +#define DT_DeleteMSG (data_tag)36 +#define DT_SecurityChallenge (data_tag)37 +#define DT_SecurityChallengeResponse (data_tag)38 +#define DT_SuspendedFlag (data_tag)39 +#define DT_ResourceReport (data_tag)40 +#define DT_PartialResultsAvailable (data_tag)41 +#define DT_ContinueFlag (data_tag)42 +#define DT_ResultSetWanted (data_tag)43 + +#define UNUSED -1 + +/* number of bytes required to represent the following sizes in compressed + integer format + */ +#define CompressedInt1Byte 128 /* 2 ^ 7 */ +#define CompressedInt2Byte 16384 /* 2 ^ 14 */ +#define CompressedInt3Byte 2097152 /* 2 ^ 21 */ +/* others may follow ... */ + +/* types of query */ +#define QT_0 "0" /* query whose non-standard format has been agreed upon + client and server */ +/* values for InitAPDU option element */ +#define WILL_USE TRUE +#define WILL_NOT_USE FALSE +#define WILL_SUPPORT TRUE +#define WILL_NOT_SUPPORT FALSE + +/* values for InitResponseAPDU result element */ +#define ACCEPT TRUE +#define REJECT FALSE + +/* values for SearchAPDU replace indicator element */ +#define ON TRUE +#define OFF FALSE + +/* values for SearchResponseAPDU search status element */ +#define SUCCESS 0 /* intuitive huh? */ +#define FAILURE 1 + +/* values for SearchResponseAPDU result set status element */ +#define SUBSET 1 +#define INTERIM 2 +#define NONE 3 + +/* values for SearchResponseAPDU present status element */ +/* SUCCESS already defined */ +#define PARTIAL_1 1 +#define PARTIAL_2 2 +#define PARTIAL_3 3 +#define PARTIAL_4 4 +#define PS_NONE 5 /* can't use NONE since it was used by result + set status */ + +#define DIAGNOSTIC_CODE_SIZE (size_t)3 + +typedef struct diagnosticRecord + { boolean SURROGATE; + char DIAG[DIAGNOSTIC_CODE_SIZE]; + char* ADDINFO; + } diagnosticRecord; + +#define D_PermanentSystemError "S1" +#define D_TemporarySystemError "S2" +#define D_UnsupportedSearch "S3" +#define D_TermsOnlyStopWords "S5" +#define D_TooManyArgumentWords "S6" +#define D_TooManyBooleanOperators "S7" +#define D_TooManyTruncatedWords "S8" +#define D_TooMany IncompleteSubfields "S9" +#define D_TruncatedWordsTooShort "SA" +#define D_InvalidFormatForRecordNumber "SB" +#define D_TooManyCharactersInSearch "SC" +#define D_TooManyRecordsRetrieved "SD" +#define D_PresentRequestOutOfRange "SF" +#define D_SystemErrorInPresentRecords "SG" +#define D_RecordNotAuthorizedToBeSent "SH" +#define D_RecordExceedsPrefMessageSize "SI" +#define D_RecordExceedsMaxRecordSize "SJ" +#define D_ResultSetNotSuppAsSearchTerm "SK" +#define D_OnlyOneRsltSetAsSrchTermSupp "SL" +#define D_OnlyANDingOfASnglRsltSetSupp "SM" +#define D_RsltSetExistsNoReplace "SN" +#define D_ResultSetNamingNotSupported "SO" +#define D_CombinationDatabasesNotSupp "SP" +#define D_ElementSetNamesNotSupported "SQ" +#define D_ElementSetNameNotValid "SR" +#define D_OnlyASingleElmntSetNameSupp "SS" +#define D_ResultSetDeletedByTarget "ST" +#define D_ResultSetIsInUse "SU" +#define D_DatabasesIsLocked "SV" +#define D_TerminatedByNoContinueResp "SW" +#define D_ResultSetDoesNotExist "SX" +#define D_ResExNoResultsAvailable "SY" +#define D_ResExUnpredictableResults "SZ" +#define D_ResExValidSubsetOfResults "T1" +#define D_AccessControlFailure "T2" +#define D_SecurityNotIssuedReqTerm "T3" +#define D_SecurityNotBeIssuedRecNotInc "T4" + +/*----------------------------------------------------------------------*/ + +/* for internal error handling */ + +extern char* readErrorPosition; /* pos where buf stoped making sense */ + +/* the following are macros so that they can return OUT of the function + which calls them + */ + +#define RETURN_ON_NULL(var) \ + if (var == NULL) \ + return(NULL); /* jump out of caller */ + +#define REPORT_READ_ERROR(pos) \ + { readErrorPosition = (pos); \ + return(NULL); /* jump out of caller */ \ + } + +#define CHECK_FOR_SPACE_LEFT(spaceNeeded,spaceLeft) \ + { if (*spaceLeft >= spaceNeeded) \ + (*spaceLeft) -= spaceNeeded; \ + else \ + { *spaceLeft = 0; \ + return(NULL); /* jump out of the caller */ \ + } \ + } + +/*----------------------------------------------------------------------*/ + +diagnosticRecord* makeDiag _AP((boolean surrogate,char* code,char* addInfo)); +void freeDiag _AP((diagnosticRecord* diag)); +char* writeDiag _AP((diagnosticRecord* diag,char* buffer,long* len)); +char* readDiag _AP((diagnosticRecord** diag,char* buffer)); + +char* writeCompressedInteger _AP((unsigned long num,char* buf,long* len)); +char* readCompressedInteger _AP((unsigned long *num,char* buf)); +char* writeCompressedIntWithPadding _AP((unsigned long num,unsigned long size, + char* buffer,long* len)); +unsigned long writtenCompressedIntSize _AP((unsigned long num)); + +char* writeTag _AP((data_tag tag,char* buf,long* len)); +char* readTag _AP((data_tag* tag,char* buf)); +data_tag peekTag _AP((char* buf)); +unsigned long writtenTagSize _AP((data_tag tag)); + +any* makeAny _AP((unsigned long size,char* data)); +void freeAny _AP((any* a)); +any* duplicateAny _AP((any* a)); +char* writeAny _AP((any* a,data_tag tag,char* buffer,long* len)); +char* readAny _AP((any** anAny,char* buffer)); +unsigned long writtenAnySize _AP((data_tag tag,any* a)); + +any* stringToAny _AP((char* s)); +char* anyToString _AP((any* a)); +unsigned long writtenStringSize _AP((data_tag tag,char* s)); + +any* longToAny _AP((long Num)); +long anyToLong _AP((any* a)); + +char* writeString _AP((char* s,data_tag tag,char* buffer,long* len)); +char* readString _AP((char** s,char* buffer)); + +bit_map* makeBitMap (unsigned long numBits,...); + +void freeBitMap _AP((bit_map* bm)); +boolean bitAtPos _AP((long pos,bit_map* bm)); +char* writeBitMap _AP((bit_map* bm,data_tag tag,char* buffer,long* len)); +char* readBitMap _AP((bit_map** bm,char* buffer)); + +char* writeByte _AP((unsigned long byte,char* buf,long* len)); +char* readByte _AP((unsigned char* byte,char* buf)); + +char* writeBoolean _AP((boolean flag,char* buf,long* len)); +char* readBoolean _AP((boolean* flag,char* buf)); + +char* writePDUType _AP((pdu_type pduType,char* buf,long* len)); +char* readPDUType _AP((pdu_type* pduType,char* buf)); +pdu_type peekPDUType _AP((char* buf)); + +char* writeBinaryInteger _AP((long num,unsigned long size, + char* buf,long* len)); +char* readBinaryInteger _AP((long* num,unsigned long size,char* buf)); +unsigned long writtenCompressedBinIntSize _AP((long num)); + +char* writeNum _AP((long num,data_tag tag,char* buffer,long* len)); +char* readNum _AP((long* num,char* buffer)); +unsigned long writtenNumSize _AP((data_tag tag,long num)); + +void doList _AP((void** list,void (*func)())); + +char* writeProtocolVersion _AP((char* buf,long* len)); +char* defaultImplementationID _AP((void)); +char* defaultImplementationName _AP((void)); +char* defaultImplementationVersion _AP((void)); + +/*----------------------------------------------------------------------*/ + + +/* +** Routines originally from ZType1.c -- FM +** +**----------------------------------------------------------------------*/ + +/* This file implements the type 1 query defined in appendices B & C + of the SR 1 spec. + */ + +/*----------------------------------------------------------------------*/ +/* types and constants */ + +/* new data tags */ +#define DT_AttributeList (data_tag)44 +#define DT_Term (data_tag)45 +#define DT_Operator (data_tag)46 + +#define QT_BooleanQuery "1" /* standard boolean query */ + +/* general attribute code - use in place of any attribute */ +#define IGNORE "ig" + +/* use value codes */ +#define UV_ISBN "ub" +#define CORPORATE_NAME "uc" +#define ISSN "us" +#define PERSONAL_NAME "up" +#define SUBJECT "uj" +#define TITLE "ut" +#define GEOGRAPHIC_NAME "ug" +#define CODEN "ud" +#define SUBJECT_SUBDIVISION "ue" +#define SERIES_TITLE "uf" +#define MICROFORM_GENERATION "uh" +#define PLACE_OF_PUBLICATION "ui" +#define NUC_CODE "uk" +#define LANGUAGE "ul" +#define COMBINATION_OF_USE_VALUES "um" +#define SYSTEM_CONTROL_NUMBER "un" +#define DATE "uo" +#define LC_CONTROL_NUMBER "ur" +#define MUSIC_PUBLISHERS_NUMBER "uu" +#define GOVERNMENT_DOCUMENTS_NUMBER "uv" +#define SUBJECT_CLASSIFICATION "uw" +#define RECORD_TYPE "uy" + +/* relation value codes */ +#define EQUAL "re" +#define GREATER_THAN "rg" +#define GREATER_THAN_OR_EQUAL "ro" +#define LESS_THAN "rl" +#define LESS_THAN_OR_EQUAL "rp" +#define NOT_EQUAL "rn" + +/* position value codes */ +#define FIRST_IN_FIELD "pf" +#define FIRST_IN_SUBFIELD "ps" +#define FIRST_IN_A_SUBFIELD "pa" +#define FIRST_IN_NOT_A_SUBFIELD "pt" +#define ANY_POSITION_IN_FIELD "py" + +/* structure value codes */ +#define PHRASE "sp" +#define WORD "sw" +#define KEY "sk" +#define WORD_LIST "sl" + +/* truncation value codes */ +#define NO_TRUNCATION "tn" +#define RIGHT_TRUNCATION "tr" +#define PROC_NUM_INCLUDED_IN_SEARCH_ARG "ti" + +/* completeness value codes */ +#define INCOMPLETE_SUBFIELD "ci" +#define COMPLETE_SUBFIELD "cs" +#define COMPLETEFIELD "cf" + +/* operator codes */ +#define AND "a" +#define OR "o" +#define AND_NOT "n" + +/* term types */ +#define TT_Attribute 1 +#define TT_ResultSetID 2 +#define TT_Operator 3 + +#define ATTRIBUTE_SIZE 3 +#define OPERATOR_SIZE 2 + +typedef struct query_term { + /* type */ + long TermType; + /* for term */ + char Use[ATTRIBUTE_SIZE]; + char Relation[ATTRIBUTE_SIZE]; + char Position[ATTRIBUTE_SIZE]; + char Structure[ATTRIBUTE_SIZE]; + char Truncation[ATTRIBUTE_SIZE]; + char Completeness[ATTRIBUTE_SIZE]; + any* Term; + /* for result set */ + any* ResultSetID; + /* for operator */ + char Operator[OPERATOR_SIZE]; +} query_term; + +/*----------------------------------------------------------------------*/ +/* functions */ + +query_term* makeAttributeTerm _AP(( + char* use,char* relation,char* position,char* structure, + char* truncation,char* completeness,any* term)); +query_term* makeResultSetTerm _AP((any* resultSet)); +query_term* makeOperatorTerm _AP((char* operatorCode)); +void freeTerm _AP((query_term* qt)); +char* writeQueryTerm _AP((query_term* qt,char* buffer,long* len)); +char* readQueryTerm _AP((query_term** qt,char* buffer)); +any* writeQuery _AP((query_term** terms)); +query_term** readQuery _AP((any* info)); + +/*----------------------------------------------------------------------*/ + + +/* +** Routines originally from UI.c -- FM +** +**----------------------------------------------------------------------*/ + +char * +generate_search_apdu _AP((char* buff, /* buffer to hold the apdu */ + long *buff_len, /* number of bytes written to the buffer */ + char *seed_words, /* string of the seed words */ + char *database_name, + DocObj** docobjs, + long maxDocsRetrieved + )); + +char * +generate_retrieval_apdu _AP((char *buff, + long *buff_len, + any *docID, + long chunk_type, + long start_line, long end_line, + char *type, + char *database_name)); + + +long +interpret_message _AP((char *request_message, + long request_length, + char *response_message, + long response_buffer_length, /* length of the buffer (modified)*/ + FILE *connection, + boolean verbose)); + + +void +display_text_record_completely _AP((WAISDocumentText *record, + boolean quote_string_quotes)); + +char *trim_junk _AP((char *headline)); + + + +/* +** Routines originally from ZProt.c -- FM +** +**----------------------------------------------------------------------*/ + +/* APDU types */ + +typedef struct InitAPDU { + pdu_type PDUType; + boolean willSearch,willPresent,willDelete; + boolean supportAccessControl,supportResourceControl; + long PreferredMessageSize; + long MaximumRecordSize; + char* IDAuthentication; + char* ImplementationID; + char* ImplementationName; + char* ImplementationVersion; + any* ReferenceID; + void* UserInformationField; + } InitAPDU; + +typedef struct InitResponseAPDU { + pdu_type PDUType; + boolean Result; + boolean willSearch,willPresent,willDelete; + boolean supportAccessControl,supportResourceControl; + long PreferredMessageSize; + long MaximumRecordSize; + char* IDAuthentication; + char* ImplementationID; + char* ImplementationName; + char* ImplementationVersion; + any* ReferenceID; + void* UserInformationField; + } InitResponseAPDU; + +typedef struct SearchAPDU { + pdu_type PDUType; + long SmallSetUpperBound; + long LargeSetLowerBound; + long MediumSetPresentNumber; + boolean ReplaceIndicator; + char* ResultSetName; + char** DatabaseNames; + char* QueryType; + char** ElementSetNames; + any* ReferenceID; + void* Query; + } SearchAPDU; + +typedef struct SearchResponseAPDU { + pdu_type PDUType; + long SearchStatus; + long ResultCount; + long NumberOfRecordsReturned; + long NextResultSetPosition; + long ResultSetStatus; + long PresentStatus; + any* ReferenceID; + void* DatabaseDiagnosticRecords; + } SearchResponseAPDU; + +typedef struct PresentAPDU { + pdu_type PDUType; + long NumberOfRecordsRequested; + long ResultSetStartPosition; + char* ResultSetID; + char* ElementSetNames; + any* ReferenceID; + void* PresentInfo; + } PresentAPDU; + +typedef struct PresentResponseAPDU { + pdu_type PDUType; + boolean PresentStatus; + long NumberOfRecordsReturned; + long NextResultSetPosition; + any* ReferenceID; + void* DatabaseDiagnosticRecords; + } PresentResponseAPDU; + +/*----------------------------------------------------------------------*/ +/* Functions */ + +InitAPDU* makeInitAPDU _AP((boolean search,boolean present,boolean deleteIt, + boolean accessControl,boolean resourceControl, + long prefMsgSize,long maxMsgSize, + char* auth,char* id,char* name, char* version, + any* refID,void* userInfo)); +void freeInitAPDU _AP((InitAPDU* init)); +char* writeInitAPDU _AP((InitAPDU* init,char* buffer,long* len)); +char* readInitAPDU _AP((InitAPDU** init,char* buffer)); + +InitResponseAPDU* makeInitResponseAPDU _AP((boolean result, + boolean search,boolean present,boolean deleteIt, + boolean accessControl,boolean resourceControl, + long prefMsgSize,long maxMsgSize, + char* auth,char* id,char* name, char* version, + any* refID,void* userInfo)); +void freeInitResponseAPDU _AP((InitResponseAPDU* init)); +char* writeInitResponseAPDU _AP((InitResponseAPDU* init,char* buffer,long* len)); +char* readInitResponseAPDU _AP((InitResponseAPDU** init,char* buffer)); +InitResponseAPDU* replyToInitAPDU _AP((InitAPDU* init,boolean result,void* userInfo)); + +SearchAPDU* makeSearchAPDU _AP((long small,long large, long medium, + boolean replace,char* name,char** databases, + char* type,char** elements,any* refID,void* queryInfo)); +void freeSearchAPDU _AP((SearchAPDU* query)); +char* writeSearchAPDU _AP((SearchAPDU* query,char* buffer,long* len)); +char* readSearchAPDU _AP((SearchAPDU** query,char* buffer)); + +SearchResponseAPDU* makeSearchResponseAPDU _AP((long result,long count, + long recordsReturned,long nextPos, + long resultStatus,long presentStatus, + any* refID,void* records)); +void freeSearchResponseAPDU _AP((SearchResponseAPDU* queryResponse)); +char* writeSearchResponseAPDU _AP((SearchResponseAPDU* queryResponse,char* buffer,long* len)); +char* readSearchResponseAPDU _AP((SearchResponseAPDU** queryResponse,char* buffer)); + +PresentAPDU* makePresentAPDU _AP((long recsReq, long startPos, + char* resultID,any* refID,void* info)); +void freePresentAPDU _AP((PresentAPDU* present)); +char* writePresentAPDU _AP((PresentAPDU* present,char* buffer,long* len)); +char* readPresentAPDU _AP((PresentAPDU** present,char* buffer)); + +PresentResponseAPDU* makePresentResponseAPDU _AP((boolean status,long recsRet, + long nextPos,any* refID, + void* records)); +void freePresentResponseAPDU _AP((PresentResponseAPDU* present)); +char* writePresentResponseAPDU _AP((PresentResponseAPDU* present,char* buffer,long* len)); +char* readPresentResponseAPDU _AP((PresentResponseAPDU** present,char* buffer)); + +/*----------------------------------------------------------------------*/ +/* user extension hooks: */ + +extern char* writeInitInfo _AP((InitAPDU* init,char* buffer,long* len)); +extern char* readInitInfo _AP((void** info,char* buffer)); + +extern char* writeInitResponseInfo _AP((InitResponseAPDU* init,char* buffer,long* len)); +extern char* readInitResponseInfo _AP((void** info,char* buffer)); + +extern char* writeSearchInfo _AP((SearchAPDU* query,char* buffer,long* len)); +extern char* readSearchInfo _AP((void** info,char* buffer)); + +extern char* writeSearchResponseInfo _AP((SearchResponseAPDU* query,char* buffer,long* len)); +extern char* readSearchResponseInfo _AP((void** info,char* buffer)); + +extern char* writePresentInfo _AP((PresentAPDU* present,char* buffer,long* len)); +extern char* readPresentInfo _AP((void** info,char* buffer)); + +extern char* writePresentResponseInfo _AP((PresentResponseAPDU* present,char* buffer,long* len)); +extern char* readPresentResponseInfo _AP((void** info,char* buffer)); + + +#endif /* HTVMSWAIS_H */ diff --git a/WWW/Library/Implementation/HTWAIS.c b/WWW/Library/Implementation/HTWAIS.c new file mode 100644 index 00000000..e0659f17 --- /dev/null +++ b/WWW/Library/Implementation/HTWAIS.c @@ -0,0 +1,1103 @@ +/* WorldWideWeb - Wide Area Informaion Server Access HTWAIS.c +** ================================================== +** +** This module allows a WWW server or client to read data from a +** remote WAIS +** server, and provide that data to a WWW client in hypertext form. +** Source files, once retrieved, are stored and used to provide +** information about the index when that is acessed. +** +** Authors +** BK Brewster Kahle, Thinking Machines, <Brewster@think.com> +** TBL Tim Berners-Lee, CERN <timbl@info.cern.ch> +** FM Foteos Macrides, WFEB <macrides@sci.wfeb.edu> +** +** History +** Sep 91 TBL adapted shell-ui.c (BK) with HTRetrieve.c from WWW. +** Feb 91 TBL Generated HTML cleaned up a bit (quotes, escaping) +** Refers to lists of sources. +** Mar 93 TBL Lib 2.0 compatible module made. +** May 94 FM Added DIRECT_WAIS support for VMS. +** +** Bugs +** Uses C stream i/o to read and write sockets, which won't work +** on VMS TCP systems. +** +** Should cache connections. +** +** ANSI C only as written +** +** Bugs fixed +** NT Nathan Torkington (Nathan.Torkington@vuw.ac.nz) +** +** WAIS comments: +** +** 1. Separate directories for different system's .o would help +** 2. Document ids are rather long! +** +** WWW Address mapping convention: +** +** /servername/database/type/length/document-id +** +** /servername/database?word+word+word +*/ +/* WIDE AREA INFORMATION SERVER SOFTWARE: + No guarantees or restrictions. See the readme file for the full standard + disclaimer. + + Brewster@think.com +*/ + +#include "HTUtils.h" +#include "tcp.h" +#include "HTParse.h" +#include "HTAccess.h" /* We implement a protocol */ +#include "HTML.h" /* The object we will generate */ +#include "HTFormat.h" +#include "HTTCP.h" +#include "HTCJK.h" +#include "HTAlert.h" +/* #include "HTWSRC.h" */ /* Need some bits from here */ +/* #include "ParseWSRC.h" */ + + +/* From WAIS +** --------- +*/ + +#ifdef VMS +#include "HTVMS_WaisUI.h" +#include "HTVMS_WaisProt.h" +#else +#include <ui.h> +#endif /* VMS */ + +#define MAX_MESSAGE_LEN 100000 +#define CHARS_PER_PAGE 10000 /* number of chars retrieved in each request */ + +#define WAISSEARCH_DATE "Fri Jul 19 1991" + + +/* FROM WWW +** -------- +*/ +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +extern int HTCheckForInterrupt NOPARAMS; + +#define DIRECTORY "/cnidr.org:210/directory-of-servers" +/* #define DIRECTORY "/quake.think.com:210/directory-of-servers" */ + +#define BIG 1024 /* identifier size limit @@@@@ */ + +#define BUFFER_SIZE 4096 /* Arbitrary size for efficiency */ + +#define HEX_ESCAPE '%' + +extern HTCJKlang HTCJK; + +extern int WWW_TraceFlag; /* Control diagnostic output */ +extern FILE * logfile; /* Log file output */ + +PRIVATE BOOL as_gate; /* Client is using us as gateway */ + +PRIVATE char line[2048]; /* For building strings to display */ + /* Must be able to take id */ + +#define PUTC(c) (*target->isa->put_character)(target, c) +#define PUTS(s) (*target->isa->put_string)(target, s) +#define START(e) (*target->isa->start_element)(target, e, 0, 0, 0) +#define END(e) (*target->isa->end_element)(target, e, 0) +#define FREE_TARGET (*target->isa->_free)(target) + +struct _HTStructured { + CONST HTStructuredClass * isa; + /* ... */ +}; + +struct _HTStream { + CONST HTStreamClass * isa; + /* ... */ +}; + + +/* ------------------------------------------------------------------------ */ +/* ---------------- Local copy of connect_to_server calls ----------------- */ +/* ------------------------------------------------------------------------ */ + +/* Returns 1 on success, 0 on fail, -1 on interrupt. */ +static int fd_mosaic_connect_to_server ARGS3(char *, host_name, long, port, +long *, fd) +{ + /* New version. */ + char dummy[256]; + int status; + + sprintf (dummy, "wais://%s:%d/", host_name, port); + + status = HTDoConnect (dummy, "WAIS", 210, (int *)fd); + if (status == HT_INTERRUPTED) + { + if (TRACE) + fprintf (stderr, "===WAIS=== interrupted in connect\n"); + HTAlert ("Connection interrupted."); + return -1; + } + if (status < 0) + return 0; + return 1; +} + +/* Returns 1 on success, 0 on fail, -1 on interrupt. */ +#ifdef VMS +static int mosaic_connect_to_server ARGS3(char *, host_name, long, port, + long *, fdp) +#else +static int mosaic_connect_to_server ARGS3(char *, host_name, long, port, + FILE **, fp) +#endif /* VMS */ +{ +#ifndef VMS + FILE* file; +#endif /* VMS */ + long fd; + int rv; + + rv = fd_mosaic_connect_to_server (host_name, port, &fd); + if(rv == 0) + { + HTAlert ("Could not connect to WAIS server."); + return 0; + } + else if (rv == -1) + { + HTAlert ("Connection interrupted."); + return -1; + } + +#ifndef VMS + if ((file = fdopen(fd,"r+")) == NULL) + { + HTAlert ("Could not open WAIS connection for reading."); + return 0; + } + + *fp = file; +#else + *fdp = fd; +#endif /* VMS */ + return 1; +} + + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + + +/* showDiags +*/ +/* modified from Jonny G's version in ui/question.c */ + +void showDiags ARGS2( + HTStream *, target, + diagnosticRecord **, d) +{ + long i; + + for (i = 0; d[i] != NULL; i++) { + if (d[i]->ADDINFO != NULL) { + PUTS("Diagnostic code is "); + PUTS(d[i]->DIAG); + PUTC(' '); + PUTS(d[i]->ADDINFO); + PUTC('\n'); ; + } + } +} + +/* Matrix of allowed characters in filenames +** ----------------------------------------- +*/ + +PRIVATE BOOL acceptable[256]; +PRIVATE BOOL acceptable_inited = NO; + +PRIVATE void init_acceptable NOARGS +{ + unsigned int i; + char * good = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$"; + for(i=0; i<256; i++) acceptable[i] = NO; + for(;*good; good++) acceptable[(unsigned int)*good] = YES; + acceptable_inited = YES; +} + +/* Transform file identifier into WWW address +** ------------------------------------------ +** +** +** On exit, +** returns nil if error +** pointer to malloced string (must be freed) if ok +*/ +char * WWW_from_archie ARGS1 (char *, file) +{ + char * end; + char * result; + char * colon; + for(end=file; *end > ' '; end++); /* assumes ASCII encoding*/ + result = (char *)malloc(10 + (end-file)); + if (!result) return result; /* Malloc error */ + strcpy(result, "file://"); + strncat(result, file, end-file); + colon = strchr(result+7, ':'); /* Expect colon after host */ + if (colon) { + for(; colon[0]; colon[0]=colon[1], colon++); /* move down */ + } + return result; +} /* WWW_from_archie */ + +/* Transform document identifier into URL +** -------------------------------------- +** +** Bugs: A static buffer of finite size is used! +** The format of the docid MUST be good! +** +** On exit, +** returns nil if error +** pointer to malloced string (must be freed) if ok +*/ +PRIVATE char hex [17] = "0123456789ABCDEF"; +extern char from_hex PARAMS((char a)); /* In HTWSRC @@ */ + +PRIVATE char * WWW_from_WAIS ARGS1(any *, docid) + +{ + static char buf[BIG]; + char * q = buf; + char * p = (docid->bytes); + int i, l; + if (TRACE) { + char *p; + fprintf(stderr, "WAIS id (%d bytes) is ", (int)docid->size); + for(p=docid->bytes; p<docid->bytes+docid->size; p++) { + if ((*p >= ' ') && (*p<= '~')) /* Assume ASCII! */ + fprintf(stderr, "%c", *p); + else + fprintf(stderr, "<%x>", (unsigned)*p); + } + fprintf(stderr, "\n"); + } + for (p=docid->bytes; (p<docid->bytes+docid->size) && (q<&buf[BIG]);) { + if (TRACE) fprintf(stderr, " Record type %d, length %d\n", + p[0], p[1]); + if (*p>10) { + if(TRACE) + fprintf(stderr, "Eh? DOCID record type of %d!\n", *p); + return 0; + } + { /* Bug fix -- allow any byte value 15 Apr 93 */ + unsigned int i = (unsigned) *p++; + + if (i > 99) { + *q++ = (i/100) + '0'; + i = i % 100; + } + if (i > 9) { + *q++ = (i/10) + '0'; + i = i % 10; + } + *q++ = i + '0'; /* Record type */ + } + *q++ = '='; /* Separate */ + l = *p++; /* Length */ + for(i=0; i<l; i++, p++){ + if (!acceptable[*p]) { + *q++ = HEX_ESCAPE; /* Means hex commming */ + *q++ = hex[(*p) >> 4]; + *q++ = hex[(*p) & 15]; + } + else *q++ = *p; + } + *q++= ';'; /* Terminate field */ + } + *q++ = 0; /* Terminate string */ + if (TRACE) fprintf(stderr, "WWW form of id: %s\n", buf); + { + char * result = (char *)malloc(strlen(buf)+1); + if (!result) + outofmem(__FILE__, "WWW_from_WAIS"); + strcpy(result, buf); + return result; + } +} /* WWW_from_WAIS */ + + +/* Transform URL into WAIS document identifier +** ------------------------------------------- +** +** On entry, +** docname points to valid name produced originally by +** WWW_from_WAIS +** On exit, +** docid->size is valid +** docid->bytes is malloced and must later be freed. +*/ +PRIVATE any * WAIS_from_WWW ARGS2 (any *, docid, char *, docname) +{ + char *z; /* Output pointer */ + char *sor; /* Start of record - points to size field. */ + char *p; /* Input pointer */ + char *q; /* Poisition of "=" */ + char *s; /* Position of semicolon */ + int n; /* size */ + if (TRACE) fprintf(stderr, "WWW id (to become WAIS id): %s\n", docname); + for(n=0, p = docname; *p; p++) { /* Count sizes of strings */ + n++; + if (*p == ';') n--; /* Not converted */ + else if (*p == HEX_ESCAPE) n=n-2; /* Save two bytes */ + docid->size = n; + } + + if (!(docid->bytes = (char *) malloc(docid->size))) /* result record */ + outofmem(__FILE__, "WAIS_from_WWW"); + z = docid->bytes; + + for(p = docname; *p; ) { /* Convert of strings */ + /* Record type */ + + *z = 0; /* Initialize record type */ + while (*p >= '0' && *p <= '9') { + *z = *z*10 + (*p++ - '0'); /* Decode decimal record type */ + } + z++; + if (*p != '=') return 0; + q = p; + +/* *z++ = *p++ - '0'; + q = strchr(p , '='); + if (!q) return 0; +*/ + s = strchr(q, ';'); /* (Check only) */ + if (!s) return 0; /* Bad! No ';'; */ + sor = z; /* Remember where the size field was */ + z++; /* Skip record size for now */ + for(p=q+1; *p!=';' ; ) { + if (*p == HEX_ESCAPE) { + char c; + unsigned int b; + p++; + c = *p++; + b = from_hex(c); + c = *p++; + if (!c) break; /* Odd number of chars! */ + *z++ = (b<<4) + from_hex(c); + } else { + *z++ = *p++; /* Record */ + } + } + *sor = (z-sor-1); /* Fill in size -- not counting size itself */ + p++; /* After semicolon: start of next record */ + } + + if (TRACE) { + char *p; + fprintf(stderr, "WAIS id (%d bytes) is ", (int)docid->size); + for(p=docid->bytes; p<docid->bytes+docid->size; p++) { + if ((*p >= ' ') && (*p<= '~')) /* Assume ASCII! */ + fprintf(stderr, "%c", *p); + else + fprintf(stderr, "<%x>", (unsigned)*p); + } + fprintf(stderr, "\n"); + } + return docid; /* Ok */ + +} /* WAIS_from_WWW */ + + +/* Send a plain text record to the client output_text_record() +** -------------------------------------- +*/ + +PRIVATE void output_text_record ARGS4( + HTStream *, target, + WAISDocumentText *, record, + boolean, quote_string_quotes, + boolean, binary) +{ + long count; + /* printf(" Text\n"); + print_any(" DocumentID: ", record->DocumentID); + printf(" VersionNumber: %d\n", record->VersionNumber); + */ + + if (binary) { + (*target->isa->put_block)(target, + record->DocumentText->bytes, + record->DocumentText->size); + return; + } + + for(count = 0; count < record->DocumentText->size; count++){ + long ch = (unsigned char)record->DocumentText->bytes[count]; + if (ch == 27) { /* What is this in for? Tim */ + + /* then we have an escape code */ + /* if the next letter is '(' or ')', then ignore two letters */ + if('(' == record->DocumentText->bytes[count + 1] || + ')' == record->DocumentText->bytes[count + 1]) + count += 1; /* it is a term marker */ + else count += 4; /* it is a paragraph marker */ + } else if (ch == '\n' || ch == '\r') { + PUTC('\n'); + } else if (HTCJK != NOCJK || ch == '\t' || isprint(ch)){ + PUTC(ch); + } + } +} /* output text record */ + + + +/* Format A Search response for the client display_search_response +** --------------------------------------- +*/ +/* modified from tracy shen's version in wutil.c + * displays either a text record or a set of headlines. + */ +void +display_search_response ARGS4( + HTStructured *, target, + SearchResponseAPDU *, response, + char *, database, + char *, keywords) +{ + WAISSearchResponse *info; + long i, k; + + BOOL archie = strstr(database, "archie")!=0; /* Specical handling */ + + if (TRACE) fprintf(stderr, "HTWAIS: Displaying search response\n"); + PUTS("Index "); + START(HTML_EM); + PUTS(database); + END(HTML_EM); + sprintf(line, " contains the following %d item%s relevant to \"", + (int)(response->NumberOfRecordsReturned), + response->NumberOfRecordsReturned ==1 ? "" : "s"); + PUTS(line); + START(HTML_EM); + PUTS(keywords); + END(HTML_EM); + PUTS("\".\n"); + PUTS("The first figure after each entry is its relative score, "); + PUTS("the second is the number of lines in the item."); + START(HTML_BR); + START(HTML_BR); + PUTS("\n"); + START(HTML_OL); + + if ( response->DatabaseDiagnosticRecords != 0 ) { + info = (WAISSearchResponse *)response->DatabaseDiagnosticRecords; + i =0; + + if (info->Diagnostics != NULL) + showDiags((HTStream*)target, info->Diagnostics); + + if ( info->DocHeaders != 0 ) { + for (k=0; info->DocHeaders[k] != 0; k++ ) { + WAISDocumentHeader* head = info->DocHeaders[k]; + char * headline = trim_junk(head->Headline); + any * docid = head->DocumentID; + char * docname; /* printable version of docid */ + i++; + +/* Make a printable string out of the document id. +*/ + if (TRACE) fprintf(stderr, + "HTWAIS: %2ld: Score: %4ld, lines:%4ld '%s'\n", + i, + (long int)(info->DocHeaders[k]->Score), + (long int)(info->DocHeaders[k]->Lines), + headline); + + START(HTML_LI); + + if (archie) { + char * www_name = WWW_from_archie(headline); + if (www_name) { + HTStartAnchor(target, NULL, www_name); + PUTS(headline); + + END(HTML_A); + FREE(www_name); + } else { + PUTS(headline); + PUTS(" (bad file name)"); + } + } else { /* Not archie */ + docname = WWW_from_WAIS(docid); + if (docname) { + char * dbname = HTEscape(database, URL_XPALPHAS); + sprintf(line, "/%s/%s/%d/%s", /* W3 address */ + dbname, + head->Types ? head->Types[0] : "TEXT", + (int)(head->DocumentLength), + docname); + HTStartAnchor(target, NULL, ( (head->Types) + && (!strcmp(head->Types[0], "URL"))) ? + headline : line); /* NT, Sep 93 */ + PUTS(headline); + END(HTML_A); + FREE(dbname); + FREE(docname); + } else { + PUTS("(bad doc id)"); + } + } + + sprintf(line, "%5ld %5ld ", + head->Score, + head->Lines); + PUTS( line); + } /* next document header */ + } /* if there were any document headers */ + + if ( info->ShortHeaders != 0 ) { + k =0; + while (info->ShortHeaders[k] != 0 ) { + i++; + PUTS( "(Short Header record, can't display)"); + } + } + if ( info->LongHeaders != 0 ) { + k =0; + while (info->LongHeaders[k] != 0) { + i++; + PUTS( "\nLong Header record, can't display\n"); + } + } + if ( info->Text != 0 ) { + k =0; + while (info->Text[k] != 0) { + i++; + PUTS( "\nText record\n"); + output_text_record((HTStream*)target, info->Text[k++], false, false); + } + } + if ( info->Headlines != 0 ) { + k =0; + while (info->Headlines[k] != 0) { + i++; + PUTS( "\nHeadline record, can't display\n"); + /* dsply_headline_record( info->Headlines[k++]); */ + } + } + if ( info->Codes != 0 ) { + k =0; + while (info->Codes[k] != 0) { + i++; + PUTS( "\nCode record, can't display\n"); + /* dsply_code_record( info->Codes[k++]); */ + } + } + } /* Loop: display user info */ + END(HTML_OL); + PUTC('\n'); ; +} + + + + +/* Load by name HTLoadWAIS +** ============ +** +** This renders any object or search as required +*/ +PUBLIC int HTLoadWAIS ARGS4( + CONST char *, arg, + HTParentAnchor *, anAnchor, + HTFormat, format_out, + HTStream*, sink) + +#define MAX_KEYWORDS_LENGTH 1000 +#define MAX_SERVER_LENGTH 1000 +#define MAX_DATABASE_LENGTH 1000 +#define MAX_SERVICE_LENGTH 1000 +#define MAXDOCS 200 + +{ + static CONST char * error_header = +"<h1>Access error</h1>\nThe following error occured in accesing a WAIS server:<P>\n"; + char * key; /* pointer to keywords in URL */ + char* request_message = NULL; /* arbitrary message limit */ + char* response_message = NULL; /* arbitrary message limit */ + long request_buffer_length; /* how of the request is left */ + SearchResponseAPDU *retrieval_response = 0; + char keywords[MAX_KEYWORDS_LENGTH + 1]; + char *server_name; + char *wais_database = NULL; /* name of current database */ + char *www_database; /* Same name escaped */ + char *service; + char *doctype; + char *doclength; + long document_length; + char *docname; +#ifdef VMS + long connection = 0; +#else + FILE *connection = NULL; +#endif /* VMS */ + char * names; /* Copy of arg to be hacked up */ + BOOL ok = NO; + int return_status = HT_LOADED; + int rv; + + extern FILE * connect_to_server(); + + if (!acceptable_inited) init_acceptable(); + + +/* Decipher and check syntax of WWW address: +** ---------------------------------------- +** +** First we remove the "wais:" if it was spcified. 920110 +*/ + names = HTParse(arg, "", PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION); + key = strchr(names, '?'); + + if (key) { + char * p; + *key++ = 0; /* Split off keywords */ + for (p=key; *p; p++) if (*p == '+') *p = ' '; + HTUnEscape(key); + } + if (names[0]== '/') { + server_name = names+1; + if (as_gate =(*server_name == '/')) + server_name++; /* Accept one or two */ + www_database = strchr(server_name,'/'); + if (www_database) { + *www_database++ = 0; /* Separate database name */ + doctype = strchr(www_database, '/'); + if (key) ok = YES; /* Don't need doc details */ + else if (doctype) { /* If not search parse doc details */ + *doctype++ = 0; /* Separate rest of doc address */ + doclength = strchr(doctype, '/'); + if(doclength) { + *doclength++ = 0; + document_length = atol(doclength); + if (document_length) { + docname=strchr(doclength, '/'); + if (docname) { + *docname++ = 0; + ok = YES; /* To avoid a goto! */ + } /* if docname */ + } /* if document_length valid */ + } /* if doclength */ + } else { /* no doctype? Assume index required */ + if (!key) key = ""; + ok = YES; + } /* if doctype */ + } /* if database */ + } + + if (!ok) + return HTLoadError(sink, 500, "Syntax error in WAIS URL"); + + if (TRACE) fprintf(stderr, "HTWAIS: Parsed OK\n"); + + service = strchr(names, ':'); + if (service) *service++ = 0; + else service = "210"; + + if (server_name[0] == 0) +#ifdef VMS + connection = 0; +#else + connection = NULL; +#endif /* VMS */ + + else if (!(key && !*key)) + { + int status; + if (TRACE) + fprintf (stderr, "===WAIS=== calling mosaic_connect_to_server\n"); + status = mosaic_connect_to_server + (server_name, atoi(service), &connection); + if (status == 0) + { + if (TRACE) + fprintf (stderr, "===WAIS=== connection failed\n"); + FREE(names); + return HT_NOT_LOADED; + } + else if (status == -1) + { + if (TRACE) + fprintf (stderr, "===WAIS=== connection interrupted\n"); + FREE(names); + return HT_INTERRUPTED; + } + } + + StrAllocCopy(wais_database,www_database); + HTUnEscape(wais_database); + + /* This below fixed size stuff is terrible */ +#ifdef VMS + if (!(request_message = + (char*)calloc((size_t)MAX_MESSAGE_LEN*sizeof(char),1))) + outofmem(__FILE__, "HTLoadWAIS"); + if (!(response_message = + (char*)calloc((size_t)MAX_MESSAGE_LEN*sizeof(char),1))) + outofmem(__FILE__, "HTLoadWAIS"); +#else + request_message = (char*)s_malloc((size_t)MAX_MESSAGE_LEN * sizeof(char)); + response_message = (char*)s_malloc((size_t)MAX_MESSAGE_LEN * sizeof(char)); +#endif /* VMS */ + +/* If keyword search is performed but there are no keywords, +** the user has followed a link to the index itself. It would be +** appropriate at this point to send him the .SRC file - how? +*/ + + if (key && !*key) { /* I N D E X */ + +#ifdef CACHE_FILE_PREFIX + char filename[256]; + FILE * fp; +#endif + HTStructured * target = HTML_new(anAnchor, format_out, sink); + + START(HTML_HEAD); + PUTS("\n"); + HTStartIsIndex(target, "Enter WAIS query: ", NULL); + PUTS("\n"); + + { + START(HTML_TITLE); + PUTS(wais_database); + PUTS(" (WAIS Index)"); + END(HTML_TITLE); + PUTS("\n"); + END(HTML_HEAD); + PUTS("\n"); + + START(HTML_H1); + PUTS("WAIS Index: "); + START(HTML_EM); + PUTS(wais_database); + END(HTML_EM); + END(HTML_H1); + PUTS("\n"); + PUTS("This is a link for searching the "); + START(HTML_EM); + PUTS(wais_database); + END(HTML_EM); + PUTS(" WAIS Index.\n"); + + } + /* If we have seen a source file for this database, use that: + */ + +#ifdef CACHE_FILE_PREFIX + sprintf(filename, "%sWSRC-%s:%s:%.100s.txt", + CACHE_FILE_PREFIX, + server_name, service, www_database); + + fp = fopen(filename, "r"); /* Have we found this already? */ + if (TRACE) fprintf(stderr, + "HTWAIS: Description of server %s %s.\n", + filename, + fp ? "exists already" : "does NOT exist!"); + + if (fp) { + char c; + START(HTML_PRE); /* Preformatted description */ + PUTS("\n"); + while((c=getc(fp))!=EOF) PUTC(c); /* Transfer file */ + END(HTML_PRE); + fclose(fp); +#endif + START(HTML_P); + PUTS("\nEnter the 's'earch command and then specify search words.\n"); + + FREE_TARGET; + + } else if (key) { /* S E A R C H */ + char *p; + HTStructured * target; + + strncpy(keywords, key, MAX_KEYWORDS_LENGTH); + while(p=strchr(keywords, '+')) *p = ' '; + + /* Send advance title to get something fast to the other end */ + + target = HTML_new(anAnchor, format_out, sink); + + START(HTML_HEAD); + PUTS("\n"); + HTStartIsIndex(target, "Enter WAIS query: ", NULL); + PUTS("\n"); + START(HTML_TITLE); + PUTS(keywords); + PUTS(" (in "); + PUTS(wais_database); + PUTS(")"); + END(HTML_TITLE); + PUTS("\n"); + END(HTML_HEAD); + PUTS("\n"); + + START(HTML_H1); + PUTS("WAIS Search of \""); + START(HTML_EM); + PUTS(keywords); + END(HTML_EM); + PUTS("\" in: "); + START(HTML_EM); + PUTS(wais_database); + END(HTML_EM); + END(HTML_H1); + PUTS("\n"); + + request_buffer_length = MAX_MESSAGE_LEN; /* Amount left */ + if (TRACE) fprintf(stderr, "HTWAIS: Search for `%s' in `%s'\n", + keywords, wais_database); + if(NULL == + generate_search_apdu(request_message + HEADER_LENGTH, + &request_buffer_length, + keywords, wais_database, NULL, MAXDOCS)) { +#ifdef VMS + HTAlert ("HTWAIS: Request too large."); + return_status = HT_NOT_LOADED; + FREE_TARGET; + goto CleanUp; +#else + panic("request too large"); +#endif /* VMS */ + } + + HTProgress("Searching WAIS database..."); + rv = interpret_message (request_message, + MAX_MESSAGE_LEN - request_buffer_length, + response_message, + MAX_MESSAGE_LEN, + connection, + false /* true verbose */ + ); + + if (rv == HT_INTERRUPTED) { + HTAlert ("Search interrupted."); + return_status = HT_INTERRUPTED; + FREE_TARGET; + goto CleanUp; + } + + else if(!rv) { +#ifdef VMS + HTAlert ("HTWAIS: Return message too large."); + return_status = HT_NOT_LOADED; + FREE_TARGET; + goto CleanUp; +#else + panic("returned message too large"); +#endif /* VMS */ + + } else { /* returned message ok */ + + SearchResponseAPDU *query_response = 0; + readSearchResponseAPDU(&query_response, + response_message + HEADER_LENGTH); + display_search_response(target, + query_response, wais_database, keywords); + if (query_response->DatabaseDiagnosticRecords) + freeWAISSearchResponse( + query_response->DatabaseDiagnosticRecords); + freeSearchResponseAPDU( query_response); + } /* returned message not too large */ + + FREE_TARGET; + + } else { /* D O C U M E N T F E T C H */ + + HTFormat format_in; + boolean binary; /* how to transfer stuff coming over */ + HTStream * target; + long count; + any doc_chunk; + any * docid = &doc_chunk; + if (TRACE) fprintf(stderr, + "HTWAIS: Retrieve document id `%s' type `%s' length %ld\n", + docname, doctype, document_length); + + format_in = + !strcmp(doctype, "WSRC") ? HTAtom_for("application/x-wais-source") : + !strcmp(doctype, "TEXT") ? HTAtom_for("text/plain") : + !strcmp(doctype, "HTML") ? HTAtom_for("text/html") : + !strcmp(doctype, "GIF") ? HTAtom_for("image/gif") : + HTAtom_for("application/octet-stream"); + binary = + 0 != strcmp(doctype, "WSRC") && + 0 != strcmp(doctype, "TEXT") && + 0 != strcmp(doctype, "HTML") ; + + + target = HTStreamStack(format_in, format_out, sink, anAnchor); + if (!target) return HTLoadError(sink, 500, + "Can't convert format of WAIS document"); +/* Decode hex or litteral format for document ID +*/ + WAIS_from_WWW(docid, docname); + + +/* Loop over slices of the document +*/ + for(count = 0; + count * CHARS_PER_PAGE < document_length; + count++){ +#ifdef VMS + char *type = NULL; + + StrAllocCopy(type, doctype); +#else + char *type = s_strdup(doctype); /* Gets freed I guess */ +#endif /* VMS */ + request_buffer_length = MAX_MESSAGE_LEN; /* Amount left */ + if (TRACE) fprintf(stderr, "HTWAIS: Slice number %ld\n", count); + + if(HTCheckForInterrupt()) + { + HTAlert ("Data transfer interrupted."); + (*target->isa->_abort)(target, NULL); +#ifdef VMS + FREE(type); +#endif /* VMS */ + return_status = HT_NOT_LOADED; + goto CleanUp; + } + + if(0 == + generate_retrieval_apdu(request_message + HEADER_LENGTH, + &request_buffer_length, + docid, + CT_byte, + count * CHARS_PER_PAGE, + ((count + 1) * CHARS_PER_PAGE <= document_length ? + (count + 1) * CHARS_PER_PAGE : + document_length), + type, + wais_database + )) { +#ifdef VMS + HTAlert ("HTWAIS: Request too long."); + return_status = HT_NOT_LOADED; + FREE_TARGET; + FREE(type); + FREE(docid->bytes); + goto CleanUp; +#else + panic("request too long"); +#endif /* VMS */ + } + + /* Actually do the transaction given by request_message */ + HTProgress("Fetching WAIS document..."); + rv = interpret_message (request_message, + MAX_MESSAGE_LEN - request_buffer_length, + response_message, + MAX_MESSAGE_LEN, + connection, + false /* true verbose */ + ); + + if (rv == HT_INTERRUPTED) + { + HTAlert ("Data transfer interrupted."); + return_status = HT_INTERRUPTED; + FREE_TARGET; + FREE(type); + FREE(docid->bytes); + goto CleanUp; + } + + else if (!rv) + { +#ifdef VMS + HTAlert ("HTWAIS: Return message too large."); + return_status = HT_NOT_LOADED; + FREE_TARGET; + FREE(type); + FREE(docid->bytes); + goto CleanUp; +#else + panic("Returned message too large"); +#endif /* VMS */ + } + + /* Parse the result which came back into memory. + */ + readSearchResponseAPDU(&retrieval_response, + response_message + HEADER_LENGTH); + + if(NULL == ((WAISSearchResponse *) + retrieval_response->DatabaseDiagnosticRecords)->Text){ + /* display_search_response(target, retrieval_response, + wais_database, keywords); */ + PUTS("No text was returned!\n"); + /* panic("No text was returned"); */ + } else { + + output_text_record(target, + ((WAISSearchResponse *) + retrieval_response->DatabaseDiagnosticRecords)->Text[0], + false, binary); + + } /* If text existed */ + +#ifdef VMS + FREE(type); +#endif /* VMS */ + } /* Loop over slices */ + + FREE_TARGET; + FREE(docid->bytes); + + freeWAISSearchResponse( retrieval_response->DatabaseDiagnosticRecords); + freeSearchResponseAPDU( retrieval_response); + + } /* If document rather than search */ + + + +CleanUp: +/* (This postponed until later, after a timeout:) +*/ +#ifdef VMS + if (connection) + NETCLOSE((int)connection); +#else + if (connection) + fclose(connection); +#endif /* VMS */ + FREE(wais_database); +#ifdef VMS + FREE(request_message); + FREE(response_message); +#else + s_free(request_message); + s_free(response_message); +#endif /* VMS */ + FREE(names); + return (return_status); +} + +#ifdef GLOBALDEF_IS_MACRO +#define _HTWAIS_C_1_INIT { "wais", HTLoadWAIS, NULL } +GLOBALDEF(HTProtocol, HTWAIS, _HTWAIS_C_1_INIT); +#else +GLOBALDEF PUBLIC HTProtocol HTWAIS = { "wais", HTLoadWAIS, NULL }; +#endif /* GLOBALDEF_IS_MACRO */ diff --git a/WWW/Library/Implementation/HTWAIS.h b/WWW/Library/Implementation/HTWAIS.h new file mode 100644 index 00000000..4aa885dc --- /dev/null +++ b/WWW/Library/Implementation/HTWAIS.h @@ -0,0 +1,44 @@ +/* WAIS protocol module for the W3 library + WAIS PROTOCOL INTERFACE + + This module does not actually perform the WAIS protocol directly, but it does using one + or more libraries of the freeWAIS distribution. The ui.a library came with the old free + WAIS from TMC, the client.a and wais.a libraries are needed from the freeWAIS from + CNIDR. + + If you include this module in the library, you must also + + Register the HTWAIS protocol at initialisation (e.g. HTInit or HTSInit) by compiling + it with -DDIRECT_WAIS + + Link with the WAIS libraries + + The wais source files are parsed by a separate and independent module, HTWSRC . You + can include HTWSRC without including direct wais using this module, and your WWW code + will be able to read source files, and access WAIS indexes through a gateway. + + A WAIS-WWW gateway is just a normal W3 server with a libwww compiled with this module. + + Anyways, this interface won't change much: + + */ +#ifndef HTWAIS_H +#define HTWAIS_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTAccess.h" + +#ifdef GLOBALREF_IS_MACRO +extern GLOBALREF(HTProtocol, HTWAIS); +#else +GLOBALREF HTProtocol HTWAIS; +#endif /* GLOBALDEF_IS_MACRO */ + +#endif /* HTWAIS_H */ + +/* + Tim BL + +*/ diff --git a/WWW/Library/Implementation/HTWSRC.c b/WWW/Library/Implementation/HTWSRC.c new file mode 100644 index 00000000..cca6394d --- /dev/null +++ b/WWW/Library/Implementation/HTWSRC.c @@ -0,0 +1,478 @@ +/* Parse WAIS Source file HTWSRC.c +** ====================== +** +** This module parses a stream with WAIS source file +** format information on it and creates a structured stream. +** That structured stream is then converted into whatever. +** +** 3 June 93 Bug fix: Won't crash if no description +*/ + +#include "HTUtils.h" +#include "tcp.h" + +#include "HTWSRC.h" + + +/* #include <sys/types.h> already in tcp.h */ +/* #include <sys/stat.h> this too */ +/* #include <stdio.h> included in HTUtils.h -- FM */ +#include "HTML.h" +#include "HTParse.h" + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +#define BIG 10000 /* Arbitrary limit to value length */ +#define PARAM_MAX BIG +#define CACHE_PERIOD (7*86400) /* Time to keep .src file in seconds */ + +#define HEX_ESCAPE '%' + +struct _HTStructured { + CONST HTStructuredClass * isa; + /* ... */ +}; + +#define PUTC(c) (*me->target->isa->put_character)(me->target, c) +#define PUTS(s) (*me->target->isa->put_string)(me->target, s) +#define START(e) (*me->target->isa->start_element)(me->target, e, 0, 0, 0) +#define END(e) (*me->target->isa->end_element)(me->target, e, 0) + + +/* Here are the parameters which can be specified in a source file +*/ +PRIVATE CONST char* par_name[] = { + "version", + "ip-address", +#define PAR_IP_NAME 2 + "ip-name", +#define PAR_TCP_PORT 3 + "tcp-port", +#define PAR_DATABASE_NAME 4 + "database-name", +#define PAR_COST 5 + "cost", +#define PAR_COST_UNIT 6 + "cost-unit", +#define PAR_FREE 7 + "free", +#define PAR_MAINTAINER 8 + "maintainer", +#define PAR_DESCRIPTION 9 + "description", + "keyword-list", + "source", + "window-geometry", + "configuration", + "script", + "update-time", + "contact-at", + "last-contacted", + "confidence", + "num-docs-to-request", + "font", + "font-size", +#define PAR_UNKNOWN 22 + "unknown", + 0, /* Terminate list */ +#define PAR_COUNT 23 +} ; + + +enum tokenstate { beginning, before_tag, colon, before_value, + value, bracketed_value, quoted_value, escape_in_quoted, done }; + + +/* Stream Object +** ------------ +** +** The target is the structured stream down which the +** parsed results will go. +** +** all the static stuff below should go in here to make it reentrant +*/ + +struct _HTStream { + CONST HTStreamClass * isa; + HTStructured * target; + char * par_value[PAR_COUNT]; + enum tokenstate state; + char param[BIG+1]; + int param_number; + int param_count; +}; + + + + +PUBLIC CONST char * hex = "0123456789ABCDEF"; + +/* Decode one hex character +*/ + +PUBLIC char from_hex ARGS1(char, c) +{ + return (c>='0')&&(c<='9') ? c-'0' + : (c>='A')&&(c<='F') ? c-'A'+10 + : (c>='a')&&(c<='f') ? c-'a'+10 + : 0; +} + + +/* State machine +** ------------- +** +** On entry, +** me->state is a valid state (see WSRC_init) +** c is the next character +** On exit, +** returns 1 Done with file +** 0 Continue. me->state is updated if necessary. +** -1 Syntax error error +*/ + + +/* Treat One Character +** ------------------- +*/ +PRIVATE void WSRCParser_put_character ARGS2(HTStream*, me, char, c) +{ + switch (me->state) { + case beginning: + if (c=='(') me->state = before_tag; + break; + + case before_tag: + if (c==')') { + me->state = done; + return; /* Done with input file */ + } else if (c==':') { + me->param_count = 0; + me->state = colon; + } /* Ignore other text */ + break; + + case colon: + if (WHITE(c)) { + me->param[me->param_count++] = 0; /* Terminate */ + for(me->param_number = 0; par_name[me->param_number]; me->param_number++) { + if (0==strcmp(par_name[me->param_number], me->param)) { + break; + } + } + if (!par_name[me->param_number]) { /* Unknown field */ + if (TRACE) fprintf(stderr, + "HTWSRC: Unknown field `%s' in source file\n", + me->param); + me->param_number = PAR_UNKNOWN; + me->state = before_value; /* Could be better ignore */ + return; + } + me->state = before_value; + } else { + if (me->param_count < PARAM_MAX) me->param[me->param_count++] = c; + } + break; + + case before_value: + if (c==')') { + me->state = done; + return; /* Done with input file */ + } + if (WHITE(c)) return; /* Skip white space */ + me->param_count = 0; + if (c=='"') { + me->state = quoted_value; + break; + } + me->state = (c=='"') ? quoted_value : + (c=='(') ? bracketed_value : value; + me->param[me->param_count++] = c; /* Don't miss first character */ + break; + + case value: + if (WHITE(c)) { + me->param[me->param_count] = 0; + StrAllocCopy(me->par_value[me->param_number], me->param); + me->state = before_tag; + } else { + if (me->param_count < PARAM_MAX) me->param[me->param_count++] = c; + } + break; + + case bracketed_value: + if (c==')') { + me->param[me->param_count] = 0; + StrAllocCopy(me->par_value[me->param_number], me->param); + me->state = before_tag; + break; + } + if (me->param_count < PARAM_MAX) me->param[me->param_count++] = c; + break; + + case quoted_value: + if (c=='"') { + me->param[me->param_count] = 0; + StrAllocCopy(me->par_value[me->param_number], me->param); + me->state = before_tag; + break; + } + + if (c=='\\') { /* Ignore escape but switch state */ + me->state = escape_in_quoted; + break; + } + /* Fall through! */ + + case escape_in_quoted: + if (me->param_count < PARAM_MAX) me->param[me->param_count++] = c; + me->state = quoted_value; + break; + + case done: /* Ignore anything after EOF */ + return; + + } /* switch me->state */ +} + + +/* Open Cache file +** =============== +** +** Bugs: Maybe for filesystem-challenged platforms (MSDOS for example) we +** should make a hash code for the filename. +*/ + +#ifdef CACHE_FILE_PREFIX +PRIVATE BOOL write_cache ARGS1(HTStream *, me) +{ + FILE * fp; + char cache_file_name[256]; + char * www_database; + if (!me->par_value[PAR_DATABASE_NAME] + || !me->par_value[PAR_IP_NAME] + ) return NO; + + www_database = HTEscape(me->par_value[PAR_DATABASE_NAME], URL_XALPHAS); + sprintf(cache_file_name, "%sWSRC-%s:%s:%.100s.txt", + CACHE_FILE_PREFIX, + me->par_value[PAR_IP_NAME], + me->par_value[PAR_TCP_PORT] ? me->par_value[PAR_TCP_PORT] : "210", + www_database); + FREE(www_database); + fp = fopen(cache_file_name, "w"); + if (!fp) return NO; + + if (me->par_value[PAR_DESCRIPTION]) + fputs(me->par_value[PAR_DESCRIPTION], fp); + else + fputs("Description not available\n", fp); + fclose(fp); + return YES; +} +#endif + +/* Output equivalent HTML +** ---------------------- +** +*/ + +void give_parameter ARGS2(HTStream *, me, int, p) +{ + PUTS(par_name[p]); + if (me->par_value[p]) { + PUTS(": "); + PUTS(me->par_value[p]); + PUTS("; "); + } else { + PUTS(" NOT GIVEN in source file; "); + } +} + + +/* Generate Outout +** =============== +*/ +PRIVATE void WSRC_gen_html ARGS2(HTStream *, me, BOOL, source_file) + +{ + if (me->par_value[PAR_DATABASE_NAME]) { + char * shortname = 0; + int l; + StrAllocCopy(shortname, me->par_value[PAR_DATABASE_NAME]); + l = strlen(shortname); + if ( l > 4 && !strcasecomp(shortname + l -4, ".src")) { + shortname[l-4] = 0; /* Chop of .src -- boring! */ + } + + START(HTML_HEAD); + PUTS("\n"); + START(HTML_TITLE); + PUTS(shortname); + PUTS(source_file ? " WAIS source file" : " index"); + END(HTML_TITLE); + PUTS("\n"); + END(HTML_HEAD); + + START(HTML_H1); + PUTS(shortname); + PUTS(source_file ? " description" : " index"); + END(HTML_H1); + PUTS("\n"); + FREE(shortname); + } + + START(HTML_DL); /* Definition list of details */ + + if (source_file) { + START(HTML_DT); + PUTS("Access links"); + START(HTML_DD); + if (me->par_value[PAR_IP_NAME] && + me->par_value[PAR_DATABASE_NAME]) { + + char WSRC_address[256]; + char * www_database; + www_database = HTEscape(me->par_value[PAR_DATABASE_NAME], + URL_XALPHAS); + sprintf(WSRC_address, "wais://%s%s%s/%s", + me->par_value[PAR_IP_NAME], + me->par_value[PAR_TCP_PORT] ? ":" : "", + me->par_value[PAR_TCP_PORT] ? me->par_value[PAR_TCP_PORT] :"", + www_database); + + HTStartAnchor(me->target, NULL, WSRC_address); + PUTS("Direct access"); + END(HTML_A); + /** Proxy will be used if defined, so let user know that - FM **/ + PUTS(" (or via proxy server, if defined), or"); + START(HTML_BR); + /** Offer W3 Consortium gateway - FM **/ + sprintf(WSRC_address, "http://www.w3.org:8001/%s%s%s/%s", + me->par_value[PAR_IP_NAME], + me->par_value[PAR_TCP_PORT] ? ":" : "", + me->par_value[PAR_TCP_PORT] ? me->par_value[PAR_TCP_PORT] :"", + www_database); + HTStartAnchor(me->target, NULL, WSRC_address); + PUTS("through W3 Consortium gateway"); + END(HTML_A); + + FREE(www_database); + + } else { + give_parameter(me, PAR_IP_NAME); + give_parameter(me, PAR_DATABASE_NAME); + } + + } /* end if source_file */ + + if (me->par_value[PAR_MAINTAINER]) { + START(HTML_DT); + PUTS("Maintainer"); + START(HTML_DD); + PUTS(me->par_value[PAR_MAINTAINER]); + } + if (me->par_value[PAR_IP_NAME]) { + START(HTML_DT); + PUTS("Host"); + START(HTML_DD); + PUTS(me->par_value[PAR_IP_NAME]); + } + + END(HTML_DL); + + if (me->par_value[PAR_DESCRIPTION]) { + START(HTML_PRE); /* Preformatted description */ + PUTS(me->par_value[PAR_DESCRIPTION]); + END(HTML_PRE); + } + + (*me->target->isa->_free)(me->target); + + return; +} /* generate html */ + + +PRIVATE void WSRCParser_put_string ARGS2(HTStream *, context, CONST char*, str) +{ + CONST char *p; + for(p=str; *p; p++) + WSRCParser_put_character(context, *p); +} + + +PRIVATE void WSRCParser_write ARGS3( + HTStream *, context, + CONST char*, str, + int, l) +{ + CONST char *p; + CONST char *e = str+l; + for(p=str; p<e; p++) + WSRCParser_put_character(context, *p); +} + + +PRIVATE void WSRCParser_free ARGS1(HTStream *, me) +{ + WSRC_gen_html(me, YES); +#ifdef CACHE_FILE_PREFIX + write_cache(me); +#endif + { + int p; + for (p = 0; par_name[p]; p++) { /* Clear out old values */ + FREE(me->par_value[p]); + } + } + FREE(me); +} + +PRIVATE void WSRCParser_abort ARGS2(HTStream *, me, HTError, e) +{ + WSRCParser_free(me); +} + + +/* Stream subclass -- method routines +** --------------- +*/ + +HTStreamClass WSRCParserClass = { + "WSRCParser", + WSRCParser_free, + WSRCParser_abort, + WSRCParser_put_character, + WSRCParser_put_string, + WSRCParser_write + +}; + + +/* Converter from WAIS Source to whatever +** -------------------------------------- +*/ +PUBLIC HTStream* HTWSRCConvert ARGS3( + HTPresentation *, pres, + HTParentAnchor *, anchor, + HTStream *, sink) +{ + HTStream * me = (HTStream*) malloc(sizeof(*me)); + if (!me) outofmem(__FILE__, "HTWSRCConvert"); + + me->isa = &WSRCParserClass; + me->target = HTML_new(anchor, pres->rep_out, sink); + + { + int p; + for(p=0; p < PAR_COUNT; p++) { /* Clear out parameter values */ + me->par_value[p] = 0; + } + } + me->state = beginning; + + return me; +} + diff --git a/WWW/Library/Implementation/HTWSRC.h b/WWW/Library/Implementation/HTWSRC.h new file mode 100644 index 00000000..2e247a67 --- /dev/null +++ b/WWW/Library/Implementation/HTWSRC.h @@ -0,0 +1,46 @@ +/* A parser for WAIS source files + WAIS SOURCE FILE PARSER + + This converter returns a stream object into which a WAIS source file can be written. + The result is put via a structured stream into whatever format was required for the + output stream. + + See also: HTWAIS protocol interface module + + */ +#ifndef HTWSRC_H +#define HTWSRC_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ + +#include "HTFormat.h" + +extern HTStream* HTWSRCConvert PARAMS(( + HTPresentation * pres, + HTParentAnchor * anchor, + HTStream * sink)); + +/* + +Escaping Strings + + HTDeSlash takes out the invlaid characters in a URL path ELEMENT by converting them + into hex-escaped characters. HTEnSlash does the reverse. + + Each returns a pointer to a newly allocated string which must eventually be freed by + the caller. + + */ +extern char * HTDeSlash PARAMS((CONST char * str)); + +extern char * HTEnSlash PARAMS((CONST char * str)); + +#endif + +/* + + Tim BL + + */ diff --git a/WWW/Library/Implementation/HTWriter.c b/WWW/Library/Implementation/HTWriter.c new file mode 100644 index 00000000..03a55648 --- /dev/null +++ b/WWW/Library/Implementation/HTWriter.c @@ -0,0 +1,189 @@ +/* FILE WRITER HTWrite.c +** =========== +** +*/ +#include "HTUtils.h" +#include "tcp.h" + +#include "HTWriter.h" + +#define BUFFER_SIZE 4096 /* Tradeoff */ + +/*#include <stdio.h> included by HTUtils.h -- FM */ + +#include "LYLeaks.h" + +#define FREE(x) if (x) {free(x); x = NULL;} + +/* HTML Object +** ----------- +*/ + +struct _HTStream { + CONST HTStreamClass * isa; + + int soc; + char *write_pointer; + char buffer[BUFFER_SIZE]; +#ifdef NOT_ASCII + BOOL make_ascii; /* Are we writing to the net? */ +#endif +}; + + +/* Write the buffer out to the socket +** ---------------------------------- +*/ + +PRIVATE void flush ARGS1(HTStream *, me) +{ + char *read_pointer = me->buffer; + char *write_pointer = me->write_pointer; + +#ifdef NOT_ASCCII + if (me->make_ascii) { + char * p; + for(p = me->buffer; p < me->write_pointer; p++) + *p = TOASCII(*p); + } +#endif + while (read_pointer < write_pointer) { + int status; + status = NETWRITE(me->soc, me->buffer, /* Put timeout? @@@ */ + write_pointer - read_pointer); + if (status<0) { + if(TRACE) fprintf(stderr, + "HTWrite: Error: write() on socket returns %d !!!\n", status); + return; + } + read_pointer = read_pointer + status; + } + me->write_pointer = me->buffer; +} + + +/*_________________________________________________________________________ +** +** A C T I O N R O U T I N E S +*/ + +/* Character handling +** ------------------ +*/ + +PRIVATE void HTWriter_put_character ARGS2(HTStream *, me, char, c) +{ + if (me->write_pointer == &me->buffer[BUFFER_SIZE]) flush(me); + *me->write_pointer++ = c; +} + + + +/* String handling +** --------------- +** +** Strings must be smaller than this buffer size. +*/ +PRIVATE void HTWriter_put_string ARGS2(HTStream *, me, CONST char*, s) +{ + int l = strlen(s); + if (me->write_pointer + l > &me->buffer[BUFFER_SIZE]) flush(me); + strcpy(me->write_pointer, s); + me->write_pointer = me->write_pointer + l; +} + + +/* Buffer write. Buffers can (and should!) be big. +** ------------ +*/ +PRIVATE void HTWriter_write ARGS3(HTStream *, me, CONST char*, s, int, l) +{ + + CONST char *read_pointer = s; + CONST char *write_pointer = s+l; + + flush(me); /* First get rid of our buffer */ + + while (read_pointer < write_pointer) { + int status = NETWRITE(me->soc, (char *)read_pointer, + write_pointer - read_pointer); + if (status<0) { + if(TRACE) fprintf(stderr, + "HTWriter_write: Error on socket output stream!!!\n"); + return; + } + read_pointer = read_pointer + status; + } +} + + + + +/* Free an HTML object +** ------------------- +** +** Note that the SGML parsing context is freed, but the created object is not, +** as it takes on an existence of its own unless explicitly freed. +*/ +PRIVATE void HTWriter_free ARGS1(HTStream *, me) +{ + flush(me); + NETCLOSE(me->soc); + FREE(me); +} + +PRIVATE void HTWriter_abort ARGS2(HTStream *, me, HTError, e) +{ + HTWriter_free(me); +} + + +/* Structured Object Class +** ----------------------- +*/ +PRIVATE CONST HTStreamClass HTWriter = /* As opposed to print etc */ +{ + "SocketWriter", + HTWriter_free, + HTWriter_abort, + HTWriter_put_character, HTWriter_put_string, + HTWriter_write +}; + + +/* Subclass-specific Methods +** ------------------------- +*/ + +PUBLIC HTStream* HTWriter_new ARGS1(int, soc) +{ + HTStream* me = (HTStream*)malloc(sizeof(*me)); + if (me == NULL) outofmem(__FILE__, "HTML_new"); + me->isa = &HTWriter; + +#ifdef NOT_ASCII + me->make_ascii = NO; +#endif + me->soc = soc; + me->write_pointer = me->buffer; + return me; +} + +/* Subclass-specific Methods +** ------------------------- +*/ + +PUBLIC HTStream* HTASCIIWriter ARGS1(int, soc) +{ + HTStream* me = (HTStream*)malloc(sizeof(*me)); + if (me == NULL) outofmem(__FILE__, "HTML_new"); + me->isa = &HTWriter; + +#ifdef NOT_ASCII + me->make_ascii = YES; +#endif + me->soc = soc; + me->write_pointer = me->buffer; + return me; +} + diff --git a/WWW/Library/Implementation/HTWriter.h b/WWW/Library/Implementation/HTWriter.h new file mode 100644 index 00000000..9fa5f8c6 --- /dev/null +++ b/WWW/Library/Implementation/HTWriter.h @@ -0,0 +1,28 @@ +/* */ + +/* Unix File or Socket Writer HTWriter.c +** -------------------------- +** +** This version of the stream object just writes to a socket. +** The socket is assumed open and closed afterward. +** +** There are two versions (identical on ASCII machines) +** one of which converts to ASCII on output. +** +** Bugs: +** strings written must be less than buffer size. +*/ + +#ifndef HTWRITE_H +#define HTWRITE_H + +#include "HTStream.h" + +extern HTStream * HTWriter_new PARAMS((int soc)); + +extern HTStream * HTASCIIWriter PARAMS((int soc)); + +#endif +/* + + */ diff --git a/WWW/Library/Implementation/HText.h b/WWW/Library/Implementation/HText.h new file mode 100644 index 00000000..7b65751a --- /dev/null +++ b/WWW/Library/Implementation/HText.h @@ -0,0 +1,262 @@ +/* Rich Hypertext object for libWWW + RICH HYPERTEXT OBJECT + + */ + +/* + + This is the C interface to the Objective-C (or whatever) Style-oriented HyperText + class. It is used when a style-oriented text object is available or craeted in order to + display hypertext. + + */ +#ifndef HTEXT_H +#define HTEXT_H +#include "HTAnchor.h" +#include "HTStyle.h" +#include "HTStream.h" +#include "SGML.h" + +#ifdef SHORT_NAMES +#define HTMainText HTMaText +#define HTMainAnchor HtMaAnch +#define HText_new HTHTNew +#define HText_new2 HTHTNew2 +#define HText_free HTHTFree +#define HText_beginAppend HTHTBeAp +#define HText_endAppend HTHTEnAp +#define HText_setStyle HTHTSeSt +#define HText_appendCharacter HTHTApCh +#define HText_appendImage HTHTApIm +#define HText_appendText HTHTApTe +#define HText_appendParagraph HTHTApPa +#define HText_beginAnchor HTHTBeAn +#define HText_endAnchor HTHTEnAn +#define HText_dump HTHTDump +#define HText_nodeAnchor HTHTNoAn +#define HText_select HTHTSele +#define HText_selectAnchor HTHTSeAn +#define HText_applyStyle HTHTApSt +#define HText_updateStyle HTHTUpSt +#define HText_selectionStyle HTHTStyl +#define HText_replaceSel HTHTRepl +#define HText_applyToSimilar HTHTApTo +#define HText_selectUnstyled HTHTSeUn +#define HText_unlinkSelection HTHTUnSe +#define HText_linkSelTo HTHTLiSe +#define HText_referenceSelected HTHTRefS +#endif + +#ifndef THINK_C +#ifndef HyperText /* Objective C version defined HyperText */ +typedef struct _HText HText; /* Normal Library */ +#endif +#else +class CHyperText; /* Mac Think-C browser hook */ +typedef CHyperText HText; +#endif + +extern HText * HTMainText; /* Pointer to current main text */ +extern HTParentAnchor * HTMainAnchor; /* Pointer to current text's anchor */ + +/* + +Creation and deletion + + HTEXT_NEW: CREATE HYPERTEXT OBJECT + + There are several methods depending on how much you want to specify. The output stream + is used with objects which need to output the hypertext to a stream. The structure is + for objects which need to refer to the structure which is kep by the creating stream. + + */ + extern HText * HText_new PARAMS((HTParentAnchor * anchor)); + + extern HText * HText_new2 PARAMS((HTParentAnchor * anchor, + HTStream * output_stream)); + + extern HText * HText_new3 PARAMS((HTParentAnchor * anchor, + HTStream * output_stream, + HTStructured * structure)); + +/* + + FREE HYPERTEXT OBJECT + + */ +extern void HText_free PARAMS((HText * me)); + + +/* + +Object Building methods + + These are used by a parser to build the text in an object HText_beginAppend must be + called, then any combination of other append calls, then HText_endAppend. This allows + optimised handling using buffers and caches which are flushed at the end. + + */ +extern void HText_beginAppend PARAMS((HText * text)); + +extern void HText_endAppend PARAMS((HText * text)); + +/* + + SET THE STYLE FOR FUTURE TEXT + + */ + +extern void HText_setStyle PARAMS((HText * text, HTStyle * style)); + +/* + + ADD ONE CHARACTER + + */ +extern void HText_appendCharacter PARAMS((HText * text, char ch)); + +/* + + ADD A ZERO-TERMINATED STRING + + */ + +extern void HText_appendText PARAMS((HText * text, CONST char * str)); + +/* + + NEW PARAGRAPH + + and similar things + + */ +extern void HText_appendParagraph PARAMS((HText * text)); + +extern void HText_appendLineBreak PARAMS((HText * text)); + +extern void HText_appendHorizontalRule PARAMS((HText * text)); + + + +/* + + START/END SENSITIVE TEXT + + */ + +/* + + The anchor object is created and passed to HText_beginAnchor. The senstive text is + added to the text object, and then HText_endAnchor is called. Anchors may not be + nested. + + */ +extern void HText_beginAnchor PARAMS((HText * text, HTChildAnchor * anc)); +extern void HText_endAnchor PARAMS((HText * text)); + + +/* + + APPEND AN INLINE IMAGE + + The image is handled by the creation of an anchor whose destination is the image + document to be included. The semantics is the intended inline display of the image. + + An alternative implementation could be, for example, to begin an anchor, append the + alternative text or "IMAGE", then end the anchor. This would simply generate some text + linked to the image itself as a separate document. + + */ +extern void HText_appendImage PARAMS(( + HText * text, + HTChildAnchor * anc, + CONST char * alternative_text, + int alignment, + BOOL isMap)); + +/* + + DUMP DIAGNOSTICS TO STDERR + + */ + +extern void HText_dump PARAMS((HText * me)); + +/* + + RETURN THE ANCHOR ASSOCIATED WITH THIS NODE + + */ +extern HTParentAnchor * HText_nodeAnchor PARAMS((HText * me)); + + +/* + +Browsing functions + + */ + + +/* + + BRING TO FRONT AND HIGHLIGHT IT + + */ + + +extern BOOL HText_select PARAMS((HText * text)); +extern BOOL HText_selectAnchor PARAMS((HText * text, HTChildAnchor* anchor)); + +/* + +Editing functions + + These are called from the application. There are many more functions not included here + from the orginal text object. These functions NEED NOT BE IMPLEMENTED in a browser + which cannot edit. + + */ +/* Style handling: +*/ +/* Apply this style to the selection +*/ +extern void HText_applyStyle PARAMS((HText * me, HTStyle *style)); + +/* Update all text with changed style. +*/ +extern void HText_updateStyle PARAMS((HText * me, HTStyle *style)); + +/* Return style of selection +*/ +extern HTStyle * HText_selectionStyle PARAMS(( + HText * me, + HTStyleSheet* sheet)); + +/* Paste in styled text +*/ +extern void HText_replaceSel PARAMS((HText * me, + CONST char *aString, + HTStyle* aStyle)); + +/* Apply this style to the selection and all similarly formatted text +** (style recovery only) +*/ +extern void HTextApplyToSimilar PARAMS((HText * me, HTStyle *style)); + +/* Select the first unstyled run. +** (style recovery only) +*/ +extern void HTextSelectUnstyled PARAMS((HText * me, HTStyleSheet *sheet)); + + +/* Anchor handling: +*/ +extern void HText_unlinkSelection PARAMS((HText * me)); +extern HTAnchor * HText_referenceSelected PARAMS((HText * me)); +extern HTAnchor * HText_linkSelTo PARAMS((HText * me, HTAnchor* anchor)); + + +#endif /* HTEXT_H */ +/* + + end */ diff --git a/WWW/Library/Implementation/LYLeaks.h b/WWW/Library/Implementation/LYLeaks.h new file mode 100644 index 00000000..4109be80 --- /dev/null +++ b/WWW/Library/Implementation/LYLeaks.h @@ -0,0 +1,149 @@ +#ifndef __LYLEAKS_H +/* + * Avoid include redundancy + * Include only if finding memory leaks. + */ +#define __LYLEAKS_H + +/* + * Copyright (c) 1994, University of Kansas, All Rights Reserved + * + * Include File: LYLeaks.h + * Purpose: Header to convert requests for allocation to Lynx + * custom functions to track memory leaks. + * Remarks/Portability/Dependencies/Restrictions: + * For the stdlib.h allocation functions to be overriden by the + * Lynx memory tracking functions all modules allocating, + * freeing, or resizing memory must have LY_FIND_LEAKS + * defined before including this file. + * This header file should be included in every source file which + * does any memory manipulation through use of the + * stdlib.h memory functions. + * For proper reporting of memory leaks, the function LYLeaks + * should be registered for execution by atexit as the + * very first executable statement in main. + * This code is slow and should not be used except in debugging + * circumstances (don't define LY_FIND_LEAKS). + * If you are using LY_FIND_LEAKS and don't want the LYLeak* + * memory functions to be used in a certain file, + * define NO_MEMORY_TRACKING before including this file. + * The only safe way to call the LYLeak* functions is to use + * the below macros because they depend on the static + * string created by __FILE__ to not be dynamic in + * nature (don't free it and assume will exist at all + * times during execution). + * Revision History: + * 05-26-94 created for Lynx 2-3-1, Garrett Arch Blythe + */ + +/* + * Required includes + */ +#include <stdlib.h> +#include "HTUtils.h" + +/* + * Constant defines + */ +#define MAX_CONTENT_LENGTH 50 +#ifdef VMS +#define LEAKAGE_SINK "sys$login:Lynx.leaks" +#else +#define LEAKAGE_SINK "Lynx.leaks" +#endif /* VMS */ + +/* + * Data structures + */ +typedef struct SourceLocation_tag { + /* + * The file name and line number of where an event took place. + */ + CONST char *cp_FileName; + short ssi_LineNumber; +} +SourceLocation; + +typedef struct AllocationList_tag { + /* + * A singly linked list. + */ + struct AllocationList_tag *ALp_Next; + + /* + * The memory pointer allocated. + * If set to NULL, then an invalid request was made. + * The invalid pointer also. + */ + void *vp_Alloced; + void *vp_BadRequest; + + /* + * The size in bytes of the allocated memory. + */ + size_t st_Bytes; + + /* + * The source location of specific event (calloc, malloc, free). + * realloc kept separate since will track last realloc on pointer. + */ + SourceLocation SL_memory; + SourceLocation SL_realloc; +} +AllocationList; + +/* + * Global variable declarations + */ + +/* + * Macros + */ +#if defined(LY_FIND_LEAKS) && !defined(NO_MEMORY_TRACKING) +/* + * Only use these macros if we are to track memory allocations. + * The reason for using a macro instead of a define is that we want + * to track where the initial allocation took place or where + * the last reallocation took place. + * Track where the allocation took place by the __FILE__ and __LINE__ + * defines which are automatic to the compiler. + */ +#ifdef malloc +#undef malloc +#endif /* malloc */ +#define malloc(st_bytes) LYLeakMalloc(st_bytes, __FILE__, __LINE__) + +#ifdef calloc +#undef calloc +#endif /* calloc */ +#define calloc(st_number, st_bytes) LYLeakCalloc(st_number, st_bytes, \ + __FILE__, __LINE__) + +#ifdef realloc +#undef realloc +#endif /* realloc */ +#define realloc(vp_alloced, st_newbytes) LYLeakRealloc(vp_alloced, \ + st_newbytes, __FILE__, __LINE__) + +#ifdef free +#undef free +#endif /* free */ +#define free(vp_alloced) LYLeakFree(vp_alloced, __FILE__, __LINE__) + +#endif /* LY_FIND_LEAKS && !NO_MEMORY_TRACKING */ + +/* + * Function declarations + * See the appropriate source file for usage. + */ +PUBLIC void LYLeaks NOPARAMS; +PUBLIC void *LYLeakMalloc PARAMS((size_t st_bytes, CONST char *cp_File, CONST + short ssi_Line)); +PUBLIC void *LYLeakCalloc PARAMS((size_t st_number, size_t st_bytes, CONST char + *cp_File, CONST short ssi_Line)); +PUBLIC void *LYLeakRealloc PARAMS((void *vp_alloced, size_t st_newbytes, CONST + char *cp_File, CONST short ssi_Line)); +PUBLIC void LYLeakFree PARAMS((void *vp_alloced, CONST char *cp_File, CONST + short ssi_Line)); + +#endif /* __LYLEAKS_H */ diff --git a/WWW/Library/Implementation/LYexit.h b/WWW/Library/Implementation/LYexit.h new file mode 100644 index 00000000..4d7ca529 --- /dev/null +++ b/WWW/Library/Implementation/LYexit.h @@ -0,0 +1,54 @@ +#ifndef __LYEXIT_H +/* + * Avoid include redundancy + */ +#define __LYEXIT_H + +/* + * Copyright (c) 1994, University of Kansas, All Rights Reserved + * + * Include File: LYexit.h + * Purpose: Provide an atexit function for libraries without such. + * Remarks/Portability/Dependencies/Restrictions: + * Include this header in every file that you have an exit or + * atexit statment. + * Revision History: + * 06-15-94 created Lynx 2-3-1 Garrett Arch Blythe + */ + +/* + * Required includes + */ +#include <stdlib.h> +#include "HTUtils.h" + +/* + * Constant defines + */ +#define exit LYexit +#define atexit LYatexit +#define ATEXITSIZE 32 + +/* + * Data structures + */ + +/* + * Global variable declarations + */ + +/* + * Macros + */ + +/* + * Function declarations + */ +extern void LYexit PARAMS((int status)); +#ifdef __STDC__ +extern int LYatexit(void (*function)()); +#else +extern int LYatexit(); +#endif /* __STDC__ */ + +#endif /* __LYEXIT_H */ diff --git a/WWW/Library/Implementation/Makefile b/WWW/Library/Implementation/Makefile new file mode 100644 index 00000000..7f028976 --- /dev/null +++ b/WWW/Library/Implementation/Makefile @@ -0,0 +1,489 @@ +# Makefile generated by imake - do not edit! +# $XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $ +# +# The cpp used on this machine replaces all newlines and multiple tabs and +# spaces in a macro expansion with a single space. Imake tries to compensate +# for this, but is not always successful. +# + +########################################################################### +# Makefile generated from "Imake.tmpl" and </tmp/IIf.a00214> +# $XConsortium: Imake.tmpl,v 1.77 89/12/18 17:01:37 jim Exp $ +# +# Platform-specific parameters may be set in the appropriate .cf +# configuration files. Site-wide parameters may be set in the file +# site.def. Full rebuilds are recommended if any parameters are changed. +# +# If your C preprocessor doesn't define any unique symbols, you'll need +# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing +# "make Makefile", "make Makefiles", or "make World"). +# +# If you absolutely can't get imake to work, you'll need to set the +# variables at the top of each Makefile as well as the dependencies at the +# bottom (makedepend will do this automatically). +# + +########################################################################### +# platform-specific configuration parameters - edit sun.cf to change + +# platform: $XConsortium: sun.cf,v 1.38 89/12/23 16:10:10 jim Exp $ +# operating system: SunOS 4.0.3 + +########################################################################### +# site-specific configuration parameters - edit site.def to change + + SHELL = /bin/sh + + TOP = . + CURRENT_DIR = . + + AR = ar clq + BOOTSTRAPCFLAGS = + CC = cc + + COMPRESS = compress + CPP = /lib/cpp $(STD_CPP_DEFINES) + PREPROCESSCMD = cc -E $(STD_CPP_DEFINES) + INSTALL = install + LD = ld + LINT = lint + LINTLIBFLAG = -C + LINTOPTS = -axz + LN = ln -s + MAKE = make + MV = mv + CP = cp + RANLIB = ranlib + RANLIBINSTFLAGS = + RM = rm -f + STD_INCLUDES = + STD_CPP_DEFINES = + STD_DEFINES = + EXTRA_LOAD_FLAGS = + EXTRA_LIBRARIES = + TAGS = ctags + + SHAREDCODEDEF = -DSHAREDCODE + SHLIBDEF = -DSUNSHLIB + + PROTO_DEFINES = + + INSTPGMFLAGS = + + INSTBINFLAGS = -m 0755 + INSTUIDFLAGS = -m 4755 + INSTLIBFLAGS = -m 0664 + INSTINCFLAGS = -m 0444 + INSTMANFLAGS = -m 0444 + INSTDATFLAGS = -m 0444 + INSTKMEMFLAGS = -m 4755 + + DESTDIR = + + TOP_INCLUDES = -I$(INCROOT) + + CDEBUGFLAGS = -O + CCOPTIONS = + COMPATFLAGS = + + ALLINCLUDES = $(STD_INCLUDES) $(TOP_INCLUDES) $(INCLUDES) $(EXTRA_INCLUDES) + ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(DEFINES) $(COMPATFLAGS) + CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES) + LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES) + LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES) + LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS) + LDCOMBINEFLAGS = -X -r + + MACROFILE = sun.cf + RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut + + IMAKE_DEFINES = + + IRULESRC = $(CONFIGDIR) + IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES) + + ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Imake.rules \ + $(IRULESRC)/Project.tmpl $(IRULESRC)/site.def \ + $(IRULESRC)/$(MACROFILE) $(EXTRA_ICONFIGFILES) + +########################################################################### +# X Window System Build Parameters +# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $ + +########################################################################### +# X Window System make variables; this need to be coordinated with rules +# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $ + + PATHSEP = / + USRLIBDIR = $(DESTDIR)/usr/lib + BINDIR = $(DESTDIR)/usr/bin/X11 + INCROOT = $(DESTDIR)/usr/include + BUILDINCROOT = $(TOP) + BUILDINCDIR = $(BUILDINCROOT)/X11 + BUILDINCTOP = .. + INCDIR = $(INCROOT)/X11 + ADMDIR = $(DESTDIR)/usr/adm + LIBDIR = $(USRLIBDIR)/X11 + CONFIGDIR = $(LIBDIR)/config + LINTLIBDIR = $(USRLIBDIR)/lint + + FONTDIR = $(LIBDIR)/fonts + XINITDIR = $(LIBDIR)/xinit + XDMDIR = $(LIBDIR)/xdm + AWMDIR = $(LIBDIR)/awm + TWMDIR = $(LIBDIR)/twm + GWMDIR = $(LIBDIR)/gwm + MANPATH = $(DESTDIR)/usr/man + MANSOURCEPATH = $(MANPATH)/man + MANDIR = $(MANSOURCEPATH)n + LIBMANDIR = $(MANSOURCEPATH)3 + XAPPLOADDIR = $(LIBDIR)/app-defaults + + SOXLIBREV = 4.2 + SOXTREV = 4.0 + SOXAWREV = 4.0 + SOOLDXREV = 4.0 + SOXMUREV = 4.0 + SOXEXTREV = 4.0 + + FONTCFLAGS = -t + + INSTAPPFLAGS = $(INSTDATFLAGS) + + IMAKE = imake + DEPEND = makedepend + RGB = rgb + FONTC = bdftosnf + MKFONTDIR = mkfontdir + MKDIRHIER = /bin/sh $(BINDIR)/mkdirhier.sh + + CONFIGSRC = $(TOP)/config + CLIENTSRC = $(TOP)/clients + DEMOSRC = $(TOP)/demos + LIBSRC = $(TOP)/lib + FONTSRC = $(TOP)/fonts + INCLUDESRC = $(TOP)/X11 + SERVERSRC = $(TOP)/server + UTILSRC = $(TOP)/util + SCRIPTSRC = $(UTILSRC)/scripts + EXAMPLESRC = $(TOP)/examples + CONTRIBSRC = $(TOP)/../contrib + DOCSRC = $(TOP)/doc + RGBSRC = $(TOP)/rgb + DEPENDSRC = $(UTILSRC)/makedepend + IMAKESRC = $(CONFIGSRC) + XAUTHSRC = $(LIBSRC)/Xau + XLIBSRC = $(LIBSRC)/X + XMUSRC = $(LIBSRC)/Xmu + TOOLKITSRC = $(LIBSRC)/Xt + AWIDGETSRC = $(LIBSRC)/Xaw + OLDXLIBSRC = $(LIBSRC)/oldX + XDMCPLIBSRC = $(LIBSRC)/Xdmcp + BDFTOSNFSRC = $(FONTSRC)/bdftosnf + MKFONTDIRSRC = $(FONTSRC)/mkfontdir + EXTENSIONSRC = $(TOP)/extensions + + DEPEXTENSIONLIB = + EXTENSIONLIB = -lXext + + DEPXLIB = $(DEPEXTENSIONLIB) + XLIB = $(EXTENSIONLIB) -lX11 + + DEPXAUTHLIB = $(USRLIBDIR)/libXau.a + XAUTHLIB = -lXau + + DEPXMULIB = + XMULIB = -lXmu + + DEPOLDXLIB = + OLDXLIB = -loldX + + DEPXTOOLLIB = + XTOOLLIB = -lXt + + DEPXAWLIB = + XAWLIB = -lXaw + + LINTEXTENSIONLIB = $(USRLIBDIR)/llib-lXext.ln + LINTXLIB = $(USRLIBDIR)/llib-lX11.ln + LINTXMU = $(USRLIBDIR)/llib-lXmu.ln + LINTXTOOL = $(USRLIBDIR)/llib-lXt.ln + LINTXAW = $(USRLIBDIR)/llib-lXaw.ln + + DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB) + + DEPLIBS1 = $(DEPLIBS) + DEPLIBS2 = $(DEPLIBS) + DEPLIBS3 = $(DEPLIBS) + +########################################################################### +# Imake rules for building libraries, programs, scripts, and data files +# rules: $XConsortium: Imake.rules,v 1.67 89/12/18 17:14:15 jim Exp $ + +########################################################################### +# start of Imakefile + +# Make WWW under unix for a.n.other unix system (bsd) +# Use this as a template + +TK_WWW_SOURCE_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/Tcl + +TK_WWW_INSTALL_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/$(WWW_MACH) + +TK_WWW_HOME_PAGE=http://www.w3.org/default.html +TK_WWW_START_PAGE=$(TK_WWW_HOME_PAGE) + +CC = gcc -fno-builtin -Wall + +CDEBUGFLAGS = -O3 -pipe + +COMPATFLAGS = -I/afs/athena.mit.edu/course/other/cdsdev/www-compat +CCOPTIONS = + +BINDIR = $(TK_WWW_INSTALL_PATH) + +# For W3 distribution, machine type for subdirectories +WWW_MACH = unix_x + +# The ASIS repository's name for the machine we are on +ASIS_MACH = hardware/os + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = /tmp + +# Common Makefile for W3 Library Code +# ----------------------------------- +# +# (c) CERN 1990, 1991 -- see Copyright.html for conditions +# +# This file should be invariant between systems. +# DEPENDENCIES NOT COMPLETE + +# +# make Compile and link the software (private version) +# make clean Remove intermediate files + +WC = $(WWW)/Library +CMN = $(WWW)/Library/Implementation/ + +# Where shall we put the objects and built library? + +LOB = $(WTMP)/Library/$(WWW_MACH) + +# Bug: This path, if relative, is taken relative to the directory +# in which this makefile is, not the pwd. This screws up the +# recursive invocation + +VC = 2.14 + +CFLAGS2 = $(CFLAGS) -I$(CMN) + +CERNLIBBIN = $(WWW)/bin + +COMMON = $(LOB)/HTParse.o $(LOB)/HTAccess.o $(LOB)/HTTP.o \ + $(LOB)/HTFile.o $(LOB)/HTFTP.o $(LOB)/HTTCP.o \ + $(LOB)/SGML.o $(LOB)/HTMLDTD.o $(LOB)/HTChunk.o \ + $(LOB)/HTPlain.o $(LOB)/HTWriter.o $(LOB)/HTFWriter.o \ + $(LOB)/HTMLGen.o \ + $(LOB)/HTAtom.o $(LOB)/HTAnchor.o $(LOB)/HTStyle.o \ + $(LOB)/HTList.o $(LOB)/HTString.o $(LOB)/HTAlert.o \ + $(LOB)/HTRules.o $(LOB)/HTFormat.o $(LOB)/HTInit.o $(LOB)/HTMIME.o \ + $(LOB)/HTHistory.o $(LOB)/HTNews.o $(LOB)/HTGopher.o \ + $(LOB)/HTTelnet.o $(LOB)/HTWSRC.o $(HTWAIS) + +CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTFile.c \ + $(CMN)HTFTP.c $(CMN)HTTCP.c $(CMN)SGML.c \ + $(CMN)HTMLDTD.c \ + $(CMN)HTPlain.c $(CMN)HTWriter.c $(CMN)HTFWriter.c $(CMN)HTMLGen.c \ + $(CMN)HTChunk.c $(CMN)HTAtom.c $(CMN)HTAnchor.c $(CMN)HTStyle.c \ + $(CMN)HTList.c $(CMN)HTString.c $(CMN)HTAlert.c $(CMN)HTRules.c \ + $(CMN)HTFormat.c $(CMN)HTInit.c $(CMN)HTMIME.c $(CMN)HTHistory.c \ + $(CMN)HTNews.c $(CMN)HTGopher.c $(CMN)HTTelnet.c \ + $(CMN)HTWAIS.c $(CMN)HTWSRC.c + +HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTFile.h \ + $(CMN)HTFTP.h $(CMN)HTTCP.h \ + $(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \ + $(CMN)HTPlain.h $(CMN)HTWriter.h \ + $(CMN)HTFWriter.h $(CMN)HTMLGen.h \ + $(CMN)HTStream.h \ + $(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \ + $(CMN)HTList.h \ + $(CMN)HTString.h $(CMN)HTAlert.h $(CMN)HTRules.h \ + $(CMN)HTFormat.h $(CMN)HTInit.h \ + $(CMN)HTMIME.h $(CMN)HTHistory.h $(CMN)HTNews.h \ + $(CMN)HTGopher.h \ + $(CMN)HTUtils.h $(CMN)tcp.h $(CMN)WWW.h $(CMN)HText.h \ + $(CMN)HTTelnet.h \ + $(CMN)HTWAIS.h $(CMN)HTWSRC.h + +SOURCES = $(CFILES) $(HFILES) $(CMN)Version.make $(CMN)CommonMakefile \ + $(WWW)/README.txt $(WWW)/Copyright.txt $(WWW)/BUILD +SPECIFIC = $(WWW)/All + +# Library +# +# On SGI, ranlib is unnecessary and does not exist so we ignore errors +# for that step +all: $(LOB)/libwww.a + $(MV) $(LOB)/libwww.a $(WC)/$(WWW_MACH) + +$(LOB)/libwww.a : $(COMMON) + ar r $(LOB)/libwww.a $(COMMON) + -ranlib $(LOB)/libwww.a + +# Clean up everything generatable except final products +clean :: + $(RM) $(LOB) + +# Clean up everything generatable including final products + +cleanall :: clean + $(RM) $(LOB)/libwww.a + +# Common code +# ----------- + +# Directory for object files - .created checks it exists + +OE = $(LOB)/.created +$(OE) : + -mkdir $(WTMP) + -mkdir $(WTMP)/Library + -mkdir $(WTMP)/Library/$(WWW_MACH) + touch $@ + +$(LOB)/HTList.o : $(OE) $(CMN)HTList.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTList.c + +$(LOB)/HTAnchor.o : $(OE) $(CMN)HTAnchor.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAnchor.c + +$(LOB)/HTFormat.o : $(OE) $(CMN)HTFormat.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFormat.c + +$(LOB)/HTInit.o : $(OE) $(CMN)HTInit.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTInit.c + +$(LOB)/HTMIME.o : $(OE) $(CMN)HTMIME.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMIME.c + +$(LOB)/HTHistory.o : $(OE) $(CMN)HTHistory.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTHistory.c + +$(LOB)/HTNews.o : $(OE) $(CMN)HTNews.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTNews.c + +$(LOB)/HTGopher.o : $(OE) $(CMN)HTGopher.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGopher.c + +$(LOB)/HTTelnet.o : $(OE) $(CMN)HTTelnet.c $(CMN)HTUtils.h $(CMN)HTTelnet.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTelnet.c + +$(LOB)/HTStyle.o : $(OE) $(CMN)HTStyle.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTStyle.c + +$(LOB)/HTAtom.o : $(OE) $(CMN)HTAtom.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAtom.c + +$(LOB)/HTChunk.o : $(OE) $(CMN)HTChunk.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTChunk.c + +$(LOB)/HTString.o : $(OE) $(CMN)HTString.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTString.c + +$(LOB)/HTAlert.o : $(OE) $(CMN)HTAlert.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTAlert.c + +$(LOB)/HTRules.o : $(OE) $(CMN)HTRules.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTRules.c + +$(LOB)/SGML.o : $(OE) $(CMN)SGML.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)SGML.c + +$(LOB)/HTMLGen.o : $(OE) $(CMN)HTMLGen.c $(CMN)HTUtils.h $(CMN)HTMLDTD.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLGen.c + +$(LOB)/HTMLDTD.o : $(OE) $(CMN)HTMLDTD.c $(CMN)SGML.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLDTD.c + +$(LOB)/HTPlain.o : $(OE) $(CMN)HTPlain.c $(CMN)HTPlain.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPlain.c + +$(LOB)/HTWAIS.o : $(OE) $(CMN)HTWAIS.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(WAISINC) $(CMN)HTWAIS.c + +$(LOB)/HTWSRC.o : $(OE) $(CMN)HTWSRC.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWSRC.c + +$(LOB)/HTWriter.o : $(OE) $(CMN)HTWriter.c $(CMN)HTWriter.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWriter.c + +$(LOB)/HTFWriter.o : $(OE) $(CMN)HTFWriter.c $(CMN)HTFWriter.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFWriter.c + +# Communications & Files + +$(LOB)/HTTP.o : $(OE) $(CMN)HTTP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTP.c + +$(LOB)/HTTCP.o : $(OE) $(CMN)HTTCP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTCP.c + +$(LOB)/HTFile.o : $(OE) $(CMN)HTFile.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFile.c + +$(LOB)/HTFTP.o : $(OE) $(CMN)HTFTP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFTP.c + +$(LOB)/HTAccess.o : $(OE) $(CMN)HTAccess.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAccess.c + +$(LOB)/HTParse.o : $(OE) $(CMN)HTParse.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTParse.c + +########################################################################### +# common rules for all Makefiles - do not edit + +emptyrule:: + +clean:: + $(RM_CMD) \#* + +Makefile:: + -@if [ -f Makefile ]; then \ + echo " $(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \ + $(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \ + else exit 0; fi + $(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR) + +tags:: + $(TAGS) -w *.[ch] + $(TAGS) -xw *.[ch] > TAGS + +saber: + #load $(ALLDEFINES) $(SRCS) + +osaber: + #load $(ALLDEFINES) $(OBJS) + +########################################################################### +# empty rules for directories that do not have SUBDIRS - do not edit + +install:: + @echo "install in $(CURRENT_DIR) done" + +install.man:: + @echo "install.man in $(CURRENT_DIR) done" + +Makefiles:: + +includes:: + +########################################################################### +# dependencies generated by makedepend + diff --git a/WWW/Library/Implementation/SGML.c b/WWW/Library/Implementation/SGML.c new file mode 100644 index 00000000..9cddd35a --- /dev/null +++ b/WWW/Library/Implementation/SGML.c @@ -0,0 +1,2145 @@ +/* General SGML Parser code SGML.c +** ======================== +** +** This module implements an HTStream object. To parse an +** SGML file, create this object which is a parser. The object +** is (currently) created by being passed a DTD structure, +** and a target HTStructured oject at which to throw the parsed stuff. +** +** 6 Feb 93 Binary seraches used. Intreface modified. +*/ +#include "HTUtils.h" +#include "tcp.h" /* For FROMASCII */ + +#include "SGML.h" +#include "HTMLDTD.h" +#include "HTCJK.h" + +#include <ctype.h> +/*#include <stdio.h> included in HTUtils.h -- FM */ +#include "HTChunk.h" + +#include "LYLeaks.h" + +#define INVALID (-1) + +#define FREE(x) if (x) {free(x); x = NULL;} + +PUBLIC HTCJKlang HTCJK = NOCJK; /* CJK enum value. */ +PUBLIC BOOL HTPassEightBitRaw = FALSE; /* Pass 161-172,174-255 raw. */ +PUBLIC BOOL HTPassEightBitNum = FALSE; /* Pass ^ numeric entities raw. */ +PUBLIC BOOL HTPassHighCtrlRaw = FALSE; /* Pass 127-160,173, raw. */ +PUBLIC BOOL HTPassHighCtrlNum = FALSE; /* Pass €-Ÿ raw. */ + +extern BOOLEAN LYCheckForCSI PARAMS((HTStructured *target, char **url)); +extern void LYDoCSI PARAMS((char *url, CONST char *comment, char **csi)); + +/* The State (context) of the parser +** +** This is passed with each call to make the parser reentrant +** +*/ + +#define MAX_ATTRIBUTES 36 /* Max number of attributes per element */ + + +/* Element Stack +** ------------- +** This allows us to return down the stack reselcting styles. +** As we return, attribute values will be garbage in general. +*/ +typedef struct _HTElement HTElement; +struct _HTElement { + HTElement * next; /* Previously nested element or 0 */ + HTTag* tag; /* The tag at this level */ +}; + + +/* Internal Context Data Structure +** ------------------------------- +*/ +struct _HTStream { + + CONST HTStreamClass * isa; /* inherited from HTStream */ + + CONST SGML_dtd *dtd; + HTStructuredClass *actions; /* target class */ + HTStructured *target; /* target object */ + + HTTag *current_tag; + int current_attribute_number; + HTChunk *string; + HTElement *element_stack; + enum sgml_state { S_text, S_litteral, + S_tag, S_tag_gap, S_attr, S_attr_gap, S_equals, S_value, + S_ero, S_cro, + S_exclamation, S_comment, S_doctype, S_marked, + S_sgmlent, S_sgmlele, S_sgmlatt, + S_squoted, S_dquoted, S_end, S_entity, + S_esc, S_dollar, S_paren, S_nonascii_text, + S_dollar_paren, + S_esc_sq, S_dollar_sq, S_paren_sq, S_nonascii_text_sq, + S_dollar_paren_sq, + S_esc_dq, S_dollar_dq, S_paren_dq, S_nonascii_text_dq, + S_dollar_paren_dq, + S_in_kanji, S_junk_tag} state; +#ifdef CALLERDATA + void * callerData; +#endif /* CALLERDATA */ + BOOL present[MAX_ATTRIBUTES]; /* Flags: attribute is present? */ + char * value[MAX_ATTRIBUTES]; /* malloc'd strings or NULL if none */ + + BOOL lead_exclamation; + BOOL first_dash; + BOOL end_comment; + BOOL doctype_bracket; + BOOL first_bracket; + BOOL second_bracket; + + char * recover; + int recover_index; + char * include; + int include_index; + char * url; + char * csi; + int csi_index; +} ; + + +#define PUTC(ch) ((*context->actions->put_character)(context->target, ch)) + +extern BOOL historical_comments; +extern BOOL minimal_comments; +extern BOOL soft_dquotes; + +/* Handle Attribute +** ---------------- +*/ +/* PUBLIC CONST char * SGML_default = ""; ?? */ + +PRIVATE void handle_attribute_name ARGS2( + HTStream *, context, + CONST char *, s) +{ + + HTTag * tag = context->current_tag; + attr * attributes = tag->attributes; + + int high, low, i, diff; /* Binary search for attribute name */ + for (low = 0, high = tag->number_of_attributes; + high > low; + diff < 0 ? (low = i+1) : (high = i)) { + i = (low + (high-low)/2); + diff = strcasecomp(attributes[i].name, s); + if (diff == 0) { /* success: found it */ + context->current_attribute_number = i; + context->present[i] = YES; + FREE(context->value[i]); + return; + } /* if */ + + } /* for */ + + if (TRACE) + fprintf(stderr, "SGML: Unknown attribute %s for tag %s\n", + s, context->current_tag->name); + context->current_attribute_number = INVALID; /* Invalid */ +} + + +/* Handle attribute value +** ---------------------- +*/ +PRIVATE void handle_attribute_value ARGS2( + HTStream *, context, + CONST char *, s) +{ + if (context->current_attribute_number != INVALID) { + StrAllocCopy(context->value[context->current_attribute_number], s); + } else { + if (TRACE) + fprintf(stderr, "SGML: Attribute value %s ignored\n", s); + } + context->current_attribute_number = INVALID; /* can't have two assignments! */ +} + + +/* Handle entity +** ------------- +** +** On entry, +** s contains the entity name zero terminated +** Bugs: +** If the entity name is unknown, the terminator is treated as +** a printable non-special character in all cases, even if it is '<' +** Bug-fix: +** Modified SGML_character() so we only come here with terminator +** as '\0' and check a FoundEntity flag. -- Foteos Macrides +*/ + +PRIVATE BOOL FoundEntity = FALSE; + +PRIVATE void handle_entity ARGS2( + HTStream *, context, + char, term) +{ + CONST char ** entities = context->dtd->entity_names; + CONST char *s = context->string->data; + int high, low, i, diff; + + /* + ** Use Lynx special characters directly for nbsp, ensp, emsp, + ** thinsp, and shy so we go through the HTML_put_character() + ** filters instead of using HTML_put_string(). - FM + */ + if (!strcmp(s, "nbsp")) { + PUTC(1); + FoundEntity = TRUE; + return; + } + if (!strcmp(s, "ensp") || !strcmp(s, "emsp") || !strcmp(s, "thinsp")) { + PUTC(2); + FoundEntity = TRUE; + return; + } + if (!strcmp(s, "shy")) { + PUTC(7); + FoundEntity = TRUE; + return; + } + + /* + ** Handle all other entities normally. - FM + */ + FoundEntity = FALSE; + for (low = 0, high = context->dtd->number_of_entities; + high > low; + diff < 0 ? (low = i+1) : (high = i)) { /* Binary serach */ + i = (low + (high-low)/2); + diff = strcmp(entities[i], s); /* Case sensitive! */ + if (diff == 0) { /* success: found it */ + (*context->actions->put_entity)(context->target, i); + FoundEntity = TRUE; + return; + } + } + /* + ** If entity string not found, display as text. + */ + if (TRACE) + fprintf(stderr, "SGML: Unknown entity %s\n", s); + PUTC('&'); + { + CONST char *p; + for (p = s; *p; p++) { + PUTC(*p); + } + } + if (term != '\0') + PUTC(term); +} + + +/* Handle comment +** -------------- +*/ +PRIVATE void handle_comment ARGS1( + HTStream *, context) +{ + CONST char *s = context->string->data; + + if (TRACE) + fprintf(stderr, "SGML Comment:\n<%s>\n", s); + + if (context->csi == NULL && + strncmp(s, "!--#", 4) == 0 && + LYCheckForCSI(context->target, (char **)&context->url) == TRUE) { + LYDoCSI(context->url, s, (char **)&context->csi); + } + + return; +} + + +/* Handle identifier +** ----------------- +*/ +PRIVATE void handle_identifier ARGS1( + HTStream *, context) +{ + CONST char *s = context->string->data; + + if (TRACE) + fprintf(stderr, "SGML Identifier\n<%s>\n", s); + + return; +} + + +/* Handle doctype +** -------------- +*/ +PRIVATE void handle_doctype ARGS1( + HTStream *, context) +{ + CONST char *s = context->string->data; + + if (TRACE) + fprintf(stderr, "SGML Doctype\n<%s>\n", s); + + return; +} + + +/* Handle marked +** ------------- +*/ +PRIVATE void handle_marked ARGS1( + HTStream *, context) +{ + CONST char *s = context->string->data; + + if (TRACE) + fprintf(stderr, "SGML Marked Section:\n<%s>\n", s); + + return; +} + + +/* Handle sgmlent +** -------------- +*/ +PRIVATE void handle_sgmlent ARGS1( + HTStream *, context) +{ + CONST char *s = context->string->data; + + if (TRACE) + fprintf(stderr, "SGML Entity Declaration:\n<%s>\n", s); + + return; +} + + +/* Handle sgmlent +** -------------- +*/ +PRIVATE void handle_sgmlele ARGS1( + HTStream *, context) +{ + CONST char *s = context->string->data; + + if (TRACE) + fprintf(stderr, "SGML Element Declaration:\n<%s>\n", s); + + return; +} + + +/* Handle sgmlatt +** -------------- +*/ +PRIVATE void handle_sgmlatt ARGS1( + HTStream *, context) +{ + CONST char *s = context->string->data; + + if (TRACE) + fprintf(stderr, "SGML Attribute Declaration:\n<%s>\n", s); + + return; +} + + +/* End element +** ----------- +*/ +PRIVATE void end_element ARGS2( + HTStream *, context, + HTTag *, old_tag) +{ + if (TRACE) + fprintf(stderr, "SGML: End </%s>\n", old_tag->name); + if (old_tag->contents == SGML_EMPTY) { + if (TRACE) + fprintf(stderr, "SGML: Illegal end tag </%s> found.\n", + old_tag->name); + return; + } +#ifdef WIND_DOWN_STACK + while (context->element_stack) { /* Loop is error path only */ +#else + if (context->element_stack) { /* Substitute and remove one stack element */ +#endif /* WIND_DOWN_STACK */ + HTElement * N = context->element_stack; + HTTag * t = N->tag; + + if (old_tag != t) { /* Mismatch: syntax error */ + if (context->element_stack->next) { /* This is not the last level */ + if (TRACE) fprintf(stderr, + "SGML: Found </%s> when expecting </%s>. </%s> assumed.\n", + old_tag->name, t->name, t->name); + } else { /* last level */ + if (TRACE) fprintf(stderr, + "SGML: Found </%s> when expecting </%s>. </%s> Ignored.\n", + old_tag->name, t->name, old_tag->name); + return; /* Ignore */ + } + } + + context->element_stack = N->next; /* Remove from stack */ + FREE(N); + (*context->actions->end_element)(context->target, + t - context->dtd->tags, (char **)&context->include); +#ifdef WIND_DOWN_STACK + if (old_tag == t) + return; /* Correct sequence */ +#else + return; +#endif /* WIND_DOWN_STACK */ + + /* Syntax error path only */ + + } + if (TRACE) + fprintf(stderr, "SGML: Extra end tag </%s> found and ignored.\n", + old_tag->name); +} + + +/* Start a element +*/ +PRIVATE void start_element ARGS1( + HTStream *, context) +{ + HTTag * new_tag = context->current_tag; + + if (TRACE) + fprintf(stderr, "SGML: Start <%s>\n", new_tag->name); + (*context->actions->start_element)( + context->target, + new_tag - context->dtd->tags, + context->present, + (CONST char**) context->value, /* coerce type for think c */ + (char **)&context->include); + if (new_tag->contents != SGML_EMPTY) { /* i.e. tag not empty */ + HTElement * N = (HTElement *)malloc(sizeof(HTElement)); + if (N == NULL) + outofmem(__FILE__, "start_element"); + N->next = context->element_stack; + N->tag = new_tag; + context->element_stack = N; + } +} + + +/* Find Tag in DTD tag list +** ------------------------ +** +** On entry, +** dtd points to dtd structire including valid tag list +** string points to name of tag in question +** +** On exit, +** returns: +** NULL tag not found +** else address of tag structure in dtd +*/ +PUBLIC HTTag * SGMLFindTag ARGS2( + CONST SGML_dtd*, dtd, + CONST char *, string) +{ + int high, low, i, diff; + for (low = 0, high=dtd->number_of_tags; + high > low; + diff < 0 ? (low = i+1) : (high = i)) { /* Binary serach */ + i = (low + (high-low)/2); + diff = strcasecomp(dtd->tags[i].name, string); /* Case insensitive */ + if (diff == 0) { /* success: found it */ + return &dtd->tags[i]; + } + } + return NULL; +} + +/*________________________________________________________________________ +** Public Methods +*/ + + +/* Could check that we are back to bottom of stack! @@ */ + +PUBLIC void SGML_free ARGS1( + HTStream *, context) +{ + int i; + HTElement * cur; + HTElement * next; + + (*context->actions->_free)(context->target); + HTChunkFree(context->string); + + /* free strings */ + for (i = 0; i < MAX_ATTRIBUTES; i++) + FREE(context->value[i]); + + FREE(context->recover); + FREE(context->include); + FREE(context->url); + FREE(context->csi); + + cur = context->element_stack; + while (cur) { + next = cur->next; + FREE(cur); + cur = next; + } + + FREE(context); +} + +PUBLIC void SGML_abort ARGS2( + HTStream *, context, + HTError, e) +{ + int i; + + (*context->actions->_abort)(context->target, e); + HTChunkFree(context->string); + + /* free strings */ + for (i = 0; i < MAX_ATTRIBUTES; i++) + FREE(context->value[i]); + + FREE(context->recover); + FREE(context->include); + FREE(context->url); + FREE(context->csi); + + FREE(context); +} + + +/* Read and write user callback handle +** ----------------------------------- +** +** The callbacks from the SGML parser have an SGML context parameter. +** These calls allow the caller to associate his own context with a +** particular SGML context. +*/ + +#ifdef CALLERDATA +PUBLIC void* SGML_callerData ARGS1( + HTStream *, context) +{ + return context->callerData; +} + +PUBLIC void SGML_setCallerData ARGS2( + HTStream *, context, + void*, data) +{ + context->callerData = data; +} +#endif /* CALLERDATA */ + +PUBLIC void SGML_character ARGS2( + HTStream *, context, + char, c) +{ + CONST SGML_dtd *dtd = context->dtd; + HTChunk *string = context->string; + CONST char * EntityName; + extern int current_char_set; + extern char *LYchar_set_names[]; + extern CONST char * HTMLGetEntityName PARAMS((int i)); + +top: + /* + ** Ignore low ISO 646 7-bit control characters + ** if HTCJK is not set. - FM + */ + if ((unsigned char)c < 32 && + c != 9 && c != 10 && c != 13 && + HTCJK == NOCJK) + return; + + /* + ** Ignore 127 if we don't have HTPassHighCtrlRaw + ** or HTCJK set. - FM + */ + if (c == 127 && + !(HTPassHighCtrlRaw || HTCJK != NOCJK)) + return; + + /* + ** Ignore 8-bit control characters 128 - 159 if + ** neither HTPassHighCtrlRaw nor HTCJK is set. - FM + */ + if ((unsigned char)c > 127 && (unsigned char)c < 160 && + !(HTPassHighCtrlRaw || HTCJK != NOCJK)) + return; + + /* + ** Handle character based on context->state. + */ + switch(context->state) { + + case S_in_kanji: + context->state = S_text; + PUTC(c); + break; + + case S_text: + if (HTCJK != NOCJK && (c & 0200) != 0) { + /* + ** Setting up for Kanji multibyte handling (based on + ** Takuya ASADA's (asada@three-a.co.jp) CJK Lynx). - FM + */ + context->state = S_in_kanji; + PUTC(c); + break; + } else if (HTCJK != NOCJK && c == '\033') { + /* + ** Setting up for CJK escape sequence handling (based on + ** Takuya ASADA's (asada@three-a.co.jp) CJK Lynx). - FM + */ + context->state = S_esc; + PUTC(c); + break; + } + if (c == '&' && (!context->element_stack || + (context->element_stack->tag && + (context->element_stack->tag->contents == + SGML_MIXED || + context->element_stack->tag->contents == + SGML_RCDATA)))) { + /* + ** Setting up for possible entity, without the leading '&'. - FM + */ + string->size = 0; + context->state = S_ero; + } else if (c == '<') { + /* + ** Setting up for possible tag. - FM + */ + string->size = 0; + context->state = (context->element_stack && + context->element_stack->tag && + context->element_stack->tag->contents == SGML_LITTERAL) + ? + S_litteral : S_tag; + /* + ** Convert 160 (nbsp) to Lynx special character if + ** neither HTPassHighCtrlRaw nor HTCJK is set. - FM + */ + } else if ((unsigned char)c == 160 && + !(HTPassHighCtrlRaw || HTCJK != NOCJK)) { + PUTC(1); + /* + ** Convert 173 (shy) to Lynx special character if + ** neither HTPassHighCtrlRaw nor HTCJK is set. - FM + */ + } else if ((unsigned char)c == 173 && + !(HTPassHighCtrlRaw || HTCJK != NOCJK)) { + PUTC(7); + /* + ** If it's any other (> 160) 8-bit chararcter, and + ** we have not set HTPassEightBitRaw nor HTCJK, nor + ** have the "ISO Latin 1" character set selected, + ** back translate for our character set. - FM + */ + } else if ((unsigned char)c > 160 && + !(HTPassEightBitRaw || HTCJK != NOCJK) && + strncmp(LYchar_set_names[current_char_set], + "ISO Latin 1", 11)) { + int i; + int value; + + string->size = 0; + value = (int)((unsigned char)c - 160); + EntityName = HTMLGetEntityName(value); + for (i = 0; EntityName[i]; i++) + HTChunkPutc(string, EntityName[i]); + HTChunkTerminate(string); + handle_entity(context, '\0'); + string->size = 0; + if (!FoundEntity) + PUTC(';'); + /* + ** If we get to here, pass the character. - FM + */ + } else { + PUTC(c); + } + break; + + /* + ** In litteral mode, waits only for specific end tag (for + ** compatibility with old servers, and for Lynx). - FM + */ + case S_litteral : + HTChunkPutc(string, c); + if (TOUPPER(c) != ((string->size == 1) ? + '/' : + context->element_stack->tag->name[string->size-2])) { + int i; + + /* + ** If complete match, end litteral. + */ + if ((c == '>') && + (!context->element_stack->tag->name[string->size-2])) { + end_element(context, context->element_stack->tag); + string->size = 0; + context->current_attribute_number = INVALID; + context->state = S_text; + break; + } + /* + ** If Mismatch: recover string. + */ + PUTC('<'); + for (i = 0; i < string->size; i++) /* recover */ + PUTC(string->data[i]); + string->size = 0; + context->state = S_text; + } + break; + + /* + ** Character reference (numeric entity) or named entity. + */ + case S_ero: + if (c == '#') { + /* + ** Setting up for possible numeric entity. + */ + context->state = S_cro; /* &# is Char Ref Open */ + break; + } + context->state = S_entity; /* Fall through! */ + + /* + ** Handle possible named entity. + */ + case S_entity: + if ((unsigned char)c < 127 && isalnum((unsigned char)c)) { + /* + ** Accept valid ASCII character. - FM + */ + HTChunkPutc(string, c); + } else if (string->size == 0) { + /* + ** It was an ampersand that's just text, so output + ** the ampersand and recycle this character. - FM + */ + PUTC('&'); + context->state = S_text; + goto top; + } else { + /* + ** Terminate entity name and try to handle it. - FM + */ + HTChunkTerminate(string); + handle_entity(context, '\0'); + string->size = 0; + context->state = S_text; + /* + ** Don't eat the terminator if we didn't find the + ** entity name and therefore sent the raw string + ** via handle_entity(), or if the terminator is + ** not the "standard" semi-colon for HTML. - FM + */ + if (!FoundEntity || c != ';') + goto top; + } + break; + + /* + ** Handle possible numeric entity. + */ + case S_cro: + if ((unsigned char)c < 127 && isdigit((unsigned char)c)) { + /* + ** Accept only valid ASCII digits. - FM + */ + HTChunkPutc(string, c); /* accumulate a character NUMBER */ + } else if (string->size == 0) { + /* + ** No digits following the "&#" so recover + ** them and recycle the character. - FM + */ + PUTC('&'); + PUTC('#'); + context->state = S_text; + goto top; + } else if ((unsigned char)c > 127 || isalnum((unsigned char)c)) { + /* + ** We have digit(s), but not a valid terminator, + ** so recover the "&#" and digit(s) and recycle + ** the character. - FM + */ + int i; + PUTC('&'); + PUTC('#'); + for (i = 0; i < string->size; i++) /* recover */ + PUTC(string->data[i]); + string->size = 0; + context->state = S_text; + goto top; + } else { + /* + ** Terminate the numeric entity and try to handle it. - FM + */ + int value, i; + HTChunkTerminate(string); + if (sscanf(string->data, "%d", &value) == 1) { + if (value == 8482) { + /* + ** trade Treat as reg. - FM + */ + value = 174; + } + /* + ** Show the numeric entity if the value: + ** (1) Is greater than 255 (until we support Unicode). + ** (2) Is less than 32, and not valid or we don't + ** have HTCJK set. + ** (3) Is 127 and we don't have HTPassHighCtrlRaw or + ** HTCJK set. + ** (4) Is 128 - 159 and we don't have HTPassHighCtrlNum + ** set. + ** - FM + */ + if ((value > 255) || + (value < 32 && + value != 9 && value != 10 && value != 13 && + HTCJK == NOCJK) || + (value == 127 && + !(HTPassHighCtrlRaw || HTCJK != NOCJK)) || + (value > 127 && value < 160 && + !HTPassHighCtrlNum)) { + if (value == 8194 || value == 8195 || value == 8201) { + /* + ** ensp, emsp or thinsp. - FM + */ + PUTC(2); + break; + } + if (value == 8211 || value == 8212) { + /* + ** ndash or mdash. - FM + */ + PUTC('-'); + break; + } + /* + ** Unhandled or llegal value. Recover the "&#" + ** and digit(s), and recycle the terminator. - FM + */ + PUTC('&'); + PUTC('#'); + string->size--; + for (i = 0; i < string->size; i++) /* recover */ + PUTC(string->data[i]); + string->size = 0; + context->state = S_text; + goto top; + } else if (value == 160) { + /* + ** Use Lynx special character for 160 (nbsp). - FM + */ + PUTC(1); + } else if (value == 173) { + /* + ** Use Lynx special character for 173 (shy) - FM + */ + PUTC(7); + } else if (value < 161 || HTPassEightBitNum || + !strncmp(LYchar_set_names[current_char_set], + "ISO Latin 1", 11)) { + /* + ** No conversion needed. - FM + */ + PUTC(FROMASCII((char)value)); + } else { + /* + ** Convert and handle as named entity. - FM + */ + value -= 160; + EntityName = HTMLGetEntityName(value); + if (EntityName && EntityName[0] != '\0') { + string->size = 0; + for (i = 0; EntityName[i]; i++) + HTChunkPutc(string, EntityName[i]); + HTChunkTerminate(string); + handle_entity(context, '\0'); + /* + ** Add a semi-colon if something went wrong + ** and handle_entity() sent the string. - FM + */ + if (!FoundEntity) { + PUTC(';'); + } + } else { + /* + ** Our conversion failed, so recover the "&#" + ** and digit(s), and recycle the terminator. - FM + */ + PUTC('&'); + PUTC('#'); + string->size--; + for (i = 0; i < string->size; i++) /* recover */ + PUTC(string->data[i]); + string->size = 0; + context->state = S_text; + goto top; + } + } + /* + ** If we get to here, we succeeded. Hoorah!!! - FM + */ + string->size = 0; + context->state = S_text; + /* + ** Don't eat the terminator if it's not + ** the "standard" semi-colon for HTML. - FM + */ + if (c != ';') + goto top; + } else { + /* + ** Not an entity, and don't know why not, so add the + ** terminator to the string, output the "&#", and + ** process the string via the recover element. - FM + */ + string->size--; + HTChunkPutc(string, c); + HTChunkTerminate(string); + PUTC('&'); + PUTC('#'); + if (context->recover == NULL) { + StrAllocCopy(context->recover, string->data); + context->recover_index = 0; + } else { + StrAllocCat(context->recover, string->data); + } + string->size = 0; + context->state = S_text; + break; + } + } + break; + + /* + ** Tag + */ + case S_tag: /* new tag */ + if ((unsigned char)c < 127 && isalnum((unsigned char)c)) { + /* + ** Add valid ASCII character. - FM + */ + HTChunkPutc(string, c); + } else if (c == '!' && !string->size) { /* <! */ + /* + ** Terminate and set up for possible comment, + ** identifier, declaration, or marked section. - FM + */ + context->state = S_exclamation; + context->lead_exclamation = TRUE; + context->doctype_bracket = FALSE; + context->first_bracket = FALSE; + HTChunkPutc(string, c); + break; + } else if (WHITE(c) && !string->size) { /* <WHITE */ + /* + ** Recover the '<' and WHITE character. - FM + */ + context->state = S_text; + PUTC('<'); + goto top; + } else { /* End of tag name */ + /* + ** Try to handle tag. - FM + */ + HTTag * t; + if (c == '/') { + if (TRACE) + if (string->size!=0) + fprintf(stderr,"SGML: `<%s/' found!\n", string->data); + context->state = S_end; + break; + } + HTChunkTerminate(string) ; + + t = SGMLFindTag(dtd, string->data); + if (!t) { + if (c == ':' && 0 == strcasecomp(string->data, "URL")) { + /* + ** Treat <URL: as text rather than a junk tag, + ** so we display it and the URL (Lynxism 8-). - FM + */ + int i; + PUTC('<'); + for (i = 0; i < 3; i++) /* recover */ + PUTC(string->data[i]); + PUTC(c); + if (TRACE) + fprintf(stderr, "SGML: Treating <%s%c as text\n", + string->data, c); + string->size = 0; + context->state = S_text; + } else { + if (TRACE) + fprintf(stderr, "SGML: *** Unknown element %s\n", + string->data); + context->state = (c == '>') ? S_text : S_junk_tag; + } + break; + } + context->current_tag = t; + + /* + ** Clear out attributes. + */ + { + int i; + for (i=0; i< context->current_tag->number_of_attributes; i++) + context->present[i] = NO; + } + string->size = 0; + context->current_attribute_number = INVALID; + + if (c == '>') { + if (context->current_tag->name) + start_element(context); + context->state = S_text; + } else { + context->state = S_tag_gap; + } + } + break; + + case S_exclamation: + if (context->lead_exclamation && c == '-') { + /* + ** Set up for possible comment. - FM + */ + context->lead_exclamation = FALSE; + context->first_dash = TRUE; + HTChunkPutc(string, c); + break; + } + if (context->lead_exclamation && c == '[') { + /* + ** Set up for possible marked section. - FM + */ + context->lead_exclamation = FALSE; + context->first_bracket = TRUE; + context->second_bracket = FALSE; + HTChunkPutc(string, c); + context->state = S_marked; + break; + } + if (context->first_dash && c == '-') { + /* + ** Set up to handle comment. - FM + */ + context->lead_exclamation = FALSE; + context->first_dash = FALSE; + context->end_comment = FALSE; + HTChunkPutc(string, c); + context->state = S_comment; + break; + } + context->lead_exclamation = FALSE; + context->first_dash = FALSE; + if (c == '>') { + /* + ** Try to handle identifier. - FM + */ + HTChunkTerminate(string); + handle_identifier(context); + string->size = 0; + context->state = S_text; + break; + } + if (WHITE(c)) { + if (string->size == 8 && + !strncasecomp(string->data, "!DOCTYPE", 8)) { + /* + ** Set up for DOCTYPE declaration. - FM + */ + HTChunkPutc(string, c); + context->doctype_bracket = FALSE; + context->state = S_doctype; + break; + } + if (string->size == 7 && + !strncasecomp(string->data, "!ENTITY", 7)) { + /* + ** Set up for ENTITY declaration. - FM + */ + HTChunkPutc(string, c); + context->first_dash = FALSE; + context->end_comment = TRUE; + context->state = S_sgmlent; + break; + } + if (string->size == 8 && + !strncasecomp(string->data, "!ELEMENT", 8)) { + /* + ** Set up for ELEMENT declaration. - FM + */ + HTChunkPutc(string, c); + context->first_dash = FALSE; + context->end_comment = TRUE; + context->state = S_sgmlele; + break; + } + if (string->size == 8 && + !strncasecomp(string->data, "!ATTLIST", 8)) { + /* + ** Set up for ATTLIST declaration. - FM + */ + HTChunkPutc(string, c); + context->first_dash = FALSE; + context->end_comment = TRUE; + context->state = S_sgmlatt; + break; + } + } + HTChunkPutc(string, c); + break; + + case S_comment: /* Expecting comment. - FM */ + if (historical_comments) { + /* + ** Any '>' terminates. - FM + */ + if (c == '>') { + HTChunkTerminate(string); + handle_comment(context); + string->size = 0; + context->end_comment = FALSE; + context->first_dash = FALSE; + context->state = S_text; + break; + } + HTChunkPutc(string, c); + break; + } + if (!context->first_dash && c == '-') { + HTChunkPutc(string, c); + context->first_dash = TRUE; + break; + } + if (context->first_dash && c == '-') { + HTChunkPutc(string, c); + context->first_dash = FALSE; + if (!context->end_comment) + context->end_comment = TRUE; + else if (!minimal_comments) + /* + ** Validly treat '--' pairs as successive comments + ** (for minimal, any "--WHITE>" terminates). - FM + */ + context->end_comment = FALSE; + break; + } + if (context->end_comment && c == '>') { + /* + ** Terminate and handle the comment. - FM + */ + HTChunkTerminate(string); + handle_comment(context); + string->size = 0; + context->end_comment = FALSE; + context->first_dash = FALSE; + context->state = S_text; + break; + } + context->first_dash = FALSE; + if (context->end_comment && !isspace(c)) + context->end_comment = FALSE; + HTChunkPutc(string, c); + break; + + case S_doctype: /* Expecting DOCTYPE. - FM */ + if (context->doctype_bracket) { + HTChunkPutc(string, c); + if (c == ']') + context->doctype_bracket = FALSE; + break; + } + if (c == '[' && WHITE(string->data[string->size - 1])) { + HTChunkPutc(string, c); + context->doctype_bracket = TRUE; + break; + } + if (c == '>') { + HTChunkTerminate(string); + handle_doctype(context); + string->size = 0; + context->state = S_text; + break; + } + HTChunkPutc(string, c); + break; + + case S_marked: /* Expecting marked section. - FM */ + if (context->first_bracket && c == '[') { + HTChunkPutc(string, c); + context->first_bracket = FALSE; + context->second_bracket = TRUE; + break; + } + if (context->second_bracket && c == ']' && + string->data[string->size - 1] == ']') { + HTChunkPutc(string, c); + context->second_bracket = FALSE; + break; + } + if (!context->second_bracket && c == '>') { + HTChunkTerminate(string); + handle_marked(context); + string->size = 0; + context->state = S_text; + break; + } + HTChunkPutc(string, c); + break; + + case S_sgmlent: /* Expecting ENTITY. - FM */ + if (!context->first_dash && c == '-') { + HTChunkPutc(string, c); + context->first_dash = TRUE; + break; + } + if (context->first_dash && c == '-') { + HTChunkPutc(string, c); + context->first_dash = FALSE; + if (!context->end_comment) + context->end_comment = TRUE; + else + context->end_comment = FALSE; + break; + } + if (context->end_comment && c == '>') { + HTChunkTerminate(string); + handle_sgmlent(context); + string->size = 0; + context->end_comment = FALSE; + context->first_dash = FALSE; + context->state = S_text; + break; + } + context->first_dash = FALSE; + HTChunkPutc(string, c); + break; + + case S_sgmlele: /* Expecting ELEMENT. - FM */ + if (!context->first_dash && c == '-') { + HTChunkPutc(string, c); + context->first_dash = TRUE; + break; + } + if (context->first_dash && c == '-') { + HTChunkPutc(string, c); + context->first_dash = FALSE; + if (!context->end_comment) + context->end_comment = TRUE; + else + context->end_comment = FALSE; + break; + } + if (context->end_comment && c == '>') { + HTChunkTerminate(string); + handle_sgmlele(context); + string->size = 0; + context->end_comment = FALSE; + context->first_dash = FALSE; + context->state = S_text; + break; + } + context->first_dash = FALSE; + HTChunkPutc(string, c); + break; + + case S_sgmlatt: /* Expecting ATTLIST. - FM */ + if (!context->first_dash && c == '-') { + HTChunkPutc(string, c); + context->first_dash = TRUE; + break; + } + if (context->first_dash && c == '-') { + HTChunkPutc(string, c); + context->first_dash = FALSE; + if (!context->end_comment) + context->end_comment = TRUE; + else + context->end_comment = FALSE; + break; + } + if (context->end_comment && c == '>') { + HTChunkTerminate(string); + handle_sgmlatt(context); + string->size = 0; + context->end_comment = FALSE; + context->first_dash = FALSE; + context->state = S_text; + break; + } + context->first_dash = FALSE; + HTChunkPutc(string, c); + break; + + case S_tag_gap: /* Expecting attribute or '>' */ + if (WHITE(c)) + break; /* Gap between attributes */ + if (c == '>') { /* End of tag */ + if (context->current_tag->name) + start_element(context); + context->state = S_text; + break; + } + HTChunkPutc(string, c); + context->state = S_attr; /* Get attribute */ + break; + + /* accumulating value */ + case S_attr: + if (WHITE(c) || (c == '>') || (c == '=')) { /* End of word */ + HTChunkTerminate(string); + handle_attribute_name(context, string->data); + string->size = 0; + if (c == '>') { /* End of tag */ + if (context->current_tag->name) + start_element(context); + context->state = S_text; + break; + } + context->state = (c == '=' ? S_equals: S_attr_gap); + } else { + HTChunkPutc(string, c); + } + break; + + case S_attr_gap: /* Expecting attribute or '=' or '>' */ + if (WHITE(c)) + break; /* Gap after attribute */ + if (c == '>') { /* End of tag */ + if (context->current_tag->name) + start_element(context); + context->state = S_text; + break; + } else if (c == '=') { + context->state = S_equals; + break; + } + HTChunkPutc(string, c); + context->state = S_attr; /* Get next attribute */ + break; + + case S_equals: /* After attr = */ + if (WHITE(c)) + break; /* Before attribute value */ + if (c == '>') { /* End of tag */ + if (TRACE) + fprintf(stderr, "SGML: found = but no value\n"); + if (context->current_tag->name) + start_element(context); + context->state = S_text; + break; + + } else if (c == '\'') { + context->state = S_squoted; + break; + + } else if (c == '"') { + context->state = S_dquoted; + break; + } + HTChunkPutc(string, c); + context->state = S_value; + break; + + case S_value: + if (WHITE(c) || (c == '>')) { /* End of word */ + HTChunkTerminate(string) ; + handle_attribute_value(context, string->data); + string->size = 0; + if (c == '>') { /* End of tag */ + if (context->current_tag->name) + start_element(context); + context->state = S_text; + break; + } + else context->state = S_tag_gap; + } else { + HTChunkPutc(string, c); + } + break; + + case S_squoted: /* Quoted attribute value */ + if (c == '\'') { /* End of attribute value */ + HTChunkTerminate(string) ; + handle_attribute_value(context, string->data); + string->size = 0; + context->state = S_tag_gap; + } else if (c == '\033') { + /* + ** Setting up for possible single quotes in CJK escape + ** sequences. - Takuya ASADA (asada@three-a.co.jp) + */ + context->state = S_esc_sq; + HTChunkPutc(string, c); + } else { + HTChunkPutc(string, c); + } + break; + + case S_dquoted: /* Quoted attribute value */ + if (c == '"' || /* Valid end of attribute value */ + (soft_dquotes && /* If emulating old Netscape bug, treat '>' */ + c == '>')) { /* as a co-terminator of dquoted and tag */ + HTChunkTerminate(string) ; + handle_attribute_value(context, string->data); + string->size = 0; + context->state = S_tag_gap; + if (c == '>') /* We emulated the Netscape bug, so we go */ + goto top; /* back and treat it as the tag terminator */ + } else if (c == '\033') { + /* + ** Setting up for possible double quotes in CJK escape + ** sequences. - Takuya ASADA (asada@three-a.co.jp) + */ + context->state = S_esc_dq; + HTChunkPutc(string, c); + } else { + HTChunkPutc(string, c); + } + break; + + case S_end: /* </ */ + if ((unsigned char)c < 127 && isalnum((unsigned char)c)) + HTChunkPutc(string, c); + else { /* End of end tag name */ + HTTag * t=0; + HTChunkTerminate(string) ; + if (!*string->data) { /* Empty end tag */ + if (context->element_stack) + t = context->element_stack->tag; + } else { + t = SGMLFindTag(dtd, string->data); + } + if (!t) { + if (TRACE) + fprintf(stderr, "Unknown end tag </%s>\n", string->data); + } else { + BOOL tag_OK = (c == '>' || WHITE(c)); + context->current_tag = t; + if (tag_OK && + (!strcasecomp(string->data, "DD") || + !strcasecomp(string->data, "DT") || + !strcasecomp(string->data, "LI") || + !strcasecomp(string->data, "LH") || + !strcasecomp(string->data, "TD") || + !strcasecomp(string->data, "TH") || + !strcasecomp(string->data, "TR") || + !strcasecomp(string->data, "THEAD") || + !strcasecomp(string->data, "TFOOT") || + !strcasecomp(string->data, "TBODY") || + !strcasecomp(string->data, "COLGROUP"))) { + /* + ** Don't treat these end tags as invalid, + ** nor act on them. - FM + */ + if (TRACE) + fprintf(stderr, + "SGML: `</%s%c' found! Ignoring it.\n", + string->data, c); + string->size = 0; + context->current_attribute_number = INVALID; + if (c != '>') { + context->state = S_junk_tag; + } else { + context->state = S_text; + } + break; + } else if (tag_OK && + !strcasecomp(string->data, "P")) { + /* + ** Treat a P end tag like a P start tag (Ugh, + ** what a hack! 8-). - FM + */ + if (TRACE) + fprintf(stderr, + "SGML: `</%s%c' found! Treating as '<%s%c'.\n", + string->data, c, string->data, c); + { + int i; + for (i = 0; + i < context->current_tag->number_of_attributes; + i++) { + context->present[i] = NO; + } + } + string->size = 0; + context->current_attribute_number = INVALID; + if (context->current_tag->name) + start_element(context); + if (c != '>') { + context->state = S_junk_tag; + } else { + context->state = S_text; + } + break; + } else if (tag_OK && + !strcasecomp(string->data, "FONT")) { + /* + ** Treat a FONT end tag as a FONT start tag with + ** a dummy END attribute. It's too likely to be + ** interdigited and mess up the parsing, so we've + ** declared FONT as SGML_EMPTY and will handle the + ** end tag in HTML_start_element. - FM + */ + if (TRACE) + fprintf(stderr, + "SGML: `</%s%c' found! Treating as '<%s%c'.\n", + string->data, c, string->data, c); + { + int i; + for (i = 0; + i < context->current_tag->number_of_attributes; + i++) { + context->present[i] = (i == HTML_FONT_END); + } + } + string->size = 0; + context->current_attribute_number = INVALID; + if (context->current_tag->name) + start_element(context); + if (c != '>') { + context->state = S_junk_tag; + } else { + context->state = S_text; + } + break; + } else { + /* + ** Handle all other end tags normally. - FM + */ + end_element( context, context->current_tag); + } + } + + string->size = 0; + context->current_attribute_number = INVALID; + if (c != '>') { + if (TRACE && !WHITE(c)) + fprintf(stderr,"SGML: `</%s%c' found!\n", string->data, c); + context->state = S_junk_tag; + } else { + context->state = S_text; + } + } + break; + + + case S_esc: /* Expecting '$'or '(' following CJK ESC. */ + if (c == '$') { + context->state = S_dollar; + } else if (c == '(') { + context->state = S_paren; + } else { + context->state = S_text; + } + PUTC(c); + break; + + case S_dollar: /* Expecting '@', 'B', 'A' or '(' after CJK "ESC$". */ + if (c == '@' || c == 'B' || c == 'A') { + context->state = S_nonascii_text; + } else if (c == '(') { + context->state = S_dollar_paren; + } + PUTC(c); + break; + + case S_dollar_paren: /* Expecting 'C' after CJK "ESC$(". */ + if (c == 'C') { + context->state = S_nonascii_text; + } else { + context->state = S_text; + } + PUTC(c); + break; + + case S_paren: /* Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(". */ + if (c == 'B' || c == 'J' || c == 'T') { + context->state = S_text; + } else if (c == 'I') { + context->state = S_nonascii_text; + } else { + context->state = S_text; + } + PUTC(c); + break; + + case S_nonascii_text: /* Expecting CJK ESC after non-ASCII text. */ + if (c == '\033') { + context->state = S_esc; + } + PUTC(c); + break; + + case S_esc_sq: /* Expecting '$'or '(' following CJK ESC. */ + if (c == '$') { + context->state = S_dollar_sq; + } else if (c == '(') { + context->state = S_paren_sq; + } else { + context->state = S_squoted; + } + HTChunkPutc(string, c); + break; + + case S_dollar_sq: /* Expecting '@', 'B', 'A' or '(' after CJK "ESC$". */ + if (c == '@' || c == 'B' || c == 'A') { + context->state = S_nonascii_text_sq; + } else if (c == '(') { + context->state = S_dollar_paren_sq; + } + HTChunkPutc(string, c); + break; + + case S_dollar_paren_sq: /* Expecting 'C' after CJK "ESC$(". */ + if (c == 'C') { + context->state = S_nonascii_text_sq; + } else { + context->state = S_squoted; + } + HTChunkPutc(string, c); + break; + + case S_paren_sq: /* Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(". */ + if (c == 'B' || c == 'J' || c == 'T') { + context->state = S_squoted; + } else if (c == 'I') { + context->state = S_nonascii_text_sq; + } else { + context->state = S_squoted; + } + HTChunkPutc(string, c); + break; + + case S_nonascii_text_sq: /* Expecting CJK ESC after non-ASCII text. */ + if (c == '\033') { + context->state = S_esc_sq; + } + HTChunkPutc(string, c); + break; + + case S_esc_dq: /* Expecting '$'or '(' following CJK ESC. */ + if (c == '$') { + context->state = S_dollar_dq; + } else if (c == '(') { + context->state = S_paren_dq; + } else { + context->state = S_dquoted; + } + HTChunkPutc(string, c); + break; + + case S_dollar_dq: /* Expecting '@', 'B', 'A' or '(' after CJK "ESC$". */ + if (c == '@' || c == 'B' || c == 'A') { + context->state = S_nonascii_text_dq; + } else if (c == '(') { + context->state = S_dollar_paren_dq; + } + HTChunkPutc(string, c); + break; + + case S_dollar_paren_dq: /* Expecting 'C' after CJK "ESC$(". */ + if (c == 'C') { + context->state = S_nonascii_text_dq; + } else { + context->state = S_dquoted; + } + HTChunkPutc(string, c); + break; + + case S_paren_dq: /* Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(". */ + if (c == 'B' || c == 'J' || c == 'T') { + context->state = S_dquoted; + } else if (c == 'I') { + context->state = S_nonascii_text_dq; + } else { + context->state = S_dquoted; + } + HTChunkPutc(string, c); + break; + + case S_nonascii_text_dq: /* Expecting CJK ESC after non-ASCII text. */ + if (c == '\033') { + context->state = S_esc_dq; + } + HTChunkPutc(string, c); + break; + + case S_junk_tag: + if (c == '>') { + context->state = S_text; + } + } /* switch on context->state */ + + /* + ** Check whether we've added anything to the recover buffer. - FM + */ + if (context->recover != NULL) { + if (context->recover[context->recover_index] == '\0') { + FREE(context->recover); + context->recover_index = 0; + } else { + c = context->recover[context->recover_index]; + context->recover_index++; + goto top; + } + } + + /* + ** Check whether an external function has added + ** anything to the include buffer. - FM + */ + if (context->include != NULL) { + if (context->include[context->include_index] == '\0') { + FREE(context->include); + context->include_index = 0; + } else { + c = context->include[context->include_index]; + context->include_index++; + goto top; + } + } + + /* + ** Check whether an external function has added + ** anything to the csi buffer. - FM + */ + if (context->csi != NULL) { + if (context->csi[context->csi_index] == '\0') { + FREE(context->csi); + context->csi_index = 0; + } else { + c = context->csi[context->csi_index]; + context->csi_index++; + goto top; + } + } +} /* SGML_character */ + + +PUBLIC void SGML_string ARGS2( + HTStream *, context, + CONST char*, str) +{ + CONST char *p; + for (p = str; *p; p++) + SGML_character(context, *p); +} + + +PUBLIC void SGML_write ARGS3( + HTStream *, context, + CONST char*, str, + int, l) +{ + CONST char *p; + CONST char *e = str+l; + for (p = str; p < e; p++) + SGML_character(context, *p); +} + +/*_______________________________________________________________________ +*/ + +/* Structured Object Class +** ----------------------- +*/ +PUBLIC CONST HTStreamClass SGMLParser = +{ + "SGMLParser", + SGML_free, + SGML_abort, + SGML_character, + SGML_string, + SGML_write, +}; + +/* Create SGML Engine +** ------------------ +** +** On entry, +** dtd represents the DTD, along with +** actions is the sink for the data as a set of routines. +** +*/ + +PUBLIC HTStream* SGML_new ARGS2( + CONST SGML_dtd *, dtd, + HTStructured *, target) +{ + int i; + HTStream* context = (HTStream *) malloc(sizeof(*context)); + if (!context) + outofmem(__FILE__, "SGML_begin"); + + context->isa = &SGMLParser; + context->string = HTChunkCreate(128); /* Grow by this much */ + context->dtd = dtd; + context->target = target; + context->actions = (HTStructuredClass*)(((HTStream*)target)->isa); + /* Ugh: no OO */ + context->state = S_text; + context->element_stack = 0; /* empty */ +#ifdef CALLERDATA + context->callerData = (void*) callerData; +#endif /* CALLERDATA */ + for (i = 0; i < MAX_ATTRIBUTES; i++) + context->value[i] = 0; + + context->lead_exclamation = FALSE; + context->first_dash = FALSE; + context->end_comment = FALSE; + context->doctype_bracket = FALSE; + context->first_bracket = FALSE; + context->second_bracket = FALSE; + context->recover = NULL; + + context->recover_index = 0; + context->include = NULL; + context->include_index = 0; + context->url = NULL; + context->csi = NULL; + context->csi_index = 0; + + return context; +} + +/* Asian character conversion functions +** ==================================== +** +** Added 24-Mar-96 by FM, based on: +** +//////////////////////////////////////////////////////////////////////// +Copyright (c) 1993 Electrotechnical Laboratry (ETL) + +Permission to use, copy, modify, and distribute this material +for any purpose and without fee is hereby granted, provided +that the above copyright notice and this permission notice +appear in all copies, and that the name of ETL not be +used in advertising or publicity pertaining to this +material without the specific, prior written permission +of an authorized representative of ETL. +ETL MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY +OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS", +WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. +///////////////////////////////////////////////////////////////////////// +Content-Type: program/C; charset=US-ASCII +Program: SJIS.c +Author: Yutaka Sato <ysato@etl.go.jp> +Description: +History: + 930923 extracted from codeconv.c of cosmos +/////////////////////////////////////////////////////////////////////// +*/ + +PUBLIC int TREAT_SJIS = 1; + +PUBLIC void JISx0201TO0208_EUC ARGS4( + register unsigned char, IHI, + register unsigned char, ILO, + register unsigned char *, OHI, + register unsigned char *, OLO) +{ + static char *table[] = { + "\xA1\xA3", "\xA1\xD6", "\xA1\xD7", "\xA1\xA2", "\xA1\xA6", "\xA5\xF2", + "\xA5\xA1", "\xA5\xA3", "\xA5\xA5", "\xA5\xA7", "\xA5\xA9", + "\xA5\xE3", "\xA5\xE5", "\xA5\xE7", "\xA5\xC3", "\xA1\xBC", + "\xA5\xA2", "\xA5\xA4", "\xA5\xA6", "\xA5\xA8", "\xA5\xAA", + "\xA5\xAB", "\xA5\xAD", "\xA5\xAF", "\xA5\xB1", "\xA5\xB3", + "\xA5\xB5", "\xA5\xB7", "\xA5\xB9", "\xA5\xBB", "\xA5\xBD", + "\xA5\xBF", "\xA5\xC1", "\xA5\xC4", "\xA5\xC6", "\xA5\xC8", + "\xA5\xCA", "\xA5\xCB", "\xA5\xCC", "\xA5\xCD", "\xA5\xCE", + "\xA5\xCF", "\xA5\xD2", "\xA5\xD5", "\xA5\xD8", "\xA5\xDB", + "\xA5\xDE", "\xA5\xDF", "\xA5\xE0", "\xA5\xE1", "\xA5\xE2", + "\xA5\xE4", "\xA5\xE6", "\xA5\xE8", "\xA5\xE9", "\xA5\xEA", + "\xA5\xEB", "\xA5\xEC", "\xA5\xED", "\xA5\xEF", "\xA5\xF3", + "\xA1\xAB", "\xA1\xAC" + }; + + if ((IHI == 0x8E) && (ILO >= 0xA1) && (ILO <= 0xDF)) { + *OHI = table[ILO - 0xA1][0]; + *OLO = table[ILO - 0xA1][1]; + } else { + *OHI = IHI; + *OLO = ILO; + } +} + +PUBLIC unsigned char * SJIS_TO_JIS1 ARGS3( + register unsigned char, HI, + register unsigned char, LO, + register unsigned char *, JCODE) +{ + HI -= (HI <= 0x9F) ? 0x71 : 0xB1; + HI = (HI << 1) + 1; + if (0x7F < LO) + LO--; + if (0x9E <= LO) { + LO -= 0x7D; + HI++; + } else { + LO -= 0x1F; + } + JCODE[0] = HI; + JCODE[1] = LO; + return JCODE; +} + +PUBLIC unsigned char * JIS_TO_SJIS1 ARGS3( + register unsigned char, HI, + register unsigned char, LO, + register unsigned char *, SJCODE) +{ + if (HI & 1) + LO += 0x1F; + else + LO += 0x7D; + if (0x7F <= LO) + LO++; + + HI = ((HI - 0x21) >> 1) + 0x81; + if (0x9F < HI) + HI += 0x40; + SJCODE[0] = HI; + SJCODE[1] = LO; + return SJCODE; +} + +PUBLIC unsigned char * EUC_TO_SJIS1 ARGS3( + unsigned char, HI, + unsigned char, LO, + register unsigned char *, SJCODE) +{ + if (HI == 0x8E) JISx0201TO0208_EUC(HI, LO, &HI, &LO); + JIS_TO_SJIS1(HI&0x7F, LO&0x7F, SJCODE); + return SJCODE; +} + +PUBLIC void JISx0201TO0208_SJIS ARGS3( + register unsigned char, I, + register unsigned char *, OHI, + register unsigned char *, OLO) +{ + unsigned char SJCODE[2]; + + JISx0201TO0208_EUC('\x8E', I, OHI, OLO); + JIS_TO_SJIS1(*OHI&0x7F, *OLO&0x7F, SJCODE); + *OHI = SJCODE[0]; + *OLO = SJCODE[1]; +} + +PUBLIC unsigned char * SJIS_TO_EUC1 ARGS3( + unsigned char, HI, + unsigned char, LO, + unsigned char *, EUC) +{ + SJIS_TO_JIS1(HI, LO, EUC); + EUC[0] |= 0x80; + EUC[1] |= 0x80; + return EUC; +} + +PUBLIC unsigned char * SJIS_TO_EUC ARGS2( + unsigned char *, src, + unsigned char *, dst) +{ + register unsigned char hi, lo, *sp, *dp; + register int in_sjis = 0; + + for (sp = src, dp = dst; (0 != (hi = sp[0]));) { + lo = sp[1]; + if (TREAT_SJIS && IS_SJIS(hi, lo, in_sjis)) { + SJIS_TO_JIS1(hi,lo,dp); + dp[0] |= 0x80; + dp[1] |= 0x80; + dp += 2; + sp += 2; + } else { + *dp++ = *sp++; + } + } + *dp = 0; + return dst; +} + +PUBLIC unsigned char * EUC_TO_SJIS ARGS2( + unsigned char *, src, + unsigned char *, dst) +{ + register unsigned char *sp, *dp; + + for (sp = src, dp = dst; *sp;) { + if (*sp & 0x80) { + if (sp[1] && (sp[1] & 0x80)) { + JIS_TO_SJIS1(sp[0]&0x7F, sp[1]&0x7F, dp); + dp += 2; + sp += 2; + } else { + sp++; + } + } else { + *dp++ = *sp++; + } + } + *dp = 0; + return dst; +} + +PUBLIC unsigned char * EUC_TO_JIS ARGS4( + unsigned char *, src, + unsigned char *, dst, + CONST char *, toK, + CONST char *, toA) +{ + register unsigned char kana_mode = 0; + register unsigned char cch; + register unsigned char *sp = src; + register unsigned char *dp = dst; + register int i; + + while (0 != (cch = *sp++)) { + if (cch & 0x80) { + if (!kana_mode) { + kana_mode = ~kana_mode; + for (i = 0; toK[i]; i++) { + *dp++ = (unsigned char)toK[i]; + } + } + if (*sp & 0x80) { + *dp++ = cch & ~0x80; + *dp++ = *sp++ & ~0x80; + } + } else { + if (kana_mode) { + kana_mode = ~kana_mode; + for (i = 0; toA[i]; i++) { + *dp++ = (unsigned char)toA[i]; + *dp = '\0'; + } + } + *dp++ = cch; + } + } + if (kana_mode) { + for (i = 0; toA[i]; i++) { + *dp++ = (unsigned char)toA[i]; + } + } + + if (dp) + *dp = 0; + return dst; +} + +PUBLIC unsigned char * TO_EUC ARGS2( + unsigned char *, jis, + unsigned char *, euc) +{ + register unsigned char *s, *d, c, jis_stat; + register to1B, to2B; + register int in_sjis = 0; + + s = jis; + d = euc; + jis_stat = 0; + to2B = TO_2BCODE; + to1B = TO_1BCODE; + + while (0 != (c = *s++)) { + if (c == ESC) { + if (*s == to2B) { + if ((s[1] == 'B') || (s[1] == '@') || (s[1] == 'A')) { + jis_stat = 0x80; + s += 2; + continue; + } else if ((s[1] == '(') && s[2] && (s[2] == 'C')) { + jis_stat = 0x80; + s += 3; + continue; + } + } else { + if (*s == to1B) { + if ((s[1]=='B') || (s[1]=='J') || + (s[1]=='H') || (s[1]=='T')) { + jis_stat = 0; + s += 2; + continue; + } + } + } + } + if (IS_SJIS(c,*s,in_sjis)) { + SJIS_TO_EUC1(c, *s, d); + d += 2; + s++; + } else { + if (jis_stat && (0x20 < c)) { + *d++ = jis_stat | c; + } else { + *d++ = c; + } + } + } + *d = 0; + return euc; +} + +PUBLIC void TO_SJIS ARGS2( + unsigned char *, any, + unsigned char *, sjis) +{ + unsigned char *euc; + + if (!any || !sjis) + return; + + euc = (unsigned char*)malloc(strlen((CONST char *)any)+1); + if (euc == NULL) + outofmem(__FILE__, "TO_SJIS"); + + TO_EUC(any, euc); + EUC_TO_SJIS(euc, sjis); + FREE(euc); +} + +PUBLIC void TO_JIS ARGS2( + unsigned char *, any, + unsigned char *, jis) +{ + unsigned char *euc; + + if (!any || !jis) + return; + + euc = (unsigned char*)malloc(strlen((CONST char *)any)+1); + if (euc == NULL) + outofmem(__FILE__, "TO_JIS"); + + TO_EUC(any, euc); + EUC_TO_JIS(euc, jis, TO_KANJI, TO_ASCII); + FREE(euc); +} diff --git a/WWW/Library/Implementation/SGML.h b/WWW/Library/Implementation/SGML.h new file mode 100644 index 00000000..5fa799d5 --- /dev/null +++ b/WWW/Library/Implementation/SGML.h @@ -0,0 +1,202 @@ +/* SGML parse and stream definition for libwww + SGML AND STRUCTURED STREAMS + + The SGML parser is a state machine. It is called for every character + + of the input stream. The DTD data structure contains pointers + + to functions which are called to implement the actual effect of the + + text read. When these functions are called, the attribute structures pointed to by the + DTD are valid, and the function is passed a pointer to the curent tag structure, and an + "element stack" which represents the state of nesting within SGML elements. + + The following aspects are from Dan Connolly's suggestions: Binary search, Strcutured + object scheme basically, SGML content enum type. + + (c) Copyright CERN 1991 - See Copyright.html + + */ +#ifndef SGML_H +#define SGML_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* HTUTILS_H */ +#include "HTStream.h" + +/* + +SGML content types + + */ +typedef enum _SGMLContent{ + SGML_EMPTY, /* no content */ + SGML_LITTERAL, /* character data. Recognised excat close tag only. litteral + Old www server compatibility only! Not SGML */ + SGML_CDATA, /* character data. recognize </ only */ + SGML_RCDATA, /* replaceable character data. recognize </ and &ref; */ + SGML_MIXED, /* elements and parsed character data. recognize all markup */ + SGML_ELEMENT /* any data found will be returned as an error*/ + } SGMLContent; + + +typedef struct { + char * name; /* The (constant) name of the attribute */ + /* Could put type info in here */ +} attr; + + +/* A tag structure describes an SGML element. +** ----------------------------------------- +** +** +** name is the string which comes after the tag opener "<". +** +** attributes points to a zero-terminated array +** of attribute names. +** +** litteral determines how the SGML engine parses the charaters +** within the element. If set, tag openers are ignored +** except for that which opens a matching closing tag. +** +*/ +typedef struct _tag HTTag; +struct _tag{ + char * name; /* The name of the tag */ + attr * attributes; /* The list of acceptable attributes */ + int number_of_attributes; /* Number of possible attributes */ + SGMLContent contents; /* End only on end tag @@ */ +}; + + + + +/* DTD Information +** --------------- +** +** Not the whole DTD, but all this parser usues of it. +*/ +typedef struct { + HTTag * tags; /* Must be in strcmp order by name */ + int number_of_tags; + CONST char ** entity_names; /* Must be in strcmp order by name */ + int number_of_entities; +} SGML_dtd; + + +/* SGML context passed to parsers +*/ +typedef struct _HTSGMLContext *HTSGMLContext; /* Hidden */ + + +/*__________________________________________________________________________ +*/ + +/* + +Structured Object definition + + A structured object is something which can reasonably be represented in SGML. I'll + rephrase that. A structured object is am ordered tree-structured arrangement of data + which is representable as text.The SGML parer outputs to a Structured object. A + Structured object can output its contents to another Structured Object. It's a kind of + typed stream. The architecure is largely Dan Conolly's. Elements and entities are + passed to the sob by number, implying a knowledge of the DTD. Knowledge of the SGML + syntax is not here, though. + + Superclass: HTStream + + The creation methods will vary on the type of Structured Object.Maybe the callerData is + enough info to pass along. + + */ +typedef struct _HTStructured HTStructured; + +typedef struct _HTStructuredClass{ + + char* name; /* Just for diagnostics */ + + void (*_free) PARAMS(( + HTStructured* me)); + + void (*_abort) PARAMS(( + HTStructured* me, + HTError e)); + + void (*put_character) PARAMS(( + HTStructured* me, + char ch)); + + void (*put_string) PARAMS(( + HTStructured* me, + CONST char * str)); + + void (*_write) PARAMS(( + HTStructured* me, + CONST char * str, + int len)); + + void (*start_element) PARAMS(( + HTStructured* me, + int element_number, + CONST BOOL* attribute_present, + CONST char** attribute_value, + char ** include)); + + void (*end_element) PARAMS(( + HTStructured* me, + int element_number, + char ** include)); + + void (*put_entity) PARAMS(( + HTStructured* me, + int entity_number)); + +}HTStructuredClass; + +/* + +Find a Tag by Name + + Returns a pointer to the tag within the DTD. + + */ +extern HTTag * SGMLFindTag PARAMS((CONST SGML_dtd* dtd, CONST char * string)); + + +/* + +Create an SGML parser + + */ +/* +** On entry, +** dtd must point to a DTD structure as defined above +** callbacks must point to user routines. +** callData is returned in callbacks transparently. +** On exit, +** The default tag starter has been processed. +*/ + + +extern HTStream* SGML_new PARAMS(( + CONST SGML_dtd * dtd, + HTStructured * target)); + +extern CONST HTStreamClass SGMLParser; + + +#endif /* SGML_H */ + + + + + + + + + +/* + + */ diff --git a/WWW/Library/Implementation/Version.make b/WWW/Library/Implementation/Version.make new file mode 100644 index 00000000..4b4b380f --- /dev/null +++ b/WWW/Library/Implementation/Version.make @@ -0,0 +1 @@ +VC = 2.14 diff --git a/WWW/Library/Implementation/crypt.c b/WWW/Library/Implementation/crypt.c new file mode 100644 index 00000000..ffc466c7 --- /dev/null +++ b/WWW/Library/Implementation/crypt.c @@ -0,0 +1,129 @@ +/* + * UFC-crypt: ultra fast crypt(3) implementation + * + * Copyright (C) 1991, 1992, Free Software Foundation, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * @(#)crypt.c 2.19 5/28/92 + * + * Semiportable C version + * + */ + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif + +#include "ufc-crypt.h" + +#include "LYLeaks.h" + +#ifdef _UFC_32_ + +/* + * 32 bit version + */ + +extern long32 _ufc_keytab[16][2]; +extern long32 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[]; + +#define SBA(sb, v) (*(long32*)((char*)(sb)+(v))) + +static ufc_long ary[4]; + +ufc_long *_ufc_doit(l1, l2, r1, r2, itr) + ufc_long l1, l2, r1, r2, itr; + { int i; + long32 s, *k; + register long32 *sb0 = _ufc_sb0; + register long32 *sb1 = _ufc_sb1; + register long32 *sb2 = _ufc_sb2; + register long32 *sb3 = _ufc_sb3; + + while(itr--) { + k = &_ufc_keytab[0][0]; + for(i=8; i--; ) { + s = *k++ ^ r1; + l1 ^= SBA(sb1, s & 0xffff); l2 ^= SBA(sb1, (s & 0xffff)+4); + l1 ^= SBA(sb0, s >>= 16); l2 ^= SBA(sb0, (s) +4); + s = *k++ ^ r2; + l1 ^= SBA(sb3, s & 0xffff); l2 ^= SBA(sb3, (s & 0xffff)+4); + l1 ^= SBA(sb2, s >>= 16); l2 ^= SBA(sb2, (s) +4); + + s = *k++ ^ l1; + r1 ^= SBA(sb1, s & 0xffff); r2 ^= SBA(sb1, (s & 0xffff)+4); + r1 ^= SBA(sb0, s >>= 16); r2 ^= SBA(sb0, (s) +4); + s = *k++ ^ l2; + r1 ^= SBA(sb3, s & 0xffff); r2 ^= SBA(sb3, (s & 0xffff)+4); + r1 ^= SBA(sb2, s >>= 16); r2 ^= SBA(sb2, (s) +4); + } + s=l1; l1=r1; r1=s; s=l2; l2=r2; r2=s; + } + ary[0] = l1; ary[1] = l2; ary[2] = r1; ary[3] = r2; + return ary; + } + +#endif + +#ifdef _UFC_64_ + +/* + * 64 bit version + */ + +extern long64 _ufc_keytab[16]; +extern long64 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[]; + +#define SBA(sb, v) (*(long64*)((char*)(sb)+(v))) + +static ufc_long ary[4]; + +ufc_long *_ufc_doit(l1, l2, r1, r2, itr) + ufc_long l1, l2, r1, r2, itr; + { int i; + long64 l, r, s, *k; + register long64 *sb0 = _ufc_sb0; + register long64 *sb1 = _ufc_sb1; + register long64 *sb2 = _ufc_sb2; + register long64 *sb3 = _ufc_sb3; + + l = (((long64)l1) << 32) | ((long64)l2); + r = (((long64)r1) << 32) | ((long64)r2); + + while(itr--) { + k = &_ufc_keytab[0]; + for(i=8; i--; ) { + s = *k++ ^ r; + l ^= SBA(sb3, (s >> 0) & 0xffff); + l ^= SBA(sb2, (s >> 16) & 0xffff); + l ^= SBA(sb1, (s >> 32) & 0xffff); + l ^= SBA(sb0, (s >> 48) & 0xffff); + + s = *k++ ^ l; + r ^= SBA(sb3, (s >> 0) & 0xffff); + r ^= SBA(sb2, (s >> 16) & 0xffff); + r ^= SBA(sb1, (s >> 32) & 0xffff); + r ^= SBA(sb0, (s >> 48) & 0xffff); + } + s=l; l=r; r=s; + } + + ary[0] = l >> 32; ary[1] = l & 0xffffffff; + ary[2] = r >> 32; ary[3] = r & 0xffffffff; + return ary; + } + +#endif diff --git a/WWW/Library/Implementation/crypt_util.c b/WWW/Library/Implementation/crypt_util.c new file mode 100644 index 00000000..9ed7e95d --- /dev/null +++ b/WWW/Library/Implementation/crypt_util.c @@ -0,0 +1,981 @@ +/* + * UFC-crypt: ultra fast crypt(3) implementation + * + * Copyright (C) 1991, 1992, Free Software Foundation, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * @(#)crypt_util.c 2.40 09/21/92 + * + * Support routines + * + */ + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif + +#ifdef DEBUG +/*#include <stdio.h> included by HTUTils.h - FM */ +#endif + +#ifndef STATIC +#define STATIC static +#endif + +#ifndef DOS +#include "patchlevel.h" +#include "ufc-crypt.h" +#else +/* + * Thanks to greg%wind@plains.NoDak.edu (Greg W. Wettstein) + * for DOS patches + */ +#include "pl.h" +#include "ufc.h" +#endif + +#include "LYLeaks.h" + +static char patchlevel_str[] = PATCHLEVEL; + +/* + * Permutation done once on the 56 bit + * key derived from the original 8 byte ASCII key. + */ +static int pc1[56] = { + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 +}; + +/* + * How much to rotate each 28 bit half of the pc1 permutated + * 56 bit key before using pc2 to give the i' key + */ +static int rots[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +/* + * Permutation giving the key + * of the i' DES round + */ +static int pc2[48] = { + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 +}; + +/* + * The E expansion table which selects + * bits from the 32 bit intermediate result. + */ +static int esel[48] = { + 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1 +}; +static int e_inverse[64]; + +/* + * Permutation done on the + * result of sbox lookups + */ +static int perm32[32] = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +/* + * The sboxes + */ +static int sbox[8][4][16]= { + { { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 }, + { 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8 }, + { 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0 }, + { 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 } + }, + + { { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10 }, + { 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5 }, + { 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15 }, + { 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 } + }, + + { { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8 }, + { 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1 }, + { 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7 }, + { 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 } + }, + + { { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 }, + { 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9 }, + { 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4 }, + { 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 } + }, + + { { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 }, + { 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6 }, + { 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14 }, + { 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 } + }, + + { { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11 }, + { 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8 }, + { 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6 }, + { 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 } + }, + + { { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 }, + { 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6 }, + { 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2 }, + { 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 } + }, + + { { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 }, + { 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2 }, + { 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8 }, + { 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 } + } +}; + +/* + * This is the initial + * permutation matrix + */ +static int initial_perm[64] = { + 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 +}; + +/* + * This is the final + * permutation matrix + */ +static int final_perm[64] = { + 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25 +}; + +/* + * The 16 DES keys in BITMASK format + */ +#ifdef _UFC_32_ +long32 _ufc_keytab[16][2]; +#endif +#ifdef _UFC_64_ +long64 _ufc_keytab[16]; +#endif + +#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.') +#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') + +/* Macro to set a bit (0..23) */ +#define BITMASK(i) ( (1L<<(11L-(i)%12L+3L)) << ((i)<12L?16L:0L) ) + +/* + * sb arrays: + * + * Workhorses of the inner loop of the DES implementation. + * They do sbox lookup, shifting of this value, 32 bit + * permutation and E permutation for the next round. + * + * Kept in 'BITMASK' format. + */ + +#ifdef _UFC_32_ +long32 _ufc_sb0[8192], _ufc_sb1[8192], _ufc_sb2[8192], _ufc_sb3[8192]; +static long32 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3}; +#endif + +#ifdef _UFC_64_ +long64 _ufc_sb0[4096], _ufc_sb1[4096], _ufc_sb2[4096], _ufc_sb3[4096]; +static long64 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3}; +#endif + +/* + * eperm32tab: do 32 bit permutation and E selection + * + * The first index is the byte number in the 32 bit value to be permuted + * - second - is the value of this byte + * - third - selects the two 32 bit values + * + * The table is used and generated internally in init_des to speed it up + */ +static ufc_long eperm32tab[4][256][2]; + +/* + * do_pc1: permform pc1 permutation in the key schedule generation. + * + * The first index is the byte number in the 8 byte ASCII key + * - second - - the two 28 bits halfs of the result + * - third - selects the 7 bits actually used of each byte + * + * The result is kept with 28 bit per 32 bit with the 4 most significant + * bits zero. + */ +static ufc_long do_pc1[8][2][128]; + +/* + * do_pc2: permform pc2 permutation in the key schedule generation. + * + * The first index is the septet number in the two 28 bit intermediate values + * - second - - - septet values + * + * Knowledge of the structure of the pc2 permutation is used. + * + * The result is kept with 28 bit per 32 bit with the 4 most significant + * bits zero. + */ +static ufc_long do_pc2[8][128]; + +/* + * efp: undo an extra e selection and do final + * permutation giving the DES result. + * + * Invoked 6 bit a time on two 48 bit values + * giving two 32 bit longs. + */ +static ufc_long efp[16][64][2]; + +/* + * revfinal: undo final permutation and do E expension. + * + * Invoked 6 bit a time on DES output + * giving 4 32 bit longs. + */ +static ufc_long revfinal[11][64][4]; + + +static unsigned char bytemask[8] = { + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 +}; + +static ufc_long longmask[32] = { + 0x80000000, 0x40000000, 0x20000000, 0x10000000, + 0x08000000, 0x04000000, 0x02000000, 0x01000000, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, + 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, + 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, + 0x00000008, 0x00000004, 0x00000002, 0x00000001 +}; + +#ifdef DEBUG + +pr_bits(a, n) + ufc_long *a; + int n; + { ufc_long i, j, t, tmp; + n /= 8; + for(i = 0; i < n; i++) { + tmp=0; + for(j = 0; j < 8; j++) { + t=8*i+j; + tmp|=(a[t/24] & BITMASK(t % 24))?bytemask[j]:0; + } + (void)printf("%02x ",tmp); + } + printf(" "); + } + +static set_bits(v, b) + ufc_long v; + ufc_long *b; + { ufc_long i; + *b = 0; + for(i = 0; i < 24; i++) { + if(v & longmask[8 + i]) + *b |= BITMASK(i); + } + } + +#endif + +/* + * Silly rewrite of 'bzero'. I do so + * because some machines don't have + * bzero and some don't have memset. + */ + +STATIC void clearmem(start, cnt) + char *start; + int cnt; + { while(cnt--) + *start++ = '\0'; + } + +static int initialized = 0; + +/* lookup a 6 bit value in sbox */ + +#define s_lookup(i,s) sbox[(i)][(((s)>>4) & 0x2)|((s) & 0x1)][((s)>>1) & 0xf]; + +/* + * Initialize unit - may be invoked directly + * by fcrypt users. + */ + +void init_des() + { int comes_from_bit; + int bit, sg; + ufc_long j; + ufc_long mask1, mask2; + + /* + * Create the do_pc1 table used + * to affect pc1 permutation + * when generating keys + */ + for(bit = 0; bit < 56; bit++) { + comes_from_bit = pc1[bit] - 1; + mask1 = bytemask[comes_from_bit % 8 + 1]; + mask2 = longmask[bit % 28 + 4]; + for(j = 0; j < 128; j++) { + if(j & mask1) + do_pc1[comes_from_bit / 8][bit / 28][j] |= mask2; + } + } + + /* + * Create the do_pc2 table used + * to affect pc2 permutation when + * generating keys + */ + for(bit = 0; bit < 48; bit++) { + comes_from_bit = pc2[bit] - 1; + mask1 = bytemask[comes_from_bit % 7 + 1]; + mask2 = BITMASK(bit % 24); + for(j = 0; j < 128; j++) { + if(j & mask1) + do_pc2[comes_from_bit / 7][j] |= mask2; + } + } + + /* + * Now generate the table used to do combined + * 32 bit permutation and e expansion + * + * We use it because we have to permute 16384 32 bit + * longs into 48 bit in order to initialize sb. + * + * Looping 48 rounds per permutation becomes + * just too slow... + * + */ + + clearmem((char*)eperm32tab, sizeof(eperm32tab)); + + for(bit = 0; bit < 48; bit++) { + ufc_long mask1,comes_from; + + comes_from = perm32[esel[bit]-1]-1; + mask1 = bytemask[comes_from % 8]; + + for(j = 256; j--;) { + if(j & mask1) + eperm32tab[comes_from / 8][j][bit / 24] |= BITMASK(bit % 24); + } + } + + /* + * Create the sb tables: + * + * For each 12 bit segment of an 48 bit intermediate + * result, the sb table precomputes the two 4 bit + * values of the sbox lookups done with the two 6 + * bit halves, shifts them to their proper place, + * sends them through perm32 and finally E expands + * them so that they are ready for the next + * DES round. + * + */ + for(sg = 0; sg < 4; sg++) { + int j1, j2; + int s1, s2; + + for(j1 = 0; j1 < 64; j1++) { + s1 = s_lookup(2 * sg, j1); + for(j2 = 0; j2 < 64; j2++) { + ufc_long to_permute, inx; + + s2 = s_lookup(2 * sg + 1, j2); + to_permute = (((ufc_long)s1 << 4) | + (ufc_long)s2) << (24 - 8 * (ufc_long)sg); + +#ifdef _UFC_32_ + inx = ((j1 << 6) | j2) << 1; + sb[sg][inx ] = eperm32tab[0][(to_permute >> 24) & 0xff][0]; + sb[sg][inx+1] = eperm32tab[0][(to_permute >> 24) & 0xff][1]; + sb[sg][inx ] |= eperm32tab[1][(to_permute >> 16) & 0xff][0]; + sb[sg][inx+1] |= eperm32tab[1][(to_permute >> 16) & 0xff][1]; + sb[sg][inx ] |= eperm32tab[2][(to_permute >> 8) & 0xff][0]; + sb[sg][inx+1] |= eperm32tab[2][(to_permute >> 8) & 0xff][1]; + sb[sg][inx ] |= eperm32tab[3][(to_permute) & 0xff][0]; + sb[sg][inx+1] |= eperm32tab[3][(to_permute) & 0xff][1]; +#endif +#ifdef _UFC_64_ + inx = ((j1 << 6) | j2); + sb[sg][inx] = + ((long64)eperm32tab[0][(to_permute >> 24) & 0xff][0] << 32) | + (long64)eperm32tab[0][(to_permute >> 24) & 0xff][1]; + sb[sg][inx] |= + ((long64)eperm32tab[1][(to_permute >> 16) & 0xff][0] << 32) | + (long64)eperm32tab[1][(to_permute >> 16) & 0xff][1]; + sb[sg][inx] |= + ((long64)eperm32tab[2][(to_permute >> 8) & 0xff][0] << 32) | + (long64)eperm32tab[2][(to_permute >> 8) & 0xff][1]; + sb[sg][inx] |= + ((long64)eperm32tab[3][(to_permute) & 0xff][0] << 32) | + (long64)eperm32tab[3][(to_permute) & 0xff][1]; +#endif + } + } + } + + /* + * Create an inverse matrix for esel telling + * where to plug out bits if undoing it + */ + for(bit=48; bit--;) { + e_inverse[esel[bit] - 1 ] = bit; + e_inverse[esel[bit] - 1 + 32] = bit + 48; + } + + /* + * create efp: the matrix used to + * undo the E expansion and effect final permutation + */ + clearmem((char*)efp, sizeof efp); + for(bit = 0; bit < 64; bit++) { + int o_bit, o_long; + ufc_long word_value, mask1, mask2; + int comes_from_f_bit, comes_from_e_bit; + int comes_from_word, bit_within_word; + + /* See where bit i belongs in the two 32 bit long's */ + o_long = bit / 32; /* 0..1 */ + o_bit = bit % 32; /* 0..31 */ + + /* + * And find a bit in the e permutated value setting this bit. + * + * Note: the e selection may have selected the same bit several + * times. By the initialization of e_inverse, we only look + * for one specific instance. + */ + comes_from_f_bit = final_perm[bit] - 1; /* 0..63 */ + comes_from_e_bit = e_inverse[comes_from_f_bit]; /* 0..95 */ + comes_from_word = comes_from_e_bit / 6; /* 0..15 */ + bit_within_word = comes_from_e_bit % 6; /* 0..5 */ + + mask1 = longmask[bit_within_word + 26]; + mask2 = longmask[o_bit]; + + for(word_value = 64; word_value--;) { + if(word_value & mask1) + efp[comes_from_word][word_value][o_long] |= mask2; + } + } + + + /* + * Create revfinal: an array to undo final + * the effects of efp + */ + clearmem((char*)revfinal, sizeof(revfinal)); + for(bit = 0; bit < 96; bit++) { + int ibit = initial_perm[esel[bit % 48] - 1 + ((bit >= 48) ? 32 : 0)] - 1; + mask1 = bytemask[ibit % 6 + 2]; + mask2 = BITMASK(bit % 24); + for(j = 64; j--;) { + if(j & mask1) { + revfinal[ibit / 6][j][bit / 24] |= mask2; + } + } + } + + initialized++; + } + +/* + * Process the elements of the sb table permuting the + * bits swapped in the expansion by the current salt. + */ + +#ifdef _UFC_32_ +STATIC void shuffle_sb(k, saltbits) + long32 *k; + ufc_long saltbits; + { ufc_long j; + long32 x; + for(j=4096; j--;) { + x = (k[0] ^ k[1]) & (long32)saltbits; + *k++ ^= x; + *k++ ^= x; + } + } +#endif + +#ifdef _UFC_64_ +STATIC void shuffle_sb(k, saltbits) + long64 *k; + ufc_long saltbits; + { ufc_long j; + long64 x; + for(j=4096; j--;) { + x = ((*k >> 32) ^ *k) & (long64)saltbits; + *k++ ^= (x << 32) | x; + } + } +#endif + +/* + * Setup the unit for a new salt + * Hopefully we'll not see a new salt in each crypt call. + */ + +static unsigned char current_salt[3] = "&&"; /* invalid value */ +static ufc_long current_saltbits = 0; +static int direction = 0; + +STATIC void setup_salt(s) + char *s; + { ufc_long i, j, saltbits; + + if(!initialized) + init_des(); + + if(s[0] == current_salt[0] && s[1] == current_salt[1]) + return; + current_salt[0] = s[0]; current_salt[1] = s[1]; + + /* + * This is the only crypt change to DES: + * entries are swapped in the expansion table + * according to the bits set in the salt. + */ + saltbits = 0; + for(i = 0; i < 2; i++) { + long c=ascii_to_bin(s[i]); +#ifdef notdef + /* + * Some applications do rely on illegal + * salts. It seems that UFC-crypt behaves + * identically to standard crypt + * implementations on illegal salts -- glad + */ + if(c < 0 || c > 63) + c = 0; +#endif + for(j = 0; j < 6; j++) { + if((c >> j) & 0x1) + saltbits |= BITMASK(6 * i + j); + } + } + + /* + * Permute the sb table values + * to reflect the changed e + * selection table + */ + shuffle_sb(_ufc_sb0, current_saltbits ^ saltbits); + shuffle_sb(_ufc_sb1, current_saltbits ^ saltbits); + shuffle_sb(_ufc_sb2, current_saltbits ^ saltbits); + shuffle_sb(_ufc_sb3, current_saltbits ^ saltbits); + + current_saltbits = saltbits; + } + +STATIC void ufc_mk_keytab(key) + char *key; + { ufc_long v1, v2, *k1; + int i; +#ifdef _UFC_32_ + long32 v, *k2 = &_ufc_keytab[0][0]; +#endif +#ifdef _UFC_64_ + long64 v, *k2 = &_ufc_keytab[0]; +#endif + + v1 = v2 = 0; k1 = &do_pc1[0][0][0]; + for(i = 8; i--;) { + v1 |= k1[*key & 0x7f]; k1 += 128; + v2 |= k1[*key++ & 0x7f]; k1 += 128; + } + + for(i = 0; i < 16; i++) { + k1 = &do_pc2[0][0]; + + v1 = (v1 << rots[i]) | (v1 >> (28 - rots[i])); + v = k1[(v1 >> 21) & 0x7f]; k1 += 128; + v |= k1[(v1 >> 14) & 0x7f]; k1 += 128; + v |= k1[(v1 >> 7) & 0x7f]; k1 += 128; + v |= k1[(v1 ) & 0x7f]; k1 += 128; + +#ifdef _UFC_32_ + *k2++ = v; + v = 0; +#endif +#ifdef _UFC_64_ + v <<= 32; +#endif + + v2 = (v2 << rots[i]) | (v2 >> (28 - rots[i])); + v |= k1[(v2 >> 21) & 0x7f]; k1 += 128; + v |= k1[(v2 >> 14) & 0x7f]; k1 += 128; + v |= k1[(v2 >> 7) & 0x7f]; k1 += 128; + v |= k1[(v2 ) & 0x7f]; + + *k2++ = v; + } + + direction = 0; + } + +/* + * Undo an extra E selection and do final permutations + */ + +ufc_long *_ufc_dofinalperm(l1, l2, r1, r2) + ufc_long l1,l2,r1,r2; + { ufc_long v1, v2, x; + static ufc_long ary[2]; + + x = (l1 ^ l2) & current_saltbits; l1 ^= x; l2 ^= x; + x = (r1 ^ r2) & current_saltbits; r1 ^= x; r2 ^= x; + + v1=v2=0; l1 >>= 3; l2 >>= 3; r1 >>= 3; r2 >>= 3; + + v1 |= efp[15][ r2 & 0x3f][0]; v2 |= efp[15][ r2 & 0x3f][1]; + v1 |= efp[14][(r2 >>= 6) & 0x3f][0]; v2 |= efp[14][ r2 & 0x3f][1]; + v1 |= efp[13][(r2 >>= 10) & 0x3f][0]; v2 |= efp[13][ r2 & 0x3f][1]; + v1 |= efp[12][(r2 >>= 6) & 0x3f][0]; v2 |= efp[12][ r2 & 0x3f][1]; + + v1 |= efp[11][ r1 & 0x3f][0]; v2 |= efp[11][ r1 & 0x3f][1]; + v1 |= efp[10][(r1 >>= 6) & 0x3f][0]; v2 |= efp[10][ r1 & 0x3f][1]; + v1 |= efp[ 9][(r1 >>= 10) & 0x3f][0]; v2 |= efp[ 9][ r1 & 0x3f][1]; + v1 |= efp[ 8][(r1 >>= 6) & 0x3f][0]; v2 |= efp[ 8][ r1 & 0x3f][1]; + + v1 |= efp[ 7][ l2 & 0x3f][0]; v2 |= efp[ 7][ l2 & 0x3f][1]; + v1 |= efp[ 6][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 6][ l2 & 0x3f][1]; + v1 |= efp[ 5][(l2 >>= 10) & 0x3f][0]; v2 |= efp[ 5][ l2 & 0x3f][1]; + v1 |= efp[ 4][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 4][ l2 & 0x3f][1]; + + v1 |= efp[ 3][ l1 & 0x3f][0]; v2 |= efp[ 3][ l1 & 0x3f][1]; + v1 |= efp[ 2][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 2][ l1 & 0x3f][1]; + v1 |= efp[ 1][(l1 >>= 10) & 0x3f][0]; v2 |= efp[ 1][ l1 & 0x3f][1]; + v1 |= efp[ 0][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 0][ l1 & 0x3f][1]; + + ary[0] = v1; ary[1] = v2; + return ary; + } + +/* + * crypt only: convert from 64 bit to 11 bit ASCII + * prefixing with the salt + */ + +STATIC char *output_conversion(v1, v2, salt) + ufc_long v1, v2; + char *salt; + { static char outbuf[14]; + int i, s, shf; + + outbuf[0] = salt[0]; + outbuf[1] = salt[1] ? salt[1] : salt[0]; + + for(i = 0; i < 5; i++) { + shf = (26 - 6 * i); /* to cope with MSC compiler bug */ + outbuf[i + 2] = bin_to_ascii((v1 >> shf) & 0x3f); + } + + s = (v2 & 0xf) << 2; + v2 = (v2 >> 2) | ((v1 & 0x3) << 30); + + for(i = 5; i < 10; i++) { + shf = (56 - 6 * i); + outbuf[i + 2] = bin_to_ascii((v2 >> shf) & 0x3f); + } + + outbuf[12] = bin_to_ascii(s); + outbuf[13] = 0; + + return outbuf; + } + +ufc_long *_ufc_doit(); + +/* + * UNIX crypt function + */ + +char *crypt(key, salt) + char *key, *salt; + { ufc_long *s; + char ktab[9]; + + /* + * Hack DES tables according to salt + */ + setup_salt(salt); + + /* + * Setup key schedule + */ + clearmem(ktab, sizeof ktab); + (void)strncpy(ktab, key, 8); + ufc_mk_keytab(ktab); + + /* + * Go for the 25 DES encryptions + */ + s = _ufc_doit((ufc_long)0, (ufc_long)0, + (ufc_long)0, (ufc_long)0, (ufc_long)25); + /* + * Do final permutations + */ + s = _ufc_dofinalperm(s[0], s[1], s[2], s[3]); + + /* + * And convert back to 6 bit ASCII + */ + return output_conversion(s[0], s[1], salt); + } + +/* + * To make fcrypt users happy. + * They don't need to call init_des. + */ + +char *fcrypt(key, salt) + char *key; + char *salt; + { return crypt(key, salt); + } + +/* + * UNIX encrypt function. Takes a bitvector + * represented by one byte per bit and + * encrypt/decrypt according to edflag + */ + +void encrypt(block, edflag) + char *block; + int edflag; + { ufc_long l1, l2, r1, r2, *s; + int i; + + /* + * Undo any salt changes to E expansion + */ + setup_salt(".."); + + /* + * Reverse key table if + * changing operation (encrypt/decrypt) + */ + if((edflag == 0) != (direction == 0)) { + for(i = 0; i < 8; i++) { +#ifdef _UFC_32_ + long32 x; + x = _ufc_keytab[15-i][0]; + _ufc_keytab[15-i][0] = _ufc_keytab[i][0]; + _ufc_keytab[i][0] = x; + + x = _ufc_keytab[15-i][1]; + _ufc_keytab[15-i][1] = _ufc_keytab[i][1]; + _ufc_keytab[i][1] = x; +#endif +#ifdef _UFC_64_ + long64 x; + x = _ufc_keytab[15-i]; + _ufc_keytab[15-i] = _ufc_keytab[i]; + _ufc_keytab[i] = x; +#endif + } + direction = edflag; + } + + /* + * Do initial permutation + E expansion + */ + i = 0; + for(l1 = 0; i < 24; i++) { + if(block[initial_perm[esel[i]-1]-1]) + l1 |= BITMASK(i); + } + for(l2 = 0; i < 48; i++) { + if(block[initial_perm[esel[i]-1]-1]) + l2 |= BITMASK(i-24); + } + + i = 0; + for(r1 = 0; i < 24; i++) { + if(block[initial_perm[esel[i]-1+32]-1]) + r1 |= BITMASK(i); + } + for(r2 = 0; i < 48; i++) { + if(block[initial_perm[esel[i]-1+32]-1]) + r2 |= BITMASK(i-24); + } + + /* + * Do DES inner loops + final conversion + */ + s = _ufc_doit(l1, l2, r1, r2, (ufc_long)1); + /* + * Do final permutations + */ + s = _ufc_dofinalperm(s[0], s[1], s[2], s[3]); + + /* + * And convert to bit array + */ + l1 = s[0]; r1 = s[1]; + for(i = 0; i < 32; i++) { + *block++ = (l1 & longmask[i]) != 0; + } + for(i = 0; i < 32; i++) { + *block++ = (r1 & longmask[i]) != 0; + } + + } + +/* + * UNIX setkey function. Take a 64 bit DES + * key and setup the machinery. + */ + +void setkey(key) + char *key; + { int i,j; + unsigned char c; + unsigned char ktab[8]; + + setup_salt(".."); /* be sure we're initialized */ + + for(i = 0; i < 8; i++) { + for(j = 0, c = 0; j < 8; j++) + c = c << 1 | *key++; + ktab[i] = c >> 1; + } + + ufc_mk_keytab(ktab); + } + +/* + * Ultrix crypt16 function, thanks to pcl@convex.oxford.ac.uk (Paul Leyland) + */ + +char *crypt16(key, salt) + char *key, *salt; + { ufc_long *s, *t; + char ktab[9], ttab[9]; + static char q[14], res[25]; + /* + * Hack DES tables according to salt + */ + setup_salt(salt); + + /* + * Setup key schedule + */ + clearmem(ktab, sizeof ktab); + (void)strncpy(ktab, key, 8); + ufc_mk_keytab(ktab); + + /* + * Go for first 20 DES encryptions + */ + s = _ufc_doit((ufc_long)0, (ufc_long)0, + (ufc_long)0, (ufc_long)0, (ufc_long)20); + + /* + * And convert back to 6 bit ASCII + */ + strcpy (res, output_conversion(s[0], s[1], salt)); + + clearmem(ttab, sizeof ttab); + if (strlen (key) > 8) (void)strncpy(ttab, key+8, 8); + ufc_mk_keytab(ttab); + + /* + * Go for second 5 DES encryptions + */ + t = _ufc_doit((ufc_long)0, (ufc_long)0, + (ufc_long)0, (ufc_long)0, (ufc_long)5); + /* + * And convert back to 6 bit ASCII + */ + strcpy (q, output_conversion(t[0], t[1], salt)); + strcpy (res+13, q+2); + + clearmem(ktab, sizeof ktab); + (void)strncpy(ktab, key, 8); + ufc_mk_keytab(ktab); + + return res; + } + +/* + * Experimental -- not supported -- may choke your dog + */ + +void ufc_setup_password(cookie, s) + long *cookie; + char *s; + { char c; + int i; + ufc_long x; + ufc_long dl1, dl2, dr1, dr2; + + setup_salt(s); + dl1 = dl2 = dr1 = dr2 = 0; + for(i = 0, s += 2; c = *s++; i++) { + int x = ascii_to_bin(c); + dl1 |= revfinal[i][x][0]; + dl2 |= revfinal[i][x][1]; + dr1 |= revfinal[i][x][2]; + dr2 |= revfinal[i][x][3]; + } + x = (dl1 ^ dl2) & current_saltbits; + x = (dr1 ^ dr2) & current_saltbits; + cookie[0] = dl1 ^ x; cookie[1] = dl2 ^ x; + cookie[2] = dr1 ^ x; cookie[3] = dr2 ^ x; + } + +void ufc_do_pw(cookie, guess) + long *cookie; + char *guess; + { char ktab[9]; + ufc_long *s; + clearmem(ktab, sizeof ktab); + (void)strncpy(ktab, guess, 8); + ufc_mk_keytab(ktab); + s = _ufc_doit((ufc_long)0, (ufc_long)0, + (ufc_long)0, (ufc_long)0, (ufc_long)25); + cookie[0] = s[0]; cookie[1] = s[1]; + cookie[2] = s[2]; cookie[3] = s[3]; + } diff --git a/WWW/Library/Implementation/getline.c b/WWW/Library/Implementation/getline.c new file mode 100644 index 00000000..7f6ff038 --- /dev/null +++ b/WWW/Library/Implementation/getline.c @@ -0,0 +1,74 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* CHANGED FOR VMS */ + +/* + * <getline.c> + */ + +#include "HTUtils.h" +#include "tcp.h" +#include <stddef.h> + +#include "LYLeaks.h" + +/* Read up to (and including) a newline from STREAM into *LINEPTR + (and null-terminate it). *LINEPTR is a pointer returned from malloc (or + NULL), pointing to *N characters of space. It is realloc'd as + necessary. Returns the number of characters read (not including the + null terminator), or -1 on error or EOF. */ + +int getline(char **lineptr, size_t *n, FILE *stream) +{ +static char line[256]; +char *ptr; +unsigned int len; + + if (lineptr == NULL || n == NULL) + { + SOCKET_ERRNO = EINVAL; + return -1; + } + + if (ferror (stream)) + return -1; + + if (feof(stream)) + return -1; + + fgets(line,256,stream); + + ptr = strchr(line,'\n'); + if (ptr) + *ptr = '\0'; + + len = strlen(line); + + if ((len+1) < 256) + { + ptr = realloc(*lineptr, 256); + if (ptr == NULL) + return(-1); + *lineptr = ptr; + *n = 256; + } + + strcpy(*lineptr,line); + return(len); +} diff --git a/WWW/Library/Implementation/getpass.c b/WWW/Library/Implementation/getpass.c new file mode 100644 index 00000000..a7f0de7b --- /dev/null +++ b/WWW/Library/Implementation/getpass.c @@ -0,0 +1,64 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* CHANGED FOR VMS */ + +/* + * <getpass.c> + */ + +#include "HTUtils.h" +/*#include <stdio.h> included by HTUtils.h -- FM */ +#include <descrip.h> +#include <psldef.h> +#include <iodef.h> +#include <starlet.h> + +#include "LYLeaks.h" + +PUBLIC char * getpass ARGS1(CONST char *, prompt) +{ + static char *buf; + + int result; + $DESCRIPTOR(devnam,"SYS$INPUT"); + int chan; + int promptlen; + struct { + short result; + short count; + int info; + } iosb; + + promptlen = strlen(prompt); + + buf = (char *)malloc(256); + if (buf == NULL) + return(NULL); + + result = sys$assign(&devnam, &chan, PSL$C_USER, 0, 0); + + result = sys$qiow(0, chan, IO$_READPROMPT | IO$M_PURGE |IO$M_NOECHO, &iosb, 0, 0, + buf, 255, 0, 0, prompt, promptlen); + + buf[iosb.count] = '\0'; + + result = sys$dassgn(chan); + + return buf; +} diff --git a/WWW/Library/Implementation/patchlevel.h b/WWW/Library/Implementation/patchlevel.h new file mode 100644 index 00000000..c37c8654 --- /dev/null +++ b/WWW/Library/Implementation/patchlevel.h @@ -0,0 +1,24 @@ +/* + * UFC-crypt: ultra fast crypt(3) implementation + * + * Copyright (C) 1991, 1992, Free Software Foundation, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * @(#)patchlevel.h 1.11 7/15/92 + * + */ + +#define PATCHLEVEL "UFC-crypt, patchlevel 1e, @(#)patchlevel.h 1.11 7/15/92" diff --git a/WWW/Library/Implementation/tcp.h b/WWW/Library/Implementation/tcp.h new file mode 100644 index 00000000..2e5ada8d --- /dev/null +++ b/WWW/Library/Implementation/tcp.h @@ -0,0 +1,524 @@ +/* System dependencies in the W3 library + SYSTEM DEPENDENCIES + + System-system differences for TCP include files and macros. This + file includes for each system the files necessary for network and + file I/O. It should be used in conjunction with HTUtils.h to help + ensure portability across as many platforms and flavors of platforms + as possible. + + AUTHORS + + TBL Tim Berners-Lee, W3 project, CERN, <timbl@info.cern.ch> + EvA Eelco van Asperen <evas@cs.few.eur.nl> + MA Marc Andreessen NCSA + AT Aleksandar Totic <atotic@ncsa.uiuc.edu> + SCW Susan C. Weber <sweber@kyle.eitech.com> + + HISTORY: + 22 Feb 91 Written (TBL) as part of the WWW library. + 16 Jan 92 PC code from EvA + 22 Apr 93 Merged diffs bits from xmosaic release + 29 Apr 93 Windows/NT code from SCW + 20 May 94 A.Harper Add support for VMS CMU TCP/IP transport + 3 Oct 94 A.Harper Add support for VMS SOCKETSHR/NETLIB + 15 Jul 95 S. Bjorndahl Gnu C for VMS Globaldef/ref support + +*/ + +#ifndef TCP_H +#define TCP_H + +#ifndef HTUTILS_H +#include "HTUtils.h" +#endif /* !HTUTILS_H */ + +/* + +Default values + + These values may be reset and altered by system-specific sections + later on. there are also a bunch of defaults at the end . + + */ +/* Default values of those: */ +#define NETCLOSE close /* Routine to close a TCP-IP socket */ +#define NETREAD HTDoRead /* Routine to read from a TCP-IP socket */ +#define NETWRITE write /* Routine to write to a TCP-IP socket */ +#define SOCKET_READ read /* normal socket read routine */ +#define IOCTL ioctl /* normal ioctl routine for sockets */ +#define SOCKET_ERRNO errno /* normal socket errno */ + +/* Unless stated otherwise, */ +#define SELECT /* Can handle >1 channel. */ +#define GOT_SYSTEM /* Can call shell with string */ + +#ifdef unix +#define GOT_PIPE +#endif /* unix */ + +typedef struct sockaddr_in SockA; /* See netinet/in.h */ + + +#ifndef STDIO_H +#include <stdio.h> +#define STDIO_H +#endif /* !STDIO_H */ + +#ifdef _AIX +#define AIX +#endif /* _AIX */ +#ifdef AIX +#define unix +#endif /* AIX */ + +#ifdef _IBMR2 +#define USE_DIRENT /* sys V style directory open */ +#endif /* _IBMR2 */ + +/* Solaris. */ +#if defined(sun) && defined(__svr4__) && !defined(USE_DIRENT) +#define USE_DIRENT /* sys V style directory open */ +#endif /* sun && __svr4__ && !USE_DIRENT */ + +#ifdef __alpha +#define USE_DIRENT +#endif /* __alpha */ + +#ifndef USE_DIRENT +#ifdef SVR4 +#define USE_DIRENT +#endif /* SVR4 */ +#endif /* !USE_DIRENT */ + +#ifndef SOLARIS2 +#include <string.h> /* For bzero etc */ +#endif /* !SOLARIS2 */ + +/* Use builtin strdup when appropriate. */ +#if defined(ultrix) || defined(VMS) || defined(NeXT) +extern char *strdup (); +#endif /* ultrix || VMS || NeXT */ + +/* + + M ACROS FOR CONVERTING CHARACTERS + + */ +#ifndef TOASCII +#define TOASCII(c) (c) +#define FROMASCII(c) (c) +#endif /* !TOASCII */ + + +/* + +VAX/VMS + + Under VMS, there are many versions of TCP-IP. Define one if you do + not use Digital's UCX product: + + UCX DEC's "Ultrix connection" (default) + CMU_TCP Available via FTP from sacusr.mp.usbr.gov + SOCKETSHR Eckhart Meyer's interface to NETLIB + WIN_TCP From Wollongong, now GEC software. + MULTINET From SRI, now from TGV Inv. + DECNET Cern's TCP socket emulation over DECnet + + The last three do not interfere with the + unix i/o library, and so they need special calls to read, write and + close sockets. In these cases the socket number is a VMS channel + number, so we make the @@@ HORRIBLE @@@ assumption that a channel + number will be greater than 10 but a unix file descriptor less than + 10. It works. + + */ +#ifdef VMS + +#ifdef UCX +#undef IOCTL +#define IOCTL HTioctl +#endif /* UCX */ + +#ifdef WIN_TCP +#undef SOCKET_READ +#undef NETWRITE +#undef NETCLOSE +#undef IOCTL +#define SOCKET_READ(s,b,l) ((s)>10 ? netread((s),(b),(l)) : read((s),(b),(l))) +#define NETWRITE(s,b,l) ((s)>10 ? netwrite((s),(b),(l)) : write((s),(b),(l))) +#define NETCLOSE(s) ((s)>10 ? netclose(s) : close(s)) +#define IOCTL(a,b,c) -1 /* disables ioctl function */ +#define NO_IOCTL /* flag to check if ioctl is disabled */ +#endif /* WIN_TCP */ + +#ifdef CMU_TCP +#undef SOCKET_READ +#undef NETREAD +#undef NETWRITE +#undef NETCLOSE +#define SOCKET_READ(s,b,l) (cmu_get_sdc((s)) != 0 ? cmu_read((s),(b),(l)) : read((s),(b),(l))) +#define NETREAD(s,b,l) (cmu_get_sdc((s)) != 0 ? HTDoRead((s),(b),(l)) : read((s),(b),(l))) +#define NETWRITE(s,b,l) (cmu_get_sdc((s)) != 0 ? cmu_write((s),(b),(l)) : write((s),(b),(l))) +#define NETCLOSE(s) (cmu_get_sdc((s)) != 0 ? cmu_close((s)) : close((s))) +#endif /* CMU_TCP */ + +#ifdef MULTINET +#undef NETCLOSE +#undef SOCKET_READ +#undef NETWRITE +#undef IOCTL +#undef SOCKET_ERRNO +#define SOCKET_READ(s,b,l) ((s)>10 ? socket_read((s),(b),(l)) : \ + read((s),(b),(l))) +#define NETWRITE(s,b,l) ((s)>10 ? socket_write((s),(b),(l)) : \ + write((s),(b),(l))) +#define NETCLOSE(s) ((s)>10 ? socket_close(s) : close(s)) +#define IOCTL socket_ioctl +#define SOCKET_ERRNO socket_errno +#endif /* MULTINET */ + +#ifdef SOCKETSHR_TCP +#undef SOCKET_READ +#undef NETREAD +#undef NETWRITE +#undef NETCLOSE +#undef IOCTL +#define SOCKET_READ(s,b,l) (si_get_sdc((s)) != 0 ? si_read((s),(b),(l)) : \ + read((s),(b),(l))) +#define NETREAD(s,b,l) (si_get_sdc((s)) != 0 ? HTDoRead((s),(b),(l)) : \ + read((s),(b),(l))) +#define NETWRITE(s,b,l) (si_get_sdc((s)) != 0 ? si_write((s),(b),(l)) : \ + write((s),(b),(l))) +#define NETCLOSE(s) (si_get_sdc((s)) != 0 ? si_close((s)) : close((s))) +#define IOCTL si_ioctl +#endif /* SOCKETSHR_TCP */ + +#include <string.h> + +#ifndef STDIO_H +#include <stdio.h> +#define STDIO_H +#endif /* !STDIO_H */ + +#include <file.h> +#include <stat.h> +#include <unixio.h> +#include <unixlib.h> + +#define INCLUDES_DONE + +#ifdef MULTINET /* Include from standard Multinet directories */ +#ifndef __SOCKET_TYPEDEFS +#define __SOCKET_TYPEDEFS 1 +#endif /* !__SOCKET_TYPEDEFS */ +#include <time.h> +#include <types.h> +#ifdef __TIME_T +#define __TYPES 1 +#define __TYPES_LOADED 1 +#endif /* __TIME_T */ +#ifdef __SOCKET_TYPEDEFS +#undef __SOCKET_TYPEDEFS +#endif /* __SOCKET_TYPEDEFS */ +#include "multinet_root:[multinet.include.sys]types.h" +#ifndef __SOCKET_TYPEDEFS +#define __SOCKET_TYPEDEFS 1 +#endif /* !__SOCKET_TYPEDEFS */ +#include "multinet_root:[multinet.include]errno.h" +#ifdef __TYPES +#define __TIME_T 1 +#endif /* __TYPE */ +#ifdef __TIME_LOADED +#define __TIME 1 /* to avoid double definitions in in.h */ +#endif /* __TIME_LOADED */ +#include "multinet_root:[multinet.include.sys]time.h" +#include "multinet_root:[multinet.include.sys]socket.h" +#include "multinet_root:[multinet.include.netinet]in.h" +#include "multinet_root:[multinet.include.arpa]inet.h" +#include "multinet_root:[multinet.include]netdb.h" +#include "multinet_root:[multinet.include.sys]ioctl.h" +#endif /* MULTINET */ + + +#ifdef DECNET +#include <types.h> +#include <errno.h> +#include <time.h> +#include "types.h" /* for socket.h */ +#include "socket.h" +#include "dn" +#include "dnetdb" +/* #include "vms.h" */ +#endif /* DECNET */ + + +#ifdef UCX +#include <types.h> +#include <errno.h> +#include <time.h> +#include <socket.h> +#include <in.h> +#include <inet.h> +#if defined(TCPWARE) && !defined(__DECC) +#include "tcpware_include:netdb.h" +#include "tcpware_include:ucx$inetdef.h" +#else +#include <netdb.h> +#include <ucx$inetdef.h> +#endif /* TCPWARE */ +#endif /* UCX */ + + +#ifdef CMU_TCP +#include <types.h> +#include <errno.h> +#include "cmuip_root:[syslib]time.h" +#include "cmuip_root:[syslib]socket.h" +#include <in.h> +#include <inet.h> +#include <netdb.h> +#include "cmuip_root:[syslib]ioctl.h" +#endif /* CMU_TCP */ + + +#ifdef SOCKETSHR_TCP +#include <types.h> +#include <errno.h> +#include <time.h> +#include <socket.h> +#include <in.h> +#include <inet.h> +#include <netdb.h> +#include "socketshr_library:socketshr.h" +#include "socketshr_library:ioctl.h" +#endif /* SOCKETSHR_TCP */ + +#ifdef WIN_TCP +#include <types.h> +#include <errno.h> +#include <time.h> +#include <socket.h> +#include <in.h> +#include <inet.h> +#include <netdb.h> +#ifndef NO_IOCTL +#include <ioctl.h> +#endif /* !NO_IOCTL */ +#endif /* WIN_TCP */ + + +#define TCP_INCLUDES_DONE + +/* + + On VMS machines, the linker needs to be told to put global data sections into + a data + segment using these storage classes. (MarkDonszelmann) + + */ +#if defined(VAXC) && !defined(__DECC) +#define GLOBALDEF globaldef +#define GLOBALREF globalref +#else +#ifdef __GNUC__ /* this added by Sterling Bjorndahl */ +#define GLOBALREF_IS_MACRO 1 +#define GLOBALDEF_IS_MACRO 1 +#include <gnu_hacks.h> /* defines GLOBALREF and GLOBALDEF for GNUC on VMS */ +#endif /* __GNUC__ */ +#endif /* VAXC && !DECC */ + +#endif /* VMS */ + +/* + * On non-VMS machines and for DECC on VMS, the GLOBALDEF and GLOBALREF + * storage types default to normal C storage types. + */ +#ifndef GLOBALREF +#define GLOBALDEF +#define GLOBALREF extern +#endif /* !GLOBALREF */ + + +/* +SCO ODT unix version + */ +#ifdef SCO +#define sco +#endif /* SCO */ +#ifdef sco +#include <sys/fcntl.h> +#define USE_DIRENT +#endif /* sco */ + +/* +Intergraph CLIX + */ +#ifdef CLIX +#include <sys/fcntl.h> +#define USE_DIRENT +#endif /* CLIX */ + +#ifdef ISC +#ifndef NO_UNISTD_H +#include <sys/unistd.h> +#endif /* !NO_UNISTD_H */ +#else +#if !defined(NO_UNISTD_H) && !defined(VMS) +#include <unistd.h> +#endif /* !NO_UNISTD_H && !VMS */ +#endif /* ISC */ + +#if defined(SVR4) || defined(UNIXWARE) +#include <sys/fcntl.h> +#ifndef NO_FILIO_H /* BSD Interactive doesn't have filio.h. */ +#include <sys/filio.h> +#endif /* !NO_FILIO_H */ +#endif /* SVR4 || UNIXWARE */ + +/* +SOLARIS 2 + */ +#ifdef SOLARIS2 +#include <sys/filio.h> +#endif /* SOLARIS2 */ + +/* +MIPS unix + */ +/* Mips hack (bsd4.3/sysV mixture...) */ +#ifdef mips +extern int errno; +#endif /* mips */ + +/* +Regular BSD unix versions +========================= + These are a default unix where not already defined specifically. + */ +#ifndef INCLUDES_DONE +#include <sys/types.h> +/* #include <streams/streams.h> not ultrix */ +#ifndef SOLARIS2 +#include <string.h> +#endif /* !SOLARIS2 */ +#include <errno.h> /* independent */ +#ifdef SCO +#include <sys/timeb.h> +#include <time.h> +#endif /* SCO */ +#ifdef AIX +#include <time.h> +#endif /* AIX */ +#include <sys/time.h> /* independent */ +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/file.h> /* For open() etc */ +#if defined(NeXT) || defined(sony_news) +#ifndef mode_t +typedef unsigned short mode_t; +#endif /* !mode_t */ +#ifndef pid_t +typedef int pid_t; +#endif /* !pid_t */ +#ifndef S_ISREG +#define S_ISREG(m) (((m) & 0170000) == 0100000) +#endif /* S_ISREG */ +#ifndef WEXITSTATUS +#ifdef sony_news +#define WEXITSTATUS(s) WIFEXITED(s) +#else +#define WEXITSTATUS(s) (((s).w_status >> 8) & 0377) +#endif /* sony_news */ +#endif /* !WEXITSTATUS */ +#ifndef WTERMSIG +#ifdef sony_news +#define WTERMSIG(s) (s).w_termsig +#else +#define WTERMSIG(s) (((s).w_status >> 8) & 0177) +#endif /* sony_news */ +#endif /* !WTERMSIG */ +#endif /* NeXT || sony_news */ +#define INCLUDES_DONE +#endif /* Normal includes */ + +/* Interactive UNIX for i386 and i486 -- Thanks to jeffrey@itm.itm.org */ +#ifdef ISC +#include <net/errno.h> +#include <sys/types.h> +#include <sys/tty.h> +#include <sys/sioctl.h> +#include <sys/bsdtypes.h> +#include <sys/fcntl.h> +#ifndef MERGE +#define MERGE +#include <sys/pty.h> +#undef MERGE +#else +#include <sys/pty.h> +#endif /* !MERGE */ +#ifndef USE_DIRENT +#define USE_DIRENT /* sys V style directory open */ +#endif /* USE_DIRENT */ +#include <sys/dirent.h> +#endif /* ISC */ + +/* Directory reading stuff - BSD or SYS V +*/ +#if defined(UNIX) && !defined(unix) +#define unix +#endif /* UNIX && !unix */ + +#ifdef unix /* if this is to compile on a UNIX machine */ +#define GOT_READ_DIR 1 /* if directory reading functions are available */ +#ifdef USE_DIRENT /* sys v version */ +#include <dirent.h> +#define direct dirent +#else +#include <sys/dir.h> +#endif /* USE_DIRENT */ +#if defined(sun) && defined(__svr4__) +#include <sys/fcntl.h> +#include <limits.h> +#else +#if defined(__hpux) || defined(LINUX) || defined (__FreeBSD__) +#include <limits.h> +#endif /* __hpux || LINUX || __FreeBSD__ */ +#endif /* sun && __svr4__ */ +#if !defined(MAXINT) && defined(INT_MAX) +#define MAXINT INT_MAX +#endif /* !MAXINT && INT_MAX */ +#endif /* unix */ + +/* +Defaults +======== + INCLUDE FILES FOR TCP + */ +#ifndef TCP_INCLUDES_DONE +#ifndef NO_IOCTL +#include <sys/ioctl.h> /* EJB */ +#endif /* !NO_IOCTL */ +#include <sys/socket.h> +#include <netinet/in.h> +#ifndef __hpux /* this may or may not be good -marc */ +#include <arpa/inet.h> /* Must be after netinet/in.h */ +#endif /* !__hpux */ +#include <netdb.h> +#endif /* TCP includes */ + +/* + MACROS FOR MANIPULATING MASKS FOR SELECT() + */ +#ifdef SELECT +#ifndef FD_SET +typedef unsigned int fd_set; +#define FD_SET(fd,pmask) (*(pmask)) |= (1<<(fd)) +#define FD_CLR(fd,pmask) (*(pmask)) &= ~(1<<(fd)) +#define FD_ZERO(pmask) (*(pmask))=0 +#define FD_ISSET(fd,pmask) (*(pmask) & (1<<(fd))) +#endif /* !FD_SET */ +#endif /* SELECT */ + + +#endif /* TCP_H */ diff --git a/WWW/Library/Implementation/ufc-crypt.h b/WWW/Library/Implementation/ufc-crypt.h new file mode 100644 index 00000000..13da8b47 --- /dev/null +++ b/WWW/Library/Implementation/ufc-crypt.h @@ -0,0 +1,108 @@ +/* + * UFC-crypt: ultra fast crypt(3) implementation + * + * Copyright (C) 1991, 1992, Free Software Foundation, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * @(#)ufc-crypt.h 1.16 09/21/92 + * + * Definitions of datatypes + * + */ + +/* + * Requirements for datatypes: + * + * A datatype 'ufc_long' of at least 32 bit + * *and* + * A type 'long32' of exactly 32 bits (_UFC_32_) + * *or* + * A type 'long64' of exactly 64 bits (_UFC_64_) + * + * 'int' is assumed to be at least 8 bit + */ + +/* + * #ifdef's for various architectures + */ + +#ifdef cray +/* thanks to <hutton@opus.sdsc.edu> (Tom Hutton) for testing */ +typedef unsigned long ufc_long; +typedef unsigned long long64; +#define _UFC_64_ +#endif + +#ifdef convex +/* thanks to pcl@convex.oxford.ac.uk (Paul Leyland) for testing */ +typedef unsigned long ufc_long; +typedef long long long64; +#define _UFC_64_ +#endif + +#ifdef ksr +/* + * Note - the KSR machine does not define a unique symbol + * which we can check. So you MUST add '-Dksr' to your Makefile. + * Thanks to lijewski@theory.tc.cornell.edu (Mike Lijewski) for + * the patch. + */ +typedef unsigned long ufc_long; +typedef unsigned long long64; +#define _UFC_64_ +#endif + +/* + * For debugging 64 bit code etc with 'gcc' + */ + +#ifdef GCC3232 +typedef unsigned long ufc_long; +typedef unsigned long long32; +#define _UFC_32_ +#endif + +#ifdef GCC3264 +typedef unsigned long ufc_long; +typedef long long long64; +#define _UFC_64_ +#endif + +#ifdef GCC6432 +typedef long long ufc_long; +typedef unsigned long long32; +#define _UFC_32_ +#endif + +#ifdef GCC6464 +typedef long long ufc_long; +typedef long long long64; +#define _UFC_64_ +#endif + +/* + * Catch all for 99.95% of all UNIX machines + */ + +#ifndef _UFC_64_ +#ifndef _UFC_32_ +#define _UFC_32_ +typedef unsigned long ufc_long; +typedef unsigned long long32; +#endif +#endif + + diff --git a/WWW/Library/apollo_m68k/Makefile b/WWW/Library/apollo_m68k/Makefile new file mode 100644 index 00000000..20aa9ac8 --- /dev/null +++ b/WWW/Library/apollo_m68k/Makefile @@ -0,0 +1,38 @@ +# Make WWW for apollo NOTE WWW macro changed for unwritable source tree ** +# + +# For W3 distribution, machine type for subdirectories +WWW_MACH = apollo_m68k + +# For ASIS installation, the ASIS code for the machine/os +ASIS_MACH = apollo-68k/sr-10.3 + + +CFLAGS = -O -DDEBUG +LFLAGS = +CC = cc +LFLAGS = + +# Directory for installed binary: +BINDIR = /usr/local/bin + + + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. +#WWW = /user/timbl/hypertext/WWW + +# Where should temporary (object) files go? +WTMP = ../.. + + +# Where is the W3 object library? +# LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) +LIBDIR = /tmp/WWWLibrary_Build + +include $(WWW)/Library/Implementation/CommonMakefile + + + + diff --git a/WWW/Library/clix/Makefile b/WWW/Library/clix/Makefile new file mode 100644 index 00000000..e1996d27 --- /dev/null +++ b/WWW/Library/clix/Makefile @@ -0,0 +1,30 @@ +# Make WWW under unix for a.n.other unix system (bsd) +# Use this as a template + +# For W3 distribution, machine type for subdirectories +WWW_MACH = clix + +# The ASIS repository's name for the machine we are on +ASIS_MACH = hardware/os + + +CFLAGS = -O -DDEBUG -DUSG -DUNIX -DCLIX +LFLAGS = +CC = cc + +# Directory for installed binary: +BINDIR = /usr/local/bin + +# Where is the W3 object library to be installed (not normally done)? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +include $(WWW)/Library/Implementation/CommonMakefile + diff --git a/WWW/Library/convex/Makefile b/WWW/Library/convex/Makefile new file mode 100644 index 00000000..fcd28786 --- /dev/null +++ b/WWW/Library/convex/Makefile @@ -0,0 +1,32 @@ +# Make WWW under ConvexOS + +# For W3 distribution, machine type for subdirectories +WWW_MACH = convex + +# The ASIS repository's name for the machine we are on +ASIS_MACH = hardware/os + + +CFLAGS = -O -DDEBUG -Dunix +LFLAGS = +CC = cc + +# Directory for installed binary: +BINDIR = /usr/local/bin + +# Where is the W3 object library to be installed (not normally done)? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + +# HTWAIS = $(LOB)/HTWAIS.o +# WAIS = YES +# WAISINC = -I/tmp/freeWAIS-0.202/ir +# WAISCFLAGS = -DDIRECT_WAIS + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/decstation/Makefile b/WWW/Library/decstation/Makefile new file mode 100644 index 00000000..b35d278c --- /dev/null +++ b/WWW/Library/decstation/Makefile @@ -0,0 +1,23 @@ +# Platform-specific Makefile for W3 Library (decstation) +# ----------------------------------------- +# + +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +#CFLAGS = +CFLAGS = -O +CC = cc +#LFLAGS = -lresolv +LFLAGS = + +ASIS_MACH = dec-station/ultrix-4.2 +WWW_MACH = decstation + +# Directory for installed binary: +LIBDIR = /usr/local/lib + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/duns/Makefile b/WWW/Library/duns/Makefile new file mode 100644 index 00000000..4852817e --- /dev/null +++ b/WWW/Library/duns/Makefile @@ -0,0 +1,489 @@ +# Makefile generated by imake - do not edit! +# $XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $ +# +# The cpp used on this machine replaces all newlines and multiple tabs and +# spaces in a macro expansion with a single space. Imake tries to compensate +# for this, but is not always successful. +# + +########################################################################### +# Makefile generated from "Imake.tmpl" and </tmp/IIf.a00214> +# $XConsortium: Imake.tmpl,v 1.77 89/12/18 17:01:37 jim Exp $ +# +# Platform-specific parameters may be set in the appropriate .cf +# configuration files. Site-wide parameters may be set in the file +# site.def. Full rebuilds are recommended if any parameters are changed. +# +# If your C preprocessor doesn't define any unique symbols, you'll need +# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing +# "make Makefile", "make Makefiles", or "make World"). +# +# If you absolutely can't get imake to work, you'll need to set the +# variables at the top of each Makefile as well as the dependencies at the +# bottom (makedepend will do this automatically). +# + +########################################################################### +# platform-specific configuration parameters - edit sun.cf to change + +# platform: $XConsortium: sun.cf,v 1.38 89/12/23 16:10:10 jim Exp $ +# operating system: SunOS 4.0.3 + +########################################################################### +# site-specific configuration parameters - edit site.def to change + + SHELL = /bin/sh + + TOP = . + CURRENT_DIR = . + + AR = ar clq + BOOTSTRAPCFLAGS = + CC = cc + + COMPRESS = compress + CPP = /lib/cpp $(STD_CPP_DEFINES) + PREPROCESSCMD = cc -E $(STD_CPP_DEFINES) + INSTALL = install + LD = ld + LINT = lint + LINTLIBFLAG = -C + LINTOPTS = -axz + LN = ln -s + MAKE = make + MV = mv + CP = cp + RANLIB = ranlib + RANLIBINSTFLAGS = + RM = rm -f + STD_INCLUDES = + STD_CPP_DEFINES = + STD_DEFINES = + EXTRA_LOAD_FLAGS = + EXTRA_LIBRARIES = + TAGS = ctags + + SHAREDCODEDEF = -DSHAREDCODE + SHLIBDEF = -DSUNSHLIB + + PROTO_DEFINES = + + INSTPGMFLAGS = + + INSTBINFLAGS = -m 0755 + INSTUIDFLAGS = -m 4755 + INSTLIBFLAGS = -m 0664 + INSTINCFLAGS = -m 0444 + INSTMANFLAGS = -m 0444 + INSTDATFLAGS = -m 0444 + INSTKMEMFLAGS = -m 4755 + + DESTDIR = + + TOP_INCLUDES = -I$(INCROOT) + + CDEBUGFLAGS = -O + CCOPTIONS = + COMPATFLAGS = + + ALLINCLUDES = $(STD_INCLUDES) $(TOP_INCLUDES) $(INCLUDES) $(EXTRA_INCLUDES) + ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(DEFINES) $(COMPATFLAGS) + CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES) + LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES) + LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES) + LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS) + LDCOMBINEFLAGS = -X -r + + MACROFILE = sun.cf + RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut + + IMAKE_DEFINES = + + IRULESRC = $(CONFIGDIR) + IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES) + + ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Imake.rules \ + $(IRULESRC)/Project.tmpl $(IRULESRC)/site.def \ + $(IRULESRC)/$(MACROFILE) $(EXTRA_ICONFIGFILES) + +########################################################################### +# X Window System Build Parameters +# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $ + +########################################################################### +# X Window System make variables; this need to be coordinated with rules +# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $ + + PATHSEP = / + USRLIBDIR = $(DESTDIR)/usr/lib + BINDIR = $(DESTDIR)/usr/bin/X11 + INCROOT = $(DESTDIR)/usr/include + BUILDINCROOT = $(TOP) + BUILDINCDIR = $(BUILDINCROOT)/X11 + BUILDINCTOP = .. + INCDIR = $(INCROOT)/X11 + ADMDIR = $(DESTDIR)/usr/adm + LIBDIR = $(USRLIBDIR)/X11 + CONFIGDIR = $(LIBDIR)/config + LINTLIBDIR = $(USRLIBDIR)/lint + + FONTDIR = $(LIBDIR)/fonts + XINITDIR = $(LIBDIR)/xinit + XDMDIR = $(LIBDIR)/xdm + AWMDIR = $(LIBDIR)/awm + TWMDIR = $(LIBDIR)/twm + GWMDIR = $(LIBDIR)/gwm + MANPATH = $(DESTDIR)/usr/man + MANSOURCEPATH = $(MANPATH)/man + MANDIR = $(MANSOURCEPATH)n + LIBMANDIR = $(MANSOURCEPATH)3 + XAPPLOADDIR = $(LIBDIR)/app-defaults + + SOXLIBREV = 4.2 + SOXTREV = 4.0 + SOXAWREV = 4.0 + SOOLDXREV = 4.0 + SOXMUREV = 4.0 + SOXEXTREV = 4.0 + + FONTCFLAGS = -t + + INSTAPPFLAGS = $(INSTDATFLAGS) + + IMAKE = imake + DEPEND = makedepend + RGB = rgb + FONTC = bdftosnf + MKFONTDIR = mkfontdir + MKDIRHIER = /bin/sh $(BINDIR)/mkdirhier.sh + + CONFIGSRC = $(TOP)/config + CLIENTSRC = $(TOP)/clients + DEMOSRC = $(TOP)/demos + LIBSRC = $(TOP)/lib + FONTSRC = $(TOP)/fonts + INCLUDESRC = $(TOP)/X11 + SERVERSRC = $(TOP)/server + UTILSRC = $(TOP)/util + SCRIPTSRC = $(UTILSRC)/scripts + EXAMPLESRC = $(TOP)/examples + CONTRIBSRC = $(TOP)/../contrib + DOCSRC = $(TOP)/doc + RGBSRC = $(TOP)/rgb + DEPENDSRC = $(UTILSRC)/makedepend + IMAKESRC = $(CONFIGSRC) + XAUTHSRC = $(LIBSRC)/Xau + XLIBSRC = $(LIBSRC)/X + XMUSRC = $(LIBSRC)/Xmu + TOOLKITSRC = $(LIBSRC)/Xt + AWIDGETSRC = $(LIBSRC)/Xaw + OLDXLIBSRC = $(LIBSRC)/oldX + XDMCPLIBSRC = $(LIBSRC)/Xdmcp + BDFTOSNFSRC = $(FONTSRC)/bdftosnf + MKFONTDIRSRC = $(FONTSRC)/mkfontdir + EXTENSIONSRC = $(TOP)/extensions + + DEPEXTENSIONLIB = + EXTENSIONLIB = -lXext + + DEPXLIB = $(DEPEXTENSIONLIB) + XLIB = $(EXTENSIONLIB) -lX11 + + DEPXAUTHLIB = $(USRLIBDIR)/libXau.a + XAUTHLIB = -lXau + + DEPXMULIB = + XMULIB = -lXmu + + DEPOLDXLIB = + OLDXLIB = -loldX + + DEPXTOOLLIB = + XTOOLLIB = -lXt + + DEPXAWLIB = + XAWLIB = -lXaw + + LINTEXTENSIONLIB = $(USRLIBDIR)/llib-lXext.ln + LINTXLIB = $(USRLIBDIR)/llib-lX11.ln + LINTXMU = $(USRLIBDIR)/llib-lXmu.ln + LINTXTOOL = $(USRLIBDIR)/llib-lXt.ln + LINTXAW = $(USRLIBDIR)/llib-lXaw.ln + + DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB) + + DEPLIBS1 = $(DEPLIBS) + DEPLIBS2 = $(DEPLIBS) + DEPLIBS3 = $(DEPLIBS) + +########################################################################### +# Imake rules for building libraries, programs, scripts, and data files +# rules: $XConsortium: Imake.rules,v 1.67 89/12/18 17:14:15 jim Exp $ + +########################################################################### +# start of Imakefile + +# Make WWW under unix for a.n.other unix system (bsd) +# Use this as a template + +TK_WWW_SOURCE_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/Tcl + +TK_WWW_INSTALL_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/$(WWW_MACH) + +TK_WWW_HOME_PAGE=http://www.w3.org/default.html +TK_WWW_START_PAGE=$(TK_WWW_HOME_PAGE) + +CC = gcc -fno-builtin -Wall + +CDEBUGFLAGS = -O3 -pipe + +COMPATFLAGS = -I/afs/athena.mit.edu/course/other/cdsdev/www-compat +CCOPTIONS = + +BINDIR = $(TK_WWW_INSTALL_PATH) + +# For W3 distribution, machine type for subdirectories +WWW_MACH = unix.x + +# The ASIS repository's name for the machine we are on +ASIS_MACH = hardware/os + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = /tmp + +# Common Makefile for W3 Library Code +# ----------------------------------- +# +# (c) CERN 1990, 1991 -- see Copyright.html for conditions +# +# This file should be invariant between systems. +# DEPENDENCIES NOT COMPLETE + +# +# make Compile and link the software (private version) +# make clean Remove intermediate files + +WC = $(WWW)/Library +CMN = $(WWW)/Library/Implementation/ + +# Where shall we put the objects and built library? + +LOB = $(WTMP)/Library/$(WWW_MACH) + +# Bug: This path, if relative, is taken relative to the directory +# in which this makefile is, not the pwd. This screws up the +# recursive invocation + +VC = 2.14 + +CFLAGS2 = $(CFLAGS) -I$(CMN) + +CERNLIBBIN = $(WWW)/bin + +COMMON = $(LOB)/HTParse.o $(LOB)/HTAccess.o $(LOB)/HTTP.o \ + $(LOB)/HTFile.o $(LOB)/HTFTP.o $(LOB)/HTTCP.o \ + $(LOB)/SGML.o $(LOB)/HTMLDTD.o $(LOB)/HTChunk.o \ + $(LOB)/HTPlain.o $(LOB)/HTWriter.o $(LOB)/HTFWriter.o \ + $(LOB)/HTMLGen.o \ + $(LOB)/HTAtom.o $(LOB)/HTAnchor.o $(LOB)/HTStyle.o \ + $(LOB)/HTList.o $(LOB)/HTString.o $(LOB)/HTAlert.o \ + $(LOB)/HTRules.o $(LOB)/HTFormat.o $(LOB)/HTInit.o $(LOB)/HTMIME.o \ + $(LOB)/HTHistory.o $(LOB)/HTNews.o $(LOB)/HTGopher.o \ + $(LOB)/HTTelnet.o $(LOB)/HTWSRC.o $(HTWAIS) + +CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTFile.c \ + $(CMN)HTFTP.c $(CMN)HTTCP.c $(CMN)SGML.c \ + $(CMN)HTMLDTD.c \ + $(CMN)HTPlain.c $(CMN)HTWriter.c $(CMN)HTFWriter.c $(CMN)HTMLGen.c \ + $(CMN)HTChunk.c $(CMN)HTAtom.c $(CMN)HTAnchor.c $(CMN)HTStyle.c \ + $(CMN)HTList.c $(CMN)HTString.c $(CMN)HTAlert.c $(CMN)HTRules.c \ + $(CMN)HTFormat.c $(CMN)HTInit.c $(CMN)HTMIME.c $(CMN)HTHistory.c \ + $(CMN)HTNews.c $(CMN)HTGopher.c $(CMN)HTTelnet.c \ + $(CMN)HTWAIS.c $(CMN)HTWSRC.c + +HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTFile.h \ + $(CMN)HTFTP.h $(CMN)HTTCP.h \ + $(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \ + $(CMN)HTPlain.h $(CMN)HTWriter.h \ + $(CMN)HTFWriter.h $(CMN)HTMLGen.h \ + $(CMN)HTStream.h \ + $(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \ + $(CMN)HTList.h \ + $(CMN)HTString.h $(CMN)HTAlert.h $(CMN)HTRules.h \ + $(CMN)HTFormat.h $(CMN)HTInit.h \ + $(CMN)HTMIME.h $(CMN)HTHistory.h $(CMN)HTNews.h \ + $(CMN)HTGopher.h \ + $(CMN)HTUtils.h $(CMN)tcp.h $(CMN)WWW.h $(CMN)HText.h \ + $(CMN)HTTelnet.h \ + $(CMN)HTWAIS.h $(CMN)HTWSRC.h + +SOURCES = $(CFILES) $(HFILES) $(CMN)Version.make $(CMN)CommonMakefile \ + $(WWW)/README.txt $(WWW)/Copyright.txt $(WWW)/BUILD +SPECIFIC = $(WWW)/All + +# Library +# +# On SGI, ranlib is unnecessary and does not exist so we ignore errors +# for that step +all: $(LOB)/libwww.a + $(MV) $(LOB)/libwww.a $(WC)/$(WWW_MACH) + +$(LOB)/libwww.a : $(COMMON) + ar r $(LOB)/libwww.a $(COMMON) + -ranlib $(LOB)/libwww.a + +# Clean up everything generatable except final products +clean :: + $(RM) $(LOB) + +# Clean up everything generatable including final products + +cleanall :: clean + $(RM) $(LOB)/libwww.a + +# Common code +# ----------- + +# Directory for object files - .created checks it exists + +OE = $(LOB)/.created +$(OE) : + -mkdir $(WTMP) + -mkdir $(WTMP)/Library + -mkdir $(WTMP)/Library/$(WWW_MACH) + touch $@ + +$(LOB)/HTList.o : $(OE) $(CMN)HTList.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTList.c + +$(LOB)/HTAnchor.o : $(OE) $(CMN)HTAnchor.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAnchor.c + +$(LOB)/HTFormat.o : $(OE) $(CMN)HTFormat.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFormat.c + +$(LOB)/HTInit.o : $(OE) $(CMN)HTInit.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTInit.c + +$(LOB)/HTMIME.o : $(OE) $(CMN)HTMIME.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMIME.c + +$(LOB)/HTHistory.o : $(OE) $(CMN)HTHistory.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTHistory.c + +$(LOB)/HTNews.o : $(OE) $(CMN)HTNews.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTNews.c + +$(LOB)/HTGopher.o : $(OE) $(CMN)HTGopher.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGopher.c + +$(LOB)/HTTelnet.o : $(OE) $(CMN)HTTelnet.c $(CMN)HTUtils.h $(CMN)HTTelnet.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTelnet.c + +$(LOB)/HTStyle.o : $(OE) $(CMN)HTStyle.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTStyle.c + +$(LOB)/HTAtom.o : $(OE) $(CMN)HTAtom.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAtom.c + +$(LOB)/HTChunk.o : $(OE) $(CMN)HTChunk.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTChunk.c + +$(LOB)/HTString.o : $(OE) $(CMN)HTString.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTString.c + +$(LOB)/HTAlert.o : $(OE) $(CMN)HTAlert.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTAlert.c + +$(LOB)/HTRules.o : $(OE) $(CMN)HTRules.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTRules.c + +$(LOB)/SGML.o : $(OE) $(CMN)SGML.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)SGML.c + +$(LOB)/HTMLGen.o : $(OE) $(CMN)HTMLGen.c $(CMN)HTUtils.h $(CMN)HTMLDTD.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLGen.c + +$(LOB)/HTMLDTD.o : $(OE) $(CMN)HTMLDTD.c $(CMN)SGML.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLDTD.c + +$(LOB)/HTPlain.o : $(OE) $(CMN)HTPlain.c $(CMN)HTPlain.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPlain.c + +$(LOB)/HTWAIS.o : $(OE) $(CMN)HTWAIS.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(WAISINC) $(CMN)HTWAIS.c + +$(LOB)/HTWSRC.o : $(OE) $(CMN)HTWSRC.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWSRC.c + +$(LOB)/HTWriter.o : $(OE) $(CMN)HTWriter.c $(CMN)HTWriter.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWriter.c + +$(LOB)/HTFWriter.o : $(OE) $(CMN)HTFWriter.c $(CMN)HTFWriter.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFWriter.c + +# Communications & Files + +$(LOB)/HTTP.o : $(OE) $(CMN)HTTP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTP.c + +$(LOB)/HTTCP.o : $(OE) $(CMN)HTTCP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTCP.c + +$(LOB)/HTFile.o : $(OE) $(CMN)HTFile.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFile.c + +$(LOB)/HTFTP.o : $(OE) $(CMN)HTFTP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFTP.c + +$(LOB)/HTAccess.o : $(OE) $(CMN)HTAccess.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAccess.c + +$(LOB)/HTParse.o : $(OE) $(CMN)HTParse.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTParse.c + +########################################################################### +# common rules for all Makefiles - do not edit + +emptyrule:: + +clean:: + $(RM_CMD) \#* + +Makefile:: + -@if [ -f Makefile ]; then \ + echo " $(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \ + $(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \ + else exit 0; fi + $(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR) + +tags:: + $(TAGS) -w *.[ch] + $(TAGS) -xw *.[ch] > TAGS + +saber: + #load $(ALLDEFINES) $(SRCS) + +osaber: + #load $(ALLDEFINES) $(OBJS) + +########################################################################### +# empty rules for directories that do not have SUBDIRS - do not edit + +install:: + @echo "install in $(CURRENT_DIR) done" + +install.man:: + @echo "install.man in $(CURRENT_DIR) done" + +Makefiles:: + +includes:: + +########################################################################### +# dependencies generated by makedepend + diff --git a/WWW/Library/freebsd/Makefile b/WWW/Library/freebsd/Makefile new file mode 100644 index 00000000..04fd07ab --- /dev/null +++ b/WWW/Library/freebsd/Makefile @@ -0,0 +1,27 @@ +# Make WWW under FreeBSD +# + +# For W3 distribution, machine type for subdirectories +WWW_MACH = freebsd + +# The ASIS repository's name for the machine we are on +ASIS_MACH = i386/FreeBSD + + +CFLAGS += -O -DDEBUG +LFLAGS = + +# Directory for installed binary: +BINDIR = /usr/local/bin + +# Where is the W3 object library to be installed (not normally done)? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/isc/Makefile b/WWW/Library/isc/Makefile new file mode 100644 index 00000000..29de6885 --- /dev/null +++ b/WWW/Library/isc/Makefile @@ -0,0 +1,30 @@ +# Make WWW under unix for a.n.other unix system (bsd) +# Use this as a template + +# For W3 distribution, machine type for subdirectories +WWW_MACH = isc + +# The ASIS repository's name for the machine we are on +ASIS_MACH = intel/isc + + +CFLAGS = -DDEBUG -O -DISC -Dvfork=fork +LFLAGS = +CC = cc + +# Directory for installed binary: +BINDIR = /usr/local/bin + +# Where is the W3 object library to be installed (not normally done)? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +include $(WWW)/Library/Implementation/CommonMakefile + diff --git a/WWW/Library/mips/Makefile b/WWW/Library/mips/Makefile new file mode 100644 index 00000000..1c84cbe0 --- /dev/null +++ b/WWW/Library/mips/Makefile @@ -0,0 +1,29 @@ +# Makefile for WWW under svr4 +# + +# For W3 distribution, machine type for subdirectories +WWW_MACH = mips + +# The ASIS repository's name for the machine we are on +ASIS_MACH = mips/mips + + +#CFLAGS = -DDEBUG -systype svr3 -DMIPS -DNO_BCOPY +CFLAGS = -O -DDEBUG -systype svr3 -I/svr3/usr/include/bsd +LFLAGS = +CC = cc + +# Directory for installed binary: +BINDIR = /usr/local/bin + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + +# Where is the W3 object library? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/netbsd/Makefile b/WWW/Library/netbsd/Makefile new file mode 100644 index 00000000..ae92760a --- /dev/null +++ b/WWW/Library/netbsd/Makefile @@ -0,0 +1,29 @@ +# Make WWW under NetBSD +# + +# For W3 distribution, machine type for subdirectories +WWW_MACH = netbsd + +# The ASIS repository's name for the machine we are on +ASIS_MACH = i386/NetBSD + + +CFLAGS = -O -DUSE_DIRENT +LFLAGS = +CC = cc + +# Directory for installed binary: +BINDIR = /usr/local/bin + +# Where is the W3 object library to be installed (not normally done)? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + +include $(WWW)/Library/Implementation/CommonMakefile + diff --git a/WWW/Library/next/Makefile b/WWW/Library/next/Makefile new file mode 100644 index 00000000..bd35c89a --- /dev/null +++ b/WWW/Library/next/Makefile @@ -0,0 +1,40 @@ +# Platform-specific Makefile for W3 Library (NeXT) +# ----------------------------------------- +# +# Library compiled with fudge to alow XMOSAIC to +# pick up binary files... for now. + +WWW = ../.. + +# Where should temporary (object) files go? Normally, WTMP = $(WWW) +#WTMP = /tmp +WTMP = $(WWW) + +# For MACH 3.0 it seems -bsd is needed to order to define errno +# in /usr/include/bsd/sys/errno.h. But __STRICT_BSD__ is needed for +# errno. + +CFLAGS = -Wall -O -DXMOSAIC_HACK + +# Yes please, I want direct WAIS access +# +#WAIS = ../../../freeWAIS +#WAISINC = -I$(WAIS)/ir +#WAISCFLAGS = -DDIRECT_WAIS +#WAISLIB = $(WAIS)/bin/client.a $(WAIS)/bin/wais.a +# $(WAIS)/bin/inv.a $(WAIS)/bin/wais.a +#HTWAIS = $(WTMP)/Library/$(WWW_MACH)/HTWAIS.o + +CC = cc +# For testing memory leaks only! Use /NextDeveloper/MallocDebug app +LFLAGS = -lMallocDebug +#LFLAGS = + +WWW_MACH = next +ASIS_MACH = next/2.0 + + +# Directory for installed binary: +LIBDIR = /usr/local/lib + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/osf/Makefile b/WWW/Library/osf/Makefile new file mode 100644 index 00000000..a81457a6 --- /dev/null +++ b/WWW/Library/osf/Makefile @@ -0,0 +1,23 @@ +# Platform-specific Makefile for W3 Library (decstation) +# ----------------------------------------- +# + +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +#CFLAGS = +CFLAGS = -DSYS5 +CC = cc -O +#LFLAGS = +LFLAGS = + +ASIS_MACH = alpha/osf1 +WWW_MACH = osf + +# Directory for installed binary: +LIBDIR = /usr/local/lib + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/ptx/Makefile b/WWW/Library/ptx/Makefile new file mode 100644 index 00000000..772d6c6a --- /dev/null +++ b/WWW/Library/ptx/Makefile @@ -0,0 +1,29 @@ +# Make WWW under Sequent's DYNIX/ptx +# + +# For W3 distribution, machine type for subdirectories +WWW_MACH = ptx + +# The ASIS repository's name for the machine we are on +ASIS_MACH = Sequent/ptx + + +CFLAGS = -O -DDEBUG -DUSE_DIRENT -DSVR4 -DNO_IOCTL -DUSE_FCNTL +LFLAGS = +CC = cc + +# Directory for installed binary: +BINDIR = /usr/local/bin + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +# Where is the W3 object library? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/rs6000/Makefile b/WWW/Library/rs6000/Makefile new file mode 100644 index 00000000..334a5a4a --- /dev/null +++ b/WWW/Library/rs6000/Makefile @@ -0,0 +1,29 @@ +# Make WWW under unix for rs6000 with no gcc +# + +# For W3 distribution, machine type for subdirectories +WWW_MACH = rs6000 + +# For ASIS +ASIS_MACH = ibm-rs6000/aix-3.2 + +# Directory for installed binary: +BINDIR = /usr/local/bin + +# The AIX compiler does not define unix... AIX will do it and avoid realloc bug + +CFLAGS = -O -DDEBUG +CC = cc +LFLAGS = + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = $(WWW) + +# Where is the W3 object library? +LIBDIR = /usr/local/lib + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/sco/Makefile b/WWW/Library/sco/Makefile new file mode 100644 index 00000000..a00a948b --- /dev/null +++ b/WWW/Library/sco/Makefile @@ -0,0 +1,33 @@ +# Make WWW under unix for a.n.other unix system (bsd) +# Use this as a template + +# For W3 distribution, machine type for subdirectories +WWW_MACH = sco + +# The ASIS repository's name for the machine we are on +# SCO does not presently have ranlib. Ignore the error +# message about that when the CommonMakefile tries to +# invoke it. +ASIS_MACH = intel/sco + + +CFLAGS = -O -DDEBUG -DSVR4 +LFLAGS = +CC = cc + +# Directory for installed binary: +BINDIR = /usr/local/bin + +# Where is the W3 object library to be installed (not normally done)? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +include $(WWW)/Library/Implementation/CommonMakefile + diff --git a/WWW/Library/sgi/Makefile b/WWW/Library/sgi/Makefile new file mode 100644 index 00000000..c91bdd66 --- /dev/null +++ b/WWW/Library/sgi/Makefile @@ -0,0 +1,30 @@ +# Make WWW for Silicon Graphics +# +# For W3 distribution, machine type for subdirectories +WWW_MACH = sgi + +# Architecutre in ASIS scheme +# SGI does not presently have ranlib. Ignore the error +# message about that when the CommonMakefile tries to +# invoke it. +ASIS_MACH = sgi/iris-3.5 + +CFLAGS = -DDEBUG -O +CC = cc +LFLAGS = + +# Directory for installed binary: +BINDIR = /usr/local/bin + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +# Where is the W3 object library? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/snake/Makefile b/WWW/Library/snake/Makefile new file mode 100644 index 00000000..06db3012 --- /dev/null +++ b/WWW/Library/snake/Makefile @@ -0,0 +1,33 @@ +# Make WWW under unix for HP 700 or 800 (Snake) using cc +# + +# For W3 distribution, machine type for subdirectories +WWW_MACH = snake + +# Distribution point for ASIS repository +ASIS_MACH = hp-700/hpux-8.0 + +CFLAGS = -O -DDEBUG + +# Link with BSD library for getwd() +LFLAGS = -lBSD + +#CC = cc +#CC = gcc + +# Directory for installed binary: +BINDIR = /usr/local/bin + + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +# Where is the W3 object library? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/solaris2/Makefile b/WWW/Library/solaris2/Makefile new file mode 100644 index 00000000..a390cae9 --- /dev/null +++ b/WWW/Library/solaris2/Makefile @@ -0,0 +1,29 @@ +# Make WWW under unix for sun 4 +# + +# For W3 distribution, machine type for subdirectories +WWW_MACH = solaris2 + +# The ASIS repository's name for the machine we are on +ASIS_MACH = sun-4/sunos-5.2 + + +CFLAGS = -O -DDEBUG -DNGROUPS=16 -Dd_namlen=d_reclen -DNO_BCOPY -DSOLARIS2 -DSVR4 -DUSE_DIRENT +LFLAGS = + +# Directory for installed binary: +BINDIR = /usr/local/bin + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +# Where is the W3 object library? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +include $(WWW)/Library/Implementation/CommonMakefile + diff --git a/WWW/Library/sun3/Makefile b/WWW/Library/sun3/Makefile new file mode 100644 index 00000000..ee9c6288 --- /dev/null +++ b/WWW/Library/sun3/Makefile @@ -0,0 +1,29 @@ +# Make WWW under unix for sun 3 +# +# For W3 distribution, machine type for subdirectories +WWW_MACH = sun3 + +# For ASIS installation, the ASIS code for the machine/os +ASIS_MACH = sun-3/sunos-4.1.1 + +# Directory for installed binary: +BINDIR = /usr/local/bin + +CFLAGS = -DDEBUG -O +LFLAGS = + +# Directory for installed binary: +BINDIR = /usr/local/bin + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +# Where is the W3 object library? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/sun4/Makefile b/WWW/Library/sun4/Makefile new file mode 100644 index 00000000..cf14decb --- /dev/null +++ b/WWW/Library/sun4/Makefile @@ -0,0 +1,29 @@ +# Make WWW under unix for sun 4 +# + +# For W3 distribution, machine type for subdirectories +WWW_MACH = sun4 + +# The ASIS repository's name for the machine we are on +ASIS_MACH = sun-4/sunos-4.1.1 + + +CFLAGS = -DDEBUG -O +LFLAGS = + +# Directory for installed binary: +BINDIR = /usr/local/bin + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +# Where is the W3 object library? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +include $(WWW)/Library/Implementation/CommonMakefile + diff --git a/WWW/Library/svr4/Makefile b/WWW/Library/svr4/Makefile new file mode 100644 index 00000000..a97e6656 --- /dev/null +++ b/WWW/Library/svr4/Makefile @@ -0,0 +1,29 @@ +# Make WWW under svr4 +# + +# For W3 distribution, machine type for subdirectories +WWW_MACH = svr4 + +# The ASIS repository's name for the machine we are on +ASIS_MACH = generic/svr4 + + +CFLAGS = -O -DDEBUG -DUSE_DIRENT -DSVR4 +LFLAGS = +CC = cc + +# Directory for installed binary: +BINDIR = /usr/local/bin + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +# Where is the W3 object library? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/unix/Makefile b/WWW/Library/unix/Makefile new file mode 100644 index 00000000..c8d32fd9 --- /dev/null +++ b/WWW/Library/unix/Makefile @@ -0,0 +1,30 @@ +# Make WWW under unix for a.n.other unix system (bsd) +# Use this as a template + +# For W3 distribution, machine type for subdirectories +WWW_MACH = unix + +# The ASIS repository's name for the machine we are on +ASIS_MACH = hardware/os + + +CFLAGS = -O -DDEBUG +LFLAGS = +CC = cc + +# Directory for installed binary: +BINDIR = /usr/local/bin + +# Where is the W3 object library to be installed (not normally done)? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = ../.. + + +include $(WWW)/Library/Implementation/CommonMakefile + diff --git a/WWW/Library/unix_x/Makefile b/WWW/Library/unix_x/Makefile new file mode 100644 index 00000000..23c12453 --- /dev/null +++ b/WWW/Library/unix_x/Makefile @@ -0,0 +1,491 @@ +# Makefile generated by imake - do not edit! +# $XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $ +# +# The cpp used on this machine replaces all newlines and multiple tabs and +# spaces in a macro expansion with a single space. Imake tries to compensate +# for this, but is not always successful. +# + +########################################################################### +# Makefile generated from "Imake.tmpl" and </tmp/IIf.a02602> +# $XConsortium: Imake.tmpl,v 1.77 89/12/18 17:01:37 jim Exp $ +# +# Platform-specific parameters may be set in the appropriate .cf +# configuration files. Site-wide parameters may be set in the file +# site.def. Full rebuilds are recommended if any parameters are changed. +# +# If your C preprocessor doesn't define any unique symbols, you'll need +# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing +# "make Makefile", "make Makefiles", or "make World"). +# +# If you absolutely can't get imake to work, you'll need to set the +# variables at the top of each Makefile as well as the dependencies at the +# bottom (makedepend will do this automatically). +# + +########################################################################### +# platform-specific configuration parameters - edit sun.cf to change + +# platform: $XConsortium: sun.cf,v 1.38 89/12/23 16:10:10 jim Exp $ +# operating system: SunOS 4.1.1 + +########################################################################### +# site-specific configuration parameters - edit site.def to change + +# site: $XConsortium: site.def,v 1.21 89/12/06 11:46:50 jim Exp $ + + SHELL = /bin/sh + + TOP = ../../../. + CURRENT_DIR = ./../Library/unix_x + + AR = ar cq + BOOTSTRAPCFLAGS = + CC = gcc -DNOSTDHDRS -fstrength-reduce -fpcc-struct-return -fwritable-strings -traditional + + COMPRESS = compress + CPP = /lib/cpp $(STD_CPP_DEFINES) + PREPROCESSCMD = gcc -DNOSTDHDRS -fstrength-reduce -fpcc-struct-return -fwritable-strings -traditional -E $(STD_CPP_DEFINES) + INSTALL = install + LD = ld + LINT = lint + LINTLIBFLAG = -C + LINTOPTS = -axz + LN = ln -s + MAKE = make + MV = mv + CP = cp + RANLIB = ranlib + RANLIBINSTFLAGS = + RM = rm -f + STD_INCLUDES = + STD_CPP_DEFINES = + STD_DEFINES = + EXTRA_LOAD_FLAGS = + EXTRA_LIBRARIES = + TAGS = ctags + + SHAREDCODEDEF = -DSHAREDCODE + SHLIBDEF = -DSUNSHLIB + + PROTO_DEFINES = + + INSTPGMFLAGS = + + INSTBINFLAGS = -m 0755 + INSTUIDFLAGS = -m 4755 + INSTLIBFLAGS = -m 0664 + INSTINCFLAGS = -m 0444 + INSTMANFLAGS = -m 0444 + INSTDATFLAGS = -m 0444 + INSTKMEMFLAGS = -m 4755 + + DESTDIR = + + TOP_INCLUDES = -I$(INCROOT) + + CDEBUGFLAGS = -O + CCOPTIONS = + COMPATFLAGS = + + ALLINCLUDES = $(STD_INCLUDES) $(TOP_INCLUDES) $(INCLUDES) $(EXTRA_INCLUDES) + ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(DEFINES) $(COMPATFLAGS) + CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES) + LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES) + LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES) + LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS) + LDCOMBINEFLAGS = -X -r + + MACROFILE = sun.cf + RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut + + IMAKE_DEFINES = + + IRULESRC = $(CONFIGDIR) + IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES) + + ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Imake.rules \ + $(IRULESRC)/Project.tmpl $(IRULESRC)/site.def \ + $(IRULESRC)/$(MACROFILE) $(EXTRA_ICONFIGFILES) + +########################################################################### +# X Window System Build Parameters +# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $ + +########################################################################### +# X Window System make variables; this need to be coordinated with rules +# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $ + + PATHSEP = / + USRLIBDIR = $(DESTDIR)/usr/lib + BINDIR = $(DESTDIR)/usr/bin/X11 + INCROOT = $(DESTDIR)/usr/include + BUILDINCROOT = $(TOP) + BUILDINCDIR = $(BUILDINCROOT)/X11 + BUILDINCTOP = .. + INCDIR = $(INCROOT)/X11 + ADMDIR = $(DESTDIR)/usr/adm + LIBDIR = $(USRLIBDIR)/X11 + CONFIGDIR = $(LIBDIR)/config + LINTLIBDIR = $(USRLIBDIR)/lint + + FONTDIR = $(LIBDIR)/fonts + XINITDIR = $(LIBDIR)/xinit + XDMDIR = $(LIBDIR)/xdm + AWMDIR = $(LIBDIR)/awm + TWMDIR = $(LIBDIR)/twm + GWMDIR = $(LIBDIR)/gwm + MANPATH = $(DESTDIR)/usr/man + MANSOURCEPATH = $(MANPATH)/man + MANDIR = $(MANSOURCEPATH)n + LIBMANDIR = $(MANSOURCEPATH)3 + XAPPLOADDIR = $(LIBDIR)/app-defaults + + SOXLIBREV = 4.2 + SOXTREV = 4.0 + SOXAWREV = 4.0 + SOOLDXREV = 4.0 + SOXMUREV = 4.0 + SOXEXTREV = 4.0 + + FONTCFLAGS = -t + + INSTAPPFLAGS = $(INSTDATFLAGS) + + IMAKE = imake + DEPEND = makedepend + RGB = rgb + FONTC = bdftosnf + MKFONTDIR = mkfontdir + MKDIRHIER = /bin/sh $(BINDIR)/mkdirhier.sh + + CONFIGSRC = $(TOP)/config + CLIENTSRC = $(TOP)/clients + DEMOSRC = $(TOP)/demos + LIBSRC = $(TOP)/lib + FONTSRC = $(TOP)/fonts + INCLUDESRC = $(TOP)/X11 + SERVERSRC = $(TOP)/server + UTILSRC = $(TOP)/util + SCRIPTSRC = $(UTILSRC)/scripts + EXAMPLESRC = $(TOP)/examples + CONTRIBSRC = $(TOP)/../contrib + DOCSRC = $(TOP)/doc + RGBSRC = $(TOP)/rgb + DEPENDSRC = $(UTILSRC)/makedepend + IMAKESRC = $(CONFIGSRC) + XAUTHSRC = $(LIBSRC)/Xau + XLIBSRC = $(LIBSRC)/X + XMUSRC = $(LIBSRC)/Xmu + TOOLKITSRC = $(LIBSRC)/Xt + AWIDGETSRC = $(LIBSRC)/Xaw + OLDXLIBSRC = $(LIBSRC)/oldX + XDMCPLIBSRC = $(LIBSRC)/Xdmcp + BDFTOSNFSRC = $(FONTSRC)/bdftosnf + MKFONTDIRSRC = $(FONTSRC)/mkfontdir + EXTENSIONSRC = $(TOP)/extensions + + DEPEXTENSIONLIB = $(USRLIBDIR)/libXext.a + EXTENSIONLIB = -lXext + + DEPXLIB = $(DEPEXTENSIONLIB) + XLIB = $(EXTENSIONLIB) -lX11 + + DEPXAUTHLIB = $(USRLIBDIR)/libXau.a + XAUTHLIB = -lXau + + DEPXMULIB = + XMULIB = -lXmu + + DEPOLDXLIB = + OLDXLIB = -loldX + + DEPXTOOLLIB = + XTOOLLIB = -lXt + + DEPXAWLIB = + XAWLIB = -lXaw + + LINTEXTENSIONLIB = $(USRLIBDIR)/llib-lXext.ln + LINTXLIB = $(USRLIBDIR)/llib-lX11.ln + LINTXMU = $(USRLIBDIR)/llib-lXmu.ln + LINTXTOOL = $(USRLIBDIR)/llib-lXt.ln + LINTXAW = $(USRLIBDIR)/llib-lXaw.ln + + DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB) + + DEPLIBS1 = $(DEPLIBS) + DEPLIBS2 = $(DEPLIBS) + DEPLIBS3 = $(DEPLIBS) + +########################################################################### +# Imake rules for building libraries, programs, scripts, and data files +# rules: $XConsortium: Imake.rules,v 1.67 89/12/18 17:14:15 jim Exp $ + +########################################################################### +# start of Imakefile + +# Make WWW under unix for a.n.other unix system (bsd) +# Use this as a template + +TK_WWW_SOURCE_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/Tcl + +TK_WWW_INSTALL_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/$WWW_MACH + +TK_WWW_HOME_PAGE=http://www.w3.org/default.html +TK_WWW_START_PAGE=$(TK_WWW_HOME_PAGE) + +CC = gcc -fno-builtin -Wall + +CDEBUGFLAGS = -O3 -pipe + +COMPATFLAGS = -I/afs/athena.mit.edu/course/other/cdsdev/www-compat +CCOPTIONS = + +BINDIR = $(TK_WWW_INSTALL_PATH) + +# For W3 distribution, machine type for subdirectories +WWW_MACH = unix_x + +# The ASIS repository's name for the machine we are on +ASIS_MACH = hardware/os + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = /tmp + +# Common Makefile for W3 Library Code +# ----------------------------------- +# +# (c) CERN 1990, 1991 -- see Copyright.html for conditions +# +# This file should be invariant between systems. +# DEPENDENCIES NOT COMPLETE + +# +# make Compile and link the software (private version) +# make clean Remove intermediate files + +WC = $(WWW)/Library +CMN = $(WWW)/Library/Implementation/ + +# Where shall we put the objects and built library? + +LOB = $(WTMP)/Library/$(WWW_MACH) + +# Bug: This path, if relative, is taken relative to the directory +# in which this makefile is, not the pwd. This screws up the +# recursive invocation + +VC = 2.14 + +CFLAGS2 = $(CFLAGS) -I$(CMN) + +CERNLIBBIN = $(WWW)/bin + +COMMON = $(LOB)/HTParse.o $(LOB)/HTAccess.o $(LOB)/HTTP.o \ + $(LOB)/HTBTree.o \ + $(LOB)/HTFile.o $(LOB)/HTFTP.o $(LOB)/HTTCP.o \ + $(LOB)/SGML.o $(LOB)/HTMLDTD.o $(LOB)/HTChunk.o \ + $(LOB)/HTPlain.o $(LOB)/HTWriter.o $(LOB)/HTFWriter.o \ + $(LOB)/HTMLGen.o \ + $(LOB)/HTAtom.o $(LOB)/HTAnchor.o $(LOB)/HTStyle.o \ + $(LOB)/HTList.o $(LOB)/HTString.o $(LOB)/HTAlert.o \ + $(LOB)/HTRules.o $(LOB)/HTFormat.o $(LOB)/HTInit.o $(LOB)/HTMIME.o \ + $(LOB)/HTHistory.o $(LOB)/HTNews.o $(LOB)/HTGopher.o \ + $(LOB)/HTTelnet.o $(LOB)/HTWSRC.o $(HTWAIS) + +CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTBTree.c \ + $(CMN)HTFile.c \ + $(CMN)HTFTP.c $(CMN)HTTCP.c $(CMN)SGML.c \ + $(CMN)HTMLDTD.c \ + $(CMN)HTPlain.c $(CMN)HTWriter.c $(CMN)HTFWriter.c $(CMN)HTMLGen.c \ + $(CMN)HTChunk.c $(CMN)HTAtom.c $(CMN)HTAnchor.c $(CMN)HTStyle.c \ + $(CMN)HTList.c $(CMN)HTString.c $(CMN)HTAlert.c $(CMN)HTRules.c \ + $(CMN)HTFormat.c $(CMN)HTInit.c $(CMN)HTMIME.c $(CMN)HTHistory.c \ + $(CMN)HTNews.c $(CMN)HTGopher.c $(CMN)HTTelnet.c \ + $(CMN)HTWAIS.c $(CMN)HTWSRC.c + +HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTBTree.h \ + $(CMN)HTFile.h \ + $(CMN)HTFTP.h $(CMN)HTTCP.h \ + $(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \ + $(CMN)HTPlain.h $(CMN)HTWriter.h \ + $(CMN)HTFWriter.h $(CMN)HTMLGen.h \ + $(CMN)HTStream.h \ + $(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \ + $(CMN)HTList.h \ + $(CMN)HTString.h $(CMN)HTAlert.h $(CMN)HTRules.h \ + $(CMN)HTFormat.h $(CMN)HTInit.h \ + $(CMN)HTMIME.h $(CMN)HTHistory.h $(CMN)HTNews.h \ + $(CMN)HTGopher.h \ + $(CMN)HTUtils.h $(CMN)tcp.h $(CMN)WWW.h $(CMN)HText.h \ + $(CMN)HTTelnet.h \ + $(CMN)HTWAIS.h $(CMN)HTWSRC.h + +SOURCES = $(CFILES) $(HFILES) $(CMN)Version.make $(CMN)CommonMakefile \ + $(WWW)/README.txt $(WWW)/Copyright.txt $(WWW)/BUILD +SPECIFIC = $(WWW)/All + +# Library +# +# On SGI, ranlib is unnecessary and does not exist so we ignore errors +# for that step +all: $(LOB)/libwww.a + $(MV) $(LOB)/libwww.a $(WC)/$(WWW_MACH) + +$(LOB)/libwww.a : $(COMMON) + ar r $(LOB)/libwww.a $(COMMON) + -ranlib $(LOB)/libwww.a + +# Clean up everything generatable except final products +clean :: + $(RM) $(LOB) + +# Clean up everything generatable including final products + +cleanall :: clean + $(RM) $(LOB)/libwww.a + +# Common code +# ----------- + +# Directory for object files - .created checks it exists + +OE = $(LOB)/.created +$(OE) : + -mkdir $(WTMP) + -mkdir $(WTMP)/Library + -mkdir $(WTMP)/Library/$(WWW_MACH) + touch $@ + +$(LOB)/HTList.o : $(OE) $(CMN)HTList.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTList.c + +$(LOB)/HTAnchor.o : $(OE) $(CMN)HTAnchor.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAnchor.c + +$(LOB)/HTFormat.o : $(OE) $(CMN)HTFormat.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFormat.c + +$(LOB)/HTInit.o : $(OE) $(CMN)HTInit.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTInit.c + +$(LOB)/HTMIME.o : $(OE) $(CMN)HTMIME.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMIME.c + +$(LOB)/HTHistory.o : $(OE) $(CMN)HTHistory.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTHistory.c + +$(LOB)/HTNews.o : $(OE) $(CMN)HTNews.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTNews.c + +$(LOB)/HTGopher.o : $(OE) $(CMN)HTGopher.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGopher.c + +$(LOB)/HTTelnet.o : $(OE) $(CMN)HTTelnet.c $(CMN)HTUtils.h $(CMN)HTTelnet.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTelnet.c + +$(LOB)/HTStyle.o : $(OE) $(CMN)HTStyle.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTStyle.c + +$(LOB)/HTAtom.o : $(OE) $(CMN)HTAtom.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAtom.c + +$(LOB)/HTChunk.o : $(OE) $(CMN)HTChunk.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTChunk.c + +$(LOB)/HTString.o : $(OE) $(CMN)HTString.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTString.c + +$(LOB)/HTAlert.o : $(OE) $(CMN)HTAlert.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTAlert.c + +$(LOB)/HTRules.o : $(OE) $(CMN)HTRules.c $(CMN)HTUtils.h $(CMN)Version.make + $(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTRules.c + +$(LOB)/SGML.o : $(OE) $(CMN)SGML.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)SGML.c + +$(LOB)/HTMLGen.o : $(OE) $(CMN)HTMLGen.c $(CMN)HTUtils.h $(CMN)HTMLDTD.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLGen.c + +$(LOB)/HTMLDTD.o : $(OE) $(CMN)HTMLDTD.c $(CMN)SGML.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLDTD.c + +$(LOB)/HTPlain.o : $(OE) $(CMN)HTPlain.c $(CMN)HTPlain.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPlain.c + +$(LOB)/HTWAIS.o : $(OE) $(CMN)HTWAIS.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(WAISINC) $(CMN)HTWAIS.c + +$(LOB)/HTWSRC.o : $(OE) $(CMN)HTWSRC.c $(CMN)HTUtils.h $(CMN)HTList.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWSRC.c + +$(LOB)/HTWriter.o : $(OE) $(CMN)HTWriter.c $(CMN)HTWriter.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWriter.c + +$(LOB)/HTFWriter.o : $(OE) $(CMN)HTFWriter.c $(CMN)HTFWriter.h $(CMN)HTStream.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFWriter.c + +# Communications & Files + +$(LOB)/HTTP.o : $(OE) $(CMN)HTTP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTP.c + +$(LOB)/HTTCP.o : $(OE) $(CMN)HTTCP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTCP.c + +$(LOB)/HTBTree.o : $(OE) $(CMN)HTBTree.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTBTree.c + +$(LOB)/HTFile.o : $(OE) $(CMN)HTFile.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFile.c + +$(LOB)/HTFTP.o : $(OE) $(CMN)HTFTP.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFTP.c + +$(LOB)/HTAccess.o : $(OE) $(CMN)HTAccess.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAccess.c + +$(LOB)/HTParse.o : $(OE) $(CMN)HTParse.c $(CMN)HTUtils.h + $(CC) -c -o $@ $(CFLAGS2) $(CMN)HTParse.c + +########################################################################### +# common rules for all Makefiles - do not edit + +emptyrule:: + +clean:: + $(RM_CMD) \#* + +Makefile:: + -@if [ -f Makefile ]; then \ + echo " $(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \ + $(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \ + else exit 0; fi + $(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR) + +tags:: + $(TAGS) -w *.[ch] + $(TAGS) -xw *.[ch] > TAGS + +########################################################################### +# empty rules for directories that do not have SUBDIRS - do not edit + +install:: + @echo "install in $(CURRENT_DIR) done" + +install.man:: + @echo "install.man in $(CURRENT_DIR) done" + +Makefiles:: + +includes:: + +########################################################################### +# dependencies generated by makedepend + diff --git a/WWW/Library/vax_ultrix/Makefile b/WWW/Library/vax_ultrix/Makefile new file mode 100644 index 00000000..2caf766c --- /dev/null +++ b/WWW/Library/vax_ultrix/Makefile @@ -0,0 +1,33 @@ +# Make WWW under ultrix with gcc +# + + +# For W3 distribution, machine type for subdirectories +WWW_MACH = vax_ultrix + +# For ASIS installation, the ASIS code for the machine/os +ASIS_MACH = none + +CC = gcc +CFLAGS = -O -DDEBUG -Wall +LFLAGS = -O + +# This is bug fix for out-of-date ultrix on cernvax +# LFLAGS = -O pfcode.o -lresolv + +# Directory for installed binary: +BINDIR = /usr/local/unix + + +#_________________ OK if normal W3 distribution +# Where is the WWW source root? +WWW = ../.. + +# Where should temporary (object) files go? +WTMP = /tmp + + +# Where is the W3 object library? +LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH) + +include $(WWW)/Library/Implementation/CommonMakefile diff --git a/WWW/Library/vms/COPYING.LIB b/WWW/Library/vms/COPYING.LIB new file mode 100644 index 00000000..eb685a5e --- /dev/null +++ b/WWW/Library/vms/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/WWW/Library/vms/descrip.mms b/WWW/Library/vms/descrip.mms new file mode 100644 index 00000000..b178e538 --- /dev/null +++ b/WWW/Library/vms/descrip.mms @@ -0,0 +1,258 @@ +! Make WorldWideWeb LIBRARY under VMS +! ======================================================= +! +! History: +! 14 Aug 91 (TBL) Reconstituted +! 25 Jun 92 (JFG) Added TCP socket emulation over DECnet +! 07 Sep 93 (MD) Remade for version 2.09a +! 10 Dec 93 (FM) Upgrade for version 2.14 with Lynx v2.1 +! 13 Dec 93 (FM) Added conditional compilations for VAXC vs. DECC +! (MMS can't handle a MODULE list as large as the +! WWWLibrary has become, so this just illustrates +! how you'd set it up if it could 8-). +! 26 Oct 94 (RLD) Updated to work with VAX/VMS v5.5-1 and AXP/VMS v6.1 +! 31 Oct 94 (RLD) Updated for Lynx v2.3.4, supporting OpenCMU and +! TCPWare +! 18 Nov 94 (FM) Updated for SOCKETSHR/NETLIB +! 07 Dec 94 (FM) Updated for DECC/VAX, VAXC/VAX and DECC/AXP +! 03 May 95 (FM) Include /NoMember for DECC (not the default on AXP, +! and the code assumes byte alignment). +! 07 Jul 95 (FM) Added GNUC support. +! +! Bugs: +! The dependencies are anything but complete - they were +! just enough to allow the files to be compiled. +! +! Instructions: +! Copy [WWW.LIBRARY.VMS]DESCRIP.MMS into [WWW.LIBRARY.IMPLEMENTATION] +! Use the correct command line for your TCP/IP implementation, +! inside the IMPLEMENTATION directory: +! +! $ MMS/MACRO=(MULTINET=1) for VAXC - MultiNet +! $ MMS/MACRO=(WIN_TCP=1) for VAXC - Wollongong TCP/IP +! $ MMS/MACRO=(UCX=1) for VAXC - UCX +! $ MMS/MACRO=(CMU_TCP=1) for VAXC - OpenCMU TCP/IP +! $ MMS/MACRO=(SOCKETSHR_TCP=1) for VAXC - SOCKETSHR/NETLIB +! $ MMS/MACRO=(TCPWARE=1) for VAXC - TCPWare TCP/IP +! $ MMS/MACRO=(DECNET=1) for VAXC - socket emulation over DECnet +! +! $ MMS/MACRO=(MULTINET=1,DEC_C=1) for DECC - MultiNet +! $ MMS/MACRO=(WIN_TCP=1,DEC_C=1) for DECC - Wollongong TCP/IP +! $ MMS/MACRO=(UCX=1,DEC_C=1) for DECC - UCX +! $ MMS/MACRO=(CMU_TCP=1,DEC_C=1) for DECC - OpenCMU TCP/IP +! $ MMS/MACRO=(SOCKETSHR_TCP=1,DEC_C=1) for DECC - SOCKETSHR/NETLIB +! $ MMS/MACRO=(TCPWARE=1,DEC_C=1) for DECC - TCPWare TCP/IP +! $ MMS/MACRO=(DECNET=1,DEC_C=1) for DECC - socket emulation over DECnet +! +! $ MMS/MACRO=(MULTINET=1,GNU_C=1) for GNUC - MultiNet +! $ MMS/MACRO=(WIN_TCP=1,GNU_C=1) for GNUC - Wollongong TCP/IP +! $ MMS/MACRO=(UCX=1,GNU_C=1) for GNUC - UCX +! $ MMS/MACRO=(CMU_TCP=1,GNU_C=1) for GNUC - OpenCMU TCP/IP +! $ MMS/MACRO=(SOCKETSHR_TCP=1,GNU_C=1) for GNUC - SOCKETSHR/NETLIB +! $ MMS/MACRO=(TCPWARE=1,GNU_C=1) for GNUC - TCPWare TCP/IP +! $ MMS/MACRO=(DECNET=1,GNU_C=1) for GNUC - socket emulation over DECnet +! +! To compile with debug mode: +! +! $ MMS/MACRO=(MULTINET=1, DEBUG=1) for Multinet +! +! +! If you are on HEP net and want to build using the really latest sources on +! PRIAM:: then define an extra macro U=PRIAM::, e.g. +! +! $ MMS/MACRO=(MULTINET=1, U=PRIAM::) for Multinet +! +! This will copy the sources from PRIAM as necessary. You can also try +! +! $ MMS/MACRO=(U=PRIAM::) descrip.mms +! +! to update this file. + + +.include Version.make + +! debug flags +.ifdef DEBUG +DEBUGFLAGS = /Debug /NoOptimize +.endif + +! defines valid for all compilations +EXTRADEFINES = DEBUG, ACCESS_AUTH, VC="""$(VC)""" + +! DECC flags for all compilations +.ifdef DEC_C +DCFLAGS = /NoMember /Warning=(disable=implicitfunc) +.endif + +.ifdef UCX +TCP = UCX +.ifdef DEC_C +CFLAGS = /decc/Prefix=All $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), UCX) +.else +CFLAGS = $(DEBUGFLAGS) /Define=($(EXTRADEFINES), UCX) +.endif +.endif + +.ifdef TCPWARE +TCP = TCPWARE +.ifdef DEC_C +CFLAGS = /decc/Prefix=All $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), UCX, TCPWARE) +.else +CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), UCX, TCPWARE) +.endif +.endif + +.ifdef MULTINET +TCP = MULTINET +.ifdef DEC_C +CFLAGS = /decc/Prefix=ANSI $(DEBUGFLAGS) $(DCFLAGS) /Define=(_DECC_V4_SOURCE, __SOCKET_TYPEDEFS, $(EXTRADEFINES), MULTINET) +.else +CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), MULTINET) +.endif +.endif + +.ifdef WIN_TCP +TCP = WIN_TCP +.ifdef DEC_C +CFLAGS = /decc/Prefix=ANSI $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), WIN_TCP) +.else +CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), WIN_TCP) +.endif +.endif + +.ifdef CMU_TCP +TCP = CMU_TCP +.ifdef DEC_C +CFLAGS = /decc/Prefix=ANSI $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), CMU_TCP) +.else +CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), CMU_TCP) +.endif +.endif + +.ifdef SOCKETSHR_TCP +TCP = SOCKETSHR_TCP +.ifdef DEC_C +CFLAGS = /decc/Prefix=ANSI $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), SOCKETSHR_TCP) +.else +CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), SOCKETSHR_TCP) +.endif +.endif + +.ifdef DECNET +TCP = DECNET +.ifdef DEC_C +CFLAGS = /decc/Prefix=All $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), DECNET) +.else +CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), DECNET) +.endif +.endif + +.ifdef TCP +.else +TCP = MULTINET ! (Default to MULTINET) +.ifdef DEC_C +CFLAGS = /decc/Prefix=ANSI $(DEBUGFLAGS) $(DCFLAGS) /Define=(_DECC_V4_SOURCE, __SOCKET_TYPEDEFS, $(EXTRADEFINES), MULTINET) +.else +CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), MULTINET) +.endif +.endif + +.ifdef GNU_C +CC = gcc +.endif + +!HEADERS = HTUtils.h, HTStream.h, tcp.h, HText.h - +! HTParse.h, HTAccess.h, HTTP.h, HTFile.h, - +! HTBTree.h, HTTCP.h, SGML.h, - +! HTML.h, HTMLDTD.h, HTChunk.h, HTPlain.h, - +! HTWriter.h, HTFwriter.h, HTMLGen.h, - +! HTAtom.h, HTAnchor.h, HTStyle.h, - +! HTList.h, HTString.h, HTAlert.h, - +! HTRules.h, HTFormat.h, HTInit.h, - +! HTMIME.h, HTHistory.h, HTTelnet.h, - +! HTFinger.h, HTAABrow.h, HTAAFile.h, - +! HTAAProt.h, HTAAServ.h, HTAAUtil.h, - +! HTAssoc.h, HTPasswd.h, HTAuth.h, HTUU.h, - +! HTVMSUtils.h, ufc-crypt.h, patchlevel.h + +MODULES = HTParse, HTAccess, HTTP, HTFile, HTBTree, HTFTP, HTTCP, HTString, - + SGML, HTMLDTD, HTChunk, HTPlain, HTWriter, HTFWriter, HTMLGen, - + HTAtom, HTAnchor, HTStyle, HTList, HTAlert, HTRules, HTFormat, - + HTInit, HTMIME, HTHistory, HTNews, HTGopher, HTTelnet, HTFinger, - + HTWSRC, HTAAUtil, HTAABrow, HTAAServ, HTAAFile, HTPasswd, HTGroup, - + HTACL, HTAuth, HTAAProt, HTAssoc, HTLex, HTUU, HTVMSUtils, getpass, - + getline, crypt, crypt_util, HTWAIS, HTVMS_WaisUI, HTVMS_WaisProt + +!.ifdef DECNET ! Strip FTP, Gopher, News, WAIS +!HEADERS = $(COMMON_HEADERS) +!MODULES = $(COMMON_MODULES) +!.else +!HEADERS = $(COMMON_HEADERS), $(EXTRA_HEADERS), $(WAIS_HEADER) +!MODULES = $(COMMON_MODULES), $(EXTRA_MODULES), $(WAIS_MODULE) +!.endif + +!___________________________________________________________________ +! WWW Library + +!library : $(HEADERS) wwwlib_$(TCP)($(MODULES)) +library : wwwlib_$(TCP)($(MODULES)) + @ Continue + +build_$(TCP).com : descrip.mms + $(MMS) /NoAction /From_Sources /Output = Build_$(TCP).com /Macro = ($(TCP)=1) + +clean : + - Set Protection = (Owner:RWED) *.*;-1 + - Purge /NoLog /NoConfirm + - Delete /NoLog /NoConfirm *.obj;,*.olb; + +!___________________________________________________________________ +! Simple Dependencies + + +!HTString.obj : HTString.c HTString.h tcp.h Version.make HTUtils.h +!HTAtom.obj : HTAtom.c HTAtom.h HTUtils.h HTString.h +!HTChunk.obj : HTChunk.c HTChunk.h HTUtils.h +!HTList.obj : HTList.c HTList.h HTUtils.h +!HTBTree.obj : HTBTree.c HTBTree.h HTUtils.h +!HTMLDTD.obj : HTMLDTD.c HTMLDTD.h SGML.h +!HTPlain.obj : HTPlain.c HTPlain.h HTStream.h +!HTWriter.obj : HTWriter.c HTWriter.h HTStream.h +!HTFWriter.obj : HTFWriter.c HTFWriter.h HTStream.h +!HTMLGen.obj : HTMLGen.c HTMLGen.h HTUtils.h HTMLDTD.h +!HTAlert.obj : HTAlert.c HTAlert.h HTUtils.h Version.make +!HTRules.obj : HTRules.c HTRules.h HTUtils.h Version.make +!HTInit.obj : HTInit.c HTInit.h HTUtils.h HTList.h +!HTMIME.obj : HTMIME.c HTMIME.h HTUtils.h HTList.h +!HTTelnet.obj : HTTelnet.c HTTelnet.h HTUtils.h +!HTWAIS.obj : HTWAIS.c HTWAIS.h HTUtils.h HTList.h +!HTWSRC.obj : HTWSRC.c HTWSRC.h HTUtils.h HTList.h +!HTAccess.obj : HTAccess.c HTAccess.h HTUtils.h +!HTAnchor.obj : HTAnchor.c HTAnchor.h HTUtils.h HTList.h +!HTFile.obj : HTFile.c HTFile.h HTUtils.h HTVMSUtils.h +!HTFormat.obj : HTFormat.c HTFormat.h HTUtils.h HTML.h SGML.h HTPlain.h HTMLGen.h HTList.h +!HTFTP.obj : HTFTP.c HTFTP.h HTUtils.h +!HTGopher.obj : HTGopher.c HTGopher.h HTUtils.h HTList.h +!HTFinger.obj : HTFinger.c HTFinger.h HTUtils.h HTList.h +!HTHistory.obj : HTHistory.c HTHistory.h HTUtils.h HTList.h +!HTNews.obj : HTNews.c HTNews.h HTUtils.h HTList.h +!HTParse.obj : HTParse.c HTParse.h HTUtils.h +!HTStyle.obj : HTStyle.c HTStyle.h HTUtils.h +!HTTCP.obj : HTTCP.c HTTCP.h HTUtils.h tcp.h +!HTTP.obj : HTTP.c HTTP.h HTUtils.h +!SGML.obj : SGML.c SGML.h HTUtils.h +!HTAABrow.obj : HTAABrow.c HTUtils.h +!HTAAFile.obj : HTAAFile.c HTUtils.h +!HTAAProt.obj : HTAAProt.c HTUtils.h +!HTAAServ.obj : HTAAServ.c HTUtils.h +!HTAAUtil.obj : HTAAUtil.c HTUtils.h +!HTACL.obj : HTACL.c HTUtils.h +!HTGroup.obj : HTGroup.c HTUtils.h +!HTLex.obj : HTLex.c HTUtils.h +!HTAssoc.obj : HTAssoc.c HTAssoc.h HTAAUtil.h HTString.h +!HTPasswd.obj : HTPasswd.c HTPasswd.h HTUtils.h HTAAUtil.h HTFile.h tcp.h +!HTAuth.obj : HTAuth.c HTAuth.h HTUtils.h HTPasswd.h HTAssoc.h HTUU.h +!HTUU.obj : HTUU.c HTUU.h HTUtils.h +!crypt.obj : crypt.c ufc-crypt.h +!HTVMSUtils.obj : HTVMSUtils.c HTVMSUtils.h HTUtils.h +!crypt_util.obj : crypt_util.c ufc-crypt.h patchlevel.h diff --git a/WWW/Library/vms/libmake.com b/WWW/Library/vms/libmake.com new file mode 100644 index 00000000..75cb23de --- /dev/null +++ b/WWW/Library/vms/libmake.com @@ -0,0 +1,186 @@ +$ v = 'f$verify(0)' +$! LIBMAKE.COM +$! +$! Command file to build the WWWLibrary on VMS systems. +$! +$! 26-Jul-1995 F.Macrides macrides@sci.wfeb.edu +$! Adding support for GNUC. +$! 03-May-1995 F.Macrides macrides@sci.wfeb.edu +$! Include /nomember for compilations with DECC. It's not the +$! default on AXP and the code assumes byte alignment. +$! 07-Dec-1994 F.Macrides macrides@sci.wfeb.edu +$! Updated for DECC/VAX, VAXC/VAX and DECC/AXP +$! 03-NOV-1994 A.Harper A.Harper@kcl.ac.uk +$! Mods to support SOCKETSHR/NETLIB and add a /DEBUG/NOOPT option +$! 02-Jun-1994 F.Macrides macrides@sci.wfeb.edu +$! Mods to support TCPWare (To use non-blocking connects, you need +$! the DRIVERS_V405B.INC patch from FTP.PROCESS.COM for TCPware for +$! OpenVMS Version 4.0-5, or a higher version of TCPWare, which will +$! have that bug in the TCPDRIVER fixed. Otherwise, add NO_IOCTL to +$! the /define=(...) list.) +$! 20-May-1994 Andy Harper A.Harper@bay.cc.kcl.ac.uk +$! Added support for the CMU TCP/IP transport +$! 13-Dec-1993 F.Macrides macrides@sci.wfeb.edu +$! Mods for conditional compilations with VAXC versus DECC +$! 10-Dec-1993 F.Macrides macrides@sci.wfeb.edu +$! Initial version, for WWWLibrary v2.14 with Lynx v2.1 +$! +$ ON CONTROL_Y THEN GOTO CLEANUP +$ ON ERROR THEN GOTO CLEANUP +$ agent = 0 +$ extra = "" +$ IF P1 .EQS. "" +$ THEN +$ write sys$output "Acceptable TCP/IP agents are" +$ write sys$output " [1] MultiNet (default)" +$ write sys$output " [2] UCX" +$ write sys$output " [3] WIN_TCP" +$ write sys$output " [4] CMU_TCP" +$ write sys$output " [5] SOCKETSHR_TCP" +$ write sys$output " [6] TCPWARE" +$ write sys$output " [7] DECNET" +$ read sys$command/prompt="Agent [1,2,3,4,5,6,7] (RETURN = [1]) " agent +$ ENDIF +$ if agent .eq. 1 .or. agent .eqs. "" .or. p1 .eqs. "MULTINET" then - + transport = "MULTINET" +$ if agent .eq. 2 .or. p1 .eqs. "UCX" then transport = "UCX" +$ if agent .eq. 3 .or. p1 .eqs. "WIN_TCP" then transport = "WIN_TCP" +$ if agent .eq. 4 .or. p1 .eqs. "CMU_TCP" then transport = "CMU_TCP" +$ if agent .eq. 5 .or. p1 .eqs. "SOCKETSHR_TCP" then transport = "SOCKETSHR_TCP" +$ if agent .eq. 6 .or. p1 .eqs. "TCPWARE" then transport = "TCPWARE" +$ if agent .eq. 7 .or. p1 .eqs. "DECNET" then transport = "DECNET" +$! +$ if transport .eqs. "TCPWARE" then extra = ",UCX" +$! +$ cc_opts = "" +$ if p2 .nes. "" then cc_opts = "/DEBUG/NOOPT" +$! +$ IF f$trnlnm("VAXCMSG") .eqs. "DECC$MSG" .or. - + f$trnlnm("DECC$CC_DEFAULT") .eqs. "/DECC" .or. - + f$trnlnm("DECC$CC_DEFAULT") .eqs. "/VAXC" +$ THEN +$ v1 = f$verify(1) +$! DECC: +$ v1 = 'f$verify(0)' +$ If transport .eqs. "UCX" .or. transport .eqs. "TCPWARE" +$ Then +$ v1 = f$verify(1) +$! +$ cc/decc/prefix=all /nomember 'cc_opts'- + /warning=(disable=implicitfunc)- + /DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra',VC="""2.14""")- + /INCLUDE=([-.Implementation]) - + [-.Implementation]HTString.c +$! +$ cc := cc/decc/prefix=all /nomember 'cc_opts'- + /warning=(disable=implicitfunc)- + /DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra')- + /INCLUDE=([-.Implementation]) +$! +$ v1 = 'f$verify(0)' +$ Else +$ if transport .eqs. "MULTINET" then - + extra = ",_DECC_V4_SOURCE,__SOCKET_TYPEDEFS" +$ v1 = f$verify(1) +$! +$ cc/decc/prefix=ansi /nomember 'cc_opts'- + /warning=(disable=implicitfunc)- + /DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra',VC="""2.14""")- + /INCLUDE=([-.Implementation]) - + [-.Implementation]HTString.c +$! +$ cc := cc/decc/prefix=ansi /nomember 'cc_opts'- + /warning=(disable=implicitfunc)- + /DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra')- + /INCLUDE=([-.Implementation]) +$! +$ v1 = 'f$verify(0)' +$ EndIf +$ ELSE +$ IF f$search("gnu_cc:[000000]gcclib.olb") .nes. "" +$ THEN +$ v1 = f$verify(1) +$! GNUC: +$! +$ gcc/DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra',VC="""2.14""") 'cc_opts'- + /INCLUDE=([-.Implementation]) - + [-.Implementation]HTString.c +$! +$ cc := gcc/DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra') 'cc_opts'- + /INCLUDE=([-.Implementation]) +$! +$ v1 = 'f$verify(0)' +$ ELSE +$ v1 = f$verify(1) +$! VAXC: +$! +$ cc/DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra',VC="""2.14""") 'cc_opts'- + /INCLUDE=([-.Implementation]) - + [-.Implementation]HTString.c +$! +$ cc := cc/DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra') 'cc_opts'- + /INCLUDE=([-.Implementation]) +$! +$ v1 = 'f$verify(0)' +$ ENDIF +$ ENDIF +$ v1 = f$verify(1) +$ cc [-.Implementation]HTParse.c +$ cc [-.Implementation]HTAccess.c +$ cc [-.Implementation]HTTP.c +$ cc [-.Implementation]HTFile.c +$ cc [-.Implementation]HTBTree.c +$ cc [-.Implementation]HTFTP.c +$ cc [-.Implementation]HTTCP.c +$ cc [-.Implementation]SGML.c +$ cc [-.Implementation]HTMLDTD.c +$ cc [-.Implementation]HTChunk.c +$ cc [-.Implementation]HTPlain.c +$ cc [-.Implementation]HTWriter.c +$ cc [-.Implementation]HTFWriter.c +$ cc [-.Implementation]HTMLGen.c +$ cc [-.Implementation]HTAtom.c +$ cc [-.Implementation]HTAnchor.c +$ cc [-.Implementation]HTStyle.c +$ cc [-.Implementation]HTList.c +$ cc [-.Implementation]HTAlert.c +$ cc [-.Implementation]HTRules.c +$ cc [-.Implementation]HTFormat.c +$ cc [-.Implementation]HTInit.c +$ cc [-.Implementation]HTMIME.c +$ cc [-.Implementation]HTHistory.c +$ cc [-.Implementation]HTNews.c +$ cc [-.Implementation]HTGopher.c +$ cc [-.Implementation]HTTelnet.c +$ cc [-.Implementation]HTFinger.c +$ cc [-.Implementation]HTWSRC.c +$ cc [-.Implementation]HTAAUtil.c +$ cc [-.Implementation]HTAABrow.c +$ cc [-.Implementation]HTAAServ.c +$ cc [-.Implementation]HTAAFile.c +$ cc [-.Implementation]HTPasswd.c +$ cc [-.Implementation]HTGroup.c +$ cc [-.Implementation]HTACL.c +$ cc [-.Implementation]HTAuth.c +$ cc [-.Implementation]HTAAProt.c +$ cc [-.Implementation]HTAssoc.c +$ cc [-.Implementation]HTLex.c +$ cc [-.Implementation]HTUU.c +$ cc [-.Implementation]HTVMSUtils.c +$ cc [-.Implementation]getpass.c +$ cc [-.Implementation]getline.c +$ cc [-.Implementation]crypt.c +$ cc [-.Implementation]crypt_util.c +$ cc [-.Implementation]HTWAIS.c +$ cc [-.Implementation]HTVMS_WaisUI.c +$ cc [-.Implementation]HTVMS_WaisProt.c +$! +$ If f$search("[-.Implementation]WWWLib_''transport'.olb") .eqs. "" Then - + LIBRARY/Create [-.Implementation]WWWLib_'transport'.olb +$ LIBRARY/Replace [-.Implementation]WWWLib_'transport'.olb *.obj +$ Delete/nolog/noconf *.obj;* +$! +$ v1 = 'f$verify(0)' +$ CLEANUP: +$ v1 = f$verify(v) +$exit diff --git a/WWW/Makefile b/WWW/Makefile new file mode 100644 index 00000000..41a90056 --- /dev/null +++ b/WWW/Makefile @@ -0,0 +1,9 @@ +# Make basic WWW distribution +# +# See the README and the documentation on the web. +# When you have done BUILD you will have www so you will be able to +# read the documentation online. +# +all : + BUILD + diff --git a/WWW/README.txt b/WWW/README.txt new file mode 100644 index 00000000..ad5d8bee --- /dev/null +++ b/WWW/README.txt @@ -0,0 +1,208 @@ + Read Me + WORLDWIDEWEB CERN-DISTRIBUTED CODE + + See the CERN copyright[1] . This is the README file which you get when you + unwrap one of our tar files. These files contain information about + hypertext, hypertext systems, and the WorldWideWeb project. If you have + taken this with a .tar file, you will have only a subset of the files. + + THIS FILE IS A VERY ABRIDGED VERSION OF THE INFORMATION AVAILABLE ON THE + WEB. IF IN DOUBT, READ THE WEB DIRECTLY. If you have not got ANY browser + installed yet, do this by telnet to info.cern.ch (no username or password). + + Files from info.cern.ch are also mirrored on ftp.ripe.net. + +Archive Directory structure + + Under /pub/www[2] , besides this README file, you'll find bin[3] , src[4] + and doc[5] directories. The main archives are as follows: + + bin/xxx/bbbb Executable binaries of program bbbb for system xxx. + Check what's there before you bother compiling. (Note + HP700/8800 series is "snake") + + bin/next/WorldWideWeb_v.vv.tar.Z + The Hypertext Browser/editor for the NeXT -- binary. + + src/WWWLibrary_v.vv.tar.Z + The W3 Library. All source, and Makefiles for + selected systems. + + src/WWWLineMode_v.vv.tar.Z + The Line mode browser - all source, and Makefiles for + selected systems. Requires the Library[6] . + + src/WWWDaemon_v.vv.tar.Z + The HTTP daemon, and WWW-WAIS gateway programs. + Source. Requires the Library. + + src/WWWMailRobot_v.vv.tar.Z + The Mail Robot. + + doc/WWWBook.tar.Z A snapshot of our internal documentation - we prefer + you to access this on line -- see warnings below. + +Basic WWW software installation from source + + This applies to the line mode client and the server. Below, $prod means + LineMode or Daemon depending on which you are building. + + GENERATED DIRECTORY STRUCTURE + + The tar files are all designed to be unwrapped in the same (this) directory. + They create different parts of a common directory tree under that directory. + There may be some duplication. They also generate a few files in this + directory: README.*, Copyright.*, and some installation instructions (.txt). + + The directory structure is, for product $prod and machine $WWW_MACH + + WWW/$prod/Implementation + Source files for a given product + + WWW/$prod/Implementation/CommonMakefile + The machine-independent parts of the Makefile for this + product + + + Read Me (65/66) + WWW/$prod/$WWW_MACH/ Area for compiling for a given system + + WWW/All/$WWW_MACH/Makefile.include + The machine-dependent parts of the makefile for any + product + + WWW/All/Implementation/Makefile.product + A makefile which includes both parts above and so can + be used from any product, any machine. + + COMPILATION ON ALREADY SUPPORTED PLATFORMS + + You must get the WWWLibrary tar file as well as the products you want and + unwrap them all from the same directory. + + You must define the environmant variable WWW_MACH to be the architecure of + your machine (sun4, decstation, rs6000, sgi, snake, etc) + + In directory WWW, type BUILD. + + COMPILATION ON NEW PLATFORMS + + If your machine is not on the list: + + Make up a new subdirectory of that name under WWW/$prod and WWW/All, + copying the contents of a basically similar architecture's directory. + + Check the WWW/All/$WWW_MACH/Makefile.include for suitable directory and + flag definitions. + + Check the file tcp.h for the system-specific include file coordinates, + etc. + + Send any changes you have to make back to www-request@info.cern.ch for + inclusion into future releases. + + Once you have this set up, type BUILD. + +NeXTStep Browser/Editor + + The browser for the NeXT is those files contained in the application + directory WWW/Next/Implementation/WorldWideWeb.app and is compiled. When you + install the app, you may want to configure the default page, + WorldWideWeb.app/default.html. These must point to some useful information! + You should keep it up to date with pointers to info on your site and + elsewhere. If you use the CERN home page note there is a link at the bottom + to the master copy on our server. You should set up the address of your + local news server with + + dwrite WorldWideWeb NewsHost news + + replacing the last word with the actual address of your news host. See + Installation instructions[7] . + +Line Mode browser + + Binaries of this for some systems are available in /pub/www/bin/ . The + binaries can be picked up, set executable, and run immediately. + + If there is no binary, see "Installation from source" above. + + (See Installation notes[8] ). Do the same thing (in the same directory) to + the WWWLibrary_v.cc.tar.Z file to get the common library. + + + Read Me (65/130) + You will have an ASCII printable manual in the file + WWW/LineMode/Defaults/line-mode-guide.txt which you can print out at this + stage. This is a frozen copy of some of the online documentation. + + Whe you install the browser, you may configure a default page. This is + /usr/local/lib/WWW/default.html for the line mode browser. This must point + to some useful information! You should keep it up to date with pointers to + info on your site and elsewhere. If you use the CERN home page note there is + a link at the bottom to the master copy on our server. + + Some basic documentation on the browser is delivered with the home page in + the directory WWW/LineMode/Defaults. A separate tar file of that directory + (WWWLineModeDefaults.tar.Z) is available if you just want to update that. + + The rest of the documentation is in hypertext, and so wil be readable most + easily with a browser. We suggest that after installing the browser, you + browse through the basic documentation so that you are aware of the options + and customisation possibilities for example. + +Server + + The server can be run very simply under the internet daemon, to export a + file directory tree as a browsable hypertext tree. Binaries are avilable + for some platofrms, otherwise follow instructions above for compiling and + then go on to " Installing the basic W3 server[9] ". + +XMosaic + + XMosaic is an X11/Motif W3 browser. + + The sources and binaries are distributed separately from + FTP.NCSA.UIUC.EDU[10] , in /Web/xmosaic[11] . Binaries are available for + some platforms. If you have to build from source, check the README in the + distribution. + + The binaries can be picked up, uncompressed, set "executable" and run + immediately. + +Viola browser for X11 + + Viola is an X11 application for reading global hypertext. If a binary is + available from your machine, in /pub/www/bin/.../viola*, then take that and + also the Viola "apps" tar file which contains the scripts you will need. + + To generate this from source, you will need both the W3 library and the + Viola source files. There is an Imakefile with the viola source directory. + You will need to generate the XPA and XPM libraries and the W3 library + befere you make viola itself. + +Documentation + + In the /pub/www/doc[12] directory are a number articles, preprints and + guides on the web. + + See the online WWW bibliography[13] for a list of these and other articles, + books, etc. and also the list of WWW Manuals[14] available in text and + postscript form. + +General + + Your comments will of course be most appreciated, on code, or information on + the web which is out of date or misleading. If you write your own hypertext + and make it available by anonymous ftp or using a server, tell us and we'll + put some pointers to it in ours. Thus spreads the web... + + Read Me (66/195) + Tim Berners-Lee + + WorldWideWeb project + + CERN, 1211 Geneva 23, Switzerland + + Tel: +41 22 767 3755; Fax: +41 22 767 7155; email: timbl@info.cern.ch + + |