Initial revision
authorjbj <devnull@localhost>
Fri, 28 Feb 2003 19:23:24 +0000 (19:23 +0000)
committerjbj <devnull@localhost>
Fri, 28 Feb 2003 19:23:24 +0000 (19:23 +0000)
CVS patchset: 6073
CVS date: 2003/02/28 19:23:24

263 files changed:
neon/.cvsignore [new file with mode: 0644]
neon/.package [new file with mode: 0644]
neon/.release.sh [new file with mode: 0755]
neon/AUTHORS [new file with mode: 0644]
neon/BUGS [new file with mode: 0644]
neon/ChangeLog [new file with mode: 0644]
neon/INSTALL.win32 [new file with mode: 0644]
neon/Makefile.in [new file with mode: 0644]
neon/NEWS [new file with mode: 0644]
neon/README [new file with mode: 0644]
neon/THANKS [new file with mode: 0644]
neon/TODO [new file with mode: 0644]
neon/autogen.sh [new file with mode: 0644]
neon/config.guess [new file with mode: 0755]
neon/config.hw [new file with mode: 0644]
neon/config.hw.in [new file with mode: 0644]
neon/config.sub [new file with mode: 0755]
neon/configure.in [new file with mode: 0644]
neon/doc/.cvsignore [new file with mode: 0644]
neon/doc/TODO [new file with mode: 0644]
neon/doc/html.xsl [new file with mode: 0644]
neon/doc/man.xsl [new file with mode: 0644]
neon/doc/manual.css [new file with mode: 0644]
neon/doc/manual.xml [new file with mode: 0644]
neon/doc/parsing-xml.txt [new file with mode: 0644]
neon/doc/ref/alloc.xml [new file with mode: 0644]
neon/doc/ref/auth.xml [new file with mode: 0644]
neon/doc/ref/buf.xml [new file with mode: 0644]
neon/doc/ref/bufapp.xml [new file with mode: 0644]
neon/doc/ref/bufcr.xml [new file with mode: 0644]
neon/doc/ref/bufdest.xml [new file with mode: 0644]
neon/doc/ref/bufutil.xml [new file with mode: 0644]
neon/doc/ref/err.xml [new file with mode: 0644]
neon/doc/ref/getst.xml [new file with mode: 0644]
neon/doc/ref/init.xml [new file with mode: 0644]
neon/doc/ref/neon.xml [new file with mode: 0644]
neon/doc/ref/opts.xml [new file with mode: 0644]
neon/doc/ref/req.xml [new file with mode: 0644]
neon/doc/ref/reqbody.xml [new file with mode: 0644]
neon/doc/ref/reqhdr.xml [new file with mode: 0644]
neon/doc/ref/resolve.xml [new file with mode: 0644]
neon/doc/ref/sess.xml [new file with mode: 0644]
neon/doc/ref/shave.xml [new file with mode: 0644]
neon/doc/ref/sslca.xml [new file with mode: 0644]
neon/doc/ref/sslcert.xml [new file with mode: 0644]
neon/doc/ref/ssldname.xml [new file with mode: 0644]
neon/doc/ref/sslvfy.xml [new file with mode: 0644]
neon/doc/ref/status.xml [new file with mode: 0644]
neon/doc/ref/tok.xml [new file with mode: 0644]
neon/doc/ref/vers.xml [new file with mode: 0644]
neon/doc/refentry.xml [new file with mode: 0644]
neon/doc/using-neon.txt [new file with mode: 0644]
neon/doc/xref-man.xsl [new file with mode: 0644]
neon/example/.cvsignore [new file with mode: 0644]
neon/example/nbrowse.glade [new file with mode: 0644]
neon/example/nbrowse/callbacks.c [new file with mode: 0644]
neon/example/nbrowse/callbacks.h [new file with mode: 0644]
neon/example/nbrowse/interface.c [new file with mode: 0644]
neon/example/nbrowse/interface.h [new file with mode: 0644]
neon/example/nbrowse/main.c [new file with mode: 0644]
neon/example/nbrowse/support.c [new file with mode: 0644]
neon/example/nbrowse/support.h [new file with mode: 0644]
neon/example/nget.c [new file with mode: 0644]
neon/example/nserver.c [new file with mode: 0644]
neon/install-sh [new file with mode: 0755]
neon/lib/basename.c [new file with mode: 0644]
neon/lib/basename.h [new file with mode: 0644]
neon/lib/dirname.c [new file with mode: 0644]
neon/lib/dirname.h [new file with mode: 0644]
neon/lib/fnmatch.c [new file with mode: 0644]
neon/lib/fnmatch.h [new file with mode: 0644]
neon/lib/getopt.c [new file with mode: 0644]
neon/lib/getopt.h [new file with mode: 0644]
neon/lib/getopt1.c [new file with mode: 0644]
neon/lib/linelen.c [new file with mode: 0644]
neon/lib/netrc.c [new file with mode: 0644]
neon/lib/netrc.h [new file with mode: 0644]
neon/lib/rpmatch.c [new file with mode: 0644]
neon/lib/snprintf.c [new file with mode: 0644]
neon/lib/snprintf.h [new file with mode: 0644]
neon/lib/strcasecmp.c [new file with mode: 0644]
neon/lib/yesno.c [new file with mode: 0644]
neon/ltconfig [new file with mode: 0755]
neon/ltmain.sh [new file with mode: 0644]
neon/macros/ChangeLog [new file with mode: 0644]
neon/macros/ac_c_bigendian_cross.m4 [new file with mode: 0644]
neon/macros/acconfig.h [new file with mode: 0644]
neon/macros/aclocal-include.m4 [new file with mode: 0644]
neon/macros/compiler-flags.m4 [new file with mode: 0644]
neon/macros/gnome-common.m4 [new file with mode: 0644]
neon/macros/gnome-gnorba-check.m4 [new file with mode: 0644]
neon/macros/gnome-orbit-check.m4 [new file with mode: 0644]
neon/macros/gnome-print-check.m4 [new file with mode: 0644]
neon/macros/gnome-pthread-check.m4 [new file with mode: 0644]
neon/macros/gnome-support.m4 [new file with mode: 0644]
neon/macros/gnome-x-checks.m4 [new file with mode: 0644]
neon/macros/gnome.m4 [new file with mode: 0644]
neon/macros/neon-checks.m4 [new file with mode: 0644]
neon/macros/neon-debug.m4 [new file with mode: 0644]
neon/macros/neon-socks.m4 [new file with mode: 0644]
neon/macros/neon-ssl.m4 [new file with mode: 0644]
neon/macros/neon-test.m4 [new file with mode: 0644]
neon/macros/neon-warnings.m4 [new file with mode: 0644]
neon/macros/neon-xml-parser.m4 [new file with mode: 0644]
neon/macros/neon.m4 [new file with mode: 0644]
neon/macros/readline.m4 [new file with mode: 0644]
neon/macros/socklen-arg-type.m4 [new file with mode: 0644]
neon/macros/strftime.m4 [new file with mode: 0644]
neon/neon-config.in [new file with mode: 0644]
neon/neon.dsp [new file with mode: 0644]
neon/neon.dsw [new file with mode: 0644]
neon/neon.mak [new file with mode: 0644]
neon/src/.cvsignore [new file with mode: 0644]
neon/src/COPYING.LIB [new file with mode: 0644]
neon/src/ChangeLog [new file with mode: 0644]
neon/src/Makefile.in [new file with mode: 0644]
neon/src/Makefile.incl [new file with mode: 0644]
neon/src/README [new file with mode: 0644]
neon/src/TODO [new file with mode: 0644]
neon/src/base64.c [new file with mode: 0644]
neon/src/base64.h [new file with mode: 0644]
neon/src/dates.c [new file with mode: 0644]
neon/src/dates.h [new file with mode: 0644]
neon/src/dav_207.c [new file with mode: 0644]
neon/src/dav_207.h [new file with mode: 0644]
neon/src/dav_basic.c [new file with mode: 0644]
neon/src/dav_basic.h [new file with mode: 0644]
neon/src/dav_locks.c [new file with mode: 0644]
neon/src/dav_locks.h [new file with mode: 0644]
neon/src/dav_props.c [new file with mode: 0644]
neon/src/dav_props.h [new file with mode: 0644]
neon/src/hip_xml.c [new file with mode: 0644]
neon/src/hip_xml.h [new file with mode: 0644]
neon/src/http_auth.c [new file with mode: 0644]
neon/src/http_auth.h [new file with mode: 0644]
neon/src/http_basic.c [new file with mode: 0644]
neon/src/http_basic.h [new file with mode: 0644]
neon/src/http_cookies.c [new file with mode: 0644]
neon/src/http_cookies.h [new file with mode: 0644]
neon/src/http_private.h [new file with mode: 0644]
neon/src/http_redirect.c [new file with mode: 0644]
neon/src/http_redirect.h [new file with mode: 0644]
neon/src/http_request.c [new file with mode: 0644]
neon/src/http_request.h [new file with mode: 0644]
neon/src/http_utils.c [new file with mode: 0644]
neon/src/http_utils.h [new file with mode: 0644]
neon/src/md5.c [new file with mode: 0644]
neon/src/md5.h [new file with mode: 0644]
neon/src/ne_207.c [new file with mode: 0644]
neon/src/ne_207.h [new file with mode: 0644]
neon/src/ne_acl.c [new file with mode: 0644]
neon/src/ne_acl.h [new file with mode: 0644]
neon/src/ne_alloc.c [new file with mode: 0644]
neon/src/ne_alloc.h [new file with mode: 0644]
neon/src/ne_auth.c [new file with mode: 0644]
neon/src/ne_auth.h [new file with mode: 0644]
neon/src/ne_basic.c [new file with mode: 0644]
neon/src/ne_basic.h [new file with mode: 0644]
neon/src/ne_compress.c [new file with mode: 0644]
neon/src/ne_compress.h [new file with mode: 0644]
neon/src/ne_cookies.c [new file with mode: 0644]
neon/src/ne_cookies.h [new file with mode: 0644]
neon/src/ne_dates.c [new file with mode: 0644]
neon/src/ne_dates.h [new file with mode: 0644]
neon/src/ne_defs.h [new file with mode: 0644]
neon/src/ne_i18n.c [new file with mode: 0644]
neon/src/ne_i18n.h [new file with mode: 0644]
neon/src/ne_locks.c [new file with mode: 0644]
neon/src/ne_locks.h [new file with mode: 0644]
neon/src/ne_md5.c [new file with mode: 0644]
neon/src/ne_md5.h [new file with mode: 0644]
neon/src/ne_private.h [new file with mode: 0644]
neon/src/ne_props.c [new file with mode: 0644]
neon/src/ne_props.h [new file with mode: 0644]
neon/src/ne_redirect.c [new file with mode: 0644]
neon/src/ne_redirect.h [new file with mode: 0644]
neon/src/ne_request.c [new file with mode: 0644]
neon/src/ne_request.h [new file with mode: 0644]
neon/src/ne_session.c [new file with mode: 0644]
neon/src/ne_session.h [new file with mode: 0644]
neon/src/ne_socket.c [new file with mode: 0644]
neon/src/ne_socket.h [new file with mode: 0644]
neon/src/ne_string.c [new file with mode: 0644]
neon/src/ne_string.h [new file with mode: 0644]
neon/src/ne_uri.c [new file with mode: 0644]
neon/src/ne_uri.h [new file with mode: 0644]
neon/src/ne_utils.c [new file with mode: 0644]
neon/src/ne_utils.h [new file with mode: 0644]
neon/src/ne_xml.c [new file with mode: 0644]
neon/src/ne_xml.h [new file with mode: 0644]
neon/src/neon.h [new file with mode: 0644]
neon/src/neon_config.h [new file with mode: 0644]
neon/src/neon_defs.h [new file with mode: 0644]
neon/src/neon_i18n.c [new file with mode: 0644]
neon/src/neon_i18n.h [new file with mode: 0644]
neon/src/neon_md5.h [new file with mode: 0644]
neon/src/nsocket.h [new file with mode: 0644]
neon/src/socket.c [new file with mode: 0644]
neon/src/socket.h [new file with mode: 0644]
neon/src/sslcerts.c [new file with mode: 0644]
neon/src/string_utils.c [new file with mode: 0644]
neon/src/string_utils.h [new file with mode: 0644]
neon/src/uri.c [new file with mode: 0644]
neon/src/uri.h [new file with mode: 0644]
neon/src/xalloc.c [new file with mode: 0644]
neon/src/xalloc.h [new file with mode: 0644]
neon/test/.cvsignore [new file with mode: 0644]
neon/test/COPYING [new file with mode: 0644]
neon/test/ChangeLog [new file with mode: 0644]
neon/test/Makefile.in [new file with mode: 0644]
neon/test/README [new file with mode: 0644]
neon/test/STATUS [new file with mode: 0644]
neon/test/acl.c [new file with mode: 0644]
neon/test/auth.c [new file with mode: 0644]
neon/test/basic.c [new file with mode: 0644]
neon/test/child.c [new file with mode: 0644]
neon/test/child.h [new file with mode: 0644]
neon/test/common/ChangeLog [new file with mode: 0644]
neon/test/common/README [new file with mode: 0644]
neon/test/common/child.c [new file with mode: 0644]
neon/test/common/child.h [new file with mode: 0644]
neon/test/common/run.sh [new file with mode: 0755]
neon/test/common/tests.c [new file with mode: 0644]
neon/test/common/tests.h [new file with mode: 0644]
neon/test/compress.c [new file with mode: 0644]
neon/test/cookies.c [new file with mode: 0644]
neon/test/expired.pem [new file with mode: 0644]
neon/test/htdocs/plain [new file with mode: 0644]
neon/test/http-tests.c [new file with mode: 0644]
neon/test/lock.c [new file with mode: 0644]
neon/test/makekeys.sh [new file with mode: 0755]
neon/test/notvalid.pem [new file with mode: 0644]
neon/test/openssl.conf [new file with mode: 0644]
neon/test/props.c [new file with mode: 0644]
neon/test/redirect.c [new file with mode: 0644]
neon/test/regress.c [new file with mode: 0644]
neon/test/request.c [new file with mode: 0644]
neon/test/resolve.c [new file with mode: 0644]
neon/test/run.sh [new file with mode: 0755]
neon/test/server.c [new file with mode: 0644]
neon/test/server.key [new file with mode: 0644]
neon/test/session.c [new file with mode: 0644]
neon/test/skeleton.c [new file with mode: 0644]
neon/test/sock-tests.c [new file with mode: 0644]
neon/test/socket.c [new file with mode: 0644]
neon/test/ssl.c [new file with mode: 0644]
neon/test/string-tests.c [new file with mode: 0644]
neon/test/stubs.c [new file with mode: 0644]
neon/test/tests.c [new file with mode: 0644]
neon/test/tests.h [new file with mode: 0644]
neon/test/uri-tests.c [new file with mode: 0644]
neon/test/util-tests.c [new file with mode: 0644]
neon/test/utils.c [new file with mode: 0644]
neon/test/utils.h [new file with mode: 0644]
neon/test/wrongcn.pem [new file with mode: 0644]
neon/test/xml.c [new file with mode: 0644]
neon/tools/ChangeLog [new file with mode: 0644]
neon/tools/README [new file with mode: 0644]
neon/tools/cvsdist [new file with mode: 0755]
neon/tools/mk [new file with mode: 0755]
neon/tools/mklsm [new file with mode: 0755]
neon/tools/reconf [new file with mode: 0755]
neon/tools/update-pot.sh [new file with mode: 0755]

diff --git a/neon/.cvsignore b/neon/.cvsignore
new file mode 100644 (file)
index 0000000..ba4ab4f
--- /dev/null
@@ -0,0 +1,9 @@
+config.h.in
+config.h
+configure
+config.status
+config.log
+Makefile
+aclocal.m4
+nget
+config.cache
diff --git a/neon/.package b/neon/.package
new file mode 100644 (file)
index 0000000..493f404
--- /dev/null
@@ -0,0 +1 @@
+announce-list=neon@webdav.org
diff --git a/neon/.release.sh b/neon/.release.sh
new file mode 100755 (executable)
index 0000000..78cdcc5
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+major=`echo $1 | sed "s/\..*//g"`
+minor=`echo $1 | sed "s/^[0-9]*\.\(.*\)\.[0-9]*$/\1/g"`
+rel=`echo $1 | sed "s/^.*\./\1/g"`
+version=$1
+
+for f in config.hw; do
+in=$f.in
+out=$f
+sed -e "s/@VERSION@/$version/g" \
+    -e "s/@MAJOR@/$major/g" \
+    -e "s/@MINOR@/$minor/g" \
+    -e "s/@RELEASE@/$release/g" < $in > $out
+done
diff --git a/neon/AUTHORS b/neon/AUTHORS
new file mode 100644 (file)
index 0000000..f64fe18
--- /dev/null
@@ -0,0 +1 @@
+Joe Orton <joe@orton.demon.co.uk>
diff --git a/neon/BUGS b/neon/BUGS
new file mode 100644 (file)
index 0000000..5a4e3e2
--- /dev/null
+++ b/neon/BUGS
@@ -0,0 +1,7 @@
+
+Known problems/bugs in neon                                        -*- text -*-
+---------------------------      Id: BUGS,v 1.2 2000/07/27 20:59:19 joe Exp 
+
+1.  Each new SSL request is opening a new connection to the server.
+    VERY slow.
+
diff --git a/neon/ChangeLog b/neon/ChangeLog
new file mode 100644 (file)
index 0000000..3481156
--- /dev/null
@@ -0,0 +1,24 @@
+Wed May 10 19:17:24 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in: Print configuration message, check for ranlib.
+
+Wed May 10 19:16:43 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c (main): Allow output to stdout.
+
+Wed May 10 19:15:56 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.in: Added shared, install* targets
+
+Wed May 10 17:47:30 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c (pretty_progress_bar): New function, from
+       cadaver.
+
+Wed May 10 14:42:45 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * example/nget.c: New file.
+
+Wed May 10 14:41:39 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * configure.in, Makefile.in, .cvsignore, install-sh: New files.
diff --git a/neon/INSTALL.win32 b/neon/INSTALL.win32
new file mode 100644 (file)
index 0000000..04e1611
--- /dev/null
@@ -0,0 +1,14 @@
+\r
+To install neon on Windows you need expat-lite already installed on your system.\r
+It can be taken from the Apache distribution or downloaded from the expat website.\r
+Now simply point make to the expat sources:\r
+\r
+  nmake /f neon.mak EXPAT_SRC=\path\to\expat-lite\r
+\r
+This should work with Microsoft VC++ 5 and 6. \r
+\r
+After compiling the Release subdirectory contains neons.lib, against which you can\r
+link your program. When you run your program make sure the XMLPARSE.DLL from\r
+expat is accessable, i.e. is in your PATH.\r
+\r
+\r
diff --git a/neon/Makefile.in b/neon/Makefile.in
new file mode 100644 (file)
index 0000000..3389afb
--- /dev/null
@@ -0,0 +1,80 @@
+
+CFLAGS = -Ilib -I. -Isrc @CFLAGS@ @DEFS@
+LDFLAGS = -L. @LDFLAGS@
+LIBS = @LIBS@
+CC = @CC@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL = @INSTALL@
+AR = ar
+RANLIB = @RANLIB@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+libdir = @libdir@
+man1dir = @mandir@/man1
+# Hmmmmm... the i18n stuff seems to want this:
+datadir = $(prefix)/@DATADIRNAME@
+# Previously I had:
+# datadir = @datadir@/sitecopy
+docdir = $(prefix)/doc/sitecopy
+sc_datadir = $(datadir)/sitecopy
+includedir = @includedir@
+neonincludes = $(includedir)/libneon
+
+top_srcdir = @top_srcdir@
+# Where does top_builddir come from?
+top_builddir = @top_srcdir@
+srcdir = @srcdir@
+# intl stuff
+localedir = $(datadir)/locale
+gnulocaledir = $(prefix)/share/locale
+gettextsrcdir = $(prefix)/share/gettext
+aliaspath = $(localedir):.
+@SET_MAKE@
+
+OBJECTS = src/http_request.o src/http_basic.o src/dav_basic.o src/dav_207.o \
+       src/string_utils.o src/dates.o src/xalloc.o src/hip_xml.o \
+       src/base64.o src/md5.o src/http_utils.o src/uri.o src/socket.o \
+       src/http_auth.o
+
+DIST_HEADERS = http_request.h http_utils.h uri.h socket.h http_basic.h \
+        dav_basic.h dav_207.h dav_props.h hip_xml.h dates.h string_utils.h
+
+libneon.a: $(OBJECTS)
+       $(AR) cru $@ $(OBJECTS)
+       $(RANLIB) $@
+
+shared: libneon.so
+
+libneon.so: $(OBJECTS)
+       gcc -shared -o $@ $(OBJECTS)
+
+examples: nget
+
+nget: libneon.a example/nget.o lib/basename.o
+       $(CC) $(LDFLAGS) -o $@  example/nget.o lib/basename.o -lneon
+
+install: install-static install-headers
+
+install-static: libneon.a
+       $(INSTALL) -d $(libdir)
+       cp libneon.a $(libdir)/libneon.a
+
+install-shared: libneon.so
+       $(INSTALL) -d $(libdir)
+       cp libneon.so $(libdir)/libneon.so
+
+install-headers:
+       $(INSTALL) -d $(neonincludes)
+       @for h in $(DIST_HEADERS); do \
+               echo Installing $$h into $(neonincludes); \
+               $(INSTALL_DATA) src/$$h $(neonincludes)/$$h; \
+       done
+
+neon_dir = src
+config_h = config.h
+
+include src/Makefile.incl
+
diff --git a/neon/NEWS b/neon/NEWS
new file mode 100644 (file)
index 0000000..5b96550
--- /dev/null
+++ b/neon/NEWS
@@ -0,0 +1,2 @@
+Changes in release 0.1.0
+- Initial release.
diff --git a/neon/README b/neon/README
new file mode 100644 (file)
index 0000000..a2a3478
--- /dev/null
@@ -0,0 +1,25 @@
+
+neon is an HTTP and WebDAV client library.
+
+Current features:
+
+ - High-level interface to HTTP and WebDAV methods.
+ - Low-level interface to HTTP request handling, to allow implementing
+   new methods easily.
+ - Persistent connection support (HTTP/1.1 and HTTP/1.0 aware)
+ - Basic and digest authentication (RFC2617) (including auth-int, md5-sess)
+ - Proxy support (including basic/digest authentication)
+ - Generic WebDAV 207 XML response handling mechanism
+ - XML parsing using expat or libxml parser
+ - Easy generation of error messages from 207 error responses
+ - Basic HTTP/1.1 methods: GET, PUT, HEAD, OPTIONS, conditional PUT
+ - WebDAV resource manipulation: MOVE, COPY, DELETE, MKCOL.
+ - WebDAV metadata support: set and remove properties (PROPPATCH), query
+   any set of properties (PROPFIND).
+
+Provides lower-level interfaces to directly implement new HTTP
+methods, and higher-level interfaces so that you don't have to
+worry about the lower-level stuff.
+
+Joe Orton
+<joe@orton.demon.co.uk>
diff --git a/neon/THANKS b/neon/THANKS
new file mode 100644 (file)
index 0000000..accdb97
--- /dev/null
@@ -0,0 +1,10 @@
+
+Greg Stein <gstein@lyra.org>
+Michael Sobolev <mss@despair.spb.ru>
+
+Originators of stolen code:
+
+Daniel Veillard <Daniel.Veillard@w3.org>
+Eric S Raymond <esr@snark.thyrsus.com>
+Ulrich Drepper <drepper@gnu.org>
+
diff --git a/neon/TODO b/neon/TODO
new file mode 100644 (file)
index 0000000..5556706
--- /dev/null
+++ b/neon/TODO
@@ -0,0 +1,104 @@
+
+To Do List for neon                                      -*- text -*-
+-------------------     Id: TODO,v 1.3 2000/05/13 20:52:43 joe Exp 
+
+Please submit feature requests to <mailto:neon@webdav.org>
+
+1. Support for HTTP-extended authoring methods ala WebRFM etc; using
+   New-URI header etc.  Also support the BROWSE and INDEX methods.  The
+   protocol is documented at:
+   http://www.ics.uci.edu/pub/ietf/webdav/ns_dav.html
+
+2. Add proper domain support to authentication code. (requires full
+   URI parsing support). Need to tell the auth layer the server
+   details.
+
+3. Add a callback to determine whether a specific request should go
+   through the proxy server or not... based on URI, maybe method too
+   since lots of HTTP/1.0 proxies break on DAV requests?
+
+4. Better cnonce generation for authentication: use /dev/random or
+   whatever like mod_auth_digest.
+
+5. Add request hooks to remove all authentication code from
+   http_request.c.
+
+6. PUT/GET with ranges... http_get_range
+
+7. hip_xml/dav_207/dav_prop might need a revamp.
+
+   hip_xml: use an expat-esque interface; i.e. make hip_xml_parser
+   opaque and add API to set handlers rather than initialize
+   structures directly. Two problems need to be solved more elegantly:
+
+   1) Allowing "sub-handler" for allowing parsing property element
+   contents independantly of the overally 207 response parse.
+
+   2) Allow "mixed-mode" parsing of XML which mixes cdata + elements.
+
+   Probably, make hip_xml more of a "utility" layer rather than a
+   "driving" layer; abstract out expat/libxml, namespace lookups,
+   element name -> id mapping, easy CDATA handling...
+
+8. WebDAV class 2 locking. Problems: this requires parsing the XML
+   within a property element. This could be achieved by doing a
+   completely new XML parse of the property value returned by end_prop
+   from the 207 code, but this is a bit noddy. Options:
+
+   a) Ideally: extend hip_xml and the 207 layer to allow doing a
+   proper start/end_element/cdata parse of the XML *within* a
+   property. This is tricky since it means extending hip_xml to
+   *dynamically* determine whether to be in "collect" mode or not. Add
+   another callback for this?
+
+9. DeltaV support (http://www.webdav.org/deltav/). See also the
+   inversion project (http://inversion.tigris.org/) who might build a
+   versioning system over DAV.
+
+10. ACL support (http://www.webdav.org/acl/)
+
+11. DASL support (http://www.webdav.org/dasl/). Xythos have server
+    support for this (www.sharemation.com).
+
+12. SSL/TLS support... make it pluggable so we don't have to export
+    crypto-related code at ALL?
+
+13. Should we really be abort()'ing on out-of-memory? It makes a lot
+    of code MUCH simpler (esp. sbuffer_* usage).
+
+14. Nicer request-header manipulation... some kind of indexed data
+    structure, so we're sure we don't add the same header to the
+    request twice (e.g. Cache-Control). Must be at least as flexible
+    as sbuffer usage, though.
+
+16. Socket status notification (socket.c:sock_register_*) is awful.
+
+17. Should we really be i18n'izing the low-level error messages in
+    http_request.c, dav_207.c ? It seems nice and clever to, so the
+    user REALLY know what is going wrong with the server (probably),
+    but it is maybe a bit frightening.
+    
+18. PROPFIND/propnames support.
+
+19. libtool support, for proper shared libraries.
+
+20. Add full URI parser + handling. Or stop pretending we are doing
+    "URI" parsing, and just handle HTTP URL's.
+
+21. Storing multiple authentication "sessions" within an actual
+    http_auth_session, so I log into e.g. /foo/ and /bar/ (which
+    are not in the same authentication domain)
+    switch between them without having to re-enter passwords all the
+    time.
+
+22. Handle PROPFIND property error responses properly.
+
+23. Mechanism for aborting a request mid-response; e.g., when a GET
+    fails due to out of disk space, abort the download.
+
+24. In a PROPFIND response, if a property were to include e.g., a
+    DAV:multistatus element, this would not be handled correctly.
+
+25. A BSD C library has an MD5 implementation in the C Library...
+    support this. (someone who runs a BSD will need to do this)
+
diff --git a/neon/autogen.sh b/neon/autogen.sh
new file mode 100644 (file)
index 0000000..2bd9617
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+rm -f config.cache
+([ ! -d macros ] || (echo -n aclocal...\  && aclocal -I macros)) && \
+([ ! -r acconfig.h ] || (echo -n autoheader...\  && autoheader)) && \
+([ ! -r macros/acconfig.h ] || (echo -n autoheader...\  && autoheader -l macros)) && \
+echo -n autoconf...\  && autoconf
diff --git a/neon/config.guess b/neon/config.guess
new file mode 100755 (executable)
index 0000000..a1b76ce
--- /dev/null
@@ -0,0 +1,1034 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
+#
+# This file 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 version 2 of the License, 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# The master version of this file is at the FSF in /home/gd/gnu/lib.
+# Please send patches to the Autoconf mailing list <autoconf@gnu.org>.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit system type (host/target name).
+#
+# Only a few systems have been added to this list; please add others
+# (but try to keep the structure clean).
+#
+
+# Use $HOST_CC if defined. $CC may point to a cross-compiler
+if test x"$CC_FOR_BUILD" = x; then
+  if test x"$HOST_CC" != x; then
+    CC_FOR_BUILD="$HOST_CC"
+  else
+    if test x"$CC" != x; then
+      CC_FOR_BUILD="$CC"
+    else
+      CC_FOR_BUILD=cc
+    fi
+  fi
+fi
+
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 8/24/94.)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy; exit 1' 1 2 15
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    alpha:OSF1:*:*)
+       if test $UNAME_RELEASE = "V4.0"; then
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+       fi
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       cat <<EOF >$dummy.s
+       .globl main
+       .ent main
+main:
+       .frame \$30,0,\$26,0
+       .prologue 0
+       .long 0x47e03d80 # implver $0
+       lda \$2,259
+       .long 0x47e20c21 # amask $2,$1
+       srl \$1,8,\$2
+       sll \$2,2,\$2
+       sll \$0,3,\$0
+       addl \$1,\$0,\$0
+       addl \$2,\$0,\$0
+       ret \$31,(\$26),1
+       .end main
+EOF
+       $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+       if test "$?" = 0 ; then
+               ./$dummy
+               case "$?" in
+                       7)
+                               UNAME_MACHINE="alpha"
+                               ;;
+                       15)
+                               UNAME_MACHINE="alphaev5"
+                               ;;
+                       14)
+                               UNAME_MACHINE="alphaev56"
+                               ;;
+                       10)
+                               UNAME_MACHINE="alphapca56"
+                               ;;
+                       16)
+                               UNAME_MACHINE="alphaev6"
+                               ;;
+               esac
+       fi
+       rm -f $dummy.s $dummy
+       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr [[A-Z]] [[a-z]]`
+       exit 0 ;;
+    21064:Windows_NT:50:3)
+       echo alpha-dec-winnt3.5
+       exit 0 ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       echo m68k-cbm-sysv4
+       exit 0;;
+    amiga:NetBSD:*:*)
+      echo m68k-cbm-netbsd${UNAME_RELEASE}
+      exit 0 ;;
+    amiga:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-amigaos
+       exit 0 ;;
+    arc64:OpenBSD:*:*)
+       echo mips64el-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    arc:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    hkmips:OpenBSD:*:*)
+       echo mips-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    pmax:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sgi:OpenBSD:*:*)
+       echo mips-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    wgrisc:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       echo arm-acorn-riscix${UNAME_RELEASE}
+       exit 0;;
+    arm32:NetBSD:*:*)
+       echo arm-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+       exit 0 ;;
+    SR2?01:HI-UX/MPP:*:*)
+       echo hppa1.1-hitachi-hiuxmpp
+       exit 0;;
+    Pyramid*:OSx*:*:*|MIS*:OSx*:*:*|MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       if test "`(/bin/universe) 2>/dev/null`" = att ; then
+               echo pyramid-pyramid-sysv3
+       else
+               echo pyramid-pyramid-bsd
+       fi
+       exit 0 ;;
+    NILE*:*:*:dcosx)
+       echo pyramid-pyramid-svr4
+       exit 0 ;;
+    sun4H:SunOS:5.*:*)
+       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    i86pc:SunOS:5.*:*)
+       echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:*:*)
+       case "`/usr/bin/arch -k`" in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       exit 0 ;;
+    sun3*:SunOS:*:*)
+       echo m68k-sun-sunos${UNAME_RELEASE}
+       exit 0 ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+       case "`/bin/arch`" in
+           sun3)
+               echo m68k-sun-sunos${UNAME_RELEASE}
+               ;;
+           sun4)
+               echo sparc-sun-sunos${UNAME_RELEASE}
+               ;;
+       esac
+       exit 0 ;;
+    aushp:SunOS:*:*)
+       echo sparc-auspex-sunos${UNAME_RELEASE}
+       exit 0 ;;
+    atari*:NetBSD:*:*)
+       echo m68k-atari-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    atari*:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor 
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit 0 ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit 0 ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit 0 ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit 0 ;;
+    sun3*:NetBSD:*:*)
+       echo m68k-sun-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sun3*:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mac68k:NetBSD:*:*)
+       echo m68k-apple-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mac68k:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvme68k:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvme88k:OpenBSD:*:*)
+       echo m88k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    powerpc:machten:*:*)
+       echo powerpc-apple-machten${UNAME_RELEASE}
+       exit 0 ;;
+    macppc:NetBSD:*:*)
+        echo powerpc-apple-netbsd${UNAME_RELEASE}
+        exit 0 ;;
+    RISC*:Mach:*:*)
+       echo mips-dec-mach_bsd4.3
+       exit 0 ;;
+    RISC*:ULTRIX:*:*)
+       echo mips-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+       echo vax-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+       echo clipper-intergraph-clix${UNAME_RELEASE}
+       exit 0 ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       sed 's/^        //' << EOF >$dummy.c
+#ifdef __cplusplus
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       $CC_FOR_BUILD $dummy.c -o $dummy \
+         && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+         && rm $dummy.c $dummy && exit 0
+       rm -f $dummy.c $dummy
+       echo mips-mips-riscos${UNAME_RELEASE}
+       exit 0 ;;
+    Night_Hawk:Power_UNIX:*:*)
+       echo powerpc-harris-powerunix
+       exit 0 ;;
+    m88k:CX/UX:7*:*)
+       echo m88k-harris-cxux7
+       exit 0 ;;
+    m88k:*:4*:R4*)
+       echo m88k-motorola-sysv4
+       exit 0 ;;
+    m88k:*:3*:R3*)
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+        if [ $UNAME_PROCESSOR = mc88100 -o $UNAME_PROCESSOR = mc88110 ] ; then
+       if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \
+            -o ${TARGET_BINARY_INTERFACE}x = x ] ; then
+               echo m88k-dg-dgux${UNAME_RELEASE}
+       else
+               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+       fi
+        else echo i586-dg-dgux${UNAME_RELEASE}
+        fi
+       exit 0 ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       echo m88k-dolphin-sysv3
+       exit 0 ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       echo m88k-tektronix-sysv3
+       exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       echo m68k-tektronix-bsd
+       exit 0 ;;
+    *:IRIX*:*:*)
+       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       exit 0 ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+       exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i?86:AIX:*:*)
+       echo i386-ibm-aix
+       exit 0 ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               sed 's/^                //' << EOF >$dummy.c
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+               rm -f $dummy.c $dummy
+               echo rs6000-ibm-aix3.2.5
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               echo rs6000-ibm-aix3.2.4
+       else
+               echo rs6000-ibm-aix3.2
+       fi
+       exit 0 ;;
+    *:AIX:*:4)
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+       if /usr/sbin/lsattr -EHl ${IBM_CPU_ID} | grep POWER >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=4.${UNAME_RELEASE}
+       fi
+       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       exit 0 ;;
+    *:AIX:*:*)
+       echo rs6000-ibm-aix
+       exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+       echo romp-ibm-bsd4.4
+       exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC NetBSD and
+       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+       exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       echo rs6000-bull-bosx
+       exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+       echo m68k-bull-sysv3
+       exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       echo m68k-hp-bsd
+       exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       echo m68k-hp-bsd4.4
+       exit 0 ;;
+    9000/[34678]??:HP-UX:*:*)
+       case "${UNAME_MACHINE}" in
+           9000/31? )            HP_ARCH=m68000 ;;
+           9000/[34]?? )         HP_ARCH=m68k ;;
+           9000/6?? | 9000/7?? | 9000/80[024] | 9000/8?[136790] | 9000/892 )
+              sed 's/^              //' << EOF >$dummy.c
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+               {
+               case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+               case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+               case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+                   switch (bits)
+                       {
+                       case 64: puts ("hppa2.0w"); break;
+                       case 32: puts ("hppa2.0n"); break;
+                       default: puts ("hppa2.0"); break;
+                       } break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+                   puts ("hppa2.0"); break;
+              #endif
+               default: puts ("hppa1.0"); break;
+               }
+                  exit (0);
+              }
+EOF
+       ($CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+       rm -f $dummy.c $dummy
+       esac
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       exit 0 ;;
+    3050*:HI-UX:*:*)
+       sed 's/^        //' << EOF >$dummy.c
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+       rm -f $dummy.c $dummy
+       echo unknown-hitachi-hiuxwe2
+       exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+       echo hppa1.1-hp-bsd
+       exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+       echo hppa1.0-hp-bsd
+       exit 0 ;;
+    *9??*:MPE*:*:*)
+       echo hppa1.0-hp-mpeix
+       exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+       echo hppa1.1-hp-osf
+       exit 0 ;;
+    hp8??:OSF1:*:*)
+       echo hppa1.0-hp-osf
+       exit 0 ;;
+    i?86:OSF1:*:*)
+       if [ -x /usr/sbin/sysversion ] ; then
+           echo ${UNAME_MACHINE}-unknown-osf1mk
+       else
+           echo ${UNAME_MACHINE}-unknown-osf1
+       fi
+       exit 0 ;;
+    parisc*:Lites*:*:*)
+       echo hppa1.1-hp-lites
+       exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*X-MP:*:*:*)
+       echo xmp-cray-unicos
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+       echo ymp-cray-unicos${UNAME_RELEASE}
+       exit 0 ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+       exit 0 ;;
+    CRAY*TS:*:*:*)
+       echo t90-cray-unicos${UNAME_RELEASE}
+       exit 0 ;;
+    CRAY*T3E:*:*:*)
+       echo t3e-cray-unicosmk${UNAME_RELEASE}
+       exit 0 ;;
+    CRAY-2:*:*:*)
+       echo cray2-cray-unicos
+        exit 0 ;;
+    F300:UNIX_System_V:*:*)
+        FUJITSU_SYS=`uname -p | tr [A-Z] [a-z] | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit 0 ;;
+    F301:UNIX_System_V:*:*)
+       echo f301-fujitsu-uxpv`echo $UNAME_RELEASE | sed 's/ .*//'`
+       exit 0 ;;
+    hp3[0-9][05]:NetBSD:*:*)
+       echo m68k-hp-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    hp300:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sparc*:BSD/OS:*:*)
+       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    i?86:BSD/386:*:* | i?86:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    *:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    *:FreeBSD:*:*)
+       if test -x /usr/bin/objformat; then
+           if test "elf" = "`/usr/bin/objformat`"; then
+               echo ${UNAME_MACHINE}-unknown-freebsdelf`echo ${UNAME_RELEASE}|sed -e 's/[-_].*//'`
+               exit 0
+           fi
+       fi
+       echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit 0 ;;
+    *:NetBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+       exit 0 ;;
+    *:OpenBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+       exit 0 ;;
+    i*:CYGWIN*:*)
+       echo ${UNAME_MACHINE}-pc-cygwin
+       exit 0 ;;
+    i*:MINGW*:*)
+       echo ${UNAME_MACHINE}-pc-mingw32
+       exit 0 ;;
+    p*:CYGWIN*:*)
+       echo powerpcle-unknown-cygwin
+       exit 0 ;;
+    prep*:SunOS:5.*:*)
+       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    *:GNU:*:*)
+       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       exit 0 ;;
+    *:Linux:*:*)
+       # uname on the ARM produces all sorts of strangeness, and we need to
+       # filter it out.
+       case "$UNAME_MACHINE" in
+         armv*)                      UNAME_MACHINE=$UNAME_MACHINE ;;
+         arm* | sa110*)              UNAME_MACHINE="arm" ;;
+       esac
+
+       # The BFD linker knows what the default object file format is, so
+       # first see if it will tell us. cd to the root directory to prevent
+       # problems with other programs or directories called `ld' in the path.
+       ld_help_string=`cd /; ld --help 2>&1`
+       ld_supported_emulations=`echo $ld_help_string \
+                        | sed -ne '/supported emulations:/!d
+                                   s/[         ][      ]*/ /g
+                                   s/.*supported emulations: *//
+                                   s/ .*//
+                                   p'`
+        case "$ld_supported_emulations" in
+         i?86linux)  echo "${UNAME_MACHINE}-pc-linux-gnuaout"      ; exit 0 ;;
+         i?86coff)   echo "${UNAME_MACHINE}-pc-linux-gnucoff"      ; exit 0 ;;
+         sparclinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+         armlinux)   echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+         m68klinux)  echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+         elf32ppc)   echo "powerpc-unknown-linux-gnu"              ; exit 0 ;;
+       esac
+
+       if test "${UNAME_MACHINE}" = "alpha" ; then
+               sed 's/^        //'  <<EOF >$dummy.s
+               .globl main
+               .ent main
+       main:
+               .frame \$30,0,\$26,0
+               .prologue 0
+               .long 0x47e03d80 # implver $0
+               lda \$2,259
+               .long 0x47e20c21 # amask $2,$1
+               srl \$1,8,\$2
+               sll \$2,2,\$2
+               sll \$0,3,\$0
+               addl \$1,\$0,\$0
+               addl \$2,\$0,\$0
+               ret \$31,(\$26),1
+               .end main
+EOF
+               LIBC=""
+               $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+               if test "$?" = 0 ; then
+                       ./$dummy
+                       case "$?" in
+                       7)
+                               UNAME_MACHINE="alpha"
+                               ;;
+                       15)
+                               UNAME_MACHINE="alphaev5"
+                               ;;
+                       14)
+                               UNAME_MACHINE="alphaev56"
+                               ;;
+                       10)
+                               UNAME_MACHINE="alphapca56"
+                               ;;
+                       16)
+                               UNAME_MACHINE="alphaev6"
+                               ;;
+                       esac
+
+                       objdump --private-headers $dummy | \
+                         grep ld.so.1 > /dev/null
+                       if test "$?" = 0 ; then
+                               LIBC="libc1"
+                       fi
+               fi
+               rm -f $dummy.s $dummy
+               echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} ; exit 0
+       elif test "${UNAME_MACHINE}" = "mips" ; then
+         cat >$dummy.c <<EOF
+#ifdef __cplusplus
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __MIPSEB__
+  printf ("%s-unknown-linux-gnu\n", argv[1]);
+#endif
+#ifdef __MIPSEL__
+  printf ("%sel-unknown-linux-gnu\n", argv[1]);
+#endif
+  return 0;
+}
+EOF
+         $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+         rm -f $dummy.c $dummy
+       else
+         # Either a pre-BFD a.out linker (linux-gnuoldld)
+         # or one that does not give us useful --help.
+         # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout.
+         # If ld does not provide *any* "supported emulations:"
+         # that means it is gnuoldld.
+         echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations:"
+         test $? != 0 && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0
+
+         case "${UNAME_MACHINE}" in
+         i?86)
+           VENDOR=pc;
+           ;;
+         *)
+           VENDOR=unknown;
+           ;;
+         esac
+         # Determine whether the default compiler is a.out or elf
+         cat >$dummy.c <<EOF
+#include <features.h>
+#ifdef __cplusplus
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __ELF__
+# ifdef __GLIBC__
+#  if __GLIBC__ >= 2
+    printf ("%s-${VENDOR}-linux-gnu\n", argv[1]);
+#  else
+    printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+#  endif
+# else
+   printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+  printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]);
+#endif
+  return 0;
+}
+EOF
+         $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+         rm -f $dummy.c $dummy
+       fi ;;
+# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.  earlier versions
+# are messed up and put the nodename in both sysname and nodename.
+    i?86:DYNIX/ptx:4*:*)
+       echo i386-sequent-sysv4
+       exit 0 ;;
+    i?86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+       exit 0 ;;
+    i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*)
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE}
+       else
+               echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
+       fi
+       exit 0 ;;
+    i?86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+       else
+               echo ${UNAME_MACHINE}-pc-sysv32
+       fi
+       exit 0 ;;
+    i?86:UnixWare:*:*)
+       if /bin/uname -X 2>/dev/null >/dev/null ; then
+         (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+           && UNAME_MACHINE=i586
+       fi
+       echo ${UNAME_MACHINE}-unixware-${UNAME_RELEASE}-${UNAME_VERSION}
+       exit 0 ;;
+    pc:*:*:*)
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+       echo i386-pc-msdosdjgpp
+        exit 0 ;;
+    Intel:Mach:3*:*)
+       echo i386-pc-mach3
+       exit 0 ;;
+    paragon:*:*:*)
+       echo i860-intel-osf1
+       exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+       fi
+       exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       echo m68010-convergent-sysv
+       exit 0 ;;
+    M68*:*:R3V[567]*:*)
+       test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m68*:LynxOS:2.*:*)
+       echo m68k-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+       echo m68k-atari-sysv4
+       exit 0 ;;
+    i?86:LynxOS:2.*:* | i?86:LynxOS:3.[01]*:*)
+       echo i386-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    TSUNAMI:LynxOS:2.*:*)
+       echo sparc-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    rs6000:LynxOS:2.*:* | PowerPC:LynxOS:2.*:*)
+       echo rs6000-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    SM[BE]S:UNIX_SV:*:*)
+       echo mips-dde-sysv${UNAME_RELEASE}
+       exit 0 ;;
+    RM*:ReliantUNIX-*:*:*)
+       echo mips-sni-sysv4
+       exit 0 ;;
+    RM*:SINIX-*:*:*)
+       echo mips-sni-sysv4
+       exit 0 ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               echo ${UNAME_MACHINE}-sni-sysv4
+       else
+               echo ns32k-sni-sysv
+       fi
+       exit 0 ;;
+    PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                           # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit 0 ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       echo hppa1.1-stratus-sysv4
+       exit 0 ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       echo i860-stratus-sysv4
+       exit 0 ;;
+    mc68*:A/UX:*:*)
+       echo m68k-apple-aux${UNAME_RELEASE}
+       exit 0 ;;
+    news*:NEWS-OS:*:6*)
+       echo mips-sony-newsos6
+       exit 0 ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R4000:UNIX_SV:*:*)
+       if [ -d /usr/nec ]; then
+               echo mips-nec-sysv${UNAME_RELEASE}
+       else
+               echo mips-unknown-sysv${UNAME_RELEASE}
+       fi
+        exit 0 ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       echo powerpc-be-beos
+       exit 0 ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       echo powerpc-apple-beos
+       exit 0 ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       echo i586-pc-beos
+       exit 0 ;;
+    SX-4:SUPER-UX:*:*)
+       echo sx4-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    SX-5:SUPER-UX:*:*)
+       echo sx5-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    Power*:Rhapsody:*:*)
+       echo powerpc-apple-rhapsody${UNAME_RELEASE}
+       exit 0 ;;
+    *:Rhapsody:*:*)
+       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+       exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+         ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+       printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+       printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+  printf ("vax-dec-bsd\n"); exit (0);
+#else
+  printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+       echo c1-convex-bsd
+       exit 0 ;;
+    c2*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit 0 ;;
+    c34*)
+       echo c34-convex-bsd
+       exit 0 ;;
+    c38*)
+       echo c38-convex-bsd
+       exit 0 ;;
+    c4*)
+       echo c4-convex-bsd
+       exit 0 ;;
+    esac
+fi
+
+#echo '(Unable to guess system type)' 1>&2
+
+exit 1
diff --git a/neon/config.hw b/neon/config.hw
new file mode 100644 (file)
index 0000000..9b235ff
--- /dev/null
@@ -0,0 +1,38 @@
+/* 
+   Win32 config.h
+   Copyright (C) 1999-2000, Peter Boos <pedib@colorfullife.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: config.hw,v 1.1 2000/12/13 20:15:06 joe Exp 
+*/
+
+#ifdef WIN32
+#define HAVE_STRING_H
+#define HAVE_EXPAT
+#define HAVE_MEMCPY
+
+
+// Win32 uses a underscore, so we use a macro to eliminate that.
+#define snprintf                       _snprintf
+#define vsnprintf                      _vsnprintf
+#define strcasecmp                     strcmpi
+#define strncasecmp                    strnicmp
+#define ssize_t                                int
+
+#include <io.h>
+#define read _read
+#endif
diff --git a/neon/config.hw.in b/neon/config.hw.in
new file mode 100644 (file)
index 0000000..3d86b27
--- /dev/null
@@ -0,0 +1,53 @@
+/* 
+   Win32 config.h
+   Copyright (C) 1999-2000, Peter Boos <pedib@colorfullife.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+#ifdef _WIN32\r
+#define WIN32\r
+#endif\r
+
+#ifdef WIN32
+\r
+#define NEON_VERSION "@VERSION@"\r
+#define NEON_VERSION_MAJOR (@MAJOR@)\r
+#define NEON_VERSION_MINOR (@MINOR@)\r
+\r
+\r
+#define HAVE_STRING_H
+#define HAVE_EXPAT
+#define HAVE_OLD_EXPAT
+#define HAVE_MEMCPY
+
+#define HAVE_STDLIB_H
+#define HAVE_STRING_H
+#define HAVE_LIMITS_H
+
+/* Win32 uses a underscore, so we use a macro to eliminate that. */
+#define snprintf                       _snprintf
+#define vsnprintf                      _vsnprintf
+#define strcasecmp                     strcmpi
+#define strncasecmp                    strnicmp
+#define ssize_t                                int
+#define inline                          __inline\r
+#define off_t                           _off_t\r
+
+#include <io.h>
+#define read _read
+\r
+#endif
diff --git a/neon/config.sub b/neon/config.sub
new file mode 100755 (executable)
index 0000000..692de9b
--- /dev/null
@@ -0,0 +1,993 @@
+#! /bin/sh
+# Configuration validation subroutine script, version 1.1.
+#   Copyright (C) 1991, 92-97, 1998 Free Software Foundation, Inc.
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file 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 version 2 of the License, 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+if [ x$1 = x ]
+then
+       echo Configuration name missing. 1>&2
+       echo "Usage: $0 CPU-MFR-OPSYS" 1>&2
+       echo "or     $0 ALIAS" 1>&2
+       echo where ALIAS is a recognized configuration type. 1>&2
+       exit 1
+fi
+
+# First pass through any local machine types.
+case $1 in
+       *local*)
+               echo $1
+               exit 0
+               ;;
+       *)
+       ;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  linux-gnu*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+       -apple)
+               os=
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco5)
+               os=sco3.2v5
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+       -psos*)
+               os=-psos
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       tahoe | i860 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \
+               | arme[lb] | pyramid | mn10200 | mn10300 | tron | a29k \
+               | 580 | i960 | h8300 \
+               | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w \
+               | alpha | alphaev[4-7] | alphaev56 | alphapca5[67] \
+               | we32k | ns16k | clipper | i370 | sh | powerpc | powerpcle \
+               | 1750a | dsp16xx | pdp11 | mips64 | mipsel | mips64el \
+               | mips64orion | mips64orionel | mipstx39 | mipstx39el \
+               | sparc | sparclet | sparclite | sparc64 | v850)
+               basic_machine=$basic_machine-unknown
+               ;;
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i[34567]86)
+         basic_machine=$basic_machine-pc
+         ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       vax-* | tahoe-* | i[34567]86-* | i860-* | m32r-* | m68k-* | m68000-* \
+             | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \
+             | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+             | power-* | none-* | 580-* | cray2-* | h8300-* | i960-* \
+             | xmp-* | ymp-* \
+             | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* \
+             | alpha-* | alphaev[4-7]-* | alphaev56-* | alphapca5[67] \
+             | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \
+             | clipper-* | orion-* \
+             | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \
+             | sparc64-* | mips64-* | mipsel-* \
+             | mips64el-* | mips64orion-* | mips64orionel-*  \
+             | mipstx39-* | mipstx39el-* \
+             | f301-* | armv*-*)
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-cbm
+               ;;
+       amigaos | amigados)
+               basic_machine=m68k-cbm
+               os=-amigaos
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-cbm
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       aux)
+               basic_machine=m68k-apple
+               os=-aux
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       cray2)
+               basic_machine=cray2-cray
+               os=-unicos
+               ;;
+       [ctj]90-cray)
+               basic_machine=c90-cray
+               os=-unicos
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k7[0-9][0-9] | hp7[0-9][0-9] | hp9k8[0-9]7 | hp8[0-9]7)
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hppa-next)
+               os=-nextstep3
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               os=-mpeix
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               os=-mpeix
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+       i[34567]86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv32
+               ;;
+       i[34567]86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv4
+               ;;
+       i[34567]86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv
+               ;;
+       i[34567]86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-solaris2
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       mipsel*-linux*)
+               basic_machine=mipsel-unknown
+               os=-linux-gnu
+               ;;
+       mips*-linux*)
+               basic_machine=mips-unknown
+               os=-linux-gnu
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       netwinder)
+               basic_machine=armv4l-corel
+               os=-linux
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+        pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pentium | p5 | k5 | nexen)
+               basic_machine=i586-pc
+               ;;
+       pentiumpro | p6 | k6 | 6x86)
+               basic_machine=i686-pc
+               ;;
+       pentiumii | pentium2)
+               basic_machine=i786-pc
+               ;;
+       pentium-* | p5-* | k5-* | nexen-*)
+               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumpro-* | p6-* | k6-* | 6x86-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumii-* | pentium2-*)
+               basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=rs6000-ibm
+               ;;
+       ppc)    basic_machine=powerpc-unknown
+               ;;
+       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       tx39)
+               basic_machine=mipstx39-unknown
+               ;;
+       tx39el)
+               basic_machine=mipstx39el-unknown
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       xmp)
+               basic_machine=xmp-cray
+               os=-unicos
+               ;;
+        xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       *mint | *MiNT)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       mips)
+               if [ x$os = x-linux-gnu ]; then
+                       basic_machine=mips-unknown
+               else
+                       basic_machine=mips-mips
+               fi
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sparc)
+               basic_machine=sparc-sun
+               ;;
+        cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+       # -solaris* is a basic system type, with this one exception.
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -svr4*)
+               os=-sysv4
+               ;;
+       -unixware*)
+               os=-sysv4.2uw
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+             | -aos* \
+             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+             | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+             | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -rhapsody* \
+             | -openstep* | -mpeix* | -oskit*)
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -linux*)
+               os=`echo $os | sed -e 's|linux|linux-gnu|'`
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       -ns2 )
+               os=-nextstep2
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+        -*mint | -*MiNT)
+               os=-mint
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-corel)
+               os=-linux
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+        pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               # This also exists in the configure program, but was not the
+               # default.
+               # os=-sunos4
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-be)
+               os=-beos
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigaos
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-next )
+               os=-nextstep
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+        *-gould)
+               os=-sysv
+               ;;
+        *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+        *-sgi)
+               os=-irix
+               ;;
+        *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       f301-fujitsu)
+               os=-uxpv
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -aix*)
+                               vendor=ibm
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -vxsim* | -vxworks*)
+                               vendor=wrs
+                               ;;
+                       -aux*)
+                               vendor=apple
+                               ;;
+                       -*mint | -*MiNT)
+                               vendor=atari
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
diff --git a/neon/configure.in b/neon/configure.in
new file mode 100644 (file)
index 0000000..ba6c745
--- /dev/null
@@ -0,0 +1,42 @@
+
+AC_INIT(src/http_request.c)
+
+AC_CONFIG_HEADER(config.h)
+
+AC_DEFINE([_GNU_SOURCE])
+
+AC_PROG_CC
+AC_PROG_MAKE_SET
+AC_ISC_POSIX
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+AC_LANG_C
+AC_C_INLINE
+AC_C_CONST
+
+AC_HEADER_STDC
+
+AC_EXEEXT
+
+NEON_XML_PARSER
+
+LIBNEON_SOURCE_CHECKS
+
+AC_OUTPUT(Makefile)
+
+cat <<EOF
+
+Using configuration:
+
+  Install prefix:  ${prefix}
+  Compiler:        ${CC}
+  XML Parser:      ${neon_xml_parser_message}
+
+Now run: 'make' to compile the neon library (libneon.a)
+         'make shared' to compile a shared library with GCC (libneon.so)
+         'make examples' to compile the example programs
+
+  (Note: You might need to use GNU make if this is not the default 'make'.
+   GNU make could be installed as 'gmake' on your system.)
+
+EOF
diff --git a/neon/doc/.cvsignore b/neon/doc/.cvsignore
new file mode 100644 (file)
index 0000000..2c46ef5
--- /dev/null
@@ -0,0 +1,8 @@
+*.?
+*.html
+*.pdf
+*.ps
+*.tex
+*.sgml
+*.junk
+html
diff --git a/neon/doc/TODO b/neon/doc/TODO
new file mode 100644 (file)
index 0000000..febed30
--- /dev/null
@@ -0,0 +1,156 @@
+/* List of interfaces needing reference documentation.             -*- c -*- */
+
+/* ne_session.h */
+
+### DONE: basics
+ne_session *ne_session_create(const char *scheme, const char *hostname, int port);
+void ne_session_destroy(ne_session *sess);
+void ne_close_connection(ne_session *sess);
+void ne_session_proxy(ne_session *sess, const char *hostname, int port);
+
+### DONE: error handling
+void ne_set_error(ne_session *sess, const char *format, ...);
+const char *ne_get_error(ne_session *sess);
+
+### DONE: options
+void ne_set_useragent(ne_session *sess, const char *product);
+void ne_set_expect100(ne_session *sess, int use_expect100);
+void ne_set_persist(ne_session *sess, int persist);
+void ne_set_read_timeout(ne_session *sess, int timeout);
+
+### TODO: progress + status callbcacks
+void ne_set_progress(ne_session *sess, ne_progress progress, void *userdata);
+
+### TODO: status callback
+typedef enum ne_conn_status;
+typedef void (*ne_notify_status)(void *userdata, ne_conn_status status, const char *info);
+void ne_set_status(ne_session *sess, ne_notify_status status, void *userdata);
+
+### DONE: SSL verification
+
+typedef struct ne_ssl_dname;
+char *ne_ssl_readable_dname(const ne_ssl_dname *dn);
+typedef struct ne_ssl_certificate;
+#define NE_SSL_*
+typedef int (*ne_ssl_verify_fn)(void *userdata, int failures,
+                               const ne_ssl_certificate *cert);
+void ne_ssl_set_verify(ne_session *sess, ne_ssl_verify_fn fn, void *userdata);
+
+### DONE: SSL server certs
+int ne_ssl_load_ca(ne_session *sess, const char *file);
+int ne_ssl_load_default_ca(ne_session *sess);
+
+### TODO: SSL client certs
+typedef int (*ne_ssl_keypw_fn)(void *userdata, char *pwbuf, size_t len);
+void ne_ssl_keypw_prompt(ne_session *sess, ne_ssl_keypw_fn fn, void *ud);
+int ne_ssl_load_pkcs12(ne_session *sess, const char *fn);
+int ne_ssl_load_pem(ne_session *sess, const char *certfn, const char *keyfn);
+typedef void (*ne_ssl_provide_fn)(void *userdata, ne_session *sess,
+                                 const ne_ssl_dname *server);
+void ne_ssl_provide_ccert(ne_session *sess, ne_ssl_provide_fn fn, void *userdata);
+
+#ifdef NEON_SSL
+SSL_CTX *ne_ssl_get_context(ne_session *sess);
+X509 *ne_ssl_server_cert(ne_session *req);
+#endif
+
+### TODO: utility functions
+int ne_version_pre_http11(ne_session *sess);
+const char *ne_get_server_hostport(ne_session *sess);
+const char *ne_get_scheme(ne_session *sess);
+void ne_fill_server_uri(ne_session *sess, ne_uri *uri);
+
+/* end of ne_session.h *****************************************/
+
+/* ne_request.h */
+
+### DONE: request basics
+ne_request *ne_request_create(ne_session *sess, const char *method, const char *path);
+int ne_request_dispatch(ne_request *req);
+void ne_request_destroy(ne_request *req);
+
+### DONE: request status
+const ne_status *ne_get_status(ne_request *req);
+
+### TODO: request bodies
+void ne_set_request_body_buffer(ne_request *req, const char *buf, size_t count);
+int ne_set_request_body_fd(ne_request *req, int fd, size_t count);
+
+typedef ssize_t (*ne_provide_body)(void *userdata, 
+                                  char *buffer, size_t buflen);
+void ne_set_request_body_provider(ne_request *req, size_t size,
+                                 ne_provide_body provider, void *userdata);
+
+### TODO: response bodies
+typedef int (*ne_accept_response)(void *userdata, ne_request *req, ne_status *st);
+int ne_accept_2xx(void *userdata, ne_request *req, ne_status *st);
+int ne_accept_always(void *userdata, ne_request *req, ne_status *st);
+void ne_add_response_body_reader(ne_request *req, ne_accept_response accpt,
+                                ne_block_reader reader, void *userdata);
+
+### TODO: response headers
+typedef void (*ne_header_handler)(void *userdata, const char *value);
+void ne_add_response_header_handler(ne_request *req, const char *name, 
+                                   ne_header_handler hdl, void *userdata);
+void ne_add_response_header_catcher(ne_request *req, 
+                                   ne_header_handler hdl, void *userdata);
+
+void ne_duplicate_header(void *userdata, const char *value);
+void ne_handle_numeric_header(void *userdata, const char *value);
+
+### DONE: request headers
+void ne_add_request_header(ne_request *req, const char *name, const char *value);
+void ne_print_request_header(ne_request *req, const char *name, const char *format, ...);
+
+### TODO: misc
+ne_session *ne_get_session(ne_request *req);
+
+### TODO: caller-pulls request interface
+int ne_begin_request(ne_request *req);
+int ne_end_request(ne_request *req);
+ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen);
+
+### TODO: hooks
+typedef void (*ne_free_hooks)(void *cookie);
+typedef void (*ne_create_request_fn)(void *userdata, ne_request *req,
+                                    const char *method, const char *path);
+void ne_hook_create_request(ne_session *sess, ne_create_request_fn fn, void *userdata);
+
+typedef void (*ne_pre_send_fn)(void *userdata, ne_buffer *header);
+void ne_hook_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata);
+
+typedef int (*ne_post_send_fn)(void *userdata, const ne_status *status);
+void ne_hook_post_send(ne_session *sess, ne_post_send_fn fn, void *userdata);
+
+typedef void (*ne_destroy_fn)(void *userdata);
+void ne_hook_destroy_request(ne_session *sess, ne_destroy_fn fn, void *userdata);
+
+void ne_hook_destroy_session(ne_session *sess, ne_destroy_fn fn, void *userdata);
+
+typedef void *(*ne_accessor_fn)(void *userdata);
+void ne_hook_session_accessor(ne_session *sess, const char *id, ne_accessor_fn, void *userdata);
+void ne_hook_request_accessor(ne_request *req, const char *id, ne_accessor_fn, void *userdata);
+
+void *ne_null_accessor(void *userdata);
+
+void *ne_session_hook_private(ne_session *sess, const char *id);
+
+void *ne_request_hook_private(ne_request *req, const char *id);
+
+/* ne_207.h */
+/* ne_acl.h */
+/* DONE: ne_alloc.h */
+/* DONE: ne_auth.h */
+/* ne_basic.h */
+/* ne_compress.h */
+/* ne_cookies.h */
+/* ne_dates.h */
+/* ne_locks.h */
+/* ne_props.h */
+/* ne_redirect.h */
+/* ne_socket.h */
+/* MOSTLY DONE: ne_string.h */
+/* ne_uri.h */
+/* ne_utils.h */
+/* ne_xml.h */
+
diff --git a/neon/doc/html.xsl b/neon/doc/html.xsl
new file mode 100644 (file)
index 0000000..42fc397
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version='1.0'?>
+
+<!-- This file wraps around the DocBook HTML XSL stylesheet to customise
+   - some parameters; add the CSS stylesheet, etc.
+ -->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                version='1.0'
+                xmlns="http://www.w3.org/TR/xhtml1/transitional"
+                exclude-result-prefixes="#default">
+
+<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl"/>
+
+<xsl:variable name="html.stylesheet">../manual.css</xsl:variable>
+
+<!-- for sections with id attributes, use the id in the filename.
+  This produces meaningful (and persistent) URLs for the sections. -->
+<xsl:variable name="use.id.as.filename" select="1"/>
+
+<!-- enable the shaded verbatim (programlisting etc) blocks. -->
+<!-- <xsl:variable name="shade.verbatim" select="1"/> -->
+
+<!-- add class="programlisting" attribute on the <pre>s from
+  <programlisting>s so that the CSS can style them nicely. -->
+<xsl:attribute-set name="shade.verbatim.style">
+  <xsl:attribute name="class">programlisting</xsl:attribute>
+</xsl:attribute-set>
+
+<!-- use sane ANSI C function prototypes -->
+<xsl:variable name="funcsynopsis.style">ansi</xsl:variable>
+
+<!-- don't generate table of contents within each chapter chunk. -->
+<xsl:variable name="generate.chapter.toc" select="0"/>
+
+<xsl:variable name="generate.appendix.toc" select="0"/>
+
+</xsl:stylesheet>
+
diff --git a/neon/doc/man.xsl b/neon/doc/man.xsl
new file mode 100644 (file)
index 0000000..cad9744
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version='1.0'?>
+
+<!-- This file wraps around the xmlto db2man XSL stylesheet to
+customise some parameters. -->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                version='1.0'>
+
+<xsl:import href="/usr/share/xmlto/xsl/db2man/docbook.xsl"/>
+
+<!-- use an improved xref -->
+<xsl:import href="./xref-man.xsl"/>
+
+</xsl:stylesheet>
diff --git a/neon/doc/manual.css b/neon/doc/manual.css
new file mode 100644 (file)
index 0000000..034d7a7
--- /dev/null
@@ -0,0 +1,29 @@
+
+p, pre.funcsynopsisinfo { margin-left: 0.4em; margin-right: 0.4em; }
+
+div.funcsynopsis { line-height: 0.5em; }
+
+div.legalnotice { font-size: 80%; margin-left: 2em; }
+
+a:visited { color: darkgreen; }
+
+div.navheader { border-top: thin solid #bbf2bb; }
+div.navfooter { border-bottom: thin solid #bbf2bb; }
+
+pre.programlisting { 
+       background-color: #dddddd;
+       margin-left: 0.6em; margin-right: 1em;
+       padding: 0.3em; width: 50em;
+}
+
+h1.title { border-bottom: thick solid #bbf2bb; padding-bottom: 0.1em; }
+
+div.toc { 
+       border-left: thick solid #bbf2bb; padding-left: 0.5em;
+       }
+
+h2, h3 { padding-left: 0.2em; padding-top: -0.1em; }
+
+h2 { background-color: #bbf2bb; font-size: 110%; 
+     padding-bottom: 0.1em; padding-top: 0.1em; }
+
diff --git a/neon/doc/manual.xml b/neon/doc/manual.xml
new file mode 100644 (file)
index 0000000..14b8a68
--- /dev/null
@@ -0,0 +1,519 @@
+<?xml version='1.0'?> <!-- -*- DocBook -*- -->
+
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+  "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+
+<!ENTITY fdl SYSTEM "fdl.sgml">
+
+]>
+
+<book>
+  <bookinfo>
+    <title>neon HTTP/WebDAV client library</title>
+    <author>
+      <firstname>Joe</firstname><surname>Orton</surname>
+      <affiliation>
+       <address><email>neon@webdav.org</email></address>
+      </affiliation>
+    </author>
+    <copyright><year>2001</year><holder>Joe Orton</holder></copyright>
+
+    <legalnotice>
+      <para>Permission is granted to copy, distribute and/or modify this document
+      under the terms of the GNU Free Documentation License, Version 1.1
+      or any later version published by the Free Software Foundation;
+      with no Invariant Sections, with no Front-Cover Texts, 
+      and with no Back-Cover Texts.
+      A copy of the license is included in the section entitled "GNU
+      Free Documentation License".</para>
+    </legalnotice>
+
+  </bookinfo>
+  
+  <chapter>
+    <title>Introduction to neon</title>
+    <para>This is neon, an HTTP and WebDAV client library</para>
+    
+    <sect1>
+      <title>How to use neon from your application</title>
+      
+      <para>The neon source package is designed to be easily
+      incorporated into applications:</para>
+      
+      <itemizedlist>
+       <listitem>
+         
+         <para>autoconf macros are distributed in the 'macros'
+         subdirectory of the neon distribution.  Use NEON_LIBRARY
+         from your configure.in to check for the presence of the
+         neon library installed on the system.  The macro adds an
+         '--with-neon=...'  argument to configure, which allows the
+         user to specify a location for the library (the standard
+         /usr and /usr/local directories are checked automatically
+         without having to be specified).</para></listitem>
+         
+         <listitem><para>The 'src' directory of the neon package can be
+         imported directly into your application, if you do not wish
+         to add an external dependency.  If you wish to bundle, use
+         the NEON_BUNDLED macro to configure neon in your application:
+         here, the neon sources are bundled in a directory called
+         'libneon':</para>
+         
+         <literal>
+           NEON_BUNDLED(libneon, ...)
+         </literal>
+         
+         
+         <para>If your application supports builds where srcdir != builddir,
+         you should use the NEON_VPATH_BUNDLED macro like this:</para>
+         
+         <literal>
+           NEON_VPATH_BUNDLED(${srcdir}/libneon, libneon, ...)
+         </literal>
+         
+         <para>If you use this macro, a '--with-included-neon' option
+         will be added to the generated configure script.  This
+         allows the user to force the bundled neon to be used in the
+         application, rather than any neon library found on the
+         system. If you allow neon to be configured this way, you
+         must also configure an XML parser. Use the NEON_XML_PARSER
+         macro to do this.</para></listitem>
+         
+         <listitem><para>The final argument to the _BUNDLED macros is a
+         set of actions which are executed if the bundled build *is*
+         chosen (rather than an external neon which might have been
+         found on the user's system).  In here, use either the
+         NEON_LIBTOOL_BUILD or NEON_NORMAL_BUILD macro to set up the
+         neon Makefile appropriately: including adding the neon source
+         directory to the recursive make.</para></listitem>
+         
+       </itemizedlist>
+       
+       <para>A full fragment might be:</para>
+       
+       <literal>
+         NEON_BUNDLED(libneon, [
+         NEON_NORMAL_BUILD
+         NEON_XML_PARSER
+         SUBDIRS="libneon $SUBDIRS"
+         ])
+       </literal>
+       
+       <para>This means the bundled neon source directory (called 'libneon')
+       is used if no neon is found on the system, and the standard XML
+       parser search is used.</para>
+       
+      </sect1>
+      
+      <sect1>
+       <title>neon API guidelines</title> 
+       
+       <para>neon reserves the namespace <literal>ne_*</literal>: an
+       application which uses neon may not use symbols within this
+       namespace.</para>
+       
+      </sect1>
+      
+      <sect1>
+       <title>Protocol compliance</title>
+       
+       <para>neon is intended to be compliant with all relevant IETF and W3C
+       standards.</para>
+       
+       <sect2><title>RFC2518</title>
+       
+       <para>neon is deliberately not compliant with section 23.4.2, and
+       treats property names as a (namespace-URI, name) pair. This is
+       <ulink
+         url="http://lists.w3.org/Archives/Public/w3c-dist-auth/1999OctDec/0343.html">generally
+       considered</ulink> to be the correct behaviour by the WebDAV
+       WG and is likely to change in a future revision of the spec.</para>
+       
+      </sect2>
+      
+      <sect2>
+       <title>RFC2616</title>
+       
+       <para>The redirect interface is deliberately not compliant with
+       section 10.3, and will automatically follow redirects for the
+       <literal>PROPFIND</literal> and <literal>OPTIONS</literal>
+       methods as well as <literal>GET</literal> and
+       <literal>HEAD</literal>.  It <ulink url="http://www.apachelabs.org/apache-mbox/200102.mbox/%3C20010224232203.G799@waka.ebuilt.net%3E">has been stated</ulink> that this was the intent of the specification by one of the authors.</para>
+
+      </sect2>
+      
+    </sect1>
+    
+  </chapter>
+  
+  <chapter>
+    <title>The neon API for the C language</title>
+    
+    <sect1>
+      <title>Sessions</title>
+      
+      <para>An HTTP session is created using the
+      <citerefentry><refentrytitle>ne_session_create</refentrytitle></citerefentry>
+      function</para>
+    </sect1>
+    
+    <sect1>
+      <title>Low-level request interface</title>
+    </sect1>
+    
+    <sect1>
+      <title>Basic HTTP and WebDAV methods</title>
+      <para>ne_basic.h</para>
+    </sect1>
+    
+    <sect1>
+      <title>HTTP authentication</title>
+
+      <para>Authentication is supported using callbacks: using the
+      <citerefentry><refentrytitle>ne_set_server_auth</refentrytitle></citerefentry> 
+      function, a callback can be registered which will supply
+      authentication credentials upon demand.  In an interactive 
+      application, this will typically be done using some form of
+      username/password prompt.</para>
+
+      <para>Two types of authentication are supported: server
+      authentication (via the <function>ne_set_server_auth</function>
+      function), and proxy authentication (via the
+      <function>ne_set_proxy_auth</function> function).</para>
+    </sect1>
+
+    <sect1>
+      <title>Parsing XML</title>
+      <para>ne_xml.h functions</para>
+    </sect1>
+    
+    <sect1>
+      <title>WebDAV properties</title>
+      <para>ne_props.h functions</para>
+    </sect1>
+    
+    <sect1>
+      <title>WebDAV locking</title>
+      <para>ne_locks.h functions</para>
+    </sect1>
+
+    <sect1>
+      <title>Utility functions</title>
+      <para>stuff</para>
+      <sect2>
+       <title>String handling</title>
+      </sect2>
+      <sect2>
+       <title>Date/time manipulation</title>
+      </sect2>
+    </sect1>
+
+  </chapter>
+  
+  <reference>
+    
+    <title>neon API reference</title>
+    
+    <refentry>
+
+      <refentryinfo>
+       <title>neon</title>
+      </refentryinfo>
+      
+      <refmeta>
+       <refentrytitle>ne_session_create</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname>ne_session_create</refname>
+       <refname>ne_session_close_connection</refname>
+       <refname>ne_session_server</refname>
+       <refname>ne_session_destroy</refname>
+       <refpurpose>Manipulate HTTP sessions</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+         <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+         <funcprototype>
+           <funcdef><type>ne_session *</type><function>ne_session_create</function></funcdef>
+           <void/>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>int <function>ne_session_server</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>const char *<parameter>hostname</parameter></paramdef>
+           <paramdef>int <parameter>port</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>int <function>ne_close_connection</function></funcdef>
+           <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_session_destroy</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>An <type>ne_session *</type> object is used to group a
+       sequence of HTTP requests made to a server, enabling the 
+       use of a persistent connection to be used across all the 
+       requests, shared authentication credentials, and more.</para>
+
+       <para>The <function>ne_session_server</function> call sets the 
+       server to be used for the session.  This function must be
+       called before any requests are made using the session.</para>
+      </refsect1>
+
+      <refsect1>
+       <title>Return Values</title>
+       <para>On success, <function>ne_session_server</function>
+       returns <returnvalue>0</returnvalue> (<errorname>NE_OK</errorname>),
+       or a non-zero value if an error occurred.</para>
+      </refsect1>
+
+      <refsect1>
+       <title>Errors</title>
+       <para><errorname>NE_LOOKUP</errorname>: the hostname cannot be resolved.</para>
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+       <para>Create and initialize a session:</para>
+       <programlisting>
+
+  ne_session *sess = ne_session_create();
+  if (ne_session_server(sess, "my.host.name", 80) == NE_LOOKUP) {
+     printf("Host not found!");
+  } else {
+     /* ... use sess ... */
+  }
+  ne_session_destroy(sess);
+       </programlisting>
+      </refsect1>
+    </refentry>
+
+    <refentry>
+
+      <refmeta>
+       <refentrytitle>ne_session_proxy</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname>ne_session_proxy</refname>
+       <refname>ne_session_decide_proxy</refname>
+       <refpurpose>Proxy server settings</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcprototype>
+           <funcdef>int <function>ne_session_proxy</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>const char *<parameter>hostname</parameter></paramdef>
+           <paramdef>int <parameter>port</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_error</function></funcdef>
+           <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+           <paramdef>const char *<parameter>error</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>const char *<function>ne_get_error</function></funcdef>
+           <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>const char *<function>ne_get_scheme</function></funcdef>
+           <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>const char *<function>ne_get_server_hostport</function></funcdef>
+           <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_useragent</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>const char *<parameter>product</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_expect100</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>int <parameter>use_expect100</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_persist</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>int <parameter>use_persist</parameter></paramdef>
+         </funcprototype>
+
+         <!-- not sure if this is really the best way to represent a 
+              function typedef; all the examples in-line it, which is nasty.
+              -->
+         <funcprototype>
+           <funcdef>typedef int (*ne_use_proxy)</funcdef>
+           <paramdef>void *<parameter>userdata</parameter></paramdef>
+           <paramdef>const char *<parameter>scheme</parameter></paramdef>
+           <paramdef>const char *<parameter>hostname</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_session_decide_proxy</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>ne_use_proxy <parameter>use_proxy</parameter></paramdef>
+           <paramdef>void *<parameter>userdata</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+      
+      <refsect1>
+       <title>Description</title>
+       <para>An <type>ne_session *</type> object is a foo.</para>
+      </refsect1>
+      
+    </refentry>
+    
+    <refentry>
+
+      <refentryinfo><title>neon</title></refentryinfo>
+
+      <refmeta>
+       <refentrytitle>neon-config</refentrytitle>
+       <manvolnum>1</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname>neon-config</refname>
+       <refpurpose>provide information about installed copy of neon 
+       library</refpurpose>
+      </refnamediv>
+
+      <refsynopsisdiv>
+
+       <cmdsynopsis>
+         <command>neon-config</command>
+         <arg choice="opt"><option>--prefix</option></arg><sbr/>
+         <group>
+           <arg><option>--cflags</option></arg>
+           <arg><option>--libs</option></arg>
+           <arg><option>--support</option> <replaceable>feature</replaceable></arg>
+           <arg><option>--help</option></arg>
+           <arg><option>--version</option></arg>
+         </group>
+       </cmdsynopsis>
+
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>The <command>neon-config</command> script provides
+information about an installed copy of the neon library.  The
+<option>--cflags</option> and <option>--libs</option> options instruct
+how to compile and link an application against the library; the
+<option>--version</option> and <option>--support</option> options can
+help determine whether the library meets the applications
+requirements.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Options</title>
+
+       <variablelist>
+
+         <varlistentry>
+           <term><option>--cflags</option></term>
+           <listitem><para>Print the flags which should be passed to
+the C compiler when compiling object files, when the object files use
+neon header files.</para></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><option>--libs</option></term>
+           <listitem><para>Print the flags which should be passed to
+the linker when linking an application which uses the neon
+library</para></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><option>--version</option></term>
+           <listitem><para>Print the version of the library</para></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><option>--prefix</option> <replaceable>dir</replaceable></term>
+           <listitem><para>If <replaceable>dir</replaceable> is given; relocate output of
+<option>--cflags</option> and <option>--libs</option> as if neon was
+installed in given prefix directory.  Otherwise, print the
+installation prefix of the library.</para></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><option>--support</option> <replaceable>feature</replaceable></term>
+           <listitem><para>The script exits with success if
+<replaceable>feature</replaceable> is supported by the
+library.</para></listitem>
+          </varlistentry>
+
+         <varlistentry>
+           <term><option>--help</option></term>
+           <listitem><para>Print help message; includes list of known
+           features and whether they are supported or not.</para></listitem>
+         </varlistentry>
+
+       </variablelist>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Example</title>
+
+       <para>Below is a Makefile fragment which could be used to
+build an application against an installed neon library, when the
+<command>neon-config</command> script can be found in
+<envar>$PATH</envar>.</para>
+
+       <programlisting>
+CFLAGS = `neon-config --cflags`
+LIBS = `neon-config --libs`
+OBJECTS = myapp.o
+TARGET = myapp
+
+$(TARGET): $(OBJECTS)
+       $(CC) $(LDFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS)
+
+myapp.o: myapp.c
+       $(CC) $(CFLAGS) -c myapp.c -o myapp.o
+</programlisting>
+
+       </refsect1>
+
+    </refentry>
+
+  </reference>
+
+&fdl;
+  
+</book>
diff --git a/neon/doc/parsing-xml.txt b/neon/doc/parsing-xml.txt
new file mode 100644 (file)
index 0000000..dc3e467
--- /dev/null
@@ -0,0 +1,170 @@
+
+Requirements for XML parsing in neon
+------------------------------------
+
+Before describing the interface given in neon for parsing XML, here
+are the requirements which it must satisfy:
+
+ 1. to support using either libxml or expat as the underlying parser
+ 2. to allow "independent" sections to handle parsing one XML
+    document
+ 3. to map element namespaces/names to an integer for easier
+    comparison.
+
+A description of requirement (2) is useful since it is the "hard"
+requirement, and adds most of the complexity of interface: WebDAV
+PROPFIND responses are made up of a large boilerplate XML
+
+ <multistatus><response><propstat><prop>...</prop></propstat> etc.
+
+neon should handle the parsing of these standard elements, and expose
+the meaning of the response using a convenient interface.  But, within
+the <prop> elements, there may also be fragments of XML: neon can
+never know how to parse these, since they are property- and hence
+application-specific. The simplest example of this is the
+DAV:resourcetype property.
+
+So there is requirement (2) that two "independent" sections of code
+can handle the parsing of one XML document.
+
+Callback-based XML parsing
+--------------------------
+
+There are two ways of parsing XML documents commonly used:
+
+ 1. Build an in-memory tree of the document
+ 2. Use callbacks
+
+Where practical, using callbacks is more efficient than building a
+tree, so this is what neon uses.  The standard interface for
+callback-based XML parsing is called SAX, so understanding the SAX
+interface is useful to understanding the XML parsing interface
+provided by neon.
+
+The SAX interface works by registering callbacks which are called *as
+the XML is parsed*. The most important callbacks are for 'start
+element' and 'end element'. For instance, if the XML document below is
+parsed by a SAX-like interface:
+
+  <hello>
+    <foobar></foobar>
+  </hello>
+
+Say we have registered callbacks "startelm" for 'start element' and
+"endelm" for 'end element'.  Simplified somewhat, the callbacks will
+be called in this order, with these arguments:
+
+ 1. startelm("hello")
+ 2. startelm("foobar")
+ 3. endelm("foobar")
+ 4. endelm("hello")
+
+See the expat 'xmlparse.h' header for a more complete definition of a
+SAX-like interface.
+
+The hip_xml interface
+---------------------
+
+The hip_xml interface satisfies requirement (2) by introducing the
+"handler" concept. A handler is made up of these things:
+
+ - a set of XML elements
+ - a callback to validate an element
+ - a callback which is called when an element is opened
+ - a callback which is called when an element is closed
+ - (optionally, a callback which is called for CDATA)
+
+Registering a handler essentially says:
+
+ "If you encounter any of this set of elements, I want these
+  callbacks to be called."
+
+Handlers are kept in a STACK inside hip_xml.  The first handler
+registered becomes the BASE of the stack, subsequent handlers are
+PUSHed on top.
+
+During XML parsing, the handler which is used for an XML element is
+recorded.  When a new element is started, the search for a handler for
+this element begins at the handler used for the parent element, and
+carries on up the stack.  For the root element, the search always
+starts at the BASE of the stack.
+
+A user's guide to hip_xml
+-------------------------
+
+The first thing to do when using hip_xml is to know what set of XML
+elements you are going to be parsing.  This can usually be done by
+looking at the DTD provided for the documents you are going to be
+parsing.  The DTD is also very useful in writing the 'validate'
+callback function, since it can tell you what parent/child pairs are
+valid, and which aren't.
+
+In this example, we'll parse XML documents which look like:
+
+<T:list-of-things xmlns:T="http://things.org/">
+  <T:a-thing>foo</T:a-thing>
+  <T:a-thing>bar</T:a-thing>
+</T:list-of-things>
+
+So, given the set of elements, declare the element id's and the
+element array:
+
+#define ELM_listofthings (HIP_ELM_UNUSED)
+#define ELM_a_thing (HIP_ELM_UNUSED + 1)
+
+const static struct my_elms[] = {
+   { "http://things.org/", "list-of-things", ELM_listofthings, 0 },
+   { "http://things.org/", "a-thing", ELM_a_thing, HIP_XML_CDATA },
+   { NULL }
+};
+
+This declares we know about two elements: list-of-things, and a-thing,
+and that the 'a-thing' element contains character data.
+
+The definition of the validation callback is very simple:
+
+static int validate(hip_xml_elmid parent, hip_xml_elmid child)
+{
+    /* Only allow 'list-of-things' as the root element. */
+    if (parent == HIP_ELM_root && child == ELM_listofthings ||
+        parent = ELM_listofthings && child == ELM_a_thing) {
+       return HIP_XML_VALID;
+    } else {
+        return HIP_XML_INVALID;
+    }
+}
+
+For this example, we can ignore the start-element callback, and just
+use the end-element callback:
+
+static int endelm(void *userdata, const struct hip_xml_elm *s,
+                 const char *cdata)
+{
+    printf("Got a thing: %s\n", cdata);
+    return 0;
+}
+
+This endelm callback just prints the cdata which was contained in the
+"a-thing" element.
+
+Now, on to parsing. A new parser object is created for parsing each
+XML document.  Creating a new parser object is as simple as:
+
+  hip_xml_parser *parser;
+
+  parser = hip_xml_create();
+
+Next register the handler, passing NULL as the start-element callback,
+and also as userdata, which we don't use here.
+
+  hip_xml_push_handler(parser, my_elms, 
+                      validate, NULL, endelm,
+                      NULL);
+
+Finally, call hip_xml_parse, passing the chunks of XML document to the
+hip_xml as you get them. The output should be:
+
+  Got a thing: foo
+  Got a thing: bar
+
+for the XML document.
diff --git a/neon/doc/ref/alloc.xml b/neon/doc/ref/alloc.xml
new file mode 100644 (file)
index 0000000..1101a82
--- /dev/null
@@ -0,0 +1,88 @@
+    <refentry id="refalloc">
+
+      <refmeta>
+       <refentrytitle>ne_malloc</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_malloc">ne_malloc</refname>
+       <refname id="ne_calloc">ne_calloc</refname>
+       <refname id="ne_realloc">ne_realloc</refname>
+       <refname id="ne_strdup">ne_strdup</refname>
+       <refname id="ne_strndup">ne_strndup</refname>
+        <refname id="ne_oom_callback">ne_oom_callback</refname>
+       <refpurpose>memory allocation wrappers</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_alloc.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>void *<function>ne_malloc</function></funcdef>
+           <paramdef>size_t <parameter>size</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void *<function>ne_calloc</function></funcdef>
+           <paramdef>size_t <parameter>size</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void *<function>ne_realloc</function></funcdef>
+           <paramdef>void * <parameter>size</parameter></paramdef>
+           <paramdef>size_t <parameter>len</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>char *<function>ne_strdup</function></funcdef>
+           <paramdef>const char *<parameter>s</parameter></paramdef>
+           <paramdef>size_t <parameter>size</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>char *<function>ne_strndup</function></funcdef>
+           <paramdef>const char *<parameter>s</parameter></paramdef>
+           <paramdef>size_t <parameter>size</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_oom_callback</function></funcdef>
+           <paramdef>void *(<parameter>callback</parameter>)(void)</paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>The functions <function>ne_malloc</function>,
+<function>ne_calloc</function>, <function>ne_realloc</function>,
+<function>ne_strdup</function> and <function>ne_strdnup</function>
+provide wrappers for the equivalent functions in the standard C
+library.  The wrappers provide the extra guarantee that if the C
+library equivalent returns &null; when no memory is available, an
+optional callback will be called, and the library will then call
+<function>abort</function>().</para>
+
+       <para><function>ne_oom_callback</function> registers a callback
+which will be invoked if an out of memory error is detected.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Notes</title>
+
+       <para>If the operating system uses optimistic memory
+allocation, the C library memory allocation routines will not return
+&null;, so it is not possible to gracefully handle memory allocation
+failures.</para>
+
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/auth.xml b/neon/doc/ref/auth.xml
new file mode 100644 (file)
index 0000000..f56cea6
--- /dev/null
@@ -0,0 +1,114 @@
+    <refentry id="refauth">
+
+      <refmeta>
+       <refentrytitle>ne_set_server_auth</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_set_server_auth">ne_set_server_auth</refname>
+       <refname id="ne_set_proxy_auth">ne_set_proxy_auth</refname>
+       <refname id="ne_forget_auth">ne_forget_auth</refname>
+       <refpurpose>register authentication callbacks</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_auth.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>typedef int (*<function>ne_request_auth</function>)</funcdef>
+           <paramdef>void *<parameter>userdata</parameter></paramdef>
+           <paramdef>const char *<parameter>realm</parameter></paramdef>
+           <paramdef>int <parameter>attempt</parameter></paramdef>
+           <paramdef>char *<parameter>username</parameter></paramdef>
+           <paramdef>char *<parameter>password</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_server_auth</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>ne_request_auth <parameter>callback</parameter></paramdef>
+           <paramdef>void *<parameter>userdata</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_proxy_auth</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>ne_request_auth <parameter>callback</parameter></paramdef>
+           <paramdef>void *<parameter>userdata</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_forget_auth</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>The <type>ne_request_auth</type> function type defines a
+callback which is invoked when a server or proxy server requires user
+authentication for a particular request.  The
+<parameter>realm</parameter> string is supplied by the server. <!--
+FIXME --> The <parameter>attempt</parameter> is a counter giving the
+number of times the request has been retried with different
+authentication credentials.  The first time the callback is invoked
+for a particular request, <parameter>attempt</parameter> will be zero.</para>
+
+       <para>To retry the request using new authentication
+credentials, the callback should return zero, and the
+<parameter>username</parameter> and <parameter>password</parameter>
+buffers must contain <literal>NUL</literal>-terminated strings.  The
+<literal>NE_ABUFSIZ</literal> constant gives the size of these
+buffers.</para>
+
+       <tip>
+         <para>If you only wish to allow the user one attempt to enter
+credentials, use the value of the <parameter>attempt</parameter>
+parameter as the return value of the callback.</para>
+       </tip>
+
+       <para>To abort the request, the callback should return a
+non-zero value; in which case the contents of the
+<parameter>username</parameter> and <parameter>password</parameter>
+buffers are ignored.</para>
+
+       <para>The <function>ne_forget_auth</function> function can be
+used to discard the cached authentication credentials.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+
+       <programlisting>
+/* Function which prompts for a line of user input: */
+extern char *prompt_for(const char *prompt);
+
+static int
+my_auth(void *userdata, const char *realm, int attempts,
+        char *username, char *password)
+{
+   strncpy(username, prompt_for("Username: "), NE_ABUFSIZ);
+   strncpy(password, prompt_for("Password: "), NE_ABUFSIZ);
+   return attempts;
+}
+
+int main(...)
+{
+   &egsess;
+
+   ne_set_server_auth(sess, my_auth, NULL);
+
+   /* ... */
+}</programlisting>
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/buf.xml b/neon/doc/ref/buf.xml
new file mode 100644 (file)
index 0000000..3f8987c
--- /dev/null
@@ -0,0 +1,53 @@
+    <refentry id="refbuf">
+
+      <refmeta>
+       <refentrytitle>ne_buffer</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_buffer">ne_buffer</refname>
+       <refpurpose>string buffer handling</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <programlisting>#include &lt;ne_string.h&gt;
+
+typedef struct {
+    char *data;
+    size_t used;
+    size_t length;
+} <type>ne_buffer</type>;</programlisting>
+
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>The <type>ne_buffer</type> type represents an expandable
+memory buffer for holding &nul;-terminated strings.  The
+<structfield>data</structfield> field points to the beginnning of the
+string, the length of which is given by the
+<structfield>used</structfield> field.  The current size of memory
+allocated is given by the <structfield>length</structfield> field.  It
+is not recommended that the fields of a buffer are manipulated
+directly.  The <structfield>data</structfield> pointer may change when
+the buffer is modified.</para>
+
+       <para>A buffer is created using <xref
+linkend="ne_buffer_create"/> or <xref
+linkend="ne_buffer_create_sized"/>, and destroyed using <xref
+linkend="ne_buffer_destroy"/> or <xref linkend="ne_buffer_finish"/>.
+The functions <xref linkend="ne_buffer_append"/>, <xref
+linkend="ne_buffer_zappend"/> and <xref linkend="ne_buffer_concat"/> are
+used to append data to a buffer.</para>
+
+       <para>If the string referenced by the
+<structfield>data</structfield> pointer is modified directly (rather
+than using one of the functions listed above),
+<function>ne_buffer_altered</function> must be called.</para>
+
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/bufapp.xml b/neon/doc/ref/bufapp.xml
new file mode 100644 (file)
index 0000000..a75ce81
--- /dev/null
@@ -0,0 +1,89 @@
+    <refentry id="refbufapp">
+
+      <refmeta>
+       <refentrytitle>ne_buffer_append</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_buffer_append">ne_buffer_append</refname>
+       <refname id="ne_buffer_zappend">ne_buffer_zappend</refname>
+       <refname id="ne_buffer_concat">ne_buffer_concat</refname>
+       <refpurpose>append data to a string buffer</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>void <function>ne_buffer_append</function></funcdef>
+           <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+           <paramdef>const char *<parameter>string</parameter></paramdef>
+           <paramdef>size_t <parameter>len</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_buffer_zappend</function></funcdef>
+           <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+           <paramdef>const char *<parameter>string</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_buffer_concat</function></funcdef>
+           <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+           <paramdef>const char *<parameter>str</parameter></paramdef>
+           <paramdef>...</paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>The <function>ne_buffer_append</function> and
+<function>ne_buffer_zappend</function> functions append a string to
+the end of a buffer; extending the buffer as necessary.  The
+<parameter>len</parameter> passed to
+<function>ne_buffer_append</function> specifies the length of the
+string to append; there must be no &nul; terminator in the first
+<parameter>len</parameter> bytes of the string.
+<function>ne_buffer_zappend</function> must be passed a
+&nul;-terminated string.</para>
+
+       <para>The <function>ne_buffer_concat</function> function takes
+a variable-length argument list following <parameter>str</parameter>;
+each argument must be a <type>char *</type> pointer to a
+&nul;-terminated string.  A &null; pointer must be given as the last
+argument to mark the end of the list.  The strings (including
+<parameter>str</parameter>) are appended to the buffer in the order
+given. None of the strings passed to
+<function>ne_buffer_concat</function> are modified.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+
+       <para>The following code will output "<literal>Hello, world.
+And goodbye.</literal>".</para>
+       
+       <programlisting>ne_buffer *buf = ne_buffer_create();
+ne_buffer_zappend(buf, "Hello");
+ne_buffer_concat(buf, ", world. ", "And ", "goodbye.", NULL);
+puts(buf->data);
+ne_buffer_destroy(buf);</programlisting>
+      </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+
+       <para><xref linkend="ne_buffer"/>, <xref linkend="ne_buffer_create"/>,
+<xref linkend="ne_buffer_destroy"/></para>
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/bufcr.xml b/neon/doc/ref/bufcr.xml
new file mode 100644 (file)
index 0000000..0c572a6
--- /dev/null
@@ -0,0 +1,60 @@
+    <refentry id="refbufcr">
+
+      <refmeta>
+       <refentrytitle>ne_buffer_create</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_buffer_create">ne_buffer_create</refname>
+       <refname id="ne_buffer_create_sized">ne_buffer_ncreate</refname>
+       <refpurpose>general purpose of group of functions</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_alloc.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>ne_buffer *<function>ne_buffer_create</function></funcdef>
+           <void/>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>ne_buffer *<function>ne_buffer_ncreate</function></funcdef>
+           <paramdef>size_t <parameter>size</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para><function>ne_buffer_create</function> creates a new
+buffer object, with an implementation-defined initial size.
+<function>ne_buffer_ncreate</function> creates an
+<type>ne_buffer</type> where the minimum initial size is given in the
+<parameter>size</parameter> parameter.  The buffer created will
+contain the empty string (<literal>""</literal>).</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Return value</title>
+
+       <para>Both functions return a pointer to a new buffer object,
+and never &null;.</para>
+
+       </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+
+       <para><xref linkend="ne_buffer"/></para>
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/bufdest.xml b/neon/doc/ref/bufdest.xml
new file mode 100644 (file)
index 0000000..5dc4dfc
--- /dev/null
@@ -0,0 +1,81 @@
+    <refentry id="refbufdest">
+
+      <refmeta>
+       <refentrytitle>ne_buffer_destroy</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_buffer_destroy">ne_buffer_destroy</refname>
+       <refname id="ne_buffer_finish">ne_buffer_finish</refname>
+       <refpurpose>destroy a buffer object</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>void <function>ne_buffer_destroy</function></funcdef>
+           <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>char *<function>ne_buffer_finish</function></funcdef>
+           <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para><function>ne_buffer_destroy</function> frees all memory
+associated with the buffer.  <function>ne_buffer_finish</function>
+frees the buffer structure, but not the actual string stored in the
+buffer, which is returned and must be <function>free</function>()d by
+the caller.</para>
+
+       <para>Any use of the buffer object after calling either of these
+functions gives undefined behaviour.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Return value</title>
+
+       <para><function>ne_buffer_finish</function> returns the
+<function>malloc</function>-allocated string stored in the buffer.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+
+       <para>An example use of <function>ne_buffer_finish</function>;
+the <function>duplicate</function> function returns a string made up of 
+<parameter>n</parameter> copies of <parameter>str</parameter>:</para>
+
+       <programlisting>static char *duplicate(int n, const char *str)
+{
+  ne_buffer *buf = ne_buffer_create();
+  while (n--) {
+    ne_buffer_zappend(buf, str);
+  }
+  return ne_buffer_finish(buf);
+}</programlisting>
+
+      </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+
+       <para><xref linkend="ne_buffer"/>, <xref linkend="ne_buffer_create"/>,
+<xref linkend="ne_buffer_zappend"/></para>
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/bufutil.xml b/neon/doc/ref/bufutil.xml
new file mode 100644 (file)
index 0000000..3f3f3c5
--- /dev/null
@@ -0,0 +1,62 @@
+    <refentry id="refbufutil">
+
+      <refmeta>
+       <refentrytitle>ne_buffer_clear</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_buffer_clear">ne_buffer_clear</refname>
+       <refname id="ne_buffer_grow">ne_buffer_grow</refname>
+       <refname id="ne_buffer_altered">ne_buffer_altered</refname>
+       <refpurpose>general purpose of group of functions</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>void <function>ne_buffer_clear</function></funcdef>
+           <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_buffer_altered</function></funcdef>
+           <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_buffer_grow</function></funcdef>
+           <paramdef>ne_buffer *<parameter>buf</parameter></paramdef>
+           <paramdef>size_t <parameter>size</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>The <function>ne_buffer_clear</function> function sets
+the string stored in <parameter>buf</parameter> to be the empty string
+(<literal>""</literal>).</para>
+
+       <para>The <function>ne_buffer_altered</function> function must
+be used after the string stored in the buffer
+<parameter>buf</parameter> is modified by directly rather than using
+<xref linkend="ne_buffer_append"/>, <xref linkend="ne_buffer_zappend"/> 
+or <xref linkend="ne_buffer_concat"/>.</para>
+
+       <para>The <function>ne_buffer_grow</function> function
+ensures that at least <parameter>size</parameter> bytes are allocated
+for the string; this can be used if a large amount of data is going to
+be appended to the buffer and may result in more efficient memory
+allocation.</para>
+
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/err.xml b/neon/doc/ref/err.xml
new file mode 100644 (file)
index 0000000..50551b5
--- /dev/null
@@ -0,0 +1,66 @@
+    <refentry id="referr">
+
+      <refmeta>
+       <refentrytitle>ne_get_error</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_get_error">ne_get_error</refname>
+       <refname id="ne_set_error">ne_set_error</refname>
+       <refpurpose>error handling for HTTP sessions</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>const char *<function>ne_get_error</function></funcdef>
+           <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_error</function></funcdef>
+           <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+           <paramdef>const char *<parameter>format</parameter></paramdef>
+           <paramdef>...</paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+      
+      <refsect1>
+       <title>Description</title>
+
+       <para>The session error string is used to store any
+human-readable error information associated with any errors which
+occur whilst using the HTTP session.</para>
+
+       <para>The <function>ne_get_error</function> function returns the current
+session error string.  This string persists only until it is changed by a
+subsequent operation on the session.</para>
+
+       <para>The <function>ne_set_error</function> function can be
+used to set a new session error string, using a
+<function>printf</function>-style format string interface.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+       <para>Retrieve the current error string:</para>
+       <programlisting>&egsess;
+...
+printf("Error was: %s\n", ne_get_error(sess));</programlisting>
+
+       <para>Set a new error string:</para>
+       <programlisting>&egsess;
+...
+ne_set_error(sess, "Response missing header %s", "somestring");</programlisting>
+      </refsect1>
+      
+    </refentry>
diff --git a/neon/doc/ref/getst.xml b/neon/doc/ref/getst.xml
new file mode 100644 (file)
index 0000000..9d44277
--- /dev/null
@@ -0,0 +1,60 @@
+    <refentry id="refgetst">
+
+      <refmeta>
+       <refentrytitle>ne_get_status</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_get_status">ne_get_status</refname>
+       <refpurpose>retrieve HTTP response status for request</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>const ne_status *<function>ne_get_status</function></funcdef>
+           <paramdef>const ne_request *<parameter>request</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>The <function>ne_get_status</function> function returns
+a pointer to the HTTP status object giving the result of a request
+(once dispatched).  The object persists until the associated request
+is destroyed.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+
+       <para><xref linkend="ne_get_status"/>, <xref
+       linkend="ne_request_create"/></para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Example</title>
+       
+       <para>Display the response status code of applying the
+<literal>HEAD</literal> method to some resource.</para>
+
+       <programlisting>ne_request *req = ne_request_create(sess, "HEAD", "/foo/bar");
+if (ne_request_dispatch(req))
+   /* handle errors... */
+else
+   printf("Response status code was %d\n", ne_get_status(req)->code);
+ne_request_destroy(req);</programlisting>
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/init.xml b/neon/doc/ref/init.xml
new file mode 100644 (file)
index 0000000..32dcda5
--- /dev/null
@@ -0,0 +1,55 @@
+<refentry id="refsockinit">
+
+  <refmeta>
+    <refentrytitle>ne_sock_init</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname id="ne_sock_init">ne_sock_init</refname>
+    <refpurpose>perform library initialization</refpurpose>
+  </refnamediv>
+  
+  <refsynopsisdiv>
+
+    <funcsynopsis>
+
+      <funcsynopsisinfo>#include &lt;ne_socket.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>ne_sock_init</function></funcdef>
+       <void/>
+      </funcprototype>
+
+    </funcsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>In some platforms and configurations, &neon; may be using
+    some socket or SSL libraries which require global initialization
+    before use.  To perform this initialization, the
+    <function>ne_sock_init</function> function must be called once
+    before any other library functions are used.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+
+    <para><function>ne_sock_init</function> returns zero on success,
+    or non-zero on error.  If an error occurs, no further use of the
+    &neon; library should be attempted.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><xref linkend="refneon"/></para>
+  </refsect1>
+
+</refentry>
+
diff --git a/neon/doc/ref/neon.xml b/neon/doc/ref/neon.xml
new file mode 100644 (file)
index 0000000..219f682
--- /dev/null
@@ -0,0 +1,75 @@
+<refentry id="refneon">
+
+  <refmeta>
+    <refentrytitle>neon</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>neon</refname>
+    <refpurpose>neon API conventions</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>neon is an HTTP and WebDAV client library.  The major
+    abstractions exposed are of an HTTP <emphasis>session</emphasis>,
+    created by <xref linkend="ne_session_create"/>; and an HTTP
+    <emphasis>request</emphasis>, created by <xref
+    linkend="ne_request_create"/>.  HTTP authentication is handled
+    transparently for server and proxy servers, see <xref
+    linkend="ne_set_server_auth"/>; complete SSL/TLS support is also
+    included, see <xref linkend="ne_ssl_set_verify"/>.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Conventions</title>
+
+    <para>Some conventions are used throughout the neon API, to
+    provide a consistent and simple interface; these are documented
+    below.</para>
+
+  </refsect1>
+
+  <refsect2>
+    <title>URI paths, WebDAV metadata</title>
+
+    <para>The path strings passed to any function must be
+    <emphasis>URI-encoded</emphasis> by the application: neon never
+    performs any URI encoding or decoding automatically.  WebDAV
+    property names and values must be used un UTF-8.</para>
+
+  </refsect2>
+
+  <refsect2>
+    <title>Memory handling</title>
+
+    <para>neon does not attempt to cope gracefully with an
+    out-of-memory situation; instead, by default,
+    <function>abort</function> is called to terminate the application.
+    Optionally an application-provided function be called before
+    abort; see <xref linkend="ne_oom_callback"/>.</para>
+  
+  </refsect2>
+
+  <refsect2>
+    <title>Callbacks and userdata</title>
+
+    <para>Whenever a callback is registered, a
+    <literal>userdata</literal> variable is also used to allow the
+    application to associate a context with the callback.  The
+    userdata is of type <type>void *</type>, allowing any pointer to
+    be used.</para>
+
+  </refsect2>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para><xref linkend="refsess"/>, <xref linkend="refalloc"/></para>
+  </refsect1>
+
+</refentry>
+
diff --git a/neon/doc/ref/opts.xml b/neon/doc/ref/opts.xml
new file mode 100644 (file)
index 0000000..fe8f368
--- /dev/null
@@ -0,0 +1,126 @@
+    <refentry id="refopts">
+
+      <refmeta>
+       <refentrytitle>ne_set_useragent</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_set_useragent">ne_set_useragent</refname>
+       <refname id="ne_set_persist">ne_set_persist</refname>
+       <refname id="ne_set_read_timeout">ne_set_read_timeout</refname>
+       <refname id="ne_set_expect100">ne_set_expect100</refname>
+       <refpurpose>common settings for HTTP sessions</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_useragent</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>const char *<parameter>product</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_persist</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>int <parameter>flag</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_read_timeout</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>int <parameter>timeout</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_expect100</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>int <parameter>flag</parameter></paramdef>
+         </funcprototype>
+
+<!--
+         <funcprototype>
+           <funcdef>const char *<function>ne_get_scheme</function></funcdef>
+           <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>const char *<function>ne_get_server_hostport</function></funcdef>
+           <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+         </funcprototype>
+-->
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+       
+       <!-- TODO: intro para? -->
+
+       <para>The <literal>User-Agent</literal> request header is used
+to identify the software which generated the request for statistical
+or debugging purposes.  neon does not send a
+<literal>User-Agent</literal> header unless a call is made to the
+<function>ne_set_useragent</function>.
+<function>ne_set_useragent</function> must be passed a product string
+conforming to RFC2616's product token grammar; of the form
+<literal>"Product/Version"</literal>.</para>
+
+       <para>By default neon will use a persistent connection
+whenever possible.  For specific applications, or for debugging
+purposes, it is sometimes useful to disable persistent connections.
+The <function>ne_set_persist</function> function will disable
+persistent connections if passed a <parameter>flag</parameter>
+parameter of <literal>0</literal>, and will enable them
+otherwise.</para>
+
+       <para>When neon reads from a socket, by default the read
+operation will time out after 60 seconds, and the request will fail
+giving an <errorcode>NE_TIMEOUT</errorcode> error.  To configure this
+timeout interval, call <function>ne_set_read_timeout</function> giving
+the desired number of seconds as the <parameter>timeout</parameter>
+parameter.</para>
+
+       <para>An extension introduced in the HTTP/1.1 specification
+was the use of the <literal>Expect: 100-continue</literal> header.
+This header allows an HTTP client to be informed of the expected
+response status before the request message body is sent: a useful
+optimisation for situations where a large message body is to be sent.
+The <function>ne_set_expect100</function> function can be used to
+enable this feature by passing the <parameter>flag</parameter>
+parameter as any non-zero integer.</para>
+
+<warning><para>Unfortunately, if this header is sent to a server which
+is not fully compliant with the HTTP/1.1 specification, a deadlock
+occurs resulting in a temporarily "hung" connection.  neon will
+recover gracefully from this situation, but only after a 15 second
+timeout.  It is highly recommended that this option is not enabled
+unless it is known that the server in use correctly implements
+<literal>Expect: 100-continue</literal> support.</para></warning>
+
+      </refsect1>      
+
+      <refsect1>
+       <title>Examples</title>
+       <para>Set a user-agent string:</para>
+       <programlisting>&egsess;
+ne_set_useragent(sess, "MyApplication/2.1");</programlisting>
+
+       <para>Disable use of persistent connections:</para>
+       <programlisting>ne_session *sess = ne_session_create(...);
+ne_set_persist(sess, 0);</programlisting>
+
+       <para>Set a 30 second read timeout:</para>
+       <programlisting>&egsess;
+ne_set_read_timeout(sess, 30);</programlisting>
+
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/req.xml b/neon/doc/ref/req.xml
new file mode 100644 (file)
index 0000000..85394cb
--- /dev/null
@@ -0,0 +1,170 @@
+    <refentry id="refreq">
+
+      <refmeta>
+       <refentrytitle>ne_request_create</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_request_create">ne_request_create</refname>
+       <refname id="ne_request_dispatch">ne_request_dispatch</refname>
+       <refname id="ne_request_destroy">ne_request_destroy</refname>
+       <refpurpose>low-level HTTP request handling</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>ne_request *<function>ne_request_create</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>const char *<parameter>method</parameter></paramdef>
+           <paramdef>const char *<parameter>path</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>int <function>ne_request_dispatch</function></funcdef>
+           <paramdef>ne_request *<parameter>req</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_request_destroy</function></funcdef>
+           <paramdef>ne_request *<parameter>req</parameter></paramdef>
+         </funcprototype>
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>An HTTP request, represented by the
+<type>ne_request</type> type, specifies that some operation is to be
+performed on some resource.  The
+<function>ne_request_create</function> function creates a request
+object, specifying the operation in the <parameter>method</parameter>
+parameter. The location of the resource is determined by the server in
+use for the session given by the <parameter>sess</parameter> parameter,
+combined with the <parameter>path</parameter> parameter.  The
+<parameter>path</parameter> string used must conform to the 
+<literal>abs_path</literal> definition given in RFC2396.</para>
+
+       <para>To dispatch a request, and process the response, the
+<function>ne_request_dispatch</function> function can be used.  An
+alternative is to use the (more complex, but more flexible)
+combination of the <function>ne_begin_request</function>,
+<function>ne_end_request</function>, and
+<function>ne_read_response_block</function> functions; see <xref
+linkend="ne_begin_request"/>.</para>
+
+       <para>To add extra headers in the request, the functions <xref
+linkend="ne_add_request_header"/> and <xref
+linkend="ne_print_request_header"/> can be used.  To include a message
+body with the request, one of the functions <xref
+linkend="ne_set_request_body_buffer"/>, <xref
+linkend="ne_set_request_body_fd"/>, or <xref
+linkend="ne_set_request_body_provider"/> can be used.</para>
+
+       <para>The return value of
+<function>ne_request_dispatch</function> indicates merely whether the
+request was sent and the response read successfully.  To discover the
+result of the operation, use <function><xref linkend="ne_get_status"/></function>, along with
+any processing of the response headers and message body.</para>
+
+       <para>A request can only be dispatched once: calling
+<function>ne_request_dispatch</function> more than once on a single
+<type>ne_request</type> object produces undefined behaviour.  Once all
+processing associated a the request object has been completed, use the
+<function>ne_request_destroy</function> function to destroy the
+resources associated with it.  Any subsequent use of the request
+object produces undefined behaviour.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Return value</title>
+
+       <para>The <function>ne_request_create</function> function
+returns a pointer to a request object (and never &null;).</para>
+
+       <para>The <function>ne_request_dispatch</function> function
+returns zero if the request was dispatched successfully, and a
+non-zero error code otherwise.</para>
+
+      </refsect1>
+
+      <refsect1>
+        <title>Notes</title>
+       
+       <para><!-- TODO: abs_path description --></para>
+
+      </refsect1>
+
+
+      <refsect1>
+       <title>Errors</title>
+
+       <variablelist>
+         <varlistentry><term><errorcode>NE_ERROR</errorcode></term>
+           <listitem>
+             <para>Request failed (see session error string)</para>
+           </listitem>
+         </varlistentry>
+         <varlistentry><term><errorcode>NE_LOOKUP</errorcode></term>
+           <listitem>
+             <para>The DNS lookup for the server (or proxy server) failed.</para>
+           </listitem>
+         </varlistentry>
+         <varlistentry><term><errorcode>NE_AUTH</errorcode></term>
+           <listitem>
+             <para>Authentication failed on the server.</para>
+           </listitem>
+         </varlistentry>
+         <varlistentry><term><errorcode>NE_PROXYAUTH</errorcode></term>
+           <listitem>
+             <para>Authentication failed on the proxy server.</para>
+           </listitem>
+         </varlistentry>
+         <varlistentry><term><errorcode>NE_CONNECT</errorcode></term>
+           <listitem>
+             <para>A connection to the server could not be established.</para>
+           </listitem>
+         </varlistentry>
+         <varlistentry><term><errorcode>NE_TIMEOUT</errorcode></term>
+           <listitem>
+             <para>A timeout occurred while waiting for the server to respond.</para>
+           </listitem>
+         </varlistentry>
+       </variablelist>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Example</title>
+       
+       <para>An example of applying a <literal>MKCOL</literal>
+       operation to the resource at the location
+       <literal>http://www.example.com/foo/bar/</literal>:</para>
+
+       <programlisting>ne_session *sess = ne_session_create("http", "www.example.com", 80);
+ne_request *req = ne_request_create(sess, "MKCOL", "/foo/bar/");
+if (ne_request_dispatch(req)) {
+   printf("Request failed: %s\n", ne_get_error(sess));
+}
+ne_request_destroy(req);</programlisting>
+      </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+       
+       <para><xref linkend="ne_get_error"/>, <xref
+linkend="ne_set_error"/>, <xref linkend="ne_get_status"/>, <xref
+linkend="ne_add_request_header"/>, <xref
+linkend="ne_set_request_body_buffer"/>.</para>
+
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/reqbody.xml b/neon/doc/ref/reqbody.xml
new file mode 100644 (file)
index 0000000..e2c8eb3
--- /dev/null
@@ -0,0 +1,69 @@
+    <refentry id="refreqbody">
+
+      <refmeta>
+       <refentrytitle>ne_set_request_body_buffer</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_set_request_body_buffer">ne_set_request_body_buffer</refname>
+       <refname id="ne_set_request_body_fd">ne_set_request_body_fd</refname>
+       <refpurpose>include a message body with a request</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_request_body_buffer</function></funcdef>
+           <paramdef>ne_request *<parameter>req</parameter></paramdef>
+           <paramdef>const char *<parameter>buf</parameter></paramdef>
+           <paramdef>size_t <parameter>count</parameter></paramdef>
+         </funcprototype>
+
+         <!-- this is a better interface for set_request_body_fd:
+         <funcprototype>
+           <funcdef>int <function>ne_set_request_body_fd</function></funcdef>
+           <paramdef>ne_request *<parameter>req</parameter></paramdef>
+           <paramdef>int <parameter>fd</parameter></paramdef>
+           <paramdef>off_t <parameter>begin</parameter></paramdef>
+           <paramdef>size_t <parameter>count</parameter></paramdef>
+         </funcprototype>
+         -->
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>The <function>ne_set_request_body_buffer</function>
+function specifies that a message body should be included with the
+body, which is stored in the <parameter>count</parameter> bytes buffer
+<parameter>buf</parameter>.</para>
+
+<!--
+       <para>The <function>ne_set_request_body_fd</function> function
+can be used to include a message body with a request which is read
+from a file descriptor.  The body is read from the file descriptor
+<parameter>fd</parameter>, which must be a associated with a seekable
+file (not a pipe, socket, or FIFO).  <parameter>count</parameter>
+bytes are read, beginning at offset <parameter>begin</parameter>
+(passing <parameter>begin</parameter> as zero means the body is read
+from the beginning of the file).</para>
+
+-->
+
+      </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+
+       <para><xref linkend="ne_request_create"/></para>
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/reqhdr.xml b/neon/doc/ref/reqhdr.xml
new file mode 100644 (file)
index 0000000..4d811b1
--- /dev/null
@@ -0,0 +1,63 @@
+    <refentry id="refreqhdr">
+
+      <refmeta>
+       <refentrytitle>ne_add_request_header</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_add_request_header">ne_add_request_header</refname>
+       <refname id="ne_print_request_header">ne_print_request_header</refname>
+       <refpurpose>add headers to a request</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_request.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>void <function>ne_add_request_header</function></funcdef>
+           <paramdef>ne_request *<parameter>request</parameter></paramdef>
+           <paramdef>const char *<parameter>name</parameter></paramdef>
+           <paramdef>const char *<parameter>value</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_print_request_header</function></funcdef>
+           <paramdef>ne_request *<parameter>request</parameter></paramdef>
+           <paramdef>const char *<parameter>name</parameter></paramdef>
+           <paramdef>const char *<parameter>format</parameter></paramdef>
+           <paramdef>...</paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>The functions <function>ne_add_request_header</function>
+and <function>ne_print_request_header</function> can be used to add
+headers to a request, before it is sent.</para>
+
+       <para><function>ne_add_request_header</function> simply adds a
+header of given <parameter>name</parameter>, with given
+<parameter>value</parameter>.</para>
+
+       <para><function>ne_print_request_header</function> adds a
+header of given <parameter>name</parameter>, taking the value from the
+<function>printf</function>-like <parameter>format</parameter> string
+parameter and subsequent variable-length argument list.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+
+       <para><xref linkend="ne_request_create"/></para>
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/resolve.xml b/neon/doc/ref/resolve.xml
new file mode 100644 (file)
index 0000000..9314880
--- /dev/null
@@ -0,0 +1,160 @@
+<refentry id="refresolve">
+
+  <refmeta>
+    <refentrytitle>ne_addr_resolve</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname id="ne_addr_resolve">ne_addr_resolve</refname>
+    <refname id="ne_addr_result">ne_addr_result</refname>
+    <refname id="ne_addr_first">ne_addr_first</refname>
+    <refname id="ne_addr_next">ne_addr_next</refname>
+    <refname id="ne_addr_print">ne_addr_print</refname>
+    <refname id="ne_addr_error">ne_addr_error</refname>
+    <refname id="ne_addr_destroy">ne_addr_destroy</refname>
+    <refpurpose>functions to resolve hostnames to addresses</refpurpose>
+  </refnamediv>
+  
+  <refsynopsisdiv>
+
+    <funcsynopsis>
+
+      <funcsynopsisinfo>#include &lt;ne_socket.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>ne_sock_addr *<function>ne_addr_resolve</function></funcdef>
+        <paramdef>const char *<parameter>hostname</parameter></paramdef>
+        <paramdef>int <parameter>flags</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>ne_addr_result</function></funcdef>
+        <paramdef>const ne_sock_addr *<parameter>addr</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>ne_inet_addr *<function>ne_addr_first</function></funcdef>
+        <paramdef>ne_sock_addr *<parameter>addr</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>ne_inet_addr *<function>ne_addr_next</function></funcdef>
+        <paramdef>ne_sock_addr *<parameter>addr</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>char *<function>ne_addr_print</function></funcdef>
+        <paramdef>const ne_inet_addr *<parameter>iaddr</parameter></paramdef>
+        <paramdef>char *<parameter>buffer</parameter></paramdef>
+        <paramdef>size_t <parameter>bufsiz</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>char *<function>ne_addr_error</function></funcdef>
+        <paramdef>const ne_sock_addr *<parameter>addr</parameter></paramdef>
+        <paramdef>char *<parameter>buffer</parameter></paramdef>
+        <paramdef>size_t <parameter>bufsiz</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>void <function>ne_addr_destroy</function></funcdef>
+        <paramdef>ne_sock_addr *<parameter>addr</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>The <function>ne_addr_resolve</function> function resolves
+    the given <parameter>hostname</parameter>, returning an
+    <type>ne_sock_addr</type> object representing the address (or
+    addresses) associated with the hostname.  The
+    <parameter>flags</parameter> parameter is currently unused, and
+    should be passed as 0.</para>
+
+    <para>The <parameter>hostname</parameter> passed to
+    <function>ne_addr_resolve</function> can be a DNS hostname
+    (e.g. <literal>www.example.com</literal>) or an IPv4 dotted quad
+    (e.g. <literal>192.0.34.72</literal>); or, on systems which
+    support IPv6, an IPv6 hex address, which may be enclosed in
+    brackets, e.g. <literal>[::1]</literal>.</para>
+
+    <para>To determine whether the hostname was successfully resolved,
+    the <function>ne_addr_result</function> function is used, which
+    returns non-zero if an error occurred.  If an error did occur, the
+    <function>ne_addr_error</function> function can be used, which
+    will copy the error string into a given
+    <parameter>buffer</parameter> (of size
+    <parameter>bufsiz</parameter>.</para>
+
+    <para>The functions <function>ne_addr_first</function> and
+    <function>ne_addr_next</function> are used to retrieve the
+    Internet addresses associated with an address object which has
+    been successfully resolved.  <function>ne_addr_first</function>
+    returns the first address; <function>ne_addr_next</function>
+    returns the next address after the most recent call to
+    <function>ne_addr_next</function> or
+    <function>ne_addr_first</function>, or &null; if there are no more
+    addresses.  The <type>ne_inet_addr</type> pointer returned by
+    these functions can be passed to
+    <function>ne_sock_connect</function> to connect a socket.</para>
+
+    <para>To return the string representation of a particular network
+    address, the <function>ne_addr_print</function> function can be 
+    used.</para>
+
+    <para>After the address object has been used, it should be
+    destroyed using <function>ne_addr_destroy</function>.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+
+    <para><function>ne_addr_resolve</function> returns a pointer to an
+    address object, and never &null;.
+    <function>ne_addr_error</function> and
+    <function>ne_addr_print</function> both return the
+    <parameter>buffer</parameter> parameter .</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <para>The code below prints out the set of addresses associated
+    with the hostname <literal>www.google.com</literal>.</para>
+
+    <programlisting>ne_sock_addr *addr;
+char buf[256];
+
+addr = ne_addr_resolve("www.google.com", 0);
+if (ne_addr_result(addr)) {
+    printf("Could not resolve www.google.com: %s\n",
+           ne_addr_error(addr, buf, sizeof buf));
+} else {
+    ne_inet_addr *ia;
+    printf("www.google.com:");
+    for (ia = ne_addr_first(addr); ia != NULL; ia = ne_addr_next(addr)) {
+        printf(" %s", ne_addr_print(ia, buf, sizeof buf));
+    }
+    putchar('\n');
+}
+ne_addr_destroy(addr);
+</programlisting>
+  </refsect1>
+
+<!--
+  <refsect1>
+    <title>See also</title>
+
+    <para><xref linkend="ne_sock_connect"/></para>
+  </refsect1>
+-->
+
+</refentry>
+
diff --git a/neon/doc/ref/sess.xml b/neon/doc/ref/sess.xml
new file mode 100644 (file)
index 0000000..b3b0047
--- /dev/null
@@ -0,0 +1,111 @@
+     <refentry id="refsess">
+
+      <refentryinfo>
+       <title>neon</title>
+      </refentryinfo>
+      
+      <refmeta>
+       <refentrytitle>ne_session_create</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_session_create">ne_session_create</refname>
+       <refname id="ne_close_connection">ne_close_connection</refname>
+       <refname id="ne_session_proxy">ne_session_proxy</refname>
+       <refname id="ne_session_destroy">ne_session_destroy</refname>
+       <refpurpose>set up HTTP sessions</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+         <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+         <funcprototype>
+           <funcdef>ne_session *<function>ne_session_create</function></funcdef>
+           <paramdef>const char *<parameter>scheme</parameter></paramdef>
+           <paramdef>const char *<parameter>hostname</parameter></paramdef>
+           <paramdef>int <parameter>port</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_session_proxy</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>const char *<parameter>hostname</parameter></paramdef>
+           <paramdef>int <parameter>port</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_close_connection</function></funcdef>
+           <paramdef>ne_sesssion *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_session_destroy</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>An <type>ne_session</type> object represents an HTTP
+session, which groups a set of HTTP requests made to a certain server.
+Any requests made using the session can use a persistent connection,
+and share cached authentication credentials and other common 
+attributes.</para>
+
+       <para>A new HTTP session is created using
+<function>ne_session_create</function>, giving the
+<parameter>hostname</parameter> and <parameter>port</parameter> of the
+server to use, along with the <parameter>scheme</parameter> used to
+contact the server (usually <literal>"http"</literal>).</para>
+
+       <para>To enable SSL/TLS for the session, pass the string
+<literal>"https"</literal> as the <parameter>scheme</parameter>
+parameter, and either register a certificate verification function
+(see <xref linkend="ne_ssl_set_verify"/>) or load the appropriate CA
+certificate (see <xref linkend="ne_ssl_load_ca"/>, <xref
+linkend="ne_ssl_load_default_ca"/>).</para>
+
+       <para>If an HTTP proxy server should be used for the session,
+<function>ne_session_proxy</function> must be called giving the
+hostname and port on which to contact the proxy.</para>
+
+       <para>If it is known that the session will not be used for a
+significant period of time, <function>ne_close_connection</function>
+can be called to close the connection, if one remains open.  Use of
+this function is entirely optional, but it must not be called if there
+is a request active using the session.</para>
+
+       <para>Once a session has been completed,
+<function>ne_session_destroy</function> must be called to destroy the
+resources associated with the session.  Any subsequent use of the
+session pointer produces undefined behaviour.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Return Values</title>
+       <para><function>ne_session_create</function> will return
+       a pointer to a new session object (and never &null;).</para>
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+       <para>Create and destroy a session:</para>
+       <programlisting>ne_session *sess;
+sess = ne_session_create("http", "host.example.com", 80);
+/* ... use sess ... */
+ne_session_destroy(sess);
+</programlisting>
+      </refsect1>
+
+      <refsect1>
+       <title>See Also</title>
+       <para><xref linkend="ne_ssl_set_verify"/>, <xref linkend="ne_ssl_load_ca"/></para>
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/shave.xml b/neon/doc/ref/shave.xml
new file mode 100644 (file)
index 0000000..56881c1
--- /dev/null
@@ -0,0 +1,51 @@
+    <refentry id="refshave">
+
+      <refmeta>
+       <refentrytitle>ne_shave</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname>ne_shave</refname>
+       <refpurpose>whitespace trimmer</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>char *<function>ne_shave</function></funcdef>
+           <paramdef>char *<parameter>str</parameter></paramdef>
+           <paramdef>const char *<parameter>whitespace</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para><function>ne_shave</function> returns a portion of
+<parameter>str</parameter> with any leading or trailing characters in
+the <parameter>whitespace</parameter> array removed.
+<parameter>str</parameter> may be modified.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+
+       <para>The following code segment will output
+       <literal>"fish"</literal>:</para>
+       
+       <programlisting>char s[] = ".!.fish!.!";
+puts(ne_shave(s, ".!"));</programlisting>
+
+      </refsect1>
+
+    </refentry>
+
diff --git a/neon/doc/ref/sslca.xml b/neon/doc/ref/sslca.xml
new file mode 100644 (file)
index 0000000..cbac071
--- /dev/null
@@ -0,0 +1,81 @@
+    <refentry id="refsslca">
+
+      <refmeta>
+       <refentrytitle>ne_ssl_load_ca</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_ssl_load_ca">ne_ssl_load_ca</refname>
+       <refname id="ne_ssl_load_default_ca">ne_ssl_load_default_ca</refname>
+       <refpurpose>load SSL Certificate Authorities</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>int <function>ne_ssl_load_ca</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>const char *<parameter>filename</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>int <function>ne_ssl_load_default_ca</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>To indicate that a given CA certificate is trusted by the user,
+the certificate can be loaded using the <function>ne_ssl_load_ca</function>
+function.  The <parameter>filename</parameter> parameter given must specify
+the location of a PEM-encoded CA certificate.</para>
+
+       <para>The SSL library in use by neon may include a default set
+of CA certificates; calling the
+<function>ne_ssl_load_default_ca</function> function will indicate
+that these CAs are trusted by the user.</para>
+
+       <para>If no CA certificates are loaded, or the server presents
+a certificate which is invalid in some way, then the certificate must
+be manually verified (see <xref linkend="ne_ssl_set_verify"/>), otherwise the
+connection will fail.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Return value</title>
+
+       <para>Both <function>ne_ssl_load_ca</function> and
+<function>ne_ssl_load_default_ca</function> functions return
+<literal>0</literal> on success, or non-zero on failure.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+
+       <para>Load the CA certificate stored in <filename>/path/to/cacert.pem</filename>:</para>
+       <programlisting>&egsess;
+
+if (ne_ssl_load_ca(sess, "/path/to/cacert.pem")) {
+   printf("Could not load CA cert: %s\n", ne_get_error(sess));
+}</programlisting>
+      </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+
+       <para><xref linkend="ne_ssl_get_error"/>, <xref
+       linkend="ne_ssl_set_verify"/></para> </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/sslcert.xml b/neon/doc/ref/sslcert.xml
new file mode 100644 (file)
index 0000000..9696a45
--- /dev/null
@@ -0,0 +1,66 @@
+    <refentry id="refsslcert">
+
+      <refmeta>
+       <refentrytitle>ne_ssl_certificate</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_ssl_certificate">ne_ssl_certificate</refname>
+       <refname id="ne_ssl_dname">ne_ssl_dname</refname>
+       <refpurpose>structures representing SSL certificates</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+
+       <programlisting>#include &lt;ne_session.h&gt;
+
+/* A simplified X.509 distinguished name. */
+typedef struct {
+    const char *country, *state, *locality, *organization;
+    const char *organizationalUnit;
+    const char *commonName;
+} <type>ne_ssl_dname</type>;
+
+/* A simplified SSL certificate. */
+typedef struct {
+    const <type>ne_ssl_dname</type> *subject, *issuer;
+    const char *from, *until;
+} <type>ne_ssl_certificate</type>;
+
+</programlisting>
+
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+       
+       <para>The <type>ne_ssl_dname</type> structure is used to
+represent a simplified X.509 distinguished name, as used in SSL
+certificates; a distinguished name is used to uniquely identify an
+entity.  Along with the fields giving the geographical and
+organizational location of the entity, the
+<structfield>commonName</structfield> field will be assigned the DNS
+hostname of the entity.  The
+<function>ne_ssl_readable_dname</function> function can be used to
+create a single-line string out of an <type>ne_ssl_dname</type>
+structure.</para>
+
+       <para>The <type>ne_ssl_certificate</type> structure is used to
+represent a simplified SSL certificate; containing the distinguished
+names of the <firstterm>issuer</firstterm> and
+<firstterm>subject</firstterm> of the certificate.  The issuer is the
+entity which has digitally signed the certificate to guarantee its
+authenticity; the subject is the owner of the certificate.  A
+certificate is only valid for a certain period of time: the
+<structfield>from</structfield> and <structfield>until</structfield>
+contain strings giving the validity period.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>See Also</title>
+       <para><xref linkend="ne_ssl_dname"/>, <xref linkend="ne_ssl_set_verify"/></para>
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/ssldname.xml b/neon/doc/ref/ssldname.xml
new file mode 100644 (file)
index 0000000..70df21a
--- /dev/null
@@ -0,0 +1,53 @@
+
+    <refentry id="refssldname">
+
+      <refmeta>
+       <refentrytitle>ne_ssl_readable_dname</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_ssl_readable_dname">ne_ssl_readable_dname</refname> <refpurpose>create a
+       single-line readable string from a distinguised
+       name</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>const char *<function>ne_ssl_readable_dname</function></funcdef>
+           <paramdef>const ne_ssl_dname *<parameter>dname</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>The <function>ne_ssl_readable_dname</function> function
+creates a single-line, human-readable string out of an
+<type>ne_ssl_dname</type> object.  The returned string is
+<function>malloc</function>()-allocated, and must be
+<function>free</function>()d by the caller.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Return value</title>
+
+       <para><function>malloc</function>-allocated string.</para>
+      </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+
+       <para><xref linkend="ne_ssl_certificate"/></para>
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/sslvfy.xml b/neon/doc/ref/sslvfy.xml
new file mode 100644 (file)
index 0000000..ade5028
--- /dev/null
@@ -0,0 +1,140 @@
+    <refentry id="refsslvfy">
+
+      <refmeta>
+       <refentrytitle>ne_ssl_set_verify</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_ssl_set_verify">ne_ssl_set_verify</refname>
+       <refpurpose>register an SSL certificate verification callback</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_session.h&gt;</funcsynopsisinfo>
+
+         <!-- hard to put data type declarations here -->
+
+         <funcprototype>
+           <funcdef>typedef int (*<function>ne_ssl_verify_fn</function>)</funcdef>
+           <paramdef>void *<parameter>userdata</parameter></paramdef>
+           <paramdef>int <parameter>failures</parameter></paramdef>
+           <paramdef>const ne_ssl_certificate *<parameter>cert</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>void <function>ne_ssl_set_verify</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>ne_ssl_verify_fn <parameter>verify_fn</parameter></paramdef>
+           <paramdef>void *<parameter>userdata</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>To enable manual SSL certificate verification, a
+callback can be registered using
+<function>ne_ssl_set_verify</function>.  If such a callback is not
+registered, when a connection is established to an SSL server which
+does not present a certificate signed by a trusted CA (see <xref
+linkend="ne_ssl_load_ca"/>), or if the certificate presented is invalid in
+some way, the connection will fail.</para>
+
+       <para>When the callback is invoked, the
+<parameter>failures</parameter> parameter gives a bitmask indicating
+in what way the automatic certificate verification failed.  The value
+is equal to the bit-wise OR of one or more of the following
+constants (and is guaranteed to be non-zero):</para>
+
+       <variablelist>
+         <varlistentry><term><filename>NE_SSL_NOTYETVALID</filename></term>
+           <listitem>
+             <para>The certificate is not yet valid.</para>
+           </listitem>
+         </varlistentry>
+         <varlistentry><term><filename>NE_SSL_EXPIRED</filename></term>
+           <listitem>
+             <para>The certificate has expired.</para>
+           </listitem>
+         </varlistentry>
+         <varlistentry><term><filename>NE_SSL_CNMISMATCH</filename></term>
+           <listitem>
+             <para>The hostname used for the session does not match
+the hostname to which the certificate was issued: this could mean that
+the connection has been intercepted.</para>
+           </listitem>
+         </varlistentry>
+         <varlistentry><term><filename>NE_SSL_UNKNOWNCA</filename></term>
+           <listitem>
+             <para>The Certificate Authority which signed the certificate
+is not trusted.</para>
+           </listitem>
+         </varlistentry>
+       </variablelist>
+
+       <para>The <parameter>cert</parameter> parameter passed to the
+callback describes the certificate which was presented by the server,
+see <xref linkend="ne_ssl_certificate"/> for more details.  The certificate
+object given is only valid until the callback returns.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Return value</title>
+
+       <para>The verification callback must return zero to indicate
+that the certificate should be trusted; and non-zero otherwise (in
+which case, the connection will fail).</para>
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+
+       <para>Manual certificate verification:</para>
+       <programlisting>
+static int
+my_verify(void *userdata, int failures, const ne_ssl_certificate *cert)
+{
+   /* this leaks return values of ne_ssl_readable_dname for simplicity! */
+   printf("Issuer: %s\n", ne_ssl_readable_dname(cert->issuer);
+   printf("Subject: %s\n", ne_ssl_readable_dname(cert->subject);
+   if (failures &amp; NE_SSL_CNMISMATCH) {
+      printf("Server certificate was issued to `%s'; "
+             "connection may have been intercepted!\n",
+             cert->subject->commonName);
+   }
+   if (failures &amp; NE_SSL_EXPIRED) {
+      printf("Server certificate expired on %s!", cert->until);
+   }
+   /* ... check for other failures ... */
+   if (prompt_user())
+      return 1; /* fail verification */
+   else
+      return 0; /* trust certificate */
+}
+
+int
+main(...)
+{
+    ne_session *sess = ne_session_create("https", "some.host.name", 443);
+    ne_ssl_set_verify(sess, my_verify, NULL);
+    ...
+}</programlisting>
+
+      </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+
+       <para><xref linkend="ne_ssl_certificate"/>, <xref linkend="ne_ssl_load_ca"/>, 
+        <xref linkend="ne_ssl_dname"/>, <xref linkend="ne_ssl_readable_dname"/></para>
+      </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/status.xml b/neon/doc/ref/status.xml
new file mode 100644 (file)
index 0000000..0290ac2
--- /dev/null
@@ -0,0 +1,75 @@
+    <refentry id="refstatus">
+
+      <refmeta>
+       <refentrytitle>ne_status</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname id="ne_status">ne_status</refname>
+       <refpurpose>HTTP status structure</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <programlisting>#include &lt;ne_utils.h&gt;
+
+typedef struct {
+    int major_version, minor_version;
+    int code, klass;
+    const char *reason_phrase;
+} <type>ne_status</type>;</programlisting>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>An <type>ne_status</type> type represents an HTTP
+response status; used in response messages giving a result of request.
+The <structfield>major_version</structfield> and
+<structfield>minor_version</structfield> fields give the HTTP version
+supported by the server issuing the response.  The
+<structfield>code</structfield> field gives the status code of the
+result (lying between 100 and 999 inclusive), and the
+<structfield>klass</structfield> field gives the class, which is equal
+to the most significant digit of the status.</para>
+
+       <para>There are five classes of HTTP status code defined by
+       RFC2616:</para>
+       
+       <variablelist>
+         <varlistentry>
+           <term><literal>1xx</literal></term>
+           <listitem><para>Informational response.</para></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><literal>2xx</literal></term>
+           <listitem><para>Success: the operation was successful</para></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><literal>3xx</literal></term>
+           <listitem><para>Redirection</para></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><literal>4xx</literal></term> <listitem><para>Client
+           error: the request made was incorrect in some
+           manner.</para></listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><literal>5xx</literal></term>
+           <listitem><para>Server error</para></listitem>
+         </varlistentry>
+       </variablelist>
+
+      </refsect1>
+
+      <refsect1> <title>See also</title> <para><xref
+linkend="ne_get_status"/>, <xref
+linkend="ne_parse_status_line"/></para> </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/tok.xml b/neon/doc/ref/tok.xml
new file mode 100644 (file)
index 0000000..2e6211f
--- /dev/null
@@ -0,0 +1,76 @@
+    <refentry id="reftok">
+
+      <refmeta>
+       <refentrytitle>ne_token</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname>ne_token</refname>
+       <refname>ne_qtoken</refname>
+       <refpurpose>string tokenizers</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_string.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>char *<function>ne_token</function></funcdef>
+           <paramdef>char **<parameter>str</parameter></paramdef>
+           <paramdef>char <parameter>sep</parameter></paramdef>
+         </funcprototype>
+
+         <funcprototype>
+           <funcdef>char *<function>ne_qtoken</function></funcdef>
+           <paramdef>char **<parameter>str</parameter></paramdef>
+           <paramdef>char <parameter>sep</parameter></paramdef>
+           <paramdef>const char *<parameter>quotes</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <!-- FIXME: italics on tokenize -->
+
+       <para><function>ne_token</function> and
+<function>ne_qtoken</function> tokenize the string at the location
+stored in the pointer <parameter>str</parameter>.  Each time the
+function is called, it returns the next token, and modifies the
+<parameter>str</parameter> pointer to point to the remainer of the
+string, or &null; if there are no more tokens in the string.  A token
+is delimited by the separator character <parameter>sep</parameter>; if
+<function>ne_qtoken</function> is used any quoted segments of the
+string are skipped when searching for a separator.  A quoted segment
+is enclosed in a pair of one of the characters given in the
+<parameter>quotes</parameter> string.</para>
+
+       <para>The string being tokenized is modified each time
+the tokenizing function is called; replacing the next separator
+character with a &nul; terminator.</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+
+       <para>The following function prints out each token in a
+comma-separated string <parameter>list</parameter>, which is
+modified in-place:</para>
+
+       <programlisting>static void splitter(char *list)
+{
+  do {
+    printf("Token: %s\n", ne_token(&amp;list, ','));
+  while (list);
+}</programlisting>
+
+ </refsect1>
+
+    </refentry>
diff --git a/neon/doc/ref/vers.xml b/neon/doc/ref/vers.xml
new file mode 100644 (file)
index 0000000..9d180fe
--- /dev/null
@@ -0,0 +1,61 @@
+<refentry id="refvers">
+
+  <refmeta>
+    <refentrytitle>ne_version_match</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>ne_version_match</refname>
+    <refname>ne_version_string</refname>
+    <refpurpose>library versioning</refpurpose>
+  </refnamediv>
+  
+  <refsynopsisdiv>
+
+    <funcsynopsis>
+
+      <funcsynopsisinfo>#include &lt;ne_utils.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>ne_version_match</function></funcdef>
+        <paramdef>int <parameter>major</parameter></paramdef>
+        <paramdef>int <parameter>minor</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>const char *<function>ne_version_string</function></funcdef>
+       <void/>
+      </funcprototype>
+
+    </funcsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>The <function>ne_version_match</function> function returns
+    non-zero if the library version is not of major version
+    <parameter>major</parameter>, or the minor version is less than
+    <parameter>minor</parameter>.</para>
+
+    <para>The <function>ne_version_string</function> function returns
+    a string giving the library version.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <para>To require &neon; 1.x, version 1.2 or later:</para>
+
+    <programlisting>if (ne_version_match(1, 2)) {
+    printf("Library version out of date: 1.2 required, found %s.",
+           ne_version_string());
+    exit(1);
+}</programlisting>
+
+  </refsect1>
+
+</refentry>
diff --git a/neon/doc/refentry.xml b/neon/doc/refentry.xml
new file mode 100644 (file)
index 0000000..6610ac7
--- /dev/null
@@ -0,0 +1,59 @@
+
+    <!-- ******************************************************************* -->
+
+    <refentry id="refXXXX">
+
+      <refmeta>
+       <refentrytitle>this_api_call</refentrytitle>
+       <manvolnum>3</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+       <refname>this_api_call</refname>
+       <refname>other_api_call</refname>
+       <refpurpose>general purpose of group of functions</refpurpose>
+      </refnamediv>
+      
+      <refsynopsisdiv>
+       
+       <funcsynopsis>
+
+         <funcsynopsisinfo>#include &lt;ne_header.h&gt;</funcsynopsisinfo>
+
+         <funcprototype>
+           <funcdef>void <function>ne_set_useragent</function></funcdef>
+           <paramdef>ne_session *<parameter>session</parameter></paramdef>
+           <paramdef>const char *<parameter>product</parameter></paramdef>
+         </funcprototype>
+
+       </funcsynopsis>
+       
+      </refsynopsisdiv>
+
+      <refsect1>
+       <title>Description</title>
+
+       <para>XXX</para>
+
+      </refsect1>
+
+      <refsect1>
+       <title>Return value</title>
+
+       <para>XXX</para>
+      </refsect1>
+
+      <refsect1>
+       <title>Examples</title>
+
+       <para>XXX</para>
+      </refsect1>
+
+      <refsect1>
+       <title>See also</title>
+
+       <para>XXX</para>
+      </refsect1>
+
+    </refentry>
+
diff --git a/neon/doc/using-neon.txt b/neon/doc/using-neon.txt
new file mode 100644 (file)
index 0000000..5475152
--- /dev/null
@@ -0,0 +1,132 @@
+
+Guide to neon
+=============       Id: using-neon.txt,v 1.4 2000/07/16 16:20:12 joe Exp 
+
+Using libneon from applications
+-------------------------------
+
+The neon source package is designed to be easily incorporated into
+applications:
+
+- autoconf macros are distributed in the 'macros' subdirectory of the
+  neon distribution.  Use NEON_LIBRARY from your configure.in to check
+  for the presence of the neon library installed on the system.  The
+  macro adds an '--with-neon=...' argument to configure, which allows
+  the user to specify a location for the library (the standard /usr
+  and /usr/local directories are checked automatically without having
+  to be specified).
+
+- The 'src' directory of the neon package can be imported directly
+  into your application, if you do not wish to add an external
+  dependency.  The NEON_LIBRARY autoconf macro can fall back to
+  compile and link this source code into your application if libneon
+  is not found on the system.  Place the source code in a subdirectory
+  of your package named 'libneon', and pass the 'bundled' argument to
+  the macro, as follows:
+
+     NEON_LIBRARY([bundled])
+
+  In your Makefile, $NEONOBJS will be added to $LIBOBJS if the bundled
+  neon is required, so you need to set this variable to the set of
+  neon object files which you require in your application, e.g.:
+
+     NEONOBJS = libneon/http_utils.o libneon/http_request.o \
+               libneon/http_auth.o libneon/string_utils.o \
+               libneon/socket.o libneon/md5.o libneon/dates.o
+     LIBOBJS = @LIBOBJS@  
+     LIBS = @LIBS@
+     CFLAGS = @CFLAGS@ etc...
+
+  CFLAGS and LIBS will be adjusted by the NEON_LIBRARY as appropriate
+  for compiling against the bundled sources or the existing library.
+
+The neon API
+============
+
+neon offers two levels of API for use in applications:
+
+- Low-level HTTP request/response handling
+- High-level method invocation
+
+The low-level interface allows for easily designing new method
+handlers, taking care of things like persistent connections,
+authentication, and proxy servers.  The high-level interface allows
+you to easily integrate existing HTTP (and WebDAV) methods into your
+application.  This document details both interfaces.
+
+N.B.: Documentation is always WRONG.  The definitive API reference is in
+src/*.c, with src/*.h is hopefully fairly similar.
+
+An Important Note
+-----------------
+
+Most neon functions which allocate memory with malloc() will call
+abort() if malloc() returns NULL.  This is a design decision, the
+rationale being:
+
+- it makes the interfaces cleaner.
+
+- if malloc() DOES return NULL there is not much you can do about it.
+
+- Apparently, malloc() won't return NULL on systems which over-commit
+  memory (e.g. Linux), so it doesn't make any real difference anyway.
+
+The author is open to persuasion on this: mail neon@webdav.org.
+
+The http_session type
+---------------------
+
+The http_session type is used whether you are writing to the low-level
+or the high-level interface.  An http_session object is created to
+store data which persists beyond a single HTTP request:
+
+ - Protocol options, e.g. (proxy) server details
+ - Authentication information
+ - Persistent connection
+
+A session is created with the 'http_session_create' call.  Before
+creating a request for the session, the server details must be set, as
+follows:
+
+   http_session *sess;
+   /* Initialize the socket library */
+   sock_init();
+   /* Create the session */
+   sess = http_session_create();
+   /* Optionally, set a proxy server */
+   http_session_proxy(sess, "proxy.myisp.com", 8080);
+   /* Set the server */
+   http_session_server(sess, "my.server.com", 80);
+
+The proxy server should be set BEFORE the origin server; otherwise a
+DNS lookup will be performed on the origin server by the
+http_session_server call, which may well fail if the client is
+firewalled.  http_session_{proxy,server} will return HTTP_LOOKUP if
+the DNS lookup fails; otherwise HTTP_OK.
+
+The 'http_set_expect100' call can be used to determine whether the
+"Expect: 100-continue" header is sent with requests.  This header,
+when supported correctly by the server, means that an error response
+(e.g. 401) can be returned before the request body is sent.  This is
+useful if you are doing a PUT with a 100mb request body.  Note that
+Apache/1.3.6 and before do not implement this feature correctly, so
+use with care.  http_options can tell you whether the Apache 
+
+The 'http_set_persist' call can be used to turn off persistent
+connection handling: it is on by default.
+
+The 'http_set_useragent' call can be used to set the User-Agent header
+to be sent with requests.  A product token of the form
+"myhttpclient/0.1.2" should be passed, and will have "neon/x.y.z"
+appended in the actual header sent.
+
+When a session has been finished with, it should be destroyed using
+http_session_destroy.  Any subsequent operations on the session object
+will have undefined results (i.e. will segfault).
+
+Low-level HTTP Request/Response Handling
+----------------------------------------
+
+...
+
+
diff --git a/neon/doc/xref-man.xsl b/neon/doc/xref-man.xsl
new file mode 100644 (file)
index 0000000..249bda5
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version='1.0'?>
+<!-- vim:set sts=2 shiftwidth=2 syntax=sgml: -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                version='1.0'>
+
+<!-- THIS FILE IS UNDER WHATEVER LICENSE xmlto's XSL is -->
+
+<xsl:template match="xref">
+  <xsl:variable name="targets" select="id(@linkend)"/>
+  <xsl:variable name="target" select="$targets[1]"/>
+  <xsl:variable name="type" select="local-name($target)"/>
+
+  <xsl:choose>
+    <xsl:when test="$type=''">
+      <xsl:message>
+        <xsl:text>xref to nonexistent id </xsl:text>
+        <xsl:value-of select="@linkend"/>
+      </xsl:message>
+    </xsl:when>
+
+    <xsl:when test="$type='refentry'">
+      <xsl:call-template name="do-citerefentry">
+        <xsl:with-param name="refentrytitle"
+                        select="$target/refmeta/refentrytitle[1]"/>
+        <xsl:with-param name="manvolnum"
+                       select="$target/refmeta/manvolnum"/>
+      </xsl:call-template>
+    </xsl:when>
+
+    <xsl:when test="$type='refname'">
+      <xsl:call-template name="do-citerefentry">
+        <xsl:with-param name="refentrytitle" select="$target"/>
+        <xsl:with-param name="manvolnum"
+          select="$target/../../refmeta/manvolnum"/>
+      </xsl:call-template>
+    </xsl:when>
+
+    <xsl:otherwise>
+      <xsl:text>[xref to </xsl:text>
+      <xsl:value-of select="$type"/>
+      <xsl:text>]</xsl:text>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/neon/example/.cvsignore b/neon/example/.cvsignore
new file mode 100644 (file)
index 0000000..e19f123
--- /dev/null
@@ -0,0 +1,2 @@
+*.lo
+
diff --git a/neon/example/nbrowse.glade b/neon/example/nbrowse.glade
new file mode 100644 (file)
index 0000000..d557925
--- /dev/null
@@ -0,0 +1,284 @@
+<?xml version="1.0"?>
+<GTK-Interface>
+
+<project>
+  <name>nbrowse</name>
+  <program_name>nbrowse</program_name>
+  <directory></directory>
+  <source_directory>nbrowse</source_directory>
+  <pixmaps_directory>nbrowse</pixmaps_directory>
+  <language>C</language>
+  <gnome_support>True</gnome_support>
+  <gettext_support>True</gettext_support>
+  <output_support_files>False</output_support_files>
+  <output_build_files>False</output_build_files>
+</project>
+
+<widget>
+  <class>GtkWindow</class>
+  <name>nbrowse</name>
+  <signal>
+    <name>remove</name>
+    <handler>gtk_main_quit</handler>
+    <last_modification_time>Sun, 18 Jun 2000 11:49:12 GMT</last_modification_time>
+  </signal>
+  <title>WebDAV Browser</title>
+  <type>GTK_WINDOW_TOPLEVEL</type>
+  <position>GTK_WIN_POS_CENTER</position>
+  <modal>False</modal>
+  <default_width>400</default_width>
+  <default_height>300</default_height>
+  <allow_shrink>False</allow_shrink>
+  <allow_grow>True</allow_grow>
+  <auto_shrink>False</auto_shrink>
+
+  <widget>
+    <class>GtkVBox</class>
+    <name>vbox1</name>
+    <homogeneous>False</homogeneous>
+    <spacing>0</spacing>
+
+    <widget>
+      <class>GtkMenuBar</class>
+      <name>menubar1</name>
+      <shadow_type>GTK_SHADOW_OUT</shadow_type>
+      <child>
+       <padding>0</padding>
+       <expand>False</expand>
+       <fill>False</fill>
+      </child>
+
+      <widget>
+       <class>GtkMenuItem</class>
+       <name>file1</name>
+       <stock_item>GNOMEUIINFO_MENU_FILE_TREE</stock_item>
+
+       <widget>
+         <class>GtkMenu</class>
+         <name>file1_menu</name>
+
+         <widget>
+           <class>GtkMenuItem</class>
+           <name>change_server1</name>
+           <signal>
+             <name>activate</name>
+             <handler>on_change_server1_activate</handler>
+             <last_modification_time>Thu, 15 Jun 2000 10:47:10 GMT</last_modification_time>
+           </signal>
+           <label>Change server...</label>
+           <right_justify>False</right_justify>
+         </widget>
+
+         <widget>
+           <class>GtkMenuItem</class>
+           <name>separator3</name>
+           <right_justify>False</right_justify>
+         </widget>
+
+         <widget>
+           <class>GtkPixmapMenuItem</class>
+           <name>exit1</name>
+           <signal>
+             <name>activate</name>
+             <handler>on_exit1_activate</handler>
+             <last_modification_time>Thu, 15 Jun 2000 10:46:15 GMT</last_modification_time>
+           </signal>
+           <stock_item>GNOMEUIINFO_MENU_EXIT_ITEM</stock_item>
+         </widget>
+       </widget>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GtkHPaned</class>
+      <name>hpaned1</name>
+      <handle_size>10</handle_size>
+      <gutter_size>6</gutter_size>
+      <position>149</position>
+      <child>
+       <padding>0</padding>
+       <expand>True</expand>
+       <fill>True</fill>
+      </child>
+
+      <widget>
+       <class>GtkScrolledWindow</class>
+       <name>scrolledwindow2</name>
+       <hscrollbar_policy>GTK_POLICY_ALWAYS</hscrollbar_policy>
+       <vscrollbar_policy>GTK_POLICY_ALWAYS</vscrollbar_policy>
+       <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+       <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+       <child>
+         <shrink>True</shrink>
+         <resize>False</resize>
+       </child>
+
+       <widget>
+         <class>GtkTree</class>
+         <name>tree</name>
+         <selection_mode>GTK_SELECTION_BROWSE</selection_mode>
+         <view_mode>GTK_TREE_VIEW_ITEM</view_mode>
+         <view_line>True</view_line>
+       </widget>
+      </widget>
+
+      <widget>
+       <class>GtkScrolledWindow</class>
+       <name>scrolledwindow1</name>
+       <hscrollbar_policy>GTK_POLICY_ALWAYS</hscrollbar_policy>
+       <vscrollbar_policy>GTK_POLICY_ALWAYS</vscrollbar_policy>
+       <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+       <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+       <child>
+         <shrink>True</shrink>
+         <resize>True</resize>
+       </child>
+
+       <widget>
+         <class>GnomeIconList</class>
+         <name>iconlist1</name>
+         <can_focus>True</can_focus>
+         <selection_mode>GTK_SELECTION_SINGLE</selection_mode>
+         <icon_width>78</icon_width>
+         <row_spacing>4</row_spacing>
+         <column_spacing>2</column_spacing>
+         <text_spacing>2</text_spacing>
+         <text_editable>False</text_editable>
+         <text_static>False</text_static>
+       </widget>
+      </widget>
+    </widget>
+  </widget>
+</widget>
+
+<widget>
+  <class>GnomeDialog</class>
+  <name>enter_url</name>
+  <title>Enter URL</title>
+  <type>GTK_WINDOW_DIALOG</type>
+  <position>GTK_WIN_POS_CENTER</position>
+  <modal>True</modal>
+  <allow_shrink>False</allow_shrink>
+  <allow_grow>False</allow_grow>
+  <auto_shrink>False</auto_shrink>
+  <auto_close>False</auto_close>
+  <hide_on_close>False</hide_on_close>
+
+  <widget>
+    <class>GtkVBox</class>
+    <child_name>GnomeDialog:vbox</child_name>
+    <name>dialog-vbox1</name>
+    <homogeneous>False</homogeneous>
+    <spacing>8</spacing>
+    <child>
+      <padding>4</padding>
+      <expand>True</expand>
+      <fill>True</fill>
+    </child>
+
+    <widget>
+      <class>GtkHButtonBox</class>
+      <child_name>GnomeDialog:action_area</child_name>
+      <name>dialog-action_area1</name>
+      <layout_style>GTK_BUTTONBOX_END</layout_style>
+      <spacing>8</spacing>
+      <child_min_width>85</child_min_width>
+      <child_min_height>27</child_min_height>
+      <child_ipad_x>7</child_ipad_x>
+      <child_ipad_y>0</child_ipad_y>
+      <child>
+       <padding>0</padding>
+       <expand>False</expand>
+       <fill>True</fill>
+       <pack>GTK_PACK_END</pack>
+      </child>
+
+      <widget>
+       <class>GtkButton</class>
+       <name>button1</name>
+       <can_default>True</can_default>
+       <can_focus>True</can_focus>
+       <stock_button>GNOME_STOCK_BUTTON_OK</stock_button>
+      </widget>
+
+      <widget>
+       <class>GtkButton</class>
+       <name>button3</name>
+       <can_default>True</can_default>
+       <can_focus>True</can_focus>
+       <stock_button>GNOME_STOCK_BUTTON_CANCEL</stock_button>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GtkHBox</class>
+      <name>hbox1</name>
+      <homogeneous>False</homogeneous>
+      <spacing>0</spacing>
+      <child>
+       <padding>0</padding>
+       <expand>True</expand>
+       <fill>True</fill>
+      </child>
+
+      <widget>
+       <class>GnomePixmap</class>
+       <name>pixmap1</name>
+       <filename>gnome-question.png</filename>
+       <child>
+         <padding>0</padding>
+         <expand>True</expand>
+         <fill>True</fill>
+       </child>
+      </widget>
+
+      <widget>
+       <class>GtkVBox</class>
+       <name>vbox2</name>
+       <border_width>7</border_width>
+       <homogeneous>False</homogeneous>
+       <spacing>6</spacing>
+       <child>
+         <padding>0</padding>
+         <expand>True</expand>
+         <fill>True</fill>
+       </child>
+
+       <widget>
+         <class>GtkLabel</class>
+         <name>label1</name>
+         <label>Enter a URL to browse:
+(for example, http://www.driveway.com/user.myname/)</label>
+         <justify>GTK_JUSTIFY_CENTER</justify>
+         <wrap>False</wrap>
+         <xalign>0.5</xalign>
+         <yalign>0.5</yalign>
+         <xpad>0</xpad>
+         <ypad>0</ypad>
+         <child>
+           <padding>0</padding>
+           <expand>False</expand>
+           <fill>False</fill>
+         </child>
+       </widget>
+
+       <widget>
+         <class>GtkEntry</class>
+         <name>entry1</name>
+         <can_focus>True</can_focus>
+         <editable>True</editable>
+         <text_visible>True</text_visible>
+         <text_max_length>0</text_max_length>
+         <text></text>
+         <child>
+           <padding>0</padding>
+           <expand>False</expand>
+           <fill>False</fill>
+         </child>
+       </widget>
+      </widget>
+    </widget>
+  </widget>
+</widget>
+
+</GTK-Interface>
diff --git a/neon/example/nbrowse/callbacks.c b/neon/example/nbrowse/callbacks.c
new file mode 100644 (file)
index 0000000..3e9f7e9
--- /dev/null
@@ -0,0 +1,40 @@
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <gnome.h>
+
+#include "callbacks.h"
+#include "interface.h"
+#include "support.h"
+
+
+void change_server()
+{
+        
+}
+
+
+void
+on_change_server1_activate             (GtkMenuItem     *menuitem,
+                                        gpointer         user_data)
+{
+
+}
+
+
+void
+on_exit1_activate                      (GtkMenuItem     *menuitem,
+                                        gpointer         user_data)
+{
+
+}
+
+
+void
+on_about1_activate                     (GtkMenuItem     *menuitem,
+                                        gpointer         user_data)
+{
+
+}
+
diff --git a/neon/example/nbrowse/callbacks.h b/neon/example/nbrowse/callbacks.h
new file mode 100644 (file)
index 0000000..e3db59c
--- /dev/null
@@ -0,0 +1,14 @@
+#include <gnome.h>
+
+
+void
+on_change_server1_activate             (GtkMenuItem     *menuitem,
+                                        gpointer         user_data);
+
+void
+on_exit1_activate                      (GtkMenuItem     *menuitem,
+                                        gpointer         user_data);
+
+void
+on_about1_activate                     (GtkMenuItem     *menuitem,
+                                        gpointer         user_data);
diff --git a/neon/example/nbrowse/interface.c b/neon/example/nbrowse/interface.c
new file mode 100644 (file)
index 0000000..6332abd
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * DO NOT EDIT THIS FILE - it is generated by Glade.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <gnome.h>
+
+#include "callbacks.h"
+#include "interface.h"
+#include "support.h"
+
+GtkWidget *tree;
+GtkWidget *iconlist;
+
+static GnomeUIInfo file1_menu_uiinfo[] =
+{
+  {
+    GNOME_APP_UI_ITEM, N_("Change server..."),
+    NULL,
+    on_change_server1_activate, NULL, NULL,
+    GNOME_APP_PIXMAP_NONE, NULL,
+    0, 0, NULL
+  },
+  GNOMEUIINFO_SEPARATOR,
+  GNOMEUIINFO_MENU_EXIT_ITEM (on_exit1_activate, NULL),
+  GNOMEUIINFO_END
+};
+
+static GnomeUIInfo menubar1_uiinfo[] =
+{
+  GNOMEUIINFO_MENU_FILE_TREE (file1_menu_uiinfo),
+  GNOMEUIINFO_END
+};
+
+GtkWidget*
+create_nbrowse (void)
+{
+  GtkWidget *nbrowse;
+  GtkWidget *vbox1;
+  GtkWidget *menubar1;
+  GtkWidget *hpaned1;
+  GtkWidget *scrolledwindow2;
+  GtkWidget *scrolledwindow1;
+
+  nbrowse = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_object_set_data (GTK_OBJECT (nbrowse), "nbrowse", nbrowse);
+  gtk_window_set_title (GTK_WINDOW (nbrowse), _("neon WebDAV Browser"));
+  /*gtk_window_set_position (GTK_WINDOW (nbrowse), GTK_WIN_POS_CENTER); */
+  gtk_window_set_default_size (GTK_WINDOW (nbrowse), 400, 300);
+
+  vbox1 = gtk_vbox_new (FALSE, 0);
+  gtk_widget_ref (vbox1);
+  gtk_object_set_data_full (GTK_OBJECT (nbrowse), "vbox1", vbox1,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (vbox1);
+  gtk_container_add (GTK_CONTAINER (nbrowse), vbox1);
+
+  menubar1 = gtk_menu_bar_new ();
+  gtk_widget_ref (menubar1);
+  gtk_object_set_data_full (GTK_OBJECT (nbrowse), "menubar1", menubar1,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (menubar1);
+  gtk_box_pack_start (GTK_BOX (vbox1), menubar1, FALSE, FALSE, 0);
+  gnome_app_fill_menu (GTK_MENU_SHELL (menubar1), menubar1_uiinfo,
+                       NULL, FALSE, 0);
+
+  gtk_widget_ref (menubar1_uiinfo[0].widget);
+  gtk_object_set_data_full (GTK_OBJECT (nbrowse), "file1",
+                            menubar1_uiinfo[0].widget,
+                            (GtkDestroyNotify) gtk_widget_unref);
+
+  gtk_widget_ref (file1_menu_uiinfo[0].widget);
+  gtk_object_set_data_full (GTK_OBJECT (nbrowse), "change_server1",
+                            file1_menu_uiinfo[0].widget,
+                            (GtkDestroyNotify) gtk_widget_unref);
+
+  gtk_widget_ref (file1_menu_uiinfo[1].widget);
+  gtk_object_set_data_full (GTK_OBJECT (nbrowse), "separator3",
+                            file1_menu_uiinfo[1].widget,
+                            (GtkDestroyNotify) gtk_widget_unref);
+
+  gtk_widget_ref (file1_menu_uiinfo[2].widget);
+  gtk_object_set_data_full (GTK_OBJECT (nbrowse), "exit1",
+                            file1_menu_uiinfo[2].widget,
+                            (GtkDestroyNotify) gtk_widget_unref);
+
+  hpaned1 = gtk_hpaned_new ();
+  gtk_widget_ref (hpaned1);
+  gtk_paned_set_gutter_size( GTK_HPANED(hpaned1), 12 );
+
+  gtk_object_set_data_full (GTK_OBJECT (nbrowse), "hpaned1", hpaned1,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (hpaned1);
+  gtk_box_pack_start (GTK_BOX (vbox1), hpaned1, TRUE, TRUE, 0);
+  gtk_paned_set_position (GTK_PANED (hpaned1), 149);
+
+  scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL);
+  gtk_widget_ref (scrolledwindow2);
+  gtk_object_set_data_full (GTK_OBJECT (nbrowse), "scrolledwindow2", scrolledwindow2,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolledwindow2), 
+                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+  gtk_widget_show (scrolledwindow2);
+  gtk_paned_pack1 (GTK_PANED (hpaned1), scrolledwindow2, FALSE, TRUE);
+
+  tree = gtk_tree_new ();
+  gtk_widget_ref (tree);
+  gtk_object_set_data_full (GTK_OBJECT (nbrowse), "tree", tree,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (tree);
+  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolledwindow2), tree);
+  gtk_tree_set_selection_mode (GTK_TREE (tree), GTK_SELECTION_SINGLE);
+  gtk_tree_set_view_mode (GTK_TREE (tree), GTK_TREE_VIEW_ITEM);
+
+  scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
+  gtk_widget_ref (scrolledwindow1);
+  gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolledwindow1), 
+                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+  gtk_object_set_data_full (GTK_OBJECT (nbrowse), "scrolledwindow1", scrolledwindow1,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (scrolledwindow1);
+  gtk_paned_pack2 (GTK_PANED (hpaned1), scrolledwindow1, TRUE, TRUE);
+
+  iconlist = gnome_icon_list_new_flags (78, NULL, 0);
+  gtk_widget_ref (iconlist);
+  gnome_icon_list_set_selection_mode( GNOME_ICON_LIST(iconlist), GTK_SELECTION_MULTIPLE );
+  gtk_object_set_data_full (GTK_OBJECT (nbrowse), "iconlist", iconlist,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (iconlist);
+  gtk_container_add (GTK_CONTAINER (scrolledwindow1), iconlist);
+
+  gtk_signal_connect (GTK_OBJECT (nbrowse), "remove",
+                      GTK_SIGNAL_FUNC (gtk_main_quit),
+                      NULL);
+
+  return nbrowse;
+}
+
+GtkWidget*
+create_enter_url (void)
+{
+  GtkWidget *enter_url;
+  GtkWidget *dialog_vbox1;
+  GtkWidget *hbox1;
+  gchar *pixmap1_filename;
+  GtkWidget *pixmap1;
+  GtkWidget *vbox2;
+  GtkWidget *label1;
+  GtkWidget *entry1;
+  GtkWidget *dialog_action_area1;
+  GtkWidget *button1;
+  GtkWidget *button3;
+
+  enter_url = gnome_dialog_new (_("Enter URL"), NULL);
+  gtk_object_set_data (GTK_OBJECT (enter_url), "enter_url", enter_url);
+  GTK_WINDOW (enter_url)->type = GTK_WINDOW_DIALOG;
+  gtk_window_set_position (GTK_WINDOW (enter_url), GTK_WIN_POS_CENTER);
+  gtk_window_set_modal (GTK_WINDOW (enter_url), TRUE);
+  gtk_window_set_policy (GTK_WINDOW (enter_url), FALSE, FALSE, FALSE);
+
+  dialog_vbox1 = GNOME_DIALOG (enter_url)->vbox;
+  gtk_object_set_data (GTK_OBJECT (enter_url), "dialog_vbox1", dialog_vbox1);
+  gtk_widget_show (dialog_vbox1);
+
+  hbox1 = gtk_hbox_new (FALSE, 0);
+  gtk_widget_ref (hbox1);
+  gtk_object_set_data_full (GTK_OBJECT (enter_url), "hbox1", hbox1,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (hbox1);
+
+  gtk_box_pack_start (GTK_BOX (dialog_vbox1), hbox1, TRUE, TRUE, 0);
+
+  pixmap1 = gtk_type_new (gnome_pixmap_get_type ());
+  pixmap1_filename = gnome_pixmap_file ("nbrowse/gnome-question.png");
+  if (pixmap1_filename)
+    gnome_pixmap_load_file (GNOME_PIXMAP (pixmap1), pixmap1_filename);
+  else
+    g_warning (_("Couldn't find pixmap file: %s"), "gnome-question.png");
+  g_free (pixmap1_filename);
+  gtk_widget_ref (pixmap1);
+  gtk_object_set_data_full (GTK_OBJECT (enter_url), "pixmap1", pixmap1,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (pixmap1);
+  gtk_box_pack_start (GTK_BOX (hbox1), pixmap1, TRUE, TRUE, 0);
+
+  vbox2 = gtk_vbox_new (FALSE, 6);
+  gtk_widget_ref (vbox2);
+  gtk_object_set_data_full (GTK_OBJECT (enter_url), "vbox2", vbox2,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (vbox2);
+  gtk_box_pack_start (GTK_BOX (hbox1), vbox2, TRUE, TRUE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox2), 7);
+
+  label1 = gtk_label_new (_("Enter a URL to browse:\n(for example, http://www.driveway.com/user.myname/)"));
+  gtk_widget_ref (label1);
+  gtk_object_set_data_full (GTK_OBJECT (enter_url), "label1", label1,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (label1);
+  gtk_box_pack_start (GTK_BOX (vbox2), label1, FALSE, FALSE, 0);
+
+  entry1 = gtk_entry_new ();
+  gtk_widget_ref (entry1);
+  gtk_object_set_data_full (GTK_OBJECT (enter_url), "entry1", entry1,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (entry1);
+  gtk_box_pack_start (GTK_BOX (vbox2), entry1, FALSE, FALSE, 0);
+
+  dialog_action_area1 = GNOME_DIALOG (enter_url)->action_area;
+  gtk_object_set_data (GTK_OBJECT (enter_url), "dialog_action_area1", dialog_action_area1);
+  gtk_widget_show (dialog_action_area1);
+  gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1), GTK_BUTTONBOX_END);
+  gtk_button_box_set_spacing (GTK_BUTTON_BOX (dialog_action_area1), 8);
+
+  gnome_dialog_append_button (GNOME_DIALOG (enter_url), GNOME_STOCK_BUTTON_OK);
+  button1 = g_list_last (GNOME_DIALOG (enter_url)->buttons)->data;
+  gtk_widget_ref (button1);
+  gtk_object_set_data_full (GTK_OBJECT (enter_url), "button1", button1,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (button1);
+  GTK_WIDGET_SET_FLAGS (button1, GTK_CAN_DEFAULT);
+
+  gnome_dialog_append_button (GNOME_DIALOG (enter_url), GNOME_STOCK_BUTTON_CANCEL);
+  button3 = g_list_last (GNOME_DIALOG (enter_url)->buttons)->data;
+  gtk_widget_ref (button3);
+  gtk_object_set_data_full (GTK_OBJECT (enter_url), "button3", button3,
+                            (GtkDestroyNotify) gtk_widget_unref);
+  gtk_widget_show (button3);
+  GTK_WIDGET_SET_FLAGS (button3, GTK_CAN_DEFAULT);
+
+  return enter_url;
+}
+
diff --git a/neon/example/nbrowse/interface.h b/neon/example/nbrowse/interface.h
new file mode 100644 (file)
index 0000000..71bb808
--- /dev/null
@@ -0,0 +1,8 @@
+/*
+ * DO NOT EDIT THIS FILE - it is generated by Glade.
+ */
+
+GtkWidget* create_nbrowse (void);
+GtkWidget* create_enter_url (void);
+extern GtkWidget *tree;
+extern GtkWidget *iconlist;
diff --git a/neon/example/nbrowse/main.c b/neon/example/nbrowse/main.c
new file mode 100644 (file)
index 0000000..c5a2a20
--- /dev/null
@@ -0,0 +1,337 @@
+/* 
+   nbrowse, dummy WebDAV browsing in GNOME
+   Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+   
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   Id: main.c,v 1.3 2000/07/16 16:15:44 joe Exp 
+*/
+
+/* 
+  Aims to demonstrate that a blocking-IO based HTTP library can be
+  easily integrated with a GUI polling loop.
+   
+  TODO: 
+  - Stop it crashing all the time
+  - Allow one PROPFIND at a time, i.e., ignore clicks once
+    already loading a tree
+  - Fetch lockdiscovery and display "locked" icons
+*/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <signal.h>
+
+#include <pthread.h>
+
+#include <gnome.h>
+
+#include <gtk/gtktree.h>
+
+
+#include <neon_config.h>
+
+#include <dav_props.h>
+#include <dav_basic.h>
+#include <uri.h>
+#include <xalloc.h>
+
+#include "basename.h"
+#include "interface.h"
+#include "support.h"
+
+#define ELM_getcontentlength (1001)
+#define ELM_resourcetype (1002)
+#define ELM_getlastmodified (1003)
+#define ELM_executable (1004)
+#define ELM_collection (1005)
+#define ELM_displayname (1006)
+
+enum resource_type {
+    res_normal,
+    res_collection,
+    res_reference,
+};
+
+http_session *sess;
+/* since we let >1 thread spawn at a time, 
+ * enforce mutual exclusion over access to the session state
+ */
+pthread_mutex_t sess_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct browser {
+    const char *uri;
+    GtkTree *tree;
+    GnomeIconList *list;
+    unsigned int thread_running:1;
+    pthread_t thread;
+};
+
+struct resource {
+    char *uri;
+    char *displayname;
+    enum resource_type type;
+    size_t size;
+    time_t modtime;
+    int is_executable;
+    struct resource *next;
+};
+
+static int start_propfind( struct browser *b );
+
+static int check_context( hip_xml_elmid child, hip_xml_elmid parent )
+{
+    /* FIXME */
+    return 0;    
+}
+
+/* Does a PROPFIND to load URI at tree */
+static void load_tree( GtkWidget *tree, const char *uri ) 
+{
+    struct browser *b = xmalloc(sizeof(struct browser));
+
+    /* Create the tree */
+    b->tree = GTK_TREE(tree);
+    b->list = GNOME_ICON_LIST(iconlist);
+    b->uri = uri;
+
+    start_propfind(b);
+}
+
+static void select_node_cb( GtkTreeItem *item )
+{
+    char *uri = gtk_object_get_data( GTK_OBJECT(item), "uri" );
+    static char *loaded = "l";
+
+    printf( "selected: %s\n", uri );
+
+#if 0
+    if( gtk_object_get_data( GTK_OBJECT(item), "is_loaded" ) == NULL ) {
+       gtk_object_set_data( GTK_OBJECT(item), "is_loaded", loaded );
+    } else {
+       printf( "already loaded this node.\n" );
+       return;
+    }
+#endif
+
+    gnome_icon_list_clear(GNOME_ICON_LIST(iconlist));
+    gtk_tree_remove_items (GTK_TREE(item->subtree), GTK_TREE(item->subtree)->children);
+    load_tree( item->subtree, uri );
+}
+
+static int end_element( void *userdata, const struct hip_xml_elm *elm, const char *cdata )
+{
+    struct resource *r;
+    r = dav_propfind_get_current_resource( userdata );
+    if( r == NULL ) {
+       return 0;
+    }
+    DEBUG( DEBUG_HTTP, "In resource %s\n", r->uri );
+    DEBUG( DEBUG_HTTP, "Property [%d], %s@@%s = %s\n", elm->id,
+          elm->nspace, elm->name, cdata?cdata:"undefined" );
+    switch( elm->id ) {
+    case ELM_collection:
+       r->type = res_collection;
+       break;
+    case ELM_getcontentlength:
+       if( cdata ) {
+           r->size = atoi(cdata);
+       }
+       break;
+    case ELM_displayname:
+       if( cdata ) {
+           r->displayname = xstrdup(cdata);
+       }
+       break;
+    case ELM_getlastmodified:
+       if( cdata ) {
+           r->modtime = http_dateparse(cdata);
+           /* will be -1 on error */
+       }
+       break;
+    case ELM_executable:
+       if( cdata ) {
+           if( strcasecmp( cdata, "T" ) == 0 ) {
+               r->is_executable = 1;
+           } else {
+               r->is_executable = 0;
+           }
+       }
+       break;
+    }
+    return 0;
+}
+
+static void *start_resource( void *userdata, const char *href )
+{
+    struct resource *res = xmalloc(sizeof(struct resource));
+    memset(res,0,sizeof(struct resource));
+    res->uri = xstrdup(href);
+    return res;
+}
+
+static void end_resource( void *userdata, void *resource,
+                         const char *status_line, const http_status *status,
+                         const char *description )
+{
+    struct browser *b = userdata;
+    char *abspath;
+    struct resource *res = resource;
+    char *leaf;
+
+    if( res == NULL ) {
+       return;
+    }  
+
+    DEBUG( DEBUG_HTTP, "Href: %s\n", res->uri );
+    /* char * cast is valid since res->uri is char * */
+    abspath = (char *) uri_abspath( res->uri );
+
+    if( uri_compare( abspath, b->uri ) == 0 ) {
+       return;
+    }
+
+    if( uri_has_trailing_slash(abspath) ) {
+       abspath[strlen(abspath)-1] = '\0';
+    }
+
+    leaf = base_name(abspath);
+
+    gdk_threads_enter();
+
+    if( res->type == res_collection ) {
+       GtkWidget *sub, *item;
+
+       item = gtk_tree_item_new_with_label( leaf );
+       gtk_tree_append( b->tree, item );
+
+       sub = gtk_tree_new();   
+       gtk_tree_item_set_subtree( GTK_TREE_ITEM(item), sub );
+       gtk_object_set_data_full( GTK_OBJECT(item), "uri", 
+                                 xstrdup(res->uri), free );
+       gtk_signal_connect (GTK_OBJECT(item), "select",
+                           GTK_SIGNAL_FUNC(select_node_cb), item);
+       gtk_signal_connect (GTK_OBJECT(item), "expand",
+                           GTK_SIGNAL_FUNC(select_node_cb), item);
+       gtk_widget_show( item );
+
+       gnome_icon_list_append( b->list, "/usr/share/pixmaps/mc/i-directory.png", leaf );
+
+    } else {
+       gnome_icon_list_append( b->list, "/usr/share/pixmaps/mc/i-regular.png", leaf );
+    }
+
+    gdk_threads_leave();
+
+    free( res->uri);
+    free( res );
+}
+
+/* Run in a separate thread */
+static void *do_propfind( void *ptr )
+{
+    dav_propfind_handler *ph;
+    static const dav_propname props[] = {
+       { "DAV:", "getcontentlength" },
+       { "DAV:", "resourcetype" },
+       { "DAV:", "getlastmodified" },
+       { "DAV:", "displayname" },
+       { "http://apache.org/dav/props/", "executable" },
+       { NULL }
+    };
+    static const struct hip_xml_elm elms[] = {
+       { "DAV:", "getcontentlength", ELM_getcontentlength, HIP_XML_CDATA },
+       { "DAV:", "resourcetype", ELM_resourcetype, 0 },
+       { "DAV:", "getlastmodified", ELM_getlastmodified, HIP_XML_CDATA },
+       { "http://apache.org/dav/props/", "executable", ELM_executable, HIP_XML_CDATA },
+       { "DAV:", "collection", ELM_collection, 0 },
+       { "DAV:", "displayname", ELM_displayname, HIP_XML_CDATA },
+       { NULL }
+    };
+    struct browser *b = ptr;
+
+    pthread_detach(pthread_self());
+
+    pthread_mutex_lock( &sess_mutex );
+    
+    ph = dav_propfind_create (sess, b->uri, DAV_DEPTH_ONE );
+
+    dav_propfind_set_resource_handlers (ph, start_resource, end_resource);
+
+    hip_xml_add_handler (dav_propfind_get_parser(ph), elms,
+                        check_context, NULL, end_element, ph); 
+    
+    if (dav_propfind_named (ph, props, ptr) != HTTP_OK) {
+       printf( "PROPFIND failed: %s\n", http_get_error(sess) );
+    }
+
+    pthread_mutex_unlock( &sess_mutex );
+    
+    free( b );
+
+    return NULL;
+}
+
+
+static int start_propfind( struct browser *b )
+{
+    b->thread_running = 1;
+    return pthread_create( &b->thread, NULL, do_propfind, b );
+}
+
+int
+main (int argc, char **argv)
+{
+    GtkWidget *nbrowse;
+    
+    if( argc < 3 ) {
+       printf("nbrowse: Usage 'nbrowse server.name.com /path/'.\n");
+       return -1;
+    }
+
+#ifdef ENABLE_NLS
+    bindtextdomain (PACKAGE, PACKAGE_LOCALE_DIR);
+    textdomain (PACKAGE);
+#endif
+    
+    /* Initialize threading */
+    g_thread_init(NULL);
+    
+    signal( SIGPIPE, SIG_IGN );
+   
+    gnome_init ("nbrowse", NEON_VERSION, argc, argv);
+    
+    /* Create the main window */
+    nbrowse = create_nbrowse ();
+    gtk_widget_show (nbrowse);
+
+    /* Create a new session. */
+    sess = http_session_create();
+
+    printf( "Using server: %s - path %s\n", argv[1], argv[2] );
+
+    http_session_server( sess, argv[1], 80 );
+    http_set_useragent( sess, "nbrowser/" NEON_VERSION );
+    
+    load_tree( tree, argv[2] );
+    
+    gdk_threads_enter();
+    gtk_main();
+    gdk_threads_leave();
+    return 0;
+}
+
diff --git a/neon/example/nbrowse/support.c b/neon/example/nbrowse/support.c
new file mode 100644 (file)
index 0000000..c76f333
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * DO NOT EDIT THIS FILE - it is generated by Glade.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <gnome.h>
+
+#include "support.h"
+
+/* This is an internally used function to create pixmaps. */
+static GtkWidget* create_dummy_pixmap  (GtkWidget       *widget,
+                                        gboolean         gnome_pixmap);
+
+GtkWidget*
+lookup_widget                          (GtkWidget       *widget,
+                                        const gchar     *widget_name)
+{
+  GtkWidget *parent, *found_widget;
+
+  for (;;)
+    {
+      if (GTK_IS_MENU (widget))
+        parent = gtk_menu_get_attach_widget (GTK_MENU (widget));
+      else
+        parent = widget->parent;
+      if (parent == NULL)
+        break;
+      widget = parent;
+    }
+
+  found_widget = (GtkWidget*) gtk_object_get_data (GTK_OBJECT (widget),
+                                                   widget_name);
+  if (!found_widget)
+    g_warning ("Widget not found: %s", widget_name);
+  return found_widget;
+}
+
+/* This is a dummy pixmap we use when a pixmap can't be found. */
+static char *dummy_pixmap_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"1 1 1 1",
+"  c None",
+/* pixels */
+" ",
+" "
+};
+
+/* This is an internally used function to create pixmaps. */
+static GtkWidget*
+create_dummy_pixmap                    (GtkWidget       *widget,
+                                        gboolean         gnome_pixmap)
+{
+  GdkColormap *colormap;
+  GdkPixmap *gdkpixmap;
+  GdkBitmap *mask;
+  GtkWidget *pixmap;
+
+  if (gnome_pixmap)
+    {
+      return gnome_pixmap_new_from_xpm_d (dummy_pixmap_xpm);
+    }
+
+  colormap = gtk_widget_get_colormap (widget);
+  gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask,
+                                                     NULL, dummy_pixmap_xpm);
+  if (gdkpixmap == NULL)
+    g_error ("Couldn't create replacement pixmap.");
+  pixmap = gtk_pixmap_new (gdkpixmap, mask);
+  gdk_pixmap_unref (gdkpixmap);
+  gdk_bitmap_unref (mask);
+  return pixmap;
+}
+
+/* This is an internally used function to create pixmaps. */
+GtkWidget*
+create_pixmap                          (GtkWidget       *widget,
+                                        const gchar     *filename,
+                                        gboolean         gnome_pixmap)
+{
+  GtkWidget *pixmap;
+  GdkColormap *colormap;
+  GdkPixmap *gdkpixmap;
+  GdkBitmap *mask;
+  gchar *pathname;
+
+  if (!filename || !filename[0])
+      return create_dummy_pixmap (widget, gnome_pixmap);
+
+  pathname = gnome_pixmap_file (filename);
+  if (!pathname)
+    {
+      g_warning (_("Couldn't find pixmap file: %s"), filename);
+      return create_dummy_pixmap (widget, gnome_pixmap);
+    }
+
+  if (gnome_pixmap)
+    {
+      pixmap = gnome_pixmap_new_from_file (pathname);
+      g_free (pathname);
+      return pixmap;
+    }
+
+  colormap = gtk_widget_get_colormap (widget);
+  gdkpixmap = gdk_pixmap_colormap_create_from_xpm (NULL, colormap, &mask,
+                                                   NULL, pathname);
+  if (gdkpixmap == NULL)
+    {
+      g_warning (_("Couldn't create pixmap from file: %s"), pathname);
+      g_free (pathname);
+      return create_dummy_pixmap (widget, gnome_pixmap);
+    }
+  g_free (pathname);
+
+  pixmap = gtk_pixmap_new (gdkpixmap, mask);
+  gdk_pixmap_unref (gdkpixmap);
+  gdk_bitmap_unref (mask);
+  return pixmap;
+}
+
+/* This is an internally used function to create imlib images. */
+GdkImlibImage*
+create_image                           (const gchar     *filename)
+{
+  GdkImlibImage *image;
+  gchar *pathname;
+
+  pathname = gnome_pixmap_file (filename);
+  if (!pathname)
+    {
+      g_warning (_("Couldn't find pixmap file: %s"), filename);
+      return NULL;
+    }
+
+  image = gdk_imlib_load_image (pathname);
+  g_free (pathname);
+  return image;
+}
+
diff --git a/neon/example/nbrowse/support.h b/neon/example/nbrowse/support.h
new file mode 100644 (file)
index 0000000..d9bb072
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * DO NOT EDIT THIS FILE - it is generated by Glade.
+ */
+
+#include <gnome.h>
+
+/*
+ * Public Functions.
+ */
+
+/*
+ * This function returns a widget in a component created by Glade.
+ * Call it with the toplevel widget in the component (i.e. a window/dialog),
+ * or alternatively any widget in the component, and the name of the widget
+ * you want returned.
+ */
+GtkWidget*  lookup_widget              (GtkWidget       *widget,
+                                        const gchar     *widget_name);
+
+/* get_widget() is deprecated. Use lookup_widget instead. */
+#define get_widget lookup_widget
+
+
+/*
+ * Private Functions.
+ */
+
+/* This is used to create the pixmaps in the interface. */
+GtkWidget*  create_pixmap              (GtkWidget       *widget,
+                                        const gchar     *filename,
+                                        gboolean         gnome_pixmap);
+
+GdkImlibImage* create_image            (const gchar     *filename);
+
diff --git a/neon/example/nget.c b/neon/example/nget.c
new file mode 100644 (file)
index 0000000..3cc43dc
--- /dev/null
@@ -0,0 +1,164 @@
+/* 
+   nget, neon HTTP GET tester
+   Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+                                                                     
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   Id: nget.c,v 1.3 2000/05/10 18:16:56 joe Exp 
+*/
+
+#include <sys/types.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "http_request.h"
+#include "http_basic.h"
+#include "uri.h"
+#include "socket.h"
+
+#include "basename.h"
+
+int in_progress = 0, use_stdout = 0;
+
+static void pretty_progress_bar( void *ud, size_t progress, size_t total );
+
+static const char *use_filename( struct uri *uri )
+{
+    const char *fname;
+
+    fname = base_name( uri->path );
+    if( strcmp( fname, "/" ) == 0 || strlen( fname ) == 0 ) {
+       fname = "index.html";
+    }
+    
+    return fname;
+}
+
+int main( int argc, char **argv )
+{
+    http_session *sess;
+    struct uri uri = {0}, defaults = {0};
+    struct stat st;
+    int ret;
+    const char *fname;
+    FILE *f;
+
+    if( argc < 2 || argc > 3 ) {
+       printf( "nget: Usage: \n"
+               "  `nget url': download url using appropriate filename\n"
+               "  `nget url filename': download url to given filename\n"
+               "  `nget url -': download url and display on stdout\n" );
+       return -1;
+    }
+
+    sock_register_progress( pretty_progress_bar, NULL );
+
+    defaults.port = 80;
+    
+    if( uri_parse( argv[1], &uri, &defaults ) || 
+       !uri.path || !uri.host || uri.port == -1 ) {
+       printf( "nget: Invalid URL.\n" );
+       return -1;
+    }
+    
+    if( argc == 3 ) {
+       fname = argv[2];
+    } else {
+       fname = use_filename( &uri );
+    }
+    if( strcmp( fname, "-" ) == 0 ) {
+       f = stdout;
+       use_stdout = 1;
+    } else if( stat( fname, &st ) == 0 ) {
+       printf( "nget: File `%s' already exists.\n", fname );
+       return -1;
+    } else {
+       f = fopen( fname, "w" );
+       if( f == NULL ) {
+           printf( "nget: Could not open %s: %s\n", fname, strerror(errno) );
+           return -1;
+       }
+    }
+    
+    sess = http_session_init();
+    
+    http_session_server( sess, uri.host, uri.port );
+    
+    if( !use_stdout ) {
+       printf( "nget: Downloading %s to %s\n", argv[1], fname );
+    }
+    ret = http_get( sess, uri.path, f );
+
+    if( in_progress ) {
+       printf( "\n" );
+    }
+
+    if( ret == HTTP_OK ) {
+       if( !use_stdout ) printf( "nget: Download complete.\n" );
+    } else {
+       fprintf( stderr, 
+                "nget: Download error: %s\n", http_get_error(sess));
+    }
+
+    if( !use_stdout ) 
+       fclose( f );
+
+    return ret;
+}
+
+/* Smooth progress bar from cadaver.
+ * Doesn't update the bar more than once every 100ms, since this 
+ * might give flicker, and would be bad if we are displaying on
+ * a slow link anyway.
+ */
+static void pretty_progress_bar( void *ud, size_t progress, size_t total )
+{
+    int len, n;
+    double pc;
+    static struct timeval last_call = {0};
+    struct timeval this_call;
+    if( use_stdout ) return;
+    in_progress = 1;
+    if( total == -1 ) {
+       printf( "\rProgress: %d bytes", progress );
+       return;
+    }
+    if( progress < total && gettimeofday( &this_call, NULL ) == 0 ) {
+       struct timeval diff;
+       timersub( &this_call, &last_call, &diff );
+       if( diff.tv_sec == 0 && diff.tv_usec < 100000 ) {
+           return;
+       }
+       last_call = this_call;
+    }
+    if( progress == 0 || total == 0 ) {
+       pc = 0;
+    } else {
+       pc = (double)progress / total;
+    }
+    len = pc * 30;
+    printf( "\rProgress: [" );
+    for( n = 0; n<30; n++ ) {
+       putchar( (n<len-1)?'=':
+                (n==(len-1)?'>':' ') );
+    }
+    printf( "] %5.1f%% of %d bytes", pc*100, total );
+    fflush( stdout );
+}
+
diff --git a/neon/example/nserver.c b/neon/example/nserver.c
new file mode 100644 (file)
index 0000000..7b8bf16
--- /dev/null
@@ -0,0 +1,147 @@
+/* 
+   nserver, neon Server checker.
+   Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+                                                                     
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   Id: nserver.c,v 1.2 2000/07/16 16:15:44 joe Exp 
+*/
+
+#include <sys/types.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <neon_config.h>
+#include <http_request.h>
+#include <http_basic.h>
+#include <uri.h>
+#include <socket.h>
+
+#include <getopt.h>
+
+#include "basename.h"
+
+static void conn_notify( void *userdata, sock_status status, const char *info )
+{
+    switch( status ) {
+    case sock_namelookup:
+    case sock_connecting:
+    case sock_connected:
+       break;
+    case sock_secure_details:
+       fprintf( stderr, "nserver: Using a secure connection (%s)\n", info );
+       break;
+    }
+}
+
+int main( int argc, char **argv )
+{
+    struct uri uri = {0}, defaults = {0};
+    http_session *sess;
+    http_req *req;
+    http_status status;
+    char *server = NULL, *pnt;
+    int ret;
+
+    neon_debug_init( stderr, 0 );
+
+    sess = http_session_create();
+
+    defaults.path = "/";
+    defaults.port = 0;
+    defaults.host = NULL;
+    defaults.scheme = "http";
+
+    if( argc < 2 ) {
+       printf( "nserver: Usage: %s http[s]://server.host.name[:port]\n",
+               argv[0] );
+       return -1;
+    }
+    
+    pnt = strchr( argv[1], '/' );
+    if( pnt == NULL ) {
+       uri.host = argv[1];
+       pnt = strchr( uri.host, ':' );
+       if( pnt == NULL ) {
+           uri.port = 80;
+       } else {
+           uri.port = atoi(pnt+1);
+           *pnt = '\0';
+       }
+       uri.path = "/";
+       uri.scheme = "http";
+    } else {
+       if( uri_parse( argv[1], &uri, &defaults ) || !uri.host ) {
+           printf( "nserver: Could not parse URL `%s'\n", argv[1] );
+           return -1;
+       }
+       if( strcasecmp( uri.scheme, "https" ) == 0 ) {
+           if( uri.port == 0 ) {
+               uri.port = 443;
+           }
+           if( http_set_secure( sess, 1 ) ) {
+               fprintf( stderr, "nserver: SSL not supported.\n" );
+               exit( -1 );
+           }
+       }
+    }
+
+    sock_init();
+    sock_register_notify( conn_notify, NULL );
+
+    if( uri.port == 0 ) {
+       uri.port = 80;
+    }
+    
+    printf( "nserver: Retrieving server string for server at %s (port %d):\n",
+           uri.host, uri.port );
+    
+    req = http_request_create(sess, "HEAD", uri.path );
+    if( http_session_server( sess, uri.host, uri.port ) != HTTP_OK ) {
+       printf( "nserver: Hostname `%s' not found.\n", uri.host );
+       return -1;
+    }
+
+    /* Use a standard strdup-er handler */
+    http_add_response_header_handler( req, "Server", 
+                                     http_duplicate_header, &server );
+    
+    switch( http_request_dispatch( req, &status ) ) {
+    case HTTP_OK:
+       if( server == NULL ) {
+           printf( "nserver: No server string was given.\n" );
+           ret = 1;
+       } else {
+           printf( "Server string: %s\n", server );
+           ret = 0;
+       }
+       break;
+    default:
+       printf( "nserver: Failed: %s\n", http_get_error(sess) );
+       ret = -1;
+       break;
+    }
+    
+    http_request_destroy( req );
+    http_session_destroy( sess );
+
+    return ret;
+}
diff --git a/neon/install-sh b/neon/install-sh
new file mode 100755 (executable)
index 0000000..ebc6691
--- /dev/null
@@ -0,0 +1,250 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/neon/lib/basename.c b/neon/lib/basename.c
new file mode 100644 (file)
index 0000000..358f5e8
--- /dev/null
@@ -0,0 +1,57 @@
+/* basename.c -- return the last element in a path
+   Copyright (C) 1990, 1998, 1999 Free Software Foundation, Inc.
+
+   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 version 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 this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <basename.h>
+
+#ifndef FILESYSTEM_PREFIX_LEN
+# define FILESYSTEM_PREFIX_LEN(Filename) 0
+#endif
+
+#ifndef ISSLASH
+# define ISSLASH(C) ((C) == '/')
+#endif
+
+/* In general, we can't use the builtin `basename' function if available,
+   since it has different meanings in different environments.
+   In some environments the builtin `basename' modifies its argument.
+   If NAME is all slashes, be sure to return `/'.  */
+
+char *
+base_name (char const *name)
+{
+  char const *base = name += FILESYSTEM_PREFIX_LEN (name);
+  int all_slashes = 1;
+  char const *p;
+
+  for (p = name; *p; p++)
+    {
+      if (ISSLASH (*p))
+       base = p + 1;
+      else
+       all_slashes = 0;
+    }
+
+  /* If NAME is all slashes, arrange to return `/'.  */
+  if (*base == '\0' && ISSLASH (*name) && all_slashes)
+    --base;
+
+  return (char *) base;
+}
diff --git a/neon/lib/basename.h b/neon/lib/basename.h
new file mode 100644 (file)
index 0000000..6b02212
--- /dev/null
@@ -0,0 +1,2 @@
+
+char *base_name (char const *name);
diff --git a/neon/lib/dirname.c b/neon/lib/dirname.c
new file mode 100644 (file)
index 0000000..c8cca9e
--- /dev/null
@@ -0,0 +1,75 @@
+/* dirname.c -- return all but the last element in a path
+   Copyright (C) 1990, 1998 Free Software Foundation, Inc.
+
+   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 version 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 this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/*******************************************************
+ * THIS IS A MODIFIED dirname IMPLEMENTATION:
+ * - sitecopy wants "" if there is no directory name,
+ *   standard GNU implementation gives us "."
+ * - sitecopy wants the trailing slash.
+ *******************************************************/
+
+#include "xalloc.h"
+
+#if defined STDC_HEADERS || defined HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+# ifndef strrchr
+#  define strrchr rindex
+# endif
+#endif
+
+#include "dirname.h"
+
+/* Return the leading directories part of PATH,
+   allocated with malloc.  If out of memory, return 0.
+   Assumes that trailing slashes have already been
+   removed.  */
+
+char *
+dir_name (const char *path)
+{
+  char *newpath;
+  char *slash;
+  int length;                  /* Length of result, not including NUL.  */
+
+  slash = strrchr (path, '/');
+  if (slash == 0)
+    {
+      /* File is in the current directory.  */
+      path = "";
+      length = 0;
+    }
+  else
+    {
+      /* Remove any trailing slashes from the result.
+      while (slash > path && *slash == '/')
+      --slash; */
+
+      length = slash - path + 1;
+    }
+  newpath = (char *) xmalloc (length + 1);
+  if (newpath == 0)
+    return 0;
+  strncpy (newpath, path, length);
+  newpath[length] = 0;
+  return newpath;
+}
diff --git a/neon/lib/dirname.h b/neon/lib/dirname.h
new file mode 100644 (file)
index 0000000..fc46699
--- /dev/null
@@ -0,0 +1,31 @@
+/*  Copyright (C) 1998 Free Software Foundation, Inc.
+
+    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 version 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 this program; if not, write to the Free Software Foundation,
+    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef DIRNAME_H_
+# define DIRNAME_H_ 1
+
+# ifndef PARAMS
+#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
+#   define PARAMS(Args) Args
+#  else
+#   define PARAMS(Args) ()
+#  endif
+# endif
+
+char *
+dir_name PARAMS ((const char *path));
+
+#endif /* not DIRNAME_H_ */
diff --git a/neon/lib/fnmatch.c b/neon/lib/fnmatch.c
new file mode 100644 (file)
index 0000000..4bc7cd9
--- /dev/null
@@ -0,0 +1,212 @@
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+   NOTE: The canonical source of this file is maintained with the GNU C
+   Library.  Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+   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 version 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 this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <fnmatch.h>
+#include <ctype.h>
+
+#if defined (STDC_HEADERS) || !defined (isascii)
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#define ISUPPER(c) (ISASCII (c) && isupper (c))
+
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+# ifndef errno
+extern int errno;
+# endif
+
+/* Match STRING against the filename pattern PATTERN, returning zero if
+   it matches, nonzero if not.  */
+int
+fnmatch (pattern, string, flags)
+     const char *pattern;
+     const char *string;
+     int flags;
+{
+  register const char *p = pattern, *n = string;
+  register char c;
+
+/* Note that this evalutes C many times.  */
+# define FOLD(c)       ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
+
+  while ((c = *p++) != '\0')
+    {
+      c = FOLD (c);
+
+      switch (c)
+       {
+       case '?':
+         if (*n == '\0')
+           return FNM_NOMATCH;
+         else if ((flags & FNM_FILE_NAME) && *n == '/')
+           return FNM_NOMATCH;
+         else if ((flags & FNM_PERIOD) && *n == '.' &&
+                  (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+           return FNM_NOMATCH;
+         break;
+
+       case '\\':
+         if (!(flags & FNM_NOESCAPE))
+           {
+             c = *p++;
+             c = FOLD (c);
+           }
+         if (FOLD (*n) != c)
+           return FNM_NOMATCH;
+         break;
+
+       case '*':
+         if ((flags & FNM_PERIOD) && *n == '.' &&
+             (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+           return FNM_NOMATCH;
+
+         for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
+           if (((flags & FNM_FILE_NAME) && *n == '/') ||
+               (c == '?' && *n == '\0'))
+             return FNM_NOMATCH;
+
+         if (c == '\0')
+           return 0;
+
+         {
+           char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
+           c1 = FOLD (c1);
+           for (--p; *n != '\0'; ++n)
+             if ((c == '[' || FOLD (*n) == c1) &&
+                 fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
+               return 0;
+           return FNM_NOMATCH;
+         }
+
+       case '[':
+         {
+           /* Nonzero if the sense of the character class is inverted.  */
+           register int not;
+
+           if (*n == '\0')
+             return FNM_NOMATCH;
+
+           if ((flags & FNM_PERIOD) && *n == '.' &&
+               (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+             return FNM_NOMATCH;
+
+           not = (*p == '!' || *p == '^');
+           if (not)
+             ++p;
+
+           c = *p++;
+           for (;;)
+             {
+               register char cstart = c, cend = c;
+
+               if (!(flags & FNM_NOESCAPE) && c == '\\')
+                 cstart = cend = *p++;
+
+               cstart = cend = FOLD (cstart);
+
+               if (c == '\0')
+                 /* [ (unterminated) loses.  */
+                 return FNM_NOMATCH;
+
+               c = *p++;
+               c = FOLD (c);
+
+               if ((flags & FNM_FILE_NAME) && c == '/')
+                 /* [/] can never match.  */
+                 return FNM_NOMATCH;
+
+               if (c == '-' && *p != ']')
+                 {
+                   cend = *p++;
+                   if (!(flags & FNM_NOESCAPE) && cend == '\\')
+                     cend = *p++;
+                   if (cend == '\0')
+                     return FNM_NOMATCH;
+                   cend = FOLD (cend);
+
+                   c = *p++;
+                 }
+
+               if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
+                 goto matched;
+
+               if (c == ']')
+                 break;
+             }
+           if (!not)
+             return FNM_NOMATCH;
+           break;
+
+         matched:;
+           /* Skip the rest of the [...] that already matched.  */
+           while (c != ']')
+             {
+               if (c == '\0')
+                 /* [... (unterminated) loses.  */
+                 return FNM_NOMATCH;
+
+               c = *p++;
+               if (!(flags & FNM_NOESCAPE) && c == '\\')
+                 /* XXX 1003.2d11 is unclear if this is right.  */
+                 ++p;
+             }
+           if (not)
+             return FNM_NOMATCH;
+         }
+         break;
+
+       default:
+         if (c != FOLD (*n))
+           return FNM_NOMATCH;
+       }
+
+      ++n;
+    }
+
+  if (*n == '\0')
+    return 0;
+
+  if ((flags & FNM_LEADING_DIR) && *n == '/')
+    /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
+    return 0;
+
+  return FNM_NOMATCH;
+
+# undef FOLD
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__.  */
diff --git a/neon/lib/fnmatch.h b/neon/lib/fnmatch.h
new file mode 100644 (file)
index 0000000..af1dcf5
--- /dev/null
@@ -0,0 +1,69 @@
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+NOTE: The canonical source of this file is maintained with the GNU C Library.
+Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+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 version 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 this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef        _FNMATCH_H
+
+#define        _FNMATCH_H      1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
+#undef __P
+#define        __P(protos)     protos
+#else /* Not C++ or ANSI C.  */
+#undef __P
+#define        __P(protos)     ()
+/* We can get away without defining `const' here only because in this file
+   it is used only inside the prototype for `fnmatch', which is elided in
+   non-ANSI C where `const' is problematical.  */
+#endif /* C++ or ANSI C.  */
+
+
+/* We #undef these before defining them because some losing systems
+   (HP-UX A.08.07 for example) define these in <unistd.h>.  */
+#undef FNM_PATHNAME
+#undef FNM_NOESCAPE
+#undef FNM_PERIOD
+
+/* Bits set in the FLAGS argument to `fnmatch'.  */
+#define        FNM_PATHNAME    (1 << 0) /* No wildcard can ever match `/'.  */
+#define        FNM_NOESCAPE    (1 << 1) /* Backslashes don't quote special chars.  */
+#define        FNM_PERIOD      (1 << 2) /* Leading `.' is matched only explicitly.  */
+
+#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE)
+#define        FNM_FILE_NAME   FNM_PATHNAME /* Preferred GNU name.  */
+#define        FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match.  */
+#define        FNM_CASEFOLD    (1 << 4) /* Compare without regard to case.  */
+#endif
+
+/* Value returned by `fnmatch' if STRING does not match PATTERN.  */
+#define        FNM_NOMATCH     1
+
+/* Match STRING against the filename pattern PATTERN,
+   returning zero if it matches, FNM_NOMATCH if not.  */
+extern int fnmatch __P ((const char *__pattern, const char *__string,
+                        int __flags));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* fnmatch.h */
diff --git a/neon/lib/getopt.c b/neon/lib/getopt.c
new file mode 100644 (file)
index 0000000..6557bc1
--- /dev/null
@@ -0,0 +1,1052 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to drepper@gnu.org
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98
+       Free Software Foundation, Inc.
+
+   NOTE: The canonical source of this file is maintained with the GNU C Library.
+   Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+   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 version 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 this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA.  */
+\f
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+# define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+# ifndef const
+#  define const
+# endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+# include <gnu-versions.h>
+# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#  define ELIDE_CODE
+# endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+# include <stdlib.h>
+# include <unistd.h>
+#endif /* GNU C library.  */
+
+#ifdef VMS
+# include <unixlib.h>
+# if HAVE_STRING_H - 0
+#  include <string.h>
+# endif
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+   When compiling libc, the _ macro is predefined.  */
+# ifdef HAVE_LIBINTL_H
+#  include <libintl.h>
+#  define _(msgid)     gettext (msgid)
+# else
+#  define _(msgid)     (msgid)
+# endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* 1003.2 says this must be 1 before any call.  */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+   causes problems with re-calling getopt as programs generally don't
+   know that. */
+
+int __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+\f
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+# include <string.h>
+# define my_index      strchr
+#else
+
+# if HAVE_STRING_H
+#  include <string.h>
+# else
+#  include <strings.h>
+# endif
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+#ifndef getenv
+extern char *getenv ();
+#endif
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+       return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+# if (!defined __STDC__ || !__STDC__) && !defined strlen
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen (const char *);
+# endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+\f
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+   indicating ARGV elements that should not be considered arguments.  */
+
+/* Defined in getopt_init.c  */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+   is valid for the getopt call we must make sure that the ARGV passed
+   to getopt is that one passed to the process.  */
+static void
+__attribute__ ((unused))
+store_args_and_env (int argc, char *const *argv)
+{
+  /* XXX This is no good solution.  We should rather copy the args so
+     that we can compare them later.  But we must not use malloc(3).  */
+  original_argc = argc;
+  original_argv = argv;
+}
+# ifdef text_set_element
+text_set_element (__libc_subinit, store_args_and_env);
+# endif /* text_set_element */
+
+# define SWAP_FLAGS(ch1, ch2) \
+  if (nonoption_flags_len > 0)                                               \
+    {                                                                        \
+      char __tmp = __getopt_nonoption_flags[ch1];                            \
+      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];         \
+      __getopt_nonoption_flags[ch2] = __tmp;                                 \
+    }
+#else  /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+#if defined __STDC__ && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+#ifdef _LIBC
+  /* First make sure the handling of the `__getopt_nonoption_flags'
+     string can work normally.  Our top argument must be in the range
+     of the string.  */
+  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+    {
+      /* We must extend the array.  The user plays games with us and
+        presents new arguments.  */
+      char *new_str = xmalloc (top + 1);
+      if (new_str == NULL)
+       nonoption_flags_len = nonoption_flags_max_len = 0;
+      else
+       {
+         memset (__mempcpy (new_str, __getopt_nonoption_flags,
+                            nonoption_flags_max_len),
+                 '\0', top + 1 - nonoption_flags_max_len);
+         nonoption_flags_max_len = top + 1;
+         __getopt_nonoption_flags = new_str;
+       }
+    }
+#endif
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+       {
+         /* Bottom segment is the short one.  */
+         int len = middle - bottom;
+         register int i;
+
+         /* Swap it with the top part of the top segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[top - (middle - bottom) + i];
+             argv[top - (middle - bottom) + i] = tem;
+             SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+           }
+         /* Exclude the moved bottom segment from further swapping.  */
+         top -= len;
+       }
+      else
+       {
+         /* Top segment is the short one.  */
+         int len = top - middle;
+         register int i;
+
+         /* Swap it with the bottom part of the bottom segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[middle + i];
+             argv[middle + i] = tem;
+             SWAP_FLAGS (bottom + i, middle + i);
+           }
+         /* Exclude the moved top segment from further swapping.  */
+         bottom += len;
+       }
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+#if defined __STDC__ && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  first_nonopt = last_nonopt = optind;
+
+  nextchar = NULL;
+
+  posixly_correct = getenv ("POSIXLY_CORRECT");
+
+  /* Determine how to handle the ordering of options and nonoptions.  */
+
+  if (optstring[0] == '-')
+    {
+      ordering = RETURN_IN_ORDER;
+      ++optstring;
+    }
+  else if (optstring[0] == '+')
+    {
+      ordering = REQUIRE_ORDER;
+      ++optstring;
+    }
+  else if (posixly_correct != NULL)
+    ordering = REQUIRE_ORDER;
+  else
+    ordering = PERMUTE;
+
+#ifdef _LIBC
+  if (posixly_correct == NULL
+      && argc == original_argc && argv == original_argv)
+    {
+      if (nonoption_flags_max_len == 0)
+       {
+         if (__getopt_nonoption_flags == NULL
+             || __getopt_nonoption_flags[0] == '\0')
+           nonoption_flags_max_len = -1;
+         else
+           {
+             const char *orig_str = __getopt_nonoption_flags;
+             int len = nonoption_flags_max_len = strlen (orig_str);
+             if (nonoption_flags_max_len < argc)
+               nonoption_flags_max_len = argc;
+             __getopt_nonoption_flags =
+               (char *) xmalloc (nonoption_flags_max_len);
+             if (__getopt_nonoption_flags == NULL)
+               nonoption_flags_max_len = -1;
+             else
+               memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+                       '\0', nonoption_flags_max_len - len);
+           }
+       }
+      nonoption_flags_len = nonoption_flags_max_len;
+    }
+  else
+    nonoption_flags_len = 0;
+#endif
+
+  return optstring;
+}
+\f
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns -1.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  optarg = NULL;
+
+  if (optind == 0 || !__getopt_initialized)
+    {
+      if (optind == 0)
+       optind = 1;     /* Don't scan ARGV[0], the program name.  */
+      optstring = _getopt_initialize (argc, argv, optstring);
+      __getopt_initialized = 1;
+    }
+
+  /* Test whether ARGV[optind] points to a non-option argument.
+     Either it does not have option syntax, or there is an environment flag
+     from the shell indicating it is not an option.  The later information
+     is only used when the used in the GNU libc.  */
+#ifdef _LIBC
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'              \
+                     || (optind < nonoption_flags_len                        \
+                         && __getopt_nonoption_flags[optind] == '1'))
+#else
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      /* Advance to the next ARGV-element.  */
+
+      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+        moved back by the user (who may also have changed the arguments).  */
+      if (last_nonopt > optind)
+       last_nonopt = optind;
+      if (first_nonopt > optind)
+       first_nonopt = optind;
+
+      if (ordering == PERMUTE)
+       {
+         /* If we have just processed some options following some non-options,
+            exchange them so that the options come first.  */
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (last_nonopt != optind)
+           first_nonopt = optind;
+
+         /* Skip any additional non-options
+            and extend the range of non-options previously skipped.  */
+
+         while (optind < argc && NONOPTION_P)
+           optind++;
+         last_nonopt = optind;
+       }
+
+      /* The special ARGV-element `--' means premature end of options.
+        Skip it like a null option,
+        then exchange with previous non-options as if it were an option,
+        then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+       {
+         optind++;
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (first_nonopt == last_nonopt)
+           first_nonopt = optind;
+         last_nonopt = argc;
+
+         optind = argc;
+       }
+
+      /* If we have done all the ARGV-elements, stop the scan
+        and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+       {
+         /* Set the next-arg-index to point at the non-options
+            that we previously skipped, so the caller will digest them.  */
+         if (first_nonopt != last_nonopt)
+           optind = first_nonopt;
+         return -1;
+       }
+
+      /* If we have come to a non-option and did not permute it,
+        either stop the scan or describe it to the caller and pass it by.  */
+
+      if (NONOPTION_P)
+       {
+         if (ordering == REQUIRE_ORDER)
+           return -1;
+         optarg = argv[optind++];
+         return 1;
+       }
+
+      /* We have found another option-ARGV-element.
+        Skip the initial punctuation.  */
+
+      nextchar = (argv[optind] + 1
+                 + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  /* Decode the current option-ARGV-element.  */
+
+  /* Check whether the ARGV-element is a long option.
+
+     If long_only and the ARGV-element has the form "-f", where f is
+     a valid short option, don't consider it an abbreviated form of
+     a long option that starts with f.  Otherwise there would be no
+     way to give the -f short option.
+
+     On the other hand, if there's a long option "fubar" and
+     the ARGV-element is "-fu", do consider that an abbreviation of
+     the long option, just like "--fu", and not "-f" with arg "u".
+
+     This distinction seems to be the most useful approach.  */
+
+  if (longopts != NULL
+      && (argv[optind][1] == '-'
+         || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+    {
+      char *nameend;
+      const struct option *p;
+      const struct option *pfound = NULL;
+      int exact = 0;
+      int ambig = 0;
+      int indfound = -1;
+      int option_index;
+
+      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+       /* Do nothing.  */ ;
+
+      /* Test all long options for either exact match
+        or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+       if (!strncmp (p->name, nextchar, nameend - nextchar))
+         {
+           if ((unsigned int) (nameend - nextchar)
+               == (unsigned int) strlen (p->name))
+             {
+               /* Exact match found.  */
+               pfound = p;
+               indfound = option_index;
+               exact = 1;
+               break;
+             }
+           else if (pfound == NULL)
+             {
+               /* First nonexact match found.  */
+               pfound = p;
+               indfound = option_index;
+             }
+           else
+             /* Second or later nonexact match found.  */
+             ambig = 1;
+         }
+
+      if (ambig && !exact)
+       {
+         if (opterr)
+           fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+                    argv[0], argv[optind]);
+         nextchar += strlen (nextchar);
+         optind++;
+         optopt = 0;
+         return '?';
+       }
+
+      if (pfound != NULL)
+       {
+         option_index = indfound;
+         optind++;
+         if (*nameend)
+           {
+             /* Don't test has_arg with >, because some C compilers don't
+                allow it to be used on enums.  */
+             if (pfound->has_arg)
+               optarg = nameend + 1;
+             else
+               {
+                 if (opterr)
+                  if (argv[optind - 1][1] == '-')
+                   /* --option */
+                   fprintf (stderr,
+                    _("%s: option `--%s' doesn't allow an argument\n"),
+                    argv[0], pfound->name);
+                  else
+                   /* +option or -option */
+                   fprintf (stderr,
+                    _("%s: option `%c%s' doesn't allow an argument\n"),
+                    argv[0], argv[optind - 1][0], pfound->name);
+
+                 nextchar += strlen (nextchar);
+
+                 optopt = pfound->val;
+                 return '?';
+               }
+           }
+         else if (pfound->has_arg == 1)
+           {
+             if (optind < argc)
+               optarg = argv[optind++];
+             else
+               {
+                 if (opterr)
+                   fprintf (stderr,
+                          _("%s: option `%s' requires an argument\n"),
+                          argv[0], argv[optind - 1]);
+                 nextchar += strlen (nextchar);
+                 optopt = pfound->val;
+                 return optstring[0] == ':' ? ':' : '?';
+               }
+           }
+         nextchar += strlen (nextchar);
+         if (longind != NULL)
+           *longind = option_index;
+         if (pfound->flag)
+           {
+             *(pfound->flag) = pfound->val;
+             return 0;
+           }
+         return pfound->val;
+       }
+
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+        or the option starts with '--' or is not a valid short
+        option, then it's an error.
+        Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+         || my_index (optstring, *nextchar) == NULL)
+       {
+         if (opterr)
+           {
+             if (argv[optind][1] == '-')
+               /* --option */
+               fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+                        argv[0], nextchar);
+             else
+               /* +option or -option */
+               fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+                        argv[0], argv[optind][0], nextchar);
+           }
+         nextchar = (char *) "";
+         optind++;
+         optopt = 0;
+         return '?';
+       }
+    }
+
+  /* Look at and handle the next short option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+       if (opterr)
+         {
+           if (posixly_correct)
+             /* 1003.2 specifies the format of this message.  */
+             fprintf (stderr, _("%s: illegal option -- %c\n"),
+                      argv[0], c);
+           else
+             fprintf (stderr, _("%s: invalid option -- %c\n"),
+                      argv[0], c);
+         }
+       optopt = c;
+       return '?';
+      }
+    /* Convenience. Treat POSIX -W foo same as long option --foo */
+    if (temp[0] == 'W' && temp[1] == ';')
+      {
+       char *nameend;
+       const struct option *p;
+       const struct option *pfound = NULL;
+       int exact = 0;
+       int ambig = 0;
+       int indfound = 0;
+       int option_index;
+
+       /* This is an option that requires an argument.  */
+       if (*nextchar != '\0')
+         {
+           optarg = nextchar;
+           /* If we end this ARGV-element by taking the rest as an arg,
+              we must advance to the next element now.  */
+           optind++;
+         }
+       else if (optind == argc)
+         {
+           if (opterr)
+             {
+               /* 1003.2 specifies the format of this message.  */
+               fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+                        argv[0], c);
+             }
+           optopt = c;
+           if (optstring[0] == ':')
+             c = ':';
+           else
+             c = '?';
+           return c;
+         }
+       else
+         /* We already incremented `optind' once;
+            increment it again when taking next ARGV-elt as argument.  */
+         optarg = argv[optind++];
+
+       /* optarg is now the argument, see if it's in the
+          table of longopts.  */
+
+       for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+         /* Do nothing.  */ ;
+
+       /* Test all long options for either exact match
+          or abbreviated matches.  */
+       for (p = longopts, option_index = 0; p->name; p++, option_index++)
+         if (!strncmp (p->name, nextchar, nameend - nextchar))
+           {
+             if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+               {
+                 /* Exact match found.  */
+                 pfound = p;
+                 indfound = option_index;
+                 exact = 1;
+                 break;
+               }
+             else if (pfound == NULL)
+               {
+                 /* First nonexact match found.  */
+                 pfound = p;
+                 indfound = option_index;
+               }
+             else
+               /* Second or later nonexact match found.  */
+               ambig = 1;
+           }
+       if (ambig && !exact)
+         {
+           if (opterr)
+             fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+                      argv[0], argv[optind]);
+           nextchar += strlen (nextchar);
+           optind++;
+           return '?';
+         }
+       if (pfound != NULL)
+         {
+           option_index = indfound;
+           if (*nameend)
+             {
+               /* Don't test has_arg with >, because some C compilers don't
+                  allow it to be used on enums.  */
+               if (pfound->has_arg)
+                 optarg = nameend + 1;
+               else
+                 {
+                   if (opterr)
+                     fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+                              argv[0], pfound->name);
+
+                   nextchar += strlen (nextchar);
+                   return '?';
+                 }
+             }
+           else if (pfound->has_arg == 1)
+             {
+               if (optind < argc)
+                 optarg = argv[optind++];
+               else
+                 {
+                   if (opterr)
+                     fprintf (stderr,
+                              _("%s: option `%s' requires an argument\n"),
+                              argv[0], argv[optind - 1]);
+                   nextchar += strlen (nextchar);
+                   return optstring[0] == ':' ? ':' : '?';
+                 }
+             }
+           nextchar += strlen (nextchar);
+           if (longind != NULL)
+             *longind = option_index;
+           if (pfound->flag)
+             {
+               *(pfound->flag) = pfound->val;
+               return 0;
+             }
+           return pfound->val;
+         }
+         nextchar = NULL;
+         return 'W';   /* Let the application handle it.   */
+      }
+    if (temp[1] == ':')
+      {
+       if (temp[2] == ':')
+         {
+           /* This is an option that accepts an argument optionally.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               optind++;
+             }
+           else
+             optarg = NULL;
+           nextchar = NULL;
+         }
+       else
+         {
+           /* This is an option that requires an argument.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               /* If we end this ARGV-element by taking the rest as an arg,
+                  we must advance to the next element now.  */
+               optind++;
+             }
+           else if (optind == argc)
+             {
+               if (opterr)
+                 {
+                   /* 1003.2 specifies the format of this message.  */
+                   fprintf (stderr,
+                          _("%s: option requires an argument -- %c\n"),
+                          argv[0], c);
+                 }
+               optopt = c;
+               if (optstring[0] == ':')
+                 c = ':';
+               else
+                 c = '?';
+             }
+           else
+             /* We already incremented `optind' once;
+                increment it again when taking next ARGV-elt as argument.  */
+             optarg = argv[optind++];
+           nextchar = NULL;
+         }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+                          (const struct option *) 0,
+                          (int *) 0,
+                          0);
+}
+
+#endif /* Not ELIDE_CODE.  */
+\f
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == -1)
+       break;
+
+      switch (c)
+       {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/neon/lib/getopt.h b/neon/lib/getopt.h
new file mode 100644 (file)
index 0000000..c4adc30
--- /dev/null
@@ -0,0 +1,133 @@
+/* Declarations for getopt.
+   Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+   NOTE: The canonical source of this file is maintained with the GNU C Library.
+   Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+   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 version 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 this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA.  */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument         (or 0) if the option does not take an argument,
+   required_argument   (or 1) if the option requires an argument,
+   optional_argument   (or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+#if defined (__STDC__) && __STDC__
+  const char *name;
+#else
+  char *name;
+#endif
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define        no_argument             0
+#define required_argument      1
+#define optional_argument      2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+                       const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+extern int _getopt_internal (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind,
+                            int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* getopt.h */
diff --git a/neon/lib/getopt1.c b/neon/lib/getopt1.c
new file mode 100644 (file)
index 0000000..4ce1065
--- /dev/null
@@ -0,0 +1,190 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
+     Free Software Foundation, Inc.
+
+   NOTE: The canonical source of this file is maintained with the GNU C Library.
+   Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+   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 version 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 this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA.  */
+\f
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef        NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* Not ELIDE_CODE.  */
+\f
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+       {"add", 1, 0, 0},
+       {"append", 0, 0, 0},
+       {"delete", 1, 0, 0},
+       {"verbose", 0, 0, 0},
+       {"create", 0, 0, 0},
+       {"file", 1, 0, 0},
+       {0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, "abc:d:0123456789",
+                      long_options, &option_index);
+      if (c == -1)
+       break;
+
+      switch (c)
+       {
+       case 0:
+         printf ("option %s", long_options[option_index].name);
+         if (optarg)
+           printf (" with arg %s", optarg);
+         printf ("\n");
+         break;
+
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case 'd':
+         printf ("option d with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/neon/lib/linelen.c b/neon/lib/linelen.c
new file mode 100644 (file)
index 0000000..15c6ebe
--- /dev/null
@@ -0,0 +1,57 @@
+/* Line length calculation routine.
+   Taken from fileutils-4.0i/src/ls.c
+   Copyright (C) 85, 88, 90, 91, 1995-1999 Free Software Foundation, Inc.
+
+   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 version 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 this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
+
+   Id: linelen.c,v 1.1 1999/06/25 19:26:46 joe Exp  
+ */
+
+#if HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+#ifdef GWINSZ_IN_SYS_IOCTL
+# include <sys/ioctl.h>
+#endif
+
+#ifdef WINSIZE_IN_PTEM
+# include <sys/stream.h>
+# include <sys/ptem.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* For STDOUT_FILENO */
+#endif
+
+#define DEFAULT_LINE_LENGTH 80
+
+int get_line_length( void ) {
+    int ret = DEFAULT_LINE_LENGTH;
+
+/* joe: Some systems do this with a 'struct ttysize'?
+ * There is code in tin to do it, but tin has a dodgy license.
+ * DIY, if you have such a system.
+ */
+
+#ifdef TIOCGWINSZ
+    struct winsize ws;
+    
+    if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0)
+       ret = ws.ws_col;
+    
+#endif
+    return ret;
+}
diff --git a/neon/lib/netrc.c b/neon/lib/netrc.c
new file mode 100644 (file)
index 0000000..b6af1c3
--- /dev/null
@@ -0,0 +1,423 @@
+/* netrc.c -- parse the .netrc file to get hosts, accounts, and passwords
+
+   Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   For license terms, see the file COPYING in this directory.
+
+   Compile with -DSTANDALONE to test this module. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <config.h>
+#include "netrc.h"
+
+#include "xalloc.h"
+
+/* Normally defined in xmalloc.c */
+# define xrealloc realloc
+
+#define POPBUFSIZE BUFSIZ
+
+/* Maybe add NEWENTRY to the account information list, LIST.  NEWENTRY is
+   set to a ready-to-use netrc_entry, in any event. */
+static void
+maybe_add_to_list (netrc_entry **newentry, netrc_entry **list)
+{
+    netrc_entry *a, *l;
+    a = *newentry;
+    l = *list;
+
+    /* We need an account name in order to add the entry to the list. */
+    if (a && ! a->account)
+    {
+       /* Free any allocated space. */
+       if (a->host)
+           free (a->host);
+       if (a->password)
+           free (a->password);
+    }
+    else
+    {
+       if (a)
+       {
+           /* Add the current machine into our list. */
+           a->next = l;
+           l = a;
+       }
+
+       /* Allocate a new netrc_entry structure. */
+       a = (netrc_entry *) xmalloc (sizeof (netrc_entry));
+    }
+
+    /* Zero the structure, so that it is ready to use. */
+    memset (a, 0, sizeof(*a));
+
+    /* Return the new pointers. */
+    *newentry = a;
+    *list = l;
+    return;
+}
+
+
+/* Parse FILE as a .netrc file (as described in ftp(1)), and return a
+   list of entries.  NULL is returned if the file could not be
+   parsed. */
+netrc_entry *
+parse_netrc (file)
+     char *file;
+{
+    FILE *fp;
+    char buf[POPBUFSIZE+1], *p, *tok;
+    const char *premature_token;
+    netrc_entry *current, *retval;
+    int ln;
+
+    /* The latest token we've seen in the file. */
+    enum
+    {
+       tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
+    } last_token = tok_nothing;
+
+    current = retval = NULL;
+
+    fp = fopen (file, "r");
+    if (!fp)
+    {
+       /* Just return NULL if we can't open the file. */
+       return NULL;
+    }
+
+    /* Initialize the file data. */
+    ln = 0;
+    premature_token = NULL;
+
+    /* While there are lines in the file... */
+    while (fgets(buf, POPBUFSIZE, fp))
+    {
+       ln++;
+
+       /* Strip trailing CRLF */
+       for (p = buf + strlen(buf) - 1; (p >= buf) && isspace((unsigned)*p); p--)
+           *p = '\0';
+
+       /* Parse the line. */
+       p = buf;
+
+       /* If the line is empty... */
+       if (!*p) {
+           if (last_token == tok_macdef) {     /* end of macro */
+               last_token = tok_nothing;
+           } else {
+               continue;                       /* otherwise ignore it */
+           }
+       }
+
+       /* If we are defining macros, then skip parsing the line. */
+       while (*p && last_token != tok_macdef)
+       {
+           char quote_char = 0;
+           char *pp;
+
+           /* Skip any whitespace. */
+           while (*p && isspace ((unsigned)*p))
+               p++;
+
+           /* Discard end-of-line comments. */
+           if (*p == '#')
+               break;
+
+           tok = pp = p;
+
+           /* Find the end of the token. */
+           while (*p && (quote_char || !isspace ((unsigned)*p)))
+           {
+               if (quote_char)
+               {
+                   if (quote_char == *p)
+                   {
+                       quote_char = 0;
+                       p ++;
+                   }
+                   else
+                   {
+                       *pp = *p;
+                       p ++;
+                       pp ++;
+                   }
+               }
+               else
+               {
+                   if (*p == '"' || *p == '\'')
+                       quote_char = *p;
+                   else
+                   {
+                       *pp = *p;
+                       pp ++;
+                   }
+                   p ++;
+               }
+           }
+           /* Null-terminate the token, if it isn't already. */
+           if (*p)
+               *p ++ = '\0';
+           *pp = 0;
+
+           switch (last_token)
+           {
+           case tok_login:
+               if (current)
+                   current->account = (char *) xstrdup (tok);
+               else
+                   premature_token = "login";
+               break;
+
+           case tok_machine:
+               /* Start a new machine entry. */
+               maybe_add_to_list (&current, &retval);
+               current->host = (char *) xstrdup (tok);
+               break;
+
+           case tok_password:
+               if (current)
+                   current->password = (char *) xstrdup (tok);
+               else
+                   premature_token = "password";
+               break;
+
+               /* We handle most of tok_macdef above. */
+           case tok_macdef:
+               if (!current)
+                   premature_token = "macdef";
+               break;
+
+               /* We don't handle the account keyword at all. */
+           case tok_account:
+               if (!current)
+                   premature_token = "account";
+               break;
+
+               /* We handle tok_nothing below this switch. */
+           case tok_nothing:
+               break;
+           }
+
+           if (premature_token)
+           {
+#ifdef HAVE_ERROR
+               error_at_line (0, file, ln,
+                              "warning: found \"%s\" before any host names",
+                              premature_token);
+#else
+               fprintf (stderr,
+                        "%s:%d: warning: found \"%s\" before any host names\n",
+                        file, ln, premature_token);
+#endif
+               premature_token = NULL;
+           }
+
+           if (last_token != tok_nothing)
+               /* We got a value, so reset the token state. */
+               last_token = tok_nothing;
+           else
+           {
+               /* Fetch the next token. */
+               if (!strcmp (tok, "default"))
+               {
+                   maybe_add_to_list (&current, &retval);
+               }
+               else if (!strcmp (tok, "login"))
+                   last_token = tok_login;
+
+               else if (!strcmp (tok, "user"))
+                   last_token = tok_login;
+
+               else if (!strcmp (tok, "macdef"))
+                   last_token = tok_macdef;
+
+               else if (!strcmp (tok, "machine"))
+                   last_token = tok_machine;
+
+               else if (!strcmp (tok, "password"))
+                   last_token = tok_password;
+
+               else if (!strcmp (tok, "passwd"))
+                   last_token = tok_password;
+
+               else if (!strcmp (tok, "account"))
+                   last_token = tok_account;
+
+               else
+               {
+                   fprintf (stderr, "%s:%d: warning: unknown token \"%s\"\n",
+                            file, ln, tok);
+               }
+           }
+       }
+    }
+
+    fclose (fp);
+
+    /* Finalize the last machine entry we found. */
+    maybe_add_to_list (&current, &retval);
+    free (current);
+
+    /* Reverse the order of the list so that it appears in file order. */
+    current = retval;
+    retval = NULL;
+    while (current)
+    {
+       netrc_entry *saved_reference;
+
+       /* Change the direction of the pointers. */
+       saved_reference = current->next;
+       current->next = retval;
+
+       /* Advance to the next node. */
+       retval = current;
+       current = saved_reference;
+    }
+
+    return retval;
+}
+
+
+/* Return the netrc entry from LIST corresponding to HOST.  NULL is
+   returned if no such entry exists. */
+netrc_entry *
+search_netrc (list, host)
+     netrc_entry *list;
+     const char *host;
+{
+    /* Look for the HOST in LIST. */
+    while (list)
+    {
+       if (!list->host)
+           /* We hit the default entry. */
+           break;
+
+       else if (!strcmp (list->host, host))
+           /* We found a matching entry. */
+           break;
+
+       list = list->next;
+    }
+
+    /* Return the matching entry, or NULL. */
+    return list;
+}
+
+
+#ifdef STANDALONE
+#include <sys/types.h>
+#include <sys/stat.h>
+
+extern int errno;
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+    struct stat sb;
+    char *program_name, *file, *target;
+    netrc_entry *head, *a;
+
+    if (argc < 2)
+    {
+       fprintf (stderr, "Usage: %s NETRC [HOSTNAME]...\n", argv[0]);
+       exit (1);
+    }
+
+    program_name = argv[0];
+    file = argv[1];
+    target = argv[2];
+
+    if (stat (file, &sb))
+    {
+       fprintf (stderr, "%s: cannot stat %s: %s\n", argv[0], file,
+                strerror (errno));
+       exit (1);
+    }
+
+    head = parse_netrc (file);
+    if (!head)
+    {
+       fprintf (stderr, "%s: no entries found in %s\n", argv[0], file);
+       exit (1);
+    }
+
+    if (argc > 2)
+    {
+       int i, status;
+       status = 0;
+       for (i = 2; i < argc; i++)
+       {
+           /* Print out the host that we are checking for. */
+           fputs (argv[i], stdout);
+
+           a = search_netrc (head, argv[i]);
+           if (a)
+           {
+               /* Print out the account and password (if any). */
+               fputc (' ', stdout);
+               fputs (a->account, stdout);
+               if (a->password)
+               {
+                   fputc (' ', stdout);
+                   fputs (a->password, stdout);
+               }
+           }
+           else
+               status = 1;
+
+           fputc ('\n', stdout);
+       }
+       exit (status);
+    }
+
+    /* Print out the entire contents of the netrc. */
+    a = head;
+    while (a)
+    {
+       /* Print the host name. */
+       if (a->host)
+           fputs (a->host, stdout);
+       else
+           fputs ("DEFAULT", stdout);
+
+       fputc (' ', stdout);
+
+       /* Print the account name. */
+       fputs (a->account, stdout);
+
+       if (a->password)
+       {
+           /* Print the password, if there is any. */
+           fputc (' ', stdout);
+           fputs (a->password, stdout);
+       }
+
+       fputc ('\n', stdout);
+       a = a->next;
+    }
+
+    exit (0);
+}
+#endif /* STANDALONE */
diff --git a/neon/lib/netrc.h b/neon/lib/netrc.h
new file mode 100644 (file)
index 0000000..66916b0
--- /dev/null
@@ -0,0 +1,65 @@
+/* netrc.h -- declarations for netrc.c
+   Copyright (C) 1996, Free Software Foundation, Inc.
+   Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef _NETRC_H_
+#define _NETRC_H_ 1
+
+# undef __BEGIN_DECLS
+# undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+#undef __P
+#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(WIN32) || defined(__cplusplus)
+# define __P(protos) protos
+#else
+# define __P(protos) ()
+#endif
+
+/* The structure used to return account information from the .netrc. */
+typedef struct _netrc_entry {
+  /* The exact host name given in the .netrc, NULL if default. */
+  char *host;
+
+  /* The name of the account. */
+  char *account;
+
+  /* Password for the account (NULL, if none). */
+  char *password;
+
+  /* Pointer to the next entry in the list. */
+  struct _netrc_entry *next;
+} netrc_entry;
+
+__BEGIN_DECLS
+/* Parse FILE as a .netrc file (as described in ftp(1)), and return a
+   list of entries.  NULL is returned if the file could not be
+   parsed. */
+netrc_entry *parse_netrc __P((char *file));
+
+/* Return the netrc entry from LIST corresponding to HOST.  NULL is
+   returned if no such entry exists. */
+netrc_entry *search_netrc __P((netrc_entry *list, const char *host));
+__END_DECLS
+
+#endif /* _NETRC_H_ */
diff --git a/neon/lib/rpmatch.c b/neon/lib/rpmatch.c
new file mode 100644 (file)
index 0000000..aff3832
--- /dev/null
@@ -0,0 +1,88 @@
+/* Determine whether string value is affirmation or negative response
+   according to current locale's data.
+   Copyright (C) 1996, 1998 Free Software Foundation, Inc.
+
+   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 version 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 this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if STDC_HEADERS || _LIBC
+# include <stddef.h>
+# include <stdlib.h>
+#else
+# ifndef NULL
+#  define NULL 0
+# endif
+#endif
+
+#if defined(ENABLE_NLS) && defined(HAVE_REGEX_H)
+#define RP_USE_REGEX
+#endif
+
+#ifdef RP_USE_REGEX
+# include <sys/types.h>
+# include <regex.h>
+# include <libintl.h>
+# define _(Text) gettext (Text)
+
+static int
+try (const char *response, const char *pattern, const int match,
+     const int nomatch, const char **lastp, regex_t *re)
+{
+  if (pattern != *lastp)
+    {
+      /* The pattern has changed.  */
+      if (*lastp)
+       {
+         /* Free the old compiled pattern.  */
+         regfree (re);
+         *lastp = NULL;
+       }
+      /* Compile the pattern and cache it for future runs.  */
+      if (regcomp (re, pattern, REG_EXTENDED) != 0)
+       return -1;
+      *lastp = pattern;
+    }
+
+  /* See if the regular expression matches RESPONSE.  */
+  return regexec (re, response, 0, NULL, 0) == 0 ? match : nomatch;
+}
+#endif
+
+
+int
+rpmatch (const char *response)
+{
+#ifdef RP_USE_REGEX
+  /* Match against one of the response patterns, compiling the pattern
+     first if necessary.  */
+
+  /* We cache the response patterns and compiled regexps here.  */
+  static const char *yesexpr, *noexpr;
+  static regex_t yesre, nore;
+  int result;
+
+  return ((result = try (response, _("^[yY]"), 1, 0,
+                        &yesexpr, &yesre))
+         ? result
+         : try (response, _("^[nN]"), 0, -1, &noexpr, &nore));
+#else
+  /* Test against "^[yY]" and "^[nN]", hardcoded to avoid requiring regex */
+  return (*response == 'y' || *response == 'Y' ? 1
+         : *response == 'n' || *response == 'N' ? 0 : -1);
+#endif
+}
diff --git a/neon/lib/snprintf.c b/neon/lib/snprintf.c
new file mode 100644 (file)
index 0000000..4984117
--- /dev/null
@@ -0,0 +1,835 @@
+
+/*
+   Unix snprintf implementation.
+   Version 1.1
+   
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   
+   Revision History:
+
+   sitecopy changes:
+      renamed dtoa -> doubletoa to avoid dtoa conflict with cygwin.
+
+
+   1.1:
+      *  added changes from Miles Bader
+      *  corrected a bug with %f
+      *  added support for %#g
+      *  added more comments :-)
+   1.0:
+      *  supporting must ANSI syntaxic_sugars
+   0.0:
+      *  suppot %s %c %d
+
+ THANKS(for the patches and ideas):
+     Miles Bader
+     Cyrille Rustom
+     Jacek Slabocewiz
+     Mike Parker(mouse)
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "snprintf.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>    /* for strlen() */
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>    /* for atoi() */
+#endif
+#include <ctype.h>
+
+
+/*
+ * Find the nth power of 10
+ */
+PRIVATE double
+#ifdef __STDC__
+pow_10(int n)
+#else
+pow_10(n)
+int n;
+#endif
+{ 
+  int i;
+  double P;
+
+  if (n < 0)
+    for (i = 1, P = 1., n = -n ; i <= n ; i++) {P *= .1;}
+  else
+    for (i = 1, P = 1. ; i <= n ; i++) {P *= 10.0;}
+  return P;
+}
+
+/*
+ * Find the integral part of the log in base 10 
+ * Note: this not a real log10()
+         I just need and approximation(integerpart) of x in:
+          10^x ~= r
+ * log_10(200) = 2;
+ * log_10(250) = 2;
+ */
+PRIVATE int
+#ifdef __STDC__
+log_10(double r)
+#else
+log_10(r)
+double r;
+#endif
+{ 
+  int i = 0;
+  double result = 1.;
+
+  if (r < 0.)
+    r = -r;
+
+  if (r < 1.) {
+    while (result >= r) {result *= .1; i++;}
+    return (-i);
+  } else {
+    while (result <= r) {result *= 10.; i++;}
+    return (i - 1);
+  }
+}
+
+/*
+ * This function return the fraction part of a double
+ * and set in ip the integral part.
+ * In many ways it resemble the modf() found on most Un*x
+ */
+PRIVATE double
+#ifdef __STDC__
+integral(double real, double * ip)
+#else
+integral(real, ip)
+double real;
+double * ip;
+#endif
+{ 
+  int j;
+  double i, s, p;
+  double real_integral = 0.;
+
+/* take care of the obvious */
+/* equal to zero ? */
+  if (real == 0.) {
+    *ip = 0.;
+    return (0.);
+  }
+
+/* negative number ? */
+  if (real < 0.)
+    real = -real;
+
+/* a fraction ? */
+  if ( real < 1.) {
+    *ip = 0.;
+    return real;
+  }
+/* the real work :-) */
+  for (j = log_10(real); j >= 0; j--) {
+    p = pow_10(j);
+    s = (real - real_integral)/p;
+    i = 0.;
+    while (i + 1. <= s) {i++;}
+    real_integral += i*p;
+  }
+  *ip = real_integral;
+  return (real - real_integral);
+}
+
+#define PRECISION 1.e-6
+/* 
+ * return an ascii representation of the integral part of the number
+ * and set fract to be an ascii representation of the fraction part
+ * the container for the fraction and the integral part or staticly
+ * declare with fix size 
+ */
+PRIVATE char *
+#ifdef __STDC__
+numtoa(double number, int base, int precision, char ** fract)
+#else
+numtoa(number, base, precision, fract)
+double number;
+int base;
+int precision;
+char ** fract;
+#endif
+{
+  register int i, j;
+  double ip, fp; /* integer and fraction part */
+  double fraction;
+  int digits = MAX_INT - 1;
+  static char integral_part[MAX_INT];
+  static char fraction_part[MAX_FRACT];
+  double sign;
+  int ch;
+
+/* taking care of the obvious case: 0.0 */
+  if (number == 0.) { 
+    integral_part[0] = '0';
+    integral_part[1] = '\0';
+    fraction_part[0] = '0';
+    fraction_part[1] = '\0';
+    return integral_part;
+  }
+
+/* for negative numbers */
+  if ((sign = number) < 0.) {
+    number = -number;
+    digits--; /* sign consume one digit */
+  } 
+
+  fraction = integral(number, &ip);
+  number = ip;
+/* do the integral part */
+  if ( ip == 0.) {
+    integral_part[0] = '0';
+    i = 1;
+  } else {
+    for ( i = 0; i < digits && number != 0.; ++i) {
+      number /= base;
+      fp = integral(number, &ip);
+      ch = (int)((fp + PRECISION)*base); /* force to round */
+      integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10;
+      if (! isxdigit(integral_part[i])) /* bail out overflow !! */
+        break; 
+      number = ip;
+     }
+  }
+     
+/* Oh No !! out of bound, ho well fill it up ! */
+  if (number != 0.)
+    for (i = 0; i < digits; ++i)
+      integral_part[i] = '9';
+
+/* put the sign ? */
+  if (sign < 0.)
+    integral_part[i++] = '-';
+
+  integral_part[i] = '\0';
+
+/* reverse every thing */
+  for ( i--, j = 0; j < i; j++, i--)
+    SWAP_INT(integral_part[i], integral_part[j]);  
+
+/* the fractionnal part */
+  for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision--     ) {
+    fraction_part[i] = (int)((fp + PRECISION)*10. + '0');
+    if (! isdigit(fraction_part[i])) /* underflow ? */
+      break;
+    fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.);
+  }
+  fraction_part[i] = '\0';
+
+  if (fract != (char **)0)
+    *fract = fraction_part;
+
+  return integral_part;
+
+}
+
+/* for %d and friends, it puts in holder
+ * the representation with the right padding
+ */
+PRIVATE void
+#ifdef __STDC__
+decimal(struct DATA *p, double d)
+#else
+decimal(p, d)
+struct DATA *p;
+double d;
+#endif
+{
+  char *tmp;
+
+  tmp = itoa(d);
+  p->width -= strlen(tmp);
+  PAD_RIGHT(p);
+  PUT_PLUS(d, p);
+  PUT_SPACE(d, p);
+  while (*tmp) { /* the integral */
+    PUT_CHAR(*tmp, p);
+    tmp++;
+  }
+  PAD_LEFT(p);
+}
+
+/* for %o octal representation */
+PRIVATE void
+#ifdef __STDC__
+octal(struct DATA *p, double d)
+#else
+octal(p, d)
+struct DATA *p;
+double d;
+#endif
+{
+  char *tmp;
+
+  tmp = otoa(d);
+  p->width -= strlen(tmp);
+  PAD_RIGHT(p);
+  if (p->square == FOUND) /* had prefix '0' for octal */
+    PUT_CHAR('0', p);
+  while (*tmp) { /* octal */
+    PUT_CHAR(*tmp, p);
+    tmp++;
+  }
+  PAD_LEFT(p);
+}
+
+/* for %x %X hexadecimal representation */
+PRIVATE void
+#ifdef __STDC__
+hexa(struct DATA *p, double d)
+#else
+hexa(p, d)
+struct DATA *p;
+double d;
+#endif
+{
+  char *tmp;
+
+  tmp = htoa(d);
+  p->width -= strlen(tmp);
+  PAD_RIGHT(p);
+  if (p->square == FOUND) { /* prefix '0x' for hexa */
+    PUT_CHAR('0', p); PUT_CHAR(*p->pf, p);
+  }
+  while (*tmp) { /* hexa */
+    PUT_CHAR((*p->pf == 'X' ? toupper(*tmp) : *tmp), p);
+    tmp++;
+  }
+  PAD_LEFT(p);
+}
+
+/* %s strings */
+PRIVATE void
+#ifdef __STDC__
+strings(struct DATA *p, char *tmp)
+#else
+strings(p, tmp)
+struct DATA *p;
+char *tmp;
+#endif
+{
+  int i;
+
+  i = strlen(tmp);
+  if (p->precision != NOT_FOUND) /* the smallest number */
+    i = (i < p->precision ? i : p->precision);
+  p->width -= i;
+  PAD_RIGHT(p);
+  while (i-- > 0) { /* put the sting */
+    PUT_CHAR(*tmp, p);
+    tmp++;
+  }
+  PAD_LEFT(p);
+}
+
+/* %f or %g  floating point representation */
+PRIVATE void
+#ifdef __STDC__
+floating(struct DATA *p, double d)
+#else
+floating(p, d)
+struct DATA *p;
+double d;
+#endif
+{
+  char *tmp, *tmp2;
+  int i;
+
+  DEF_PREC(p);
+  d = ROUND(d, p);
+  tmp = doubletoa(d, p->precision, &tmp2);
+  /* calculate the padding. 1 for the dot */
+  p->width = p->width -
+            ((d > 0. && p->justify == RIGHT) ? 1:0) -
+            ((p->space == FOUND) ? 1:0) -
+            strlen(tmp) - p->precision - 1;
+  PAD_RIGHT(p);  
+  PUT_PLUS(d, p);
+  PUT_SPACE(d, p);
+  while (*tmp) { /* the integral */
+    PUT_CHAR(*tmp, p);
+    tmp++;
+  }
+  if (p->precision != 0 || p->square == FOUND)
+    PUT_CHAR('.', p);  /* put the '.' */
+  if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
+    for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
+       tmp2[i] = '\0'; 
+  for (; *tmp2; tmp2++)
+    PUT_CHAR(*tmp2, p); /* the fraction */
+  
+  PAD_LEFT(p);
+} 
+
+/* %e %E %g exponent representation */
+PRIVATE void
+#ifdef __STDC__
+exponent(struct DATA *p, double d)
+#else
+exponent(p, d)
+struct DATA *p;
+double d;
+#endif
+{
+  char *tmp, *tmp2;
+  int j, i;
+
+  DEF_PREC(p);
+  j = log_10(d);
+  d = d / pow_10(j);  /* get the Mantissa */
+  d = ROUND(d, p);                  
+  tmp = doubletoa(d, p->precision, &tmp2);
+  /* 1 for unit, 1 for the '.', 1 for 'e|E',
+   * 1 for '+|-', 3 for 'exp' */
+  /* calculate how much padding need */
+  p->width = p->width - 
+             ((d > 0. && p->justify == RIGHT) ? 1:0) -
+             ((p->space == FOUND) ? 1:0) - p->precision - 7;
+  PAD_RIGHT(p);
+  PUT_PLUS(d, p);
+  PUT_SPACE(d, p);
+  while (*tmp) {/* the integral */
+    PUT_CHAR(*tmp, p);
+    tmp++;
+  }
+  if (p->precision != 0 || p->square == FOUND)
+    PUT_CHAR('.', p);  /* the '.' */
+  if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
+    for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
+       tmp2[i] = '\0'; 
+  for (; *tmp2; tmp2++)
+    PUT_CHAR(*tmp2, p); /* the fraction */
+
+  if (*p->pf == 'g' || *p->pf == 'e') { /* the exponent put the 'e|E' */
+     PUT_CHAR('e', p);
+   } else
+     PUT_CHAR('E', p);
+   if (j > 0) {  /* the sign of the exp */
+     PUT_CHAR('+', p);
+   } else {
+     PUT_CHAR('-', p);
+     j = -j;
+   }
+   tmp = itoa((double)j);
+   if (j < 9) {  /* need to pad the exponent with 0 '000' */
+     PUT_CHAR('0', p); PUT_CHAR('0', p);
+   } else if (j < 99)
+     PUT_CHAR('0', p);
+   while (*tmp) { /* the exponent */
+     PUT_CHAR(*tmp, p);
+     tmp++;
+   }
+   PAD_LEFT(p);
+}
+
+/* initialize the conversion specifiers */
+PRIVATE void
+#ifdef __STDC__
+conv_flag(char * s, struct DATA * p)
+#else
+conv_flag(s, p)
+char * s;
+struct DATA * p;
+#endif
+{
+  char number[MAX_FIELD/2];
+  int i;
+
+  p->precision = p->width = NOT_FOUND;
+  p->star_w = p->star_p = NOT_FOUND;
+  p->square = p->space = NOT_FOUND;
+  p->a_long = p->justify = NOT_FOUND;
+  p->pad = ' ';
+
+  for(;s && *s ;s++) {
+    switch(*s) {
+      case ' ': p->space = FOUND; break;
+      case '#': p->square = FOUND; break;
+      case '*': if (p->width == NOT_FOUND)
+                  p->width = p->star_w = FOUND;
+                else
+                  p->precision = p->star_p = FOUND;
+                break;
+      case '+': p->justify = RIGHT; break;
+      case '-': p->justify = LEFT; break;
+      case '.': if (p->width == NOT_FOUND)
+                  p->width = 0;
+                break;
+      case '0': p->pad = '0'; break;
+      case '1': case '2': case '3':
+      case '4': case '5': case '6':
+      case '7': case '8': case '9':     /* gob all the digits */
+        for (i = 0; isdigit(*s); i++, s++) 
+          if (i < MAX_FIELD/2 - 1)
+            number[i] = *s;
+        number[i] = '\0';
+        if (p->width == NOT_FOUND)
+          p->width = atoi(number);
+        else
+          p->precision = atoi(number);
+        s--;   /* went to far go back */
+        break;
+    }
+  }
+}
+
+PUBLIC int
+#ifdef __STDC__
+vsnprintf(char *string, size_t length, const char * format, va_list args)
+#else
+vsnprintf(string, length, format, args)
+char *string;
+size_t length;
+char * format;
+va_list args;
+#endif
+{
+  struct DATA data;
+  char conv_field[MAX_FIELD];
+  double d; /* temporary holder */
+  int state;
+  int i;
+
+  data.length = length - 1; /* leave room for '\0' */
+  data.holder = string;
+  data.pf = format;
+  data.counter = 0;
+
+
+/* sanity check, the string must be > 1 */
+  if (length < 1)
+    return -1;
+
+
+  for (; *data.pf && (data.counter < data.length); data.pf++) {
+    if ( *data.pf == '%' ) { /* we got a magic % cookie */
+      conv_flag((char *)0, &data); /* initialise format flags */
+      for (state = 1; *data.pf && state;) {
+        switch (*(++data.pf)) {
+          case '\0': /* a NULL here ? ? bail out */
+            *data.holder = '\0';
+            return data.counter;
+            break;
+          case 'f':  /* float, double */
+            STAR_ARGS(&data);
+            d = va_arg(args, double);
+            floating(&data, d);  
+            state = 0;
+            break;
+          case 'g': 
+          case 'G':
+            STAR_ARGS(&data);
+            DEF_PREC(&data);
+            d = va_arg(args, double);
+            i = log_10(d);
+            /*
+             * for '%g|%G' ANSI: use f if exponent
+             * is in the range or [-4,p] exclusively
+             * else use %e|%E
+             */
+            if (-4 < i && i < data.precision)
+              floating(&data, d);
+            else
+              exponent(&data, d);
+            state = 0;
+            break;
+          case 'e':
+          case 'E':  /* Exponent double */
+            STAR_ARGS(&data);
+            d = va_arg(args, double);
+            exponent(&data, d);
+            state = 0;
+            break;
+          case 'u':
+          case 'd':  /* decimal */
+            STAR_ARGS(&data);
+            if (data.a_long == FOUND)
+              d = va_arg(args, long);
+            else
+              d = va_arg(args, int);
+            decimal(&data, d);
+            state = 0;
+            break;
+          case 'o':  /* octal */
+            STAR_ARGS(&data);
+            if (data.a_long == FOUND)
+              d = va_arg(args, long);
+            else
+              d = va_arg(args, int);
+            octal(&data, d);
+            state = 0;
+            break;
+          case 'x': 
+          case 'X':  /* hexadecimal */
+            STAR_ARGS(&data);
+            if (data.a_long == FOUND)
+              d = va_arg(args, long);
+            else
+              d = va_arg(args, int);
+            hexa(&data, d);
+            state = 0;
+            break;
+          case 'c': /* character */
+            d = va_arg(args, int);
+            PUT_CHAR(d, &data);
+            state = 0;
+            break;
+          case 's':  /* string */
+            STAR_ARGS(&data);
+            strings(&data, va_arg(args, char *));
+            state = 0;
+            break;
+          case 'n':
+             *(va_arg(args, int *)) = data.counter; /* what's the count ? */
+             state = 0;
+             break;
+          case 'l':
+            data.a_long = FOUND;
+            break;
+          case 'h':
+            break;
+          case '%':  /* nothing just % */
+            PUT_CHAR('%', &data);
+            state = 0;
+            break;
+          case '#': case ' ': case '+': case '*':
+          case '-': case '.': case '0': case '1': 
+          case '2': case '3': case '4': case '5':
+          case '6': case '7': case '8': case '9':
+           /* initialize width and precision */
+            for (i = 0; isflag(*data.pf); i++, data.pf++) 
+              if (i < MAX_FIELD - 1)
+                conv_field[i] = *data.pf;
+            conv_field[i] = '\0';
+            conv_flag(conv_field, &data);
+            data.pf--;   /* went to far go back */
+            break;
+          default:
+            /* is this an error ? maybe bail out */
+            state = 0;
+            break;
+        } /* end switch */
+      } /* end of for state */
+    } else { /* not % */
+      PUT_CHAR(*data.pf, &data);  /* add the char the string */
+    }
+  }
+
+  *data.holder = '\0'; /* the end ye ! */
+
+  return data.counter;
+}
+
+#ifndef HAVE_SNPRINTF
+
+PUBLIC int
+#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
+snprintf(char *string, size_t length, const char * format, ...)
+#else
+snprintf(string, length, format, va_alist)
+char *string;
+size_t length;
+char * format;
+va_dcl
+#endif
+{
+  int rval;
+  va_list args;
+
+#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
+  va_start(args, format);
+#else
+  va_start(args);
+#endif
+
+  rval = vsnprintf (string, length, format, args);
+
+  va_end(args);
+
+  return rval;
+}
+
+#endif /* HAVE_SNPRINTF */
+
+
+#ifdef DRIVER
+
+#include <stdio.h>
+
+/* set of small tests for snprintf() */
+void main()
+{
+  char holder[100];
+  int i;
+
+/*
+  printf("Suite of test for snprintf:\n");
+  printf("a_format\n");
+  printf("printf() format\n");
+  printf("snprintf() format\n\n");
+*/
+/* Checking the field widths */
+
+  printf("/%%d/, 336\n");
+  snprintf(holder, sizeof holder, "/%d/\n", 336);
+  printf("/%d/\n", 336);
+  printf("%s\n", holder);
+
+  printf("/%%2d/, 336\n");
+  snprintf(holder, sizeof holder, "/%2d/\n", 336);
+  printf("/%2d/\n", 336);
+  printf("%s\n", holder);
+
+  printf("/%%10d/, 336\n");
+  snprintf(holder, sizeof holder, "/%10d/\n", 336);
+  printf("/%10d/\n", 336);
+  printf("%s\n", holder);
+
+  printf("/%%-10d/, 336\n");
+  snprintf(holder, sizeof holder, "/%-10d/\n", 336);
+  printf("/%-10d/\n", 336);
+  printf("%s\n", holder);
+
+
+/* floating points */
+
+  printf("/%%f/, 1234.56\n");
+  snprintf(holder, sizeof holder, "/%f/\n", 1234.56);
+  printf("/%f/\n", 1234.56);
+  printf("%s\n", holder);
+
+  printf("/%%e/, 1234.56\n");
+  snprintf(holder, sizeof holder, "/%e/\n", 1234.56);
+  printf("/%e/\n", 1234.56);
+  printf("%s\n", holder);
+
+  printf("/%%4.2f/, 1234.56\n");
+  snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56);
+  printf("/%4.2f/\n", 1234.56);
+  printf("%s\n", holder);
+
+  printf("/%%3.1f/, 1234.56\n");
+  snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56);
+  printf("/%3.1f/\n", 1234.56);
+  printf("%s\n", holder);
+
+  printf("/%%10.3f/, 1234.56\n");
+  snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56);
+  printf("/%10.3f/\n", 1234.56);
+  printf("%s\n", holder);
+
+  printf("/%%10.3e/, 1234.56\n");
+  snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56);
+  printf("/%10.3e/\n", 1234.56);
+  printf("%s\n", holder);
+
+  printf("/%%+4.2f/, 1234.56\n");
+  snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56);
+  printf("/%+4.2f/\n", 1234.56);
+  printf("%s\n", holder);
+
+  printf("/%%010.2f/, 1234.56\n");
+  snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56);
+  printf("/%010.2f/\n", 1234.56);
+  printf("%s\n", holder);
+
+#define BLURB "Outstanding acting !"
+/* strings precisions */
+
+  printf("/%%2s/, \"%s\"\n", BLURB);
+  snprintf(holder, sizeof holder, "/%2s/\n", BLURB);
+  printf("/%2s/\n", BLURB);
+  printf("%s\n", holder);
+
+  printf("/%%22s/ %s\n", BLURB);
+  snprintf(holder, sizeof holder, "/%22s/\n", BLURB);
+  printf("/%22s/\n", BLURB);
+  printf("%s\n", holder);
+
+  printf("/%%22.5s/ %s\n", BLURB);
+  snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB);
+  printf("/%22.5s/\n", BLURB);
+  printf("%s\n", holder);
+
+  printf("/%%-22.5s/ %s\n", BLURB);
+  snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB);
+  printf("/%-22.5s/\n", BLURB);
+  printf("%s\n", holder);
+
+/* see some flags */
+
+  printf("%%x %%X %%#x, 31, 31, 31\n");
+  snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31);
+  printf("%x %X %#x\n", 31, 31, 31);
+  printf("%s\n", holder);
+
+  printf("**%%d**%% d**%% d**, 42, 42, -42\n");
+  snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42);
+  printf("**%d**% d**% d**\n", 42, 42, -42);
+  printf("%s\n", holder);
+
+/* other flags */
+
+  printf("/%%g/, 31.4\n");
+  snprintf(holder, sizeof holder, "/%g/\n", 31.4);
+  printf("/%g/\n", 31.4);
+  printf("%s\n", holder);
+
+  printf("/%%.6g/, 31.4\n");
+  snprintf(holder, sizeof holder, "/%.6g/\n", 31.4);
+  printf("/%.6g/\n", 31.4);
+  printf("%s\n", holder);
+
+  printf("/%%.1G/, 31.4\n");
+  snprintf(holder, sizeof holder, "/%.1G/\n", 31.4);
+  printf("/%.1G/\n", 31.4);
+  printf("%s\n", holder);
+
+  printf("abc%%n\n");
+  printf("abc%n", &i); printf("%d\n", i);
+  snprintf(holder, sizeof holder, "abc%n", &i);
+  printf("%s", holder); printf("%d\n\n", i);
+  
+  printf("%%*.*s --> 10.10\n");
+  snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB);
+  printf("%*.*s\n", 10, 10, BLURB);
+  printf("%s\n", holder);
+
+  printf("%%%%%%%%\n");
+  snprintf(holder, sizeof holder, "%%%%\n");
+  printf("%%%%\n");
+  printf("%s\n", holder);
+
+#define BIG "Hello this is a too big string for the buffer"
+/*  printf("A buffer to small of 10, trying to put this:\n");*/
+  printf("<%%>, %s\n", BIG); 
+  i = snprintf(holder, 10, "%s\n", BIG);
+  printf("<%s>\n", BIG);
+  printf("<%s>\n", holder);
+}
+#endif
diff --git a/neon/lib/snprintf.h b/neon/lib/snprintf.h
new file mode 100644 (file)
index 0000000..511deca
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ Unix snprintf implementation.
+ Version 1.1
+   
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   
+   Revision History:
+
+   sitecopy changes:
+       added sys/types.h include to pick up size_t define.
+       renamed dtoa -> doubletoa to avoid dtoa conflict with cygwin.
+
+   1.1:
+      *  added changes from Miles Bader
+      *  corrected a bug with %f
+      *  added support for %#g
+      *  added more comments :-)
+   1.0:
+      *  supporting must ANSI syntaxic_sugars(see below)
+   0.0:
+      *  suppot %s %c %d
+
+    it understands:
+      Integer:
+        %lu %lu %u
+        %hd %ld %d     decimal
+        %ho %lo %o     octal
+        %hx %lx %x %X  hexa
+      Floating points:
+        %g %G %e %E %f  double
+      Strings:
+        %s %c  string
+        %%   %
+
+    Formating conversion flags:
+      - justify left
+      + Justify right or put a plus if number
+      # prefix 0x, 0X for hexa and 0 for octal
+      * precision/witdth is specify as an (int) in the arguments
+    ' ' leave a blank for number with no sign
+      l the later should be a long
+      h the later should be a short
+
+format:
+  snprintf(holder, sizeof_holder, format, ...)
+
+Return values:
+  (sizeof_holder - 1)
+
+
+ THANKS(for the patches and ideas):
+     Miles Bader
+     Cyrille Rustom
+     Jacek Slabocewiz
+     Mike Parker(mouse)
+
+Alain Magloire: alainm@rcsm.ee.mcgill.ca
+*/
+
+#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
+/**** changed for sitecopy ****/
+#include <sys/types.h>
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/* 
+ * For the FLOATING POINT FORMAT :
+ *  the challenge was finding a way to
+ *  manipulate the Real numbers without having
+ *  to resort to mathematical function(it
+ *  would require to link with -lm) and not
+ *  going down to the bit pattern(not portable)
+ *
+ *  so a number, a real is:
+
+      real = integral + fraction
+
+      integral = ... + a(2)*10^2 + a(1)*10^1 + a(0)*10^0
+      fraction = b(1)*10^-1 + b(2)*10^-2 + ...
+
+      where:
+       0 <= a(i) => 9 
+       0 <= b(i) => 9 
+    from then it was simple math
+ */
+
+/*
+ * size of the buffer for the integral part
+ * and the fraction part 
+ */
+#define MAX_INT  99 + 1 /* 1 for the null */
+#define MAX_FRACT 29 + 1
+
+/* 
+ * numtoa() uses PRIVATE buffers to store the results,
+ * So this function is not reentrant
+ */
+#define itoa(n) numtoa(n, 10, 0, (char **)0) 
+#define otoa(n) numtoa(n, 8, 0, (char **)0)
+#define htoa(n) numtoa(n, 16, 0, (char **)0)
+#define doubletoa(n, p, f) numtoa(n, 10, p, f)
+
+#define SWAP_INT(a,b) {int t; t = (a); (a) = (b); (b) = t;}
+
+/* this struct holds everything we need */
+struct DATA {
+  int length;
+  char *holder;
+  int counter;
+#ifdef __STDC__
+  const char *pf;
+#else
+  char *pf;
+#endif
+/* FLAGS */
+  int width, precision;
+  int justify; char pad;
+  int square, space, star_w, star_p, a_long ;
+};
+
+#define PRIVATE static
+#define PUBLIC extern
+/* signature of the functions */
+#ifdef __STDC__
+/* the floating point stuff */
+  PRIVATE double pow_10(int);
+  PRIVATE int log_10(double);
+  PRIVATE double integral(double, double *);
+  PRIVATE char * numtoa(double, int, int, char **);
+
+/* for the format */
+  PRIVATE void conv_flag(char *, struct DATA *);
+  PRIVATE void floating(struct DATA *, double);
+  PRIVATE void exponent(struct DATA *, double);
+  PRIVATE void decimal(struct DATA *, double);
+  PRIVATE void octal(struct DATA *, double);
+  PRIVATE void hexa(struct DATA *, double);
+  PRIVATE void strings(struct DATA *, char *);
+
+#else
+/* the floating point stuff */
+  PRIVATE double pow_10();
+  PRIVATE int log_10();
+  PRIVATE double integral();
+  PRIVATE char * numtoa();
+
+/* for the format */
+  PRIVATE void conv_flag();
+  PRIVATE void floating();
+  PRIVATE void exponent();
+  PRIVATE void decimal();
+  PRIVATE void octal();
+  PRIVATE void hexa();
+  PRIVATE void strings();
+#endif
+
+/* those are defines specific to snprintf to hopefully
+ * make the code clearer :-)
+ */
+#define RIGHT 1
+#define LEFT  0
+#define NOT_FOUND -1
+#define FOUND 1
+#define MAX_FIELD 15
+
+/* the conversion flags */
+#define isflag(c) ((c) == '#' || (c) == ' ' || \
+                   (c) == '*' || (c) == '+' || \
+                   (c) == '-' || (c) == '.' || \
+                   isdigit(c))
+
+/* round off to the precision */
+#define ROUND(d, p) \
+            (d < 0.) ? \
+             d - pow_10(-(p)->precision) * 0.5 : \
+             d + pow_10(-(p)->precision) * 0.5
+
+/* set default precision */
+#define DEF_PREC(p) \
+            if ((p)->precision == NOT_FOUND) \
+              (p)->precision = 6
+
+/* put a char */
+#define PUT_CHAR(c, p) \
+            if ((p)->counter < (p)->length) { \
+              *(p)->holder++ = (c); \
+              (p)->counter++; \
+            }
+
+#define PUT_PLUS(d, p) \
+            if ((d) > 0. && (p)->justify == RIGHT) \
+              PUT_CHAR('+', p)
+
+#define PUT_SPACE(d, p) \
+            if ((p)->space == FOUND && (d) > 0.) \
+              PUT_CHAR(' ', p)
+
+/* pad right */ 
+#define PAD_RIGHT(p) \
+            if ((p)->width > 0 && (p)->justify != LEFT) \
+              for (; (p)->width > 0; (p)->width--) \
+                 PUT_CHAR((p)->pad, p)
+
+/* pad left */
+#define PAD_LEFT(p) \
+            if ((p)->width > 0 && (p)->justify == LEFT) \
+              for (; (p)->width > 0; (p)->width--) \
+                 PUT_CHAR((p)->pad, p)
+
+/* if width and prec. in the args */
+#define STAR_ARGS(p) \
+            if ((p)->star_w == FOUND) \
+              (p)->width = va_arg(args, int); \
+            if ((p)->star_p == FOUND) \
+              (p)->precision = va_arg(args, int)
+
+
+PUBLIC int
+#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
+snprintf(char *string, size_t length, const char * format, ...);
+#else
+snprintf(string, length, format, va_alist);
+char *string;
+size_t length;
+char * format;
+va_dcl
+#endif
+
+
+
diff --git a/neon/lib/strcasecmp.c b/neon/lib/strcasecmp.c
new file mode 100644 (file)
index 0000000..366bcf2
--- /dev/null
@@ -0,0 +1,49 @@
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+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 version 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 this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <ctype.h>
+
+/* Compare S1 and S2, ignoring case, returning less than, equal to or
+   greater than zero if S1 is lexiographically less than,
+   equal to or greater than S2.  */
+int
+strcasecmp (s1, s2)
+     const char *s1;
+     const char *s2;
+{
+  register const unsigned char *p1 = (const unsigned char *) s1;
+  register const unsigned char *p2 = (const unsigned char *) s2;
+  unsigned char c1, c2;
+
+  if (p1 == p2)
+    return 0;
+
+  do
+    {
+      c1 = tolower (*p1++);
+      c2 = tolower (*p2++);
+      if (c1 == '\0')
+       break;
+    }
+  while (c1 == c2);
+
+  return c1 - c2;
+}
diff --git a/neon/lib/yesno.c b/neon/lib/yesno.c
new file mode 100644 (file)
index 0000000..8aaaf3d
--- /dev/null
@@ -0,0 +1,52 @@
+/* yesno.c -- read a yes/no response from stdin
+   Copyright (C) 1990, 1998 Free Software Foundation, Inc.
+
+   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 version 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 this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+#if HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#include <stdio.h>
+
+/* Read one line from standard input
+   and return nonzero if that line begins with y or Y,
+   otherwise return 0. */
+
+int rpmatch ();
+
+int
+yesno ()
+{
+  /* We make some assumptions here:
+     a) leading white space in the response are not vital
+     b) the first 128 characters of the answer are enough (the rest can
+       be ignored)
+     I cannot think for a situation where this is not ok.  --drepper@gnu  */
+  char buf[128];
+  int len = 0;
+  int c;
+
+  while ((c = getchar ()) != EOF && c != '\n')
+    if ((len > 0 && len < 127) || (len == 0 && !isspace (c)))
+      buf[len++] = c;
+  buf[len] = '\0';
+
+  return rpmatch (buf) == 1;
+}
diff --git a/neon/ltconfig b/neon/ltconfig
new file mode 100755 (executable)
index 0000000..e3c5a95
--- /dev/null
@@ -0,0 +1,2908 @@
+#! /bin/sh
+
+# ltconfig - Create a system-specific libtool.
+# Copyright (C) 1996-1999 Free Software Foundation, Inc.
+# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# This file 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 version 2 of the License, 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# A lot of this script is taken from autoconf-2.10.
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+echo=echo
+if test "X$1" = X--no-reexec; then
+  # Discard the --no-reexec flag, and continue.
+  shift
+elif test "X$1" = X--fallback-echo; then
+  # Avoid inline document here, it may be left over
+  :
+elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then
+  # Yippee, $echo works!
+  :
+else
+  # Restart under the correct shell.
+  exec "$SHELL" "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+  # used as fallback echo
+  shift
+  cat <<EOF
+$*
+EOF
+  exit 0
+fi
+
+# Find the correct PATH separator.  Usually this is `:', but
+# DJGPP uses `;' like DOS.
+if test "X${PATH_SEPARATOR+set}" != "Xset"; then
+  UNAME=${UNAME-`uname 2>/dev/null`}
+  case X$UNAME in
+    *-DOS) PATH_SEPARATOR=';' ;;
+    *)     PATH_SEPARATOR=':' ;;
+  esac
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "${CDPATH+set}" = set; then CDPATH=; export CDPATH; fi
+
+if test "X${echo_test_string+set}" != "Xset"; then
+  # find a string as large as possible, as long as the shell can cope with it
+  for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do
+    # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+    if (echo_test_string="`eval $cmd`") 2>/dev/null &&
+       echo_test_string="`eval $cmd`" &&
+       (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null; then
+      break
+    fi
+  done
+fi
+
+if test "X`($echo '\t') 2>/dev/null`" != 'X\t' ||
+   test "X`($echo "$echo_test_string") 2>/dev/null`" != X"$echo_test_string"; then
+  # The Solaris, AIX, and Digital Unix default echo programs unquote
+  # backslashes.  This makes it impossible to quote backslashes using
+  #   echo "$something" | sed 's/\\/\\\\/g'
+  #
+  # So, first we look for a working echo in the user's PATH.
+
+  IFS="${IFS=  }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+  for dir in $PATH /usr/ucb; do
+    if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+       test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+       test "X`($dir/echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then
+      echo="$dir/echo"
+      break
+    fi
+  done
+  IFS="$save_ifs"
+
+  if test "X$echo" = Xecho; then
+    # We didn't find a better echo, so look for alternatives.
+    if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' &&
+       test "X`(print -r "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then
+      # This shell has a builtin print -r that does the trick.
+      echo='print -r'
+    elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) &&
+        test "X$CONFIG_SHELL" != X/bin/ksh; then
+      # If we have ksh, try running ltconfig again with it.
+      ORIGINAL_CONFIG_SHELL="${CONFIG_SHELL-/bin/sh}"
+      export ORIGINAL_CONFIG_SHELL
+      CONFIG_SHELL=/bin/ksh
+      export CONFIG_SHELL
+      exec "$CONFIG_SHELL" "$0" --no-reexec ${1+"$@"}
+    else
+      # Try using printf.
+      echo='printf "%s\n"'
+      if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
+        test "X`($echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then
+       # Cool, printf works
+       :
+      elif test "X`("$ORIGINAL_CONFIG_SHELL" "$0" --fallback-echo '\t') 2>/dev/null`" = 'X\t' &&
+          test "X`("$ORIGINAL_CONFIG_SHELL" "$0" --fallback-echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then
+       CONFIG_SHELL="$ORIGINAL_CONFIG_SHELL"
+       export CONFIG_SHELL
+       SHELL="$CONFIG_SHELL"
+       export SHELL
+       echo="$CONFIG_SHELL $0 --fallback-echo"
+      elif test "X`("$CONFIG_SHELL" "$0" --fallback-echo '\t') 2>/dev/null`" = 'X\t' &&
+          test "X`("$CONFIG_SHELL" "$0" --fallback-echo "$echo_test_string") 2>/dev/null`" = X"$echo_test_string"; then
+       echo="$CONFIG_SHELL $0 --fallback-echo"
+      else
+       # maybe with a smaller string...
+       prev=:
+
+       for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do
+         if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null; then
+           break
+         fi
+         prev="$cmd"
+       done
+
+       if test "$prev" != 'sed 50q "$0"'; then
+         echo_test_string=`eval $prev`
+         export echo_test_string
+         exec "${ORIGINAL_CONFIG_SHELL}" "$0" ${1+"$@"}
+       else
+         # Oops.  We lost completely, so just stick with echo.
+         echo=echo
+       fi
+      fi
+    fi
+  fi
+fi
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e s/^X//'
+sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# The name of this program.
+progname=`$echo "X$0" | $Xsed -e 's%^.*/%%'`
+
+# Constants:
+PROGRAM=ltconfig
+PACKAGE=libtool
+VERSION=1.3
+TIMESTAMP=" (1.385.2.117 1999/04/29 13:07:13)"
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.c 1>&5'
+ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.c $LIBS 1>&5'
+rm="rm -f"
+
+help="Try \`$progname --help' for more information."
+
+# Global variables:
+default_ofile=libtool
+can_build_shared=yes
+enable_shared=yes
+# All known linkers require a `.a' archive for static linking.
+enable_static=yes
+enable_fast_install=yes
+enable_dlopen=unknown
+enable_win32_dll=no
+ltmain=
+silent=
+srcdir=
+ac_config_guess=
+ac_config_sub=
+host=
+nonopt=
+ofile="$default_ofile"
+verify_host=yes
+with_gcc=no
+with_gnu_ld=no
+need_locks=yes
+ac_ext=c
+objext=o
+libext=a
+cache_file=
+
+old_AR="$AR"
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+old_CPPFLAGS="$CPPFLAGS"
+old_LDFLAGS="$LDFLAGS"
+old_LD="$LD"
+old_LN_S="$LN_S"
+old_LIBS="$LIBS"
+old_NM="$NM"
+old_RANLIB="$RANLIB"
+old_DLLTOOL="$DLLTOOL"
+old_OBJDUMP="$OBJDUMP"
+old_AS="$AS"
+
+# Parse the command line options.
+args=
+prev=
+for option
+do
+  case "$option" in
+  -*=*) optarg=`echo "$option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) optarg= ;;
+  esac
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$prev"; then
+    eval "$prev=\$option"
+    prev=
+    continue
+  fi
+
+  case "$option" in
+  --help) cat <<EOM
+Usage: $progname [OPTION]... [HOST [LTMAIN]]
+
+Generate a system-specific libtool script.
+
+    --debug                enable verbose shell tracing
+    --disable-shared       do not build shared libraries
+    --disable-static       do not build static libraries
+    --disable-fast-install do not optimize for fast installation
+    --enable-dlopen        enable dlopen support
+    --enable-win32-dll     enable building dlls on win32 hosts
+    --help                 display this help and exit
+    --no-verify            do not verify that HOST is a valid host type
+-o, --output=FILE          specify the output file [default=$default_ofile]
+    --quiet                same as \`--silent'
+    --silent               do not print informational messages
+    --srcdir=DIR           find \`config.guess' in DIR
+    --version              output version information and exit
+    --with-gcc             assume that the GNU C compiler will be used
+    --with-gnu-ld          assume that the C compiler uses the GNU linker
+    --disable-lock         disable file locking
+    --cache-file=FILE      configure cache file
+
+LTMAIN is the \`ltmain.sh' shell script fragment or \`ltmain.c' program
+that provides basic libtool functionality.
+
+HOST is the canonical host system name [default=guessed].
+EOM
+  exit 0
+  ;;
+
+  --debug)
+    echo "$progname: enabling shell trace mode"
+    set -x
+    ;;
+
+  --disable-shared) enable_shared=no ;;
+
+  --disable-static) enable_static=no ;;
+
+  --disable-fast-install) enable_fast_install=no ;;
+
+  --enable-dlopen) enable_dlopen=yes ;;
+
+  --enable-win32-dll) enable_win32_dll=yes ;;
+
+  --quiet | --silent) silent=yes ;;
+
+  --srcdir) prev=srcdir ;;
+  --srcdir=*) srcdir="$optarg" ;;
+
+  --no-verify) verify_host=no ;;
+
+  --output | -o) prev=ofile ;;
+  --output=*) ofile="$optarg" ;;
+
+  --version) echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP"; exit 0 ;;
+
+  --with-gcc) with_gcc=yes ;;
+  --with-gnu-ld) with_gnu_ld=yes ;;
+
+  --disable-lock) need_locks=no ;;
+
+  --cache-file=*) cache_file="$optarg" ;;
+
+  -*)
+    echo "$progname: unrecognized option \`$option'" 1>&2
+    echo "$help" 1>&2
+    exit 1
+    ;;
+
+  *)
+    if test -z "$ltmain"; then
+      ltmain="$option"
+    elif test -z "$host"; then
+# This generates an unnecessary warning for sparc-sun-solaris4.1.3_U1
+#      if test -n "`echo $option| sed 's/[-a-z0-9.]//g'`"; then
+#        echo "$progname: warning \`$option' is not a valid host type" 1>&2
+#      fi
+      host="$option"
+    else
+      echo "$progname: too many arguments" 1>&2
+      echo "$help" 1>&2
+      exit 1
+    fi ;;
+  esac
+done
+
+if test -z "$ltmain"; then
+  echo "$progname: you must specify a LTMAIN file" 1>&2
+  echo "$help" 1>&2
+  exit 1
+fi
+
+if test ! -f "$ltmain"; then
+  echo "$progname: \`$ltmain' does not exist" 1>&2
+  echo "$help" 1>&2
+  exit 1
+fi
+
+# Quote any args containing shell metacharacters.
+ltconfig_args=
+for arg
+do
+  case "$arg" in
+  *" "*|*"     "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ltconfig_args="$ltconfig_args '$arg'" ;;
+  *) ltconfig_args="$ltconfig_args $arg" ;;
+  esac
+done
+
+# A relevant subset of AC_INIT.
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 5 compiler messages saved in config.log
+# 6 checking for... messages and results
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>>./config.log
+
+# NLS nuisances.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+
+if test -n "$cache_file" && test -r "$cache_file"; then
+  echo "loading cache $cache_file within ltconfig"
+  . $cache_file
+fi
+
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='       '
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+if test -z "$srcdir"; then
+  # Assume the source directory is the same one as the path to LTMAIN.
+  srcdir=`$echo "X$ltmain" | $Xsed -e 's%/[^/]*$%%'`
+  test "$srcdir" = "$ltmain" && srcdir=.
+fi
+
+trap "$rm conftest*; exit 1" 1 2 15
+if test "$verify_host" = yes; then
+  # Check for config.guess and config.sub.
+  ac_aux_dir=
+  for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+    if test -f $ac_dir/config.guess; then
+      ac_aux_dir=$ac_dir
+      break
+    fi
+  done
+  if test -z "$ac_aux_dir"; then
+    echo "$progname: cannot find config.guess in $srcdir $srcdir/.. $srcdir/../.." 1>&2
+    echo "$help" 1>&2
+    exit 1
+  fi
+  ac_config_guess=$ac_aux_dir/config.guess
+  ac_config_sub=$ac_aux_dir/config.sub
+
+  # Make sure we can run config.sub.
+  if $SHELL $ac_config_sub sun4 >/dev/null 2>&1; then :
+  else
+    echo "$progname: cannot run $ac_config_sub" 1>&2
+    echo "$help" 1>&2
+    exit 1
+  fi
+
+  echo $ac_n "checking host system type""... $ac_c" 1>&6
+
+  host_alias=$host
+  case "$host_alias" in
+  "")
+    if host_alias=`$SHELL $ac_config_guess`; then :
+    else
+      echo "$progname: cannot guess host type; you must specify one" 1>&2
+      echo "$help" 1>&2
+      exit 1
+    fi ;;
+  esac
+  host=`$SHELL $ac_config_sub $host_alias`
+  echo "$ac_t$host" 1>&6
+
+  # Make sure the host verified.
+  test -z "$host" && exit 1
+
+elif test -z "$host"; then
+  echo "$progname: you must specify a host type if you use \`--no-verify'" 1>&2
+  echo "$help" 1>&2
+  exit 1
+else
+  host_alias=$host
+fi
+
+# Transform linux* to *-*-linux-gnu*, to support old configure scripts.
+case "$host_os" in
+linux-gnu*) ;;
+linux*) host=`echo $host | sed 's/^\(.*-.*-linux\)\(.*\)$/\1-gnu\2/'`
+esac
+
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+case "$host_os" in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "${COLLECT_NAMES+set}" != set; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR cru $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+# Set a sane default for `AR'.
+test -z "$AR" && AR=ar
+
+# Set a sane default for `OBJDUMP'.
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+# If RANLIB is not set, then run the test.
+if test "${RANLIB+set}" != "set"; then
+  result=no
+
+  echo $ac_n "checking for ranlib... $ac_c" 1>&6
+  IFS="${IFS=  }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+  for dir in $PATH; do
+    test -z "$dir" && dir=.
+    if test -f $dir/ranlib || test -f $dir/ranlib$ac_exeext; then
+      RANLIB="ranlib"
+      result="ranlib"
+      break
+    fi
+  done
+  IFS="$save_ifs"
+
+  echo "$ac_t$result" 1>&6
+fi
+
+if test -n "$RANLIB"; then
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+  old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds"
+fi
+
+# Set sane defaults for `DLLTOOL', `OBJDUMP', and `AS', used on cygwin.
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+test -z "$OBJDUMP" && OBJDUMP=objdump
+test -z "$AS" && AS=as
+
+# Check to see if we are using GCC.
+if test "$with_gcc" != yes || test -z "$CC"; then
+  # If CC is not set, then try to find GCC or a usable CC.
+  if test -z "$CC"; then
+    echo $ac_n "checking for gcc... $ac_c" 1>&6
+    IFS="${IFS=        }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+    for dir in $PATH; do
+      test -z "$dir" && dir=.
+      if test -f $dir/gcc || test -f $dir/gcc$ac_exeext; then
+       CC="gcc"
+       break
+      fi
+    done
+    IFS="$save_ifs"
+
+    if test -n "$CC"; then
+      echo "$ac_t$CC" 1>&6
+    else
+      echo "$ac_t"no 1>&6
+    fi
+  fi
+
+  # Not "gcc", so try "cc", rejecting "/usr/ucb/cc".
+  if test -z "$CC"; then
+    echo $ac_n "checking for cc... $ac_c" 1>&6
+    IFS="${IFS=        }"; save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+    cc_rejected=no
+    for dir in $PATH; do
+      test -z "$dir" && dir=.
+      if test -f $dir/cc || test -f $dir/cc$ac_exeext; then
+       if test "$dir/cc" = "/usr/ucb/cc"; then
+         cc_rejected=yes
+         continue
+       fi
+       CC="cc"
+       break
+      fi
+    done
+    IFS="$save_ifs"
+    if test $cc_rejected = yes; then
+      # We found a bogon in the path, so make sure we never use it.
+      set dummy $CC
+      shift
+      if test $# -gt 0; then
+       # We chose a different compiler from the bogus one.
+       # However, it has the same name, so the bogon will be chosen
+       # first if we set CC to just the name; use the full file name.
+       shift
+       set dummy "$dir/cc" "$@"
+       shift
+       CC="$@"
+      fi
+    fi
+
+    if test -n "$CC"; then
+      echo "$ac_t$CC" 1>&6
+    else
+      echo "$ac_t"no 1>&6
+    fi
+
+    if test -z "$CC"; then
+      echo "$progname: error: no acceptable cc found in \$PATH" 1>&2
+      exit 1
+    fi
+  fi
+
+  # Now see if the compiler is really GCC.
+  with_gcc=no
+  echo $ac_n "checking whether we are using GNU C... $ac_c" 1>&6
+  echo "$progname:579: checking whether we are using GNU C" >&5
+
+  $rm conftest.c
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+  if { ac_try='${CC-cc} -E conftest.c'; { (eval echo $progname:587: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+    with_gcc=yes
+  fi
+  $rm conftest.c
+  echo "$ac_t$with_gcc" 1>&6
+fi
+
+# Allow CC to be a program name with arguments.
+set dummy $CC
+compiler="$2"
+
+echo $ac_n "checking for object suffix... $ac_c" 1>&6
+$rm conftest*
+echo 'int i = 1;' > conftest.c
+echo "$progname:601: checking for object suffix" >& 5
+if { (eval echo $progname:602: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; }; then
+  # Append any warnings to the config.log.
+  cat conftest.err 1>&5
+
+  for ac_file in conftest.*; do
+    case $ac_file in
+    *.c) ;;
+    *) objext=`echo $ac_file | sed -e s/conftest.//` ;;
+    esac
+  done
+else
+  cat conftest.err 1>&5
+  echo "$progname: failed program was:" >&5
+  cat conftest.c >&5
+fi
+$rm conftest*
+echo "$ac_t$objext" 1>&6
+
+echo $ac_n "checking for $compiler option to produce PIC... $ac_c" 1>&6
+pic_flag=
+special_shlib_compile_flags=
+wl=
+link_static_flag=
+no_builtin_flag=
+
+if test "$with_gcc" = yes; then
+  wl='-Wl,'
+  link_static_flag='-static'
+
+  case "$host_os" in
+  beos* | irix5* | irix6* | osf3* | osf4*)
+    # PIC is the default for these OSes.
+    ;;
+  aix*)
+    # Below there is a dirty hack to force normal static linking with -ldl
+    # The problem is because libdl dynamically linked with both libc and
+    # libC (AIX C++ library), which obviously doesn't included in libraries
+    # list by gcc. This cause undefined symbols with -static flags.
+    # This hack allows C programs to be linked with "-static -ldl", but
+    # we not sure about C++ programs.
+    link_static_flag="$link_static_flag ${wl}-lC"
+    ;;
+  cygwin* | mingw* | os2*)
+    # We can build DLLs from non-PIC.
+    ;;
+  amigaos*)
+    # FIXME: we need at least 68020 code to build shared libraries, but
+    # adding the `-m68020' flag to GCC prevents building anything better,
+    # like `-m68040'.
+    pic_flag='-m68020 -resident32 -malways-restore-a4'
+    ;;
+  *)
+    pic_flag='-fPIC'
+    ;;
+  esac
+else
+  # PORTME Check for PIC flags for the system compiler.
+  case "$host_os" in
+  aix3* | aix4*)
+    # All AIX code is PIC.
+    link_static_flag='-bnso -bI:/lib/syscalls.exp'
+    ;;
+
+  hpux9* | hpux10* | hpux11*)
+    # Is there a better link_static_flag that works with the bundled CC?
+    wl='-Wl,'
+    link_static_flag="${wl}-a ${wl}archive"
+    pic_flag='+Z'
+    ;;
+
+  irix5* | irix6*)
+    wl='-Wl,'
+    link_static_flag='-non_shared'
+    # PIC (with -KPIC) is the default.
+    ;;
+
+  cygwin* | mingw* | os2*)
+    # We can build DLLs from non-PIC.
+    ;;
+
+  osf3* | osf4*)
+    # All OSF/1 code is PIC.
+    wl='-Wl,'
+    link_static_flag='-non_shared'
+    ;;
+
+  sco3.2v5*)
+    pic_flag='-Kpic'
+    link_static_flag='-dn'
+    special_shlib_compile_flags='-belf'
+    ;;
+
+  solaris*)
+    pic_flag='-KPIC'
+    link_static_flag='-Bstatic'
+    wl='-Wl,'
+    ;;
+
+  sunos4*)
+    pic_flag='-PIC'
+    link_static_flag='-Bstatic'
+    wl='-Qoption ld '
+    ;;
+
+  sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+    pic_flag='-KPIC'
+    link_static_flag='-Bstatic'
+    wl='-Wl,'
+    ;;
+
+  uts4*)
+    pic_flag='-pic'
+    link_static_flag='-Bstatic'
+    ;;
+
+  *)
+    can_build_shared=no
+    ;;
+  esac
+fi
+
+if test -n "$pic_flag"; then
+  echo "$ac_t$pic_flag" 1>&6
+
+  # Check to make sure the pic_flag actually works.
+  echo $ac_n "checking if $compiler PIC flag $pic_flag works... $ac_c" 1>&6
+  $rm conftest*
+  echo "int some_variable = 0;" > conftest.c
+  save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS $pic_flag -DPIC"
+  echo "$progname:732: checking if $compiler PIC flag $pic_flag works" >&5
+  if { (eval echo $progname:733: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; } && test -s conftest.$objext; then
+    # Append any warnings to the config.log.
+    cat conftest.err 1>&5
+    
+    case "$host_os" in
+    hpux9* | hpux10* | hpux11*)
+      # On HP-UX, both CC and GCC only warn that PIC is supported... then they
+      # create non-PIC objects.  So, if there were any warnings, we assume that
+      # PIC is not supported.
+      if test -s conftest.err; then
+       echo "$ac_t"no 1>&6
+       can_build_shared=no
+       pic_flag=
+      else
+       echo "$ac_t"yes 1>&6
+       pic_flag=" $pic_flag"
+      fi
+      ;;
+    *)
+      echo "$ac_t"yes 1>&6
+      pic_flag=" $pic_flag"
+      ;;
+    esac
+  else
+    # Append any errors to the config.log.
+    cat conftest.err 1>&5
+    can_build_shared=no
+    pic_flag=
+    echo "$ac_t"no 1>&6
+  fi
+  CFLAGS="$save_CFLAGS"
+  $rm conftest*
+else
+  echo "$ac_t"none 1>&6
+fi
+
+# Check to see if options -o and -c are simultaneously supported by compiler
+echo $ac_n "checking if $compiler supports -c -o file.o... $ac_c" 1>&6
+$rm -r conftest 2>/dev/null
+mkdir conftest
+cd conftest
+$rm conftest*
+echo "int some_variable = 0;" > conftest.c
+mkdir out
+# According to Tom Tromey, Ian Lance Taylor reported there are C compilers
+# that will create temporary files in the current directory regardless of
+# the output directory.  Thus, making CWD read-only will cause this test
+# to fail, enabling locking or at least warning the user not to do parallel
+# builds.
+chmod -w .
+save_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -o out/conftest2.o"
+echo "$progname:785: checking if $compiler supports -c -o file.o" >&5
+if { (eval echo $progname:786: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.o; then
+
+  # The compiler can only warn and ignore the option if not recognized
+  # So say no if there are warnings
+    if test -s out/conftest.err; then
+      echo "$ac_t"no 1>&6
+      compiler_c_o=no
+    else
+      echo "$ac_t"yes 1>&6
+      compiler_c_o=yes
+    fi
+else
+  # Append any errors to the config.log.
+  cat out/conftest.err 1>&5
+  compiler_c_o=no
+  echo "$ac_t"no 1>&6
+fi
+CFLAGS="$save_CFLAGS"
+chmod u+w .
+$rm conftest* out/*
+rmdir out
+cd ..
+rmdir conftest
+$rm -r conftest 2>/dev/null
+
+if test x"$compiler_c_o" = x"yes"; then
+  # Check to see if we can write to a .lo
+  echo $ac_n "checking if $compiler supports -c -o file.lo... $ac_c" 1>&6
+  $rm conftest*
+  echo "int some_variable = 0;" > conftest.c
+  save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -c -o conftest.lo"
+  echo "$progname:818: checking if $compiler supports -c -o file.lo" >&5
+if { (eval echo $progname:819: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; } && test -s conftest.lo; then
+
+    # The compiler can only warn and ignore the option if not recognized
+    # So say no if there are warnings
+      if test -s conftest.err; then
+       echo "$ac_t"no 1>&6
+       compiler_o_lo=no
+      else
+       echo "$ac_t"yes 1>&6
+       compiler_o_lo=yes
+      fi
+  else
+    # Append any errors to the config.log.
+    cat conftest.err 1>&5
+    compiler_o_lo=no
+    echo "$ac_t"no 1>&6
+  fi
+  CFLAGS="$save_CFLAGS"
+  $rm conftest*
+else
+  compiler_o_lo=no
+fi
+
+# Check to see if we can do hard links to lock some files if needed
+hard_links="nottested"
+if test "$compiler_c_o" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  echo $ac_n "checking if we can lock with hard links... $ac_c" 1>&6
+  hard_links=yes
+  $rm conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  echo "$ac_t$hard_links" 1>&6
+  $rm conftest*
+  if test "$hard_links" = no; then
+    echo "*** WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+if test "$with_gcc" = yes; then
+  # Check to see if options -fno-rtti -fno-exceptions are supported by compiler
+  echo $ac_n "checking if $compiler supports -fno-rtti -fno-exceptions ... $ac_c" 1>&6
+  $rm conftest*
+  echo "int some_variable = 0;" > conftest.c
+  save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -fno-rtti -fno-exceptions -c conftest.c"
+  echo "$progname:870: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+  if { (eval echo $progname:871: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; } && test -s conftest.o; then
+
+    # The compiler can only warn and ignore the option if not recognized
+    # So say no if there are warnings
+      if test -s conftest.err; then
+       echo "$ac_t"no 1>&6
+       compiler_rtti_exceptions=no
+      else
+       echo "$ac_t"yes 1>&6
+       compiler_rtti_exceptions=yes
+      fi
+  else
+    # Append any errors to the config.log.
+    cat conftest.err 1>&5
+    compiler_rtti_exceptions=no
+    echo "$ac_t"no 1>&6
+  fi
+  CFLAGS="$save_CFLAGS"
+  $rm conftest*
+
+  if test "$compiler_rtti_exceptions" = "yes"; then
+    no_builtin_flag=' -fno-builtin -fno-rtti -fno-exceptions'
+  else
+    no_builtin_flag=' -fno-builtin'
+  fi
+  
+fi
+
+# Check for any special shared library compilation flags.
+if test -n "$special_shlib_compile_flags"; then
+  echo "$progname: warning: \`$CC' requires \`$special_shlib_compile_flags' to build shared libraries" 1>&2
+  if echo "$old_CC $old_CFLAGS " | egrep -e "[         ]$special_shlib_compile_flags[  ]" >/dev/null; then :
+  else
+    echo "$progname: add \`$special_shlib_compile_flags' to the CC or CFLAGS env variable and reconfigure" 1>&2
+    can_build_shared=no
+  fi
+fi
+
+echo $ac_n "checking if $compiler static flag $link_static_flag works... $ac_c" 1>&6
+$rm conftest*
+echo 'main(){return(0);}' > conftest.c
+save_LDFLAGS="$LDFLAGS"
+LDFLAGS="$LDFLAGS $link_static_flag"
+echo "$progname:914: checking if $compiler static flag $link_static_flag works" >&5
+if { (eval echo $progname:915: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+  echo "$ac_t$link_static_flag" 1>&6
+else
+  echo "$ac_t"none 1>&6
+  link_static_flag=
+fi
+LDFLAGS="$save_LDFLAGS"
+$rm conftest*
+
+if test -z "$LN_S"; then
+  # Check to see if we can use ln -s, or we need hard links.
+  echo $ac_n "checking whether ln -s works... $ac_c" 1>&6
+  $rm conftest.dat
+  if ln -s X conftest.dat 2>/dev/null; then
+    $rm conftest.dat
+    LN_S="ln -s"
+  else
+    LN_S=ln
+  fi
+  if test "$LN_S" = "ln -s"; then
+    echo "$ac_t"yes 1>&6
+  else
+    echo "$ac_t"no 1>&6
+  fi
+fi
+
+# Make sure LD is an absolute path.
+if test -z "$LD"; then
+  ac_prog=ld
+  if test "$with_gcc" = yes; then
+    # Check if gcc -print-prog-name=ld gives a path.
+    echo $ac_n "checking for ld used by GCC... $ac_c" 1>&6
+    echo "$progname:947: checking for ld used by GCC" >&5
+    ac_prog=`($CC -print-prog-name=ld) 2>&5`
+    case "$ac_prog" in
+    # Accept absolute paths.
+    [\\/]* | [A-Za-z]:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+      # Canonicalize the path of ld
+      ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+       ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+    "")
+      # If it fails, then pretend we are not using GCC.
+      ac_prog=ld
+      ;;
+    *)
+      # If it is relative, then search for the first ld in PATH.
+      with_gnu_ld=unknown
+      ;;
+    esac
+  elif test "$with_gnu_ld" = yes; then
+    echo $ac_n "checking for GNU ld... $ac_c" 1>&6
+    echo "$progname:971: checking for GNU ld" >&5
+  else
+    echo $ac_n "checking for non-GNU ld""... $ac_c" 1>&6
+    echo "$progname:974: checking for non-GNU ld" >&5
+  fi
+
+  if test -z "$LD"; then
+    IFS="${IFS=        }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+    for ac_dir in $PATH; do
+      test -z "$ac_dir" && ac_dir=.
+      if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+       LD="$ac_dir/$ac_prog"
+       # Check to see if the program is GNU ld.  I'd rather use --version,
+       # but apparently some GNU ld's only accept -v.
+       # Break only if it was the GNU/non-GNU ld that we prefer.
+       if "$LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then
+         test "$with_gnu_ld" != no && break
+       else
+         test "$with_gnu_ld" != yes && break
+       fi
+      fi
+    done
+    IFS="$ac_save_ifs"
+  fi
+
+  if test -n "$LD"; then
+    echo "$ac_t$LD" 1>&6
+  else
+    echo "$ac_t"no 1>&6
+  fi
+
+  if test -z "$LD"; then
+    echo "$progname: error: no acceptable ld found in \$PATH" 1>&2
+    exit 1
+  fi
+fi
+
+# Check to see if it really is or is not GNU ld.
+echo $ac_n "checking if the linker ($LD) is GNU ld... $ac_c" 1>&6
+# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
+  with_gnu_ld=yes
+else
+  with_gnu_ld=no
+fi
+echo "$ac_t$with_gnu_ld" 1>&6
+
+# See if the linker supports building shared libraries.
+echo $ac_n "checking whether the linker ($LD) supports shared libraries... $ac_c" 1>&6
+
+allow_undefined_flag=
+no_undefined_flag=
+need_lib_prefix=unknown
+need_version=unknown
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+archive_cmds=
+archive_expsym_cmds=
+old_archive_from_new_cmds=
+export_dynamic_flag_spec=
+whole_archive_flag_spec=
+thread_safe_flag_spec=
+hardcode_libdir_flag_spec=
+hardcode_libdir_separator=
+hardcode_direct=no
+hardcode_minus_L=no
+hardcode_shlibpath_var=unsupported
+runpath_var=
+always_export_symbols=no
+export_symbols_cmds='$NM $libobjs | $global_symbol_pipe | sed '\''s/.* //'\'' | sort | uniq > $export_symbols'
+# include_expsyms should be a list of space-separated symbols to be *always*
+# included in the symbol list
+include_expsyms=
+# exclude_expsyms can be an egrep regular expression of symbols to exclude
+# it will be wrapped by ` (' and `)$', so one must not match beginning or
+# end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+# as well as any symbol that contains `d'.
+exclude_expsyms="_GLOBAL_OFFSET_TABLE_"
+# Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+# platforms (ab)use it in PIC code, but their linkers get confused if
+# the symbol is explicitly referenced.  Since portable code cannot
+# rely on this symbol name, it's probably fine to never include it in
+# preloaded symbol tables.
+
+case "$host_os" in
+cygwin* | mingw*)
+  # FIXME: the MSVC++ port hasn't been tested in a loooong time
+  # When not using gcc, we currently assume that we are using
+  # Microsoft Visual C++.
+  if test "$with_gcc" != yes; then
+    with_gnu_ld=no
+  fi
+  ;;
+
+esac
+
+ld_shlibs=yes
+if test "$with_gnu_ld" = yes; then
+  # If archive_cmds runs LD, not CC, wlarc should be empty
+  wlarc='${wl}'
+
+  # See if GNU ld supports shared libraries.
+  case "$host_os" in
+  aix3* | aix4*)
+    # On AIX, the GNU linker is very broken
+    ld_shlibs=no
+    cat <<EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+EOF
+    ;;
+
+  amigaos*)
+    archive_cmds='$rm $objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $objdir/a2ixlibrary.data~$AR cru $lib $libobjs~$RANLIB $lib~(cd $objdir && a2ixlibrary -32)'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_minus_L=yes
+
+    # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports
+    # that the semantics of dynamic libraries on AmigaOS, at least up
+    # to version 4, is to share data among multiple programs linked
+    # with the same dynamic library.  Since this doesn't match the
+    # behavior of shared libraries on other platforms, we can use
+    # them.
+    ld_shlibs=no
+    ;;
+
+  beos*)
+    if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+      allow_undefined_flag=unsupported
+      # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+      # support --undefined.  This deserves some investigation.  FIXME
+      archive_cmds='$CC -nostart $libobjs $deplibs $linkopts ${wl}-soname $wl$soname -o $lib'
+    else
+      ld_shlibs=no
+    fi
+    ;;
+
+  cygwin* | mingw*)
+    # hardcode_libdir_flag_spec is actually meaningless, as there is
+    # no search path for DLLs.
+    hardcode_libdir_flag_spec='-L$libdir'
+    allow_undefined_flag=unsupported
+    always_export_symbols=yes
+
+    # Extract the symbol export list from an `--export-all' def file,
+    # then regenerate the def file from the symbol export list, so that
+    # the compiled dll only exports the symbol export list.
+    export_symbols_cmds='rm -f $objdir/$soname-ltdll.c~
+      sed -e "/^# \/\* ltdll\.c starts here \*\//,/^# \/\* ltdll.c ends here \*\// { s/^# //; p; }" -e d < $0 > $objdir/$soname-ltdll.c~
+      (cd $objdir && $CC -c $soname-ltdll.c)~
+      $DLLTOOL --export-all --exclude-symbols DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12 --output-def $objdir/$soname-def  $objdir/$soname-ltdll.$objext $libobjs~
+      sed -e "1,/EXPORTS/d" -e "s/ @ [0-9]* ; *//" < $objdir/$soname-def > $export_symbols'
+
+    archive_expsym_cmds='echo EXPORTS > $objdir/$soname-def~
+      _lt_hint=1;
+      for symbol in `cat $export_symbols`; do
+       echo "  \$symbol @ \$_lt_hint ; " >> $objdir/$soname-def;
+       _lt_hint=`expr 1 + \$_lt_hint`;
+      done~
+      $CC -Wl,--base-file,$objdir/$soname-base -Wl,--dll -nostartfiles -Wl,-e,__cygwin_dll_entry@12 -o $lib $objdir/$soname-ltdll.$objext $libobjs $deplibs $linkopts~
+      $DLLTOOL --as=$AS --dllname $soname --exclude-symbols DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12 --def $objdir/$soname-def --base-file $objdir/$soname-base --output-exp $objdir/$soname-exp~
+      $CC -Wl,--base-file,$objdir/$soname-base $objdir/$soname-exp -Wl,--dll -nostartfiles -Wl,-e,__cygwin_dll_entry@12 -o $lib $objdir/$soname-ltdll.$objext $libobjs $deplibs $linkopts~
+      $DLLTOOL --as=$AS --dllname $soname --exclude-symbols DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12 --def $objdir/$soname-def --base-file $objdir/$soname-base --output-exp $objdir/$soname-exp~
+      $CC $objdir/$soname-exp -Wl,--dll -nostartfiles -Wl,-e,__cygwin_dll_entry@12 -o $lib $objdir/$soname-ltdll.$objext $libobjs $deplibs $linkopts'
+
+      old_archive_from_new_cmds='$DLLTOOL --as=$AS --dllname $soname --def $objdir/$soname-def --output-lib $objdir/$libname.a'
+    ;;
+
+  netbsd*)
+    if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+      archive_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname -o $lib'
+      archive_expsym_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+    else
+      archive_cmds='$LD -Bshareable $libobjs $deplibs $linkopts -o $lib'
+      # can we support soname and/or expsyms with a.out? -oliva
+    fi
+    ;;
+
+  sunos4*)
+    archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linkopts'
+    wlarc=
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  *)
+    if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+      archive_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname -o $lib'
+      archive_expsym_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+    else
+      ld_shlibs=no
+    fi
+    ;;
+  esac
+
+  if test "$ld_shlibs" = yes; then
+    runpath_var=LD_RUN_PATH
+    hardcode_libdir_flag_spec='${wl}--rpath ${wl}$libdir'
+    export_dynamic_flag_spec='${wl}--export-dynamic'
+    whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+  fi
+else
+  # PORTME fill in a description of your system's linker (not GNU ld)
+  case "$host_os" in
+  aix3*)
+    allow_undefined_flag=unsupported
+    always_export_symbols=yes
+    archive_expsym_cmds='$LD -o $objdir/$soname $libobjs $deplibs $linkopts -bE:$export_symbols -T512 -H512 -bM:SRE~$AR cru $lib $objdir/$soname'
+    # Note: this linker hardcodes the directories in LIBPATH if there
+    # are no directories specified by -L.
+    hardcode_minus_L=yes
+    if test "$with_gcc" = yes && test -z "$link_static_flag"; then
+      # Neither direct hardcoding nor static linking is supported with a
+      # broken collect2.
+      hardcode_direct=unsupported
+    fi
+    ;;
+
+  aix4*)
+    hardcode_libdir_flag_spec='${wl}-b ${wl}nolibpath ${wl}-b ${wl}libpath:$libdir:/usr/lib:/lib'
+    hardcode_libdir_separator=':'
+    if test "$with_gcc" = yes; then
+      collect2name=`${CC} -print-prog-name=collect2`
+      if test -f "$collect2name" && \
+        strings "$collect2name" | grep resolve_lib_name >/dev/null
+      then
+       # We have reworked collect2
+       hardcode_direct=yes
+      else
+       # We have old collect2
+       hardcode_direct=unsupported
+       # It fails to find uninstalled libraries when the uninstalled
+       # path is not listed in the libpath.  Setting hardcode_minus_L
+       # to unsupported forces relinking
+       hardcode_minus_L=yes
+       hardcode_libdir_flag_spec='-L$libdir'
+       hardcode_libdir_separator=
+      fi
+      shared_flag='-shared'
+    else
+      shared_flag='${wl}-bM:SRE'
+      hardcode_direct=yes
+    fi
+    allow_undefined_flag=' ${wl}-berok'
+    archive_cmds="\$CC $shared_flag"' -o $objdir/$soname $libobjs $deplibs $linkopts ${wl}-bexpall ${wl}-bnoentry${allow_undefined_flag}'
+    archive_expsym_cmds="\$CC $shared_flag"' -o $objdir/$soname $libobjs $deplibs $linkopts ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}'
+    case "$host_os" in aix4.[01]|aix4.[01].*)
+      # According to Greg Wooledge, -bexpall is only supported from AIX 4.2 on
+      always_export_symbols=yes ;;
+    esac
+   ;;
+
+  amigaos*)
+    archive_cmds='$rm $objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $objdir/a2ixlibrary.data~$AR cru $lib $libobjs~$RANLIB $lib~(cd $objdir && a2ixlibrary -32)'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_minus_L=yes
+    # see comment about different semantics on the GNU ld section
+    ld_shlibs=no
+    ;;
+
+  cygwin* | mingw*)
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    # hardcode_libdir_flag_spec is actually meaningless, as there is
+    # no search path for DLLs.
+    hardcode_libdir_flag_spec=' '
+    allow_undefined_flag=unsupported
+    # Tell ltmain to make .lib files, not .a files.
+    libext=lib
+    # FIXME: Setting linknames here is a bad hack.
+    archive_cmds='$CC -o $lib $libobjs $linkopts `echo "$deplibs" | sed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+    # The linker will automatically build a .lib file if we build a DLL.
+    old_archive_from_new_cmds='true'
+    # FIXME: Should let the user specify the lib program.
+    old_archive_cmds='lib /OUT:$oldlib$oldobjs'
+    fix_srcfile_path='`cygpath -w $srcfile`'
+    ;;
+
+  freebsd1*)
+    ld_shlibs=no
+    ;;
+
+  # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+  # support.  Future versions do this automatically, but an explicit c++rt0.o
+  # does not break anything, and helps significantly (at the cost of a little
+  # extra space).
+  freebsd2.2*)
+    archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts /usr/lib/c++rt0.o'
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+  freebsd2*)
+    archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts'
+    hardcode_direct=yes
+    hardcode_minus_L=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+  freebsd*)
+    archive_cmds='$CC -shared -o $lib $libobjs $deplibs $linkopts'
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  hpux9* | hpux10* | hpux11*)
+    case "$host_os" in
+    hpux9*) archive_cmds='$rm $objdir/$soname~$LD -b +b $install_libdir -o $objdir/$soname $libobjs $deplibs $linkopts~test $objdir/$soname = $lib || mv $objdir/$soname $lib' ;;
+    *) archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linkopts' ;;
+    esac
+    hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+    hardcode_libdir_separator=:
+    hardcode_direct=yes
+    hardcode_minus_L=yes # Not in the search PATH, but as the default
+                        # location of the library.
+    export_dynamic_flag_spec='${wl}-E'
+    ;;
+
+  irix5* | irix6*)
+    if test "$with_gcc" = yes; then
+      archive_cmds='$CC -shared $libobjs $deplibs $linkopts ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
+    else
+      archive_cmds='$LD -shared $libobjs $deplibs $linkopts -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
+    fi
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    hardcode_libdir_separator=:
+    ;;
+
+  netbsd*)
+    if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts'  # a.out
+    else
+      archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linkopts'      # ELF
+    fi
+    hardcode_libdir_flag_spec='${wl}-R$libdir'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  openbsd*)
+    archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linkopts'
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  os2*)
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_minus_L=yes
+    allow_undefined_flag=unsupported
+    archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $objdir/$libname.def~$echo DATA >> $objdir/$libname.def~$echo " SINGLE NONSHARED" >> $objdir/$libname.def~$echo EXPORTS >> $objdir/$libname.def~emxexp $libobjs >> $objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $linkopts $objdir/$libname.def'
+    old_archive_from_new_cmds='emximp -o $objdir/$libname.a $objdir/$libname.def'
+    ;;
+
+  osf3* | osf4*)
+    if test "$with_gcc" = yes; then
+      allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+      archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $linkopts ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
+    else
+      allow_undefined_flag=' -expect_unresolved \*'
+      archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linkopts -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
+    fi
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    hardcode_libdir_separator=:
+    ;;
+
+  sco3.2v5*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts'
+    hardcode_shlibpath_var=no
+    runpath_var=LD_RUN_PATH
+    hardcode_runpath_var=yes
+    ;;
+
+  solaris*)
+    no_undefined_flag=' -z text'
+    # $CC -shared without GNU ld will not create a library from C++
+    # object files and a static libstdc++, better avoid it by now
+    archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linkopts'
+    archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+               $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linkopts~$rm $lib.exp'
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_shlibpath_var=no
+    case "$host_os" in
+    solaris2.[0-5] | solaris2.[0-5].*) ;;
+    *) # Supported since Solaris 2.6 (maybe 2.5.1?)
+      whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' ;;
+    esac
+    ;;
+
+  sunos4*)
+    archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linkopts'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_direct=yes
+    hardcode_minus_L=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  sysv4)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts'
+    runpath_var='LD_RUN_PATH'
+    hardcode_shlibpath_var=no
+    hardcode_direct=no #Motorola manual says yes, but my tests say they lie 
+    ;;  
+
+  sysv4.3*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts'
+    hardcode_shlibpath_var=no
+    export_dynamic_flag_spec='-Bexport'
+    ;;
+
+  uts4*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_shlibpath_var=no
+    ;;
+
+  dgux*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_shlibpath_var=no
+    ;;
+
+  *)
+    ld_shlibs=no
+    ;;
+  esac
+fi
+echo "$ac_t$ld_shlibs" 1>&6
+test "$ld_shlibs" = no && can_build_shared=no
+
+if test -z "$NM"; then
+  echo $ac_n "checking for BSD-compatible nm... $ac_c" 1>&6
+  case "$NM" in
+  [\\/]* | [A-Za-z]:[\\/]*) ;; # Let the user override the test with a path.
+  *)
+    IFS="${IFS=        }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR}"
+    for ac_dir in $PATH /usr/ucb /usr/ccs/bin /bin; do
+      test -z "$ac_dir" && ac_dir=.
+      if test -f $ac_dir/nm || test -f $ac_dir/nm$ac_exeext; then
+       # Check to see if the nm accepts a BSD-compat flag.
+       # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+       #   nm: unknown option "B" ignored
+       if ($ac_dir/nm -B /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then
+         NM="$ac_dir/nm -B"
+         break
+       elif ($ac_dir/nm -p /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then
+         NM="$ac_dir/nm -p"
+         break
+       else
+         NM=${NM="$ac_dir/nm"} # keep the first match, but
+         continue # so that we can try to find one that supports BSD flags
+       fi
+      fi
+    done
+    IFS="$ac_save_ifs"
+    test -z "$NM" && NM=nm
+    ;;
+  esac
+  echo "$ac_t$NM" 1>&6
+fi
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+echo $ac_n "checking command to parse $NM output... $ac_c" 1>&6
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Transform the above into a raw symbol and a C symbol.
+symxfrm='\1 \2\3 \3'
+
+# Transform an extracted symbol line into a proper C declaration
+global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern char \1;/p'"
+
+# Define system-specific variables.
+case "$host_os" in
+aix*)
+  symcode='[BCDT]'
+  ;;
+cygwin* | mingw*)
+  symcode='[ABCDGISTW]'
+  ;;
+hpux*) # Its linker distinguishes data from code symbols
+  global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern char \1();/p' -e 's/^. .* \(.*\)$/extern char \1;/p'"
+  ;;
+irix*)
+  symcode='[BCDEGRST]'
+  ;;
+solaris*)
+  symcode='[BDT]'
+  ;;
+sysv4)
+  symcode='[DFNSTU]'
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+if $NM -V 2>&1 | egrep '(GNU|with BFD)' > /dev/null; then
+  symcode='[ABCDGISTW]'
+fi
+
+# Try without a prefix undercore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Write the raw and C identifiers.
+  global_symbol_pipe="sed -n -e 's/^.*[        ]\($symcode\)[  ][      ]*\($ac_symprfx\)$sympat$/$symxfrm/p'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+  $rm conftest*
+  cat > conftest.c <<EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(){}
+#ifdef __cplusplus
+}
+#endif
+main(){nm_test_var='a';nm_test_func();return(0);}
+EOF
+
+  echo "$progname:1507: checking if global_symbol_pipe works" >&5
+  if { (eval echo $progname:1508: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; } && test -s conftest.$objext; then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if { echo "$progname:1511: eval \"$NM conftest.$objext | $global_symbol_pipe > $nlist\"" >&5; eval "$NM conftest.$objext | $global_symbol_pipe > $nlist 2>&5"; } && test -s "$nlist"; then
+
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+       mv -f "$nlist"T "$nlist"
+      else
+       rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if egrep ' nm_test_var$' "$nlist" >/dev/null; then
+       if egrep ' nm_test_func$' "$nlist" >/dev/null; then
+         cat <<EOF > conftest.c
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EOF
+         # Now generate the symbol file.
+         eval "$global_symbol_to_cdecl"' < "$nlist" >> conftest.c'
+
+         cat <<EOF >> conftest.c
+#if defined (__STDC__) && __STDC__
+# define lt_ptr_t void *
+#else
+# define lt_ptr_t char *
+# define const
+#endif
+
+/* The mapping between symbol names and symbols. */
+const struct {
+  const char *name;
+  lt_ptr_t address;
+}
+lt_preloaded_symbols[] =
+{
+EOF
+         sed 's/^. \(.*\) \(.*\)$/  {"\2", (lt_ptr_t) \&\2},/' < "$nlist" >> conftest.c
+         cat <<\EOF >> conftest.c
+  {0, (lt_ptr_t) 0}
+};
+
+#ifdef __cplusplus
+}
+#endif
+EOF
+         # Now try linking the two files.
+         mv conftest.$objext conftstm.$objext
+         save_LIBS="$LIBS"
+         save_CFLAGS="$CFLAGS"
+         LIBS="conftstm.$objext"
+         CFLAGS="$CFLAGS$no_builtin_flag"
+         if { (eval echo $progname:1563: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+           pipe_works=yes
+         else
+           echo "$progname: failed program was:" >&5
+           cat conftest.c >&5
+         fi
+         LIBS="$save_LIBS"
+       else
+         echo "cannot find nm_test_func in $nlist" >&5
+       fi
+      else
+       echo "cannot find nm_test_var in $nlist" >&5
+      fi
+    else
+      echo "cannot run $global_symbol_pipe" >&5
+    fi
+  else
+    echo "$progname: failed program was:" >&5
+    cat conftest.c >&5
+  fi
+  $rm conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    global_symbol_pipe=
+  fi
+done
+if test "$pipe_works" = yes; then
+  echo "${ac_t}ok" 1>&6
+else
+  echo "${ac_t}failed" 1>&6
+fi
+
+if test -z "$global_symbol_pipe"; then
+  global_symbol_to_cdecl=
+fi
+
+# Check hardcoding attributes.
+echo $ac_n "checking how to hardcode library paths into programs... $ac_c" 1>&6
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" || \
+   test -n "$runpath_var"; then
+
+  # We can hardcode non-existant directories.
+  if test "$hardcode_direct" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$hardcode_shlibpath_var" != no &&
+     test "$hardcode_minus_L" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action=unsupported
+fi
+echo "$ac_t$hardcode_action" 1>&6
+
+
+reload_flag=
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+echo $ac_n "checking for $LD option to reload object files... $ac_c" 1>&6
+# PORTME Some linkers may need a different reload flag.
+reload_flag='-r'
+echo "$ac_t$reload_flag" 1>&6
+test -n "$reload_flag" && reload_flag=" $reload_flag"
+
+# PORTME Fill in your ld.so characteristics
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+file_magic_cmd=
+file_magic_test_file=
+deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [regex]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given egrep regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+echo $ac_n "checking dynamic linker characteristics... $ac_c" 1>&6
+case "$host_os" in
+aix3*)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}.so$major'
+  ;;
+
+aix4*)
+  version_type=linux
+  # AIX has no versioning support, so currently we can not hardcode correct
+  # soname into executable. Probably we can add versioning support to
+  # collect2, so additional links can be useful in future.
+  # We preserve .a as extension for shared libraries though AIX4.2
+  # and later linker supports .so
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.a'
+  shlibpath_var=LIBPATH
+  deplibs_check_method=pass_all
+  ;;
+
+amigaos*)
+  library_names_spec='$libname.ixlibrary $libname.a'
+  # Create ${libname}_ixlibrary.a entries in /sys/libs.
+  finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "(cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a)"; (cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a) || exit 1; done'
+  ;;
+
+beos*)
+  library_names_spec='${libname}.so'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  lt_cv_dlopen="load_add_on"
+  lt_cv_dlopen_libs=
+  lt_cv_dlopen_self=yes
+  ;;
+
+bsdi4*)
+  version_type=linux
+  library_names_spec='${libname}.so$major ${libname}.so'
+  soname_spec='${libname}.so'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  deplibs_check_method='file_magic ELF 32-bit LSB shared object'
+  file_magic_cmd=/usr/bin/file
+  file_magic_test_file=/shlib/libc.so
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw*)
+  version_type=windows
+  if test "$with_gcc" = yes; then
+    library_names_spec='${libname}`echo ${release} | sed -e 's/[.]/-/g'`${versuffix}.dll $libname.a'
+  else
+    library_names_spec='${libname}`echo ${release} | sed -e 's/[.]/-/g'`${versuffix}.dll $libname.lib'
+  fi
+  dynamic_linker='Win32 ld.exe'
+  deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+  file_magic_cmd='${OBJDUMP} -f'
+  need_lib_prefix=no
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  lt_cv_dlopen="LoadLibrary"
+  lt_cv_dlopen_libs=
+  ;;
+
+freebsd1*)
+  dynamic_linker=no
+  ;;
+  
+freebsd*)
+  objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout`
+  version_type=freebsd-$objformat
+  case "$version_type" in
+    freebsd-elf*)
+      deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB shared object'
+      file_magic_cmd=/usr/bin/file
+      file_magic_test_file=`echo /usr/lib/libc.so*`
+      library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so $libname.so'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      deplibs_check_method=unknown
+      library_names_spec='${libname}${release}.so$versuffix $libname.so$versuffix'
+      need_version=yes
+      ;;
+  esac
+  finish_cmds='PATH="\$PATH:/sbin" OBJFORMAT="'"$objformat"'" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+gnu*)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix ${libname}.so'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  dynamic_linker="$host_os dld.sl"
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  shlibpath_var=SHLIB_PATH
+  shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+  library_names_spec='${libname}${release}.sl$versuffix ${libname}${release}.sl$major $libname.sl'
+  soname_spec='${libname}${release}.sl$major'
+  # HP-UX runs *really* slowly unless shared libraries are mode 555.
+  postinstall_cmds='chmod 555 $lib'
+  ;;
+
+irix5* | irix6*)
+  version_type=irix
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}.so.$major'
+  library_names_spec='${libname}${release}.so.$versuffix ${libname}${release}.so.$major ${libname}${release}.so $libname.so'
+  case "$host_os" in
+  irix5*)
+    libsuff= shlibsuff=
+    # this will be overridden with pass_all, but let us keep it just in case
+    deplibs_check_method="file_magic ELF 32-bit MSB dynamic lib MIPS - version 1"
+    ;;
+  *)
+    case "$LD" in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 ") libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 ") libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 ") libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    # this will be overridden with pass_all, but let us keep it just in case
+    deplibs_check_method="file_magic ELF ${libmagic} MSB mips-[1234] dynamic lib MIPS - version 1"
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  file_magic_cmd=/usr/bin/file
+  file_magic_test_file=`echo /lib${libsuff}/libc.so*`
+  deplibs_check_method='pass_all'
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux-gnuoldld* | linux-gnuaout* | linux-gnucoff*)
+  dynamic_linker=no
+  ;;
+
+# This must be Linux ELF.
+linux-gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+  file_magic_cmd=/usr/bin/file
+  file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so`
+
+  if test -f /lib/ld.so.1; then
+    dynamic_linker='GNU ld.so'
+  else
+    # Only the GNU ld.so supports shared libraries on MkLinux.
+    case "$host_cpu" in
+    powerpc*) dynamic_linker=no ;;
+    *) dynamic_linker='Linux ld.so' ;;
+    esac
+  fi
+  ;;
+
+netbsd*)
+  version_type=sunos
+  if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major ${libname}${release}.so ${libname}.so'
+    soname_spec='${libname}${release}.so$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+openbsd*)
+  version_type=sunos
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+    need_version=no
+  fi
+  library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+os2*)
+  libname_spec='$name'
+  need_lib_prefix=no
+  library_names_spec='$libname.dll $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4*)
+  version_type=osf
+  need_version=no
+  soname_spec='${libname}${release}.so'
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so $libname.so'
+  shlibpath_var=LD_LIBRARY_PATH
+  # this will be overridden with pass_all, but let us keep it just in case
+  deplibs_check_method='file_magic COFF format alpha shared library'
+  file_magic_cmd=/usr/bin/file
+  file_magic_test_file=/shlib/libc.so
+  deplibs_check_method='pass_all'
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+sco3.2v5*)
+  version_type=osf
+  soname_spec='${libname}${release}.so$major'
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+solaris*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  deplibs_check_method="file_magic ELF [0-9][0-9]-bit [LM]SB dynamic lib"
+  file_magic_cmd=/usr/bin/file
+  file_magic_test_file=/lib/libc.so
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case "$host_vendor" in
+    ncr)
+      deplibs_check_method='pass_all'
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+      file_magic_cmd=/usr/bin/file
+      file_magic_test_file=`echo /usr/lib/libc.so*`
+      ;;
+  esac
+  ;;
+
+uts4*)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+dgux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+echo "$ac_t$dynamic_linker" 1>&6
+test "$dynamic_linker" = no && can_build_shared=no
+
+# Report the final consequences.
+echo "checking if libtool supports shared libraries... $can_build_shared" 1>&6
+
+# Only try to build win32 dlls if AC_LIBTOOL_WIN32_DLL was used in
+# configure.in, otherwise build static only libraries.
+case "$host_os" in
+cygwin* | mingw* | os2*)
+  if test x$can_build_shared = xyes; then
+    test x$enable_win32_dll = xno && can_build_shared=no
+    echo "checking if package supports dlls... $can_build_shared" 1>&6
+  fi
+;;
+esac
+
+if test -n "$file_magic_test_file" && test -n "$file_magic_cmd"; then
+  case "$deplibs_check_method" in
+  "file_magic "*)
+    file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`"
+    if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+       egrep "$file_magic_regex" > /dev/null; then
+      :
+    else
+      cat <<EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+EOF
+    fi ;;
+  esac
+fi
+
+echo $ac_n "checking whether to build shared libraries... $ac_c" 1>&6
+test "$can_build_shared" = "no" && enable_shared=no
+
+# On AIX, shared libraries and static libraries use the same namespace, and
+# are all built from PIC.
+case "$host_os" in
+aix3*)
+  test "$enable_shared" = yes && enable_static=no
+  if test -n "$RANLIB"; then
+    archive_cmds="$archive_cmds~\$RANLIB \$lib"
+    postinstall_cmds='$RANLIB $lib'
+  fi
+  ;;
+
+aix4*)
+  test "$enable_shared" = yes && enable_static=no
+  ;;
+esac
+
+echo "$ac_t$enable_shared" 1>&6
+
+# Make sure either enable_shared or enable_static is yes.
+test "$enable_shared" = yes || enable_static=yes
+
+echo "checking whether to build static libraries... $enable_static" 1>&6
+
+if test "$hardcode_action" = relink; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+echo $ac_n "checking for objdir... $ac_c" 1>&6
+rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+echo "$ac_t$objdir" 1>&6
+
+if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+if eval "test \"`echo '$''{'lt_cv_dlopen'+set}'`\" != set"; then
+  lt_cv_dlopen=no lt_cv_dlopen_libs=
+echo $ac_n "checking for dlopen""... $ac_c" 1>&6
+echo "$progname:2063: checking for dlopen" >&5
+if eval "test \"`echo '$''{'ac_cv_func_dlopen'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2068 "ltconfig"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char dlopen(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char dlopen();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_dlopen) || defined (__stub___dlopen)
+choke me
+#else
+dlopen();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo $progname:2090: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_dlopen=yes"
+else
+  echo "$progname: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_dlopen=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'dlopen`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  lt_cv_dlopen="dlopen"
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6
+echo "$progname:2108: checking for dlopen in -ldl" >&5
+ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-ldl  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2116 "ltconfig"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char dlopen();
+
+int main() {
+dlopen()
+; return 0; }
+EOF
+if { (eval echo $progname:2126: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "$progname: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for dld_link in -ldld""... $ac_c" 1>&6
+echo "$progname:2145: checking for dld_link in -ldld" >&5
+ac_lib_var=`echo dld'_'dld_link | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-ldld  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2153 "ltconfig"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char dld_link();
+
+int main() {
+dld_link()
+; return 0; }
+EOF
+if { (eval echo $progname:2163: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "$progname: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for shl_load""... $ac_c" 1>&6
+echo "$progname:2182: checking for shl_load" >&5
+if eval "test \"`echo '$''{'ac_cv_func_shl_load'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2187 "ltconfig"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char shl_load(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char shl_load();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_shl_load) || defined (__stub___shl_load)
+choke me
+#else
+shl_load();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo $progname:2209: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_shl_load=yes"
+else
+  echo "$progname: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_shl_load=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'shl_load`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  lt_cv_dlopen="shl_load"
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for shl_load in -ldld""... $ac_c" 1>&6
+echo "$progname:2227: checking for shl_load in -ldld" >&5
+ac_lib_var=`echo dld'_'shl_load | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-ldld  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2235 "ltconfig"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char shl_load();
+
+int main() {
+shl_load()
+; return 0; }
+EOF
+if { (eval echo $progname:2246: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "$progname: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+fi
+
+    
+fi
+
+  
+fi
+
+
+fi
+
+fi
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  fi
+
+  case "$lt_cv_dlopen" in
+  dlopen)
+for ac_hdr in dlfcn.h; do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "$progname:2289: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2294 "ltconfig"
+#include <$ac_hdr>
+int fnord = 0;
+EOF
+ac_try="$ac_compile conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo $progname:2299: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "$progname: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+    if test "x$ac_cv_header_dlfcn_h" = xyes; then
+      CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+    fi
+    eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+  echo $ac_n "checking whether a program can dlopen itself""... $ac_c" 1>&6
+echo "$progname:2327: checking whether a program can dlopen itself" >&5
+if test "${lt_cv_dlopen_self+set}" = set; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    lt_cv_dlopen_self=cross
+  else
+    cat > conftest.c <<EOF
+#line 2335 "ltconfig"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LTDL_GLOBAL   RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+#  define LTDL_GLOBAL  DL_GLOBAL
+# else
+#  define LTDL_GLOBAL  0
+# endif
+#endif
+
+/* We may have to define LTDL_LAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LTDL_LAZY_OR_NOW
+# ifdef RTLD_LAZY
+#  define LTDL_LAZY_OR_NOW     RTLD_LAZY
+# else
+#  ifdef DL_LAZY
+#   define LTDL_LAZY_OR_NOW    DL_LAZY
+#  else
+#   ifdef RTLD_NOW
+#    define LTDL_LAZY_OR_NOW   RTLD_NOW
+#   else
+#    ifdef DL_NOW
+#     define LTDL_LAZY_OR_NOW  DL_NOW
+#    else
+#     define LTDL_LAZY_OR_NOW  0
+#    endif
+#   endif
+#  endif
+# endif
+#endif
+
+fnord() { int i=42;}
+main() { void *self, *ptr1, *ptr2; self=dlopen(0,LTDL_GLOBAL|LTDL_LAZY_OR_NOW);
+    if(self) { ptr1=dlsym(self,"fnord"); ptr2=dlsym(self,"_fnord");
+              if(ptr1 || ptr2) exit(0); } exit(1); } 
+
+EOF
+if { (eval echo $progname:2381: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+  lt_cv_dlopen_self=yes
+else
+  echo "$progname: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  lt_cv_dlopen_self=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$lt_cv_dlopen_self" 1>&6
+
+  if test "$lt_cv_dlopen_self" = yes; then
+    LDFLAGS="$LDFLAGS $link_static_flag"
+  echo $ac_n "checking whether a statically linked program can dlopen itself""... $ac_c" 1>&6
+echo "$progname:2400: checking whether a statically linked program can dlopen itself" >&5
+if test "${lt_cv_dlopen_self_static+set}" = set; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    lt_cv_dlopen_self_static=cross
+  else
+    cat > conftest.c <<EOF
+#line 2408 "ltconfig"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LTDL_GLOBAL   RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+#  define LTDL_GLOBAL  DL_GLOBAL
+# else
+#  define LTDL_GLOBAL  0
+# endif
+#endif
+
+/* We may have to define LTDL_LAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LTDL_LAZY_OR_NOW
+# ifdef RTLD_LAZY
+#  define LTDL_LAZY_OR_NOW     RTLD_LAZY
+# else
+#  ifdef DL_LAZY
+#   define LTDL_LAZY_OR_NOW    DL_LAZY
+#  else
+#   ifdef RTLD_NOW
+#    define LTDL_LAZY_OR_NOW   RTLD_NOW
+#   else
+#    ifdef DL_NOW
+#     define LTDL_LAZY_OR_NOW  DL_NOW
+#    else
+#     define LTDL_LAZY_OR_NOW  0
+#    endif
+#   endif
+#  endif
+# endif
+#endif
+
+fnord() { int i=42;}
+main() { void *self, *ptr1, *ptr2; self=dlopen(0,LTDL_GLOBAL|LTDL_LAZY_OR_NOW);
+    if(self) { ptr1=dlsym(self,"fnord"); ptr2=dlsym(self,"_fnord");
+    if(ptr1 || ptr2) exit(0); } exit(1); } 
+
+EOF
+if { (eval echo $progname:2454: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+  lt_cv_dlopen_self_static=yes
+else
+  echo "$progname: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  lt_cv_dlopen_self_static=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$lt_cv_dlopen_self_static" 1>&6
+fi
+    ;;
+  esac
+
+  case "$lt_cv_dlopen_self" in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case "$lt_cv_dlopen_self_static" in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+
+# Copy echo and quote the copy, instead of the original, because it is
+# used later.
+ltecho="$echo"
+if test "X$ltecho" = "X$CONFIG_SHELL $0 --fallback-echo"; then
+   ltecho="$CONFIG_SHELL \$0 --fallback-echo"
+fi
+LTSHELL="$SHELL"
+
+LTCONFIG_VERSION="$VERSION"
+
+# Only quote variables if we're using ltmain.sh.
+case "$ltmain" in
+*.sh)
+  # Now quote all the things that may contain metacharacters.
+  for var in ltecho old_CC old_CFLAGS old_CPPFLAGS \
+    old_LD old_LDFLAGS old_LIBS \
+    old_NM old_RANLIB old_LN_S old_DLLTOOL old_OBJDUMP old_AS \
+    AR CC LD LN_S NM LTSHELL LTCONFIG_VERSION \
+    reload_flag reload_cmds wl \
+    pic_flag link_static_flag no_builtin_flag export_dynamic_flag_spec \
+    thread_safe_flag_spec whole_archive_flag_spec libname_spec \
+    library_names_spec soname_spec \
+    RANLIB old_archive_cmds old_archive_from_new_cmds old_postinstall_cmds \
+    old_postuninstall_cmds archive_cmds archive_expsym_cmds postinstall_cmds postuninstall_cmds \
+    file_magic_cmd export_symbols_cmds deplibs_check_method allow_undefined_flag no_undefined_flag \
+    finish_cmds finish_eval global_symbol_pipe global_symbol_to_cdecl \
+    hardcode_libdir_flag_spec hardcode_libdir_separator  \
+    sys_lib_search_path_spec sys_lib_dlsearch_path_spec \
+    compiler_c_o compiler_o_lo need_locks exclude_expsyms include_expsyms; do
+
+    case "$var" in
+    reload_cmds | old_archive_cmds | old_archive_from_new_cmds | \
+    old_postinstall_cmds | old_postuninstall_cmds | \
+    export_symbols_cmds | archive_cmds | archive_expsym_cmds | \
+    postinstall_cmds | postuninstall_cmds | \
+    finish_cmds | sys_lib_search_path_spec | sys_lib_dlsearch_path_spec)
+      # Double-quote double-evaled strings.
+      eval "$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
+      ;;
+    *)
+      eval "$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\""
+      ;;
+    esac
+  done
+
+  case "$ltecho" in
+  *'\$0 --fallback-echo"')
+    ltecho=`$echo "X$ltecho" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'`
+    ;;
+  esac
+
+  trap "$rm \"$ofile\"; exit 1" 1 2 15
+  echo "creating $ofile"
+  $rm "$ofile"
+  cat <<EOF > "$ofile"
+#! $SHELL
+
+# `$echo "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP)
+# NOTE: Changes made to this file will be lost: look at ltconfig or ltmain.sh.
+#
+# Copyright (C) 1996-1999 Free Software Foundation, Inc.
+# Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# 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 version 2 of the License, 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="sed -e s/^X//"
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "\${CDPATH+set}" = set; then CDPATH=; export CDPATH; fi
+
+### BEGIN LIBTOOL CONFIG
+EOF
+  cfgfile="$ofile"
+  ;;
+
+*)
+  # Double-quote the variables that need it (for aesthetics).
+  for var in old_CC old_CFLAGS old_CPPFLAGS \
+    old_LD old_LDFLAGS old_LIBS \
+    old_NM old_RANLIB old_LN_S old_DLLTOOL old_OBJDUMP old_AS; do
+    eval "$var=\\\"\$var\\\""
+  done
+
+  # Just create a config file.
+  cfgfile="$ofile.cfg"
+  trap "$rm \"$cfgfile\"; exit 1" 1 2 15
+  echo "creating $cfgfile"
+  $rm "$cfgfile"
+  cat <<EOF > "$cfgfile"
+# `$echo "$cfgfile" | sed 's%^.*/%%'` - Libtool configuration file.
+# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP)
+EOF
+  ;;
+esac
+
+cat <<EOF >> "$cfgfile"
+# Libtool was configured as follows, on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# CC=$old_CC CFLAGS=$old_CFLAGS CPPFLAGS=$old_CPPFLAGS \\
+# LD=$old_LD LDFLAGS=$old_LDFLAGS LIBS=$old_LIBS \\
+# NM=$old_NM RANLIB=$old_RANLIB LN_S=$old_LN_S \\
+# DLLTOOL=$old_DLLTOOL OBJDUMP=$old_OBJDUMP AS=$old_AS \\
+#   $0$ltconfig_args
+#
+# Compiler and other test output produced by $progname, useful for
+# debugging $progname, is in ./config.log if it exists.
+
+# The version of $progname that generated this script.
+LTCONFIG_VERSION=$LTCONFIG_VERSION
+
+# Shell to use when invoking shell scripts.
+SHELL=$LTSHELL
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+
+# An echo program that does not interpret backslashes.
+echo=$ltecho
+
+# The archiver.
+AR=$AR
+
+# The default C compiler.
+CC=$CC
+
+# The linker used to build libraries.
+LD=$LD
+
+# Whether we need hard or soft links.
+LN_S=$LN_S
+
+# A BSD-compatible nm program.
+NM=$NM
+
+# Used on cygwin: DLL creation program.
+DLLTOOL="$DLLTOOL"
+
+# Used on cygwin: object dumper.
+OBJDUMP="$OBJDUMP"
+
+# Used on cygwin: assembler.
+AS="$AS"
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# How to create reloadable object files.
+reload_flag=$reload_flag
+reload_cmds=$reload_cmds
+
+# How to pass a linker flag through the compiler.
+wl=$wl
+
+# Object file suffix (normally "o").
+objext="$objext"
+
+# Old archive suffix (normally "a").
+libext="$libext"
+
+# Additional compiler flags for building library objects.
+pic_flag=$pic_flag
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$compiler_c_o
+
+# Can we write directly to a .lo ?
+compiler_o_lo=$compiler_o_lo
+
+# Must we lock files when doing compilation ?
+need_locks=$need_locks
+
+# Do we need the lib prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Whether dlopen is supported.
+dlopen=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$link_static_flag
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$no_builtin_flag
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$whole_archive_flag_spec
+
+# Compiler flag to generate thread-safe objects.
+thread_safe_flag_spec=$thread_safe_flag_spec
+
+# Library versioning type.
+version_type=$version_type
+
+# Format of library name prefix.
+libname_spec=$libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME.
+library_names_spec=$library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$soname_spec
+
+# Commands used to build and install an old-style archive.
+RANLIB=$RANLIB
+old_archive_cmds=$old_archive_cmds
+old_postinstall_cmds=$old_postinstall_cmds
+old_postuninstall_cmds=$old_postuninstall_cmds
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$old_archive_from_new_cmds
+
+# Commands used to build and install a shared archive.
+archive_cmds=$archive_cmds
+archive_expsym_cmds=$archive_expsym_cmds
+postinstall_cmds=$postinstall_cmds
+postuninstall_cmds=$postuninstall_cmds
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$deplibs_check_method
+
+# Command to use when deplibs_check_method == file_magic.
+file_magic_cmd=$file_magic_cmd
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$allow_undefined_flag
+
+# Flag that forces no undefined symbols.
+no_undefined_flag=$no_undefined_flag
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$finish_cmds
+
+# Same as above, but a single script fragment to be evaled but not shown.
+finish_eval=$finish_eval
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration
+global_symbol_to_cdecl=$global_symbol_to_cdecl
+
+# This is the shared library runtime path variable.
+runpath_var=$runpath_var
+
+# This is the shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec=$hardcode_libdir_flag_spec
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator=$hardcode_libdir_separator
+
+# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into
+# the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Compile-time system search path for libraries
+sys_lib_search_path_spec=$sys_lib_search_path_spec
+
+# Run-time system search path for libraries
+sys_lib_dlsearch_path_spec=$sys_lib_dlsearch_path_spec
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path="$fix_srcfile_path"
+
+# Set to yes if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$include_expsyms
+
+EOF
+
+case "$ltmain" in
+*.sh)
+  echo '### END LIBTOOL CONFIG' >> "$ofile"
+  echo >> "$ofile"
+  case "$host_os" in
+  aix3*)
+    cat <<\EOF >> "$ofile"
+
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "${COLLECT_NAMES+set}" != set; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+EOF
+    ;;
+  esac
+
+  # Append the ltmain.sh script.
+  cat "$ltmain" >> "$ofile" || (rm -f "$ofile"; exit 1)
+
+  chmod +x "$ofile"
+  ;;
+
+*)
+  # Compile the libtool program.
+  echo "FIXME: would compile $ltmain"
+  ;;
+esac
+
+test -n "$cache_file" || exit 0
+
+# AC_CACHE_SAVE
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+exit 0
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
diff --git a/neon/ltmain.sh b/neon/ltmain.sh
new file mode 100644 (file)
index 0000000..f1b9986
--- /dev/null
@@ -0,0 +1,3892 @@
+# ltmain.sh - Provide generalized library-building support services.
+# NOTE: Changing this file will not affect anything until you rerun ltconfig.
+#
+# Copyright (C) 1996-1999 Free Software Foundation, Inc.
+# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# 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 version 2 of the License, 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Check that we have a working $echo.
+if test "X$1" = X--no-reexec; then
+  # Discard the --no-reexec flag, and continue.
+  shift
+elif test "X$1" = X--fallback-echo; then
+  # Avoid inline document here, it may be left over
+  :
+elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then
+  # Yippee, $echo works!
+  :
+else
+  # Restart under the correct shell, and then maybe $echo will work.
+  exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+  # used as fallback echo
+  shift
+  cat <<EOF
+$*
+EOF
+  exit 0
+fi
+
+# The name of this program.
+progname=`$echo "$0" | sed 's%^.*/%%'`
+modename="$progname"
+
+# Constants.
+PROGRAM=ltmain.sh
+PACKAGE=libtool
+VERSION=1.3
+TIMESTAMP=" (1.385.2.117 1999/04/29 13:07:13)"
+
+default_mode=
+help="Try \`$progname --help' for more information."
+magic="%%%MAGIC variable%%%"
+mkdir="mkdir"
+mv="mv -f"
+rm="rm -f"
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e 1s/^X//'
+sed_quote_subst='s/\([\\`\\"$\\\\]\)/\\\1/g'
+SP2NL='tr \040 \012'
+NL2SP='tr \012 \040'
+
+# NLS nuisances.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+# We save the old values to restore during execute mode.
+if test "${LC_ALL+set}" = set; then
+  save_LC_ALL="$LC_ALL"; LC_ALL=C; export LC_ALL
+fi
+if test "${LANG+set}" = set; then
+  save_LANG="$LANG"; LANG=C; export LANG
+fi
+
+if test "$LTCONFIG_VERSION" != "$VERSION"; then
+  echo "$modename: ltconfig version \`$LTCONFIG_VERSION' does not match $PROGRAM version \`$VERSION'" 1>&2
+  echo "Fatal configuration error.  See the $PACKAGE docs for more information." 1>&2
+  exit 1
+fi
+
+if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
+  echo "$modename: not configured to build any kind of library" 1>&2
+  echo "Fatal configuration error.  See the $PACKAGE docs for more information." 1>&2
+  exit 1
+fi
+
+# Global variables.
+mode=$default_mode
+nonopt=
+prev=
+prevopt=
+run=
+show="$echo"
+show_help=
+execute_dlfiles=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+
+# Parse our command line options once, thoroughly.
+while test $# -gt 0
+do
+  arg="$1"
+  shift
+
+  case "$arg" in
+  -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) optarg= ;;
+  esac
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$prev"; then
+    case "$prev" in
+    execute_dlfiles)
+      eval "$prev=\"\$$prev \$arg\""
+      ;;
+    *)
+      eval "$prev=\$arg"
+      ;;
+    esac
+
+    prev=
+    prevopt=
+    continue
+  fi
+
+  # Have we seen a non-optional argument yet?
+  case "$arg" in
+  --help)
+    show_help=yes
+    ;;
+
+  --version)
+    echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP"
+    exit 0
+    ;;
+
+  --config)
+    sed -e '1,/^### BEGIN LIBTOOL CONFIG/d' -e '/^### END LIBTOOL CONFIG/,$d' $0
+    exit 0
+    ;;
+
+  --debug)
+    echo "$progname: enabling shell trace mode"
+    set -x
+    ;;
+
+  --dry-run | -n)
+    run=:
+    ;;
+
+  --features)
+    echo "host: $host"
+    if test "$build_libtool_libs" = yes; then
+      echo "enable shared libraries"
+    else
+      echo "disable shared libraries"
+    fi
+    if test "$build_old_libs" = yes; then
+      echo "enable static libraries"
+    else
+      echo "disable static libraries"
+    fi
+    exit 0
+    ;;
+
+  --finish) mode="finish" ;;
+
+  --mode) prevopt="--mode" prev=mode ;;
+  --mode=*) mode="$optarg" ;;
+
+  --quiet | --silent)
+    show=:
+    ;;
+
+  -dlopen)
+    prevopt="-dlopen"
+    prev=execute_dlfiles
+    ;;
+
+  -*)
+    $echo "$modename: unrecognized option \`$arg'" 1>&2
+    $echo "$help" 1>&2
+    exit 1
+    ;;
+
+  *)
+    nonopt="$arg"
+    break
+    ;;
+  esac
+done
+
+if test -n "$prevopt"; then
+  $echo "$modename: option \`$prevopt' requires an argument" 1>&2
+  $echo "$help" 1>&2
+  exit 1
+fi
+
+if test -z "$show_help"; then
+
+  # Infer the operation mode.
+  if test -z "$mode"; then
+    case "$nonopt" in
+    *cc | *++ | gcc* | *-gcc*)
+      mode=link
+      for arg
+      do
+       case "$arg" in
+       -c)
+          mode=compile
+          break
+          ;;
+       esac
+      done
+      ;;
+    *db | *dbx | *strace | *truss)
+      mode=execute
+      ;;
+    *install*|cp|mv)
+      mode=install
+      ;;
+    *rm)
+      mode=uninstall
+      ;;
+    *)
+      # If we have no mode, but dlfiles were specified, then do execute mode.
+      test -n "$execute_dlfiles" && mode=execute
+
+      # Just use the default operation mode.
+      if test -z "$mode"; then
+       if test -n "$nonopt"; then
+         $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2
+       else
+         $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2
+       fi
+      fi
+      ;;
+    esac
+  fi
+
+  # Only execute mode is allowed to have -dlopen flags.
+  if test -n "$execute_dlfiles" && test "$mode" != execute; then
+    $echo "$modename: unrecognized option \`-dlopen'" 1>&2
+    $echo "$help" 1>&2
+    exit 1
+  fi
+
+  # Change the help message to a mode-specific one.
+  generic_help="$help"
+  help="Try \`$modename --help --mode=$mode' for more information."
+
+  # These modes are in order of execution frequency so that they run quickly.
+  case "$mode" in
+  # libtool compile mode
+  compile)
+    modename="$modename: compile"
+    # Get the compilation command and the source file.
+    base_compile=
+    lastarg=
+    srcfile="$nonopt"
+    suppress_output=
+
+    user_target=no
+    for arg
+    do
+      # Accept any command-line options.
+      case "$arg" in
+      -o)
+       if test "$user_target" != "no"; then
+         $echo "$modename: you cannot specify \`-o' more than once" 1>&2
+         exit 1
+       fi
+       user_target=next
+       ;;
+
+      -static)
+       build_old_libs=yes
+       continue
+       ;;
+      esac
+
+      case "$user_target" in
+      next)
+       # The next one is the -o target name
+       user_target=yes
+       continue
+       ;;
+      yes)
+       # We got the output file
+       user_target=set
+       libobj="$arg"
+       continue
+       ;;
+      esac
+
+      # Accept the current argument as the source file.
+      lastarg="$srcfile"
+      srcfile="$arg"
+
+      # Aesthetically quote the previous argument.
+
+      # Backslashify any backslashes, double quotes, and dollar signs.
+      # These are the only characters that are still specially
+      # interpreted inside of double-quoted scrings.
+      lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"`
+
+      # Double-quote args containing other shell metacharacters.
+      # Many Bourne shells cannot handle close brackets correctly in scan
+      # sets, so we specify it separately.
+      case "$lastarg" in
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*)
+       lastarg="\"$lastarg\""
+       ;;
+      esac
+
+      # Add the previous argument to base_compile.
+      if test -z "$base_compile"; then
+       base_compile="$lastarg"
+      else
+       base_compile="$base_compile $lastarg"
+      fi
+    done
+
+    case "$user_target" in
+    set)
+      ;;
+    no)
+      # Get the name of the library object.
+      libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'`
+      ;;
+    *)
+      $echo "$modename: you must specify a target with \`-o'" 1>&2
+      exit 1
+      ;;
+    esac
+
+    # Recognize several different file suffixes.
+    # If the user specifies -o file.o, it is replaced with file.lo
+    xform='[cCFSfmso]'
+    case "$libobj" in
+    *.ada) xform=ada ;;
+    *.adb) xform=adb ;;
+    *.ads) xform=ads ;;
+    *.asm) xform=asm ;;
+    *.c++) xform=c++ ;;
+    *.cc) xform=cc ;;
+    *.cpp) xform=cpp ;;
+    *.cxx) xform=cxx ;;
+    *.f90) xform=f90 ;;
+    *.for) xform=for ;;
+    esac
+
+    libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"`
+
+    case "$libobj" in
+    *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;;
+    *)
+      $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2
+      exit 1
+      ;;
+    esac
+
+    if test -z "$base_compile"; then
+      $echo "$modename: you must specify a compilation command" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    # Delete any leftover library objects.
+    if test "$build_old_libs" = yes; then
+      removelist="$obj $libobj"
+    else
+      removelist="$libobj"
+    fi
+
+    $run $rm $removelist
+    trap "$run $rm $removelist; exit 1" 1 2 15
+
+    # Calculate the filename of the output object if compiler does
+    # not support -o with -c
+    if test "$compiler_c_o" = no; then
+      output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\..*$%%'`.${objext}
+      lockfile="$output_obj.lock"
+      removelist="$removelist $output_obj $lockfile"
+      trap "$run $rm $removelist; exit 1" 1 2 15
+    else
+      need_locks=no
+      lockfile=
+    fi
+
+    # Lock this critical section if it is needed
+    # We use this script file to make the link, it avoids creating a new file
+    if test "$need_locks" = yes; then
+      until ln "$0" "$lockfile" 2>/dev/null; do
+       $show "Waiting for $lockfile to be removed"
+       sleep 2
+      done
+    elif test "$need_locks" = warn; then
+      if test -f "$lockfile"; then
+       echo "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $run $rm $removelist
+       exit 1
+      fi
+      echo $srcfile > "$lockfile"
+    fi
+
+    if test -n "$fix_srcfile_path"; then
+      eval srcfile=\"$fix_srcfile_path\"
+    fi
+
+    # Only build a PIC object if we are building libtool libraries.
+    if test "$build_libtool_libs" = yes; then
+      # Without this assignment, base_compile gets emptied.
+      fbsd_hideous_sh_bug=$base_compile
+
+      # All platforms use -DPIC, to notify preprocessed assembler code.
+      command="$base_compile $pic_flag -DPIC $srcfile"
+      if test "$build_old_libs" = yes; then
+       lo_libobj="$libobj"
+       dir=`$echo "X$libobj" | $Xsed -e 's%/[^/]*$%%'`
+       if test "X$dir" = "X$libobj"; then
+         dir="$objdir"
+       else
+         dir="$dir/$objdir"
+       fi
+       libobj="$dir/"`$echo "X$libobj" | $Xsed -e 's%^.*/%%'`
+
+       if test -d "$dir"; then
+         $show "$rm $libobj"
+         $run $rm $libobj
+       else
+         $show "$mkdir $dir"
+         $run $mkdir $dir
+         status=$?
+         if test $status -ne 0 && test ! -d $dir; then
+           exit $status
+         fi
+       fi
+      fi
+      if test "$compiler_o_lo" = yes; then
+       output_obj="$libobj"
+       command="$command -o $output_obj"
+      elif test "$compiler_c_o" = yes; then
+       output_obj="$obj"
+       command="$command -o $output_obj"
+      fi
+
+      $show "$command"
+      if $run eval "$command"; then :
+      else
+       test -n "$output_obj" && $run $rm $removelist
+       exit 1
+      fi
+
+      if test "$need_locks" = warn &&
+        test x"`cat $lockfile 2>/dev/null`" != x"$srcfile"; then
+       echo "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $run $rm $removelist
+       exit 1
+      fi
+
+      # Just move the object if needed, then go on to compile the next one
+      if test x"$output_obj" != x"$libobj"; then
+       $show "$mv $output_obj $libobj"
+       if $run $mv $output_obj $libobj; then :
+       else
+         error=$?
+         $run $rm $removelist
+         exit $error
+       fi
+      fi
+
+      # If we have no pic_flag, then copy the object into place and finish.
+      if test -z "$pic_flag" && test "$build_old_libs" = yes; then
+       # Rename the .lo from within objdir to obj
+       if test -f $obj; then
+         $show $rm $obj
+         $run $rm $obj
+       fi
+
+       $show "$mv $libobj $obj"
+       if $run $mv $libobj $obj; then :
+       else
+         error=$?
+         $run $rm $removelist
+         exit $error
+       fi
+
+       # Now arrange that obj and lo_libobj become the same file
+       $show "$LN_S $obj $lo_libobj"
+       if $run $LN_S $obj $lo_libobj; then
+         exit 0
+       else
+         error=$?
+         $run $rm $removelist
+         exit $error
+       fi
+      fi
+
+      # Allow error messages only from the first compilation.
+      suppress_output=' >/dev/null 2>&1'
+    fi
+
+    # Only build a position-dependent object if we build old libraries.
+    if test "$build_old_libs" = yes; then
+      command="$base_compile $srcfile"
+      if test "$compiler_c_o" = yes; then
+       command="$command -o $obj"
+       output_obj="$obj"
+      fi
+
+      # Suppress compiler output if we already did a PIC compilation.
+      command="$command$suppress_output"
+      $show "$command"
+      if $run eval "$command"; then :
+      else
+       $run $rm $removelist
+       exit 1
+      fi
+
+      if test "$need_locks" = warn &&
+        test x"`cat $lockfile 2>/dev/null`" != x"$srcfile"; then
+       echo "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $run $rm $removelist
+       exit 1
+      fi
+
+      # Just move the object if needed
+      if test x"$output_obj" != x"$obj"; then
+       $show "$mv $output_obj $obj"
+       if $run $mv $output_obj $obj; then :
+       else
+         error=$?
+         $run $rm $removelist
+         exit $error
+       fi
+      fi
+
+      # Create an invalid libtool object if no PIC, so that we do not
+      # accidentally link it into a program.
+      if test "$build_libtool_libs" != yes; then
+       $show "echo timestamp > $libobj"
+       $run eval "echo timestamp > \$libobj" || exit $?
+      else
+       # Move the .lo from within objdir
+       $show "$mv $libobj $lo_libobj"
+       if $run $mv $libobj $lo_libobj; then :
+       else
+         error=$?
+         $run $rm $removelist
+         exit $error
+       fi
+      fi
+    fi
+
+    # Unlock the critical section if it was locked
+    if test "$need_locks" != no; then
+      $rm "$lockfile"
+    fi
+
+    exit 0
+    ;;
+
+  # libtool link mode
+  link)
+    modename="$modename: link"
+    C_compiler="$CC" # save it, to compile generated C sources
+    CC="$nonopt"
+    case "$host" in
+    *-*-cygwin* | *-*-mingw* | *-*-os2*)
+      # It is impossible to link a dll without this setting, and
+      # we shouldn't force the makefile maintainer to figure out
+      # which system we are compiling for in order to pass an extra
+      # flag for every libtool invokation.
+      # allow_undefined=no
+
+      # FIXME: Unfortunately, there are problems with the above when trying
+      # to make a dll which has undefined symbols, in which case not
+      # even a static library is built.  For now, we need to specify
+      # -no-undefined on the libtool link line when we can be certain
+      # that all symbols are satisfied, otherwise we get a static library.
+      allow_undefined=yes
+
+      # This is a source program that is used to create dlls on Windows
+      # Don't remove nor modify the starting and closing comments
+# /* ltdll.c starts here */
+# #define WIN32_LEAN_AND_MEAN
+# #include <windows.h>
+# #undef WIN32_LEAN_AND_MEAN
+# #include <stdio.h>
+#
+# #ifdef __cplusplus
+# extern "C" {
+# #endif
+# BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved);
+# #ifdef __cplusplus
+# }
+# #endif
+#
+# #include <cygwin/cygwin_dll.h>
+# DECLARE_CYGWIN_DLL( DllMain );
+# HINSTANCE __hDllInstance_base;
+#
+# BOOL APIENTRY
+# DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved)
+# {
+#   __hDllInstance_base = hInst;
+#   return TRUE;
+# }
+# /* ltdll.c ends here */
+      # This is a source program that is used to create import libraries
+      # on Windows for dlls which lack them. Don't remove nor modify the
+      # starting and closing comments
+# /* impgen.c starts here */
+# /*   Copyright (C) 1999 Free Software Foundation, Inc.
+# 
+#  This file is part of GNU libtool.
+# 
+#  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 version 2 of the License, 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 this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#  */
+# 
+#  #include <stdio.h>          /* for printf() */
+#  #include <unistd.h>         /* for open(), lseek(), read() */
+#  #include <fcntl.h>          /* for O_RDONLY, O_BINARY */
+#  #include <string.h>         /* for strdup() */
+# 
+#  static unsigned int
+#  pe_get16 (fd, offset)
+#       int fd;
+#       int offset;
+#  {
+#    unsigned char b[2];
+#    lseek (fd, offset, SEEK_SET);
+#    read (fd, b, 2);
+#    return b[0] + (b[1]<<8);
+#  }
+# 
+#  static unsigned int
+#  pe_get32 (fd, offset)
+#      int fd;
+#      int offset;
+#  {
+#    unsigned char b[4];
+#    lseek (fd, offset, SEEK_SET);
+#    read (fd, b, 4);
+#    return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
+#  }
+# 
+#  static unsigned int
+#  pe_as32 (ptr)
+#       void *ptr;
+#  {
+#    unsigned char *b = ptr;
+#    return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
+#  }
+# 
+#  int
+#  main (argc, argv)
+#      int argc;
+#      char *argv[];
+#  {
+#      int dll;
+#      unsigned long pe_header_offset, opthdr_ofs, num_entries, i;
+#      unsigned long export_rva, export_size, nsections, secptr, expptr;
+#      unsigned long name_rvas, nexp;
+#      unsigned char *expdata, *erva;
+#      char *filename, *dll_name;
+# 
+#      filename = argv[1];
+# 
+#      dll = open(filename, O_RDONLY|O_BINARY);
+#      if (!dll)
+#      return 1;
+# 
+#      dll_name = filename;
+#    
+#      for (i=0; filename[i]; i++)
+#      if (filename[i] == '/' || filename[i] == '\\'  || filename[i] == ':')
+#          dll_name = filename + i +1;
+# 
+#      pe_header_offset = pe_get32 (dll, 0x3c);
+#      opthdr_ofs = pe_header_offset + 4 + 20;
+#      num_entries = pe_get32 (dll, opthdr_ofs + 92);
+# 
+#      if (num_entries < 1) /* no exports */
+#      return 1;
+# 
+#      export_rva = pe_get32 (dll, opthdr_ofs + 96);
+#      export_size = pe_get32 (dll, opthdr_ofs + 100);
+#      nsections = pe_get16 (dll, pe_header_offset + 4 +2);
+#      secptr = (pe_header_offset + 4 + 20 +
+#            pe_get16 (dll, pe_header_offset + 4 + 16));
+# 
+#      expptr = 0;
+#      for (i = 0; i < nsections; i++)
+#      {
+#      char sname[8];
+#      unsigned long secptr1 = secptr + 40 * i;
+#      unsigned long vaddr = pe_get32 (dll, secptr1 + 12);
+#      unsigned long vsize = pe_get32 (dll, secptr1 + 16);
+#      unsigned long fptr = pe_get32 (dll, secptr1 + 20);
+#      lseek(dll, secptr1, SEEK_SET);
+#      read(dll, sname, 8);
+#      if (vaddr <= export_rva && vaddr+vsize > export_rva)
+#      {
+#          expptr = fptr + (export_rva - vaddr);
+#          if (export_rva + export_size > vaddr + vsize)
+#              export_size = vsize - (export_rva - vaddr);
+#          break;
+#      }
+#      }
+# 
+#      expdata = (unsigned char*)malloc(export_size);
+#      lseek (dll, expptr, SEEK_SET);
+#      read (dll, expdata, export_size);
+#      erva = expdata - export_rva;
+# 
+#      nexp = pe_as32 (expdata+24);
+#      name_rvas = pe_as32 (expdata+32);
+# 
+#      printf ("EXPORTS\n");
+#      for (i = 0; i<nexp; i++)
+#      {
+#      unsigned long name_rva = pe_as32 (erva+name_rvas+i*4);
+#      printf ("\t%s @ %ld ;\n", erva+name_rva, 1+ i);
+#      }
+# 
+#      return 0;
+#  }
+# /* impgen.c ends here */
+      ;;
+    *)
+      allow_undefined=yes
+      ;;
+    esac
+    compile_command="$CC"
+    finalize_command="$CC"
+
+    compile_rpath=
+    finalize_rpath=
+    compile_shlibpath=
+    finalize_shlibpath=
+    convenience=
+    old_convenience=
+    deplibs=
+    linkopts=
+
+    if test -n "$shlibpath_var"; then
+      # get the directories listed in $shlibpath_var
+      eval lib_search_path=\`\$echo \"X \${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\`
+    else
+      lib_search_path=
+    fi
+    # now prepend the system-specific ones
+    eval lib_search_path=\"$sys_lib_search_path_spec\$lib_search_path\"
+    eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+    
+    avoid_version=no
+    dlfiles=
+    dlprefiles=
+    dlself=no
+    export_dynamic=no
+    export_symbols=
+    export_symbols_regex=
+    generated=
+    libobjs=
+    link_against_libtool_libs=
+    ltlibs=
+    module=no
+    objs=
+    prefer_static_libs=no
+    preload=no
+    prev=
+    prevarg=
+    release=
+    rpath=
+    xrpath=
+    perm_rpath=
+    temp_rpath=
+    thread_safe=no
+    vinfo=
+
+    # We need to know -static, to get the right output filenames.
+    for arg
+    do
+      case "$arg" in
+      -all-static | -static)
+       if test "X$arg" = "X-all-static"; then
+         if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+           $echo "$modename: warning: complete static linking is impossible in this configuration" 1>&2
+         fi
+         if test -n "$link_static_flag"; then
+           dlopen_self=$dlopen_self_static
+         fi
+       else
+         if test -z "$pic_flag" && test -n "$link_static_flag"; then
+           dlopen_self=$dlopen_self_static
+         fi
+       fi
+       build_libtool_libs=no
+       build_old_libs=yes
+       prefer_static_libs=yes
+       break
+       ;;
+      esac
+    done
+
+    # See if our shared archives depend on static archives.
+    test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+    # Go through the arguments, transforming them on the way.
+    while test $# -gt 0; do
+      arg="$1"
+      shift
+
+      # If the previous option needs an argument, assign it.
+      if test -n "$prev"; then
+       case "$prev" in
+       output)
+         compile_command="$compile_command @OUTPUT@"
+         finalize_command="$finalize_command @OUTPUT@"
+         ;;
+       esac
+
+       case "$prev" in
+       dlfiles|dlprefiles)
+         if test "$preload" = no; then
+           # Add the symbol object into the linking commands.
+           compile_command="$compile_command @SYMFILE@"
+           finalize_command="$finalize_command @SYMFILE@"
+           preload=yes
+         fi
+         case "$arg" in
+         *.la | *.lo) ;;  # We handle these cases below.
+         self)
+           if test "$prev" = dlprefiles; then
+             dlself=yes
+           elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+             dlself=yes
+           else
+             dlself=needless
+             export_dynamic=yes
+           fi
+           prev=
+           continue
+           ;;
+         *)
+           if test "$prev" = dlfiles; then
+             dlfiles="$dlfiles $arg"
+           else
+             dlprefiles="$dlprefiles $arg"
+           fi
+           prev=
+           ;;
+         esac
+         ;;
+       expsyms)
+         export_symbols="$arg"
+         if test ! -f "$arg"; then
+           $echo "$modename: symbol file \`$arg' does not exist"
+           exit 1
+         fi
+         prev=
+         continue
+         ;;
+       expsyms_regex)
+         export_symbols_regex="$arg"
+         prev=
+         continue
+         ;;
+       release)
+         release="-$arg"
+         prev=
+         continue
+         ;;
+       rpath | xrpath)
+         # We need an absolute path.
+         case "$arg" in
+         [\\/]* | [A-Za-z]:[\\/]*) ;;
+         *)
+           $echo "$modename: only absolute run-paths are allowed" 1>&2
+           exit 1
+           ;;
+         esac
+         if test "$prev" = rpath; then
+           case "$rpath " in
+           *" $arg "*) ;;
+           *) rpath="$rpath $arg" ;;
+           esac
+         else
+           case "$xrpath " in
+           *" $arg "*) ;;
+           *) xrpath="$xrpath $arg" ;;
+           esac
+         fi
+         prev=
+         continue
+         ;;
+       *)
+         eval "$prev=\"\$arg\""
+         prev=
+         continue
+         ;;
+       esac
+      fi
+
+      prevarg="$arg"
+
+      case "$arg" in
+      -all-static)
+       if test -n "$link_static_flag"; then
+         compile_command="$compile_command $link_static_flag"
+         finalize_command="$finalize_command $link_static_flag"
+       fi
+       continue
+       ;;
+
+      -allow-undefined)
+       # FIXME: remove this flag sometime in the future.
+       $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2
+       continue
+       ;;
+
+      -avoid-version)
+       avoid_version=yes
+       continue
+       ;;
+
+      -dlopen)
+       prev=dlfiles
+       continue
+       ;;
+
+      -dlpreopen)
+       prev=dlprefiles
+       continue
+       ;;
+
+      -export-dynamic)
+       export_dynamic=yes
+       continue
+       ;;
+
+      -export-symbols | -export-symbols-regex)
+       if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+         $echo "$modename: not more than one -exported-symbols argument allowed"
+         exit 1
+       fi
+       if test "X$arg" = "X-export-symbols"; then
+         prev=expsyms
+       else
+         prev=expsyms_regex
+       fi
+       continue
+       ;;
+
+      -L*)
+       dir=`$echo "X$arg" | $Xsed -e 's/^-L//'`
+       # We need an absolute path.
+       case "$dir" in
+       [\\/]* | [A-Za-z]:[\\/]*) ;;
+       *)
+         absdir=`cd "$dir" && pwd`
+         if test -z "$absdir"; then
+           $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2
+           exit 1
+         fi
+         dir="$absdir"
+         ;;
+       esac
+       case " $deplibs " in
+       *" $arg "*) ;;
+       *) deplibs="$deplibs $arg";;
+       esac
+       case " $lib_search_path " in
+       *" $dir "*) ;;
+       *) lib_search_path="$lib_search_path $dir";;
+       esac
+       case "$host" in
+       *-*-cygwin* | *-*-mingw* | *-*-os2*)
+         dllsearchdir=`cd "$dir" && pwd || echo "$dir"`
+         case ":$dllsearchpath:" in
+         ::) dllsearchpath="$dllsearchdir";;
+         *":$dllsearchdir:"*) ;;
+         *) dllsearchpath="$dllsearchpath:$dllsearchdir";;
+         esac
+         ;;
+       esac
+       ;;
+
+      -l*)
+       if test "$arg" = "-lc"; then
+         case "$host" in
+         *-*-cygwin* | *-*-mingw* | *-*-os2* | *-*-beos*)
+           # These systems don't actually have c library (as such)
+           continue
+           ;;
+         esac
+       elif test "$arg" = "-lm"; then
+         case "$host" in
+         *-*-cygwin* | *-*-beos*)
+           # These systems don't actually have math library (as such)
+           continue
+           ;;
+         esac
+       fi
+       deplibs="$deplibs $arg"
+       ;;
+
+      -module)
+       module=yes
+       continue
+       ;;
+
+      -no-undefined)
+       allow_undefined=no
+       continue
+       ;;
+
+      -o) prev=output ;;
+
+      -release)
+       prev=release
+       continue
+       ;;
+
+      -rpath)
+       prev=rpath
+       continue
+       ;;
+
+      -R)
+       prev=xrpath
+       continue
+       ;;
+
+      -R*)
+       dir=`$echo "X$arg" | $Xsed -e 's/^-R//'`
+       # We need an absolute path.
+       case "$dir" in
+       [\\/]* | [A-Za-z]:[\\/]*) ;;
+       *)
+         $echo "$modename: only absolute run-paths are allowed" 1>&2
+         exit 1
+         ;;
+       esac
+       case "$xrpath " in
+       *" $dir "*) ;;
+       *) xrpath="$xrpath $dir" ;;
+       esac
+       continue
+       ;;
+
+      -static)
+       # If we have no pic_flag, then this is the same as -all-static.
+       if test -z "$pic_flag" && test -n "$link_static_flag"; then
+         compile_command="$compile_command $link_static_flag"
+         finalize_command="$finalize_command $link_static_flag"
+       fi
+       continue
+       ;;
+
+      -thread-safe)
+       thread_safe=yes
+       continue
+       ;;
+
+      -version-info)
+       prev=vinfo
+       continue
+       ;;
+
+      # Some other compiler flag.
+      -* | +*)
+       # Unknown arguments in both finalize_command and compile_command need
+       # to be aesthetically quoted because they are evaled later.
+       arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+       case "$arg" in
+       *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \   ]*|*]*)
+         arg="\"$arg\""
+         ;;
+       esac
+       ;;
+
+      *.o | *.obj | *.a | *.lib)
+       # A standard object.
+       objs="$objs $arg"
+       ;;
+
+      *.lo)
+       # A library object.
+       if test "$prev" = dlfiles; then
+         dlfiles="$dlfiles $arg"
+         if test "$build_libtool_libs" = yes && test "$dlopen" = yes; then
+           prev=
+           continue
+         else
+           # If libtool objects are unsupported, then we need to preload.
+           prev=dlprefiles
+         fi
+       fi
+
+       if test "$prev" = dlprefiles; then
+         # Preload the old-style object.
+         dlprefiles="$dlprefiles "`$echo "X$arg" | $Xsed -e "$lo2o"`
+         prev=
+       fi
+       libobjs="$libobjs $arg"
+       ;;
+
+      *.la)
+       # A libtool-controlled library.
+
+       dlname=
+       libdir=
+       library_names=
+       old_library=
+
+       # Check to see that this really is a libtool archive.
+       if (sed -e '2q' $arg | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+       else
+         $echo "$modename: \`$arg' is not a valid libtool archive" 1>&2
+         exit 1
+       fi
+
+       # If the library was installed with an old release of libtool,
+       # it will not redefine variable installed.
+       installed=yes
+
+       # Read the .la file
+       # If there is no directory component, then add one.
+       case "$arg" in
+       */* | *\\*) . $arg ;;
+       *) . ./$arg ;;
+       esac
+
+       # Get the name of the library we link against.
+       linklib=
+       for l in $old_library $library_names; do
+         linklib="$l"
+       done
+
+       if test -z "$linklib"; then
+         $echo "$modename: cannot find name of link library for \`$arg'" 1>&2
+         exit 1
+       fi
+
+       # Find the relevant object directory and library name.
+       name=`$echo "X$arg" | $Xsed -e 's%^.*/%%' -e 's/\.la$//' -e 's/^lib//'`
+
+       if test "X$installed" = Xyes; then
+         dir="$libdir"
+       else
+         dir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'`
+         if test "X$dir" = "X$arg"; then
+           dir="$objdir"
+         else
+           dir="$dir/$objdir"
+         fi
+       fi
+
+       if test -n "$dependency_libs"; then
+         # Extract -R from dependency_libs
+         temp_deplibs=
+         for deplib in $dependency_libs; do
+           case "$deplib" in
+           -R*) temp_xrpath=`$echo "X$deplib" | $Xsed -e 's/^-R//'`
+                case " $rpath $xrpath " in
+                *" $temp_xrpath "*) ;;
+                *) xrpath="$xrpath $temp_xrpath";;
+                esac;;
+           -L*) case "$compile_command $temp_deplibs " in
+                *" $deplib "*) ;;
+                *) temp_deplibs="$temp_deplibs $deplib";;
+                esac;;
+           *) temp_deplibs="$temp_deplibs $deplib";;
+           esac
+         done
+         dependency_libs="$temp_deplibs"
+       fi
+
+       if test -z "$libdir"; then
+         # It is a libtool convenience library, so add in its objects.
+         convenience="$convenience $dir/$old_library"
+         old_convenience="$old_convenience $dir/$old_library"
+         deplibs="$deplibs$dependency_libs"
+         compile_command="$compile_command $dir/$old_library$dependency_libs"
+         finalize_command="$finalize_command $dir/$old_library$dependency_libs"
+         continue
+       fi
+
+       # This library was specified with -dlopen.
+       if test "$prev" = dlfiles; then
+         dlfiles="$dlfiles $arg"
+         if test -z "$dlname" || test "$dlopen" != yes || test "$build_libtool_libs" = no; then
+           # If there is no dlname, no dlopen support or we're linking statically,
+           # we need to preload.
+           prev=dlprefiles
+         else
+           # We should not create a dependency on this library, but we
+           # may need any libraries it requires.
+           compile_command="$compile_command$dependency_libs"
+           finalize_command="$finalize_command$dependency_libs"
+           prev=
+           continue
+         fi
+       fi
+
+       # The library was specified with -dlpreopen.
+       if test "$prev" = dlprefiles; then
+         # Prefer using a static library (so that no silly _DYNAMIC symbols
+         # are required to link).
+         if test -n "$old_library"; then
+           dlprefiles="$dlprefiles $dir/$old_library"
+         else
+           dlprefiles="$dlprefiles $dir/$linklib"
+         fi
+         prev=
+       fi
+
+       if test -n "$library_names" &&
+          { test "$prefer_static_libs" = no || test -z "$old_library"; }; then
+         link_against_libtool_libs="$link_against_libtool_libs $arg"
+         if test -n "$shlibpath_var"; then
+           # Make sure the rpath contains only unique directories.
+           case "$temp_rpath " in
+           *" $dir "*) ;;
+           *) temp_rpath="$temp_rpath $dir" ;;
+           esac
+         fi
+
+         # We need an absolute path.
+         case "$dir" in
+         [\\/] | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+         *)
+           absdir=`cd "$dir" && pwd`
+           if test -z "$absdir"; then
+             $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2
+             exit 1
+           fi
+           ;;
+         esac
+         
+         # This is the magic to use -rpath.
+         # Skip directories that are in the system default run-time
+         # search path, unless they have been requested with -R.
+         case " $sys_lib_dlsearch_path " in
+         *" $absdir "*) ;;
+         *)
+           case "$compile_rpath " in
+           *" $absdir "*) ;;
+           *) compile_rpath="$compile_rpath $absdir" 
+           esac
+           ;;
+         esac
+
+         case " $sys_lib_dlsearch_path " in
+         *" $libdir "*) ;;
+         *)
+           case "$finalize_rpath " in
+           *" $libdir "*) ;;
+           *) finalize_rpath="$finalize_rpath $libdir"
+           esac
+           ;;
+         esac
+
+         lib_linked=yes
+         case "$hardcode_action" in
+         immediate | unsupported)
+           if test "$hardcode_direct" = no; then
+             compile_command="$compile_command $dir/$linklib"
+             deplibs="$deplibs $dir/$linklib"
+             case "$host" in
+             *-*-cygwin* | *-*-mingw* | *-*-os2*)
+               dllsearchdir=`cd "$dir" && pwd || echo "$dir"`
+               if test -n "$dllsearchpath"; then
+                 dllsearchpath="$dllsearchpath:$dllsearchdir"
+               else
+                 dllsearchpath="$dllsearchdir"
+               fi
+               ;;
+             esac
+           elif test "$hardcode_minus_L" = no; then
+             case "$host" in
+             *-*-sunos*)
+               compile_shlibpath="$compile_shlibpath$dir:"
+               ;;
+             esac
+             case "$compile_command " in
+             *" -L$dir "*) ;;
+             *) compile_command="$compile_command -L$dir";;
+             esac
+             compile_command="$compile_command -l$name"
+             deplibs="$deplibs -L$dir -l$name"
+           elif test "$hardcode_shlibpath_var" = no; then
+             case ":$compile_shlibpath:" in
+             *":$dir:"*) ;;
+             *) compile_shlibpath="$compile_shlibpath$dir:";;
+             esac
+             compile_command="$compile_command -l$name"
+             deplibs="$deplibs -l$name"
+           else
+             lib_linked=no
+           fi
+           ;;
+
+         relink)
+           if test "$hardcode_direct" = yes; then
+             compile_command="$compile_command $absdir/$linklib"
+             deplibs="$deplibs $absdir/$linklib"
+           elif test "$hardcode_minus_L" = yes; then
+             case "$compile_command " in
+             *" -L$absdir "*) ;;
+             *) compile_command="$compile_command -L$absdir";;
+             esac
+             compile_command="$compile_command -l$name"
+             deplibs="$deplibs -L$absdir -l$name"
+           elif test "$hardcode_shlibpath_var" = yes; then
+             case ":$compile_shlibpath:" in
+             *":$absdir:"*) ;;
+             *) compile_shlibpath="$compile_shlibpath$absdir:";;
+             esac
+             compile_command="$compile_command -l$name"
+             deplibs="$deplibs -l$name"
+           else
+             lib_linked=no
+           fi
+           ;;
+
+         *)
+           lib_linked=no
+           ;;
+         esac
+
+         if test "$lib_linked" != yes; then
+           $echo "$modename: configuration error: unsupported hardcode properties"
+           exit 1
+         fi
+
+         # Finalize command for both is simple: just hardcode it.
+         if test "$hardcode_direct" = yes; then
+           finalize_command="$finalize_command $libdir/$linklib"
+         elif test "$hardcode_minus_L" = yes; then
+           case "$finalize_command " in
+           *" -L$libdir "*) ;;
+           *) finalize_command="$finalize_command -L$libdir";;
+           esac
+           finalize_command="$finalize_command -l$name"
+         elif test "$hardcode_shlibpath_var" = yes; then
+           case ":$finalize_shlibpath:" in
+           *":$libdir:"*) ;;
+           *) finalize_shlibpath="$finalize_shlibpath$libdir:";;
+           esac
+           finalize_command="$finalize_command -l$name"
+         else
+           # We cannot seem to hardcode it, guess we'll fake it.
+           case "$finalize_command " in
+           *" -L$dir "*) ;;
+           *) finalize_command="$finalize_command -L$libdir";;
+           esac
+           finalize_command="$finalize_command -l$name"
+         fi
+       else
+         # Transform directly to old archives if we don't build new libraries.
+         if test -n "$pic_flag" && test -z "$old_library"; then
+           $echo "$modename: cannot find static library for \`$arg'" 1>&2
+           exit 1
+         fi
+
+         # Here we assume that one of hardcode_direct or hardcode_minus_L
+         # is not unsupported.  This is valid on all known static and
+         # shared platforms.
+         if test "$hardcode_direct" != unsupported; then
+           test -n "$old_library" && linklib="$old_library"
+           compile_command="$compile_command $dir/$linklib"
+           finalize_command="$finalize_command $dir/$linklib"
+         else
+           case "$compile_command " in
+           *" -L$dir "*) ;;
+           *) compile_command="$compile_command -L$dir";;
+           esac
+           compile_command="$compile_command -l$name"
+           case "$finalize_command " in
+           *" -L$dir "*) ;;
+           *) finalize_command="$finalize_command -L$dir";;
+           esac
+           finalize_command="$finalize_command -l$name"
+         fi
+       fi
+
+       # Add in any libraries that this one depends upon.
+       compile_command="$compile_command$dependency_libs"
+       finalize_command="$finalize_command$dependency_libs"
+       continue
+       ;;
+
+      # Some other compiler argument.
+      *)
+       # Unknown arguments in both finalize_command and compile_command need
+       # to be aesthetically quoted because they are evaled later.
+       arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+       case "$arg" in
+       *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \   ]*|*]*)
+         arg="\"$arg\""
+         ;;
+       esac
+       ;;
+      esac
+
+      # Now actually substitute the argument into the commands.
+      if test -n "$arg"; then
+       compile_command="$compile_command $arg"
+       finalize_command="$finalize_command $arg"
+      fi
+    done
+
+    if test -n "$prev"; then
+      $echo "$modename: the \`$prevarg' option requires an argument" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+      eval arg=\"$export_dynamic_flag_spec\"
+      compile_command="$compile_command $arg"
+      finalize_command="$finalize_command $arg"
+    fi
+
+    oldlibs=
+    # calculate the name of the file, without its directory
+    outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'`
+    libobjs_save="$libobjs"
+
+    case "$output" in
+    "")
+      $echo "$modename: you must specify an output file" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+      ;;
+
+    *.a | *.lib)
+      if test -n "$link_against_libtool_libs"; then
+       $echo "$modename: error: cannot link libtool libraries into archives" 1>&2
+       exit 1
+      fi
+
+      if test -n "$deplibs"; then
+       $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2
+      fi
+
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+       $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$rpath"; then
+       $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$xrpath"; then
+       $echo "$modename: warning: \`-R' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$vinfo"; then
+       $echo "$modename: warning: \`-version-info' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$release"; then
+       $echo "$modename: warning: \`-release' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+       $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2
+      fi
+
+      # Now set the variables for building old libraries.
+      build_libtool_libs=no
+      oldlibs="$output"
+      ;;
+
+    *.la)
+      # Make sure we only generate libraries of the form `libNAME.la'.
+      case "$outputname" in
+      lib*)
+       name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'`
+       eval libname=\"$libname_spec\"
+       ;;
+      *)
+       if test "$module" = no; then
+         $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+       fi
+       if test "$need_lib_prefix" != no; then
+         # Add the "lib" prefix for modules if required
+         name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'`
+         eval libname=\"$libname_spec\"
+       else
+         libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'`
+       fi
+       ;;
+      esac
+
+      output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'`
+      if test "X$output_objdir" = "X$output"; then
+       output_objdir="$objdir"
+      else
+       output_objdir="$output_objdir/$objdir"
+      fi
+
+      if test -n "$objs"; then
+       $echo "$modename: cannot build libtool library \`$output' from non-libtool objects:$objs" 2>&1
+       exit 1
+      fi
+
+      # How the heck are we supposed to write a wrapper for a shared library?
+      if test -n "$link_against_libtool_libs"; then
+        $echo "$modename: error: cannot link shared libraries into libtool libraries" 1>&2
+        exit 1
+      fi
+
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+       $echo "$modename: warning: \`-dlopen' is ignored for libtool libraries" 1>&2
+      fi
+
+      set dummy $rpath
+      if test $# -gt 2; then
+       $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2
+      fi
+      install_libdir="$2"
+
+      oldlibs=
+      if test -z "$rpath"; then
+       if test "$build_libtool_libs" = yes; then
+         # Building a libtool convenience library.
+         libext=al
+         oldlibs="$output_objdir/$libname.$libext $oldlibs"
+         build_libtool_libs=convenience
+         build_old_libs=yes
+       fi
+       dependency_libs="$deplibs"
+
+       if test -n "$vinfo"; then
+         $echo "$modename: warning: \`-version-info' is ignored for convenience libraries" 1>&2
+       fi
+
+       if test -n "$release"; then
+         $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2
+       fi
+      else
+
+       # Parse the version information argument.
+       IFS="${IFS=     }"; save_ifs="$IFS"; IFS=':'
+       set dummy $vinfo 0 0 0
+       IFS="$save_ifs"
+
+       if test -n "$8"; then
+         $echo "$modename: too many parameters to \`-version-info'" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+       fi
+
+       current="$2"
+       revision="$3"
+       age="$4"
+
+       # Check that each of the things are valid numbers.
+       case "$current" in
+       0 | [1-9] | [1-9][0-9]*) ;;
+       *)
+         $echo "$modename: CURRENT \`$current' is not a nonnegative integer" 1>&2
+         $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+         exit 1
+         ;;
+       esac
+
+       case "$revision" in
+       0 | [1-9] | [1-9][0-9]*) ;;
+       *)
+         $echo "$modename: REVISION \`$revision' is not a nonnegative integer" 1>&2
+         $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+         exit 1
+         ;;
+       esac
+
+       case "$age" in
+       0 | [1-9] | [1-9][0-9]*) ;;
+       *)
+         $echo "$modename: AGE \`$age' is not a nonnegative integer" 1>&2
+         $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+         exit 1
+         ;;
+       esac
+
+       if test $age -gt $current; then
+         $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2
+         $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+         exit 1
+       fi
+
+       # Calculate the version variables.
+       major=
+       versuffix=
+       verstring=
+       case "$version_type" in
+       none) ;;
+
+       irix)
+         major=`expr $current - $age + 1`
+         versuffix="$major.$revision"
+         verstring="sgi$major.$revision"
+
+         # Add in all the interfaces that we are compatible with.
+         loop=$revision
+         while test $loop != 0; do
+           iface=`expr $revision - $loop`
+           loop=`expr $loop - 1`
+           verstring="sgi$major.$iface:$verstring"
+         done
+         ;;
+
+       linux)
+         major=.`expr $current - $age`
+         versuffix="$major.$age.$revision"
+         ;;
+
+       osf)
+         major=`expr $current - $age`
+         versuffix=".$current.$age.$revision"
+         verstring="$current.$age.$revision"
+
+         # Add in all the interfaces that we are compatible with.
+         loop=$age
+         while test $loop != 0; do
+           iface=`expr $current - $loop`
+           loop=`expr $loop - 1`
+           verstring="$verstring:${iface}.0"
+         done
+
+         # Make executables depend on our current version.
+         verstring="$verstring:${current}.0"
+         ;;
+
+       sunos)
+         major=".$current"
+         versuffix=".$current.$revision"
+         ;;
+
+       freebsd-aout)
+         major=".$current"
+         versuffix=".$current.$revision";
+         ;;
+
+       freebsd-elf)
+         major=".$current"
+         versuffix=".$current";
+         ;;
+
+       windows)
+         # Like Linux, but with '-' rather than '.', since we only
+         # want one extension on Windows 95.
+         major=`expr $current - $age`
+         versuffix="-$major-$age-$revision"
+         ;;
+
+       *)
+         $echo "$modename: unknown library version type \`$version_type'" 1>&2
+         echo "Fatal configuration error.  See the $PACKAGE docs for more information." 1>&2
+         exit 1
+         ;;
+       esac
+
+       # Clear the version info if we defaulted, and they specified a release.
+       if test -z "$vinfo" && test -n "$release"; then
+         major=
+         verstring="0.0"
+         if test "$need_version" = no; then
+           versuffix=
+         else
+           versuffix=".0.0"
+         fi
+       fi
+
+       # Remove version info from name if versioning should be avoided
+       if test "$avoid_version" = yes && test "$need_version" = no; then
+         major=
+         versuffix=
+         verstring=""
+       fi
+       
+       # Check to see if the archive will have undefined symbols.
+       if test "$allow_undefined" = yes; then
+         if test "$allow_undefined_flag" = unsupported; then
+           $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2
+           build_libtool_libs=no
+           build_old_libs=yes
+         fi
+       else
+         # Don't allow undefined symbols.
+         allow_undefined_flag="$no_undefined_flag"
+       fi
+
+       dependency_libs="$deplibs"
+       case "$host" in
+       *-*-cygwin* | *-*-mingw* | *-*-os2* | *-*-beos*)
+         # these systems don't actually have a c library (as such)!
+         ;;
+       *)
+         # Add libc to deplibs on all other systems.
+         deplibs="$deplibs -lc"
+         ;;
+       esac
+      fi
+
+      # Create the output directory, or remove our outputs if we need to.
+      if test -d $output_objdir; then
+       $show "${rm}r $output_objdir/$outputname $output_objdir/$libname.* $output_objdir/${libname}${release}.*"
+       $run ${rm}r $output_objdir/$outputname $output_objdir/$libname.* $output_objdir/${libname}${release}.*
+      else
+       $show "$mkdir $output_objdir"
+       $run $mkdir $output_objdir
+       status=$?
+       if test $status -ne 0 && test ! -d $output_objdir; then
+         exit $status
+       fi
+      fi
+
+      # Now set the variables for building old libraries.
+      if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+       oldlibs="$oldlibs $output_objdir/$libname.$libext"
+
+       # Transform .lo files to .o files.
+       oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP`
+      fi
+
+      if test "$build_libtool_libs" = yes; then
+       # Transform deplibs into only deplibs that can be linked in shared.
+       name_save=$name
+       libname_save=$libname
+       release_save=$release
+       versuffix_save=$versuffix
+       major_save=$major
+       # I'm not sure if I'm treating the release correctly.  I think
+       # release should show up in the -l (ie -lgmp5) so we don't want to
+       # add it in twice.  Is that correct?
+       release=""
+       versuffix=""
+       major=""
+       newdeplibs=
+       droppeddeps=no
+       case "$deplibs_check_method" in
+       pass_all)
+         # Don't check for shared/static.  Everything works.
+         # This might be a little naive.  We might want to check
+         # whether the library exists or not.  But this is on
+         # osf3 & osf4 and I'm not really sure... Just
+         # implementing what was already the behaviour.
+         newdeplibs=$deplibs
+         ;;
+       test_compile)
+         # This code stresses the "libraries are programs" paradigm to its
+         # limits. Maybe even breaks it.  We compile a program, linking it
+         # against the deplibs as a proxy for the library.  Then we can check
+         # whether they linked in statically or dynamically with ldd.
+         $rm conftest.c
+         cat > conftest.c <<EOF
+         int main() { return 0; }
+EOF
+         $rm conftest
+         $C_compiler -o conftest conftest.c $deplibs
+         if test $? -eq 0 ; then
+           ldd_output=`ldd conftest`
+           for i in $deplibs; do
+             name="`expr $i : '-l\(.*\)'`"
+             # If $name is empty we are operating on a -L argument.
+             if test "$name" != "" ; then
+               libname=`eval \\$echo \"$libname_spec\"`
+               deplib_matches=`eval \\$echo \"$library_names_spec\"`
+               set dummy $deplib_matches
+               deplib_match=$2
+               if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+                 newdeplibs="$newdeplibs $i"
+               else
+                 droppeddeps=yes
+                 echo
+                 echo "*** Warning: This library needs some functionality provided by $i."
+                 echo "*** I have the capability to make that library automatically link in when"
+                 echo "*** you link to this library.  But I can only do this if you have a"
+                 echo "*** shared version of the library, which you do not appear to have."
+               fi
+             else
+               newdeplibs="$newdeplibs $i"
+             fi
+           done
+         else
+           # Error occured in the first compile.  Let's try to salvage the situation:
+           # Compile a seperate program for each library.
+           for i in $deplibs; do
+             name="`expr $i : '-l\(.*\)'`"
+            # If $name is empty we are operating on a -L argument.
+             if test "$name" != "" ; then
+               $rm conftest
+               $C_compiler -o conftest conftest.c $i
+               # Did it work?
+               if test $? -eq 0 ; then
+                 ldd_output=`ldd conftest`
+                 libname=`eval \\$echo \"$libname_spec\"`
+                 deplib_matches=`eval \\$echo \"$library_names_spec\"`
+                 set dummy $deplib_matches
+                 deplib_match=$2
+                 if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+                   newdeplibs="$newdeplibs $i"
+                 else
+                   droppeddeps=yes
+                   echo
+                   echo "*** Warning: This library needs some functionality provided by $i."
+                   echo "*** I have the capability to make that library automatically link in when"
+                   echo "*** you link to this library.  But I can only do this if you have a"
+                   echo "*** shared version of the library, which you do not appear to have."
+                 fi
+               else
+                 droppeddeps=yes
+                 echo
+                 echo "*** Warning!  Library $i is needed by this library but I was not able to"
+                 echo "***  make it link in!  You will probably need to install it or some"
+                 echo "*** library that it depends on before this library will be fully"
+                 echo "*** functional.  Installing it before continuing would be even better."
+               fi
+             else
+               newdeplibs="$newdeplibs $i"
+             fi
+           done
+         fi
+         ;;
+       file_magic*)
+         set dummy $deplibs_check_method
+         file_magic_regex="`expr \"$deplibs_check_method\" : \"$2 \(.*\)\"`"
+         for a_deplib in $deplibs; do
+           name="`expr $a_deplib : '-l\(.*\)'`"
+           # If $name is empty we are operating on a -L argument.
+           if test "$name" != "" ; then
+             libname=`eval \\$echo \"$libname_spec\"`
+             for i in $lib_search_path; do
+                   potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+                   for potent_lib in $potential_libs; do
+                     # Follow soft links.
+                     if ls -lLd "$potlib" 2>/dev/null \
+                        | grep " -> " >/dev/null; then
+                       continue 
+                     fi
+                     # The statement above tries to avoid entering an
+                     # endless loop below, in case of cyclic links.
+                     # We might still enter an endless loop, since a link
+                     # loop can be closed while we follow links,
+                     # but so what?
+                     potlib="$potent_lib"
+                     while test -h "$potlib" 2>/dev/null; do
+                       potliblink=`ls -ld $potlib | sed 's/.* -> //'`
+                       case "$potliblink" in
+                       [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
+                       *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";;
+                       esac
+                     done
+                     if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \
+                        | sed 10q \
+                        | egrep "$file_magic_regex" > /dev/null; then
+                       newdeplibs="$newdeplibs $a_deplib"
+                       a_deplib=""
+                       break 2
+                     fi
+                   done
+             done
+             if test -n "$a_deplib" ; then
+               droppeddeps=yes
+               echo
+               echo "*** Warning: This library needs some functionality provided by $a_deplib."
+               echo "*** I have the capability to make that library automatically link in when"
+               echo "*** you link to this library.  But I can only do this if you have a"
+               echo "*** shared version of the library, which you do not appear to have."
+             fi
+           else
+             # Add a -L argument.
+             newdeplibs="$newdeplibs $a_deplib"
+           fi
+         done # Gone through all deplibs.
+         ;;
+       none | unknown | *)
+         newdeplibs=""
+         if $echo "X $deplibs" | $Xsed -e 's/ -lc$//' \
+              -e 's/ -[LR][^ ]*//g' -e 's/[    ]//g' |
+            grep . >/dev/null; then
+           echo
+           if test "X$deplibs_check_method" = "Xnone"; then
+             echo "*** Warning: inter-library dependencies are not supported in this platform."
+           else
+             echo "*** Warning: inter-library dependencies are not known to be supported."
+           fi
+           echo "*** All declared inter-library dependencies are being dropped."
+           droppeddeps=yes
+         fi
+         ;;
+       esac
+       versuffix=$versuffix_save
+       major=$major_save
+       release=$release_save
+       libname=$libname_save
+       name=$name_save
+
+       if test "$droppeddeps" = yes; then
+         if test "$module" = yes; then
+           echo
+           echo "*** Warning: libtool could not satisfy all declared inter-library"
+           echo "*** dependencies of module $libname.  Therefore, libtool will create"
+           echo "*** a static module, that should work as long as the dlopening"
+           echo "*** application is linked with the -dlopen flag."
+           if test -z "$global_symbol_pipe"; then
+             echo
+             echo "*** However, this would only work if libtool was able to extract symbol"
+             echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+             echo "*** not find such a program.  So, this module is probably useless."
+             echo "*** \`nm' from GNU binutils and a full rebuild may help."
+           fi
+           if test "$build_old_libs" = no; then
+             oldlibs="$output_objdir/$libname.$libext"
+             build_libtool_libs=module
+             build_old_libs=yes
+           else
+             build_libtool_libs=no
+           fi
+         else
+           echo "*** The inter-library dependencies that have been dropped here will be"
+           echo "*** automatically added whenever a program is linked with this library"
+           echo "*** or is declared to -dlopen it."
+         fi
+       fi
+       # Done checking deplibs!
+       deplibs=$newdeplibs
+      fi
+
+      # All the library-specific variables (install_libdir is set above).
+      library_names=
+      old_library=
+      dlname=
+      
+      # Test again, we may have decided not to build it any more
+      if test "$build_libtool_libs" = yes; then
+       # Get the real and link names of the library.
+       eval library_names=\"$library_names_spec\"
+       set dummy $library_names
+       realname="$2"
+       shift; shift
+
+       if test -n "$soname_spec"; then
+         eval soname=\"$soname_spec\"
+       else
+         soname="$realname"
+       fi
+
+       lib="$output_objdir/$realname"
+       for link
+       do
+         linknames="$linknames $link"
+       done
+
+       # Ensure that we have .o objects for linkers which dislike .lo
+       # (e.g. aix) incase we are running --disable-static
+       for obj in $libobjs; do
+         oldobj=`$echo "X$obj" | $Xsed -e "$lo2o"`
+         if test ! -f $oldobj; then
+           $show "${LN_S} $obj $oldobj"
+           $run ${LN_S} $obj $oldobj || exit $?
+         fi
+       done
+
+       # Use standard objects if they are pic
+       test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+
+       if test -n "$whole_archive_flag_spec"; then
+         if test -n "$convenience"; then
+           eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+         fi
+       else
+         gentop="$output_objdir/${outputname}x"
+         $show "${rm}r $gentop"
+         $run ${rm}r "$gentop"
+         $show "mkdir $gentop"
+         $run mkdir "$gentop"
+         status=$?
+         if test $status -ne 0 && test ! -d "$gentop"; then
+           exit $status
+         fi
+         generated="$generated $gentop"
+         
+         for xlib in $convenience; do
+           # Extract the objects.
+           case "$xlib" in
+           [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;;
+           *) xabs=`pwd`"/$xlib" ;;
+           esac
+           xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'`
+           xdir="$gentop/$xlib"
+
+           $show "${rm}r $xdir"
+           $run ${rm}r "$xdir"
+           $show "mkdir $xdir"
+           $run mkdir "$xdir"
+           status=$?
+           if test $status -ne 0 && test ! -d "$xdir"; then
+             exit $status
+           fi
+           $show "(cd $xdir && $AR x $xabs)"
+           $run eval "(cd \$xdir && $AR x \$xabs)" || exit $?
+
+           libobjs="$libobjs "`find $xdir -name \*.o -print -o -name \*.lo -print | $NL2SP`
+         done
+       fi
+
+       if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+         eval flag=\"$thread_safe_flag_spec\"
+         linkopts="$linkopts $flag"
+       fi
+
+       # Prepare the list of exported symbols
+       if test -z "$export_symbols"; then
+         if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
+           $show "generating symbol list for \`$libname.la'"
+           export_symbols="$output_objdir/$libname.exp"
+           $run $rm $export_symbols
+           eval cmds=\"$export_symbols_cmds\"
+           IFS="${IFS=         }"; save_ifs="$IFS"; IFS='~'
+           for cmd in $cmds; do
+             IFS="$save_ifs"
+             $show "$cmd"
+             $run eval "$cmd" || exit $?
+           done
+           IFS="$save_ifs"
+           if test -n "$export_symbols_regex"; then
+             $show "egrep -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\""
+             $run eval 'egrep -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+             $show "$mv \"${export_symbols}T\" \"$export_symbols\""
+             $run eval '$mv "${export_symbols}T" "$export_symbols"'
+           fi
+         fi
+       fi
+
+       if test -n "$export_symbols" && test -n "$include_expsyms"; then
+         $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"'
+       fi
+
+       # Do each of the archive commands.
+       if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+         eval cmds=\"$archive_expsym_cmds\"
+       else
+         eval cmds=\"$archive_cmds\"
+       fi
+       IFS="${IFS=     }"; save_ifs="$IFS"; IFS='~'
+       for cmd in $cmds; do
+         IFS="$save_ifs"
+         $show "$cmd"
+         $run eval "$cmd" || exit $?
+       done
+       IFS="$save_ifs"
+
+       # Create links to the real library.
+       for linkname in $linknames; do
+         if test "$realname" != "$linkname"; then
+           $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)"
+           $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $?
+         fi
+       done
+
+       # If -module or -export-dynamic was specified, set the dlname.
+       if test "$module" = yes || test "$export_dynamic" = yes; then
+         # On all known operating systems, these are identical.
+         dlname="$soname"
+       fi
+      fi
+      ;;
+
+    *.lo | *.o | *.obj)
+      if test -n "$link_against_libtool_libs"; then
+       $echo "$modename: error: cannot link libtool libraries into objects" 1>&2
+       exit 1
+      fi
+
+      if test -n "$deplibs"; then
+       $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2
+      fi
+
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+       $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$rpath"; then
+       $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$xrpath"; then
+       $echo "$modename: warning: \`-R' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$vinfo"; then
+       $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$release"; then
+       $echo "$modename: warning: \`-release' is ignored for objects" 1>&2
+      fi
+
+      case "$output" in
+      *.lo)
+       if test -n "$objs"; then
+         $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2
+         exit 1
+       fi
+       libobj="$output"
+       obj=`$echo "X$output" | $Xsed -e "$lo2o"`
+       ;;
+      *)
+       libobj=
+       obj="$output"
+       ;;
+      esac
+
+      # Delete the old objects.
+      $run $rm $obj $libobj
+
+      # Create the old-style object.
+      reload_objs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`
+
+      output="$obj"
+      eval cmds=\"$reload_cmds\"
+      IFS="${IFS=      }"; save_ifs="$IFS"; IFS='~'
+      for cmd in $cmds; do
+       IFS="$save_ifs"
+       $show "$cmd"
+       $run eval "$cmd" || exit $?
+      done
+      IFS="$save_ifs"
+
+      # Exit if we aren't doing a library object file.
+      test -z "$libobj" && exit 0
+
+      if test "$build_libtool_libs" != yes; then
+       # Create an invalid libtool object if no PIC, so that we don't
+       # accidentally link it into a program.
+       $show "echo timestamp > $libobj"
+       $run eval "echo timestamp > $libobj" || exit $?
+       exit 0
+      fi
+
+      if test -n "$pic_flag"; then
+       # Only do commands if we really have different PIC objects.
+       reload_objs="$libobjs"
+       output="$libobj"
+       eval cmds=\"$reload_cmds\"
+       IFS="${IFS=     }"; save_ifs="$IFS"; IFS='~'
+       for cmd in $cmds; do
+         IFS="$save_ifs"
+         $show "$cmd"
+         $run eval "$cmd" || exit $?
+       done
+       IFS="$save_ifs"
+      else
+       # Just create a symlink.
+       $show $rm $libobj
+       $run $rm $libobj
+       $show "$LN_S $obj $libobj"
+       $run $LN_S $obj $libobj || exit $?
+      fi
+
+      exit 0
+      ;;
+
+    # Anything else should be a program.
+    *)
+      if test -n "$vinfo"; then
+       $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2
+      fi
+
+      if test -n "$release"; then
+       $echo "$modename: warning: \`-release' is ignored for programs" 1>&2
+      fi
+
+      if test "$preload" = yes; then
+       if test "$dlopen" = unknown && test "$dlopen_self" = unknown &&
+          test "$dlopen_self_static" = unknown; then
+         $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support."
+       fi 
+      fi
+    
+      if test -n "$rpath$xrpath"; then
+       # If the user specified any rpath flags, then add them.
+       for libdir in $rpath $xrpath; do
+         # This is the magic to use -rpath.
+         case "$compile_rpath " in
+         *" $libdir "*) ;;
+         *) compile_rpath="$compile_rpath $libdir" ;;
+         esac
+         case "$finalize_rpath " in
+         *" $libdir "*) ;;
+         *) finalize_rpath="$finalize_rpath $libdir" ;;
+         esac
+       done
+      fi
+
+      # Now hardcode the library paths
+      rpath=
+      hardcode_libdirs=
+      for libdir in $compile_rpath $finalize_rpath; do
+       if test -n "$hardcode_libdir_flag_spec"; then
+         if test -n "$hardcode_libdir_separator"; then
+           if test -z "$hardcode_libdirs"; then
+             hardcode_libdirs="$libdir"
+           else
+             # Just accumulate the unique libdirs.
+             case "$hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator" in
+             *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+               ;;
+             *)
+               hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+               ;;
+             esac
+           fi
+         else
+           eval flag=\"$hardcode_libdir_flag_spec\"
+           rpath="$rpath $flag"
+         fi
+       elif test -n "$runpath_var"; then
+         case "$perm_rpath " in
+         *" $libdir "*) ;;
+         *) perm_rpath="$perm_rpath $libdir" ;;
+         esac
+       fi
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+        test -n "$hardcode_libdirs"; then
+       libdir="$hardcode_libdirs"
+       eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      compile_rpath="$rpath"
+
+      rpath=
+      hardcode_libdirs=
+      for libdir in $finalize_rpath; do
+       if test -n "$hardcode_libdir_flag_spec"; then
+         if test -n "$hardcode_libdir_separator"; then
+           if test -z "$hardcode_libdirs"; then
+             hardcode_libdirs="$libdir"
+           else
+             # Just accumulate the unique libdirs.
+             case "$hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator" in
+             *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+               ;;
+             *)
+               hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+               ;;
+             esac
+           fi
+         else
+           eval flag=\"$hardcode_libdir_flag_spec\"
+           rpath="$rpath $flag"
+         fi
+       elif test -n "$runpath_var"; then
+         case "$finalize_perm_rpath " in
+         *" $libdir "*) ;;
+         *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;;
+         esac
+       fi
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+        test -n "$hardcode_libdirs"; then
+       libdir="$hardcode_libdirs"
+       eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      finalize_rpath="$rpath"
+
+      output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'`
+      if test "X$output_objdir" = "X$output"; then
+       output_objdir="$objdir"
+      else
+       output_objdir="$output_objdir/$objdir"
+      fi
+
+      # Create the binary in the object directory, then wrap it.
+      if test ! -d $output_objdir; then
+       $show "$mkdir $output_objdir"
+       $run $mkdir $output_objdir
+       status=$?
+       if test $status -ne 0 && test ! -d $output_objdir; then
+         exit $status
+       fi
+      fi
+
+      if test -n "$libobjs" && test "$build_old_libs" = yes; then
+       # Transform all the library objects into standard objects.
+       compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+       finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+      fi
+
+      dlsyms=
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" = yes; then
+       if test -n "$NM" && test -n "$global_symbol_pipe"; then
+         dlsyms="${outputname}S.c"
+       else
+         $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2
+       fi
+      fi
+
+      if test -n "$dlsyms"; then
+       case "$dlsyms" in
+       "") ;;
+       *.c)
+         # Discover the nlist of each of the dlfiles.
+         nlist="$output_objdir/${outputname}.nm"
+
+         $show "$rm $nlist ${nlist}S ${nlist}T"
+         $run $rm "$nlist" "${nlist}S" "${nlist}T"
+
+         # Parse the name list into a source file.
+         $show "creating $output_objdir/$dlsyms"
+
+         test -z "$run" && $echo > "$output_objdir/$dlsyms" "\
+/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */
+/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+/* Prevent the only kind of declaration conflicts we can make. */
+#define lt_preloaded_symbols some_other_symbol
+
+/* External symbol declarations for the compiler. */\
+"
+
+         if test "$dlself" = yes; then
+           $show "generating symbol list for \`$output'"
+
+           test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist"
+
+           # Add our own program objects to the symbol list.
+           progfiles=`$echo "X$objs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+           for arg in $progfiles; do
+             $show "extracting global C symbols from \`$arg'"
+             $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'"
+           done
+
+           if test -n "$exclude_expsyms"; then
+             $run eval 'egrep -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+             $run eval '$mv "$nlist"T "$nlist"'
+           fi
+           
+           if test -n "$export_symbols_regex"; then
+             $run eval 'egrep -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+             $run eval '$mv "$nlist"T "$nlist"'
+           fi
+
+           # Prepare the list of exported symbols
+           if test -z "$export_symbols"; then
+             export_symbols="$output_objdir/$output.exp"
+             $run $rm $export_symbols
+             $run eval "sed -n -e '/^: @PROGRAM@$/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+           else
+             $run eval "sed -e 's/\([][.*^$]\)/\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$output.exp"'
+             $run eval 'grep -f "$output_objdir/$output.exp" < "$nlist" > "$nlist"T'
+             $run eval 'mv "$nlist"T "$nlist"'
+           fi
+         fi
+
+         for arg in $dlprefiles; do
+           $show "extracting global C symbols from \`$arg'"
+           name=`echo "$arg" | sed -e 's%^.*/%%'`
+           $run eval 'echo ": $name " >> "$nlist"'
+           $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'"
+         done
+
+         if test -z "$run"; then
+           # Make sure we have at least an empty file.
+           test -f "$nlist" || : > "$nlist"
+
+           if test -n "$exclude_expsyms"; then
+             egrep -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+             $mv "$nlist"T "$nlist"
+           fi
+
+           # Try sorting and uniquifying the output.
+           if grep -v "^: " < "$nlist" | sort +2 | uniq > "$nlist"S; then
+             :
+           else
+             grep -v "^: " < "$nlist" > "$nlist"S
+           fi
+
+           if test -f "$nlist"S; then
+             eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"'
+           else
+             echo '/* NONE */' >> "$output_objdir/$dlsyms"
+           fi
+
+           $echo >> "$output_objdir/$dlsyms" "\
+
+#undef lt_preloaded_symbols
+
+#if defined (__STDC__) && __STDC__
+# define lt_ptr_t void *
+#else
+# define lt_ptr_t char *
+# define const
+#endif
+
+/* The mapping between symbol names and symbols. */
+const struct {
+  const char *name;
+  lt_ptr_t address;
+}
+lt_preloaded_symbols[] =
+{\
+"
+
+           sed -n -e 's/^: \([^ ]*\) $/  {\"\1\", (lt_ptr_t) 0},/p' \
+               -e 's/^. \([^ ]*\) \([^ ]*\)$/  {"\2", (lt_ptr_t) \&\2},/p' \
+                 < "$nlist" >> "$output_objdir/$dlsyms"
+
+           $echo >> "$output_objdir/$dlsyms" "\
+  {0, (lt_ptr_t) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+         fi
+
+         pic_flag_for_symtable=
+         case "$host" in
+         # compiling the symbol table file with pic_flag works around
+         # a FreeBSD bug that causes programs to crash when -lm is
+         # linked before any other PIC object.  But we must not use
+         # pic_flag when linking with -static.  The problem exists in
+         # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+         *-*-freebsd2*|*-*-freebsd3.0*)
+           case "$compile_command " in
+           *" -static "*) ;;
+           *) pic_flag_for_symtable=" $pic_flag -DPIC -DFREEBSD_WORKAROUND";;
+           esac
+         esac
+
+         # Now compile the dynamic symbol file.
+         $show "(cd $output_objdir && $C_compiler -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")"
+         $run eval '(cd $output_objdir && $C_compiler -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $?
+
+         # Clean up the generated files.
+         $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T"
+         $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T"
+
+         # Transform the symbol file into the correct name.
+         compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"`
+         finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"`
+         ;;
+       *)
+         $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2
+         exit 1
+         ;;
+       esac
+      else
+       # We keep going just in case the user didn't refer to
+       # lt_preloaded_symbols.  The linker will fail if global_symbol_pipe
+       # really was required.
+
+       # Nullify the symbol file.
+       compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"`
+       finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"`
+      fi
+
+      if test -z "$link_against_libtool_libs" || test "$build_libtool_libs" != yes; then
+       # Replace the output file specification.
+       compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+       link_command="$compile_command$compile_rpath"
+
+       # We have no uninstalled library dependencies, so finalize right now.
+       $show "$link_command"
+       $run eval "$link_command"
+       status=$?
+       
+       # Delete the generated files.
+       if test -n "$dlsyms"; then
+         $show "$rm $output_objdir/${outputname}S.${objext}"
+         $run $rm "$output_objdir/${outputname}S.${objext}"
+       fi
+
+       exit $status
+      fi
+
+      if test -n "$shlibpath_var"; then
+       # We should set the shlibpath_var
+       rpath=
+       for dir in $temp_rpath; do
+         case "$dir" in
+         [\\/]* | [A-Za-z]:[\\/]*)
+           # Absolute path.
+           rpath="$rpath$dir:"
+           ;;
+         *)
+           # Relative path: add a thisdir entry.
+           rpath="$rpath\$thisdir/$dir:"
+           ;;
+         esac
+       done
+       temp_rpath="$rpath"
+      fi
+
+      if test -n "$compile_shlibpath$finalize_shlibpath"; then
+       compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+      fi
+      if test -n "$finalize_shlibpath"; then
+       finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+      fi
+
+      compile_var=
+      finalize_var=
+      if test -n "$runpath_var"; then
+       if test -n "$perm_rpath"; then
+         # We should set the runpath_var.
+         rpath=
+         for dir in $perm_rpath; do
+           rpath="$rpath$dir:"
+         done
+         compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+       fi
+       if test -n "$finalize_perm_rpath"; then
+         # We should set the runpath_var.
+         rpath=
+         for dir in $finalize_perm_rpath; do
+           rpath="$rpath$dir:"
+         done
+         finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+       fi
+      fi
+
+      if test "$hardcode_action" = relink; then
+       # Fast installation is not supported
+       link_command="$compile_var$compile_command$compile_rpath"
+       relink_command="$finalize_var$finalize_command$finalize_rpath"
+       
+       $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2
+       $echo "$modename: \`$output' will be relinked during installation" 1>&2
+      else
+       if test "$fast_install" != no; then
+         link_command="$finalize_var$compile_command$finalize_rpath"
+         if test "$fast_install" = yes; then
+           relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'`
+         else
+           # fast_install is set to needless
+           relink_command=
+         fi
+       else
+         link_command="$compile_var$compile_command$compile_rpath"
+         relink_command="$finalize_var$finalize_command$finalize_rpath"
+       fi
+      fi
+
+      # Replace the output file specification.
+      link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+      
+      # Delete the old output files.
+      $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+      $show "$link_command"
+      $run eval "$link_command" || exit $?
+
+      # Now create the wrapper script.
+      $show "creating $output"
+
+      # Quote the relink command for shipping.
+      if test -n "$relink_command"; then
+       relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+      fi
+
+      # Quote $echo for shipping.
+      if test "X$echo" = "X$SHELL $0 --fallback-echo"; then
+       case "$0" in
+       [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $0 --fallback-echo";;
+       *) qecho="$SHELL `pwd`/$0 --fallback-echo";;
+       esac
+       qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"`
+      else
+       qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"`
+      fi
+
+      # Only actually do things if our run command is non-null.
+      if test -z "$run"; then
+       # win32 will think the script is a binary if it has
+       # a .exe suffix, so we strip it off here.
+       case $output in
+         *.exe) output=`echo $output|sed 's,.exe$,,'` ;;
+       esac
+       $rm $output
+       trap "$rm $output; exit 1" 1 2 15
+
+       $echo > $output "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e 1s/^X//'
+sed_quote_subst='$sed_quote_subst'
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test \"\${CDPATH+set}\" = set; then CDPATH=; export CDPATH; fi
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+  # install mode needs the following variable:
+  link_against_libtool_libs='$link_against_libtool_libs'
+else
+  # When we are sourced in execute mode, \$file and \$echo are already set.
+  if test \"\$libtool_execute_magic\" != \"$magic\"; then
+    echo=\"$qecho\"
+    file=\"\$0\"
+    # Make sure echo works.
+    if test \"X\$1\" = X--no-reexec; then
+      # Discard the --no-reexec flag, and continue.
+      shift
+    elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then
+      # Yippee, \$echo works!
+      :
+    else
+      # Restart under the correct shell, and then maybe \$echo will work.
+      exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"}
+    fi
+  fi\
+"
+       $echo >> $output "\
+
+  # Find the directory that this script lives in.
+  thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\`
+  test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+  # Follow symbolic links until we get to the real thisdir.
+  file=\`ls -ld \"\$file\" | sed -n 's/.*-> //p'\`
+  while test -n \"\$file\"; do
+    destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\`
+
+    # If there was a directory component, then change thisdir.
+    if test \"x\$destdir\" != \"x\$file\"; then
+      case \"\$destdir\" in
+      [\\/]* | [A-Za-z]:[\\/]*) thisdir=\"\$destdir\" ;;
+      *) thisdir=\"\$thisdir/\$destdir\" ;;
+      esac
+    fi
+
+    file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\`
+    file=\`ls -ld \"\$thisdir/\$file\" | sed -n 's/.*-> //p'\`
+  done
+
+  # Try to get the absolute directory name.
+  absdir=\`cd \"\$thisdir\" && pwd\`
+  test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+       if test "$fast_install" = yes; then
+         echo >> $output "\
+  program=lt-'$outputname'
+  progdir=\"\$thisdir/$objdir\"
+  
+  if test ! -f \"\$progdir/\$program\" || \\
+     { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | sed 1q\`; \\
+       test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+    file=\"\$\$-\$program\"
+
+    if test ! -d \"\$progdir\"; then
+      $mkdir \"\$progdir\"
+    else
+      $rm \"\$progdir/\$file\"
+    fi"
+
+         echo >> $output "\
+
+    # relink executable if necessary
+    if test -n \"\$relink_command\"; then
+      if (cd \"\$thisdir\" && eval \$relink_command); then :
+      else
+       $rm \"\$progdir/\$file\"
+       exit 1
+      fi
+    fi
+
+    $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+    { $rm \"\$progdir/\$program\";
+      $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+    $rm \"\$progdir/\$file\"
+  fi"
+       else
+         echo >> $output "\
+  program='$outputname'
+  progdir=\"\$thisdir/$objdir\"
+"
+       fi
+
+       echo >> $output "\
+
+  if test -f \"\$progdir/\$program\"; then"
+
+       # Export our shlibpath_var if we have one.
+       if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+         $echo >> $output "\
+    # Add our own library path to $shlibpath_var
+    $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+    # Some systems cannot cope with colon-terminated $shlibpath_var
+    # The second colon is a workaround for a bug in BeOS R4 sed
+    $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\`
+
+    export $shlibpath_var
+"
+       fi
+
+       # fixup the dll searchpath if we need to.
+       if test -n "$dllsearchpath"; then
+         $echo >> $output "\
+    # Add the dll search path components to the executable PATH
+    PATH=$dllsearchpath:\$PATH
+"
+       fi
+
+       $echo >> $output "\
+    if test \"\$libtool_execute_magic\" != \"$magic\"; then
+      # Run the actual program with our arguments.
+"
+       case $host in
+       *-*-cygwin* | *-*-mingw | *-*-os2*)
+         # win32 systems need to use the prog path for dll
+         # lookup to work
+         $echo >> $output "\
+      exec \$progdir\\\\\$program \${1+\"\$@\"}
+"
+         ;;
+       *)
+         $echo >> $output "\
+      # Export the path to the program.
+      PATH=\"\$progdir:\$PATH\"
+      export PATH
+
+      exec \$program \${1+\"\$@\"}
+"
+         ;;
+       esac
+       $echo >> $output "\
+      \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\"
+      exit 1
+    fi
+  else
+    # The program doesn't exist.
+    \$echo \"\$0: error: \$progdir/\$program does not exist\" 1>&2
+    \$echo \"This script is just a wrapper for \$program.\" 1>&2
+    echo \"See the $PACKAGE documentation for more information.\" 1>&2
+    exit 1
+  fi
+fi\
+"
+       chmod +x $output
+      fi
+      exit 0
+      ;;
+    esac
+
+    # See if we need to build an old-fashioned archive.
+    for oldlib in $oldlibs; do
+
+      if test "$build_libtool_libs" = convenience; then
+       oldobjs="$libobjs_save"
+       addlibs="$convenience"
+       build_libtool_libs=no
+      else
+       if test "$build_libtool_libs" = module; then
+         oldobjs="$libobjs_save"
+         build_libtool_libs=no
+       else
+         oldobjs="$objs "`$echo "X$libobjs_save" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`
+       fi
+       addlibs="$old_convenience"
+      fi
+
+      if test -n "$addlibs"; then
+       gentop="$output_objdir/${outputname}x"
+       $show "${rm}r $gentop"
+       $run ${rm}r "$gentop"
+       $show "mkdir $gentop"
+       $run mkdir "$gentop"
+       status=$?
+       if test $status -ne 0 && test ! -d "$gentop"; then
+         exit $status
+       fi
+       generated="$generated $gentop"
+         
+       # Add in members from convenience archives.
+       for xlib in $addlibs; do
+         # Extract the objects.
+         case "$xlib" in
+         [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;;
+         *) xabs=`pwd`"/$xlib" ;;
+         esac
+         xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'`
+         xdir="$gentop/$xlib"
+
+         $show "${rm}r $xdir"
+         $run ${rm}r "$xdir"
+         $show "mkdir $xdir"
+         $run mkdir "$xdir"
+         status=$?
+         if test $status -ne 0 && test ! -d "$xdir"; then
+           exit $status
+         fi
+         $show "(cd $xdir && $AR x $xabs)"
+         $run eval "(cd \$xdir && $AR x \$xabs)" || exit $?
+
+         oldobjs="$oldobjs "`find $xdir -name \*.${objext} -print -o -name \*.lo -print | $NL2SP`
+       done
+      fi
+
+      # Do each command in the archive commands.
+      if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+       eval cmds=\"$old_archive_from_new_cmds\"
+      else
+       # Ensure that we have .o objects in place incase we decided
+       # not to build a shared library, and have fallen back to building
+       # static libs even though --disable-static was passed!
+       for oldobj in $oldobjs; do
+         if test ! -f $oldobj; then
+           obj=`$echo "X$oldobj" | $Xsed -e "$o2lo"`
+           $show "${LN_S} $obj $oldobj"
+           $run ${LN_S} $obj $oldobj || exit $?
+         fi
+       done
+
+       eval cmds=\"$old_archive_cmds\"
+      fi
+      IFS="${IFS=      }"; save_ifs="$IFS"; IFS='~'
+      for cmd in $cmds; do
+       IFS="$save_ifs"
+       $show "$cmd"
+       $run eval "$cmd" || exit $?
+      done
+      IFS="$save_ifs"
+    done
+
+    if test -n "$generated"; then
+      $show "${rm}r$generated"
+      $run ${rm}r$generated
+    fi
+
+    # Now create the libtool archive.
+    case "$output" in
+    *.la)
+      old_library=
+      test "$build_old_libs" = yes && old_library="$libname.$libext"
+      $show "creating $output"
+
+      if test -n "$xrpath"; then
+       temp_xrpath=
+       for libdir in $xrpath; do
+         temp_xrpath="$temp_xrpath -R$libdir"
+       done
+       dependency_libs="$temp_xrpath $dependency_libs"
+      fi
+
+      # Only create the output if not a dry run.
+      if test -z "$run"; then
+       for installed in no yes; do
+         if test "$installed" = yes; then
+           if test -z "$install_libdir"; then
+             break
+           fi
+           output="$output_objdir/$outputname"i
+         fi
+         $rm $output
+         $echo > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$dlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'\
+"
+       done
+      fi
+
+      # Do a symbolic link so that the libtool archive can be found in
+      # LD_LIBRARY_PATH before the program is installed.
+      $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)"
+      $run eval "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" || exit $?
+      ;;
+    esac
+    exit 0
+    ;;
+
+  # libtool install mode
+  install)
+    modename="$modename: install"
+
+    # There may be an optional sh(1) argument at the beginning of
+    # install_prog (especially on Windows NT).
+    if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh; then
+      # Aesthetically quote it.
+      arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"`
+      case "$arg" in
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*)
+       arg="\"$arg\""
+       ;;
+      esac
+      install_prog="$arg "
+      arg="$1"
+      shift
+    else
+      install_prog=
+      arg="$nonopt"
+    fi
+
+    # The real first argument should be the name of the installation program.
+    # Aesthetically quote it.
+    arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+    case "$arg" in
+    *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \      ]*|*]*)
+      arg="\"$arg\""
+      ;;
+    esac
+    install_prog="$install_prog$arg"
+
+    # We need to accept at least all the BSD install flags.
+    dest=
+    files=
+    opts=
+    prev=
+    install_type=
+    isdir=no
+    stripme=
+    for arg
+    do
+      if test -n "$dest"; then
+       files="$files $dest"
+       dest="$arg"
+       continue
+      fi
+
+      case "$arg" in
+      -d) isdir=yes ;;
+      -f) prev="-f" ;;
+      -g) prev="-g" ;;
+      -m) prev="-m" ;;
+      -o) prev="-o" ;;
+      -s)
+       stripme=" -s"
+       continue
+       ;;
+      -*) ;;
+
+      *)
+       # If the previous option needed an argument, then skip it.
+       if test -n "$prev"; then
+         prev=
+       else
+         dest="$arg"
+         continue
+       fi
+       ;;
+      esac
+
+      # Aesthetically quote the argument.
+      arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+      case "$arg" in
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*)
+       arg="\"$arg\""
+       ;;
+      esac
+      install_prog="$install_prog $arg"
+    done
+
+    if test -z "$install_prog"; then
+      $echo "$modename: you must specify an install program" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    if test -n "$prev"; then
+      $echo "$modename: the \`$prev' option requires an argument" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    if test -z "$files"; then
+      if test -z "$dest"; then
+       $echo "$modename: no file or destination specified" 1>&2
+      else
+       $echo "$modename: you must specify a destination" 1>&2
+      fi
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    # Strip any trailing slash from the destination.
+    dest=`$echo "X$dest" | $Xsed -e 's%/$%%'`
+
+    # Check to see that the destination is a directory.
+    test -d "$dest" && isdir=yes
+    if test "$isdir" = yes; then
+      destdir="$dest"
+      destname=
+    else
+      destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'`
+      test "X$destdir" = "X$dest" && destdir=.
+      destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'`
+
+      # Not a directory, so check to see that there is only one file specified.
+      set dummy $files
+      if test $# -gt 2; then
+       $echo "$modename: \`$dest' is not a directory" 1>&2
+       $echo "$help" 1>&2
+       exit 1
+      fi
+    fi
+    case "$destdir" in
+    [\\/]* | [A-Za-z]:[\\/]*) ;;
+    *)
+      for file in $files; do
+       case "$file" in
+       *.lo) ;;
+       *)
+         $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+         ;;
+       esac
+      done
+      ;;
+    esac
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    staticlibs=
+    future_libdirs=
+    current_libdirs=
+    for file in $files; do
+
+      # Do each installation.
+      case "$file" in
+      *.a | *.lib)
+       # Do the static libraries later.
+       staticlibs="$staticlibs $file"
+       ;;
+
+      *.la)
+       # Check to see that this really is a libtool archive.
+       if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+       else
+         $echo "$modename: \`$file' is not a valid libtool archive" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+       fi
+
+       library_names=
+       old_library=
+       # If there is no directory component, then add one.
+       case "$file" in
+       */* | *\\*) . $file ;;
+       *) . ./$file ;;
+       esac
+
+       # Add the libdir to current_libdirs if it is the destination.
+       if test "X$destdir" = "X$libdir"; then
+         case "$current_libdirs " in
+         *" $libdir "*) ;;
+         *) current_libdirs="$current_libdirs $libdir" ;;
+         esac
+       else
+         # Note the libdir as a future libdir.
+         case "$future_libdirs " in
+         *" $libdir "*) ;;
+         *) future_libdirs="$future_libdirs $libdir" ;;
+         esac
+       fi
+
+       dir="`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/"
+       test "X$dir" = "X$file/" && dir=
+       dir="$dir$objdir"
+
+       # See the names of the shared library.
+       set dummy $library_names
+       if test -n "$2"; then
+         realname="$2"
+         shift
+         shift
+
+         # Install the shared library and build the symlinks.
+         $show "$install_prog $dir/$realname $destdir/$realname"
+         $run eval "$install_prog $dir/$realname $destdir/$realname" || exit $?
+         test "X$dlname" = "X$realname" && dlname=
+
+         if test $# -gt 0; then
+           # Delete the old symlinks, and create new ones.
+           for linkname
+           do
+             test "X$dlname" = "X$linkname" && dlname=
+             if test "$linkname" != "$realname"; then
+               $show "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)"
+               $run eval "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)"
+             fi
+           done
+         fi
+
+         if test -n "$dlname"; then
+           # Install the dynamically-loadable library.
+           $show "$install_prog $dir/$dlname $destdir/$dlname"
+           $run eval "$install_prog $dir/$dlname $destdir/$dlname" || exit $?
+         fi
+
+         # Do each command in the postinstall commands.
+         lib="$destdir/$realname"
+         eval cmds=\"$postinstall_cmds\"
+         IFS="${IFS=   }"; save_ifs="$IFS"; IFS='~'
+         for cmd in $cmds; do
+           IFS="$save_ifs"
+           $show "$cmd"
+           $run eval "$cmd" || exit $?
+         done
+         IFS="$save_ifs"
+       fi
+
+       # Install the pseudo-library for information purposes.
+       name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+       instname="$dir/$name"i
+       $show "$install_prog $instname $destdir/$name"
+       $run eval "$install_prog $instname $destdir/$name" || exit $?
+
+       # Maybe install the static library, too.
+       test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library"
+       ;;
+
+      *.lo)
+       # Install (i.e. copy) a libtool object.
+
+       # Figure out destination file name, if it wasn't already specified.
+       if test -n "$destname"; then
+         destfile="$destdir/$destname"
+       else
+         destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+         destfile="$destdir/$destfile"
+       fi
+
+       # Deduce the name of the destination old-style object file.
+       case "$destfile" in
+       *.lo)
+         staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"`
+         ;;
+       *.o | *.obj)
+         staticdest="$destfile"
+         destfile=
+         ;;
+       *)
+         $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+         ;;
+       esac
+
+       # Install the libtool object if requested.
+       if test -n "$destfile"; then
+         $show "$install_prog $file $destfile"
+         $run eval "$install_prog $file $destfile" || exit $?
+       fi
+
+       # Install the old object if enabled.
+       if test "$build_old_libs" = yes; then
+         # Deduce the name of the old-style object file.
+         staticobj=`$echo "X$file" | $Xsed -e "$lo2o"`
+
+         $show "$install_prog $staticobj $staticdest"
+         $run eval "$install_prog \$staticobj \$staticdest" || exit $?
+       fi
+       exit 0
+       ;;
+
+      *)
+       # Figure out destination file name, if it wasn't already specified.
+       if test -n "$destname"; then
+         destfile="$destdir/$destname"
+       else
+         destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+         destfile="$destdir/$destfile"
+       fi
+
+       # Do a test to see if this is really a libtool program.
+       if (sed -e '4q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+         link_against_libtool_libs=
+         relink_command=
+
+         # If there is no directory component, then add one.
+         case "$file" in
+         */* | *\\*) . $file ;;
+         *) . ./$file ;;
+         esac
+
+         # Check the variables that should have been set.
+         if test -z "$link_against_libtool_libs"; then
+           $echo "$modename: invalid libtool wrapper script \`$file'" 1>&2
+           exit 1
+         fi
+
+         finalize=yes
+         for lib in $link_against_libtool_libs; do
+           # Check to see that each library is installed.
+           libdir=
+           if test -f "$lib"; then
+             # If there is no directory component, then add one.
+             case "$lib" in
+             */* | *\\*) . $lib ;;
+             *) . ./$lib ;;
+             esac
+           fi
+           libfile="$libdir/`$echo "X$lib" | $Xsed -e 's%^.*/%%g'`"
+           if test -n "$libdir" && test ! -f "$libfile"; then
+             $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2
+             finalize=no
+           fi
+         done
+
+         outputname=
+         if test "$fast_install" = no && test -n "$relink_command"; then
+           if test "$finalize" = yes && test -z "$run"; then
+             tmpdir="/tmp"
+             test -n "$TMPDIR" && tmpdir="$TMPDIR"
+             tmpdir="$tmpdir/libtool-$$"
+             if $mkdir -p "$tmpdir" && chmod 700 "$tmpdir"; then :
+             else
+               $echo "$modename: error: cannot create temporary directory \`$tmpdir'" 1>&2
+               continue
+             fi
+             outputname="$tmpdir/$file"
+             # Replace the output file specification.
+             relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'`
+
+             $show "$relink_command"
+             if $run eval "$relink_command"; then :
+             else
+               $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2
+               ${rm}r "$tmpdir"
+               continue
+             fi
+             file="$outputname"
+           else
+             $echo "$modename: warning: cannot relink \`$file'" 1>&2
+           fi
+         else
+           # Install the binary that we compiled earlier.
+           file=`$echo "X$file" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"`
+         fi
+       fi
+
+       $show "$install_prog$stripme $file $destfile"
+       $run eval "$install_prog\$stripme \$file \$destfile" || exit $?
+       test -n "$outputname" && ${rm}r "$tmpdir"
+       ;;
+      esac
+    done
+
+    for file in $staticlibs; do
+      name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+
+      # Set up the ranlib parameters.
+      oldlib="$destdir/$name"
+
+      $show "$install_prog $file $oldlib"
+      $run eval "$install_prog \$file \$oldlib" || exit $?
+
+      # Do each command in the postinstall commands.
+      eval cmds=\"$old_postinstall_cmds\"
+      IFS="${IFS=      }"; save_ifs="$IFS"; IFS='~'
+      for cmd in $cmds; do
+       IFS="$save_ifs"
+       $show "$cmd"
+       $run eval "$cmd" || exit $?
+      done
+      IFS="$save_ifs"
+    done
+
+    if test -n "$future_libdirs"; then
+      $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2
+    fi
+
+    if test -n "$current_libdirs"; then
+      # Maybe just do a dry run.
+      test -n "$run" && current_libdirs=" -n$current_libdirs"
+      exec $SHELL $0 --finish$current_libdirs
+      exit 1
+    fi
+
+    exit 0
+    ;;
+
+  # libtool finish mode
+  finish)
+    modename="$modename: finish"
+    libdirs="$nonopt"
+    admincmds=
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      for dir
+      do
+       libdirs="$libdirs $dir"
+      done
+
+      for libdir in $libdirs; do
+       if test -n "$finish_cmds"; then
+         # Do each command in the finish commands.
+         eval cmds=\"$finish_cmds\"
+         IFS="${IFS=   }"; save_ifs="$IFS"; IFS='~'
+         for cmd in $cmds; do
+           IFS="$save_ifs"
+           $show "$cmd"
+           $run eval "$cmd" || admincmds="$admincmds
+       $cmd"
+         done
+         IFS="$save_ifs"
+       fi
+       if test -n "$finish_eval"; then
+         # Do the single finish_eval.
+         eval cmds=\"$finish_eval\"
+         $run eval "$cmds" || admincmds="$admincmds
+       $cmds"
+       fi
+      done
+    fi
+
+    # Exit here if they wanted silent mode.
+    test "$show" = : && exit 0
+
+    echo "----------------------------------------------------------------------"
+    echo "Libraries have been installed in:"
+    for libdir in $libdirs; do
+      echo "   $libdir"
+    done
+    echo
+    echo "If you ever happen to want to link against installed libraries"
+    echo "in a given directory, LIBDIR, you must either use libtool, and"
+    echo "specify the full pathname of the library, or use \`-LLIBDIR'"
+    echo "flag during linking and do at least one of the following:"
+    if test -n "$shlibpath_var"; then
+      echo "   - add LIBDIR to the \`$shlibpath_var' environment variable"
+      echo "     during execution"
+    fi
+    if test -n "$runpath_var"; then
+      echo "   - add LIBDIR to the \`$runpath_var' environment variable"
+      echo "     during linking"
+    fi
+    if test -n "$hardcode_libdir_flag_spec"; then
+      libdir=LIBDIR
+      eval flag=\"$hardcode_libdir_flag_spec\"
+
+      echo "   - use the \`$flag' linker flag"
+    fi
+    if test -n "$admincmds"; then
+      echo "   - have your system administrator run these commands:$admincmds"
+    fi
+    if test -f /etc/ld.so.conf; then
+      echo "   - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+    fi
+    echo
+    echo "See any operating system documentation about shared libraries for"
+    echo "more information, such as the ld(1) and ld.so(8) manual pages."
+    echo "----------------------------------------------------------------------"
+    exit 0
+    ;;
+
+  # libtool execute mode
+  execute)
+    modename="$modename: execute"
+
+    # The first argument is the command name.
+    cmd="$nonopt"
+    if test -z "$cmd"; then
+      $echo "$modename: you must specify a COMMAND" 1>&2
+      $echo "$help"
+      exit 1
+    fi
+
+    # Handle -dlopen flags immediately.
+    for file in $execute_dlfiles; do
+      if test ! -f "$file"; then
+       $echo "$modename: \`$file' is not a file" 1>&2
+       $echo "$help" 1>&2
+       exit 1
+      fi
+
+      dir=
+      case "$file" in
+      *.la)
+       # Check to see that this really is a libtool archive.
+       if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+       else
+         $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+       fi
+
+       # Read the libtool library.
+       dlname=
+       library_names=
+
+       # If there is no directory component, then add one.
+       case "$file" in
+       */* | *\\*) . $file ;;
+       *) . ./$file ;;
+       esac
+
+       # Skip this library if it cannot be dlopened.
+       if test -z "$dlname"; then
+         # Warn if it was a shared library.
+         test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'"
+         continue
+       fi
+
+       dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+       test "X$dir" = "X$file" && dir=.
+
+       if test -f "$dir/$objdir/$dlname"; then
+         dir="$dir/$objdir"
+       else
+         $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2
+         exit 1
+       fi
+       ;;
+
+      *.lo)
+       # Just add the directory containing the .lo file.
+       dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+       test "X$dir" = "X$file" && dir=.
+       ;;
+
+      *)
+       $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2
+       continue
+       ;;
+      esac
+
+      # Get the absolute pathname.
+      absdir=`cd "$dir" && pwd`
+      test -n "$absdir" && dir="$absdir"
+
+      # Now add the directory to shlibpath_var.
+      if eval "test -z \"\$$shlibpath_var\""; then
+       eval "$shlibpath_var=\"\$dir\""
+      else
+       eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+      fi
+    done
+
+    # This variable tells wrapper scripts just to set shlibpath_var
+    # rather than running their programs.
+    libtool_execute_magic="$magic"
+
+    # Check if any of the arguments is a wrapper script.
+    args=
+    for file
+    do
+      case "$file" in
+      -*) ;;
+      *)
+       # Do a test to see if this is really a libtool program.
+       if (sed -e '4q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+         # If there is no directory component, then add one.
+         case "$file" in
+         */* | *\\*) . $file ;;
+         *) . ./$file ;;
+         esac
+
+         # Transform arg to wrapped name.
+         file="$progdir/$program"
+       fi
+       ;;
+      esac
+      # Quote arguments (to preserve shell metacharacters).
+      file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"`
+      args="$args \"$file\""
+    done
+
+    if test -z "$run"; then
+      # Export the shlibpath_var.
+      eval "export $shlibpath_var"
+
+      # Restore saved enviroment variables
+      if test "${save_LC_ALL+set}" = set; then
+       LC_ALL="$save_LC_ALL"; export LC_ALL
+      fi
+      if test "${save_LANG+set}" = set; then
+       LANG="$save_LANG"; export LANG
+      fi
+
+      # Now actually exec the command.
+      eval "exec \$cmd$args"
+
+      $echo "$modename: cannot exec \$cmd$args"
+      exit 1
+    else
+      # Display what would be done.
+      eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\""
+      $echo "export $shlibpath_var"
+      $echo "$cmd$args"
+      exit 0
+    fi
+    ;;
+
+  # libtool uninstall mode
+  uninstall)
+    modename="$modename: uninstall"
+    rm="$nonopt"
+    files=
+
+    for arg
+    do
+      case "$arg" in
+      -*) rm="$rm $arg" ;;
+      *) files="$files $arg" ;;
+      esac
+    done
+
+    if test -z "$rm"; then
+      $echo "$modename: you must specify an RM program" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    for file in $files; do
+      dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+      test "X$dir" = "X$file" && dir=.
+      name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+
+      rmfiles="$file"
+
+      case "$name" in
+      *.la)
+       # Possibly a libtool archive, so verify it.
+       if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+         . $dir/$name
+
+         # Delete the libtool libraries and symlinks.
+         for n in $library_names; do
+           rmfiles="$rmfiles $dir/$n"
+           test "X$n" = "X$dlname" && dlname=
+         done
+         test -n "$dlname" && rmfiles="$rmfiles $dir/$dlname"
+         test -n "$old_library" && rmfiles="$rmfiles $dir/$old_library"
+
+         $show "$rm $rmfiles"
+         $run $rm $rmfiles
+
+         if test -n "$library_names"; then
+           # Do each command in the postuninstall commands.
+           eval cmds=\"$postuninstall_cmds\"
+           IFS="${IFS=         }"; save_ifs="$IFS"; IFS='~'
+           for cmd in $cmds; do
+             IFS="$save_ifs"
+             $show "$cmd"
+             $run eval "$cmd"
+           done
+           IFS="$save_ifs"
+         fi
+
+         if test -n "$old_library"; then
+           # Do each command in the old_postuninstall commands.
+           eval cmds=\"$old_postuninstall_cmds\"
+           IFS="${IFS=         }"; save_ifs="$IFS"; IFS='~'
+           for cmd in $cmds; do
+             IFS="$save_ifs"
+             $show "$cmd"
+             $run eval "$cmd"
+           done
+           IFS="$save_ifs"
+         fi
+
+         # FIXME: should reinstall the best remaining shared library.
+       fi
+       ;;
+
+      *.lo)
+       if test "$build_old_libs" = yes; then
+         oldobj=`$echo "X$name" | $Xsed -e "$lo2o"`
+         rmfiles="$rmfiles $dir/$oldobj"
+       fi
+       $show "$rm $rmfiles"
+       $run $rm $rmfiles
+       ;;
+
+      *)
+       $show "$rm $rmfiles"
+       $run $rm $rmfiles
+       ;;
+      esac
+    done
+    exit 0
+    ;;
+
+  "")
+    $echo "$modename: you must specify a MODE" 1>&2
+    $echo "$generic_help" 1>&2
+    exit 1
+    ;;
+  esac
+
+  $echo "$modename: invalid operation mode \`$mode'" 1>&2
+  $echo "$generic_help" 1>&2
+  exit 1
+fi # test -z "$show_help"
+
+# We need to display help for each of the modes.
+case "$mode" in
+"") $echo \
+"Usage: $modename [OPTION]... [MODE-ARG]...
+
+Provide generalized library-building support services.
+
+    --config          show all configuration variables
+    --debug           enable verbose shell tracing
+-n, --dry-run         display commands without modifying any files
+    --features        display basic configuration information and exit
+    --finish          same as \`--mode=finish'
+    --help            display this help message and exit
+    --mode=MODE       use operation mode MODE [default=inferred from MODE-ARGS]
+    --quiet           same as \`--silent'
+    --silent          don't print informational messages
+    --version         print version information
+
+MODE must be one of the following:
+
+      compile         compile a source file into a libtool object
+      execute         automatically set library path, then run a program
+      finish          complete the installation of libtool libraries
+      install         install libraries or executables
+      link            create a library or an executable
+      uninstall       remove libraries from an installed directory
+
+MODE-ARGS vary depending on the MODE.  Try \`$modename --help --mode=MODE' for
+a more detailed description of MODE."
+  exit 0
+  ;;
+
+compile)
+  $echo \
+"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+  -o OUTPUT-FILE    set the output file name to OUTPUT-FILE
+  -static           always build a \`.o' file suitable for static linking
+
+COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix \`.c' with the
+library object suffix, \`.lo'."
+  ;;
+
+execute)
+  $echo \
+"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+  -dlopen FILE      add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to \`-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+  ;;
+
+finish)
+  $echo \
+"Usage: $modename [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges.  Use
+the \`--dry-run' option if you just want to see what would be executed."
+  ;;
+
+install)
+  $echo \
+"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command.  The first component should be
+either the \`install' or \`cp' program.
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+  ;;
+
+link)
+  $echo \
+"Usage: $modename [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+  -all-static       do not do any dynamic linking at all
+  -avoid-version    do not add a version suffix if possible
+  -dlopen FILE      \`-dlpreopen' FILE if it cannot be dlopened at runtime
+  -dlpreopen FILE   link in FILE and add its symbols to lt_preloaded_symbols
+  -export-dynamic   allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+  -export-symbols SYMFILE
+                   try to export only the symbols listed in SYMFILE
+  -export-symbols-regex REGEX
+                   try to export only the symbols matching REGEX
+  -LLIBDIR          search LIBDIR for required installed libraries
+  -lNAME            OUTPUT-FILE requires the installed library libNAME
+  -module           build a library that can dlopened
+  -no-undefined     declare that a library does not refer to external symbols
+  -o OUTPUT-FILE    create OUTPUT-FILE from the specified objects
+  -release RELEASE  specify package release information
+  -rpath LIBDIR     the created library will eventually be installed in LIBDIR
+  -R[ ]LIBDIR       add LIBDIR to the runtime path of programs and libraries
+  -static           do not do any dynamic linking of libtool libraries
+  -version-info CURRENT[:REVISION[:AGE]]
+                   specify library version info [each variable defaults to 0]
+
+All other options (arguments beginning with \`-') are ignored.
+
+Every other argument is treated as a filename.  Files ending in \`.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
+only library objects (\`.lo' files) may be specified, and \`-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
+using \`ar' and \`ranlib', or on Windows using \`lib'.
+
+If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+is created, otherwise an executable program is created."
+  ;;
+
+uninstall)
+  $echo \
+"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+  ;;
+
+*)
+  $echo "$modename: invalid operation mode \`$mode'" 1>&2
+  $echo "$help" 1>&2
+  exit 1
+  ;;
+esac
+
+echo
+$echo "Try \`$modename --help' for more information about other modes."
+
+exit 0
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
diff --git a/neon/macros/ChangeLog b/neon/macros/ChangeLog
new file mode 100644 (file)
index 0000000..9a59cc6
--- /dev/null
@@ -0,0 +1,26 @@
+Wed May 10 19:18:14 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-xml-parser.m4: Error if no XML parser is found.
+
+Wed May 10 14:33:21 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-checks.m4: New file.
+
+Wed May 10 14:26:57 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-xml-parser.m4 (NEON_XML_PARSER): Use "neon_" prefix for
+       variables.
+
+Wed May 10 13:47:04 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * acconfig.h: New file.
+
+Wed May 10 13:42:16 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon-xml-parser.m4: New file.
+
+Sun May  7 21:57:32 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * gnome-x-checks.m4 (GNOME_X_CHECKS): Check for Gtk 1.2.7 or
+       later, passing "gthread" module argument.
+
diff --git a/neon/macros/ac_c_bigendian_cross.m4 b/neon/macros/ac_c_bigendian_cross.m4
new file mode 100644 (file)
index 0000000..f52c41b
--- /dev/null
@@ -0,0 +1,81 @@
+dnl @synopsis AC_C_BIGENDIAN_CROSS
+dnl
+dnl Check endianess even when crosscompiling
+dnl (partially based on the original AC_C_BIGENDIAN).
+dnl
+dnl The implementation will create a binary, and instead of running
+dnl the binary it will be grep'ed for some symbols that will look
+dnl different for different endianess of the binary.
+dnl
+dnl @version Id: ac_c_bigendian_cross.m4,v 1.1 2001/09/24 16:27:57 joe Exp 
+dnl @author Guido Draheim <guidod@gmx.de>
+dnl
+AC_DEFUN([AC_C_BIGENDIAN_CROSS],
+[AC_CACHE_CHECK(whether byte ordering is bigendian, ac_cv_c_bigendian,
+[ac_cv_c_bigendian=unknown
+# See if sys/param.h defines the BYTE_ORDER macro.
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/param.h>], [
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif], [# It does; now see whether it defined to BIG_ENDIAN or not.
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/param.h>], [
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif], ac_cv_c_bigendian=yes, ac_cv_c_bigendian=no)])
+if test $ac_cv_c_bigendian = unknown; then
+AC_TRY_RUN([main () {
+  /* Are we little or big endian?  From Harbison&Steele.  */
+  union
+  {
+    long l;
+    char c[sizeof (long)];
+  } u;
+  u.l = 1;
+  exit (u.c[sizeof (long) - 1] == 1);
+}], ac_cv_c_bigendian=no, ac_cv_c_bigendian=yes,
+[ echo $ac_n "cross-compiling... " 2>&AC_FD_MSG ])
+fi])
+if test $ac_cv_c_bigendian = unknown; then
+AC_MSG_CHECKING(to probe for byte ordering)
+[
+cat >conftest.c <<EOF
+short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+void _ascii() { char* s = (char*) ascii_mm; s = (char*) ascii_ii; }
+short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+void _ebcdic() { char* s = (char*) ebcdic_mm; s = (char*) ebcdic_ii; }
+int main() { _ascii (); _ebcdic (); return 0; }
+EOF
+] if test -f conftest.c ; then
+     if ${CC-cc} conftest.c -o conftest.o && test -f conftest.o ; then
+        if test `grep -l BIGenDianSyS conftest.o` ; then
+           echo $ac_n ' big endian probe OK, ' 1>&AC_FD_MSG
+           ac_cv_c_bigendian=yes
+        fi
+        if test `grep -l LiTTleEnDian conftest.o` ; then
+           echo $ac_n ' little endian probe OK, ' 1>&AC_FD_MSG
+           if test $ac_cv_c_bigendian = yes ; then
+            ac_cv_c_bigendian=unknown;
+           else
+            ac_cv_c_bigendian=no
+           fi
+        fi
+        echo $ac_n 'guessing bigendian ...  ' >&AC_FD_MSG
+     fi
+  fi
+AC_MSG_RESULT($ac_cv_c_bigendian)
+fi
+if test $ac_cv_c_bigendian = yes; then
+  AC_DEFINE(WORDS_BIGENDIAN, 1, [whether byteorder is bigendian])
+  BYTEORDER=4321
+else
+  BYTEORDER=1234
+fi
+AC_DEFINE_UNQUOTED(BYTEORDER, $BYTEORDER, [1234 = LIL_ENDIAN, 4321 = BIGENDIAN])
+if test $ac_cv_c_bigendian = unknown; then
+  AC_MSG_ERROR(unknown endianess - sorry, please pre-set ac_cv_c_bigendian)
+fi
+])
diff --git a/neon/macros/acconfig.h b/neon/macros/acconfig.h
new file mode 100644 (file)
index 0000000..07ee5ad
--- /dev/null
@@ -0,0 +1,4 @@
+/* Unconditionally define _GNU_SOURCE */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
diff --git a/neon/macros/aclocal-include.m4 b/neon/macros/aclocal-include.m4
new file mode 100644 (file)
index 0000000..abf6533
--- /dev/null
@@ -0,0 +1,16 @@
+# aclocal-include.m4
+# 
+# This macro adds the name macrodir to the set of directories
+# that `aclocal' searches for macros.  
+
+# serial 1
+
+dnl AM_ACLOCAL_INCLUDE(macrodir)
+AC_DEFUN([AM_ACLOCAL_INCLUDE],
+[
+       AM_CONDITIONAL(INSIDE_GNOME_COMMON, test x = y)
+
+       test -n "$ACLOCAL_FLAGS" && ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+
+       for k in $1 ; do ACLOCAL="$ACLOCAL -I $k" ; done
+])
diff --git a/neon/macros/compiler-flags.m4 b/neon/macros/compiler-flags.m4
new file mode 100644 (file)
index 0000000..63f8e2e
--- /dev/null
@@ -0,0 +1,109 @@
+dnl GNOME_COMPILE_WARNINGS
+dnl Turn on many useful compiler warnings
+dnl For now, only works on GCC
+AC_DEFUN([GNOME_COMPILE_WARNINGS],[
+  AC_ARG_ENABLE(compile-warnings, 
+    [  --enable-compile-warnings=[no/minimum/yes]      Turn on compiler warnings.],,enable_compile_warnings=minimum)
+
+  AC_MSG_CHECKING(what warning flags to pass to the C compiler)
+  warnCFLAGS=
+  if test "x$GCC" != xyes; then
+    enable_compile_warnings=no
+  fi
+
+  if test "x$enable_compile_warnings" != "xno"; then
+    if test "x$GCC" = "xyes"; then
+      case " $CFLAGS " in
+      *[\ \    ]-Wall[\ \      ]*) ;;
+      *) warnCFLAGS="-Wall -Wunused" ;;
+      esac
+
+      ## -W is not all that useful.  And it cannot be controlled
+      ## with individual -Wno-xxx flags, unlike -Wall
+      if test "x$enable_compile_warnings" = "xyes"; then
+       warnCFLAGS="$warnCFLAGS -Wmissing-prototypes -Wmissing-declarations"
+      fi
+    fi
+  fi
+  AC_MSG_RESULT($warnCFLAGS)
+
+  AC_ARG_ENABLE(iso-c,
+    [  --enable-iso-c          Try to warn if code is not ISO C ],,
+    enable_iso_c=no)
+
+  AC_MSG_CHECKING(what language compliance flags to pass to the C compiler)
+  complCFLAGS=
+  if test "x$enable_iso_c" != "xno"; then
+    if test "x$GCC" = "xyes"; then
+      case " $CFLAGS " in
+      *[\ \    ]-ansi[\ \      ]*) ;;
+      *) complCFLAGS="$complCFLAGS -ansi" ;;
+      esac
+
+      case " $CFLAGS " in
+      *[\ \    ]-pedantic[\ \  ]*) ;;
+      *) complCFLAGS="$complCFLAGS -pedantic" ;;
+      esac
+    fi
+  fi
+  AC_MSG_RESULT($complCFLAGS)
+  if test "x$cflags_set" != "xyes"; then
+    CFLAGS="$CFLAGS $warnCFLAGS $complCFLAGS"
+    cflags_set=yes
+    AC_SUBST(cflags_set)
+  fi
+])
+
+dnl For C++, do basically the same thing.
+
+AC_DEFUN([GNOME_CXX_WARNINGS],[
+  AC_ARG_ENABLE(cxx-warnings, 
+    [  --enable-cxx-warnings=[no/minimum/yes]  Turn on compiler warnings.],,enable_cxx_warnings=minimum)
+
+  AC_MSG_CHECKING(what warning flags to pass to the C++ compiler)
+  warnCXXFLAGS=
+  if test "x$GCC" != xyes; then
+    enable_compile_warnings=no
+  fi
+  if test "x$enable_cxx_warnings" != "xno"; then
+    if test "x$GCC" = "xyes"; then
+      case " $CXXFLAGS " in
+      *[\ \    ]-Wall[\ \      ]*) ;;
+      *) warnCXXFLAGS="-Wall -Wno-unused" ;;
+      esac
+
+      ## -W is not all that useful.  And it cannot be controlled
+      ## with individual -Wno-xxx flags, unlike -Wall
+      if test "x$enable_cxx_warnings" = "xyes"; then
+       warnCXXFLAGS="$warnCXXFLAGS -Wmissing-prototypes -Wmissing-declarations -Wshadow -Woverloaded-virtual"
+      fi
+    fi
+  fi
+  AC_MSG_RESULT($warnCXXFLAGS)
+
+   AC_ARG_ENABLE(iso-cxx,
+     [  --enable-iso-cxx          Try to warn if code is not ISO C++ ],,
+     enable_iso_cxx=no)
+
+   AC_MSG_CHECKING(what language compliance flags to pass to the C++ compiler)
+   complCXXFLAGS=
+   if test "x$enable_iso_cxx" != "xno"; then
+     if test "x$GCC" = "xyes"; then
+      case " $CXXFLAGS " in
+      *[\ \    ]-ansi[\ \      ]*) ;;
+      *) complCXXFLAGS="$complCXXFLAGS -ansi" ;;
+      esac
+
+      case " $CXXFLAGS " in
+      *[\ \    ]-pedantic[\ \  ]*) ;;
+      *) complCXXFLAGS="$complCXXFLAGS -pedantic" ;;
+      esac
+     fi
+   fi
+  AC_MSG_RESULT($complCXXFLAGS)
+  if test "x$cxxflags_set" != "xyes"; then
+    CXXFLAGS="$CXXFLAGS $warnCXXFLAGS $complCXXFLAGS"
+    cxxflags_set=yes
+    AC_SUBST(cxxflags_set)
+  fi
+])
diff --git a/neon/macros/gnome-common.m4 b/neon/macros/gnome-common.m4
new file mode 100644 (file)
index 0000000..b723829
--- /dev/null
@@ -0,0 +1,14 @@
+# gnome-common.m4
+# 
+# This only for packages that are not in the GNOME CVS tree.
+
+dnl GNOME_COMMON_INIT
+
+AC_DEFUN([GNOME_COMMON_INIT],
+[
+       GNOME_ACLOCAL_DIR=`$ACLOCAL --print-ac-dir`/gnome
+       AC_SUBST(GNOME_ACLOCAL_DIR)
+
+       ACLOCAL="$ACLOCAL -I $GNOME_ACLOCAL_DIR"
+])
+
diff --git a/neon/macros/gnome-gnorba-check.m4 b/neon/macros/gnome-gnorba-check.m4
new file mode 100644 (file)
index 0000000..dbac0a6
--- /dev/null
@@ -0,0 +1,35 @@
+dnl
+dnl GNOME_GNORBA_HOOK (script-if-gnorba-found, failflag)
+dnl
+dnl if failflag is "failure" it aborts if gnorba is not found.
+dnl
+
+AC_DEFUN([GNOME_GNORBA_HOOK],[
+       GNOME_ORBIT_HOOK([],$2)
+       AC_CACHE_CHECK([for gnorba libraries],gnome_cv_gnorba_found,[
+               gnome_cv_gnorba_found=no
+               if test x$gnome_cv_orbit_found = xyes; then
+                       GNORBA_CFLAGS="`gnome-config --cflags gnorba gnomeui`"
+                       GNORBA_LIBS="`gnome-config --libs gnorba gnomeui`"
+                       if test -n "$GNORBA_LIBS"; then
+                               gnome_cv_gnorba_found=yes
+                       fi
+               fi
+       ])
+       AM_CONDITIONAL(HAVE_GNORBA, test x$gnome_cv_gnorba_found = xyes)
+       if test x$gnome_cv_orbit_found = xyes; then
+               $1
+               GNORBA_CFLAGS="`gnome-config --cflags gnorba gnomeui`"
+               GNORBA_LIBS="`gnome-config --libs gnorba gnomeui`"
+               AC_SUBST(GNORBA_CFLAGS)
+               AC_SUBST(GNORBA_LIBS)
+       else
+               if test x$2 = xfailure; then
+                       AC_MSG_ERROR(gnorba library not installed or installation problem)
+               fi
+       fi
+])
+
+AC_DEFUN([GNOME_GNORBA_CHECK], [
+       GNOME_GNORBA_HOOK([],failure)
+])
diff --git a/neon/macros/gnome-orbit-check.m4 b/neon/macros/gnome-orbit-check.m4
new file mode 100644 (file)
index 0000000..54bf33a
--- /dev/null
@@ -0,0 +1,33 @@
+dnl
+dnl GNOME_ORBIT_HOOK (script-if-orbit-found, failflag)
+dnl
+dnl if failflag is "failure" it aborts if orbit is not found.
+dnl
+
+AC_DEFUN([GNOME_ORBIT_HOOK],[
+       AC_PATH_PROG(ORBIT_CONFIG,orbit-config,no)
+       AC_PATH_PROG(ORBIT_IDL,orbit-idl,no)
+       AC_CACHE_CHECK([for working ORBit environment],gnome_cv_orbit_found,[
+               if test x$ORBIT_CONFIG = xno -o x$ORBIT_IDL = xno; then
+                       gnome_cv_orbit_found=no
+               else
+                       gnome_cv_orbit_found=yes
+               fi
+       ])
+       AM_CONDITIONAL(HAVE_ORBIT, test x$gnome_cv_orbit_found = xyes)
+       if test x$gnome_cv_orbit_found = xyes; then
+               $1
+               ORBIT_CFLAGS=`orbit-config --cflags client server`
+               ORBIT_LIBS=`orbit-config --use-service=name --libs client server`
+               AC_SUBST(ORBIT_CFLAGS)
+               AC_SUBST(ORBIT_LIBS)
+       else
+               if test x$2 = xfailure; then
+                       AC_MSG_ERROR(ORBit not installed or installation problem)
+               fi
+       fi
+])
+
+AC_DEFUN([GNOME_ORBIT_CHECK], [
+       GNOME_ORBIT_HOOK([],failure)
+])
diff --git a/neon/macros/gnome-print-check.m4 b/neon/macros/gnome-print-check.m4
new file mode 100644 (file)
index 0000000..7d98281
--- /dev/null
@@ -0,0 +1,171 @@
+# Configure paths for GNOME-PRINT
+# Chris Lahey  99-2-5
+# stolen from Manish Singh again
+# stolen back from Frank Belew
+# stolen from Manish Singh
+# Shamelessly stolen from Owen Taylor
+
+dnl AM_PATH_GNOME_PRINT([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for GNOME-PRINT, and define GNOME_PRINT_CFLAGS and GNOME_PRINT_LIBS
+dnl
+AC_DEFUN(AM_PATH_GNOME_PRINT,
+[dnl 
+dnl Get the cflags and libraries from the gnome-config script
+dnl
+AC_ARG_WITH(gnome-print-prefix,[  --with-gnome-print-prefix=PFX   Prefix where GNOME-PRINT is installed (optional)],
+            gnome_print_prefix="$withval", gnome_print_prefix="")
+AC_ARG_WITH(gnome-print-exec-prefix,[  --with-gnome-print-exec-prefix=PFX Exec prefix where GNOME-PRINT is installed (optional)],
+            gnome_print_exec_prefix="$withval", gnome_print_exec_prefix="")
+AC_ARG_ENABLE(gnome-printtest, [  --disable-gnome-printtest       Do not try to compile and run a test GNOME-PRINT program],
+                   , enable_gnome_printtest=yes)
+
+  if test x$gnome_print_exec_prefix != x ; then
+     gnome_print_args="$gnome_print_args --exec-prefix=$gnome_print_exec_prefix"
+     if test x${GNOME_CONFIG+set} != xset ; then
+        GNOME_CONFIG=$gnome_print_exec_prefix/bin/gnome-config
+     fi
+  fi
+  if test x$gnome_print_prefix != x ; then
+     gnome_print_args="$gnome_print_args --prefix=$gnome_print_prefix"
+     if test x${GNOME_CONFIG+set} != xset ; then
+        GNOME_CONFIG=$gnome_print_prefix/bin/gnome-config
+     fi
+  fi
+
+  AC_PATH_PROG(GNOME_CONFIG, gnome-config, no)
+  min_gnome_print_version=ifelse([$1], ,0.1.0,$1)
+  AC_MSG_CHECKING(for GNOME-PRINT - version >= $min_gnome_print_version)
+  no_gnome_print=""
+  if test "$GNOME_CONFIG" = "no" ; then
+    no_gnome_print=yes
+  else
+    GNOME_PRINT_CFLAGS=`$GNOME_CONFIG $gnome_printconf_args --cflags print`
+    GNOME_PRINT_LIBS=`$GNOME_CONFIG $gnome_printconf_args --libs print`
+
+    gnome_print_major_version=`$GNOME_CONFIG $gnome_print_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    gnome_print_minor_version=`$GNOME_CONFIG $gnome_print_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    gnome_print_micro_version=`$GNOME_CONFIG $gnome_print_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+    if test "x$enable_gnome_printtest" = "xyes" ; then
+      ac_save_CFLAGS="$CFLAGS"
+      ac_save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $GNOME_PRINT_CFLAGS"
+      LIBS="$LIBS $GNOME_PRINT_LIBS"
+dnl
+dnl Now check if the installed GNOME-PRINT is sufficiently new. (Also sanity
+dnl checks the results of gnome-config to some extent
+dnl
+      rm -f conf.gnome_printtest
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgnomeprint/gnome-print.h>
+
+static char*
+my_strdup (char *str)
+{
+  char *new_str;
+  
+  if (str)
+    {
+      new_str = malloc ((strlen (str) + 1) * sizeof(char));
+      strcpy (new_str, str);
+    }
+  else
+    new_str = NULL;
+  
+  return new_str;
+}
+
+int main ()
+{
+  int major, minor, micro;
+  char *tmp_version;
+
+  system ("touch conf.gnome_printtest");
+
+  /* HP/UX 9 (%@#!) writes to sscanf strings */
+  tmp_version = my_strdup("$min_gnome_print_version");
+  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+     printf("%s, bad version string\n", "$min_gnome_print_version");
+     exit(1);
+   }
+  return 0;
+#if 0
+   if (($gnome_print_major_version > major) ||
+      (($gnome_print_major_version == major) && ($gnome_print_minor_version > minor)) ||
+      (($gnome_print_major_version == major) && ($gnome_print_minor_version == minor) && ($gnome_print_micro_version >= micro)))
+    {
+      return 0;
+    }
+  else
+    {
+      printf("\n*** 'gnome-config print --version' returned %d.%d.%d, but the minimum version\n", $gnome_print_major_version, $gnome_print_minor_version, $gnome_print_micro_version);
+      printf("*** of GNOME-PRINT required is %d.%d.%d. If gnome-config is correct, then it is\n", major, minor, micro);
+      printf("*** best to upgrade to the required version.\n");
+      printf("*** If gnome-config was wrong, set the environment variable GNOME_CONFIG\n");
+      printf("*** to point to the correct copy of gnome-config, and remove the file\n");
+      printf("*** config.cache before re-running configure\n");
+      return 1;
+    }
+#endif
+}
+
+],, no_gnome_print=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+  fi
+  if test "x$no_gnome_print" = x ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$2], , :, [$2])     
+  else
+     AC_MSG_RESULT(no)
+     if test "$GNOME_CONFIG" = "no" ; then
+       echo "*** The gnome-config script installed by GNOME-LIBS could not be found"
+       echo "*** If GNOME-PRINT was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the GNOME_CONFIG environment variable to the"
+       echo "*** full path to gnome-config."
+     else
+       if test -f conf.gnome_printtest ; then
+        :
+       else
+          echo "*** Could not run GNOME-PRINT test program, checking why..."
+          CFLAGS="$CFLAGS $GNOME_PRINT_CFLAGS"
+          LIBS="$LIBS $GNOME_PRINT_LIBS"
+          AC_TRY_LINK([
+#include <stdio.h>
+#include <libgnomeprint/gnome-print.h>
+],      [ return 0; ],
+        [ echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding GNOME-PRINT or finding the wrong"
+          echo "*** version of GNOME-PRINT. If it is not finding GNOME-PRINT, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+         echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+        [ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means GNOME-PRINT was incorrectly installed"
+          echo "*** or that you have moved GNOME-PRINT since it was installed. In the latter case, you"
+          echo "*** may want to edit the gnome-config script: $GNOME_CONFIG" ])
+          CFLAGS="$ac_save_CFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+     GNOME_PRINT_CFLAGS=""
+     GNOME_PRINT_LIBS=""
+     ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(GNOME_PRINT_CFLAGS)
+  AC_SUBST(GNOME_PRINT_LIBS)
+  rm -f conf.gnome_printtest
+])
+
+AC_DEFUN([GNOME_PRINT_CHECK], [
+       AM_PATH_GNOME_PRINT(0.1.0,,[AC_MSG_ERROR(GNOME-PRINT not found)])
+])
diff --git a/neon/macros/gnome-pthread-check.m4 b/neon/macros/gnome-pthread-check.m4
new file mode 100644 (file)
index 0000000..a4eb3b4
--- /dev/null
@@ -0,0 +1,16 @@
+dnl
+dnl And better, use gthreads instead...
+dnl
+
+AC_DEFUN([GNOME_PTHREAD_CHECK],[
+       PTHREAD_LIB=""
+       AC_CHECK_LIB(pthread, pthread_create, PTHREAD_LIB="-lpthread",
+               [AC_CHECK_LIB(pthreads, pthread_create, PTHREAD_LIB="-lpthreads",
+                   [AC_CHECK_LIB(c_r, pthread_create, PTHREAD_LIB="-lc_r",
+                       [AC_CHECK_FUNC(pthread_create)]
+                   )]
+               )]
+       )
+       AC_SUBST(PTHREAD_LIB)
+       AC_PROVIDE([GNOME_PTHREAD_CHECK])
+])
diff --git a/neon/macros/gnome-support.m4 b/neon/macros/gnome-support.m4
new file mode 100644 (file)
index 0000000..2c1d049
--- /dev/null
@@ -0,0 +1,68 @@
+dnl GNOME_SUPPORT_CHECKS
+dnl    Check for various support functions needed by the standard
+dnl    Gnome libraries.  Sets LIBOBJS, might define some macros.
+dnl    This should only be used when building the Gnome libs; 
+dnl    Gnome clients should not need this macro.
+AC_DEFUN([GNOME_SUPPORT_CHECKS],[
+  # we need an `awk' to build `gnomesupport.h'
+  AC_REQUIRE([AC_PROG_AWK])
+
+  # this should go away soon
+  need_gnome_support=yes
+
+  save_LIBOBJS="$LIBOBJS"
+  LIBOBJS=
+
+  AC_CHECK_FUNCS(getopt_long,,LIBOBJS="$LIBOBJS getopt.o getopt1.o")
+
+  # for `scandir'
+  AC_HEADER_DIRENT
+
+  # copied from `configure.in' of `libiberty'
+  vars="program_invocation_short_name program_invocation_name sys_errlist"
+  for v in $vars; do
+    AC_MSG_CHECKING([for $v])
+    AC_CACHE_VAL(gnome_cv_var_$v,
+      [AC_TRY_LINK([int *p;], [extern int $v; p = &$v;],
+                  [eval "gnome_cv_var_$v=yes"],
+                  [eval "gnome_cv_var_$v=no"])])
+    if eval "test \"`echo '$gnome_cv_var_'$v`\" = yes"; then
+      AC_MSG_RESULT(yes)
+      n=HAVE_`echo $v | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+      AC_DEFINE_UNQUOTED($n)
+    else
+      AC_MSG_RESULT(no)
+    fi
+  done
+
+  AC_REPLACE_FUNCS(memmove mkstemp scandir strcasecmp strerror strndup strnlen)
+  AC_REPLACE_FUNCS(strtok_r strtod strtol strtoul vasprintf vsnprintf)
+
+  AC_CHECK_FUNCS(realpath,,LIBOBJS="$LIBOBJS canonicalize.o")
+
+  # to include `error.c' error.c has some HAVE_* checks
+  AC_CHECK_FUNCS(vprintf doprnt strerror_r)
+  AM_FUNC_ERROR_AT_LINE
+
+  # This is required if we declare setreuid () and setregid ().
+  AC_TYPE_UID_T
+
+  # see if we need to declare some functions.  Solaris is notorious for
+  # putting functions into the `libc' but not listing them in the headers
+  AC_CHECK_HEADERS(string.h strings.h stdlib.h unistd.h dirent.h)
+  GCC_NEED_DECLARATIONS(gethostname setreuid setregid getpagesize)
+  GCC_NEED_DECLARATION(scandir,[
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+])
+
+  # Turn our LIBOBJS into libtool objects.  This is gross, but it
+  # requires changes to autoconf before it goes away.
+  LTLIBOBJS=`echo "$LIBOBJS" | sed 's/\.o/.lo/g'`
+  AC_SUBST(need_gnome_support)
+  AC_SUBST(LTLIBOBJS)
+
+  LIBOBJS="$save_LIBOBJS"
+  AM_CONDITIONAL(BUILD_GNOME_SUPPORT, test "$need_gnome_support" = yes)
+])
diff --git a/neon/macros/gnome-x-checks.m4 b/neon/macros/gnome-x-checks.m4
new file mode 100644 (file)
index 0000000..e1125cc
--- /dev/null
@@ -0,0 +1,80 @@
+dnl GNOME_X_CHECKS
+dnl
+dnl Basic X11 related checks for X11.  At the end, the following will be
+dnl defined/changed:
+dnl   GTK_{CFLAGS,LIBS}      From AM_PATH_GTK
+dnl   CPPFLAGS              Will include $X_CFLAGS
+dnl   GNOME_HAVE_SM         `true' or `false' depending on whether session
+dnl                          management is available.  It is available if
+dnl                          both -lSM and X11/SM/SMlib.h exist.  (Some
+dnl                          Solaris boxes have the library but not the header)
+dnl   XPM_LIBS               -lXpm if Xpm library is present, otherwise ""
+dnl
+dnl The following configure cache variables are defined (but not used):
+dnl   gnome_cv_passdown_{x_libs,X_LIBS,X_CFLAGS}
+dnl
+AC_DEFUN([GNOME_X_CHECKS],
+[
+       AM_PATH_GTK(1.2.7,,AC_MSG_ERROR(GTK not installed, or gtk-config not in path),gthread)
+       dnl Hope that GTK_CFLAGS have only -I and -D.  Otherwise, we could
+       dnl   test -z "$x_includes" || CPPFLAGS="$CPPFLAGS -I$x_includes"
+       dnl
+       dnl Use CPPFLAGS instead of CFLAGS because AC_CHECK_HEADERS uses
+       dnl CPPFLAGS, not CFLAGS
+        CPPFLAGS="$CPPFLAGS $GTK_CFLAGS"
+
+        saved_ldflags="$LDFLAGS"
+        LDFLAGS="$LDFLAGS $GTK_LIBS"
+
+       gnome_cv_passdown_x_libs="$GTK_LIBS"
+       gnome_cv_passdown_X_LIBS="$GTK_LIBS"
+       gnome_cv_passdown_X_CFLAGS="$GTK_CFLAGS"
+       gnome_cv_passdown_GTK_LIBS="$GTK_LIBS"
+
+        LDFLAGS="$saved_ldflags $GTK_LIBS"
+
+dnl We are requiring GTK >= 1.1.1, which means this will be fine anyhow.
+       USE_DEVGTK=true
+
+dnl    AC_MSG_CHECKING([whether to use features from (unstable) GTK+ 1.1.x])
+dnl    AC_EGREP_CPP(answer_affirmatively,
+dnl    [#include <gtk/gtkfeatures.h>
+dnl    #ifdef GTK_HAVE_FEATURES_1_1_0
+dnl       answer_affirmatively
+dnl    #endif
+dnl    ], dev_gtk=yes, dev_gtk=no)
+dnl    if test "$dev_gtk" = "yes"; then
+dnl       USE_DEVGTK=true
+dnl    fi
+dnl    AC_MSG_RESULT("$dev_gtk")
+
+       GNOME_HAVE_SM=true
+       case "$GTK_LIBS" in
+        *-lSM*)
+           dnl Already found it.
+           ;;
+        *)
+           dnl Assume that if we have -lSM then we also have -lICE.
+           AC_CHECK_LIB(SM, SmcSaveYourselfDone,
+               [GTK_LIBS="-lSM -lICE $GTK_LIBS"],GNOME_HAVE_SM=false,
+               $x_libs -lICE)
+           ;;
+       esac
+
+       if test "$GNOME_HAVE_SM" = true; then
+          AC_CHECK_HEADERS(X11/SM/SMlib.h,,GNOME_HAVE_SM=false)
+       fi
+
+       if test "$GNOME_HAVE_SM" = true; then
+          AC_DEFINE(HAVE_LIBSM)
+       fi
+
+       XPM_LIBS=""
+       AC_CHECK_LIB(Xpm, XpmFreeXpmImage, [XPM_LIBS="-lXpm"], , $x_libs)
+       AC_SUBST(XPM_LIBS)
+
+       AC_REQUIRE([GNOME_PTHREAD_CHECK])
+        LDFLAGS="$saved_ldflags"
+
+       AC_PROVIDE([GNOME_X_CHECKS])
+])
diff --git a/neon/macros/gnome.m4 b/neon/macros/gnome.m4
new file mode 100644 (file)
index 0000000..a3a9ca7
--- /dev/null
@@ -0,0 +1,124 @@
+dnl
+dnl GNOME_INIT_HOOK (script-if-gnome-enabled, [failflag], [additional-inits])
+dnl
+dnl if failflag is "fail" then GNOME_INIT_HOOK will abort if gnomeConf.sh
+dnl is not found. 
+dnl
+
+AC_DEFUN([GNOME_INIT_HOOK],[
+       AC_SUBST(GNOME_LIBS)
+       AC_SUBST(GNOMEUI_LIBS)
+       AC_SUBST(GNOMEGNORBA_LIBS)
+       AC_SUBST(GTKXMHTML_LIBS)
+       AC_SUBST(ZVT_LIBS)
+       AC_SUBST(GNOME_LIBDIR)
+       AC_SUBST(GNOME_INCLUDEDIR)
+
+       AC_ARG_WITH(gnome-includes,
+       [  --with-gnome-includes   Specify location of GNOME headers],[
+       CFLAGS="$CFLAGS -I$withval"
+       ])
+       
+       AC_ARG_WITH(gnome-libs,
+       [  --with-gnome-libs       Specify location of GNOME libs],[
+       LDFLAGS="$LDFLAGS -L$withval"
+       gnome_prefix=$withval
+       ])
+
+       AC_ARG_WITH(gnome,
+       [  --with-gnome            Specify prefix for GNOME files],
+               if test x$withval = xyes; then
+                       want_gnome=yes
+                       dnl Note that an empty true branch is not
+                       dnl valid sh syntax.
+                       ifelse([$1], [], :, [$1])
+               else
+                       if test "x$withval" = xno; then
+                               want_gnome=no
+                       else
+                               want_gnome=yes
+                               LDFLAGS="$LDFLAGS -L$withval/lib"
+                               CFLAGS="$CFLAGS -I$withval/include"
+                               gnome_prefix=$withval/lib
+                       fi
+               fi,
+               want_gnome=yes)
+
+       if test "x$want_gnome" = xyes; then
+
+           AC_PATH_PROG(GNOME_CONFIG,gnome-config,no)
+           if test "$GNOME_CONFIG" = "no"; then
+             no_gnome_config="yes"
+           else
+             AC_MSG_CHECKING(if $GNOME_CONFIG works)
+             if $GNOME_CONFIG --libs-only-l gnome >/dev/null 2>&1; then
+               AC_MSG_RESULT(yes)
+               GNOME_GNORBA_HOOK([],$2)
+               GNOME_LIBS="`$GNOME_CONFIG --libs-only-l gnome`"
+               GNOMEUI_LIBS="`$GNOME_CONFIG --libs-only-l gnomeui`"
+               GNOMEGNORBA_LIBS="`$GNOME_CONFIG --libs-only-l gnorba gnomeui`"
+               GTKXMHTML_LIBS="`$GNOME_CONFIG --libs-only-l gtkxmhtml`"
+               ZVT_LIBS="`$GNOME_CONFIG --libs-only-l zvt`"
+               GNOME_LIBDIR="`$GNOME_CONFIG --libs-only-L gnorba gnomeui`"
+               GNOME_INCLUDEDIR="`$GNOME_CONFIG --cflags gnorba gnomeui`"
+                $1
+             else
+               AC_MSG_RESULT(no)
+               no_gnome_config="yes"
+              fi
+            fi
+
+           if test x$exec_prefix = xNONE; then
+               if test x$prefix = xNONE; then
+                   gnome_prefix=$ac_default_prefix/lib
+               else
+                   gnome_prefix=$prefix/lib
+               fi
+           else
+               gnome_prefix=`eval echo \`echo $libdir\``
+           fi
+       
+           if test "$no_gnome_config" = "yes"; then
+              AC_MSG_CHECKING(for gnomeConf.sh file in $gnome_prefix)
+             if test -f $gnome_prefix/gnomeConf.sh; then
+               AC_MSG_RESULT(found)
+               echo "loading gnome configuration from" \
+                    "$gnome_prefix/gnomeConf.sh"
+               . $gnome_prefix/gnomeConf.sh
+               $1
+             else
+               AC_MSG_RESULT(not found)
+               if test x$2 = xfail; then
+                 AC_MSG_ERROR(Could not find the gnomeConf.sh file that is generated by gnome-libs install)
+               fi
+             fi
+            fi
+       fi
+
+       if test -n "$3"; then
+         n="$3"
+         for i in $n; do
+           AC_MSG_CHECKING(extra library \"$i\")
+           case $i in 
+             applets)
+               AC_SUBST(GNOME_APPLETS_LIBS)
+               GNOME_APPLETS_LIBS=`$GNOME_CONFIG --libs-only-l applets`
+               AC_MSG_RESULT($GNOME_APPLETS_LIBS);;
+             capplet)
+               AC_SUBST(GNOME_CAPPLET_LIBS)
+               GNOME_CAPPLET_LIBS=`$GNOME_CONFIG --libs-only-l capplet`
+               AC_MSG_RESULT($GNOME_CAPPLET_LIBS);;
+             *)
+               AC_MSG_RESULT(unknown library)
+           esac
+         done
+       fi
+])
+
+dnl
+dnl GNOME_INIT ([additional-inits])
+dnl
+
+AC_DEFUN([GNOME_INIT],[
+       GNOME_INIT_HOOK([],fail,$1)
+])
diff --git a/neon/macros/neon-checks.m4 b/neon/macros/neon-checks.m4
new file mode 100644 (file)
index 0000000..da5fffe
--- /dev/null
@@ -0,0 +1,21 @@
+
+dnl Call these checks when compiling the libneon source package.
+
+AC_DEFUN([LIBNEON_SOURCE_CHECKS],[
+
+AC_CHECK_HEADERS(stdarg.h string.h strings.h sys/time.h regex.h \
+       stdlib.h unistd.h limits.h sys/select.h)
+
+AC_REPLACE_FUNCS(strcasecmp)
+
+dnl Check for snprintf
+AC_CHECK_FUNC(snprintf,
+       AC_DEFINE(HAVE_SNPRINTF, 1, [Define if you have snprintf]),
+       LIBOBJS="$LIBOBJS lib/snprintf.o" )
+
+AC_CHECK_FUNC(gethostbyname,
+       AC_MSG_RESULT(using libc's gethostbyname),
+       AC_CHECK_LIB(nsl,gethostbyname)
+       )
+
+])
diff --git a/neon/macros/neon-debug.m4 b/neon/macros/neon-debug.m4
new file mode 100644 (file)
index 0000000..b4d5b87
--- /dev/null
@@ -0,0 +1,41 @@
+# Copyright (C) 1998-2001 Joe Orton <joe@manyfish.co.uk>
+#
+# This file is free software; you may copy and/or distribute it with
+# or without modifications, as long as this notice is preserved.
+# This software is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE.
+
+# The above license applies to THIS FILE ONLY, the neon library code
+# itself may be copied and distributed under the terms of the GNU
+# LGPL, see COPYING.LIB for more details
+
+# This file is part of the neon HTTP/WebDAV client library.
+# See http://www.webdav.org/neon/ for the latest version. 
+# Please send any feedback to <neon@webdav.org>
+
+# Adds an --disable-debug argument to configure to allow disabling
+# debugging messages.
+#
+# Usage:
+#   NEON_WARNINGS([actions-if-debug-enabled], [actions-if-debug-disable])
+#
+
+AC_DEFUN([NEON_DEBUG], [
+
+AC_ARG_ENABLE(debug,
+       [  --disable-debug         disable runtime debugging messages ],
+       [with_debug=$enableval],
+       [with_debug=yes])  dnl Defaults to ENABLED
+
+if test "$with_debug" = "yes"; then
+       AC_DEFINE(NE_DEBUGGING, 1, [Define to enable debugging])
+       :
+       $1
+else
+       :
+       $2
+fi
+
+])
diff --git a/neon/macros/neon-socks.m4 b/neon/macros/neon-socks.m4
new file mode 100644 (file)
index 0000000..499e364
--- /dev/null
@@ -0,0 +1,45 @@
+# Copyright (C) 1998-2001 Joe Orton <joe@manyfish.co.uk>
+#
+# This file is free software; you may copy and/or distribute it with
+# or without modifications, as long as this notice is preserved.
+# This software is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE.
+
+# The above license applies to THIS FILE ONLY, the neon library code
+# itself may be copied and distributed under the terms of the GNU
+# LGPL, see COPYING.LIB for more details
+
+# This file is part of the neon HTTP/WebDAV client library.
+# See http://www.webdav.org/neon/ for the latest version. 
+# Please send any feedback to <neon@webdav.org>
+
+# Following instructions at:
+# http://www.socks.nec.com/how2socksify.html
+
+AC_DEFUN([NEON_SOCKS], [
+
+AC_ARG_WITH([--with-socks], 
+[  --with-socks[=DIR]      Use SOCKS library ],
+[
+if test "$withval" != "no"; then
+
+  if test "$withval" != "yes"; then
+       LDFLAGS="$LDFLAGS -L$withval/lib"
+       CFLAGS="$CFLAGS -I$withval/include"
+  fi
+
+  AC_CHECK_HEADERS(sock.h)
+
+  CFLAGS="$CFLAGS -DSOCKS"
+
+  AC_CHECK_LIB(socks5, connect, [NEONLIBS="$NEONLIBS -lsocks5"],
+      AC_MSG_ERROR([could not find libsocks5 for SOCKS support]))
+
+fi
+
+])
+
+])
+
diff --git a/neon/macros/neon-ssl.m4 b/neon/macros/neon-ssl.m4
new file mode 100644 (file)
index 0000000..6603a83
--- /dev/null
@@ -0,0 +1,68 @@
+# SSL macro from fetchmail
+# (C) Eric S. Raymond <esr@thyrsus.com>
+
+AC_DEFUN([NEON_SSL],[
+
+###    use option --with-ssl to compile in the SSL support
+AC_ARG_WITH(ssl,
+       [  --with-ssl=[DIR]        enable SSL support using libraries in DIR],
+       [with_ssl=$withval],
+       [with_ssl=no])
+test "$with_ssl" = "yes" && AC_DEFINE(SSL_ENABLE)
+
+if test "$with_ssl" = "yes"
+then
+#      He didn't specify an SSL location.  Let's look at some common
+#      directories where SSL has been found in the past and try and auto
+#      configure for SSL.  OpenSSL determination will be made later.
+#      This will screw up if an OpenSSL install is located in a later
+#      directory than an older SSLeay install, but the user should fix that
+#      anyways and he can override on the configure line.
+
+    for ac_dir in \
+      /usr/local/ssl \
+      /usr/ssl \
+      /local/ssl \
+      /opt/ssl \
+      ; \
+    do
+        if test -d "$ac_dir" ; then
+            with_ssl=$ac_dir
+            break;
+        fi
+    done
+fi
+
+if test -n "$with_ssl" -a "$with_ssl" != "no"
+then
+    # With the autoconfigure above, the only time this is going to be
+    # true is going to be when we could not find the headers.  If they
+    # are not in system standard locations, we are going to be broken.
+    if test "$with_ssl" = "yes"
+    then
+# Let's just define the standard location for the SSLeay root
+        with_ssl="/usr/local/ssl"
+    fi
+    if test -r $with_ssl/include/openssl/ssl.h
+    then
+###    ssl.h found under openssl.  Use openssl configuration preferentially
+        echo "Enabling OpenSSL support in $with_ssl"
+        CFLAGS="$CFLAGS -I$with_ssl/include -I$with_ssl/include/openssl"
+###    OpenBSD comes with ssl headers
+    elif test -r /usr/include/ssl/ssl.h
+    then
+        echo "Enabling SSLeay support in $with_ssl"
+        CFLAGS="$CFLAGS -I/usr/include/ssl"
+    else
+        echo "Enabling SSLeay support in $with_ssl"
+        CFLAGS="$CFLAGS -I$with_ssl/include"
+    fi
+    LDFLAGS="$LDFLAGS -L$with_ssl/lib"
+    LIBS="$LIBS -lssl -lcrypto"
+    AC_DEFINE(SSL_ENABLE)
+else
+    echo 'Disabling SSL support...'
+fi
+
+])
+
diff --git a/neon/macros/neon-test.m4 b/neon/macros/neon-test.m4
new file mode 100644 (file)
index 0000000..e748ee0
--- /dev/null
@@ -0,0 +1,24 @@
+# Copyright (C) 2001 Joe Orton <joe@manyfish.co.uk>
+#
+# This file is free software; you may copy and/or distribute it with
+# or without modifications, as long as this notice is preserved.
+# This software is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE.
+
+# The above license applies to THIS FILE ONLY, the neon library code
+# itself may be copied and distributed under the terms of the GNU
+# LGPL, see COPYING.LIB for more details
+
+# This file is part of the neon HTTP/WebDAV client library.
+# See http://www.webdav.org/neon/ for the latest version. 
+# Please send any feedback to <neon@webdav.org>
+
+# Tests needed for the neon-test common test code.
+
+AC_DEFUN([NEON_TEST], [
+
+AC_CHECK_FUNCS(pipe)
+
+])
diff --git a/neon/macros/neon-warnings.m4 b/neon/macros/neon-warnings.m4
new file mode 100644 (file)
index 0000000..3c6a645
--- /dev/null
@@ -0,0 +1,38 @@
+# Copyright (C) 1998-2000 Joe Orton.  
+# This file is free software; you may copy and/or distribute it with
+# or without modifications, as long as this notice is preserved.
+# This software is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE.
+
+# The above license applies to THIS FILE ONLY, the library code itself
+# may be copied and distributed under the terms of the GNU LGPL, see
+# COPYING.LIB for more details
+
+# This file is part of the neon HTTP/WebDAV client library.
+# See http://www.webdav.org/neon/ for the latest version. 
+# Please send any feedback to <neon@webdav.org>
+# Id: neon-warnings.m4,v 1.4 2000/07/27 15:23:24 joe Exp 
+
+# Usage:
+#   NEON_WARINGS(default)
+
+AC_DEFUN([NEON_WARNINGS],[
+
+AC_ARG_ENABLE(warnings,
+       [  --enable-warnings       enable GCC warnings during build ],
+       [with_warnings=$enableval],
+       [with_warnings=no])  dnl Defaults to DISABLED
+
+if test "$with_warnings" = "yes"; then
+       CFLAGS="$CFLAGS -Wall -ansi-pedantic -Wmissing-declarations -Winline"
+       if test -z "$with_ssl" -o "$with_ssl" = "no"; then
+               # joe: My OpenSSL ssl.h fails strict prototypes checks
+               # left right and center
+               CFLAGS="$CFLAGS -Wstrict-prototypes"
+       fi
+fi
+
+])
+
diff --git a/neon/macros/neon-xml-parser.m4 b/neon/macros/neon-xml-parser.m4
new file mode 100644 (file)
index 0000000..aca9d07
--- /dev/null
@@ -0,0 +1,84 @@
+dnl Check for XML parser.
+dnl Supports:
+dnl  *  libxml (requires version 1.8.3 or later)
+dnl  *  expat in -lexpat
+dnl  *  expat in -lxmlparse and -lxmltok (as packaged by Debian)
+dnl  *  Bundled expat if bundled is passed as arg
+
+dnl Usage:   NEON_XML_PARSER([bundled])
+
+AC_DEFUN([NEON_XML_PARSER],
+[
+
+AC_ARG_ENABLE( libxml,
+       [  --enable-libxml         force use of libxml ],
+       [neon_force_libxml=$enableval],
+       [neon_force_libxml=no])
+
+if test "$neon_force_libxml" = "no"; then
+dnl Look for expat
+AC_CHECK_LIB(expat, XML_Parse,
+       neon_expat_libs="-lexpat" neon_found_parser="expat",
+       AC_CHECK_LIB(xmlparse, XML_Parse,
+               neon_expat_libs="-lxmltok -lxmlparse" neon_found_parser="expat",
+               neon_found_parser="no",
+               -lxmltok )
+       )
+else
+       neon_found_parser="no"
+fi
+
+if test "$neon_found_parser" = "no"; then
+       # Have we got libxml 1.8.3 or later?
+       AC_CHECK_PROG(XML_CONFIG, xml-config, xml-config)
+       if test "$XML_CONFIG" != ""; then
+               # Check for recent library
+               oLIBS="$LIBS"
+               oCFLAGS="$CFLAGS"
+               LIBS="$LIBS `$XML_CONFIG --libs`"
+               CFLAGS="$CFLAGS `$XML_CONFIG --cflags`"
+               AC_CHECK_LIB(xml, xmlCreatePushParserCtxt,
+                       neon_found_parser="libxml" neon_xml_parser_message="libxml"
+                       AC_DEFINE(HAVE_LIBXML, 1, [Define if you have libxml]),
+                       CFLAGS="$oCFLAGS"
+                       LIBS="$oLIBS"
+                       AC_WARN([cannot use old libxml (1.8.3 or newer required)])
+               )
+       fi
+fi
+
+if test "$neon_found_parser" = "expat"; then
+       # This is crap. Maybe just use AC_CHECK_HEADERS and use the
+       # right file by ifdef'ing is best
+       AC_CHECK_HEADER(xmlparse.h,
+       neon_expat_incs="" neon_found_expatincs="yes",
+       AC_CHECK_HEADER(xmltok/xmlparse.h,
+       neon_expat_incs="-I/usr/include/xmltok" neon_found_expatincs="yes",
+       ))
+       if test "$neon_found_expatincs" = "yes"; then
+               AC_DEFINE(HAVE_EXPAT, 1, [Define if you have expat])
+               if test "$neon_expat_incs"; then
+                       CFLAGS="$CFLAGS $neon_expat_incs"
+               fi      
+               LIBS="$LIBS $neon_expat_libs"
+       else
+              AC_MSG_ERROR(["found expat library but could not find xmlparse.h"])
+       fi
+       neon_xml_parser_message="expat in $neon_expat_libs"
+fi
+
+if test "$neon_found_parser" = "no" ; then
+    if test "$1" = "bundled"; then
+           # Use the bundled expat sources
+           LIBOBJS="$LIBOBJS expat/xmltok/xmltok.o expat/xmltok/xmlrole.o expat/xmlparse/xmlparse.o expat/xmlparse/hashtable.o"
+           CFLAGS="$CFLAGS -DXML_DTD -Iexpat/xmlparse -Iexpat/xmltok"
+           AC_MSG_RESULT(using supplied expat XML parser)      
+           AC_DEFINE(HAVE_EXPAT, 1, [Define if you have expat] )
+           neon_xml_parser_message="supplied expat"
+    else
+       AC_MSG_ERROR([no XML parser was found])
+    fi
+
+fi
+
+])
\ No newline at end of file
diff --git a/neon/macros/neon.m4 b/neon/macros/neon.m4
new file mode 100644 (file)
index 0000000..5b4d839
--- /dev/null
@@ -0,0 +1,75 @@
+
+AC_DEFUN([NEON_LIBRARY],[
+
+AC_ARG_WITH( neon,
+       [  --with-neon            Specify location of neon library ],
+       [neon_loc="$withval"])
+
+if test "$1" = "bundle"; then
+       AC_ARG_WITH( included-neon,
+               [  --with-included-neon   Force use of included neon library ],
+               [neon_included="$withval"],
+               [neon_included="no"])
+fi
+
+AC_MSG_CHECKING(for neon location)
+
+if test "$neon_included" != "yes"; then
+    # Look for neon
+
+    if test -z "$neon_loc"; then
+    # Look in standard places
+       for d in /usr /usr/local; do
+           if test -x $d/bin/neon-config; then
+               neon_loc=$d
+           fi
+       done
+    fi
+
+    if test -x $neon_loc/bin/neon-config; then
+       # Couldn't find it!
+       AC_MSG_RESULT(found in $neon_loc)
+       NEON_CONFIG=$neon_loc/bin/neon-config
+       CFLAGS="$CFLAGS `$NEON_CONFIG --cflags`"
+       LIBS="$LIBS `$NEON_CONFIG --libs`"
+    else
+       if test "$1" = "bundle"; then
+           neon_included="yes"
+       else
+           AC_MSG_ERROR(could not find neon)
+       fi
+    fi
+fi
+
+if test "$neon_included" = "yes"; then
+    AC_MSG_RESULT(using supplied)
+    NEON_XML_PARSER
+    CFLAGS="$CFLAGS -Ilibneon"
+    LIBNEON_SOURCE_CHECKS
+    LIBOBJS="$LIBOBJS \$(NEONOBJS)"
+fi
+
+])
+
+dnl Call these checks when compiling the libneon source package.
+
+AC_DEFUN([LIBNEON_SOURCE_CHECKS],[
+
+AC_CHECK_HEADERS(stdarg.h string.h strings.h sys/time.h regex.h \
+       stdlib.h unistd.h limits.h sys/select.h)
+
+AC_REPLACE_FUNCS(strcasecmp)
+
+dnl Check for snprintf
+AC_CHECK_FUNC(snprintf,
+       AC_DEFINE(HAVE_SNPRINTF, 1, [Define if you have snprintf]),
+       LIBOBJS="$LIBOBJS lib/snprintf.o" )
+
+AC_CHECK_FUNC(gethostbyname,
+       AC_MSG_RESULT(using libc's gethostbyname),
+       AC_CHECK_LIB(nsl,gethostbyname)
+       )
+
+NEON_SSL
+
+])
diff --git a/neon/macros/readline.m4 b/neon/macros/readline.m4
new file mode 100644 (file)
index 0000000..2769ffd
--- /dev/null
@@ -0,0 +1,47 @@
+
+dnl CHECK_READLINE checks for presence of readline on the
+dnl system.
+
+AC_DEFUN([CHECK_READLINE], [
+
+AC_ARG_ENABLE(readline,
+       [  --disable-readline      disable readline support ],
+       [use_readline=$enableval],
+       [use_readline=yes])  dnl Defaults to ON (if found)
+
+if test "$use_readline" = "yes"; then
+       AC_CHECK_LIB(curses, tputs, LIBS="$LIBS -lcurses",
+               AC_CHECK_LIB(ncurses, tputs))
+       AC_CHECK_LIB(readline, readline)
+
+       AC_SEARCH_LIBS(add_history, history,
+               AC_DEFINE(HAVE_ADD_HISTORY, 1, [Define if you have the add_history function])
+       )
+
+       AC_CHECK_HEADERS(history.h readline/history.h readline.h readline/readline.h)
+
+       AC_MSG_CHECKING(whether filename_completion_function is present)
+
+       AC_TRY_LINK([
+/* need stdio.h since readline.h uses FILE * */
+#include <stdio.h>
+
+#if defined(HAVE_READLINE_H)
+#include <readline.h>
+#elif defined(HAVE_READLINE_READLINE_H)
+#include <readline/readline.h>
+#endif
+],
+[void (*foo)() = filename_completion_function;],
+       AC_DEFINE(HAVE_FILENAME_COMPLETION_FUNCTION, 1,
+               [Define if you have filename_completion_function in readline])
+       AC_MSG_RESULT(yes), 
+       AC_MSG_RESULT(no))
+       
+       msg_readline="enabled"
+else
+       msg_readline="disabled"
+fi
+
+])
+
diff --git a/neon/macros/socklen-arg-type.m4 b/neon/macros/socklen-arg-type.m4
new file mode 100644 (file)
index 0000000..2d84ac4
--- /dev/null
@@ -0,0 +1,43 @@
+dnl This function is (C) 1997,98,99 Stephan Kulow (coolo@kde.org)
+dnl Modifications (C) Joe Orton 1999,2000
+
+AC_DEFUN([SOCKLEN_ARG_TYPE],[
+
+dnl Check for the type of the third argument of getsockname
+AC_MSG_CHECKING(for the third argument of getsockname)  
+AC_CACHE_VAL(ac_cv_ksize_t,
+[AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/socket.h>
+],[
+socklen_t a=0; 
+getsockname(0,(struct sockaddr*)0, &a);
+],
+ac_cv_ksize_t=socklen_t,
+ac_cv_ksize_t=)
+if test -z "$ac_cv_ksize_t"; then
+ac_safe_cflags="$CFLAGS"
+if test "$GCC" = "yes"; then
+  CFLAGS="-Werror $CFLAGS"
+fi
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/socket.h>
+],[
+int a=0; 
+getsockname(0,(struct sockaddr*)0, &a);
+],
+ac_cv_ksize_t=int,
+ac_cv_ksize_t=size_t)
+CFLAGS="$ac_safe_cflags"
+fi
+])
+
+if test -z "$ac_cv_ksize_t"; then
+  ac_cv_ksize_t=int
+fi
+
+AC_MSG_RESULT($ac_cv_ksize_t)
+AC_DEFINE_UNQUOTED(ksize_t, $ac_cv_ksize_t, [Define to be the type of the third argument to getsockname])
+
+])
\ No newline at end of file
diff --git a/neon/macros/strftime.m4 b/neon/macros/strftime.m4
new file mode 100644 (file)
index 0000000..1f95d7e
--- /dev/null
@@ -0,0 +1,146 @@
+#serial 6
+
+dnl This macro is intended to be used solely in this file.
+dnl These are the prerequisite macros for GNU's strftime.c replacement.
+dnl FIXME: the list is far from complete
+AC_DEFUN(_jm_STRFTIME_PREREQS,
+[
+ dnl strftime.c uses localtime_r if it exists.  Check for it.
+ AC_CHECK_FUNCS(localtime_r)
+ dnl FIXME: add tests for everything in strftime.c: e.g., HAVE_BCOPY,
+ dnl HAVE_TZNAME, HAVE_TZSET, HAVE_TM_ZONE, etc.
+])
+
+dnl Determine if the strftime function has all the features of the GNU one.
+dnl
+dnl From Jim Meyering.
+dnl
+AC_DEFUN(jm_FUNC_GNU_STRFTIME,
+[AC_REQUIRE([AC_HEADER_TIME])dnl
+
+ _jm_STRFTIME_PREREQS
+
+ AC_REQUIRE([AC_C_CONST])dnl
+ AC_REQUIRE([AC_HEADER_STDC])dnl
+ AC_CHECK_HEADERS(sys/time.h)
+ AC_CACHE_CHECK([for working GNU strftime], jm_cv_func_working_gnu_strftime,
+  [AC_TRY_RUN(
+changequote(<<, >>)dnl
+<< /* Ulrich Drepper provided parts of the test program.  */
+#if STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+static int
+compare (const char *fmt, const struct tm *tm, const char *expected)
+{
+  char buf[99];
+  strftime (buf, 99, fmt, tm);
+  if (strcmp (buf, expected))
+    {
+#ifdef SHOW_FAILURES
+      printf ("fmt: \"%s\", expected \"%s\", got \"%s\"\n",
+             fmt, expected, buf);
+#endif
+      return 1;
+    }
+  return 0;
+}
+
+int
+main ()
+{
+  int n_fail = 0;
+  struct tm *tm;
+  time_t t = 738367; /* Fri Jan  9 13:06:07 1970 */
+  tm = gmtime (&t);
+
+  /* This is necessary to make strftime give consistent zone strings and
+     e.g., seconds since the epoch (%s).  */
+  putenv ("TZ=GMT0");
+
+#undef CMP
+#define CMP(Fmt, Expected) n_fail += compare ((Fmt), tm, (Expected))
+
+  CMP ("%-m", "1");            /* GNU */
+  CMP ("%A", "Friday");
+  CMP ("%^A", "FRIDAY");       /* The ^ is a GNU extension.  */
+  CMP ("%B", "January");
+  CMP ("%^B", "JANUARY");
+  CMP ("%C", "19");            /* POSIX.2 */
+  CMP ("%D", "01/09/70");      /* POSIX.2 */
+  CMP ("%F", "1970-01-09");
+  CMP ("%G", "1970");          /* GNU */
+  CMP ("%H", "13");
+  CMP ("%I", "01");
+  CMP ("%M", "06");
+  CMP ("%M", "06");
+  CMP ("%R", "13:06");         /* POSIX.2 */
+  CMP ("%S", "07");
+  CMP ("%T", "13:06:07");      /* POSIX.2 */
+  CMP ("%U", "01");
+  CMP ("%V", "02");
+  CMP ("%W", "01");
+  CMP ("%X", "13:06:07");
+  CMP ("%Y", "1970");
+  CMP ("%Z", "GMT");
+  CMP ("%_m", " 1");           /* GNU */
+  CMP ("%a", "Fri");
+  CMP ("%^a", "FRI");
+  CMP ("%b", "Jan");
+  CMP ("%^b", "JAN");
+  CMP ("%c", "Fri Jan  9 13:06:07 1970");
+  CMP ("%^c", "FRI JAN  9 13:06:07 1970");
+  CMP ("%d", "09");
+  CMP ("%e", " 9");            /* POSIX.2 */
+  CMP ("%g", "70");            /* GNU */
+  CMP ("%h", "Jan");           /* POSIX.2 */
+  CMP ("%^h", "JAN");
+  CMP ("%j", "009");
+  CMP ("%k", "13");            /* GNU */
+  CMP ("%l", " 1");            /* GNU */
+  CMP ("%m", "01");
+  CMP ("%n", "\n");            /* POSIX.2 */
+  CMP ("%p", "PM");
+  CMP ("%r", "01:06:07 PM");   /* POSIX.2 */
+  CMP ("%s", "738367");                /* GNU */
+  CMP ("%t", "\t");            /* POSIX.2 */
+  CMP ("%u", "5");             /* POSIX.2 */
+  CMP ("%w", "5");
+  CMP ("%x", "01/09/70");
+  CMP ("%y", "70");
+  CMP ("%z", "+0000");         /* GNU */
+
+  exit (n_fail ? 1 : 0);
+}
+             >>,
+changequote([, ])dnl
+            jm_cv_func_working_gnu_strftime=yes,
+             jm_cv_func_working_gnu_strftime=no,
+            dnl When crosscompiling, assume strftime is missing or broken.
+            jm_cv_func_working_gnu_strftime=no)
+  ])
+  if test $jm_cv_func_working_gnu_strftime = no; then
+    AC_SUBST(LIBOBJS)
+    LIBOBJS="$LIBOBJS strftime.$ac_objext"
+    AC_DEFINE_UNQUOTED(strftime, gnu_strftime,
+      [Define to gnu_strftime if the replacement function should be used.])
+  fi
+])
+
+AC_DEFUN(jm_FUNC_STRFTIME,
+[
+  _jm_STRFTIME_PREREQS
+  AC_REPLACE_FUNCS(strftime)
+])
diff --git a/neon/neon-config.in b/neon/neon-config.in
new file mode 100644 (file)
index 0000000..d332354
--- /dev/null
@@ -0,0 +1,73 @@
+#! /bin/sh
+# Originally from libxml, Copyright (C) Daniel Veillard
+# Modifications for neon Copyright (C) 2000 Joe Orton.
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+includedir=@includedir@
+
+usage()
+{
+    cat <<EOF
+Usage: neon-config [OPTION]
+
+Known values for OPTION are:
+
+  --prefix=DIR         change neon prefix [default $prefix]
+  --libs               print library linking information
+  --cflags             print pre-processor and compiler flags
+  --help               display this help and exit
+  --version            output version information
+EOF
+
+    exit $1
+}
+
+if test $# -eq 0; then
+    usage 1
+fi
+
+cflags=false
+libs=false
+
+while test $# -gt 0; do
+    case "$1" in
+    -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+    *) optarg= ;;
+    esac
+
+    case "$1" in
+    --prefix=*)
+       prefix=$optarg
+       ;;
+
+    --prefix)
+       echo $prefix
+       ;;
+
+    --version)
+       echo neon @NEON_VERSION@
+       exit 0
+       ;;
+
+    --help)
+       usage 0
+       ;;
+
+    --cflags)
+               echo @NEON_INCLUDEDIR@ @NEON_CFLAGS@
+               ;;
+
+    --libs)
+               echo -L@libdir@ @NEON_LIBS@
+               ;;
+
+    *)
+       usage
+       exit 1
+       ;;
+    esac
+    shift
+done
+
+exit 0
diff --git a/neon/neon.dsp b/neon/neon.dsp
new file mode 100644 (file)
index 0000000..41c512c
--- /dev/null
@@ -0,0 +1,215 @@
+# Microsoft Developer Studio Project File - Name="neon" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=neon - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "neon.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "neon.mak" CFG="neon - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "neon - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "neon - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "neon - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D 
+"_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "$(NEON_EXPAT_LITE_WIN32)" /D "NDEBUG" 
+/D "WIN32" /D "_MBCS" /D "_LIB" /D "HAVE_CONFIG_H" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"Release\neons.lib"
+
+!ELSEIF  "$(CFG)" == "neon - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" 
+/D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "$(NEON_EXPAT_LITE_WIN32)" /D 
+"_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "HAVE_CONFIG_H" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"Debug\neonsd.lib"
+
+!ENDIF
+
+# Begin Target
+
+# Name "neon - Win32 Release"
+# Name "neon - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\src\base64.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dates.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dav_207.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dav_basic.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dav_locks.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dav_props.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\hip_xml.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_auth.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_basic.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_cookies.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_redirect.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_request.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\http_utils.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\ne_alloc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\neon_i18n.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\socket.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\sslcerts.c
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\string_utils.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\uri.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Generated Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\config.hw
+
+!IF  "$(CFG)" == "neon - Win32 Release"
+
+# Begin Custom Build
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       copy .\config.hw .\config.h > nul
+       echo Created config.h from config.hw
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "neon - Win32 Debug"
+
+# Begin Custom Build
+InputPath=.\config.hw
+
+".\src\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       copy .\config.hw .\src\config.h > nul
+       echo Created config.h from config.hw
+
+# End Custom Build
+
+!ENDIF
+
+# End Source File
+# End Group
+# End Target
+# End Project
+
diff --git a/neon/neon.dsw b/neon/neon.dsw
new file mode 100644 (file)
index 0000000..a2305f5
--- /dev/null
@@ -0,0 +1,30 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "neon"=".\neon.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
+
diff --git a/neon/neon.mak b/neon/neon.mak
new file mode 100644 (file)
index 0000000..76b3c4e
--- /dev/null
@@ -0,0 +1,404 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on neon.dsp
+!IF "$(CFG)" == ""
+CFG=neon - Win32 Release
+!MESSAGE No configuration specified. Defaulting to neon - Win32 Release
+!ENDIF 
+
+!IF "$(CFG)" != "neon - Win32 Release" && "$(CFG)" != "neon - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "neon.mak" CFG="neon - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "neon - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "neon - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(EXPAT_SRC)" == ""
+!MESSAGE  
+!MESSAGE Need expat-lite source directory
+!MESSAGE  
+!MESSAGE Call with
+!MESSAGE nmake /f neon.mak EXPAT_SRC=D:\apache_1.3.20\src\lib\expat-lite 
+!MESSAGE  
+!ERROR 
+!ENDIF 
+
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+
+!IF  "$(CFG)" == "neon - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+ALL : ".\config.h" "$(OUTDIR)\neons.lib"
+
+
+CLEAN :
+       -@erase "$(INTDIR)\base64.obj"
+       -@erase "$(INTDIR)\ne_207.obj"
+       -@erase "$(INTDIR)\ne_alloc.obj"
+       -@erase "$(INTDIR)\ne_auth.obj"
+       -@erase "$(INTDIR)\ne_basic.obj"
+       -@erase "$(INTDIR)\ne_cookies.obj"
+       -@erase "$(INTDIR)\ne_dates.obj"
+       -@erase "$(INTDIR)\ne_i18n.obj"
+       -@erase "$(INTDIR)\ne_locks.obj"
+       -@erase "$(INTDIR)\ne_md5.obj"
+       -@erase "$(INTDIR)\ne_props.obj"
+       -@erase "$(INTDIR)\ne_redirect.obj"
+       -@erase "$(INTDIR)\ne_request.obj"
+       -@erase "$(INTDIR)\ne_session.obj"
+       -@erase "$(INTDIR)\ne_socket.obj"
+       -@erase "$(INTDIR)\ne_string.obj"
+       -@erase "$(INTDIR)\ne_uri.obj"
+       -@erase "$(INTDIR)\ne_utils.obj"
+       -@erase "$(INTDIR)\ne_xml.obj"
+       -@erase "$(OUTDIR)\neons.lib"
+       -@erase ".\config.h"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/c /nologo /MD /W3 /GX /O2 /I "$(EXPAT_SRC)" /D "NDEBUG" /D "HAVE_CONFIG_H" /Fo"$(INTDIR)\\" 
+
+.c{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.c{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+RSC=rc.exe
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\neon.bsc" 
+BSC32_SBRS= \
+       
+LIB32=link.exe -lib
+LIB32_FLAGS=/nologo /out:"$(OUTDIR)\neons.lib" 
+LIB32_OBJS= \
+       "$(INTDIR)\base64.obj" \
+       "$(INTDIR)\ne_xml.obj" \
+       "$(INTDIR)\ne_alloc.obj" \
+       "$(INTDIR)\ne_auth.obj" \
+       "$(INTDIR)\ne_basic.obj" \
+       "$(INTDIR)\ne_cookies.obj" \
+       "$(INTDIR)\ne_dates.obj" \
+       "$(INTDIR)\ne_i18n.obj" \
+       "$(INTDIR)\ne_locks.obj" \
+       "$(INTDIR)\ne_md5.obj" \
+       "$(INTDIR)\ne_props.obj" \
+       "$(INTDIR)\ne_redirect.obj" \
+       "$(INTDIR)\ne_request.obj" \
+       "$(INTDIR)\ne_session.obj" \
+       "$(INTDIR)\ne_socket.obj" \
+       "$(INTDIR)\ne_string.obj" \
+       "$(INTDIR)\ne_uri.obj" \
+       "$(INTDIR)\ne_utils.obj" \
+       "$(INTDIR)\ne_207.obj"
+
+"$(OUTDIR)\neons.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS)
+    $(LIB32) @<<
+  $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "neon - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+ALL : "$(OUTDIR)\neonsd.lib"
+
+
+CLEAN :
+       -@erase "$(INTDIR)\base64.obj"
+       -@erase "$(INTDIR)\ne_207.obj"
+       -@erase "$(INTDIR)\ne_alloc.obj"
+       -@erase "$(INTDIR)\ne_auth.obj"
+       -@erase "$(INTDIR)\ne_basic.obj"
+       -@erase "$(INTDIR)\ne_cookies.obj"
+       -@erase "$(INTDIR)\ne_dates.obj"
+       -@erase "$(INTDIR)\ne_i18n.obj"
+       -@erase "$(INTDIR)\ne_locks.obj"
+       -@erase "$(INTDIR)\ne_md5.obj"
+       -@erase "$(INTDIR)\ne_props.obj"
+       -@erase "$(INTDIR)\ne_redirect.obj"
+       -@erase "$(INTDIR)\ne_request.obj"
+       -@erase "$(INTDIR)\ne_session.obj"
+       -@erase "$(INTDIR)\ne_socket.obj"
+       -@erase "$(INTDIR)\ne_string.obj"
+       -@erase "$(INTDIR)\ne_uri.obj"
+       -@erase "$(INTDIR)\ne_utils.obj"
+       -@erase "$(INTDIR)\ne_xml.obj"
+       -@erase "$(INTDIR)\vc60.idb"
+       -@erase "$(INTDIR)\vc60.pdb"
+       -@erase "$(OUTDIR)\neonsd.lib"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/c /nologo /MDd /W3 /Gm /GX /Zi /Od /I "$(EXPAT_SRC)" /D "HAVE_CONFIG_H" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" 
+
+.c{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.c{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+RSC=rc.exe
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\neon.bsc" 
+BSC32_SBRS= \
+       
+LIB32=link.exe -lib
+LIB32_FLAGS=/nologo /out:"$(OUTDIR)\neonsd.lib" 
+LIB32_OBJS= \
+       "$(INTDIR)\base64.obj" \
+       "$(INTDIR)\ne_xml.obj" \
+       "$(INTDIR)\ne_alloc.obj" \
+       "$(INTDIR)\ne_auth.obj" \
+       "$(INTDIR)\ne_basic.obj" \
+       "$(INTDIR)\ne_cookies.obj" \
+       "$(INTDIR)\ne_dates.obj" \
+       "$(INTDIR)\ne_i18n.obj" \
+       "$(INTDIR)\ne_locks.obj" \
+       "$(INTDIR)\ne_md5.obj" \
+       "$(INTDIR)\ne_props.obj" \
+       "$(INTDIR)\ne_redirect.obj" \
+       "$(INTDIR)\ne_request.obj" \
+       "$(INTDIR)\ne_session.obj" \
+       "$(INTDIR)\ne_socket.obj" \
+       "$(INTDIR)\ne_string.obj" \
+       "$(INTDIR)\ne_uri.obj" \
+       "$(INTDIR)\ne_utils.obj" \
+       "$(INTDIR)\ne_207.obj"
+
+"$(OUTDIR)\neonsd.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS)
+    $(LIB32) @<<
+  $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS)
+<<
+
+!ENDIF 
+
+
+#!IF "$(NO_EXTERNAL_DEPS)" != "1"
+#!IF EXISTS("neon.dep")
+#!INCLUDE "neon.dep"
+#!ELSE 
+#!MESSAGE Warning: cannot find "neon.dep"
+#!ENDIF 
+#!ENDIF 
+
+
+!IF "$(CFG)" == "neon - Win32 Release" || "$(CFG)" == "neon - Win32 Debug"
+SOURCE=.\src\base64.c
+
+"$(INTDIR)\base64.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_207.c
+
+"$(INTDIR)\ne_207.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_alloc.c
+
+"$(INTDIR)\ne_alloc.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_auth.c
+
+"$(INTDIR)\ne_auth.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_basic.c
+
+"$(INTDIR)\ne_basic.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_cookies.c
+
+"$(INTDIR)\ne_cookies.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_dates.c
+
+"$(INTDIR)\ne_dates.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_i18n.c
+
+"$(INTDIR)\ne_i18n.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_locks.c
+
+"$(INTDIR)\ne_locks.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_md5.c
+
+"$(INTDIR)\ne_md5.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_props.c
+
+"$(INTDIR)\ne_props.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_redirect.c
+
+"$(INTDIR)\ne_redirect.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_request.c
+
+"$(INTDIR)\ne_request.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_session.c
+
+"$(INTDIR)\ne_session.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_socket.c
+
+"$(INTDIR)\ne_socket.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_string.c
+
+"$(INTDIR)\ne_string.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_uri.c
+
+"$(INTDIR)\ne_uri.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_utils.c
+
+"$(INTDIR)\ne_utils.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\ne_xml.c
+
+"$(INTDIR)\ne_xml.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\src\sslcerts.c
+SOURCE=.\config.hw
+
+!IF  "$(CFG)" == "neon - Win32 Release"
+
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       <<tempfile.bat 
+       @echo off 
+       copy .\config.hw .\src\config.h > nul 
+       echo Created config.h from config.hw
+<< 
+       
+
+!ELSEIF  "$(CFG)" == "neon - Win32 Debug"
+
+InputPath=.\config.hw
+
+".\src\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+       <<tempfile.bat 
+       @echo off 
+       copy .\config.hw .\src\config.h > nul 
+       echo Created config.h from config.hw
+<< 
+       
+
+!ENDIF 
+
+
+!ENDIF 
+
diff --git a/neon/src/.cvsignore b/neon/src/.cvsignore
new file mode 100644 (file)
index 0000000..70f06c3
--- /dev/null
@@ -0,0 +1,2 @@
+*.lo
+.libs
diff --git a/neon/src/COPYING.LIB b/neon/src/COPYING.LIB
new file mode 100644 (file)
index 0000000..161a3d1
--- /dev/null
@@ -0,0 +1,482 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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.
+\f
+  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.
+\f
+                 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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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
+\f
+     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., 59 Temple Place - Suite 330, Boston,
+    MA 02111-1307, 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/neon/src/ChangeLog b/neon/src/ChangeLog
new file mode 100644 (file)
index 0000000..aa54969
--- /dev/null
@@ -0,0 +1,351 @@
+Wed May 10 19:22:01 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon.h: Bumped version to 0.1.0.
+
+Wed May 10 17:46:48 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * uri.c (uri_parse, uri_free): New functions.
+
+Wed May 10 17:43:37 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_basic.c (get_to_fd, http_get): Set error appropriately if
+       fwrite() fails.
+
+Wed May 10 14:25:38 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_utils.c (http_debug): New function.
+
+Wed May 10 14:25:08 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_basic.c (get_callback): Call sock_call_progress.
+
+Wed May 10 14:24:20 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c (sock_call_progress): New function.  (many places): Use
+       it.
+
+Wed May 10 14:22:48 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * uri.c (uri_has_trailing_slash): Moved from being inline.
+
+Tue May  9 23:34:25 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.c: Use handler as userdata for 207 callbacks, unified
+       handler and context structures.  (start_prop, end_prop,
+       start_propelm, end_propelm): Removed functions.
+       (dav_propfind_get_current_resource): New function.
+
+Tue May  9 23:29:44 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * xalloc.[ch]: New files.
+
+Tue May  9 23:05:47 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.[ch]: Removed property and property element callbacks.
+
+Tue May  9 23:01:00 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.c: Use separate name/namespace for element names.
+       (dav_207_init_with_handler): New function.  (end_element):
+       Unescape URI in href element.
+
+Tue May  9 19:54:07 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.c (dav_propfind_allprop, dav_propfind_named, propfind,
+       start_response, end_response, start_prop, end_prop, start_propelm,
+       end_propelm): New functions; PROPFIND support.
+
+Tue May  9 19:45:17 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (build_request): Renamed from make_request.
+
+Tue May  9 19:36:01 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.[ch]: Added sock_block_reader.
+
+Tue May  9 15:52:56 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * uri.c (uri_childof): Return false when parent is the same length
+       as child.
+
+Sun May  7 15:07:49 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.c: Separated element namespace/names.
+
+Tue May  2 16:40:59 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.[ch]: Added HIP_XML_UTF8DECODE flag.
+
+Tue May  2 16:16:57 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.[ch]: Separate element name and namespace.
+
+Mon May  1 00:21:24 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.c (dav_accept_207): Moved function from dav_basic.c.
+
+       * dav_basic.c (dav_accept_207, dav_parse_xml_block): Removed
+       functions.
+
+Sun Apr 30 22:47:47 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.[ch]: Renamed dav_proppatch_item to
+       dav_proppatch_operation.
+
+Sun Apr 30 22:45:04 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.c (start_element): Clearer error message.
+
+Sun Apr 30 19:12:07 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_basic.c (http_content_type_handler, dav_hdr_handler): New
+       functions.  (http_options): Handle DAV header.
+
+Sun Apr 30 18:08:53 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_props.c (dav_proppatch): New function.
+
+Sun Apr 30 18:05:55 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_basic.c (handle_error): New function.  (end_response,
+       end_propstat): Use it.  (dav_simple_request): Don't return the 207
+       error string if we get all 2xx class status elements.
+
+Sun Apr 30 16:56:41 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_basic.c (dav_add_depth_header): New function.
+
+Sun Apr 30 14:49:06 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.c (start_element): Unknown element is only a property if
+       the parent is DAV:propstat.
+
+Sun Apr 30 14:43:28 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_basic.c (end_response, end_propstat): Only write error line
+       if we have status information and the status is not a 424.
+
+Sun Apr 30 14:28:23 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_basic.h: Added DAV_DEPTH_*.
+
+Sun Apr 30 12:47:50 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.c (check_context): Allow (and ignore) unknown elements
+       anywhere other than as the root.
+
+Sun Apr 30 12:35:39 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * string_utils.h (ASC2HEX, HEX2ASC): New macros.
+
+Sun Apr 30 12:34:37 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_auth.c [STANDALONE]: Removed. (everywhere): Switch to using
+       md5_to_ascii rather than md5_hexify.
+
+Sun Apr 30 12:32:35 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (read_response_block): Fixed to return errors
+       properly and block length to parameter.  (read_response_body):
+       Changed accordingly.
+
+Sun Apr 30 12:29:45 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.c (friendly_name): New function, was PRETTY_NAME macro.
+       (start_element, end_element): Fix COLLECT handling.
+       (hip_xml_parse): Only write parse error if the document has not
+       already been marked invalid.
+
+Sun Apr 30 12:28:36 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_basic.c (dav_simple_request): Rewritten for new 207
+       interface.  (start_response, end_response, end_propstat): New
+       functions.
+
+Sun Apr 30 12:27:52 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.c (dav_207_error): Return the parser error.
+
+Sat Apr 29 14:46:48 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c (sock_register_progress, sock_register_notify): New
+       functions.  (everywhere): Use progress + notify callbacks rather
+       than fe_*.
+
+Sat Apr 29 14:15:23 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * string_utils.c (md5_to_ascii, ascii_to_md5): New functions.
+
+Sat Apr 29 13:55:39 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.c (hip_xml_init): abort() on out-of-memory.
+
+Sat Apr 29 12:56:11 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon_i18n.h: New file.
+
+Sat Apr 29 12:55:24 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.[ch]: Re-implemented with sensible interface.
+
+Fri Apr 28 14:56:01 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_auth.c (http_auth_request_header): Renamed from
+       http_auth_request.
+
+       * http_request.c (make_request): As above.
+
+Thu Apr 13 11:52:14 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_basic.c (http_put): Switched URI and stream arguments.
+
+Thu Apr 13 09:51:21 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c: Added user_agent field to session structure.
+       (http_set_useragent): New function.  (add_fixed_headers): Only set
+       user-agent if sess->user_agent is set.
+
+Thu Apr 13 09:49:32 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (lookup_host): New function, split from
+       set_hostinfo.  (set_hostinfo): Doesn't perform DNS lookup.
+       (http_session_server): Don't do a DNS lookup if we have a proxy.
+
+Wed Apr 12 22:32:21 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_request_dispatch, http_request_create):
+       Store auth header values in local variables rather than request
+       structure.  (http_request_create): Don't leak everything on error.
+       Handle http_auth_challenge return value.
+
+Wed Apr 12 22:30:06 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_basic.c (http_options): Pass server capabilites object,
+       parse Server header to detect Apache/1.3.6 and before, indicating
+       broken 100-continue support.  (server_hdr_handler): New function.
+
+Mon Apr 10 17:42:07 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.c: Use 'int' for return values.
+
+Mon Apr 10 17:41:40 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_auth.c (is_in_domain): Dummy implementation.
+
+Mon Apr 10 17:40:21 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c: Handle read() returning 0 when it shouldn't.
+       i18n'ized error messages.
+
+Mon Apr 10 14:45:09 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dates.[ch], md5.[ch], base64.[ch]: Imported date handling
+       utilities, MD5 checksum functions, and text->base64 converter.
+
+Mon Apr 10 14:44:08 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.incl: Dependancies updated for socket.[ch].
+
+Mon Apr 10 14:43:36 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * dav_207.c: Replaced malloc() calls with xmalloc() calls.
+
+Mon Apr 10 14:42:35 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_auth.c, uri.c, string_utils.h: Replaced malloc() calls with
+       xmalloc() calls.
+
+Mon Apr 10 14:41:40 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * socket.[ch]: Imported socket handling utilities.
+
+Mon Apr 10 14:36:03 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * string_utils.h (CONCAT*): Use xmalloc.
+
+Mon Apr 10 13:52:17 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (set_sockerr): Added handling for socket errors.
+
+Sat Apr  8 13:49:07 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * string_utils.[ch]: Imported string utilites.
+
+Sat Apr  8 00:26:06 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_set_persist, http_set_expect100): New
+       functions.
+
+Sat Apr  8 00:25:37 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_basic.c (http_options): New function.
+
+Fri Apr  7 13:01:35 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * neon.h: New file.
+
+Fri Apr  7 12:59:40 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (normalize_response_length, read_response_body):
+       New functions.  (http_add_response_body_reader): Take a callback
+       to determine whether the body reader wants to read the response
+       body.
+
+Fri Apr  7 11:46:41 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (http_set_server_auth, http_set_proxy_auth): New
+       functions.  (give_creds): Use supplied callbacks for
+       authentication.  (get_request_bodysize): Send Content-Length: 0 if
+       no entity-body is being sent with a request.  (te_hdr_handler,
+       connection_hdr_handler): New functions.  (make_request): Don't use
+       Expect: 100-continue if server is not HTTP/1.1 compliant.
+       (read_message_header): Only read until HTTP_MAXIMUM_HEADER_LENGTH
+       bytes of header have been read.  (read_response_headers): No
+       hard-coded header handling.  (http_request_create): Set
+       req->method_is_head here.
+
+Thu Apr  6 14:39:28 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.c [HIP_XML_DECODE_UTF8] (decode_utf8_double): New
+       function.  (char_data) [HIP_XML_DECODE_UTF8]: Decode UTF-8.
+
+Tue Mar 28 13:54:51 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * Makefile.incl: Imported makefile fragment.
+
+Tue Mar 28 13:54:09 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.[ch] (http_get_error): New function.
+
+Thu Mar 23 18:48:42 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * hip_xml.[ch]: Imported generic XML parsing layer.
+
+       * dav_207.[ch]: Imported generic WebDAV 207 response handling.
+       
+       * dav_basic.[ch]: Imported/implemented DAV response handling and
+       basic Class 1 namespace methods.
+
+Thu Mar 23 18:46:14 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.c (add_hooks, run_hooks, http_add_destroy_hook):
+       Adding hooks support.  (add_fixed_headers): Send TE token in
+       Connection header. Only send Keep-Alive header & token to pre-1.1
+       origin servers (i.e., not proxies).
+
+Thu Mar 23 12:49:01 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_auth.[ch], uri.[ch]: Imported HTTP authentication and URI
+       handling modules.
+
+Thu Mar 23 12:47:05 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_utils.c: Imported HTTP utility functions.
+
+Thu Mar 23 12:44:38 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * http_request.[ch]: Implemented modular HTTP request handling.
+
+       * http_basic.[ch]: Implemented basic HTTP methods GET, PUT, and
+       PUT with If-Unmodified.
+
diff --git a/neon/src/Makefile.in b/neon/src/Makefile.in
new file mode 100644 (file)
index 0000000..534214a
--- /dev/null
@@ -0,0 +1,109 @@
+#
+# TODO: document properly.
+#
+# You need to AC_SUBST:
+#  - NEONOBJS
+#  - NEON_TARGET (to libneon.la or libneon.a)
+#  - NEON_INTERFACE_VERSION (if necessary, which it's probably not for you)
+#  - OBJ_EXT (to .lo or .o)
+#
+
+SHELL = @SHELL@
+
+CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+DEFS = @DEFS@
+INCLUDES = -I.. -I.
+top_builddir = @top_builddir@
+CC = @CC@
+CCLD = $(CC)
+AR = @AR@
+RANLIB = @RANLIB@
+
+LIBS = @LIBS@
+
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+libdir = @libdir@
+
+LIBTOOL = @LIBTOOL@
+LINK = $(LIBTOOL) --quiet --mode=link $(CCLD) $(LDFLAGS)
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(CFLAGS)
+NEON_INTERFACE_VERSION = @NEON_INTERFACE_VERSION@
+
+OBJECTS = @NEONOBJS@
+
+obj_ext = @OBJ_EXT@
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o
+
+NEON_TARGET = @NEON_TARGET@
+
+all: $(NEON_TARGET)
+
+.c.lo:
+       $(LIBTOOL) --quiet --mode=compile $(COMPILE) -c $< -o $@
+.c.o:
+       $(COMPILE) -c $< -o $@
+
+libneon.la: $(OBJECTS)
+       $(LINK) -rpath $(libdir) -version-info $(NEON_INTERFACE_VERSION) -o $@ $(LDFLAGS) $(OBJECTS) $(LIBS)
+
+libneon.a: $(OBJECTS)
+       $(AR) cru $@ $(OBJECTS)
+       $(RANLIB) $@
+
+clean:
+       rm -f @NEONOBJS@ $(NEON_TARGET)
+
+neonreq = http_request.h http_utils.h string_utils.h nsocket.h \
+       ne_alloc.h $(top_builddir)/config.h
+
+http_request.$(obj_ext): http_request.c neon_config.h $(neonreq) \
+       neon_i18n.h http_private.h
+
+socket.$(obj_ext): socket.c nsocket.h $(top_builddir)/config.h string_utils.h
+
+http_auth.$(obj_ext): http_auth.c http_auth.h string_utils.h \
+       $(top_builddir)/config.h dates.h base64.h neon_md5.h
+
+dav_basic.$(obj_ext): dav_basic.c $(neonreq) dav_207.h hip_xml.$(obj_ext)
+
+http_basic.$(obj_ext): http_basic.c http_basic.h $(neonreq)
+
+http_utils.$(obj_ext): http_utils.c $(top_builddir)/config.h \
+       http_utils.h dates.h neon_config.h
+
+hip_xml.$(obj_ext): hip_xml.c hip_xml.h string_utils.h http_utils.h \
+       $(top_builddir)/config.h
+
+dav_207.$(obj_ext): dav_207.c dav_207.h hip_xml.h \
+       $(top_builddir)/config.h http_utils.h neon_i18n.h
+
+string_utils.$(obj_ext): string_utils.c string_utils.h ne_alloc.h \
+       $(top_builddir)/config.h
+
+ne_alloc.$(obj_ext): ne_alloc.c ne_alloc.h $(top_builddir)/config.h
+
+dates.$(obj_ext): dates.c dates.h $(top_builddir)/config.h
+
+base64.$(obj_ext): base64.c base64.h $(top_builddir)/config.h
+
+uri.$(obj_ext): uri.c uri.h http_utils.h string_utils.h ne_alloc.h \
+       $(top_builddir)/config.h
+
+md5.$(obj_ext): md5.c neon_md5.h $(top_builddir)/config.h
+
+dav_props.$(obj_ext): dav_props.c $(top_builddir)/config.h \
+       dav_props.h dav_207.h hip_xml.h
+
+dav_locks.$(obj_ext): dav_locks.c $(neonreq) dav_locks.h dav_207.h hip_xml.h
+
+http_redirect.$(obj_ext): http_redirect.c $(neonreq) http_redirect.h \
+       uri.h http_private.h
+
+http_cookies.$(obj_ext): http_cookies.c $(neonreq) http_cookies.h uri.h \
+       http_private.h
diff --git a/neon/src/Makefile.incl b/neon/src/Makefile.incl
new file mode 100644 (file)
index 0000000..87281b8
--- /dev/null
@@ -0,0 +1,45 @@
+# Emacs, are you listening? This is a -*- makefile -*-. Thankyou.
+#
+# This is a Makefile fragment.
+# To use it, set:
+#   neon_dir   to the location of the neon source directory 
+#   config_h   to the location of the config.h file
+
+neonreq = $(neon_dir)/http_request.h $(neon_dir)/http_utils.h $(neon_dir)/string_utils.h
+
+$(neon_dir)/http_request.o: $(neon_dir)/http_request.c \
+       $(neonreq) $(config_h) $(neon_dir)/neon.h $(neon_dir)/socket.h \
+       $(neon_dir)/neon_i18n.h
+
+$(neon_dir)/socket.o: $(neon_dir)/socket.c $(neon_dir)/socket.h \
+       $(config_h) $(neon_dir)/string_utils.h
+
+$(neon_dir)/http_auth.o: $(neon_dir)/http_auth.c                       \
+       $(neon_dir)/http_auth.h $(neon_dir)/string_utils.h              \
+       $(config_h) $(neon_dir)/dates.h $(neon_dir)/base64.h            \
+       $(neon_dir)/md5.h
+
+$(neon_dir)/dav_basic.o: $(neon_dir)/dav_basic.c $(neon_dir)/dav_207.h  \
+       $(neonreq) $(neon_dir)/hip_xml.o
+
+$(neon_dir)/http_basic.o: $(neon_dir)/http_basic.c $(neon_dir)/http_basic.h \
+       $(neonreq)
+
+$(neon_dir)/http_utils.o: $(neon_dir)/http_utils.c $(neon_dir)/http_utils.h \
+       $(neon_dir)/dates.h $(config_h)
+
+$(neon_dir)/hip_xml.o: $(neon_dir)/hip_xml.c $(neon_dir)/hip_xml.h    \
+       $(neon_dir)/string_utils.h $(neon_dir)/http_utils.h $(config_h)
+
+$(neon_dir)/dav_207.o: $(neon_dir)/dav_207.c $(neon_dir)/dav_207.h \
+       $(neon_dir)/hip_xml.h $(config_h) $(neon_dir)/http_utils.h       \
+       $(neon_dir)/neon_i18n.h
+
+$(neon_dir)/dates.o: $(neon_dir)/dates.c $(neon_dir)/dates.h \
+       $(config_h)
+
+$(neon_dir)/md5.o: $(neon_dir)/md5.c $(neon_dir)/md5.h $(config_h)
+
+$(neon_dir)/dav_props.o: $(neon_dir)/dav_props.c $(neon_dir)/dav_props.h \
+       $(neon_dir)/dav_207.h $(neon_dir)/hip_xml.h
+
diff --git a/neon/src/README b/neon/src/README
new file mode 100644 (file)
index 0000000..0cb9a3f
--- /dev/null
@@ -0,0 +1,15 @@
+
+This is the source directory of the 'neon' HTTP/WebDAV client library,
+which can be bundled inside other packages. For the complete neon
+package, see:
+
+       http://www.webdav.org/neon/
+
+This source directory may be distributed and/or modified under the
+terms of the GNU Library General Public License, as given in
+COPYING.LIB.
+
+Please send questions, bug reports, feature requests, etc, regarding
+the neon library, to the mailing list at:
+
+       neon@webdav.org
diff --git a/neon/src/TODO b/neon/src/TODO
new file mode 100644 (file)
index 0000000..125bd33
--- /dev/null
@@ -0,0 +1,98 @@
+
+To Do List for neon                                      -*- text -*-
+-------------------     Id: TODO,v 1.12 2000/05/10 18:15:47 joe Exp 
+
+Please submit feature requests to <mailto:neon@webdav.org>
+
+1. Support for HTTP-extended authoring methods ala WebRFM etc; using
+   New-URI header etc.  Also support the BROWSE and INDEX methods.  The
+   protocol is documented at:
+   http://www.ics.uci.edu/pub/ietf/webdav/ns_dav.html
+
+2. Add proper domain support to authentication code. (requires full
+   URI parsing support). Need to tell the auth layer the server
+   details.
+
+3. Add a callback to determine whether a specific request should go
+   through the proxy server or not... based on URI, maybe method too
+   since lots of HTTP/1.0 proxies break on DAV requests?
+
+4. Better cnonce generation for authentication: use /dev/random or
+   whatever like mod_auth_digest.
+
+5. Add request hooks to remove all authentication code from
+   http_request.c.
+
+6. PUT/GET with ranges... http_get_range
+
+7. hip_xml/dav_207/dav_prop might need a revamp.
+
+   hip_xml: use an expat-esque interface; i.e. make hip_xml_parser
+   opaque and add API to set handlers rather than initialize
+   structures directly. Two problems need to be solved more elegantly:
+
+   1) Allowing "sub-handler" for allowing parsing property element
+   contents independantly of the overally 207 response parse.
+
+   2) Allow "mixed-mode" parsing of XML which mixes cdata + elements.
+
+   Probably, make hip_xml more of a "utility" layer rather than a
+   "driving" layer; abstract out expat/libxml, namespace lookups,
+   element name -> id mapping, easy CDATA handling...
+
+8. WebDAV class 2 locking. Problems: this requires parsing the XML
+   within a property element. This could be achieved by doing a
+   completely new XML parse of the property value returned by end_prop
+   from the 207 code, but this is a bit noddy. Options:
+
+   a) Ideally: extend hip_xml and the 207 layer to allow doing a
+   proper start/end_element/cdata parse of the XML *within* a
+   property. This is tricky since it means extending hip_xml to
+   *dynamically* determine whether to be in "collect" mode or not. Add
+   another callback for this?
+
+9. DeltaV support (http://www.webdav.org/deltav/). See also the
+   inversion project (http://inversion.tigris.org/) who might build a
+   versioning system over DAV.
+
+10. ACL support (http://www.webdav.org/acl/)
+
+11. DASL support (http://www.webdav.org/dasl/). Xythos have server
+    support for this (www.sharemation.com).
+
+12. SSL/TSL support... make it pluggable so we don't have to export
+    crypto-related code at ALL?
+
+13. Should we really be abort()'ing on out-of-memory? It makes a lot
+    of code MUCH simpler (esp. sbuffer_* usage).
+
+14. Nicer request-header manipulation... some kind of indexed data
+    structure, so we're sure we don't add the same header to the
+    request twice (e.g. Cache-Control). Must be at least as flexible
+    as sbuffer usage, though.
+
+16. Socket status notification (socket.c:sock_register_*) is awful.
+
+17. Should we really be i18n'izing the low-level error messages in
+    http_request.c, dav_207.c ? It seems nice and clever to, so the
+    user REALLY know what is going wrong with the server (probably),
+    but it is maybe a bit frightening.
+    
+18. PROPFIND/propnames support.
+
+19. libtool support, for proper shared libraries.
+
+20. Add full URI parser + handling. Or stop pretending we are doing
+    "URI" parsing, and just handle HTTP URL's.
+
+21. Storing multiple authentication "sessions" within an actual
+    http_auth_session, so I log into e.g. /foo/ and /bar/ (which
+    are not in the same authentication domain)
+    switch between them without having to re-enter passwords all the
+    time.
+
+22. Handle PROPFIND property error responses properly.
+
+23. Mechanism for aborting a request mid-response; e.g., when a GET
+    fails due to out of disk space, abort the download.
+
diff --git a/neon/src/base64.c b/neon/src/base64.c
new file mode 100644 (file)
index 0000000..54bb224
--- /dev/null
@@ -0,0 +1,103 @@
+/* 
+   Text->Base64 convertion, from RFC1521
+   Copyright (C) 1998,1999,2000 Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: base64.c,v 1.3 2000/05/09 18:26:58 joe Exp 
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "xalloc.h"
+#include "base64.h"
+
+/* Base64 specification from RFC 1521 */
+
+static const char b64_alphabet[65] = { 
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "abcdefghijklmnopqrstuvwxyz"
+    "0123456789+/=" };
+    
+char *base64( const char *text ) 
+{
+    /* The tricky thing about this is doing the padding at the end,
+     * doing the bit manipulation requires a bit of concentration only */
+    char *buffer, *point;
+    int inlen, outlen;
+    
+    /* Use 'buffer' to store the output. Work out how big it should be...
+     * This must be a multiple of 4 bytes */
+
+    inlen = strlen( text );
+    outlen = (inlen*4)/3;
+    if( (inlen % 3) > 0 ) /* got to pad */
+       outlen += 4 - (inlen % 3);
+    
+    buffer = xmalloc( outlen + 1 ); /* +1 for the \0 */
+    
+    /* now do the main stage of conversion, 3 bytes at a time,
+     * leave the trailing bytes (if there are any) for later */
+
+    for( point=buffer; inlen>=3; inlen-=3, text+=3 ) {
+       *(point++) = b64_alphabet[ (*text)>>2 ]; 
+       *(point++) = b64_alphabet[ ((*text)<<4 & 0x30) | (*(text+1))>>4 ]; 
+       *(point++) = b64_alphabet[ ((*(text+1))<<2 & 0x3c) | (*(text+2))>>6 ];
+       *(point++) = b64_alphabet[ (*(text+2)) & 0x3f ];
+    }
+
+    /* Now deal with the trailing bytes */
+    if( inlen ) {
+       /* We always have one trailing byte */
+       *(point++) = b64_alphabet[ (*text)>>2 ];
+       *(point++) = b64_alphabet[ ( ((*text)<<4 & 0x30) |
+                                    (inlen==2?(*(text+1))>>4:0) ) ]; 
+       *(point++) = (inlen==1?'=':b64_alphabet[ (*(text+1))<<2 & 0x3c ] );
+       *(point++) = '=';
+    }
+
+    /* Null-terminate */
+    *point = '\0';
+
+    return buffer;
+}
+
+#ifdef BASE64_TEST
+
+/* Standalone tester */
+
+#include <stdio.h>
+
+/* Tester for Base64 algorithm */
+int main( int argc, char **argv ) {
+    char *out;
+    if( argc != 2 ) {
+       printf( "Usage: %s plaintext\n", argv[0] );
+       exit(-1);
+    }
+    out = base64( argv[1] );
+    printf( "Plain: [%s], Base64: [%s]\n", argv[1], out );
+    return 0;
+}
+
+#endif /* BASE64_TEST */
+
diff --git a/neon/src/base64.h b/neon/src/base64.h
new file mode 100644 (file)
index 0000000..3c4f55a
--- /dev/null
@@ -0,0 +1,32 @@
+/* 
+   Text->Base64 convertion, from RFC1521
+   Copyright (C) 1998-2000 Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef BASE64_H
+#define BASE64_H
+
+/* Plain->Base64 converter - will malloc the buffer to the correct size,
+ * and fill it with the Base64 conversion of the parameter.
+ * Base64 specification from RFC 1521. You must free() it. */
+
+char *base64( const char *text );
+
+#endif /* BASE64_H */
+
diff --git a/neon/src/dates.c b/neon/src/dates.c
new file mode 100644 (file)
index 0000000..6c5e93d
--- /dev/null
@@ -0,0 +1,163 @@
+/* 
+   Date manipulation routines
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: dates.c,v 1.5 2000/05/09 18:25:37 joe Exp 
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#include <time.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+
+#include "xalloc.h"
+#include "dates.h"
+
+/* Generic date manipulation routines. */
+
+/* RFC1123: Sun, 06 Nov 1994 08:49:37 GMT */
+#define RFC1123_FORMAT "%3s, %02d %3s %4d %02d:%02d:%02d GMT"
+/* RFC850:  Sunday, 06-Nov-94 08:49:37 GMT */
+#define RFC1036_FORMAT "%s, %2d-%3s-%2d %2d:%2d:%2d GMT"
+/* asctime: Wed Jun 30 21:49:08 1993 */
+#define ASCTIME_FORMAT "%3s %3s %2d %2d:%2d:%2d %4d"
+
+static const char *rfc1123_weekdays[7] = { 
+    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 
+};
+static const char *short_months[12] = { 
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* Returns the time/date GMT, in RFC1123-type format: eg
+ *  Sun, 06 Nov 1994 08:49:37 GMT. */
+char *rfc1123_date( time_t anytime ) {
+    struct tm *gmt;
+    char *ret;
+    gmt = gmtime( &anytime );
+    ret = xmalloc( 29 + 1 ); /* dates are 29 chars long */
+/*  it goes: Sun, 06 Nov 1994 08:49:37 GMT */
+    snprintf( ret, 30, RFC1123_FORMAT,
+             rfc1123_weekdays[gmt->tm_wday], gmt->tm_mday, 
+             short_months[gmt->tm_mon], 1900 + gmt->tm_year, 
+             gmt->tm_hour, gmt->tm_min, gmt->tm_sec );
+    
+    return ret;
+}
+
+/* Takes an RFC1123-formatted date string and returns the time_t.
+ * Returns (time_t)-1 if the parse fails. */
+time_t rfc1123_parse( const char *date ) {
+    struct tm gmt = {0};
+    static char wkday[4], mon[4];
+    int n;
+/*  it goes: Sun, 06 Nov 1994 08:49:37 GMT */
+    n = sscanf( date, RFC1123_FORMAT,
+           wkday, &gmt.tm_mday, mon, &gmt.tm_year, &gmt.tm_hour,
+           &gmt.tm_min, &gmt.tm_sec );
+    /* Is it portable to check n==7 here? */
+    gmt.tm_year -= 1900;
+    for( n=0; n<12; n++ )
+       if( strcmp( mon, short_months[n] ) == 0 )
+           break;
+    /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+     * since the mktime will then fail */
+    gmt.tm_mon = n;
+    return mktime( &gmt );
+}
+
+/* Takes a string containing a RFC1036-style date and returns the time_t */
+time_t rfc1036_parse( const char *date ) {
+    struct tm gmt = {0};
+    int n;
+    static char wkday[10], mon[4];
+    /* RFC850/1036 style dates: Sunday, 06-Nov-94 08:49:37 GMT */
+    n = sscanf( RFC1036_FORMAT,
+               wkday, &gmt.tm_mday, mon, &gmt.tm_year,
+               &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec );
+    /* portable to check n here? */
+    for( n=0; n<12; n++ )
+       if( strcmp( mon, short_months[n] ) == 0 )
+           break;
+    /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+     * since the mktime will then fail */
+    gmt.tm_mon = n;
+    return mktime( &gmt );
+}
+
+
+/* (as)ctime dates are like:
+ *    Wed Jun 30 21:49:08 1993
+ */
+time_t asctime_parse( const char *date ) {
+    struct tm gmt = {0};
+    int n;
+    static char wkday[4], mon[4];
+    n = sscanf( ASCTIME_FORMAT,
+               wkday, mon, &gmt.tm_mday, 
+               &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec,
+               &gmt.tm_year );
+    /* portable to check n here? */
+    for( n=0; n<12; n++ )
+       if( strcmp( mon, short_months[n] ) == 0 )
+           break;
+    /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+     * since the mktime will then fail */
+    gmt.tm_mon = n;
+    return mktime( &gmt );
+}
+
+#undef RFC1036_FORMAT
+#undef ASCTIME_FORMAT
+#undef RFC1123_FORMAT
+
+#ifdef RFC1123_TEST
+
+int main( int argc, char **argv ) {
+    time_t now, in;
+    char *out;
+    if( argc > 1 ) {
+       printf( "Got: %s\n", argv[1] );
+       in = rfc1123_parse( argv[1] );
+       printf( "Parsed: %d\n", in );
+       out = rfc1123_date( in );
+       printf( "Back again: %s\n", out );
+    } else {
+       now = time(NULL);
+       out = rfc1123_date(now);
+       in = rfc1123_parse(out);
+       printf( "time(NULL) = %d\n", now );
+       printf( "RFC1123 Time: [%s]\n", out );
+       printf( "Parsed = %d\n", in );
+       out = rfc1123_date( in );
+       printf( "Back again: [%s]\n", out );
+    }
+    return 0;
+}
+
+#endif
+
+
diff --git a/neon/src/dates.h b/neon/src/dates.h
new file mode 100644 (file)
index 0000000..9e6a995
--- /dev/null
@@ -0,0 +1,42 @@
+/* 
+   Date manipulation routines
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef DATES_H
+#define DATES_H
+
+#include "config.h"
+
+#include <sys/types.h>
+
+/* Date manipulation routines as per RFC1123 and RFC1036 */
+
+/* Return current date/time in RFC1123 format */
+char *rfc1123_date( time_t anytime );
+
+/* Returns time from date/time in RFC1123 format */
+time_t rfc1123_parse( const char *date );
+
+time_t rfc1036_parse( const char *date );
+
+/* Parses asctime date string */
+time_t asctime_parse( const char *date );
+
+#endif /* DATES_H */
diff --git a/neon/src/dav_207.c b/neon/src/dav_207.c
new file mode 100644 (file)
index 0000000..6ee7807
--- /dev/null
@@ -0,0 +1,262 @@
+/* 
+   WebDAV 207 multi-status response handling
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: dav_207.c,v 1.17 2000/05/10 12:02:23 joe Exp  
+*/
+
+/* Generic handling for WebDAV 207 Multi-Status responses. */
+
+#include <config.h>
+
+#include "http_utils.h"
+#include "hip_xml.h"
+#include "dav_207.h"
+#include "uri.h"
+#include "xalloc.h"
+
+#include "neon_i18n.h"
+
+struct dav_207_parser_s {
+    dav_207_start_response start_response;
+    dav_207_end_response end_response;
+    dav_207_start_propstat start_propstat;
+    dav_207_end_propstat end_propstat;
+    struct hip_xml_parser parser;
+    struct hip_xml_handler handler;
+    void *userdata;
+    /* current position */
+    void *response, *propstat;
+    /* caching */
+    http_status status;
+    char *description, *href, *status_line;
+};
+
+const static struct hip_xml_elm elements[] = {
+    { "DAV:", "multistatus", DAV_ELM_multistatus, 0 },
+    { "DAV:", "response", DAV_ELM_response, 0 },
+    { "DAV:", "responsedescription", DAV_ELM_responsedescription, 0 },
+    { "DAV:", "href", DAV_ELM_href, HIP_XML_CDATA },
+    { "DAV:", "propstat", DAV_ELM_propstat, 0 },
+    { "DAV:", "prop", DAV_ELM_prop, 0 },
+    { "DAV:", "status", DAV_ELM_status, HIP_XML_CDATA },
+#if 0
+    { "", "", HIP_ELM_unknown, HIP_XML_COLLECT },
+#endif
+    { NULL }
+};
+
+/* Set the callbacks for the parser */
+void dav_207_set_response_handlers( dav_207_parser *p,
+                                   dav_207_start_response start,
+                                   dav_207_end_response end )
+{
+    p->start_response = start;
+    p->end_response = end;
+}
+
+void dav_207_set_propstat_handlers( dav_207_parser *p,
+                                   dav_207_start_propstat start,
+                                   dav_207_end_propstat end )
+{
+    p->start_propstat = start;
+    p->end_propstat = end;
+}
+
+/* Parse an input block, as hip_xml_parse */
+void dav_207_parse( void *parser, const char *block, size_t len )
+{
+    dav_207_parser *p = parser;
+    hip_xml_parse( &p->parser, block, len );
+}
+
+static int 
+start_element( void *userdata, const struct hip_xml_state *s, const hip_xml_char **atts ) 
+{
+    dav_207_parser *p = userdata;
+    
+    switch( s->elm->id ) {
+    case DAV_ELM_response:
+       /* Create new response delayed until we get HREF */
+       break;
+    case DAV_ELM_propstat:
+       if( p->start_propstat ) {
+           p->propstat = (*p->start_propstat)( p->userdata, p->response );
+       }
+       break;
+    }
+    return 0;
+}
+
+static int 
+end_element( void *userdata, const struct hip_xml_state *s, const char *cdata )
+{
+    dav_207_parser *p = userdata;
+    
+    switch( s->elm->id ) {
+    case DAV_ELM_responsedescription:
+       p->description = xstrdup(cdata);
+       break;
+    case DAV_ELM_href:
+       /* Now we have the href, begin the response */
+       if( p->start_response && cdata != NULL ) {
+           char *uri = uri_unescape( cdata );
+           p->response = (*p->start_response)( p->userdata, uri );
+           free( uri );
+       }
+       break;
+    case DAV_ELM_status:
+       switch( s->parent->elm->id ) {
+       case DAV_ELM_propstat:
+       case DAV_ELM_response:
+           p->status_line = xstrdup(cdata);
+           if( http_parse_statusline( p->status_line, &p->status ) ) {
+               snprintf( p->parser.error, BUFSIZ,
+                         _("Invalid HTTP status line in status element at line %d of response"), 
+                         hip_xml_currentline(&p->parser) );
+               HTTP_FREE( p->status_line );
+               return -1;
+           }
+           break;
+       }
+       break;
+    case DAV_ELM_propstat:
+       if( p->end_propstat ) {
+           (*p->end_propstat)( p->userdata, p->propstat, p->status_line,
+                               p->status_line?&p->status:NULL, p->description );
+       }
+       HTTP_FREE( p->status_line );
+       break;
+    case DAV_ELM_response:
+       if( p->end_response ) {
+           (*p->end_response)( p->userdata, p->response, p->status_line,
+                               p->status_line?&p->status:NULL );
+       }
+       HTTP_FREE( p->status_line );
+       break;
+    }
+    return 0;
+}
+
+/* This should map directly from the DTD... with the addition of
+ * ignoring anything we don't understand, being liberal in what we
+ * accept. */
+static int check_context( hip_xml_elmid parent, hip_xml_elmid child ) 
+{
+    switch( parent ) {
+    case HIP_ELM_root:
+       switch( child ) {
+       case DAV_ELM_multistatus:
+       case DAV_ELM_response: /* not sure why this is here... */
+           return 0;
+       default:
+       }
+       break;
+    case DAV_ELM_multistatus:
+       /* <!ELEMENT multistatus (response+, responsedescription?) > */
+       switch( child ) {
+       case DAV_ELM_response:
+       case DAV_ELM_responsedescription:
+       case HIP_ELM_unknown:
+           return 0;
+       default:
+       }
+       break;
+    case DAV_ELM_response:
+       /* <!ELEMENT response (href, ((href*, status)|(propstat+)),
+          responsedescription?) > */
+       switch( child ) {
+       case DAV_ELM_href:
+       case DAV_ELM_status:
+       case DAV_ELM_propstat:
+       case DAV_ELM_responsedescription:
+       case HIP_ELM_unknown:
+           return 0;
+       default:
+       }
+       break;
+    case DAV_ELM_propstat:
+       /* <!ELEMENT propstat (prop, status, responsedescription?) > */
+       switch( child ) {
+       case DAV_ELM_prop: 
+       case DAV_ELM_status:
+       case DAV_ELM_responsedescription:
+       case HIP_ELM_unknown:
+           return 0;
+       default:
+       }
+       break;
+    case DAV_ELM_prop:
+       /* <!ELEMENT prop ANY > */
+       switch( child ) {
+       case HIP_ELM_unknown:
+           return 0;
+       default:
+           break;
+       }
+       break;
+    case HIP_ELM_unknown:
+       return 0;
+    default:
+       break;
+    }
+    return -1;
+}
+
+static dav_207_parser *init_parser( void *userdata, struct hip_xml_handler *extra )
+{
+    dav_207_parser *p = xmalloc( sizeof(dav_207_parser) );
+    memset( p, 0, sizeof(dav_207_parser) );
+    p->handler.validate_cb = check_context;
+    p->handler.startelm_cb = start_element;
+    p->handler.endelm_cb = end_element;
+    p->handler.userdata = p;
+    p->handler.elements = elements;
+    p->handler.next = extra;
+    p->userdata = userdata;
+    hip_xml_init( &p->parser, &p->handler );
+    return p;
+}   
+
+dav_207_parser *dav_207_init_with_handler( void *userdata, 
+                                          struct hip_xml_handler *extra )
+{
+    return init_parser( userdata, extra );
+}
+
+dav_207_parser *dav_207_init( void *userdata )
+{
+    return init_parser( userdata, NULL );
+}
+
+int dav_207_finish( dav_207_parser *p ) {
+    int ret = hip_xml_finish( &p->parser );
+    HTTP_FREE( p->response );
+    free( p );
+    return ret;
+}
+
+const char *dav_207_error( dav_207_parser *p )
+{
+    return p->parser.error;
+}
+
+int dav_accept_207( void *userdata, http_req *req, http_status *status )
+{
+    return (status->code == 207);
+}
diff --git a/neon/src/dav_207.h b/neon/src/dav_207.h
new file mode 100644 (file)
index 0000000..2881d62
--- /dev/null
@@ -0,0 +1,91 @@
+/* 
+   WebDAV 207 multi-status response handling
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef DAV207_H
+#define DAV207_H
+
+#include "hip_xml.h"
+#include "http_utils.h" /* for struct http_status */
+#include "http_request.h" /* for http_req */
+
+#define DAV_ELM_multistatus (101)
+#define DAV_ELM_response (102)
+#define DAV_ELM_responsedescription (103)
+#define DAV_ELM_href (104)
+#define DAV_ELM_propstat (105)
+#define DAV_ELM_prop (106)
+#define DAV_ELM_status (107)
+
+struct dav_207_parser_s;
+typedef struct dav_207_parser_s dav_207_parser;
+
+/* The name of a WebDAV property. */
+typedef struct {
+    const char *nspace, *name;
+} dav_propname;
+
+/* The handler structure: you provide a set of callbacks.
+ * They are called in the order they are listed... start/end_prop
+ * multiple times before end_prop, start/end_propstat multiple times
+ * before an end_response, start/end_response multiple times.
+ */
+
+/* TODO: do we need to pass userdata to ALL of these? We could get away with
+ * only passing the userdata to the start_'s and relying on the caller
+ * to send it through as the _start return value if they need it. */
+
+typedef void *(*dav_207_start_response)( void *userdata, const char *href );
+typedef void (*dav_207_end_response)( 
+    void *userdata, void *response, const char *status_line, const http_status *status );
+
+typedef void *(*dav_207_start_propstat)( void *userdata, void *response );
+typedef void (*dav_207_end_propstat)( 
+    void *userdata, void *propstat, const char *status_line, const http_status *status, 
+    const char *description );
+
+/* Create a 207 parser */
+
+dav_207_parser *dav_207_init( void *userdata );
+
+dav_207_parser *dav_207_init_with_handler( void *userdata, 
+                                          struct hip_xml_handler *extra );
+
+/* Set the callbacks for the parser */
+
+void dav_207_set_response_handlers( 
+    dav_207_parser *p, dav_207_start_response start, dav_207_end_response end );
+
+void dav_207_set_propstat_handlers( 
+    dav_207_parser *p, dav_207_start_propstat start, dav_207_end_propstat end );
+
+/* Parse an input block, as hip_xml_parse */
+void dav_207_parse( void *parser, const char *block, size_t len );
+
+/* Finish parsing... returns 0 if the parse was successful, else
+ * non-zero. */
+int dav_207_finish( dav_207_parser *p );
+
+const char *dav_207_error( dav_207_parser *p );
+
+/* An acceptance function which only accepts 207 responses */
+int dav_accept_207( void *userdata, http_req *req, http_status *status );
+
+#endif /* DAV207_H */
diff --git a/neon/src/dav_basic.c b/neon/src/dav_basic.c
new file mode 100644 (file)
index 0000000..eccc2ac
--- /dev/null
@@ -0,0 +1,232 @@
+/* 
+   WebDAV Class 1 namespace operations and 207 error handling
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: dav_basic.c,v 1.12 2000/05/09 18:28:00 joe Exp 
+*/
+
+#include "config.h"
+
+#include "http_request.h"
+
+#include "dav_basic.h"
+#include "uri.h" /* for uri_has_trailing_slash */
+#include "http_basic.h" /* for http_content_type */
+#include "string_utils.h" /* for sbuffer */
+#include "dav_207.h"
+
+/* Handling of 207 errors: we keep a string buffer, and append
+ * messages to it as they come down.
+ *
+ * Note, 424 means it would have worked but something else went wrong.
+ * We will have had the error for "something else", so we display
+ * that, and skip 424 errors.  TODO: should display 'description' too,
+ * but find out whether servers actually use this first. */
+
+/* This is passed as userdata to the 207 code. */
+struct context {
+    char *href;
+    sbuffer buf;
+    unsigned int is_error;
+};
+
+static void *start_response( void *userdata, const char *href )
+{
+    struct context *ctx = userdata;
+    HTTP_FREE( ctx->href );
+    ctx->href = xstrdup(href);
+    return NULL;
+}
+
+static void handle_error( struct context *ctx,
+                        const char *status_line, const http_status *status )
+{
+    if( status && status->class != 2 ) {
+       ctx->is_error = 1;
+       if( status->code != 424 ) {
+           sbuffer_concat( ctx->buf, ctx->href, ": ", status_line, "\n", NULL );
+       }
+    }
+}
+
+static void end_response( void *userdata, void *response, const char *status_line,
+                         const http_status *status )
+{
+    struct context *ctx = userdata;
+    handle_error( ctx, status_line, status );
+}
+
+static void 
+end_propstat( void *userdata, void *propstat, const char *status_line,
+             const http_status *status, const char *description )
+{
+    struct context *ctx = userdata;
+    handle_error( ctx, status_line, status );
+}
+
+void dav_add_depth_header( http_req *req, int depth )
+{
+    sbuffer hdrs = http_get_request_header( req );
+    switch( depth ) {
+    case DAV_DEPTH_ZERO:
+       sbuffer_zappend( hdrs, "Depth: 0" EOL );
+       break;
+    case DAV_DEPTH_ONE:
+       sbuffer_zappend( hdrs, "Depth: 1" EOL );
+       break;
+    default:
+       sbuffer_zappend( hdrs, "Depth: infinity" EOL );
+       break;
+    }
+}
+
+/* Dispatch a DAV request and handle a 207 error response appropriately */
+int dav_simple_request( http_session *sess, http_req *req )
+{
+    int ret, invalid;
+    http_status status;
+    http_content_type ctype = {0};
+    struct context ctx = {0};
+    dav_207_parser *p;
+
+    p = dav_207_init( &ctx );
+    ctx.buf = sbuffer_create();
+
+    dav_207_set_response_handlers( p, start_response, end_response );
+    dav_207_set_propstat_handlers( p, NULL, end_propstat );
+    
+    http_add_response_body_reader( req, dav_accept_207, dav_207_parse, p );
+    http_add_response_header_handler( req, "Content-Type", 
+                                     http_content_type_handler, &ctype );
+
+    ret = http_request_dispatch( req, &status );
+
+    invalid = dav_207_finish( p );
+
+    if( ret == HTTP_OK ) {
+       if( status.code == 207 ) {
+           if( invalid ) { 
+               http_set_error( sess, dav_207_error(p) );
+               ret = HTTP_ERROR;
+           } else if( ctx.is_error ) {
+               http_set_error( sess, sbuffer_data(ctx.buf) );
+               ret = HTTP_ERROR;
+           }
+       } else if( status.class != 2 ) {
+           ret = HTTP_ERROR;
+       }
+    }
+
+    sbuffer_destroy(ctx.buf);
+    HTTP_FREE(ctx.href);
+
+    http_request_destroy( req );
+
+    return ret;
+}
+    
+static int copy_or_move( http_session *sess, int is_move, int no_overwrite,
+                        const char *src, const char *dest ) {
+    http_req *req = http_request_create( sess, is_move?"MOVE":"COPY", src );
+    const char *str;
+    sbuffer hdrs;
+
+#ifdef USE_DAV_LOCKS
+    /* Copy needs to read/write the source, and write to the destination */
+    dav_lock_using_resource( req, src, 
+                            is_move?dav_lockusage_write:dav_lockusage_read, 
+                            DAV_DEPTH_INFINITE );
+    dav_lock_using_resource( req, dest, dav_lockusage_write, 
+                            DAV_DEPTH_INFINITE );
+    /* And we need to be able to add members to the destination's parent */
+    dav_lock_using_parent( req, dest );
+#endif
+
+    hdrs = http_get_request_header( req );
+    str = http_get_server_hostport( sess );
+    sbuffer_concat( hdrs, "Destination: http://", str, dest, EOL, NULL );
+
+    if( no_overwrite )
+       sbuffer_zappend( hdrs, "Overwrite: F" EOL );
+
+#if 0
+    /* FIXME */
+    if( ! http_webdav_server ) {
+       /* For non-WebDAV servers */
+       strcat( req.headers, "New-URI: " );
+       strcat( req.headers, to );
+       strcat( req.headers, EOL );
+    }
+#endif
+
+    return dav_simple_request( sess, req );
+}
+
+int dav_copy( http_session *sess, const char *src, const char *dest ) {
+    return copy_or_move( sess, 0, 1, src, dest );
+}
+
+int dav_move( http_session *sess, const char *src, const char *dest ) {
+    return copy_or_move( sess, 1, 1, src, dest );
+}
+
+/* Deletes the specified resource. (and in only two lines of code!) */
+int dav_delete( http_session *sess, const char *uri ) {
+    http_req *req = http_request_create( sess, "DELETE", uri );
+
+#ifdef USE_DAV_LOCKS
+    dav_lock_using_resource( req, uri, dav_lockusage_write, DAV_DEPTH_INFINITE );
+    dav_lock_using_parent( req, uri );
+#endif
+    
+    /* joe: I asked on the DAV WG list about whether we might get a
+     * 207 error back from a DELETE... conclusion, you shouldn't if
+     * you don't send the Depth header, since we might be an HTTP/1.1
+     * client and a 2xx response indicates success to them.  But
+     * it's all a bit unclear. In any case, DAV servers today do
+     * return 207 to DELETE even if we don't send the Depth header.
+     * So we handle 207 errors appropriately. */
+
+    return dav_simple_request( sess, req );
+}
+
+int dav_mkcol( http_session *sess, const char *uri ) 
+{
+    http_req *req;
+    char *real_uri;
+    int ret;
+
+    if( uri_has_trailing_slash( uri ) ) {
+       real_uri = xstrdup( uri );
+    } else {
+       CONCAT2( real_uri, uri, "/" );
+    }
+
+    req = http_request_create( sess, "MKCOL", real_uri );
+
+#ifdef USE_DAV_LOCKS
+    dav_lock_using_resource( req, real_uri, dav_lockusage_write, 0 );
+    dav_lock_using_parent( req, real_uri );
+#endif
+    
+    ret = dav_simple_request( sess, req );
+
+    free( real_uri );
+
+    return ret;
+}
diff --git a/neon/src/dav_basic.h b/neon/src/dav_basic.h
new file mode 100644 (file)
index 0000000..a2363e5
--- /dev/null
@@ -0,0 +1,64 @@
+/* 
+   Basic WebDAV support
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef DAV_BASIC_H
+#define DAV_BASIC_H
+
+#include "http_request.h"
+
+#include "dav_207.h"
+
+#define DAV_DEPTH_ZERO (0)
+#define DAV_DEPTH_ONE (1)
+#define DAV_DEPTH_INFINITE (2)
+
+/* Adds a Depth: header to a request */
+void dav_add_depth_header( http_req *req, int depth );
+
+/* Handle a simple WebDAV request.
+ *
+ * Usage:
+ *  1. Create the request using http_request_create()
+ *  2. Set any headers, the request body, whatever.
+ *  3. Call dav_simple_request to dispatch and destroy the request.
+ *
+ * (note the request IS destroyed by this function, don't do it 
+ * yourself).
+ *
+ * Returns HTTP_* as http_request_dispatch() would. If the response is
+ * a 207, a user-friendly error message is written to the session
+ * error buffer; e.g.  DELETE /foo/ might give the error:
+ *     /foo/bar: HTTP/1.1 423 Locked
+ */
+int dav_simple_request( http_session *sess, http_req *req );
+
+/* Basic WebDAV methods:
+ *   dav_copy:  copy resoure from src to dest
+ *   dav_move:  move resource from src to dest
+ *   dav_delete: delete resource at uri
+ *   dav_mkcol: create a collection at uri (uri MUST have a trailing slash).
+ */
+int dav_copy( http_session *sess, const char *src, const char *dest );
+int dav_move( http_session *sess, const char *src, const char *dest );
+int dav_delete( http_session *sess, const char *uri );
+int dav_mkcol( http_session *sess, const char *uri );
+
+#endif
diff --git a/neon/src/dav_locks.c b/neon/src/dav_locks.c
new file mode 100644 (file)
index 0000000..65acd31
--- /dev/null
@@ -0,0 +1,622 @@
+/* 
+   WebDAV Class 2 locking operations
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: dav_locks.c,v 1.3 2000/07/16 15:39:25 joe Exp 
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "http_request.h"
+#include "dav_locks.h"
+#include "dav_basic.h"
+#include "dav_props.h"
+#include "uri.h"
+#include "dav_207.h"
+#include "neon_i18n.h"
+#include "hip_xml.h"
+
+#include "xalloc.h"
+
+#define HOOK_ID "http://webdav.org/neon/hooks/webdav-locking"
+
+/* The list of locks to submit in an If header 
+ * for the current requests */
+struct submit_locks {
+    const struct dav_lock *lock;
+    const char *uri;
+    struct submit_locks *next;
+};
+
+struct dav_lock_session_s {
+    struct dav_lock *locks;
+};
+
+/* Per-request lock structure */
+struct request_locks {
+    struct submit_locks *locks; /* for the If header */
+    dav_lock_session *session;
+};
+
+/* Context for PROPFIND/lockdiscovery callbacks */
+struct lock_discovery {
+    struct dav_lock_result *list;
+};
+
+/* Hook callbacks */
+static void *create(void *session, http_req *req, 
+                   const char *method, const char *uri);
+static void pre_send(void *private, sbuffer req);
+static void destroy(void *private);
+
+/* The hooks are needed for construction of the If header */
+static http_request_hooks lock_hooks = {
+    HOOK_ID, /* unique id for the locking hooks */
+    create,
+    NULL, /* use_body not needed */
+    pre_send,
+    NULL, /* post_send not needed */
+    destroy
+};
+
+/* bit random, these */
+#define DAV_ELM_lockdiscovery 2
+#define DAV_ELM_activelock 3
+#define DAV_ELM_lockscope 4
+#define DAV_ELM_locktype 5
+#define DAV_ELM_depth 6
+#define DAV_ELM_owner 7
+#define DAV_ELM_timeout 9
+#define DAV_ELM_locktoken 10
+#define DAV_ELM_lockinfo 11
+#define DAV_ELM_write 14
+#define DAV_ELM_exclusive 15
+#define DAV_ELM_shared 16
+
+static const struct hip_xml_elm lock_elms[] = {
+#define A(x) { "DAV:", #x, DAV_ELM_ ## x, HIP_XML_COLLECT } /* ANY */
+#define D(x) { "DAV:", #x, DAV_ELM_ ## x, 0 }                   /* normal */
+#define C(x) { "DAV:", #x, DAV_ELM_ ## x, HIP_XML_CDATA }           /* (#PCDATA) */
+#define E(x) { "DAV:", #x, DAV_ELM_ ## x, 0 /* LEAF */ }    /* EMPTY */
+    D(lockdiscovery), D(activelock),
+    D(prop),
+    D(lockscope), D(locktype), C(depth), A(owner), C(timeout), D(locktoken),
+    /* no lockentry */
+    D(lockinfo), D(lockscope), D(locktype),
+    E(write), E(exclusive), E(shared),
+    C(href),
+#undef A
+#undef D
+#undef C
+#undef E
+    { NULL, 0, 0 }
+};
+
+static void *create(void *session, http_req *req, 
+                   const char *method, const char *uri)
+{
+    struct request_locks *rl = xcalloc(sizeof *rl);
+    rl->session = session;
+    return rl;
+}
+
+static void pre_send(void *private, sbuffer req)
+{
+    struct request_locks *rl = private;
+    
+    if (rl->locks != NULL) {
+       struct submit_locks *item;
+
+       /* Add in the If header */
+       sbuffer_zappend(req, "If:");
+       for (item = rl->locks; item != NULL; item = item->next) {
+           sbuffer_concat(req, " <", item->lock->uri, "> (<",
+                          item->lock->token, ">)", NULL);
+       }
+       sbuffer_zappend(req, EOL);
+    }
+}
+
+static void destroy(void *priv)
+{
+    struct request_locks *rl = priv;
+    struct submit_locks *lock, *next;
+    
+    for (lock = rl->locks; lock != NULL; lock = next) {
+       next = lock->next;
+       free(lock);
+    }
+    
+    free(rl);
+}
+
+dav_lock_session *dav_lock_register(http_session *sess)
+{
+    dav_lock_session *locksess = xcalloc(sizeof *locksess);
+
+    /* Register the hooks */
+    http_add_hooks(sess, &lock_hooks, locksess);
+    
+    return locksess;
+}
+
+void dav_lock_unregister(dav_lock_session *sess)
+{
+    /* FIXME: free the lock list */
+    free(sess);
+}
+
+/* Submit the given lock for the given URI */
+static void submit_lock(struct request_locks *rl, struct dav_lock *lock, 
+                       const char *uri)
+{
+    struct submit_locks *slock;
+
+    /* Check for dups */
+    for (slock = rl->locks; slock != NULL; slock = slock->next) {
+       if (strcasecmp(slock->lock->token, lock->token) == 0)
+           return;
+    }
+
+    slock = xcalloc(sizeof *slock);
+    slock->lock = lock;
+    slock->uri = uri;
+    slock->next = rl->locks;
+    rl->locks = slock;
+}
+
+struct dav_lock *dav_lock_find(dav_lock_session *sess, const char *uri)
+{
+    struct dav_lock *cur;
+    for (cur = sess->locks; cur != NULL; cur = cur->next) {
+       if (uri_compare(uri, cur->uri) == 0) 
+           return cur;
+    }
+    return NULL;
+}
+
+void dav_lock_using_parent(http_req *req, const char *uri)
+{
+    struct request_locks *rl = http_get_hook_private(req, HOOK_ID);
+    char *parent = uri_parent(uri);
+
+    if (parent != NULL) {
+       struct dav_lock *lock;
+       /* Find any locks on the parent resource.
+        * FIXME: should check for depth-infinity locks too. */
+       lock = dav_lock_find(rl->session, parent);
+       if (lock) {
+           DEBUG(DEBUG_LOCKS, "Locked parent, %s on %s\n", lock->token, 
+                 lock->uri);
+           submit_lock(rl, lock, uri);
+       }
+       free(parent);
+    }
+}
+
+int dav_lock_iterate(dav_lock_session *sess, 
+                    dav_lock_walkfunc func, void *userdata)
+{
+    struct dav_lock *lock;
+    int count = 0;
+
+    for (lock = sess->locks; lock != NULL; lock = lock->next) {
+       (*func)(lock, userdata);
+       count++;
+    }
+    
+    return count;
+}
+
+void dav_lock_using_resource(http_req *req, const char *uri, int depth)
+{
+    /* Grab the private cookie for this request */
+    struct request_locks *rl = http_get_hook_private(req, HOOK_ID);
+    struct dav_lock *lock; /* all the known locks */
+    int match;
+
+    /* Iterate over the list of session locks to see if any of
+     * them apply to this resource */
+    for (lock = rl->session->locks; lock != NULL; lock = lock->next) {
+       
+       match = 0;
+       
+       if (depth == DAV_DEPTH_INFINITE && uri_childof(uri, lock->uri)) {
+           /* Case 1: this is a depth-infinity request which will 
+            * modify a lock somewhere inside the collection. */
+           DEBUG(DEBUG_LOCKS, "Has child: %s\n", lock->token);
+           match = 1;
+       } 
+       else if (uri_compare(uri, lock->uri) == 0) {
+           /* Case 2: this request is directly on a locked resource */
+           DEBUG(DEBUG_LOCKS, "Has direct lock: %s\n", lock->token);
+           match = 1;
+       }
+       else if (lock->depth == DAV_DEPTH_INFINITE && 
+                uri_childof(lock->uri, uri)) {
+           /* Case 3: there is a higher-up infinite-depth lock which
+            * covers the resource that this request will modify. */
+           DEBUG(DEBUG_LOCKS, "Is child of: %s\n", lock->token);
+           match = 1;
+       }
+       
+       if (match) {
+           submit_lock(rl, lock, uri);
+       }
+    }
+
+}
+
+void dav_lock_add(dav_lock_session *sess, struct dav_lock *lock)
+{
+    if (sess->locks != NULL) {
+       sess->locks->prev = lock;
+    }
+    lock->prev = NULL;
+    lock->next = sess->locks;
+    sess->locks = lock;
+}
+
+void dav_lock_remove(dav_lock_session *sess, struct dav_lock *lock)
+{
+    if (lock->prev != NULL) {
+       lock->prev->next = lock->next;
+    } else {
+       sess->locks = lock->next;
+    }
+    if (lock->next != NULL) {
+       lock->next->prev = lock->prev;
+    }
+}
+
+void dav_lock_free(struct dav_lock *lock)
+{
+    HTTP_FREE(lock->uri);
+    HTTP_FREE(lock->owner);
+    HTTP_FREE(lock->token);
+    free(lock);
+}
+
+int dav_unlock(http_session *sess, struct dav_lock *lock)
+{
+    http_req *req = http_request_create(sess, "UNLOCK", lock->uri);
+    http_status status;
+    int ret;
+    
+    http_print_request_header(req, "Lock-Token", "<%s>", lock->token);
+    
+    /* TODO: need this or not?
+     * it definitely goes away when lock-null resources go away */
+    dav_lock_using_parent(req, lock->uri);
+
+    ret = http_request_dispatch(req, &status);
+    
+    if (ret == HTTP_OK && status._class == 2) {
+       ret = HTTP_OK;    
+    } else {
+       ret = HTTP_ERROR;
+    }
+
+    http_request_destroy(req);
+    
+    return ret;
+}
+
+static int check_context(hip_xml_elmid parent, hip_xml_elmid child) {
+    DEBUG(DEBUG_XML, "dav_locks: check_context %d in %d\n", child, parent);
+    switch (parent) {
+    case HIP_ELM_root:
+       /* TODO: for LOCK requests only...
+        * shouldn't allow this for PROPFIND really */
+       if (child == DAV_ELM_prop)
+           return HIP_XML_VALID;
+       break;      
+    case DAV_ELM_prop:
+       if (child == DAV_ELM_lockdiscovery)
+           return HIP_XML_VALID;
+       break;
+    case DAV_ELM_lockdiscovery:
+       if (child == DAV_ELM_activelock)
+           return HIP_XML_VALID;
+       break;
+    case DAV_ELM_activelock:
+       switch (child) {
+       case DAV_ELM_lockscope:
+       case DAV_ELM_locktype:
+       case DAV_ELM_depth:
+       case DAV_ELM_owner:
+       case DAV_ELM_timeout:
+       case DAV_ELM_locktoken:
+           return HIP_XML_VALID;
+       default:
+           break;
+       }
+       break;
+    case DAV_ELM_lockscope:
+       switch (child) {
+       case DAV_ELM_exclusive:
+       case DAV_ELM_shared:
+           return HIP_XML_VALID;
+       default:
+           break;
+       }
+    case DAV_ELM_locktype:
+       if (child == DAV_ELM_write)
+           return HIP_XML_VALID;
+       break;
+       /* ... depth is PCDATA, owner is COLLECT, timeout is PCDATA */
+    case DAV_ELM_locktoken:
+       if (child == DAV_ELM_href)
+           return HIP_XML_VALID;
+       break;
+    }
+    return HIP_XML_DECLINE;
+}
+
+static int parse_depth(const char *depth) {
+    if (strcasecmp(depth, "infinity") == 0) {
+       return DAV_DEPTH_INFINITE;
+    } else if (isdigit(depth[0])) {
+       return atoi(depth);
+    } else {
+       return -1;
+    }
+}
+
+static long parse_timeout(const char *timeout) {
+    if (strcasecmp(timeout, "infinite") == 0) {
+       return DAV_TIMEOUT_INFINITE;
+    } else if (strncasecmp(timeout, "Second-", 7) == 0) {
+       long to = strtol(timeout, NULL, 10);
+       if (to == LONG_MIN || to == LONG_MAX)
+           return DAV_TIMEOUT_INVALID;
+       return to;
+    } else {
+       return DAV_TIMEOUT_INVALID;
+    }
+}
+
+static void *start_resource(void *userdata, const char *href)
+{
+    struct dav_lock_result *res = xcalloc(sizeof *res);
+    res->href = xstrdup(href);
+    return res;
+}
+
+static void end_resource(void *userdata, void *response, 
+                        const char *status_line, const http_status *status,
+                        const char *description)
+{
+    struct lock_discovery *ctx = userdata;
+    struct dav_lock_result *res = response;
+    
+    if (response == NULL)
+       return;
+
+    res->next = ctx->list;
+    if (status_line != NULL && status != NULL) {
+       res->status_line = xstrdup(status_line);
+       res->status = *status;
+       res->status.reason_phrase = /* eeeeekkk.... HACKACKACKACK */
+           res->status_line + (status->reason_phrase - res->status_line);
+    }
+    ctx->list = res;
+    DEBUG(DEBUG_LOCKS, "End of response for %s\n", res->href);
+}
+
+static int end_element_common(struct dav_lock *l, const struct hip_xml_elm *elm,
+                             const char *cdata)
+{
+    switch (elm->id){ 
+    case DAV_ELM_write:
+       l->type = dav_locktype_write;
+       break;
+    case DAV_ELM_exclusive:
+       l->scope = dav_lockscope_exclusive;
+       break;
+    case DAV_ELM_shared:
+       l->scope = dav_lockscope_shared;
+       break;
+    case DAV_ELM_depth:
+       DEBUG(DEBUG_LOCKS, "Got depth: %s\n", cdata);
+       l->depth = parse_depth(cdata);
+       if (l->depth == -1) {
+           return -1;
+       }
+       break;
+    case DAV_ELM_timeout:
+       DEBUG(DEBUG_LOCKS, "Got timeout: %s\n", cdata);
+       l->timeout = parse_timeout(cdata);
+       if (l->timeout == DAV_TIMEOUT_INVALID) {
+           return -1;
+       }
+       break;
+    case DAV_ELM_owner:
+       l->owner = strdup(cdata);
+       break;
+    case DAV_ELM_href:
+       l->token = strdup(cdata);
+       break;
+    }
+    return 0;
+}
+
+static int 
+start_element_ldisc(void *userdata, const struct hip_xml_elm *elm,
+                   const char **atts)
+{
+    struct dav_lock_result *l = dav_propfind_get_current_resource(userdata);
+    struct dav_lock *lck;
+
+    if (l == NULL)
+       return -1;
+
+    switch (elm->id) {
+    case DAV_ELM_activelock:
+       lck = xcalloc(sizeof *lck);
+       lck->next = l->lock;
+       if (l->lock != NULL)
+           l->lock->prev = l->lock;
+       l->lock = lck;
+       lck->uri = xstrdup(l->href);
+       break;
+    default:
+       break;
+    }
+
+    return 0;    
+}
+
+/* End-element handler for lock discovery PROPFIND response */
+static int 
+end_element_ldisc(void *userdata, const struct hip_xml_elm *elm, 
+                 const char *cdata) 
+{
+    struct dav_lock_result *l = dav_propfind_get_current_resource(userdata);
+
+    return end_element_common(l->lock, elm, cdata);
+}
+
+/* End-element handler for LOCK response */
+static int
+end_element_lock(void *userdata, const struct hip_xml_elm *elm, 
+                const char *cdata)
+{
+    struct dav_lock *lock = userdata;
+    return end_element_common(lock, elm, cdata);
+}
+
+/* Discover all locks on URI */
+int dav_lock_discover(http_session *sess,
+                     const char *uri, struct dav_lock_result **locks)
+{
+    dav_propfind_handler *handler = dav_propfind_create(sess, uri, 0);
+    struct lock_discovery ctx = {0};
+    const dav_propname props[] = {
+       { "DAV:", "lockdiscovery" },
+       { NULL }
+    };
+    int ret;
+    
+    dav_propfind_set_resource_handlers(handler, start_resource, end_resource);
+    hip_xml_add_handler(dav_propfind_get_parser(handler), lock_elms, 
+                       check_context, start_element_ldisc, end_element_ldisc, 
+                       handler);
+    
+    ret = dav_propfind_named(handler, props, &ctx);
+
+    if (ret == HTTP_OK) {
+       if (ctx.list != NULL) {
+           *locks = ctx.list;
+           ret = HTTP_OK;
+       } else {
+           http_set_error(sess, _("No locks found.\n"));
+           ret = HTTP_ERROR;
+       }
+    } else {
+       /* FIXME: free the list */
+    }
+
+    return ret;
+}
+
+int dav_lock(http_session *sess, struct dav_lock *lock) 
+{
+    http_req *req = http_request_create(sess, "LOCK", lock->uri);
+    http_status status;
+    sbuffer body = sbuffer_create();
+    hip_xml_parser *parser = hip_xml_create();
+    int ret, parse_failed;
+    char *locktoken = NULL;
+
+    hip_xml_add_handler(parser, lock_elms, check_context, 
+                       NULL, end_element_lock, lock);
+    
+    /* Create the body */
+    sbuffer_concat(body, "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
+                   "<lockinfo xmlns='DAV:'>" EOL " <lockscope>",
+                   lock->scope==dav_lockscope_exclusive?
+                   "<exclusive/>":"<shared/>",
+                   "</lockscope>" EOL
+                   "<locktype><write/></locktype>", NULL);
+
+    if (lock->owner) {
+       sbuffer_concat(body, "<owner>", lock->owner, "</owner>" EOL, NULL);
+    }
+    sbuffer_zappend(body, "</lockinfo>" EOL);
+
+    http_set_request_body_buffer(req, sbuffer_data(body));
+    http_add_response_body_reader(req, http_accept_2xx, 
+                                 hip_xml_parse_v, parser);
+    http_add_response_header_handler(req, "Lock-Token", 
+                                    http_duplicate_header, &locktoken);
+    http_add_request_header(req, "Content-Type", "text/xml");
+    dav_add_depth_header(req, lock->depth);
+
+    /* TODO: 
+     * By 2518, we need this only if we are creating a lock-null resource.
+     * Since we don't KNOW whether the lock we're given is a lock-null
+     * or not, we cover our bases.
+     */
+    dav_lock_using_parent(req, lock->uri);
+    /* This one is clearer from 2518 sec 8.10.4. */
+    dav_lock_using_resource(req, lock->uri, lock->depth);
+
+    ret = http_request_dispatch(req, &status);
+
+    sbuffer_destroy(body);
+    parse_failed = !hip_xml_valid(parser);
+    
+    if (ret == HTTP_OK && status._class == 2) {
+       if (parse_failed) {
+           ret = HTTP_ERROR;
+           http_set_error(sess, hip_xml_get_error(parser));
+       }
+       else if (status.code == 207) {
+           ret = HTTP_ERROR;
+           /* TODO: set the error string appropriately */
+       }
+       else if (locktoken == NULL || strlen(locktoken) < 3) {
+           /* No valid Lock-Token header: IIS5 does this */
+           /* FIXME */
+       } else {
+           /* We have a locktoken header: strip of the angle brackets */
+           if (locktoken[0] == '<')
+               locktoken++;
+           if (locktoken[strlen(locktoken)-1] == '>')
+               locktoken[strlen(locktoken)-1] = '\0';
+
+           /* FIXME */
+       }
+    } else {
+       ret = HTTP_ERROR;
+    }
+
+    http_request_destroy(req);
+
+    /* TODO: free the list */
+    return ret;
+}
diff --git a/neon/src/dav_locks.h b/neon/src/dav_locks.h
new file mode 100644 (file)
index 0000000..3c23717
--- /dev/null
@@ -0,0 +1,86 @@
+/* 
+   WebDAV Class 2 locking operations
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef DAV_LOCKS_H
+#define DAV_LOCKS_H
+
+enum dav_lock_scope {
+    dav_lockscope_exclusive,
+    dav_lockscope_shared
+};
+
+enum dav_lock_type {
+    dav_locktype_write
+};
+
+/* Would be typedef'ed to dav_lock except lock is a verb and a noun.
+ * Damn the English language. */
+struct dav_lock {
+    char *uri;
+    int depth;
+    enum dav_lock_type type;
+    enum dav_lock_scope scope;
+    char *token;
+    char *owner;
+    long timeout;
+    struct dav_lock *next;
+    struct dav_lock *prev;
+};
+
+struct dav_lock_result {
+    struct dav_lock *lock;
+    char *href;
+    http_status status;
+    char *status_line;
+    struct dav_lock_result *next;
+};
+
+#define DAV_TIMEOUT_INFINITE -1
+#define DAV_TIMEOUT_INVALID -2
+
+typedef struct dav_lock_session_s dav_lock_session;
+
+dav_lock_session *dav_lock_register(http_session *sess);
+void dav_lock_unregister(dav_lock_session *sess);
+
+void dav_lock_add(dav_lock_session *sess, struct dav_lock *lock);
+void dav_lock_remove(dav_lock_session *sess, struct dav_lock *lock);
+
+typedef void (*dav_lock_walkfunc)(struct dav_lock *lock, void *userdata);
+
+int dav_lock_iterate(dav_lock_session *sess, 
+                    dav_lock_walkfunc func, void *userdata);
+
+/* Indicate that this request is of depth n on given uri */
+void dav_lock_using_resource(http_req *req, const char *uri, int depth);
+/* Indicate that this request will modify parent collection of given URI */
+void dav_lock_using_parent(http_req *req, const char *uri);
+
+int dav_lock(http_session *sess, struct dav_lock *lock);
+int dav_lock_discover(http_session *sess,
+                     const char *uri, struct dav_lock_result **locks);
+
+struct dav_lock *dav_lock_find(dav_lock_session *sess, const char *uri);
+int dav_unlock(http_session *sess, struct dav_lock *lock);
+
+void dav_lock_free(struct dav_lock *lock);
+
+#endif /* DAV_LOCKS_H */
diff --git a/neon/src/dav_props.c b/neon/src/dav_props.c
new file mode 100644 (file)
index 0000000..1b86a12
--- /dev/null
@@ -0,0 +1,192 @@
+/* 
+   WebDAV Properties manipulation
+   Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: dav_props.c,v 1.12 2000/05/09 22:36:55 joe Exp 
+*/
+
+#include "config.h"
+
+#include "xalloc.h"
+#include "dav_props.h"
+#include "dav_basic.h"
+#include "hip_xml.h"
+
+struct dav_propfind_handler_s {
+    dav_pf_start_resource start_resource;
+    dav_pf_end_resource end_resource;
+    struct hip_xml_handler *handler;
+    http_session *sess;
+    const char *uri;
+    int depth;
+    char *href;
+    http_status status;
+    void *userdata, *resource;
+};
+
+void *dav_propfind_get_current_resource( dav_propfind_handler *handler )
+{ 
+    return handler->resource;
+}    
+
+static void *start_response( void *userdata, const char *href )
+{
+    dav_propfind_handler *ctx = userdata;
+    ctx->resource = NULL;
+    if( ctx->start_resource ) {
+       ctx->resource = (*ctx->start_resource)( ctx->userdata, href );
+    }
+    return ctx->resource;
+}
+
+static void end_response( void *userdata, void *response, 
+                         const char *status_line, const http_status *status )
+{
+    dav_propfind_handler *ctx = userdata;
+    if( ctx->end_resource ) {
+       (*ctx->end_resource)( ctx->userdata, response, status_line, status );
+    }
+}
+
+dav_propfind_handler *
+dav_propfind_create( http_session *sess, const char *uri, int depth )
+{
+    dav_propfind_handler *ret = xmalloc(sizeof(dav_propfind_handler));
+    memset( ret, 0, sizeof(dav_propfind_handler));
+    ret->uri = uri;
+    ret->depth = depth;
+    ret->sess = sess;
+    return ret;
+}
+
+void dav_propfind_set_resource_handlers( dav_propfind_handler *handler,
+                                        dav_pf_start_resource start_res,
+                                        dav_pf_end_resource end_res )
+{
+    handler->start_resource = start_res;
+    handler->end_resource = end_res;
+}
+
+void dav_propfind_set_element_handler( dav_propfind_handler *handler,
+                                      struct hip_xml_handler *elm_handler )
+{
+    handler->handler = elm_handler;
+}
+
+static int propfind( dav_propfind_handler *handler, const dav_propname *names,
+                    void *userdata )
+{
+    int ret, is_allprop = (names==NULL);
+    http_req *req = http_request_create( handler->sess, "PROPFIND", handler->uri );
+    http_status status;
+    dav_207_parser *parser;
+    sbuffer body, hdrs;
+    
+    body = sbuffer_create();
+
+    sbuffer_concat( body, 
+                   "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL 
+                   "<propfind xmlns=\"DAV:\">", NULL );
+    
+    if( !is_allprop ) {
+       int n;
+       sbuffer_zappend( body, "<prop>" EOL );
+       for( n = 0; names[n].name != NULL; n++ ) {
+           sbuffer_concat( body, "<", names[n].name, " xmlns=\"", 
+                           names[n].nspace, "\"/>" EOL, NULL );
+       }
+       sbuffer_zappend( body, "</prop></propfind>" EOL );
+    } else {
+       sbuffer_zappend( body, "</allprop></propfind>" EOL );
+    }
+
+    parser = dav_207_init_with_handler( handler, handler->handler );
+    dav_207_set_response_handlers( parser, start_response, end_response );
+    handler->userdata = userdata;
+
+    http_set_request_body_buffer( req, sbuffer_data(body) );
+
+    hdrs = http_get_request_header( req );
+    sbuffer_zappend( hdrs, "Content-Type: text/xml" EOL ); /* TODO: UTF-8? */
+    dav_add_depth_header( req, handler->depth );
+    
+    http_add_response_body_reader( req, dav_accept_207, dav_207_parse, parser );
+
+    ret = http_request_dispatch( req, &status );
+
+    http_request_destroy( req );
+
+    return ret;
+}
+
+int dav_propfind_allprop( dav_propfind_handler *handler, void *userdata )
+{
+    return propfind( handler, NULL, userdata );
+}
+
+int dav_propfind_named( dav_propfind_handler *handler, 
+                       const dav_propname *names, void *userdata )
+{
+    return propfind( handler, names, userdata );
+}
+
+
+/* The easy one... PROPPATCH */
+int dav_proppatch( http_session *sess, const char *uri, 
+                  const dav_proppatch_operation *items )
+{
+    http_req *req = http_request_create( sess, "PROPPATCH", uri );
+    sbuffer body = sbuffer_create(), hdrs;
+    int n, ret;
+    
+    /* Create the request body */
+    sbuffer_zappend( body, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" EOL
+                    "<propertyupdate xmlns=\"DAV:\">" );
+
+    for( n = 0; items[n].name != NULL; n++ ) {
+       switch( items[n].type ) {
+       case dav_propset:
+           /* <set><prop><prop-name>value</prop-name></prop></set> */
+           sbuffer_concat( body, "<set><prop>"
+                           "<", items[n].name->name, " xmlns=\"",
+                           items[n].name->nspace, "\">", items[n].value,
+                           "</", items[n].name->name, "></prop></set>" EOL, NULL );
+           break;
+
+       case dav_propremove:
+           /* <remove><prop><prop-name/></prop></remove> */
+           sbuffer_concat( body, "<remove><prop><", items[n].name->name, " xmlns=\"",
+                           items[n].name->nspace, "\"/></prop></remove>" EOL, NULL );
+           break;
+       }
+    }  
+
+    sbuffer_zappend( body, "</propertyupdate>" EOL );
+    
+    http_set_request_body_buffer( req, sbuffer_data(body) );
+
+    hdrs = http_get_request_header( req );
+    sbuffer_zappend( hdrs, "Content-Type: text/xml" EOL ); /* TODO: UTF-8? */
+    
+    ret = dav_simple_request( sess, req );
+    
+    sbuffer_destroy( body );
+    
+    return ret;
+}
+
diff --git a/neon/src/dav_props.h b/neon/src/dav_props.h
new file mode 100644 (file)
index 0000000..9f0ac0f
--- /dev/null
@@ -0,0 +1,89 @@
+/* 
+   WebDAV Properties manipulation
+   Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef DAV_PROPS_H
+#define DAV_PROPS_H
+
+#include "http_request.h"
+#include "dav_207.h"
+
+/* PROPFIND results via three callbacks: */
+
+struct dav_propfind_handler_s;
+typedef struct dav_propfind_handler_s dav_propfind_handler;
+
+/* Callback types */
+typedef void *(*dav_pf_start_resource)( void *userdata, const char *href );
+typedef void (*dav_pf_got_property)( void *userdata, void *resource,
+                                 const dav_propname *name, const hip_xml_char **atts, 
+                                 const char *value );
+typedef void (*dav_pf_end_resource)( void *userdata, void *resource,
+                                    const char *status_line, const http_status *status );
+
+/* Find properties with names in set *props on resource at URI 
+ * in set *props, with given depth. If props == NULL all properties
+ * are returned.
+ * Returns:
+ *   HTTP_OK on success.
+ *   else other HTTP_* code
+ */
+dav_propfind_handler *
+dav_propfind_create( http_session *sess, const char *uri, int depth );
+
+void dav_propfind_register( dav_propfind_handler *handler,
+                           dav_pf_start_resource start_res,
+                           dav_pf_got_property got_prop,
+                           dav_pf_end_resource end_res );
+
+void dav_propfind_set_resource_handlers( dav_propfind_handler *handler,
+                                        dav_pf_start_resource start_res,
+                                        dav_pf_end_resource end_res );
+
+void dav_propfind_set_element_handler( dav_propfind_handler *handler,
+                                    struct hip_xml_handler *elm_handler );
+
+int dav_propfind_allprop( dav_propfind_handler *handler, void *userdata );
+
+int dav_propfind_named( dav_propfind_handler *handler, 
+                       const dav_propname *names, void *userdata );
+
+/* A PROPPATCH request may include any number of operations. Pass an
+ * array of these operations to dav_proppatch, with the last item
+ * having the name element being NULL.  If the type is propset, the
+ * property of the given name is set to the new value.  If the type is
+ * propremove, the property of the given name is deleted, and the
+ * value is ignored.  */
+typedef struct {
+    const dav_propname *name;
+    enum {
+       dav_propset,
+       dav_propremove
+    } type;
+    const char *value;
+} dav_proppatch_operation;
+
+/* FIXME: shouldn't need this. */
+void *dav_propfind_get_current_resource( dav_propfind_handler *handler );
+
+int dav_proppatch( http_session *sess, const char *uri,
+                  const dav_proppatch_operation *items );
+
+#endif /* DAV_PROPS_H */
diff --git a/neon/src/hip_xml.c b/neon/src/hip_xml.c
new file mode 100644 (file)
index 0000000..1761504
--- /dev/null
@@ -0,0 +1,605 @@
+/* 
+   Higher Level Interface to XML Parsers.
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: hip_xml.c,v 1.14 2000/05/09 22:33:54 joe Exp 
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "neon_i18n.h"
+
+#include "xalloc.h"
+#include "http_utils.h"
+#include "string_utils.h"
+#include "hip_xml.h"
+
+static const char *friendly_name( const struct hip_xml_elm *elm )
+{
+    switch( elm->id ) {
+    case HIP_ELM_root:
+       return _("document root");
+    case HIP_ELM_unknown:
+       return _("unknown element");
+    default:
+       if( elm->name ) {
+           return elm->name;
+       } else {
+           return _("unspecified");
+       }
+    }
+}
+  
+/* TODO: 
+ * could move 'valid' into state, maybe allow optional
+ * continuation past an invalid branch.
+ */
+
+const static struct hip_xml_elm root_element = 
+{ "@<root>@", HIP_ELM_root, 0 };
+
+/* The callback handlers */
+static void start_element( void *userdata, const hip_xml_char *name, const hip_xml_char **atts );
+static void end_element( void *userdata, const hip_xml_char *name );
+static void char_data( void *userdata, const hip_xml_char *cdata, int len );
+
+#ifdef HAVE_EXPAT
+/* libxml doesn't seem to pass stuff back in UTF-8.
+ * maybe libxml-2.0 will do. */
+#define HIP_XML_DECODE_UTF8
+#endif
+
+#ifdef HIP_XML_DECODE_UTF8
+
+/* UTF-8 decoding */
+
+/* Single byte range 0x00 -> 0x7F */
+#define SINGLEBYTE_UTF8( ch ) ( ((unsigned char) (ch)) < 0x80 )
+
+/* Decode a double byte UTF8 string.
+ * Returns 0 on success or non-zero on error. */
+static inline int decode_utf8_double( char *dest, const char *src );
+
+#endif
+
+/* Linked list of namespace scopes */
+struct hip_xml_nspace {
+    hip_xml_char *name;
+    hip_xml_char *uri;
+    struct hip_xml_nspace *next;
+};
+
+/* And an auxiliary */
+static int parse_element( struct hip_xml_parser *p, struct hip_xml_state *state,
+                         const hip_xml_char *name, const hip_xml_char **atts );
+
+
+#ifdef HAVE_LIBXML
+
+/* Could be const as far as we care, but libxml doesn't want that */
+static xmlSAXHandler sax_handler = {
+    NULL, /* internalSubset */
+    NULL, /* isStandalone */
+    NULL, /* hasInternalSubset */
+    NULL, /* hasExternalSubset */
+    NULL, /* resolveEntity */
+    NULL, /* getEntity */
+    NULL, /* entityDecl */
+    NULL, /* notationDecl */
+    NULL, /* attributeDecl */
+    NULL, /* elementDecl */
+    NULL, /* unparsedEntityDecl */
+    NULL, /* setDocumentLocator */
+    NULL, /* startDocument */
+    NULL, /* endDocument */
+    start_element, /* startElement */
+    end_element, /* endElement */
+    NULL, /* reference */
+    char_data, /* characters */
+    NULL, /* ignorableWhitespace */
+    NULL, /* processingInstruction */
+    NULL, /* comment */
+    NULL, /* xmlParserWarning */
+    NULL, /* xmlParserError */
+    NULL, /* xmlParserError */
+    NULL, /* getParameterEntity */
+};
+
+#endif /* HAVE_LIBXML */
+
+#ifdef HIP_XML_DECODE_UTF8
+
+static inline int 
+decode_utf8_double( char *dest, const char *src ) 
+{
+    /* From utf-8 man page; two-byte encoding is:
+     *    0x00000080 - 0x000007FF:
+     *       110xxxxx 10xxxxxx
+     * If more than 8-bits of those x's are set, we fail.
+     * So, we check that the first 6 bits of the first byte are:
+     *       110000.
+     * Then decode like:
+     *       110000xx 10yyyyyy  -> xxyyyyyy
+     * Do this with a mask and a compare:
+     *       zzzzzzzz
+     *     & 11111100  <=> 0xFC
+     *    == 11000000  <=> 0XC0    
+     * 
+     * joe: A real C hacker would probably do some funky bit
+     * inversion, and turn this into an is-not-zero test, 
+     * but I'm a fake, so...
+     */
+    if( (src[0] & 0xFC) == 0xC0 ) {
+       dest[0] = ((src[0] & 0x03) << 6) | (src[1] & 0x3F);
+       return 0;
+    } else {
+       return -1;
+    }
+}
+
+#endif
+
+int hip_xml_currentline( struct hip_xml_parser *p ) {
+#ifdef HAVE_EXPAT
+    return XML_GetCurrentLineNumber(p->parser);
+#else
+    return p->parser->input->line;
+#endif
+}
+
+static int find_handler( struct hip_xml_handler *list, struct hip_xml_state *state ) {
+    struct hip_xml_handler *cur, *unk_handler = NULL;
+    const struct hip_xml_elm *unk_elm = NULL;
+    int n;
+    for( cur = list; cur != NULL; cur = cur->next ) {
+       for( n = 0; cur->elements[n].nspace != NULL; n++ ) {
+           if( strcasecmp( cur->elements[n].name, state->name ) == 0 && 
+               strcasecmp( cur->elements[n].nspace, state->nspace ) == 0 ) {
+               state->handler = cur;
+               state->elm = &cur->elements[n];
+               return 0;
+           }
+           if( !unk_elm && cur->elements[n].id == HIP_ELM_unknown ) {
+               unk_elm = &cur->elements[n];
+               unk_handler = cur;
+           }
+       }
+    }
+    if( !cur && unk_elm ) {
+       /* Give them the unknown handler */
+       state->handler = unk_handler;
+       state->elm = unk_elm;
+       return 0;
+    } else {
+       return -1;
+    }
+}
+
+char *hip_xml_escape( const char *text )
+{
+#if 0
+    sbuffer buf = sbuffer_create();
+    const char *pnt;
+    if( !buf ) return NULL;
+    /* FIXME: implement */
+#endif
+    return NULL;
+}
+
+/* Called with the start of a new element. */
+static void 
+start_element( void *userdata, const hip_xml_char *name, const hip_xml_char **atts ) 
+{
+    struct hip_xml_parser *p = (struct hip_xml_parser *)userdata;
+    struct hip_xml_state *s;
+
+    if( !p->valid ) {
+       /* We've stopped parsing */
+       DEBUG( DEBUG_XML, "Parse died. Ignoring start of element: %s\n", name );
+       return;
+    }
+    if( p->collect ) {
+       /* In Collect Mode. */
+       sbuffer_concat( p->buffer, "<", name, NULL );
+       if( atts != NULL ) {
+           int n;
+           for( n = 0; atts[n] != NULL; n+=2 ) {
+               sbuffer_concat( p->buffer, " ", atts[n], "=", atts[n+1],
+                               NULL );
+           }
+       }
+       sbuffer_zappend( p->buffer, ">" );
+       /* One deeper */
+       p->collect++;
+       return;
+    }
+    /* Set the new state */
+    s = xmalloc( sizeof(struct hip_xml_state) );
+    memset( s, 0, sizeof(struct hip_xml_state) );
+    s->parent = p->current;
+    p->current = s;
+    /* We need to handle namespaces ourselves */
+    if( parse_element( p, s, name, atts ) ) {
+       /* it bombed. */
+       p->valid = 0;
+       return;
+    }
+    /* Map the element name to an id */
+    DEBUG( DEBUG_XMLPARSE, "Mapping element name %s@@%s... ", s->nspace, s->name );
+    if( find_handler( p->handlers, s ) ) {
+       DEBUG( DEBUG_XMLPARSE, "Unexpected element\n" );
+       snprintf( p->error, BUFSIZ, "Unknown XML element `%s'", s->name );
+       p->valid = 0;
+       return;
+    }
+    
+    DEBUG( DEBUG_XMLPARSE, "mapped to id %d\n", s->elm->id );
+
+    /* Do we want cdata? */
+    p->want_cdata = ((p->current->elm->flags & HIP_XML_CDATA) 
+                    == HIP_XML_CDATA);
+    p->collect = ((p->current->elm->flags & HIP_XML_COLLECT) 
+                 == HIP_XML_COLLECT);
+
+    /* expat is not a validating parser - check the new element
+     * is valid in the current context.
+     */
+    DEBUG( DEBUG_XML, "Checking context of %s@@%s: element %s (parent: %s)\n", 
+          s->nspace, s->name, friendly_name(s->elm), friendly_name(s->parent->elm) );
+    if( (*s->handler->validate_cb)( s->parent->elm->id, s->elm->id ) ) {
+       DEBUG( DEBUG_XML, "Invalid context.\n" );
+       snprintf( p->error, BUFSIZ, 
+                 _("XML is not valid (%s found in parent %s)"),
+                 friendly_name(s->elm), friendly_name(s->parent->elm) );
+       p->valid = 0;
+    } else {
+       DEBUG( DEBUG_XML, "Valid context.\n" );
+       if( s->handler->startelm_cb ) {
+           if( (*s->handler->startelm_cb)( s->handler->userdata, s, atts ) ) {
+               DEBUG( DEBUG_XML, "Startelm callback failed.\n" );
+               p->valid = 0;
+           }
+       } else {
+           DEBUG( DEBUG_XML, "No startelm handler.\n" );
+       }
+    }
+
+}
+
+/* Destroys given state */
+static void destroy_state( struct hip_xml_state *s ) {
+    struct hip_xml_nspace *this_ns, *next_ns;
+    DEBUG( DEBUG_XMLPARSE, "Freeing namespaces...\n" );
+    HTTP_FREE( s->default_ns );
+    HTTP_FREE( s->name );
+    /* Free the namespaces */
+    this_ns = s->nspaces;
+    while( this_ns != NULL ) {
+       next_ns = this_ns->next;
+       free( this_ns->name );
+       free( this_ns->uri );
+       free( this_ns );
+       this_ns = next_ns;
+    };
+    DEBUG( DEBUG_XMLPARSE, "Finished freeing namespaces.\n" );
+    free( s );
+}
+
+static void char_data( void *userdata, const hip_xml_char *data, int len ) {
+    struct hip_xml_parser *p = userdata;
+    
+    if( !p->want_cdata || !p->valid ) return;
+    /* First, if this is the beginning of the CDATA, skip all
+     * leading whitespace, we don't want it. */
+    DEBUG( DEBUG_XMLPARSE, "Given %d bytes of cdata.\n", len );
+    if( sbuffer_size(p->buffer) == 0 ) {
+       size_t wslen = 0;
+       /* Ignore any leading whitespace */
+       while( wslen < len && 
+              ( data[wslen] == ' ' || data[wslen] == '\r' ||
+                data[wslen] == '\n' || data[wslen] == '\t' ) ) {
+           wslen++;
+       }
+       data += wslen;
+       len -= wslen;
+       DEBUG( DEBUG_XMLPARSE, "Skipped %d bytes of leading whitespace.\n", 
+              wslen );
+       if( len == 0 ) {
+           DEBUG( DEBUG_XMLPARSE, "Zero bytes of content.\n" );
+           return;
+       }
+    }
+
+#ifdef HIP_XML_DECODE_UTF8
+
+    if( (p->current->elm->flags & HIP_XML_UTF8DECODE) == HIP_XML_UTF8DECODE ) {
+       int n, m, clen;
+       char *dest;
+
+       clen = sbuffer_size(p->buffer);
+       sbuffer_grow( p->buffer, clen + len + 1 );
+       dest = sbuffer_data( p->buffer ) + clen;
+
+       for( n = 0, m = 0; n < len; n++, m++ ) {
+           if( SINGLEBYTE_UTF8( data[n] ) ) {
+               dest[m] = data[n];
+           } else {
+               /* An optimisation here: we only deal with 8-bit 
+                * data, which will be encoded as two bytes of UTF-8 */
+               if( (len - n < 2) ||
+                   decode_utf8_double( &dest[m], &data[n] ) ) {
+                   /* Failed to decode! */
+                   DEBUG( DEBUG_XML, "Could not decode UTF-8 data.\n" );
+                   strcpy( p->error, "XML parser received non-8-bit data" );
+                   p->valid = 0;
+                   return;
+               } else {
+#if 0
+                   DEBUG( DEBUG_XML, "UTF-8 two-bytes decode: "
+                          "0x%02hx 0x%02hx -> 0x%02hx!\n",
+                          data[n] & 0xFF, data[n+1] & 0xFF, dest[m] & 0xFF );
+#endif
+                   /* Skip the second byte */
+                   n += 2;
+               }
+           }
+       }
+       sbuffer_altered( p->buffer );
+    } else {
+       sbuffer_append( p->buffer, data, len );
+    }
+
+#else /* !HIP_XML_DECODE_UTF8 */
+
+    sbuffer_append( p->buffer, data, len );
+
+#endif
+
+}
+
+/* Called with the end of an element */
+static void end_element( void *userdata, const hip_xml_char *name ) {
+    struct hip_xml_parser *p = userdata;
+    struct hip_xml_state *s = p->current;
+    if( !p->valid ) {
+       /* We've stopped parsing */
+       DEBUG( DEBUG_XML, "Parse died. Ignoring end of element: %s\n", name );
+       return;
+    }
+    if( p->collect > 0 ) {
+       if( --p->collect ) {
+           sbuffer_concat( p->buffer, "</", name, ">", NULL );
+           return;
+       }
+    }
+       
+    /* process it */
+    if( s->handler->endelm_cb ) {
+       DEBUG( DEBUG_XMLPARSE, "Calling endelm callback for %s.\n", s->elm->name );
+       if( (*s->handler->endelm_cb)( s->handler->userdata, s,
+                                     p->want_cdata?sbuffer_data(p->buffer):
+                                     NULL ) ) {
+           DEBUG( DEBUG_XML, "Endelm callback failed.\n" );
+           p->valid = 0;
+       }
+    }
+    p->current = s->parent;
+    /* Move the current pointer up the branch */
+    DEBUG( DEBUG_XML, "Back in element: %s\n", friendly_name(p->current->elm) );
+    if( p->want_cdata ) {
+       sbuffer_clear( p->buffer );
+    } 
+    destroy_state( s );
+}
+
+/* Parses the attributes, and handles XML namespaces. 
+ * With a little bit of luck.
+ * Returns:
+ *   the element name on success
+ *   or NULL on error.
+ */
+static int parse_element( struct hip_xml_parser *p, struct hip_xml_state *state,
+                         const hip_xml_char *name, const hip_xml_char **atts )
+{
+    struct hip_xml_nspace *ns;
+    const hip_xml_char *pnt;
+    struct hip_xml_state *xmlt;
+
+    DEBUG( DEBUG_XMLPARSE, "Parsing elm of name: [%s]\n", name );
+    /* Parse the atts for namespace declarations... if we have any atts.
+     * expat will never pass us atts == NULL, but libxml will. */
+    if( atts != NULL ) {
+       int attn;
+       for( attn = 0; atts[attn]!=NULL; attn+=2 ) {
+           DEBUG( DEBUG_XMLPARSE, "Got attribute: [%s] = [%s]\n", atts[attn], atts[attn+1] );
+           if( strcasecmp( atts[attn], "xmlns" ) == 0 ) {
+               /* New default namespace */
+               state->default_ns = xstrdup( atts[attn+1] );
+               DEBUG( DEBUG_XMLPARSE, "New default namespace: %s\n", 
+                      state->default_ns );
+           } else if( strncasecmp( atts[attn], "xmlns:", 6 ) == 0 ) {
+               /* New namespace scope */
+               ns = xmalloc( sizeof( struct hip_xml_nspace ) );
+               ns->next = state->nspaces;
+               state->nspaces = ns;
+               ns->name = xstrdup( atts[attn]+6 ); /* skip the xmlns= */
+               ns->uri = xstrdup( atts[attn+1] );
+               DEBUG( DEBUG_XMLPARSE, "New namespace scope: %s -> %s\n",
+                      ns->name, ns->uri );
+           }
+       }
+    }
+    /* Now check the elm name for a namespace scope */
+    pnt = strchr( name, ':' );
+    if( pnt == NULL ) {
+       /* No namespace prefix - have we got a default? */
+       state->name = xstrdup(name);
+       DEBUG( DEBUG_XMLPARSE, "No prefix found, searching for default.\n" );
+       for( xmlt = state; xmlt!=NULL; xmlt=xmlt->parent ) {
+           if( xmlt->default_ns != NULL ) {
+               state->nspace = xmlt->default_ns;
+               break;
+           }
+       }
+       if( state->nspace == NULL ) {
+           DEBUG( DEBUG_XMLPARSE, "No default namespace, using empty.\n" );
+           state->nspace = "";
+       }
+    } else {
+       DEBUG( DEBUG_XMLPARSE, "Got namespace scope. Trying to resolve..." );
+       /* Have a scope - resolve it */
+       for( xmlt = state; state->nspace==NULL && xmlt!=NULL; xmlt=xmlt->parent ) {
+           for( ns = xmlt->nspaces; ns!=NULL; ns=ns->next ) {
+               /* Just compare against the bit before the :
+                * pnt points to the colon. */
+               if( strncasecmp( ns->name, name, pnt-name ) == 0 ) {
+                   /* Scope matched! Hoorah */
+                   state->nspace = ns->uri;
+                   /* end the search */
+                   break;
+               }
+           }
+       }
+       if( state->nspace != NULL ) {
+           DEBUG( DEBUG_XMLPARSE, "Resolved prefix to [%s]\n", state->nspace );
+           /* The name is everything after the ':' */
+           if( pnt[1] == '\0' ) {
+               snprintf( p->error, BUFSIZ, 
+                         "Element name missing in '%s' at line %d.",
+                         name, hip_xml_currentline(p) );
+               DEBUG( DEBUG_XMLPARSE, "No element name after ':'. Failed.\n" );
+               return -1;
+           }
+           state->name = xstrdup(pnt+1);
+       } else {
+           DEBUG( DEBUG_XMLPARSE, "Undeclared namespace.\n" );
+           snprintf( p->error, BUFSIZ, 
+                     "Undeclared namespace in '%s' at line %d.",
+                     name, hip_xml_currentline(p) );
+           return -1;
+       }
+    }
+    return 0;
+}
+
+void hip_xml_init( struct hip_xml_parser *p, struct hip_xml_handler *handlers ) 
+{
+    /* Initialize the expat stuff */
+    memset( p, 0, sizeof( struct hip_xml_parser ) );
+    /* Initialize other stuff */
+    p->valid = 1;
+    /* Placeholder for the root element */
+    p->current = p->root = xmalloc( sizeof(struct hip_xml_state) );
+    memset( p->root, 0, sizeof(struct hip_xml_state) );
+    p->root->elm = &root_element;
+    p->handlers = handlers;
+    /* Initialize the cdata buffer */
+    p->buffer = sbuffer_create();
+#ifdef HAVE_EXPAT
+    p->parser = XML_ParserCreate( NULL );
+    if( p->parser == NULL ) {
+       abort();
+    }
+    XML_SetElementHandler( p->parser, start_element, end_element );
+    XML_SetCharacterDataHandler( p->parser, char_data );
+    XML_SetUserData( p->parser, (void *) p );
+#else
+    p->parser = xmlCreatePushParserCtxt( &sax_handler, (void *)p, NULL, 0, NULL );
+    if( p->parser == NULL ) {
+       abort();
+    }
+#endif
+}   
+
+void hip_xml_parse_v( void *userdata, const char *block, size_t len ) 
+{
+    struct hip_xml_parser *p = userdata;
+    /* FIXME: The two XML parsers break all our nice abstraction by
+     * choosing different char *'s. The swine. This may kill us some
+     * day. */
+    hip_xml_parse( p, (const hip_xml_char *) block, len );
+}
+
+/* Parse the given block of input of length len */
+void hip_xml_parse( struct hip_xml_parser *p, const hip_xml_char *block, size_t len ) 
+{
+    int ret, flag;
+    /* duck out if it's broken */
+    if( !p->valid ) {
+       DEBUG( DEBUG_XML, "Not parsing %d bytes.\n", len );
+       return;
+    }
+    if( len == 0 ) {
+       flag = -1;
+       block = "";
+       DEBUG( DEBUG_XML, "Got 0-length buffer, end of document.\n" );
+    } else {   
+       DEBUG( DEBUG_XML, "Parsing %d length buffer.\n", len );
+       flag = 0;
+    }
+    /* Note, don't write a parser error if !p->valid, since an error
+     * will already have been written in that case. */
+#ifdef HAVE_EXPAT
+    ret = XML_Parse( p->parser, block, len, flag );
+    DEBUG( DEBUG_XMLPARSE, "XML_Parse returned %d\n", ret );
+    if( ret == 0 && p->valid ) {
+       snprintf( p->error, BUFSIZ,
+                 "XML parse error at line %d: %s", 
+                 XML_GetCurrentLineNumber(p->parser),
+                 XML_ErrorString(XML_GetErrorCode(p->parser)) );
+       p->valid = 0;
+    }
+#else
+    ret = xmlParseChunk( p->parser, block, len, flag );
+    DEBUG( DEBUG_XMLPARSE, "xmlParseChunk returned %d\n", ret );
+    if( p->parser->errNo && p->valid ) {
+       /* FIXME: error handling */
+       snprintf( p->error, BUFSIZ, "XML parse error at line %d.", 
+                 hip_xml_currentline(p) );
+       p->valid = 0;
+    }
+#endif
+
+}
+
+int hip_xml_finish( struct hip_xml_parser *p ) 
+{
+    struct hip_xml_state *s, *parent;
+    sbuffer_destroy( p->buffer );
+    /* Clean up any states which may remain.
+     * If p.valid, then this should be only the root element. */
+    for( s = p->current; s!=NULL; s=parent ) {
+       parent = s->parent;
+       destroy_state( s );
+    }
+#ifdef HAVE_EXPAT
+    XML_ParserFree( p->parser );
+#else
+    xmlFreeParserCtxt( p->parser );
+#endif
+    return !p->valid;
+}
+
diff --git a/neon/src/hip_xml.h b/neon/src/hip_xml.h
new file mode 100644 (file)
index 0000000..eeadff6
--- /dev/null
@@ -0,0 +1,254 @@
+/* 
+   Higher Level Interface to XML Parsers.
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef HIP_XML_H
+#define HIP_XML_H
+
+/*
+  TODO:
+  
+  There is a whopping great whole in this design. It doesn't 
+  correctly handle XML with elements interspersed with CDATA, e.g.
+    <foo>this is cdata<bar>still is</bar>still cdata here</foo>
+  To handle this, we probably need an extra flag, HIP_XML_MIXED,
+  and a cdata callback. Then, on start_element, check if we're in
+  mixed mode, and call the cdata callback with everything-up-to-now,
+  if we are, then empty the cdata buffer. Note that even with
+  this flaw the code is useful, since DAV XML responses don't 
+  use this kind of XML.
+
+  To Consider:
+
+  The sitecopy XML parsing code started off as a fairly nasty
+  hack. This, then, is the abstraction of that hack.
+  
+  Need to evaluate whether simply building the entire XML tree
+  up is actually better.
+  
+  My intuition says it's not, but, this is still quite a laborious
+  method of handling XML.
+
+*/
+
+#include <config.h>
+
+/* for BUFSIZ */
+#include <stdio.h>
+
+/* for sbuffer */
+#include "string_utils.h"
+
+#ifdef HAVE_EXPAT
+/******** Expat ***********/
+# include <xmlparse.h>
+typedef XML_Char hip_xml_char;
+
+#else /* not HAVE_EXPAT */
+# ifdef HAVE_LIBXML
+/******** libxml **********/
+#  include <parser.h>
+typedef xmlChar hip_xml_char;
+
+# else /* not HAVE_LIBXML */
+#  error need an XML parser
+# endif /* not HAVE_LIBXML */
+#endif /* not HAVE_EXPAT */
+
+/* Generic XML response handling...
+   
+   Basic principle is that you provide these things:
+     1) a list of elements which you wish to handle 
+     2) a context checking function which will validate whether a 
+     given element is valid in the given parent.
+     3) a callback function which is called when a complete element
+     has been parsed.
+    
+   This code then deals with the boring stuff like:
+     - Dealing with XML namespaces   
+     - Collecting CDATA
+
+   The element list is stored in a 'struct hip_xml_elm' array.
+   For each element, you specify the element name, an element id,
+   and some flags for that element: whether it can have children,
+   and whether it can have cdata.
+      
+   Lists of element lists can be chained together, so the elements
+   from another module don't have to be defined twice.
+
+   Say we have XML docs like:
+
+   <foo attr="yes">
+     <bar>norman</bar>
+     <bee>yesterday</bee>
+     <bar>fishing</bar>
+   </foo>
+
+   and we have one module, which handles <foo> elements, and
+   another module, which handles bar+bee elements.
+
+   The element lists are:
+
+const static struct hip_xml_elm barbee_elms[] = { 
+   {"bar", HIP_ELM_bar, HIP_XML_CDATA },
+   {"bee", HIP_ELM_bee, HIP_XML_CDATA },
+   { NULL }
+};
+
+  Note that foo doesn't take CDATA:
+
+const static struct hip_xml_elm foo_elms[] = { 
+   {"foo", HIP_ELM_foo, 0 },
+   { NULL }
+};
+
+   The context validation functions are:
+
+   int check_barbee_context( parent, child ) {
+      return ((child == HIP_ELM_bar ||
+              child == HIP_ELM_bee) && parent == HIP_ELM_foo);
+   }
+
+   int check_foo_context( a, parent, child ) {
+      return (child == HIP_ELM_foo && parent = HIP_ELM_root);
+   }
+   
+   The list-of-lists are declared:
+
+struct hip_xml_elmlist listfoo = { foo_elms, NULL },
+    listbarbee = { barbee_elms, &listfoo };
+
+    Note that listbarbee chains listfoo on the end.
+    
+*/
+
+/* Reserved element id's */
+#define HIP_ELM_unknown -1
+#define HIP_ELM_root 0
+
+typedef int hip_xml_elmid;
+
+struct hip_xml_state;
+struct hip_xml_elm;
+struct hip_xml_handler;
+
+/* An element */
+struct hip_xml_elm {
+    const char *nspace, *name;
+    hip_xml_elmid id;
+    unsigned int flags;
+};
+
+/* Function to check element context... returns 0 if child is a valid
+ * child tag of parent, else non-zero. */
+typedef int (*hip_xml_validate_cb)
+    ( hip_xml_elmid child, hip_xml_elmid parent );
+
+typedef int (*hip_xml_startelm_cb)
+    ( void *userdata, const struct hip_xml_state *s, const hip_xml_char **atts );
+
+/* Called when a complete element is parsed */
+typedef int (*hip_xml_endelm_cb)
+    ( void *userdata, const struct hip_xml_state *s, const char *cdata );
+
+/* A list of elements */
+struct hip_xml_handler {
+    const struct hip_xml_elm *elements; /* put it in static memory */
+    hip_xml_validate_cb validate_cb; /* validation function */
+    hip_xml_startelm_cb startelm_cb; /* on-complete element function */
+    hip_xml_endelm_cb endelm_cb; /* on-complete element function */
+    void *userdata;
+    struct hip_xml_handler *next;
+};
+
+struct hip_xml_state {
+    /* The element details */
+    const struct hip_xml_elm *elm;
+    hip_xml_char *name;
+    const hip_xml_char *nspace;
+    /* Namespaces declared in this element */
+    hip_xml_char *default_ns; /* A default namespace */
+    struct hip_xml_nspace *nspaces; /* List of other namespace scopes */
+    /* Extras */
+    struct hip_xml_handler *handler; /* Where the element was declared */
+    struct hip_xml_state *parent; /* The parent in the tree */
+};
+
+/* We pass around a hip_xml_parser as the userdata in the parsing
+ * library.  This maintains the current state of the parse and various
+ * other bits and bobs. Within the parse, we store the current branch
+ * of the tree, i.e., the current element and all its parents, up to
+ * the root, but nothing other than that.  */
+struct hip_xml_parser {
+    struct hip_xml_state *root; /* the root of the document */
+    struct hip_xml_state *current; /* current element in the branch */
+    sbuffer buffer; /* the CDATA/collect buffer */
+    unsigned int valid:1; /* currently valid? */
+    unsigned int want_cdata:1; /* currently collecting CDATA? */
+    unsigned int collect; /* currently collecting all children? */
+    struct hip_xml_handler *handlers; /* list of handlers */
+#ifdef HAVE_EXPAT
+    XML_Parser parser;
+#else
+    xmlParserCtxtPtr parser;
+#endif
+    char error[BUFSIZ];
+};
+
+/* Flags */
+/* This element has no children */
+#define HIP_XML_CDATA (1<<1)
+/* Collect complete contents of this node as cdata */
+#define HIP_XML_COLLECT ((1<<2) | HIP_XML_CDATA)
+/* Decode UTF-8 data in cdata. */
+#define HIP_XML_UTF8DECODE (1<<3)
+
+/* Initialise the parser p, with the list of elements we accept,
+ * the context checking function, the element callback, and the userdata
+ * for the element callback.
+ */
+void hip_xml_init( struct hip_xml_parser *p, struct hip_xml_handler *elms );
+
+/* Cleans up the parser's internal structures allocated by hip_xml_init.
+ * Returns:
+ *   0 if the parse was successful
+ *   non-zero if the parse failed.
+ */
+int hip_xml_finish( struct hip_xml_parser *p );
+
+/* Parse the given block of input of length len. Block does 
+ * not need to be NULL-terminated. Note that signed-ness of 
+ * the block characters depends upon whether libxml or expat
+ * is being used as the underlying parser. */
+void hip_xml_parse( struct hip_xml_parser *p, 
+                  const hip_xml_char *block, size_t len );
+
+/* As above taking a void * userdata, and a const char * data block,
+ * both of which are casted appropriately and passed to
+ * hip_xml_parse. */
+void hip_xml_parse_v( void *userdata, const char *block, size_t len );
+
+/* Return current parse line for errors */
+int hip_xml_currentline( struct hip_xml_parser *p );
+
+/* Returns XML-escaped text, allocated with malloc() */
+char *hip_xml_escape( const char *text );
+
+#endif /* HIP_XML_H */
diff --git a/neon/src/http_auth.c b/neon/src/http_auth.c
new file mode 100644 (file)
index 0000000..37eb218
--- /dev/null
@@ -0,0 +1,887 @@
+/* 
+   HTTP Authentication routines
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: http_auth.c,v 1.8 2000/05/09 18:33:37 joe Exp 
+*/
+
+
+/* HTTP Authentication, as per RFC2617.
+ * 
+ * TODO:
+ *  - Improve cnonce? Make it really random using /dev/random or whatever?
+ *  - Test auth-int support
+ */
+
+#include <config.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <time.h>
+
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+
+#include "base64.h"
+#include "md5.h"
+#include "dates.h"
+
+#include "http_auth.h"
+#include "string_utils.h"
+#include "uri.h"
+#include "http_utils.h"
+
+/* The MD5 digest of a zero-length entity-body */
+#define DIGEST_MD5_EMPTY "d41d8cd98f00b204e9800998ecf8427e"
+
+/* A challenge */
+struct http_auth_chall {
+    http_auth_scheme scheme;
+    char *realm;
+    char *domain;
+    char *nonce;
+    char *opaque;
+    unsigned int stale:1; /* if stale=true */
+    unsigned int got_qop:1; /* we were given a qop directive */
+    unsigned int qop_auth:1; /* "auth" token in qop attrib */
+    unsigned int qop_auth_int:1; /* "auth-int" token in qop attrib */
+    http_auth_algorithm alg;
+    struct http_auth_chall *next;
+};
+
+static const char *qop_values[] = {
+    NULL,
+    "auth",
+    "auth-int"
+};
+static const char *algorithm_names[] = {
+    "MD5",
+    "MD5-sess",
+    NULL
+};
+
+/* Private prototypes */
+static char *get_cnonce(void);
+static void clean_session( http_auth_session *sess );
+static int digest_challenge( http_auth_session *, struct http_auth_chall * );
+static int basic_challenge( http_auth_session *, struct http_auth_chall * );
+static char *request_digest( http_auth_session * );
+static char *request_basic( http_auth_session * );
+
+/* Domain handling */
+static int is_in_domain( http_auth_session *sess, const char *uri );
+static int parse_domain( http_auth_session *sess, const char *domain );
+
+/* Get the credentials, passing a temporary store for the password value */
+static int get_credentials( http_auth_session *sess, char **password );
+
+/* Initialize an auth session */
+void http_auth_init( http_auth_session *sess ) 
+{
+    memset( sess, 0, sizeof( http_auth_session ) );
+}
+
+http_auth_session *http_auth_create( void ) 
+{
+    http_auth_session *sess = xmalloc(sizeof(http_auth_session));
+    http_auth_init( sess );
+    return sess;
+}
+
+void http_auth_destroy( http_auth_session *sess ) 
+{
+    http_auth_finish( sess );
+    free( sess );
+}
+
+void http_auth_set_creds_cb( http_auth_session *sess,
+                            http_auth_request_creds callback, void *userdata )
+{
+    sess->reqcreds = callback;
+    sess->reqcreds_udata = userdata;
+}
+
+#if 0
+void http_auth_set_server( http_auth_session *sess, 
+                          const char *host, unsigned int port, const char *scheme )
+{
+    sess->host = host;
+    sess->port = port;
+    sess->req_scheme = scheme;
+}
+#endif
+
+/* Start a new request. */
+void http_auth_new_request( http_auth_session *sess,
+                           const char *method, const char *uri, 
+                           const char *body_buffer, FILE *body_stream ) 
+{
+    if( !sess->can_handle ) {
+       DEBUG( DEBUG_HTTPAUTH, "Not handling session.\n" );
+    } else if( !is_in_domain( sess, uri ) ) {
+           /* We have moved out of the authentication domain */
+           DEBUG( DEBUG_HTTPAUTH, "URI %s outside session domain, not handling.\n", uri );
+           sess->will_handle = 0;
+       } else 
+
+    {
+       DEBUG( DEBUG_HTTPAUTH, "URI %s inside session domain, will handle.\n", uri );
+
+       sess->will_handle = 1;
+       sess->uri = uri;
+       sess->method = method;
+       sess->got_body = (body_buffer!=NULL) || (body_stream!=NULL);
+       sess->body_buffer = body_buffer;
+       sess->body_stream = body_stream;
+       md5_init_ctx( &sess->response_body );
+    }
+}
+
+static void clean_session( http_auth_session *sess ) 
+{
+    sess->can_handle = 0;
+    HTTP_FREE( sess->basic );
+    HTTP_FREE( sess->unq_realm );
+    HTTP_FREE( sess->unq_nonce );
+    HTTP_FREE( sess->unq_cnonce );
+    HTTP_FREE( sess->opaque );
+    HTTP_FREE( sess->username );
+    if( sess->domain_count > 0 ) {
+       split_string_free( sess->domain );
+       sess->domain_count = 0;
+    }
+}
+
+void http_auth_finish( http_auth_session *sess ) {
+    clean_session( sess );
+}
+
+/* Returns cnonce-value. We just use base64( time ).
+ * TODO: Could improve this? */
+static char *get_cnonce(void) 
+{
+    char *ret, *tmp;
+    tmp = rfc1123_date( time(NULL) );
+    ret = base64( tmp );
+    free( tmp );
+    return ret;
+}
+
+static int 
+get_credentials( http_auth_session *sess, char **password) 
+{
+    return (*sess->reqcreds)( sess->reqcreds_udata, sess->unq_realm,
+                             &sess->username, password );
+}
+
+static int parse_domain( http_auth_session *sess, const char *domain ) {
+    char *unq, **doms;
+    int count, ret;
+
+    unq = shave_string(domain, '"' );
+    doms = split_string_c( unq, ' ', NULL, HTTP_WHITESPACE, &count );
+    if( doms != NULL ) {
+       if( count > 0 ) {
+           sess->domain = doms;
+           sess->domain_count = count;
+           ret = 0;
+       } else {
+           free( doms );
+           ret = -1;
+       }
+    } else {
+       ret = -1;
+    }
+    free( unq );
+    return ret;
+}
+
+/* Returns:
+ *    0: if uri is in NOT in domain of session
+ * else: uri IS in domain of session (or no domain known)
+ */
+static int is_in_domain( http_auth_session *sess, const char *uri )
+{
+#if 1
+    return 1;
+#else
+    /* TODO: Need proper URI comparison for this to work. */
+    int ret, dom;
+    const char *abs_path;
+    if( sess->domain_count == 0 ) {
+       DEBUG( DEBUG_HTTPAUTH, "No domain, presuming okay.\n" );
+       return 1;
+    }
+    ret = 0;
+    abs_path = uri_abspath( uri );
+    for( dom = 0; dom < sess->domain_count; dom++ ) {
+       DEBUG( DEBUG_HTTPAUTH, "Checking domain: %s\n", sess->domain[dom] );
+       if( uri_childof( sess->domain[dom], abs_path ) ) {
+           ret = 1;
+           break;
+       }
+    }
+    return ret;
+#endif
+}
+
+/* Add authentication creditials to a request */
+char *http_auth_request_header( http_auth_session *sess ) 
+{
+    if( sess->will_handle ) {
+       switch( sess->scheme ) {
+       case http_auth_scheme_basic:
+           return request_basic( sess );
+           break;
+       case http_auth_scheme_digest:
+           return request_digest( sess );
+           break;
+       default:
+           break;
+       }
+    }
+    return NULL;
+}
+
+/* Examine a Basic auth challenge.
+ * Returns 0 if an valid challenge, else non-zero. */
+static int 
+basic_challenge( http_auth_session *sess, struct http_auth_chall *parms ) 
+{
+    char *tmp, *password;
+
+    /* Verify challenge... must have a realm */
+    if( parms->realm == NULL ) {
+       return -1;
+    }
+
+    DEBUG( DEBUG_HTTPAUTH, "Got Basic challenge with realm [%s]\n", 
+          parms->realm );
+
+    clean_session( sess );
+
+    sess->unq_realm = shave_string(parms->realm, '"' );
+
+    if( get_credentials( sess, &password ) ) {
+       /* Failed to get credentials */
+       HTTP_FREE( sess->unq_realm );
+       return -1;
+    }
+
+    sess->scheme = http_auth_scheme_basic;
+
+    CONCAT3( tmp, sess->username, ":", password?password:"" );
+    sess->basic = base64( tmp );
+    free( tmp );
+
+    HTTP_FREE( password );
+
+    return 0;
+}
+
+/* Add Basic authentication credentials to a request */
+static char *request_basic( http_auth_session *sess ) 
+{
+    char *buf;
+    CONCAT3( buf, "Basic ", sess->basic, "\r\n" );
+    return buf;
+}
+
+/* Examine a digest challenge: return 0 if it is a valid Digest challenge,
+ * else non-zero. */
+static int digest_challenge( http_auth_session *sess,
+                            struct http_auth_chall *parms ) 
+{
+    struct md5_ctx tmp;
+    unsigned char tmp_md5[16];
+    char *password;
+
+    /* Do we understand this challenge? */
+    if( parms->alg == http_auth_alg_unknown ) {
+       DEBUG( DEBUG_HTTPAUTH, "Unknown algorithm.\n" );
+       return -1;
+    }
+    if( (parms->alg == http_auth_alg_md5_sess) &&
+       !( parms->qop_auth || parms->qop_auth_int ) ) {
+       DEBUG( DEBUG_HTTPAUTH, "Server did not give qop with MD5-session alg.\n" );
+       return -1;
+    }
+    if( (parms->realm==NULL) || (parms->nonce==NULL) ) {
+       DEBUG( DEBUG_HTTPAUTH, "Challenge missing nonce or realm.\n" );
+       return -1;
+    }
+
+    if( parms->stale ) {
+       /* Just a stale response, don't need to get a new username/password */
+       DEBUG( DEBUG_HTTPAUTH, "Stale digest challenge.\n" );
+    } else {
+       /* Forget the old session details */
+       DEBUG( DEBUG_HTTPAUTH, "In digest challenge.\n" );
+
+       clean_session( sess );
+       sess->unq_realm = shave_string(parms->realm, '"' );
+
+       /* Not a stale response: really need user authentication */
+       if( get_credentials( sess, &password ) ) {
+           /* Failed to get credentials */
+           HTTP_FREE( sess->unq_realm );
+           return -1;
+       }
+    }
+    sess->alg = parms->alg;
+    sess->scheme = http_auth_scheme_digest;
+    sess->unq_nonce = shave_string(parms->nonce, '"' );
+    sess->unq_cnonce = get_cnonce();
+    if( parms->domain ) {
+       if( parse_domain( sess, parms->domain ) ) {
+           /* TODO: Handle the error? */
+       }
+    } else {
+       sess->domain = NULL;
+       sess->domain_count = 0;
+    }
+    if( parms->opaque != NULL ) {
+       sess->opaque = xstrdup( parms->opaque ); /* don't strip the quotes */
+    }
+    
+    if( parms->got_qop ) {
+       /* What type of qop are we to apply to the message? */
+       DEBUG( DEBUG_HTTPAUTH, "Got qop directive.\n" );
+       sess->nonce_count = 0;
+       if( parms->qop_auth_int ) {
+           sess->qop = http_auth_qop_auth_int;
+       } else {
+           sess->qop = http_auth_qop_auth;
+       }
+    } else {
+       /* No qop at all/ */
+       sess->qop = http_auth_qop_none;
+    }
+    
+    if( !parms->stale ) {
+       /* Calculate H(A1).
+        * tmp = H( unq(username-value) ":" unq(realm-value) ":" passwd
+        */
+       DEBUG( DEBUG_HTTPAUTH, "Calculating H(A1).\n" );
+       md5_init_ctx( &tmp );
+       md5_process_bytes( sess->username, strlen(sess->username), &tmp);
+       md5_process_bytes( ":", 1, &tmp );
+       md5_process_bytes( sess->unq_realm, strlen(sess->unq_realm), &tmp );
+       md5_process_bytes( ":", 1, &tmp );
+       if( password != NULL )
+           md5_process_bytes( password, strlen(password), &tmp);
+       md5_finish_ctx( &tmp, tmp_md5 );
+       if( sess->alg == http_auth_alg_md5_sess ) {
+           unsigned char a1_md5[16];
+           struct md5_ctx a1;
+           char tmp_md5_ascii[33];
+           /* Now we calculate the SESSION H(A1)
+            *    A1 = H( ...above...) ":" unq(nonce-value) ":" unq(cnonce-value) 
+            */
+           md5_to_ascii( tmp_md5, tmp_md5_ascii );
+           md5_init_ctx( &a1 );
+           md5_process_bytes( tmp_md5_ascii, 32, &a1 );
+           md5_process_bytes( ":", 1, &a1 );
+           md5_process_bytes( sess->unq_nonce, strlen(sess->unq_nonce), &a1 );
+           md5_process_bytes( ":", 1, &a1 );
+           md5_process_bytes( sess->unq_cnonce, strlen(sess->unq_cnonce), &a1 );
+           md5_finish_ctx( &a1, a1_md5 );
+           md5_to_ascii( a1_md5, sess->h_a1 );
+           DEBUG( DEBUG_HTTPAUTH, "Session H(A1) is [%s]\n", sess->h_a1 );
+       } else {
+           md5_to_ascii( tmp_md5, sess->h_a1 );
+           DEBUG( DEBUG_HTTPAUTH, "H(A1) is [%s]\n", sess->h_a1 );
+       }
+       
+       HTTP_FREE( password );
+
+    }
+    
+    DEBUG( DEBUG_HTTPAUTH, "I like this Digest challenge.\n" );
+
+    return 0;
+}
+
+/* Return Digest authentication credentials header value for the given
+ * session. */
+static char *request_digest( http_auth_session *sess ) 
+{
+    struct md5_ctx a2, rdig;
+    unsigned char a2_md5[16], rdig_md5[16];
+    char a2_md5_ascii[33], rdig_md5_ascii[33];
+    char nc_value[9] = {0}, *ret;
+    const char *qop_value; /* qop-value */
+    size_t retlen;
+
+    /* Increase the nonce-count */
+    if( sess->qop != http_auth_qop_none ) {
+       sess->nonce_count++;
+       snprintf( nc_value, 9, "%08x", sess->nonce_count );
+       DEBUG( DEBUG_HTTPAUTH, "Nonce count is %d, nc is [%s]\n", 
+              sess->nonce_count, nc_value );
+    }
+    qop_value = qop_values[sess->qop];
+
+    /* Calculate H(A2). */
+    md5_init_ctx( &a2 );
+    md5_process_bytes( sess->method, strlen(sess->method), &a2 );
+    md5_process_bytes( ":", 1, &a2 );
+    md5_process_bytes( sess->uri, strlen(sess->uri), &a2 );
+    if( sess->qop == http_auth_qop_auth_int ) {
+       /* Calculate H(entity-body) */
+       if( sess->got_body ) {
+           char tmp_md5_ascii[33], tmp_md5[16];
+           if( sess->body_stream != NULL ) {
+               DEBUG( DEBUG_HTTPAUTH, "Digesting body stream.\n" );
+               md5_stream( sess->body_stream, tmp_md5 );
+               rewind( sess->body_stream ); /* leave it at the beginning */
+           } else if( sess->body_buffer ) {
+               DEBUG( DEBUG_HTTPAUTH, "Digesting body buffer.\n" );
+               md5_buffer( sess->body_buffer, strlen(sess->body_buffer), 
+                           tmp_md5 );
+           }
+           md5_to_ascii( tmp_md5, tmp_md5_ascii );
+           DEBUG( DEBUG_HTTPAUTH, "H(entity-body) is [%s]\n", tmp_md5_ascii );
+           /* Append to A2 */
+           md5_process_bytes( ":", 1, &a2 );
+           md5_process_bytes( tmp_md5_ascii, 32, &a2 );
+       } else {
+           /* No entity-body. */
+           DEBUG( DEBUG_HTTPAUTH, "Digesting empty entity-body.\n" );
+           md5_process_bytes( ":" DIGEST_MD5_EMPTY, 33, &a2 );
+       }
+    }
+    md5_finish_ctx( &a2, a2_md5 );
+    md5_to_ascii( a2_md5, a2_md5_ascii );
+    DEBUG( DEBUG_HTTPAUTH, "H(A2): %s\n", a2_md5_ascii );
+
+    DEBUG( DEBUG_HTTPAUTH, "Calculating Request-Digest.\n" );
+    /* Now, calculation of the Request-Digest.
+     * The first section is the regardless of qop value
+     *     H(A1) ":" unq(nonce-value) ":" */
+    md5_init_ctx( &rdig );
+
+    /* Use the calculated H(A1) */
+    md5_process_bytes( sess->h_a1, 32, &rdig );
+
+    md5_process_bytes( ":", 1, &rdig );
+    md5_process_bytes( sess->unq_nonce, strlen(sess->unq_nonce), &rdig );
+    md5_process_bytes( ":", 1, &rdig );
+    if( sess->qop != http_auth_qop_none ) {
+       /* Add on:
+        *    nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":"
+        */
+       DEBUG( DEBUG_HTTPAUTH, "Have qop directive, digesting: [%s:%s:%s]\n",
+              nc_value, sess->unq_cnonce, qop_value );
+       md5_process_bytes( nc_value, 8, &rdig );
+       md5_process_bytes( ":", 1, &rdig );
+       md5_process_bytes( sess->unq_cnonce, strlen(sess->unq_cnonce), &rdig );
+       md5_process_bytes( ":", 1, &rdig );
+       /* Store a copy of this structure (see note below) */
+       sess->stored_rdig = rdig;
+       md5_process_bytes( qop_value, strlen(qop_value), &rdig );
+       md5_process_bytes( ":", 1, &rdig );
+    } else {
+       /* Store a copy of this structure... we do this because the
+        * calculation of the rspauth= field in the Auth-Info header 
+        * is the same as this digest, up to this point. */
+       sess->stored_rdig = rdig;
+    }
+    /* And finally, H(A2) */
+    md5_process_bytes( a2_md5_ascii, 32, &rdig );
+    md5_finish_ctx( &rdig, rdig_md5 );
+    md5_to_ascii( rdig_md5, rdig_md5_ascii );
+    
+    /* Buffer size calculation. */
+    
+    retlen = 
+       6                                      /* Digest */
+       + 1 + 8 + 1 + 2 + strlen(sess->username)  /*  username="..." */
+       + 2 + 5 + 1 + 2 + strlen(sess->unq_realm) /* , realm="..." */
+       + 2 + 5 + 1 + 2 + strlen(sess->unq_nonce) /* , nonce="..." */
+       + 2 + 3 + 1 + 2 + strlen(sess->uri)       /* , uri="..." */
+       + 2 + 8 + 1 + 2 + 32                      /* , response="..." */
+       + 2 + 9 + 1 + strlen(algorithm_names[sess->alg]) /* , algorithm= */
+       ;
+
+    if( sess->opaque != NULL )
+       retlen += 2 + 6 + 1 + strlen(sess->opaque);   /* , opaque=... */
+
+    if( sess->qop != http_auth_qop_none )
+       retlen += 
+           2 + 6 + 2 + 1 + strlen(sess->unq_cnonce) +   /* , cnonce="..." */
+           2 + 2 + 1 + 8 +                       /* , nc=... */
+           2 + 3 + 1 + strlen(qop_values[sess->qop]) /* , qop=... */
+           ;
+
+    retlen += 2;   /* \r\n */
+
+    DEBUG( DEBUG_HTTPAUTH, "Calculated length of buffer: %d\n", retlen );
+
+    ret = xmalloc( retlen + 1 );
+
+    sprintf( ret,
+             "Digest username=\"%s\", realm=\"%s\""
+             ", nonce=\"%s\", uri=\"%s\", response=\"%s\""
+             ", algorithm=%s",
+             sess->username, sess->unq_realm, 
+             sess->unq_nonce, sess->uri, rdig_md5_ascii,
+             algorithm_names[sess->alg]);
+    
+    if( sess->opaque != NULL ) {
+       /* We never unquote it, so it's still quoted here */
+       strcat( ret, ", opaque=" );
+       strcat( ret, sess->opaque );
+    }
+
+    if( sess->qop != http_auth_qop_none ) {
+       /* Add in cnonce and nc-value fields */
+       strcat( ret, ", cnonce=\"" );
+       strcat( ret, sess->unq_cnonce );
+       strcat( ret, "\", nc=" );
+       strcat( ret, nc_value );
+       strcat( ret, ", qop=" );
+       strcat( ret, qop_values[sess->qop] );
+    }
+
+    DEBUG( DEBUG_HTTPAUTH, "Digest header field value:\n%s\n", ret );
+
+    strcat( ret, "\r\n" );
+
+    DEBUG( DEBUG_HTTPAUTH, "Calculated length: %d, actual length: %d\n", 
+          retlen, strlen( ret ) );
+    
+    return ret;
+}
+
+inline void http_auth_response_body( http_auth_session *sess, 
+                                    const char *buffer, size_t buffer_len ) 
+{
+    if( !sess->will_handle ||
+       sess->scheme != http_auth_scheme_digest ) return;
+    DEBUG( DEBUG_HTTPAUTH, "Digesting %d bytes of response body.\n",
+          buffer_len );
+    md5_process_bytes( buffer, buffer_len, &sess->response_body );
+}
+
+/* Pass this the value of the 'Authentication-Info:' header field, if
+ * one is received.
+ * Returns:
+ *    0 if it gives a valid authentication for the server 
+ *    non-zero otherwise (don't believe the response in this case!).
+ */
+int http_auth_verify_response( http_auth_session *sess, const char *value ) 
+{
+    char **pairs;
+    http_auth_qop qop = http_auth_qop_none;
+    char *nextnonce = NULL, /* for the nextnonce= value */
+       *rspauth = NULL, /* for the rspauth= value */
+       *cnonce = NULL, /* for the cnonce= value */
+       *nc = NULL, /* for the nc= value */
+       *unquoted, *qop_value = NULL;
+    int n, nonce_count, okay;
+    
+    if( !sess->will_handle ) {
+       /* Ignore it */
+       return 0;
+    }
+    
+    if( sess->scheme != http_auth_scheme_digest ) {
+       DEBUG( DEBUG_HTTPAUTH, "Found Auth-Info header not in response to Digest credentials - dodgy.\n" );
+       return -1;
+    }
+    
+    DEBUG (DEBUG_HTTPAUTH, "Auth-Info header: %s\n", value );
+
+    pairs = pair_string( value, ',', '=', HTTP_QUOTES, HTTP_WHITESPACE );
+    
+    for( n = 0; pairs[n]!=NULL; n+=2 ) {
+       unquoted = shave_string( pairs[n+1], '"' );
+       if( strcasecmp( pairs[n], "qop" ) == 0 ) {
+           qop_value = xstrdup( pairs[n+1] );
+           if( strcasecmp( pairs[n+1], "auth-int" ) == 0 ) {
+               qop = http_auth_qop_auth_int;
+           } else if( strcasecmp( pairs[n+1], "auth" ) == 0 ) {
+               qop = http_auth_qop_auth;
+           } else {
+               qop = http_auth_qop_none;
+           }
+       } else if( strcasecmp( pairs[n], "nextnonce" ) == 0 ) {
+           nextnonce = xstrdup( unquoted );
+       } else if( strcasecmp( pairs[n], "rspauth" ) == 0 ) {
+           rspauth = xstrdup( unquoted );
+       } else if( strcasecmp( pairs[n], "cnonce" ) == 0 ) {
+           cnonce = xstrdup( unquoted );
+       } else if( strcasecmp( pairs[n], "nc" ) == 0 ) { 
+           nc = xstrdup( pairs[n] );
+           if( sscanf( pairs[n+1], "%x", &nonce_count ) != 1 ) {
+               DEBUG( DEBUG_HTTPAUTH, "Couldn't scan [%s] for nonce count.\n",
+                      pairs[n+1] );
+           } else {
+               DEBUG( DEBUG_HTTPAUTH, "Got nonce_count: %d\n", nonce_count );
+           }
+       }
+       free( unquoted );
+    }
+    pair_string_free( pairs );
+
+    /* Presume the worst */
+    okay = -1;
+
+    if( (qop != http_auth_qop_none) && (qop_value != NULL) ) {
+       if( (rspauth == NULL) || (cnonce == NULL) || (nc == NULL) ) {
+           DEBUG( DEBUG_HTTPAUTH, "Missing rspauth, cnonce or nc with qop.\n" );
+       } else { /* Have got rspauth, cnonce and nc */
+           if( strcmp( cnonce, sess->unq_cnonce ) != 0 ) {
+               DEBUG( DEBUG_HTTPAUTH, "Response cnonce doesn't match.\n" );
+           } else if( nonce_count != sess->nonce_count ) { 
+               DEBUG( DEBUG_HTTPAUTH, "Response nonce count doesn't match.\n" );
+           } else {
+               /* Calculate and check the response-digest value.
+                * joe: IMO the spec is slightly ambiguous as to whether
+                * we use the qop which WE sent, or the qop which THEY
+                * sent...  */
+               struct md5_ctx a2;
+               unsigned char a2_md5[16], rdig_md5[16];
+               char a2_md5_ascii[33], rdig_md5_ascii[33];
+
+               DEBUG( DEBUG_HTTPAUTH, "Calculating response-digest.\n" );
+
+               /* First off, H(A2) again. */
+               md5_init_ctx( &a2 );
+               md5_process_bytes( ":", 1, &a2 );
+               md5_process_bytes( sess->uri, strlen(sess->uri), &a2 );
+               if( qop == http_auth_qop_auth_int ) {
+                   unsigned char heb_md5[16];
+                   char heb_md5_ascii[33];
+                   /* Add on ":" H(entity-body) */
+                   md5_finish_ctx( &sess->response_body, heb_md5 );
+                   md5_to_ascii( heb_md5, heb_md5_ascii );
+                   md5_process_bytes( ":", 1, &a2 );
+                   md5_process_bytes( heb_md5_ascii, 32, &a2 );
+                   DEBUG( DEBUG_HTTPAUTH, "Digested [:%s]\n", heb_md5_ascii );
+               }
+               md5_finish_ctx( &a2, a2_md5 );
+               md5_to_ascii( a2_md5, a2_md5_ascii );
+               
+               /* We have the stored digest-so-far of 
+                *   H(A1) ":" unq(nonce-value) 
+                *        [ ":" nc-value ":" unq(cnonce-value) ] for qop
+                * in sess->stored_rdig, to save digesting them again.
+                *
+                */
+               if( qop != http_auth_qop_none ) {
+                   /* Add in qop-value */
+                   DEBUG( DEBUG_HTTPAUTH, "Digesting qop-value [%s:].\n", 
+                          qop_value );
+                   md5_process_bytes( qop_value, strlen(qop_value), 
+                                      &sess->stored_rdig );
+                   md5_process_bytes( ":", 1, &sess->stored_rdig );
+               }
+               /* Digest ":" H(A2) */
+               md5_process_bytes( a2_md5_ascii, 32, &sess->stored_rdig );
+               /* All done */
+               md5_finish_ctx( &sess->stored_rdig, rdig_md5 );
+               md5_to_ascii( rdig_md5, rdig_md5_ascii );
+
+               DEBUG( DEBUG_HTTPAUTH, "Calculated response-digest of: [%s]\n",
+                      rdig_md5_ascii );
+               DEBUG( DEBUG_HTTPAUTH, "Given response-digest of:      [%s]\n",
+                      rspauth );
+
+               /* And... do they match? */
+               okay = (strcasecmp( rdig_md5_ascii, rspauth ) == 0)?0:-1;
+               DEBUG( DEBUG_HTTPAUTH, "Matched: %s\n", okay?"nope":"YES!" );
+           }
+           free( rspauth );
+           free( cnonce );
+           free( nc );
+       }
+       free( qop_value );
+    } else {
+       DEBUG( DEBUG_HTTPAUTH, "No qop directive, auth okay.\n" );
+       okay = 0;
+    }
+
+    /* Check for a nextnonce */
+    if( nextnonce != NULL ) {
+       DEBUG( DEBUG_HTTPAUTH, "Found nextnonce of [%s].\n", nextnonce );
+       if( sess->unq_nonce != NULL )
+           free( sess->unq_nonce );
+       sess->unq_nonce = nextnonce;
+    }
+
+    return okay;
+}
+
+/* A new challenge presented by the server */
+int http_auth_challenge( http_auth_session *sess, const char *value ) 
+{
+    char **pairs, *pnt, *unquoted, *key;
+    struct http_auth_chall *chall = NULL, *challenges = NULL;
+    int n, success;
+
+    DEBUG( DEBUG_HTTPAUTH, "Got new auth challenge: %s\n", value );
+
+    /* The header value may be made up of one or more challenges.
+     * We split it down into attribute-value pairs, then search for
+     * schemes in the pair keys.
+     */
+    pairs = pair_string( value, ',', '=', HTTP_QUOTES, HTTP_WHITESPACE );
+
+    for( n = 0; pairs[n]!=NULL; n+=2 ) {
+       /* Look for an auth-scheme in the key */
+       pnt = strchr( pairs[n], ' ' );
+       if( pnt != NULL ) {
+           /* We have a new challenge */
+           DEBUG( DEBUG_HTTPAUTH, "New challenge.\n" );
+           chall = xmalloc( sizeof(struct http_auth_chall) );
+           memset( chall, 0, sizeof(struct http_auth_chall) );
+           chall->next = challenges;
+           challenges = chall;
+           /* Initialize the challenge parameters */
+           /* Which auth-scheme is it (case-insensitive matching) */
+           if( strncasecmp( pairs[n], "basic ", 6 ) == 0 ) {
+               DEBUG( DEBUG_HTTPAUTH, "Basic scheme.\n" );
+               chall->scheme = http_auth_scheme_basic;
+           } else if( strncasecmp( pairs[n], "digest ", 7 ) == 0 ) {
+               DEBUG( DEBUG_HTTPAUTH, "Digest scheme.\n" );
+               chall->scheme = http_auth_scheme_digest;
+           } else {
+               DEBUG( DEBUG_HTTPAUTH, "Unknown scheme.\n" );
+               free( chall );
+               challenges = NULL;
+               break;
+           }
+           /* Now, the real key for this pair starts after the 
+            * auth-scheme... skipping whitespace */
+           while( strchr( HTTP_WHITESPACE, *(++pnt) ) != NULL )
+               /* nullop */;
+           key = pnt;
+       } else if( chall == NULL ) {
+           /* If we haven't got an auth-scheme, and we're
+            * haven't yet found a challenge, skip this pair.
+            */
+           continue;
+       } else {
+           key = pairs[n];
+       }
+       DEBUG( DEBUG_HTTPAUTH, "Got pair: [%s] = [%s]\n", key, pairs[n+1] );
+       /* Most values are quoted, so unquote them here */
+       unquoted = shave_string( pairs[n+1], '"' );
+       /* Now parse the attribute */
+       DEBUG( DEBUG_HTTPAUTH, "Unquoted pair is: [%s]\n", unquoted );
+       if( strcasecmp( key, "realm" ) == 0 ) {
+           chall->realm = pairs[n+1];
+       } else if( strcasecmp( key, "nonce" ) == 0 ) {
+           chall->nonce = pairs[n+1];
+       } else if( strcasecmp( key, "opaque" ) == 0 ) {
+           chall->opaque = pairs[n+1];
+       } else if( strcasecmp( key, "domain" ) == 0 ) {
+           chall->domain = pairs[n+1];
+       } else if( strcasecmp( key, "stale" ) == 0 ) {
+           /* Truth value */
+           chall->stale = 
+               ( strcasecmp( unquoted, "true" ) == 0 );
+       } else if( strcasecmp( key, "algorithm" ) == 0 ) {
+           if( strcasecmp( unquoted, "md5" ) == 0 ) {
+               chall->alg = http_auth_alg_md5;
+           } else if( strcasecmp( unquoted, "md5-sess" ) == 0 ) {
+               chall->alg = http_auth_alg_md5_sess;
+           } else {
+               chall->alg = http_auth_alg_unknown;
+           }
+       } else if( strcasecmp( key, "qop" ) == 0 ) {
+           char **qops;
+           int qop;
+           qops = split_string( unquoted, ',', NULL, HTTP_WHITESPACE );
+           chall->got_qop = 1;
+           for( qop = 0; qops[qop] != NULL; qop++ ) {
+               if( strcasecmp( qops[qop], "auth" ) == 0 ) {
+                   chall->qop_auth = 1;
+               } else if( strcasecmp( qops[qop], "auth-int" ) == 0 ) {
+                   chall->qop_auth_int = 1;
+               }
+           }
+           split_string_free( qops );
+       }
+       free( unquoted );
+    }
+
+    DEBUG( DEBUG_HTTPAUTH, "Finished parsing parameters.\n" );
+
+    /* Did we find any challenges */
+    if( challenges == NULL ) {
+       pair_string_free( pairs );
+       return -1;
+    }
+    
+    success = 0;
+
+    DEBUG( DEBUG_HTTPAUTH, "Looking for Digest challenges.\n" );
+
+    /* Try a digest challenge */
+    for( chall = challenges; chall != NULL; chall = chall->next ) {
+       if( chall->scheme == http_auth_scheme_digest ) {
+           if( !digest_challenge( sess, chall ) ) {
+               success = 1;
+               break;
+           }
+       }
+    }
+
+    if( !success ) {
+       DEBUG( DEBUG_HTTPAUTH, "No good Digest challenges, looking for Basic.\n" );
+       for( chall = challenges; chall != NULL; chall = chall->next ) {
+           if( chall->scheme == http_auth_scheme_basic ) {
+               if( !basic_challenge( sess, chall ) ) {
+                   success = 1;
+                   break;
+               }
+           }
+       }
+
+       if( !success ) {
+           /* No good challenges - record this in the session state */
+           DEBUG( DEBUG_HTTPAUTH, "Did not understand any challenges.\n" );
+       }
+
+    }
+    
+    /* Remember whether we can now supply the auth details */
+    sess->can_handle = success;
+
+    while( challenges != NULL ) {
+       chall = challenges->next;
+       free( challenges );
+       challenges = chall;
+    }
+
+    /* Free up the parsed header values */
+    pair_string_free( pairs );
+
+    return !success;
+}
diff --git a/neon/src/http_auth.h b/neon/src/http_auth.h
new file mode 100644 (file)
index 0000000..8b0a430
--- /dev/null
@@ -0,0 +1,206 @@
+/* 
+   HTTP authentication routines
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef HTTPAUTH_H
+#define HTTPAUTH_H
+
+#include <sys/types.h>
+
+#include "md5.h"
+
+/* HTTP Authentication - a pretty complete client implementation of RFC2617.
+ */
+
+/*
+  To use:
+ 1. A session state variable (http_auth_session) is needed for each of
+ server state and proxy state. These may be statically declared (use
+ _init/_finish), or dynamically (use _create/_destroy).
+ 2. To begin a new session, call http_auth_init() or http_auth_create().
+ Set up a callback function with http_auth_set_creds_cb() for
+ supplying the username and password on demand. See below for details.
+
+ 3. Before sending a request, pass http_auth_new_request its details,
+ on BOTH auth session variables if you are using a proxy server too.
+ 4. Call http_auth_request_header() and add your 'Authentication:'
+ header to the request if returns non-NULL. Similarly for
+ Proxy-Authentication.
+ 5. Send the request.
+
+ 6. Read the response:
+  - Pass the value of the '(Proxy|WWW)-Authenticate' header to 
+    http_auth_challenge.
+  - If there is 'Authentication-Info', save its value for later.
+  - Pass each block of the response entity-body to http_auth_response_body.
+
+ 7. After reading the complete response, if an Auth-Info header was
+ received, pass its value to http_auth_verify_response to check
+ whether the SERVER was authenticated okay, passing the saved value.
+
+ 8. If a 401 or a 407 response is received, retry once for each, by
+ going back to step 3. Note that http_auth_new_request MUST be called
+ again if the SAME request is being retried.
+
+*/
+
+/* The authentication scheme we are using */
+typedef enum {
+    http_auth_scheme_basic,
+    http_auth_scheme_digest
+} http_auth_scheme;
+
+typedef enum { 
+    http_auth_alg_md5,
+    http_auth_alg_md5_sess,
+    http_auth_alg_unknown
+} http_auth_algorithm;
+
+/* Selected method of qop which the client is using */
+typedef enum {
+    http_auth_qop_none,
+    http_auth_qop_auth,
+    http_auth_qop_auth_int
+} http_auth_qop;
+
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be placed in malloc()-allocate
+ * memory.
+ * Must return:
+ *   0 on success, 
+ *  -1 to cancel.
+ */
+typedef int (*http_auth_request_creds)( 
+    void *userdata, const char *realm,
+    char **username, char **password );
+
+/* Authentication session state. */
+typedef struct {
+    /* The scheme used for this authentication session */
+    http_auth_scheme scheme;
+    /* The callback used to request new username+password */
+    http_auth_request_creds reqcreds;
+    void *reqcreds_udata;
+
+    /*** Session details ***/
+
+    /* The username and password we are using to authenticate with */
+    char *username;
+    /* Whether we CAN supply authentication at the moment */
+    unsigned int can_handle:1;
+    /* This used for Basic auth */
+    char *basic; 
+    /* These all used for Digest auth */
+    char *unq_realm;
+    char *unq_nonce;
+    char *unq_cnonce;
+    char *opaque;
+    /* A list of domain strings */
+    unsigned int domain_count;
+    char **domain;
+    http_auth_qop qop;
+    http_auth_algorithm alg;
+    int nonce_count;
+    /* The ASCII representation of the session's H(A1) value */
+    char h_a1[33];
+    /* Used for calculation of H(entity-body) of the response */
+    struct md5_ctx response_body;
+    /* Temporary store for half of the Request-Digest
+     * (an optimisation - used in the response-digest calculation) */
+    struct md5_ctx stored_rdig;
+
+    /* Details of server... needed to reconstruct absoluteURI's when
+     * necessary */
+    const char *host;
+    const char *uri_scheme;
+    unsigned int port;
+
+    /*** Details of current request ***/
+
+    /* The method and URI we are using for the current request */
+    const char *uri;
+    const char *method;
+    /* Whether we WILL supply authentication for this request or not */
+    unsigned int will_handle:1;
+    /* Whether we have a request body for the current request */
+    unsigned int got_body:1;
+    /* And what the body is - stream or buffer */
+    FILE *body_stream;
+    const char *body_buffer;
+
+} http_auth_session;
+
+/* Initializes the authentication state for the given session,
+ * which will use the given username and password. */
+void http_auth_init( http_auth_session *sess );
+
+void http_auth_set_creds_cb(  http_auth_session *sess,
+    http_auth_request_creds callback, void *userdata );
+
+/* Finishes off the given authentication session, freeing
+ * any memory used. */
+void http_auth_finish( http_auth_session *sess );
+
+/* Creates a new authentication session.
+ * Returns non-NULL on success */
+http_auth_session * http_auth_create( void );
+
+/* Destroys an authentication session, freeing the session state
+ * itself too. */
+void http_auth_destroy( http_auth_session *sess ); 
+
+/* Call this before sending a request.  Pass ONE OF body_buffer or
+ * body_stream as non-NULL if the request will include an
+ * entity-body. If body_buffer is non-NULL, it MUST be
+ * \0-terminated. If body_stream is non-NULL, it may be read once
+ * during http_auth_challenge, then rewound.  uri must identical to
+ * Request-URI, EXCEPT for server auth state, where if the request is
+ * passing through a proxy, then uri should be the same as abs_path.  */
+void http_auth_new_request( http_auth_session *sess,
+                           const char *method, const char *uri,
+                           const char *body_buffer, FILE *body_stream );
+
+/* Returns the value of the authentication field if one is to be sent,
+ * else NULL. The return value will be taken from malloc()'ed memory,
+ * so should be free()'ed after use. */
+char *http_auth_request_header( http_auth_session *sess );
+
+/* Pass this the value of the "(Proxy,WWW)-Authenticate: " header field.
+ * Returns:
+ *   0 if we can now authenticate ourselves with the server.
+ *   non-zero if we can't
+ */
+int http_auth_challenge( http_auth_session *sess, const char *value );
+
+/* As you receive sections of the response entity-body, pass them to 
+ * this function. */
+void http_auth_response_body( http_auth_session *sess, 
+                             const char *buffer, size_t buffer_len );
+
+/* If you receive a "(Proxy-)Authentication-Info:" header, pass its value to
+ * this function. Returns zero if this successfully authenticates
+ * the response as coming from the server, and false if it hasn't. */
+int http_auth_verify_response( http_auth_session *sess, const char *value );
+
+#endif /* HTTPAUTH_H */
diff --git a/neon/src/http_basic.c b/neon/src/http_basic.c
new file mode 100644 (file)
index 0000000..ac8682d
--- /dev/null
@@ -0,0 +1,357 @@
+/* 
+   HTTP/1.1 methods
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: http_basic.c,v 1.12 2000/05/10 16:45:58 joe Exp 
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <errno.h>
+
+#include "http_request.h"
+#include "http_basic.h"
+#include "dates.h"
+#include "socket.h"
+#include "neon_i18n.h"
+
+/* Header parser to retrieve Last-Modified date */
+static void get_lastmodified( void *userdata, const char *value ) {
+    time_t *modtime = userdata;
+    *modtime = http_dateparse( value );
+}
+
+int http_getmodtime( http_session *sess, const char *uri, time_t *modtime ) 
+{
+    http_req *req = http_request_create( sess, "HEAD", uri );
+    http_status st;
+    int ret;
+
+#ifdef USE_DAV_LOCKS
+    dav_lock_using_resource( req, uri, dav_lockusage_read, 0 );
+#endif
+
+    http_add_response_header_handler( req, "Last-Modified", get_lastmodified,
+                                     modtime );
+
+    *modtime = -1;
+
+    ret = http_request_dispatch( req, &st );
+
+    if( ret == HTTP_OK && st.class != 2 ) {
+       *modtime = -1;
+       ret = HTTP_ERROR;
+    }
+
+    http_request_destroy( req );
+
+    return ret;
+}
+
+/* PUT's stream to URI */
+int http_put( http_session *sess, const char *uri, FILE *stream ) 
+{
+    http_req *req = http_request_create( sess, "PUT", uri );
+    http_status status;
+    int ret;
+    
+#ifdef USE_DAV_LOCKS
+    dav_lock_using_resource( req, uri, dav_lockusage_write, 0 );
+    dav_lock_using_parent( req, uri );
+#endif
+
+    http_set_request_body_stream( req, stream );
+       
+    ret = http_request_dispatch( req, &status );
+    
+    if( ret == HTTP_OK && status.class != 2 )
+       ret = HTTP_ERROR;
+
+    http_request_destroy( req );
+
+    return ret;
+}
+
+/* Conditional HTTP put. 
+ * PUTs stream to uri, returning HTTP_FAILED if resource as URI has
+ * been modified more recently than 'since'.
+ */
+int 
+http_put_if_unmodified( http_session *sess, const char *uri, 
+                       FILE *stream, time_t since ) {
+    http_req *req;
+    http_status status;
+    sbuffer hdrs;
+    char *date;
+    int ret;
+    
+    if( http_version_pre_http11(sess) ) {
+       time_t modtime;
+       /* Server is not minimally HTTP/1.1 compliant.  Do a HEAD to
+        * check the remote mod time. Of course, this makes the
+        * operation very non-atomic, but better than nothing. */
+       ret = http_getmodtime( sess, uri, &modtime );
+       if( ret != HTTP_OK ) return ret;
+       if( modtime != since )
+           return HTTP_FAILED;
+    }
+
+    req = http_request_create( sess, "PUT", uri );
+
+    date = rfc1123_date( since );
+    /* Add in the conditionals */
+    hdrs = http_get_request_header( req );
+    sbuffer_concat( hdrs, "If-Unmodified-Since: ", date, EOL, NULL );
+    free( date );
+    
+#ifdef USE_DAV_LOCKS
+    dav_lock_using_resource( &req, uri, dav_lockusage_write, 0 );
+    /* NB: this will give 412 if the resource doesn't exist, since PUT
+     * may modify the parent, but thats okay, since that will give
+     * a HTTP_FAILED response too. */
+#endif
+
+    http_set_request_body_stream( req, stream );
+
+    ret = http_request_dispatch( req, &status );
+    
+    if( ret == HTTP_OK ) {
+       if( status.code == 412 ) {
+           ret = HTTP_FAILED;
+       } else if( status.class != 2 ) {
+           ret = HTTP_ERROR;
+       }
+    }
+
+    http_request_destroy( req );
+
+    return ret;
+}
+
+struct get_context {
+    int error;
+    size_t total, progress;
+    http_block_reader callback; /* used in read_file */
+    FILE *file; /* used in get_to_fd */
+    void *userdata;
+};
+
+static void get_callback( void *userdata, const char *block, size_t length ) 
+{
+    struct get_context *ctx = userdata;
+
+    DEBUG( DEBUG_HTTP, "Got progress: %d out of %d\n", 
+          ctx->progress, ctx->total );
+
+    (*ctx->callback)( ctx->userdata, block, length );
+
+    /* Increase progress */
+    ctx->progress += length;
+    if( ctx->progress > ctx->total ) {
+       /* Reset the counter if we're uploading it again */
+       ctx->progress -= ctx->total;
+    }
+    sock_call_progress( ctx->progress, ctx->total );
+}
+
+int http_read_file( http_session *sess, const char *uri, 
+                   http_block_reader reader, void *userdata ) {
+    struct get_context ctx;
+    http_req *req = http_request_create( sess, "GET", uri );
+    http_status st;
+    int ret;
+    
+    ctx.total = -1;
+    ctx.progress = 0;
+    ctx.callback = reader;
+    ctx.userdata = userdata;
+
+    /* Read the value of the Content-Length header into ctx.total */
+    http_add_response_header_handler( req, "Content-Length",
+                                     http_handle_numeric_header,
+                                     &ctx.total );
+
+    http_add_response_body_reader( req, http_accept_2xx, get_callback, &ctx );
+
+#ifdef USE_DAV_LOCKS
+    dav_lock_using_resource( req, uri, dav_lockusage_read, 0 );
+#endif
+
+    ret = http_request_dispatch( req, &st );
+
+    if( ret == HTTP_OK && st.class != 2 )
+       ret = HTTP_ERROR;
+
+    http_request_destroy( req );
+
+    return ret;
+}
+
+static void get_to_fd( void *userdata, const char *block, size_t length )
+{
+    struct get_context *ctx = userdata;
+    FILE *f = ctx->file;
+    if( !ctx->error ) {
+       if( fwrite( block, length, 1, f ) < length ) {
+           ctx->error = errno;
+       }
+    }
+}
+
+/* Get to given stream */
+int http_get( http_session *sess, const char *uri, FILE *f )
+{
+    http_req *req = http_request_create( sess, "GET", uri );
+    http_status status;
+    struct get_context ctx;
+    int ret;
+
+    ctx.total = -1;
+    ctx.progress = 0;
+    ctx.callback = get_to_fd;
+    ctx.userdata = &ctx;
+    ctx.file = f;
+    ctx.error = 0;
+
+    /* Read the value of the Content-Length header into ctx.total */
+    http_add_response_header_handler( req, "Content-Length",
+                                     http_handle_numeric_header,
+                                     &ctx.total );
+
+    http_add_response_body_reader( req, http_accept_2xx, get_callback, &ctx );
+
+#ifdef USE_DAV_LOCKS
+    dav_lock_using_resource( req, uri, dav_lockusage_read, 0 );
+#endif
+
+    ret = http_request_dispatch( req, &status );
+    
+    if( ctx.error ) {
+       char buf[BUFSIZ];
+       snprintf( buf, BUFSIZ, 
+                 _("Could not write to file: %s"), strerror(ctx.error) );
+       http_set_error( sess, buf );
+       ret = HTTP_ERROR;
+    }
+
+    if( ret == HTTP_OK && status.class != 2 ) {
+       ret = HTTP_ERROR;
+    }
+
+    http_request_destroy( req );
+
+    return ret;
+}
+
+static void server_hdr_handler( void *userdata, const char *value )
+{
+    char **tokens = split_string( value, ' ', HTTP_QUOTES, NULL );
+    http_server_capabilities *caps = userdata;
+    int n;
+
+    for( n = 0; tokens[n] != NULL; n++ ) {
+       if( strncasecmp( tokens[n], "Apache/", 7 ) == 0 && 
+           strlen(tokens[n]) > 11 ) { /* 12 == "Apache/1.3.0" */
+           const char *ver = tokens[n] + 7;
+           int count;
+           char **vers;
+           vers = split_string_c( ver, '.', NULL, NULL, &count );
+           /* Apache/1.3.6 and before have broken Expect: 100 support */
+           if( count > 1 && atoi(vers[0]) < 2 && atoi(vers[1]) < 4 && atoi(vers[2]) < 7 ) {
+               caps->broken_expect100 = 1;
+           }
+           split_string_free( vers );
+       }
+    }    
+    
+    split_string_free( tokens );
+}
+
+void http_content_type_handler( void *userdata, const char *value )
+{
+    http_content_type *ct = userdata;
+    char *sep, *parms;
+
+    ct->value = xstrdup(value);
+    
+    sep = strchr( ct->value, '/' );
+    if( !sep ) {
+       HTTP_FREE( ct->value );
+       return;
+    }
+
+    *++sep = '\0';
+    ct->type = ct->value;
+    ct->subtype = sep;
+    
+    parms = strchr( ct->value, ';' );
+
+    if( parms ) {
+       *parms = '\0';
+       /* TODO: handle charset. */
+    }
+}
+
+static void dav_hdr_handler( void *userdata, const char *value )
+{
+    char **classes, **class;
+    http_server_capabilities *caps = userdata;
+    
+    classes = split_string( value, ',', HTTP_QUOTES, HTTP_WHITESPACE );
+    for( class = classes; *class!=NULL; class++ ) {
+
+       if( strcmp( *class, "1" ) == 0 ) {
+           caps->dav_class1 = 1;
+       } else if( strcmp( *class, "2" ) == 0 ) {
+           caps->dav_class2 = 1;
+       } else if( strcmp( *class, "<http://apache.org/dav/propset/fs/1>" ) == 0 ) {
+           caps->dav_executable = 1;
+       }
+    }
+    
+    split_string_free( classes );
+
+}
+
+int http_options( http_session *sess, const char *uri,
+                 http_server_capabilities *caps )
+{
+    http_req *req = http_request_create( sess, "OPTIONS", uri );
+    http_status status;
+    
+    int ret;
+
+    http_add_response_header_handler( req, "Server", server_hdr_handler, caps );
+    http_add_response_header_handler( req, "DAV", dav_hdr_handler, caps );
+
+    ret = http_request_dispatch( req, &status );
+    if( ret == HTTP_OK && status.class != 2 ) {
+       ret = HTTP_ERROR;
+    }
+    
+    http_request_destroy( req );
+
+    return ret;
+}
diff --git a/neon/src/http_basic.h b/neon/src/http_basic.h
new file mode 100644 (file)
index 0000000..365b84e
--- /dev/null
@@ -0,0 +1,103 @@
+/* 
+   HTTP/1.1 methods
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef HTTP_BASIC_H
+#define HTTP_BASIC_H
+
+#include "config.h"
+
+#include <sys/types.h> /* for time_t */
+
+#include <stdio.h> /* for FILE * */
+
+/* PUT resource at uri, reading request body from f */
+int http_put( http_session *sess, const char *uri, FILE *f );
+
+/* PUT resource at uri as above, only if it has not been modified
+ * since given modtime. If server is HTTP/1.1, uses If-Unmodified-Since
+ * header; guaranteed failure if resource is modified after 'modtime'.
+ * If server is HTTP/1.0, HEAD's the resource first to fetch current
+ * modtime; race condition if resource is modified between HEAD and PUT.
+ */
+int http_put_if_unmodified( http_session *sess,
+                           const char *uri, FILE *stream, time_t modtime );
+
+/* GET resource at uri, writing response body into f */
+int http_get( http_session *sess, const char *uri, FILE *f );
+
+/* GET resource at uri, passing response body blocks to 'reader' */
+int http_read_file( http_session *sess, const char *uri, 
+                   http_block_reader reader, void *userdata );
+
+/* Retrieve modification time of resource at uri, place in *modtime.
+ * (uses HEAD) */
+int http_getmodtime( http_session *sess, const char *uri, time_t *modtime );
+
+typedef struct {
+    const char *type, *subtype;
+    const char *charset;
+    char *value;
+} http_content_type;   
+
+/* Sets (*http_content_type)userdata appropriately. 
+ * Caller must free ->value after use */
+void http_content_type_handler( void *userdata, const char *value );
+
+/* Server capabilities: */
+typedef struct {
+    unsigned int broken_expect100:1; /* True if the server is known to
+                                     * have broken Expect:
+                                     * 100-continue support; Apache
+                                     * 1.3.6 and earlier. */
+
+    unsigned int dav_class1; /* True if Class 1 WebDAV server */
+    unsigned int dav_class2; /* True if Class 2 WebDAV server */
+    unsigned int dav_executable; /* True if supports the 'executable'
+                                 * property a. la. mod_dav */
+} http_server_capabilities;
+
+/* Determines server capabilities (using OPTIONS). 
+ * Pass uri="*" to determine proxy server capabilities if using
+ * a proxy server. */
+int http_options( http_session *sess, const char *uri, 
+                 http_server_capabilities *caps );
+
+#if 0 /* TODO: unimplemented */
+
+typedef http_content_range {
+    long start, end, total;
+} http_content_range;
+
+/* This will write to the CURRENT position of f; so if you want
+ * to do a resume download, use:
+ *      struct http_content_range range;
+ *      range.start = resume_from; 
+ *      range.end = range.total = 1000;
+ *      fseek( myfile, resume_from, SEEK_SET );
+ *      http_get_range( sess, uri, &range, myfile );
+ */
+int http_get_range( http_session *sess, const char *uri, 
+                   http_content_range *range, FILE *f );
+
+#endif
+
+
+#endif
diff --git a/neon/src/http_cookies.c b/neon/src/http_cookies.c
new file mode 100644 (file)
index 0000000..6a85628
--- /dev/null
@@ -0,0 +1,143 @@
+/* 
+   Basic cookie support for neon
+   Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: http_cookies.c,v 1.5 2000/07/16 16:26:46 joe Exp 
+*/
+
+/* A nice demo of hooks, since it doesn't need any external
+ * interface to muck with the stored states.
+ */
+
+#include <config.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <time.h>
+
+#include "http_request.h"
+#include "string_utils.h"
+#include "http_cookies.h"
+#include "xalloc.h"
+
+static void set_cookie_hdl(void *userdata, const char *value)
+{
+    char **pairs = pair_string(value, ';', '=', HTTP_QUOTES, HTTP_WHITESPACE);
+    http_cookie *cook;
+    http_cookie_cache *cache = userdata;
+    int n;
+
+    /* Check sanity */
+    if (pairs[0] == NULL || pairs[1] == NULL) {
+       /* yagaboo */
+       return;
+    }
+
+    DEBUG(DEBUG_HTTP, "Got cookie name=%s\n", pairs[0]);
+
+    /* Search for a cookie of this name */
+    DEBUG(DEBUG_HTTP, "Searching for existing cookie...\n");
+    for (cook = cache->cookies; cook != NULL; cook = cook->next) {
+       if (strcasecmp(cook->name, pairs[0]) == 0) {
+           break;
+       }
+    }
+    
+    if (cook == NULL) {
+       DEBUG(DEBUG_HTTP, "New cookie.\n");
+       cook = xmalloc(sizeof(http_cookie));
+       memset(cook, 0, sizeof(http_cookie));
+       cook->name = pairs[0];
+       cook->next = cache->cookies;
+       cache->cookies = cook;
+    } else {
+       /* Free the old value */
+       free(cook->value);
+    }
+
+    cook->value = pairs[1];
+
+    for (n = 2; pairs[n] != NULL; n+=2) {
+       DEBUG(DEBUG_HTTP, "Cookie parm %s=%s\n", pairs[n], pairs[n+1]);
+       if (strcasecmp(pairs[n], "path") == 0) {
+           cook->path = pairs[n+1];
+           pairs[n+1] = NULL;
+       } else if (strcasecmp(pairs[n], "max-age") == 0) {
+           int t = atoi(pairs[n+1]);
+           cook->expiry = time(NULL) + (time_t)t;
+       } else if (strcasecmp(pairs[n], "domain") == 0) {
+           cook->domain = pairs[n+1];
+           pairs[n+1] = NULL;
+       }
+    }
+
+    DEBUG(DEBUG_HTTP, "End of parms.\n");
+
+    pair_string_free(pairs);
+}
+
+static void *create(void *session, http_req *req, const char *method, const char *uri)
+{
+    http_cookie_cache *cache = session;
+    http_add_response_header_handler(req, "Set-Cookie", set_cookie_hdl, cache);
+    return cache;
+}
+
+/* Just before sending the request: let them add headers if they want */
+static void pre_send(void *private, sbuffer request)
+{
+    http_cookie_cache *cache = private;
+    http_cookie *cook;
+    
+    if (cache->cookies == NULL) {
+       return;
+    }
+    
+    sbuffer_zappend(request, "Cookie: ");
+
+    for (cook = cache->cookies; cook != NULL; cook=cook->next) {
+       sbuffer_concat(request, cook->name, "=", cook->value, NULL);
+       if (cook->next != NULL) {
+           sbuffer_zappend(request, "; ");
+       }
+    }
+    
+    sbuffer_zappend(request, EOL);
+    
+}
+
+static void destroy(void *private)
+{
+    /* FIXME */
+    return;
+}
+
+http_request_hooks http_cookie_hooks = {
+    "http://www.webdav.org/neon/hooks/cookies",
+    create,
+    NULL,
+    pre_send,
+    NULL,
+    destroy
+};
diff --git a/neon/src/http_cookies.h b/neon/src/http_cookies.h
new file mode 100644 (file)
index 0000000..eb92746
--- /dev/null
@@ -0,0 +1,43 @@
+/* 
+   HTTP Request Handling
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef COOKIES_H
+#define COOKIES_H
+
+struct http_cookie_s;
+typedef struct http_cookie_s http_cookie;
+
+struct http_cookie_s {
+    char *name, *value;
+    unsigned int secure:1;
+    unsigned int discard:1;
+    char *domain, *path;
+    time_t expiry; /* time at which the cookie expires */
+    http_cookie *next;
+};
+
+typedef struct {
+    http_cookie *cookies;
+} http_cookie_cache;
+
+extern http_request_hooks http_cookie_hooks;
+
+#endif /* COOKIES_H */
diff --git a/neon/src/http_private.h b/neon/src/http_private.h
new file mode 100644 (file)
index 0000000..bea8e51
--- /dev/null
@@ -0,0 +1,176 @@
+/* 
+   HTTP Request Handling
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+/* THIS IS NOT A PUBLIC INTERFACE. You CANNOT include this header file
+ * from an application.  */
+#ifndef HTTP_PRIVATE_H
+#define HTTP_PRIVATE_H
+
+#include "http_auth.h"
+
+struct host_info {
+    const char *hostname;
+    int port;
+    struct in_addr addr;
+    char *hostport; /* URI hostport segment */
+    http_auth_session auth;
+    http_request_auth auth_callback;
+    void *auth_userdata;
+};
+
+typedef enum {
+    body_buffer,
+    body_stream,
+    body_none
+} request_body;
+
+/* This is called with each of the headers in the response */
+struct header_handler {
+    const char *name;
+    http_header_handler handler;
+    void *userdata;
+    struct header_handler *next;
+};
+
+/* TODO: could unify these all into a generic callback list */
+
+struct body_reader {
+    http_block_reader handler;
+    http_accept_response accept_response;
+    unsigned int use:1;
+    void *userdata;
+    struct body_reader *next;
+};
+
+/* Store hook information */
+struct hook {
+    http_request_hooks *hooks;
+    void *private;
+    struct hook *next;
+};
+
+/* Per-request store for hooks.
+ * This is a bit noddy really. */
+struct hook_request {
+    struct hook *hook;
+    void *cookie;
+    struct hook_request *next;
+};
+
+#define HAVE_HOOK(st,func) (st->hook->hooks->func != NULL)
+#define HOOK_FUNC(st, func) (*st->hook->hooks->func)
+
+/* Session support. */
+struct http_session_s {
+    /* Connection information */
+    nsocket *socket;
+
+    struct host_info server, proxy;
+
+    /* Connection states:
+     *   0:  Not connected at all.
+     *   1:  We have a TCP connection to the next-hop server.
+     *   2:  We have a negotiated an SSL connection over the proxy's 
+     *       TCP tunnel.
+     *
+     * Note, 1 is all we need if we don't have a proxy server, or
+     * if we do have a proxy server and we're not using SSL.
+     */
+    unsigned int connected:2;
+
+    /* Settings */
+    unsigned int have_proxy:1; /* do we have a proxy server? */
+    unsigned int no_persist:1; /* set to disable persistent connections */
+    unsigned int use_secure:1; /* whether a secure connection is required */
+    int expect100_works:2; /* known state of 100-continue support */
+    unsigned int in_connect:1; /* doing a proxy CONNECT */
+    unsigned int request_secure_upgrade:1; 
+    unsigned int accept_secure_upgrade:1;
+
+    http_use_proxy proxy_decider;
+    void *proxy_decider_udata;
+
+    nssl_context *ssl_context;
+
+    struct hook *hooks;
+
+    char *location; /* 302 redirect location of last request */
+
+    char *user_agent; /* full User-Agent string */
+
+    /* The last HTTP-Version returned by the server */
+    int version_major;
+    int version_minor;
+
+    /* Error string */
+    char error[BUFSIZ];
+};
+
+struct http_req_s {
+    const char *method;
+    char *uri, *abs_path;
+    
+    /*** Request ***/
+
+    sbuffer headers;
+    request_body body;
+    FILE *body_stream;
+    const char *body_buffer;
+    size_t body_size;
+
+    /**** Response ***/
+
+    /* The transfer encoding types */
+    struct http_response {
+       unsigned int is_chunked; /* Are we using chunked TE? */
+       int length;            /* Response entity-body content-length */
+       int left;              /* Bytes left to read */
+       long int chunk_left;   /* Bytes of chunk left to read */
+    } resp;
+
+    /* List of callbacks which are passed response headers */
+    struct header_handler *header_handlers;
+    /* List of callbacks which are passed response body blocks */
+    struct body_reader *body_readers;
+
+    /*** Miscellaneous ***/
+    unsigned int method_is_head:1;
+    unsigned int use_proxy:1;
+    unsigned int use_expect100:1;
+    unsigned int can_persist:1;
+    unsigned int forced_close:1;
+    unsigned int upgrade_to_tls:1;
+
+    http_session *session;
+    http_status *status; /* TODO: get rid of this */
+
+    /* stores request-private hook info */
+    struct hook_request *hook_store;
+
+#ifdef USE_DAV_LOCKS
+    /* TODO: move this to hooks... list of locks to submit */
+    struct dav_submit_locks *if_locks;
+#endif
+
+};
+
+#endif /* HTTP_PRIVATE_H */
diff --git a/neon/src/http_redirect.c b/neon/src/http_redirect.c
new file mode 100644 (file)
index 0000000..97745da
--- /dev/null
@@ -0,0 +1,147 @@
+/* 
+   HTTP-redirect support
+   Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: http_redirect.c,v 1.4 2000/08/12 15:34:40 joe Exp 
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "http_request.h"
+#include "ne_alloc.h"
+#include "http_private.h"
+#include "http_redirect.h"
+#include "uri.h"
+#include "neon_i18n.h"
+
+struct redirect {
+    char *location;
+    http_req *req;
+    http_redirect_confirm confirm;
+    http_redirect_notify notify;
+    void *userdata;
+};
+
+static void *create(void *session, http_req *req, 
+                   const char *method, const char *uri);
+static int post_send(void *private, http_status *status);
+static void destroy(void *private);
+
+http_request_hooks redirect_hooks = {
+    "http://www.webdav.org/neon/hooks/http-redirect",
+    create,
+    NULL,
+    NULL,
+    post_send,
+    destroy
+};
+
+static void *
+create(void *session, http_req *req, const char *method, const char *uri)
+{
+    struct redirect *red = session;
+    
+    /* for handling 3xx redirects */
+    http_add_response_header_handler(req, "Location",
+                                    http_duplicate_header, &red->location);
+
+    red->req = req;
+
+    return red;
+}
+
+/* 2616 says we can't auto-redirect if the method is not GET or HEAD.
+ * We extend this to PROPFIND too, which violates a 2616 MUST, but
+ * is following the spirit of the spec, I think. */
+static int auto_redirect(struct redirect *red)
+{
+    return (red->req->method_is_head ||
+           strcasecmp(red->req->method, "GET") == 0 || 
+           strcasecmp(red->req->method, "PROPFIND") == 0);
+}
+
+static int post_send(void *private, http_status *status)
+{
+    struct redirect *red = private;
+    struct uri uri;
+
+    if ((status->code != 302 && status->code != 301) ||
+       red->location == NULL) {
+       /* Nothing to do. */
+       return HTTP_OK;
+    }
+    
+    if (uri_parse(red->location, &uri, NULL)) {
+       /* Couldn't parse the URI */
+       http_set_error(red->req->session, 
+                      _("Could not parse redirect location."));
+       return HTTP_ERROR;
+    }
+
+    DEBUG(DEBUG_HTTP, "Redirect to hostname: %s, absPath: %s\n", 
+         uri.host, uri.path);
+
+    if (strcasecmp(uri.host, red->req->session->server.hostname) != 0) {
+       /* Don't allow redirects to a different server */
+       return HTTP_OK;
+    }
+
+    if (auto_redirect(red)) {
+       if (red->notify != NULL) {
+           (*red->notify)(red->userdata, red->req->abs_path, uri.path);
+       }
+    } else {
+       /* Need user-confirmation to follow the redirect */
+       if (red->confirm == NULL || 
+           !(*red->confirm)(red->userdata, red->req->abs_path, uri.path)) {
+           return HTTP_OK;
+       }
+    }
+    
+    /* FIXME FIXME: handle red->req->uri too */
+    red->req->abs_path = ne_strdup(uri.path);
+    return HTTP_RETRY;
+}
+
+static void destroy(void *private)
+{
+    struct redirect *red = private;
+    HTTP_FREE(red->location);
+    free(red);
+}
+
+void http_redirect_register(http_session *sess, 
+                           http_redirect_confirm confirm,
+                           http_redirect_notify notify,
+                           void *userdata)
+{
+    struct redirect *red = ne_calloc(sizeof *red);
+    
+    red->confirm = confirm;
+    red->notify = notify;
+    red->userdata = userdata;
+    
+    http_add_hooks(sess, &redirect_hooks, red);
+}
diff --git a/neon/src/http_redirect.h b/neon/src/http_redirect.h
new file mode 100644 (file)
index 0000000..688d275
--- /dev/null
@@ -0,0 +1,60 @@
+/* 
+   HTTP-redirect support
+   Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: http_redirect.h,v 1.4 2000/08/12 16:04:10 joe Exp 
+*/
+
+#ifndef HTTP_REDIRECT_H
+#define HTTP_REDIRECT_H
+
+#include <http_request.h>
+
+/* Get confirmation from the user that a redirect from
+ * URI 'src' to URI 'dest' is acceptable. Should return:
+ *   Non-Zero to FOLLOW the redirect
+ *   Zero to NOT follow the redirect
+ */
+typedef int (*http_redirect_confirm)(void *userdata,
+                                    const char *src, const char *dest);
+
+/* Notify the user that a redirect has been automatically 
+ * followed from URI 'src' to URI 'dest' */
+typedef void (*http_redirect_notify)(void *userdata,
+                                    const char *src, const char *dest);
+
+/* Register redirect handling for the given session.
+ * Some redirect responses will be automatically followed.
+ * If the redirect is automatically followed, the 'notify' callback
+ * is called.
+ * For redirects which are NOT automatically followed, the
+ * 'confirm' callback is called: if this returns zero, the redirect
+ * is ignored.
+ * 
+ * 'confirm' may be passed as NULL: in this case, only automatic
+ * redirects are followed.  'notify' may also be passed as NULL,
+ * automatic redirects are still followed.
+ *
+ * 'userdata' is passed as the first argument to the confirm and
+ * notify callbacks.  */
+void http_redirect_register(http_session *sess,
+                           http_redirect_confirm confirm,
+                           http_redirect_notify notify,
+                           void *userdata);
+
+#endif /* HTTP_REDIRECT_H */
diff --git a/neon/src/http_request.c b/neon/src/http_request.c
new file mode 100644 (file)
index 0000000..b14c6cd
--- /dev/null
@@ -0,0 +1,1306 @@
+/* 
+   HTTP request/response handling
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: http_request.c,v 1.24 2000/05/10 13:24:08 joe Exp 
+*/
+
+/* This is the HTTP client request/response implementation.
+ * The goal of this code is to be modular and simple.
+ */
+
+/* TODO:
+ *  - Implement request hooks
+ *  - Move DAV locks into a hook
+ *  - Move authentication into a hook
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __EMX__
+#include <sys/select.h>
+#endif
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif 
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifndef HAVE_SNPRINTF
+#include <snprintf.h>
+#endif
+
+#include <netinet/in.h>
+
+#include "neon_i18n.h"
+
+#include "xalloc.h"
+#include "http_request.h"
+#include "http_auth.h"
+#include "socket.h"
+#include "string_utils.h" /* for sbuffer */
+#include "http_utils.h"
+#include "uri.h"
+#include "neon.h" /* for NEON_VERSION */
+
+struct host_info {
+    const char *hostname;
+    int port;
+    struct in_addr addr;
+    char *hostport; /* URI hostport segment */
+    http_auth_session auth;
+    http_request_auth auth_callback;
+    void *auth_userdata;
+};
+
+typedef enum {
+    body_buffer,
+    body_stream,
+    body_none
+} request_body;
+
+/* This is called with each of the headers in the response */
+struct header_handler {
+    const char *name;
+    http_header_handler handler;
+    void *userdata;
+    struct header_handler *next;
+};
+
+/* TODO: could unify these all into a generic callback list */
+
+struct body_reader {
+    http_block_reader handler;
+    http_accept_response accept_response;
+    unsigned int use:1;
+    void *userdata;
+    struct body_reader *next;
+};
+
+/* Session support. */
+struct http_session_s {
+    /* Connection information */
+    int socket;
+
+    struct host_info server, proxy;
+
+    unsigned int connected:1;
+
+    /* Settings */
+    unsigned int have_proxy:1; /* do we have a proxy server? */
+    unsigned int no_persist:1; /* set to disable persistent connections */
+    int expect100_works:2; /* known state of 100-continue support */
+
+    char *user_agent; /* full User-Agent string */
+
+    /* The last HTTP-Version returned by the server */
+    int version_major;
+    int version_minor;
+
+    /* Error string */
+    char error[BUFSIZ];
+};
+
+struct hook {
+    http_request_hooks *handler;
+    void *private;
+    struct hook *next;
+};
+
+struct http_req_s {
+    /* Fill in these with http_req_init */
+    const char *method;
+    char *uri;
+    char *abs_path;
+    
+    /*** Request ***/
+
+    sbuffer headers;
+    /* Set these if you want to send a request body */
+    request_body body;
+    FILE *body_stream;
+    const char *body_buffer;
+    size_t body_size;
+
+    /**** Response ***/
+
+    /* The transfer encoding types */
+    struct http_response {
+       enum {
+           te_none,
+           te_chunked,
+           te_unknown
+       } te;
+       int length;            /* Response entity-body content-length */
+       int left;              /* Bytes left to read */
+       long int chunk_left;   /* Bytes of chunk left to read */
+    } resp;
+
+    struct header_handler *header_handlers;
+    struct body_reader *body_readers;
+
+    /*** Miscellaneous ***/
+    unsigned int method_is_head:1;
+    unsigned int use_proxy:1;
+    unsigned int use_expect100:1;
+    unsigned int can_persist:1;
+    unsigned int forced_close:1;
+
+    http_session *session;
+    http_status *status; /* TODO: get rid of this */
+
+#ifdef USE_DAV_LOCKS
+    /* TODO: move this to hooks... list of locks to submit */
+    struct dav_submit_locks *if_locks;
+#endif
+
+};
+#define HTTP_PORT 80
+
+#define HTTP_EXPECT_TIMEOUT 15
+/* 100-continue only used if size > HTTP_EXPECT_MINSIZ */
+#define HTTP_EXPECT_MINSIZE 1024
+
+#define HTTP_VERSION_PRE11(s) \
+((s)->version_major<1 || ((s)->version_major==1 && (s)->version_minor<1))
+
+#define HTTP_MAXIMUM_HEADER_LENGTH 8192
+
+#define NEON_USERAGENT "neon/" NEON_VERSION;
+
+static void te_hdr_handler( void *userdata, const char *value );
+static void connection_hdr_handler( void *userdata, const char *value );
+
+static void set_hostinfo( struct host_info *info, const char *hostname, int port );
+static int lookup_host( struct host_info *info );
+
+static int open_connection( http_req *req );
+static int close_connection( http_session *req );
+
+static int set_sockerr( http_req *req, const char *doing, int sockerr );
+
+static char *get_hostport( struct host_info *host);
+static void add_fixed_headers( http_req *req );
+static int get_request_bodysize( http_req *req );
+
+static int send_request_body( http_req *req );
+static void build_request( http_req *req, sbuffer buf );
+static int read_message_header( http_req *req, sbuffer buf );
+static int read_response_block( http_req *req, struct http_response *resp,
+                               char *buffer, size_t *buflen );
+static int read_response_body( http_req *req, http_status *status );
+
+/* Initializes an HTTP session */
+http_session *http_session_init( void ) 
+{
+    http_session *sess = xmalloc(sizeof(http_session));
+    DEBUG( DEBUG_HTTP, "HTTP session begins.\n" );
+    strcpy( sess->error, "Unknown error." );
+    memset( sess, 0, sizeof(struct http_session_s) );
+    sess->version_major = -1;
+    sess->version_minor = -1;
+    return sess;
+}
+
+static void
+set_hostinfo( struct host_info *info, const char *hostname, int port )
+{
+    HTTP_FREE( info->hostport );
+    info->hostname = hostname;
+    info->port = port;
+    info->hostport = get_hostport( info );
+    http_auth_init( &info->auth );
+}
+
+static int lookup_host( struct host_info *info )
+{
+    if( host_lookup( info->hostname, &info->addr ) ) {
+       return HTTP_LOOKUP;
+    } else {
+       return HTTP_OK;
+    }
+}
+
+int http_version_pre_http11( http_session *sess )
+{
+    return HTTP_VERSION_PRE11(sess);
+}
+
+int http_session_server( http_session *sess, const char *hostname, int port )
+{
+    set_hostinfo( &sess->server, hostname, port );
+    if( !sess->have_proxy ) {
+       return lookup_host( &sess->server );
+    } else {
+       return HTTP_OK;
+    }
+}
+
+int http_session_proxy( http_session *sess, const char *hostname, int port )
+{
+    sess->have_proxy = 1;
+    set_hostinfo( &sess->proxy, hostname, port );
+    return lookup_host( &sess->proxy );
+}
+
+void http_set_server_auth( http_session *sess, 
+                          http_request_auth callback, void *userdata )
+{
+    sess->server.auth_callback = callback;
+    sess->server.auth_userdata = userdata;
+}
+
+/* Set callback to handle proxy authentication */
+void http_set_proxy_auth( http_session *sess, 
+                         http_request_auth callback, void *userdata )
+{
+    sess->proxy.auth_callback = callback;
+    sess->proxy.auth_userdata = userdata;
+}
+
+void http_set_error( http_session *sess, const char *errstring )
+{
+    strncpy( sess->error, errstring, BUFSIZ );
+    sess->error[BUFSIZ-1] = '\0';
+    STRIP_EOL( sess->error );
+}
+
+const char *http_get_error( http_session *sess ) {
+    return sess->error;
+}
+
+/* Give authentication credentials */
+static int give_creds( void *udata, const char *realm,
+                      char **username, char **password ) 
+{ 
+    http_req *req = udata;
+    http_session *sess = req->session;
+    if( req->status->code == 407 && req->use_proxy && sess->proxy.auth_callback ) {
+       return (*sess->proxy.auth_callback)( 
+           sess->proxy.auth_userdata, realm, sess->proxy.hostname,
+           username, password );
+    } else if( req->status->code == 401 && sess->server.auth_callback ) {
+       return (*sess->server.auth_callback)( 
+           sess->server.auth_userdata, realm, sess->server.hostname,
+           username, password );
+    }
+    return -1;
+}
+
+void http_duplicate_header( void *userdata, const char *value )
+{
+    char **location = userdata;
+    *location = xstrdup( value );
+}
+
+void http_handle_numeric_header( void *userdata, const char *value )
+{
+    int *location = userdata;
+    *location = atoi( value );
+}
+
+/* The body reader callback */
+static void auth_body_reader( void *userdata, const char *block, size_t length )
+{
+    http_auth_session *sess = userdata;
+    http_auth_response_body( sess, block, length );
+}
+
+int http_session_finish( http_session *sess ) {
+    DEBUG( DEBUG_HTTP, "http_session_finish called.\n" );
+    http_auth_finish( &sess->server.auth );
+    if( sess->have_proxy ) {
+       http_auth_finish( &sess->proxy.auth );
+    }
+    HTTP_FREE( sess->server.hostport );
+    HTTP_FREE( sess->proxy.hostport );
+    HTTP_FREE( sess->user_agent );
+    if( sess->connected ) close_connection(sess);
+    free( sess );
+    return HTTP_OK;
+}
+
+/* Sends the body down the socket.
+ * Returns HTTP_* code */
+int send_request_body( http_req *req ) {
+    int ret;
+    switch( req->body ) {
+    case body_stream:
+       ret = sock_transfer( fileno(req->body_stream), req->session->socket, 
+                       req->body_size );
+       DEBUG( DEBUG_HTTP, "Sent %d bytes.\n", ret );
+       rewind( req->body_stream ); /* since we may have to send it again */
+       break;
+    case body_buffer:
+       DEBUG( DEBUG_HTTP, "Sending body:\n%s\n", req->body_buffer );
+       ret = sock_send_string( req->session->socket, req->body_buffer );
+       DEBUG( DEBUG_HTTP, "sock_send_string returns: %d\n", ret );
+       break;
+    default:
+       ret = 0;
+       break;
+    }
+    if( ret < 0 ) { 
+       /* transfer failed */
+       req->forced_close = 1;
+       return set_sockerr( req, _("Could not send request body"), ret );
+    } else {
+       return HTTP_OK;
+    }
+}
+
+/* Deal with the body size.
+ * Returns 0 on success or non-zero on error. */
+static int get_request_bodysize( http_req *req ) 
+{
+    struct stat bodyst;
+    /* Do extra stuff if we have a body */
+    switch( req->body ) {
+    case body_stream:
+       /* Get file length */
+       if( fstat( fileno(req->body_stream), &bodyst ) < 0 ) {
+           /* Stat failed */
+           DEBUG( DEBUG_HTTP, "Stat failed: %s\n", strerror( errno ) );
+           return -1;
+       }
+       req->body_size = bodyst.st_size;
+       break;
+    case body_buffer:
+       req->body_size = strlen( req->body_buffer );
+       break;
+    default:
+       /* No body, so no size. */
+       break;
+    }
+    if( req->body != body_none) {
+       char tmp[BUFSIZ];
+       /* Add the body length header */
+       snprintf( tmp, BUFSIZ, "Content-Length: %d" EOL, req->body_size );
+       sbuffer_zappend( req->headers, tmp );
+    } else {
+       sbuffer_zappend( req->headers, "Content-Length: 0" EOL );
+    }
+    return 0;
+}
+
+static char *get_hostport( struct host_info *host) 
+{
+    size_t len = strlen( host->hostname );
+    char *ret = xmalloc( len + 10 );
+    strcpy( ret, host->hostname );
+    if( host->port != HTTP_PORT ) {
+       snprintf( ret + len, 9, ":%d", host->port );
+    }
+    return ret;
+}
+
+const char *http_get_server_hostport( http_session *sess ) {
+    return sess->server.hostport;
+}
+
+
+/* Lob the User-Agent, connection and host headers in to the request
+ * headers */
+static void add_fixed_headers( http_req *req ) {
+    if( req->session->user_agent ) {
+       sbuffer_concat( req->headers, 
+                       "User-Agent: ", req->session->user_agent, EOL, NULL );
+    }  
+    /* Send Connection: Keep-Alive for pre-1.1 origin servers, so we
+     * might get a persistent connection. 2068 sec 19.7.1 says we 
+     * MUST NOT do this for proxies, though. So we don't. */
+    if( HTTP_VERSION_PRE11(req->session) && !req->use_proxy ) {
+       sbuffer_zappend( req->headers, "Connection: Keep-Alive, TE" EOL );
+       sbuffer_zappend( req->headers, "Keep-Alive: " EOL );
+    } else {
+       sbuffer_zappend( req->headers, "Connection: TE" EOL );
+    }
+    /* We send TE: trailers since we understand trailers in the chunked
+     * response. */
+    sbuffer_concat( req->headers, "TE: trailers" EOL 
+                   "Host: ", req->session->server.hostport, EOL, NULL );
+}
+
+static int always_accept_response( void *userdata, http_req *req, http_status *st )
+{
+    return 1;
+}                                 
+
+int http_accept_2xx( void *userdata, http_req *req, http_status *st )
+{
+    return (st->class == 2);
+}
+
+/* Initializes the request with given method and URI.
+ * URI must be abs_path - i.e., NO scheme+hostname. It will BREAK 
+ * otherwise. */
+http_req *http_request_create( http_session *sess,
+                              const char *method, const char *uri ) {
+    sbuffer real_uri;
+    http_req *req = xmalloc( sizeof(http_req) );
+
+    /* Clear it out */
+    memset( req, 0, sizeof( http_req ) );
+
+    DEBUG( DEBUG_HTTP, "Creating request...\n" );
+
+    req->session = sess;
+    req->headers = sbuffer_create();
+    
+    /* Add in the fixed headers */
+    add_fixed_headers( req );
+
+    /* Set the standard stuff */
+    req->method = method;
+    req->method_is_head = (strcmp( req->method, "HEAD" ) == 0);
+    req->body = body_none;
+    
+
+    /* TODO: Can do clever "should we use the proxy" discrimination
+     * here, e.g. by server hostname used.  TODO: when we do that,
+     * then we might need to do a name lookup on the server here,
+     * since we omit that if we are using a proxy, normally. */
+    req->use_proxy = sess->have_proxy;
+
+    /* Add in standard callbacks */
+
+    http_auth_set_creds_cb( &sess->server.auth, give_creds, req );
+    http_add_response_body_reader( req, always_accept_response, 
+                                  auth_body_reader, &req->session->server.auth );
+    if( req->use_proxy ) {
+       http_auth_set_creds_cb( &sess->proxy.auth, give_creds, req );
+       http_add_response_body_reader( req, always_accept_response, 
+                                      auth_body_reader, &req->session->proxy.auth );
+    }
+    
+    /* Add in handlers for all the standard HTTP headers. */
+
+    http_add_response_header_handler( req, "Content-Length", 
+                                     http_handle_numeric_header, &req->resp.length );
+    http_add_response_header_handler( req, "Transfer-Encoding", 
+                                     te_hdr_handler, &req->resp );
+    http_add_response_header_handler( req, "Connection", 
+                                     connection_hdr_handler, req );
+
+    req->abs_path = uri_abspath_escape( uri );
+
+    real_uri = sbuffer_create();
+    if( req->use_proxy )
+       sbuffer_concat( real_uri, "http://", 
+                       req->session->server.hostport, NULL );
+    sbuffer_zappend( real_uri, req->abs_path );
+    req->uri = sbuffer_finish(real_uri);
+
+    DEBUG( DEBUG_HTTP, "Request created.\n" );
+
+    return req;
+}
+
+void http_set_request_body_buffer( http_req *req, const char *buffer )
+{
+    req->body = body_buffer;
+    req->body_buffer = buffer;
+    req->body_stream = NULL;
+}
+
+void http_set_request_body_stream( http_req *req, FILE *stream )
+{
+    req->body = body_stream;
+    req->body_stream = stream;
+    req->body_buffer = NULL;
+}
+
+void http_set_expect100( http_session *sess, int use_expect100 )
+{
+    if( use_expect100 ) {
+       sess->expect100_works = 1;
+    } else {
+       sess->expect100_works = -1;
+    }
+}
+
+void http_set_persist( http_session *sess, int persist )
+{
+    sess->no_persist = !persist;
+}
+
+void http_set_useragent( http_session *sess, const char *token )
+{
+    static const char *fixed = " " NEON_USERAGENT;
+    HTTP_FREE( sess->user_agent );
+    CONCAT2( sess->user_agent, token, fixed );
+}
+
+sbuffer http_get_request_header( http_req *req )
+{
+    return req->headers;
+}
+
+void
+http_add_response_header_handler( http_req *req, const char *name, 
+                                 http_header_handler hdl, void *userdata )
+{
+    struct header_handler *new = xmalloc( sizeof(struct header_handler) );
+    new->name = name;
+    new->handler = hdl;
+    new->userdata = userdata;
+    new->next = req->header_handlers;
+    req->header_handlers = new;
+}
+
+void
+http_add_response_body_reader( http_req *req, http_accept_response acpt,
+                              http_block_reader rdr, void *userdata )
+{
+    struct body_reader *new = xmalloc( sizeof(struct body_reader) );
+    new->accept_response = acpt;
+    new->handler = rdr;
+    new->userdata = userdata;
+    new->next = req->body_readers;
+    req->body_readers = new;
+}
+
+void http_request_destroy( http_req *req ) 
+{
+
+    HTTP_FREE( req->uri );
+    HTTP_FREE( req->abs_path );
+
+    sbuffer_destroy( req->headers );
+
+    DEBUG( DEBUG_HTTP, "Request ends.\n" );
+    free( req );
+}
+
+
+/* Reads a block of the response into buffer, which is of size buflen.
+ * Returns number of bytes read, 0 on end-of-response, or HTTP_* on error.
+ * TODO?: only make one actual read() call in here... 
+ */
+static int read_response_block( http_req *req, struct http_response *resp, 
+                               char *buffer, size_t *buflen ) 
+{
+    int willread, readlen, socket = req->session->socket;
+    if( resp->te==te_chunked ) {
+       /* We are doing a chunked transfer-encoding.
+        * It goes:  `SIZE CRLF CHUNK CRLF SIZE CRLF CHUNK CRLF ...'
+        * ended by a `CHUNK CRLF 0 CRLF', a 0-sized chunk.
+        * The slight complication is that we have to cope with
+        * partial reads of chunks.
+        * For this reason, resp.chunk_left contains the number of
+        * bytes left to read in the current chunk.
+        */
+       if( resp->chunk_left == 0 ) {
+           long int chunk_len;
+           /* We are at the start of a new chunk. */
+           DEBUG( DEBUG_HTTP, "New chunk.\n" );
+           readlen = sock_readline( socket, buffer, *buflen );
+           if( readlen <= 0 ) {
+               return set_sockerr( req, _("Could not read chunk size"), readlen );
+           }
+           DEBUG( DEBUG_HTTP, "[Chunk Size] < %s", buffer );
+           chunk_len = strtol( buffer, NULL, 16 );
+           if( chunk_len == LONG_MIN || chunk_len == LONG_MAX ) {
+               DEBUG( DEBUG_HTTP, "Couldn't read chunk size.\n" );
+               return -1;
+           }
+           DEBUG( DEBUG_HTTP, "Got chunk size: %ld\n", chunk_len );
+           if( chunk_len == 0 ) {
+               /* Zero-size chunk */
+               DEBUG( DEBUG_HTTP, "Zero-size chunk.\n" );
+               *buflen = 0;
+               return HTTP_OK;
+           }
+           resp->chunk_left = chunk_len;
+       }
+       willread = min( *buflen - 1, resp->chunk_left );
+    } else if( resp->length > 0 ) {
+       /* Have we finished reading the body? */
+       if( resp->left == 0 ) {
+           *buflen = 0;
+           return HTTP_OK;
+       }
+       willread = min( *buflen - 1, resp->left );
+    } else {
+       /* Read until socket-close */
+       willread = *buflen - 1;
+    }
+    DEBUG( DEBUG_HTTP, "Reading %d bytes of response body.\n", willread );
+    readlen = sock_read( socket, buffer, willread );
+    DEBUG( DEBUG_HTTP, "Got %d bytes.\n", readlen );
+    if( readlen < 0 ||
+       (readlen == 0 && (resp->length > 0 || resp->te==te_chunked)) ) {
+       return set_sockerr( req, _("Could not read response body"), readlen );
+    }
+    buffer[readlen] = '\0';
+    *buflen = readlen;
+    DEBUG( DEBUG_HTTPBODY, "Read block:\n%s\n", buffer );
+    if( resp->te==te_chunked ) {
+       resp->chunk_left -= readlen;
+       if( resp->chunk_left == 0 ) {
+           char crlfbuf[2];
+           /* If we've read a whole chunk, read a CRLF */
+           readlen = sock_fullread( socket, crlfbuf, 2 );
+           if( readlen < 0 || strncmp( crlfbuf, EOL, 2 ) != 0 ) {
+               return set_sockerr( req, _("Error reading chunked response body"),
+                                   readlen );
+           }
+       }
+    } else if( resp->length > 0 ) {
+       resp->left -= readlen;
+    }
+    return HTTP_OK;
+}
+
+/* Build a request string into the buffer.
+ * If we sent the data as we generated it, it's possible that multiple
+ * packets could go out on the wire, which is less efficient. */
+static void build_request( http_req *req, sbuffer buf ) 
+{
+    const char *uri;
+    char *tmp;
+
+    /* If we are talking to a proxy, we send them the absoluteURI
+     * as the Request-URI. If we are talking to a server, we just 
+     * send abs_path. */
+    if( req->use_proxy )
+       uri = req->uri;
+    else
+       uri = req->abs_path;
+    
+    sbuffer_clear( buf );
+
+    /* Add in the request and the user-supplied headers */
+    sbuffer_concat( buf, req->method, " ", uri, " HTTP/1.1" EOL,
+                   sbuffer_data(req->headers), NULL );
+    
+    /* Add the authorization headers in */
+    tmp = http_auth_request_header( &req->session->server.auth );
+    if( tmp != NULL ) {
+       sbuffer_concat( buf, "Authorization: ", tmp, NULL );
+       free( tmp );
+    }
+
+    if( req->use_proxy ) {
+       tmp = http_auth_request_header( &req->session->proxy.auth );
+       if( tmp != NULL ) {
+           sbuffer_concat( buf, "Proxy-Authorization: ", tmp, NULL );
+           free( tmp );
+       }
+    }
+    
+    /* Now handle the body. */
+    req->use_expect100 = 0;
+    if( req->body!=body_none && 
+       (req->session->expect100_works > -1) &&
+       (req->body_size > HTTP_EXPECT_MINSIZE) && 
+       !HTTP_VERSION_PRE11(req->session) ) {
+       /* Add Expect: 100-continue. */
+       sbuffer_zappend( buf, "Expect: 100-continue" EOL );
+       req->use_expect100 = 1;
+    }
+
+}
+
+static int set_sockerr( http_req *req, const char *doing, int code )
+{
+    switch( code ) {
+    case 0:
+       if( req->use_proxy ) {
+           snprintf( req->session->error, BUFSIZ,
+                     _("%s: connection was closed by proxy server."), doing );
+       } else {
+           snprintf( req->session->error, BUFSIZ,
+                     _("%s: connection was closed by server."), doing );
+       }
+       return HTTP_ERROR;
+    case SOCK_TIMEOUT:
+       snprintf( req->session->error, BUFSIZ, 
+                 _("%s: connection timed out."), doing );
+       return HTTP_TIMEOUT;
+    default:
+       snprintf( req->session->error, BUFSIZ,
+                 "%s: %s", doing, strerror(errno) );
+       return HTTP_ERROR;
+    }
+}
+
+/* TODO: The retry loop may be overkill. It may only be necessary to
+ * try sending the request >1 time; making the loop shorter. */
+static int send_request( http_req *req, const char *request, 
+                        sbuffer buf, http_status *status )
+{
+    http_session *sess = req->session;
+    int ret, try_again;
+    
+    do {
+
+       try_again = 0;
+
+       /* Open the connection if necessary */
+       if( !sess->connected ) {
+           ret = open_connection( req );
+           if( ret != HTTP_OK ) {
+               return ret;
+           }
+       }
+
+#ifdef DEBUGGING
+       if( (DEBUG_HTTPPLAIN&debug_mask) == DEBUG_HTTPPLAIN ) { 
+           /* Display everything mode */
+           DEBUG( DEBUG_HTTP, "Sending request headers:\n%s", request );
+       } else {
+           /* Blank out the Authorization paramaters */
+           char *reqdebug = xstrdup(request), *pnt = reqdebug;
+           while( (pnt = strstr( pnt, "Authorization: ")) != NULL ) {
+               for( pnt += 15; *pnt != '\r' && *pnt != '\0'; pnt++ ) {
+                   *pnt = 'x';
+               }
+           }
+           DEBUG( DEBUG_HTTP, "Sending request headers:\n%s", reqdebug );
+           free( reqdebug );
+       }
+#endif /* DEBUGGING */
+       
+       DEBUG( DEBUG_HTTP, "Request size: %d\n", strlen(request) );
+       /* Send the Request-Line and headers */
+       ret = sock_send_string( req->session->socket, request );
+       if( ret < 0 ) {
+           return set_sockerr( req, _("Could not send request"), ret );
+       }
+       
+       DEBUG( DEBUG_HTTP, "Request sent\n" );
+       
+       /* Now, if we are doing a Expect: 100, hang around for a short
+        * amount of time, to see if the server actually cares about the 
+        * Expect and sends us a 100 Continue response if the request
+        * is valid, else an error code if it's not. This saves sending
+        * big files to the server when they will be rejected.
+        */
+       
+       if( req->use_expect100 ) {
+           DEBUG( DEBUG_HTTP, "Waiting for response...\n" );
+           ret = sock_block( sess->socket, HTTP_EXPECT_TIMEOUT );
+           switch( ret ) {
+           case SOCK_TIMEOUT: 
+               /* Timed out - i.e. Expect: ignored. There is a danger
+                * here that the server DOES respect the Expect: header,
+                * but was going SO slowly that it didn't get time to
+                * respond within HTTP_EXPECT_TIMEOUT.
+                * TODO: while sending the body, check to see if the
+                * server has sent anything back - if it HAS, then
+                * stop sending - this is a spec compliance SHOULD */
+               DEBUG( DEBUG_HTTP, "Wait timed out.\n" );
+               sess->expect100_works = -1; /* don't try that again */
+               /* Try sending the request again without using 100-continue */
+               try_again = 1;
+               break;
+           case SOCK_ERROR: /* error */
+               return set_sockerr( req, _("Error waiting for response"), ret );
+           default:
+               DEBUG( DEBUG_HTTP, "Wait got data.\n" );
+               sess->expect100_works = 1; /* it works - use it again */
+               break;
+           }
+       } else if( req->body != body_none ) {
+           /* Just chuck the file down the socket */
+           DEBUG( DEBUG_HTTP, "Sending body...\n" );
+           ret = send_request_body( req );
+           if( ret != HTTP_OK ) return ret;
+           DEBUG( DEBUG_HTTP, "Body sent.\n" );
+       }
+       
+       /* Now, we have either:
+        *   - Sent the header and body, or
+        *   - Sent the header incl. Expect: line, and got some response.
+        * In any case, we get the status line of the response.
+        */
+       
+       /* HTTP/1.1 says that the server MAY emit any number of
+        * interim 100 (Continue) responses prior to the normal
+        * response.  So loop while we get them.  */
+       
+       do {
+           if( sock_readline( sess->socket, sbuffer_data(buf), BUFSIZ ) <= 0 ) {
+               if( try_again ) {
+                   return set_sockerr( req, _("Could not read status line"), ret );
+               }
+               try_again = 1;
+               break;
+           }
+
+           DEBUG( DEBUG_HTTP, "[Status Line] < %s", sbuffer_data(buf) );
+           
+           /* Got the status line - parse it */
+           if( http_parse_statusline( sbuffer_data(buf), status ) ) {
+               http_set_error( sess, "Could not parse response status line." );
+               return -1;
+           }
+
+           sess->version_major = status->major_version;
+           sess->version_minor = status->minor_version;
+           snprintf( sess->error, BUFSIZ, "%d %s", 
+                     status->code, status->reason_phrase );
+           STRIP_EOL( sess->error );
+
+           if( status->class == 1 ) {
+               DEBUG( DEBUG_HTTP, "Got 1xx-class.\n" );
+               /* Skip any headers, we don't need them */
+               do {
+                   ret = sock_readline( sess->socket, sbuffer_data(buf), BUFSIZ );
+                   if( ret <= 0 ) {
+                       return set_sockerr( 
+                           req, _("Error reading response headers"), ret );
+                   }
+                   DEBUG( DEBUG_HTTP, "[Ignored header] < %s", 
+                          sbuffer_data(buf) );
+               } while( strcmp( sbuffer_data(buf), EOL ) != 0 );
+       
+               if( req->use_expect100 && (status->code == 100) ) {
+                   /* We are using Expect: 100, and we got a 100-continue 
+                    * return code... send the request body */
+                   DEBUG( DEBUG_HTTP, "Got continue... sending body now.\n" );
+                   if( send_request_body( req ) != HTTP_OK )
+                       return HTTP_ERROR;
+                       
+                   DEBUG( DEBUG_HTTP, "Body sent.\n" );
+               }
+           }
+       } while( status->class == 1 );
+
+       if( try_again ) {
+           /* If we're trying again, close the conn first */
+           DEBUG( DEBUG_HTTP, "Retrying request, closing connection first.\n" );
+           close_connection( sess );
+       }
+
+    } while( try_again );
+
+    return HTTP_OK;
+}
+
+/* Read a message header from sock into buf.
+ * Returns an HTTP_* code.
+ */
+static int read_message_header( http_req *req, sbuffer buf )
+{
+    char extra[BUFSIZ], *pnt;
+    int ret, sock = req->session->socket;
+    
+    sbuffer_clear(buf);
+
+    ret = sock_readline( sock, sbuffer_data(buf), BUFSIZ );
+    if( ret <= 0 )
+       return set_sockerr( req, _("Error reading response headers"), ret );
+    DEBUG( DEBUG_HTTP, "[Header:%d] < %s", 
+          strlen(sbuffer_data(buf)), sbuffer_data(buf) );
+    if( strcmp( sbuffer_data(buf), EOL ) == 0 ) {
+       DEBUG( DEBUG_HTTP, "CRLF: End of headers.\n" );
+       return HTTP_OK;
+    }
+    while(sbuffer_size(buf) < HTTP_MAXIMUM_HEADER_LENGTH ) {
+       /* Collect any extra lines into buffer */
+       ret = sock_recv( sock, extra, 1, MSG_PEEK);
+       if( ret <= 0 ) {
+           return set_sockerr( req, _("Error reading response headers"), ret );
+       }
+       if( extra[0] != ' ' && extra[0] != '\t' ) {
+           /* No more headers */
+           return HTTP_OK;
+       }
+       ret = sock_readline( sock, extra, BUFSIZ );
+       if( ret <= 0 ) {
+           return set_sockerr( req, _("Error reading response headers"), ret);
+       }
+       DEBUG( DEBUG_HTTP, "[Cont:%d] < %s", strlen(extra), extra);
+       /* Append a space to the end of the last header, in
+        * place of the CRLF. */
+       pnt = strchr( sbuffer_data(buf), '\r' );
+       pnt[0] = ' '; pnt[1] = '\0';
+       /* Skip leading whitespace off next line */
+       for( pnt = extra; *pnt!='\0' && 
+                ( *pnt == ' ' || *pnt =='\t' ); pnt++ ) /*oneliner*/;
+       DEBUG( DEBUG_HTTP, "[Continued] < %s", pnt );
+       sbuffer_altered( buf );
+       sbuffer_zappend( buf, pnt );
+    }
+    return HTTP_ERROR;
+}
+
+static void normalize_response_length( http_req *req, http_status *status )
+{
+    /* Response entity-body length calculation, bit icky.
+     * Here, we set:
+     * length==-1 if we DO NOT know the exact body length
+     * length>=0 if we DO know the body length.
+     *
+     * RFC2616, section 4.4: 
+     * NO body is returned if the method is HEAD, or the resp status
+     * is 204 or 304
+     */
+    if( req->method_is_head || status->code==204 || status->code==304 ) {
+       req->resp.length = 0;
+    } else {
+       /* RFC2616, section 4.4: if we have a transfer encoding
+        * and a content-length, then ignore the content-length. */
+       if( (req->resp.length>-1) && 
+           (req->resp.te!=te_unknown) ) {
+           req->resp.length = -1;
+       }
+    }
+}
+
+/* Read response headers, using buffer buffer.
+ * Returns HTTP_* code. */
+static int read_response_headers( http_req *req, sbuffer buf ) 
+{
+    http_session *sess = req->session;
+    struct header_handler *hdl;
+    char *name, *value, *pnt, *hdr;
+    int ret;
+    
+    /* Read response headers */
+    while(1) {
+       ret = read_message_header( req, buf );
+       if( ret != HTTP_OK ) return ret;
+       
+       hdr = sbuffer_data(buf);
+
+       if( strcmp( hdr, EOL ) == 0 ) {
+           return HTTP_OK;
+       }
+
+       /* Now parse the header line. This is all a bit noddy. */
+       pnt = strchr( hdr, ':' );
+       if( pnt == NULL ) {
+           http_set_error( sess, "Malformed header line." );       
+           return HTTP_ERROR;
+       }
+       
+       /* Null-term name at the : */
+       *pnt = '\0';
+       name = hdr;
+       /* Strip leading whitespace from the value */
+       for( value = pnt+1; *value!='\0' && *value==' '; value++ )
+           /* nullop */;
+       STRIP_EOL( value );
+       DEBUG( DEBUG_HTTP, "Header Name: [%s], Value: [%s]\n",
+              name, value );
+       /* Iterate through the header handlers */
+       for( hdl = req->header_handlers; hdl != NULL; hdl = hdl->next ) {
+           if( strcasecmp( name, hdl->name ) == 0 ) {
+               (*hdl->handler)( hdl->userdata, value );
+           }
+       }
+    }
+
+    return HTTP_ERROR;
+}
+
+/* Read the response message body */
+static int read_response_body( http_req *req, http_status *status )
+{
+    char buffer[BUFSIZ];
+    int readlen, ret = HTTP_OK;
+    struct body_reader *rdr;
+           
+    /* If there is nothing to do... */
+    if( req->resp.length == 0 ) {
+       /* Do nothing */
+       return HTTP_OK;
+    }
+    
+    /* First off, tell all of the response body handlers that they are
+     * going to get a body, and let them work out whether they want to 
+     * handle it or not */
+    for( rdr = req->body_readers; rdr != NULL; rdr=rdr->next ) {
+       rdr->use = (*rdr->accept_response)( rdr->userdata, req, status );
+    }    
+    
+    req->resp.left = req->resp.length;
+    req->resp.chunk_left = 0;
+
+    /* Now actually read the thing */
+    
+    do {
+       /* Read a block */
+       readlen = BUFSIZ;
+       ret = read_response_block( req, &req->resp, buffer, &readlen );
+
+       /* TODO: Do we need to call them if readlen==0, or if
+        * readlen == -1, to tell them something has gone wrong? */
+          
+       if( ret == HTTP_OK ) {
+           for( rdr = req->body_readers; rdr!=NULL; rdr=rdr->next ) {
+               if( rdr->use )
+                   (*rdr->handler)( rdr->userdata, buffer, readlen );
+           }
+       }
+       
+    } while( ret == HTTP_OK && readlen > 0 );
+
+    if( ret != HTTP_OK )
+       req->forced_close = 1;
+
+    return ret;
+}
+
+/* Handler for the "Transfer-Encoding" response header */
+static void te_hdr_handler( void *userdata, const char *value ) 
+{
+    struct http_response *resp = userdata;
+    if( strcasecmp( value, "chunked" ) == 0 ) {
+       resp->te = te_chunked;
+    } else {
+       resp->te = te_unknown;
+    }
+}
+
+/* Handler for the "Connection" response header */
+static void connection_hdr_handler( void *userdata, const char *value )
+{
+    http_req *req = userdata;
+    if( strcasecmp( value, "close" ) == 0 ) {
+       req->forced_close = 1;
+    } else if( strcasecmp( value, "Keep-Alive" ) == 0 ) {
+       req->can_persist = 1;
+    }
+}
+
+
+/* HTTP/1.x request/response mechanism 
+ *
+ * Returns an HTTP_* return code. 
+ *   
+ * The status information is placed in status. The error string is
+ * placed in req->session->error
+ *
+ */
+int http_request_dispatch( http_req *req, http_status *status ) 
+{
+    http_session *sess = req->session;
+    sbuffer buf, request;
+    int ret, attempt, proxy_attempt, con_attempt, can_retry;
+    /* Response header storage */
+    char *www_auth, *proxy_auth, *authinfo, *proxy_authinfo;
+
+    /* Initialization... */
+    DEBUG( DEBUG_HTTP, "Request started...\n" );
+    http_set_error( sess, "Unknown error." );
+    ret = HTTP_OK;
+
+    if( get_request_bodysize( req ) )
+       return HTTP_ERROR;
+
+    buf = sbuffer_create_sized( BUFSIZ );
+
+    http_add_response_header_handler( req, "WWW-Authenticate",
+                                     http_duplicate_header, &www_auth );
+    http_add_response_header_handler( req, "Proxy-Authenticate",
+                                     http_duplicate_header, &proxy_auth );
+    http_add_response_header_handler( req, "Authentication-Info",
+                                     http_duplicate_header, &authinfo );
+    http_add_response_header_handler( req, "Proxy-Authentication-Info",
+                                     http_duplicate_header, &proxy_authinfo );
+    req->status = status;
+
+    request = sbuffer_create();
+    proxy_attempt = con_attempt = attempt = 1;
+    www_auth = proxy_auth = authinfo = proxy_authinfo = NULL;
+    
+    /* Loop sending the request:
+     * Retry whilst authentication fails and we supply it. */
+    
+    do {
+       
+       can_retry = 0;
+       req->can_persist = 0;
+       req->forced_close = 0;
+
+       /* Note that we pass the abs_path here... */
+       http_auth_new_request( &sess->server.auth, req->method, req->uri,
+                              req->body_buffer, req->body_stream );
+       if( req->use_proxy ) {
+           /* ...and absoluteURI here. */
+           http_auth_new_request( &sess->proxy.auth, req->method, req->uri,
+                                  req->body_buffer, req->body_stream );
+       }
+
+       build_request( req, request );
+
+#ifdef USE_DAV_LOCKS
+       /* TODO: Move this into a hook structure */
+       {
+           char *tmp = dav_lock_ifheader( req );
+           if( tmp != NULL ) {
+               sbuffer_zappend( request, tmp );
+               free( tmp );
+               if( HTTP_VERSION_PRE11(sess) ) {
+                   /* HTTP/1.0 */
+                   sbuffer_zappend( request, "Pragma: no-cache" EOL );
+               } else {
+                   /* HTTP/1.1 and above */
+                   sbuffer_zappend( request, "Cache-Control: no-cache" EOL );
+               }
+           }
+       }
+#endif /* USE_DAV_LOCKS */
+
+       /* Final CRLF */
+       sbuffer_zappend( request, EOL );
+       
+       /* Now send the request, and read the Status-Line */
+       ret = send_request( req, sbuffer_data(request), buf, status );
+       if( ret != HTTP_OK ) goto dispatch_error;
+
+       req->resp.length = -1;
+       req->resp.te = te_unknown;
+
+       /* Read the headers */
+       if( read_response_headers( req, buf ) != HTTP_OK ) {
+           ret = HTTP_ERROR;
+           goto dispatch_error;
+       }
+
+       normalize_response_length( req, status );
+
+       ret = read_response_body( req, status );
+       if( ret != HTTP_OK ) goto dispatch_error;
+
+       /* Read headers in chunked trailers */
+       if( req->resp.te == te_chunked ) {
+           ret = read_response_headers( req, buf );
+           if( ret != HTTP_OK ) goto dispatch_error;
+       }
+
+       if( proxy_authinfo != NULL && 
+           http_auth_verify_response( &sess->proxy.auth, proxy_authinfo ) ) {
+           DEBUG( DEBUG_HTTP, "Proxy response authentication invalid.\n" );
+           ret = HTTP_SERVERAUTH;
+           http_set_error( sess, _("Proxy server was not authenticated correctly.") );
+       } else if( authinfo != NULL &&
+                  http_auth_verify_response( &sess->server.auth, authinfo ) ) {
+           DEBUG( DEBUG_HTTP, "Response authenticated as invalid.\n" );
+           ret = HTTP_PROXYAUTH;
+           http_set_error( sess, _("Server was not authenticated correctly.") );
+       } else if( status->code == 401 && www_auth != NULL && attempt++ == 1) {
+           if( !http_auth_challenge( &sess->server.auth, www_auth ) ) {
+               can_retry = 1;
+           }           
+       } else if( status->code == 407 && proxy_auth != NULL && proxy_attempt++ == 1 ) {
+           if( !http_auth_challenge( &sess->proxy.auth, proxy_auth ) ) {
+               can_retry = 1;
+           }
+       }
+
+       HTTP_FREE( www_auth );
+       HTTP_FREE( proxy_auth );
+       HTTP_FREE( authinfo );
+       HTTP_FREE( proxy_authinfo );
+       
+       /* Close the connection if one of the following is true:
+        *  - We have a forced close (e.g. "Connection: close" header)
+        *  - We are not using persistent connections for this session
+        *  - this is HTTP/1.0, and they haven't said they can do
+        *    persistent connections 
+        */
+       if( req->forced_close || sess->no_persist ||
+           (HTTP_VERSION_PRE11(sess) && !req->can_persist ) ) {
+           close_connection(sess);
+       }
+    
+       /* Retry it if we had an auth challenge */
+
+    } while( can_retry );
+
+    DEBUG( DEBUG_HTTP | DEBUG_FLUSH, 
+          "Request ends, status %d class %dxx, error line:\n%s\n", 
+          status->code, status->class, sess->error );
+    DEBUG( DEBUG_HTTPBASIC, "Response: %d %s", status->code, sess->error );
+
+    switch( status->code ) {
+    case 401:
+       ret = HTTP_AUTH;
+       break;
+    case 407:
+       ret = HTTP_AUTHPROXY;
+       break;
+    default:
+       break;
+    }
+
+dispatch_error:
+    
+    sbuffer_destroy(request);
+    
+    HTTP_FREE( www_auth );
+    HTTP_FREE( proxy_auth );
+    HTTP_FREE( authinfo );
+    HTTP_FREE( proxy_authinfo );
+
+    return ret;
+}
+
+static int open_connection( http_req *req ) {
+    http_session *sess = req->session;
+    if( req->use_proxy ) {
+       DEBUG( DEBUG_SOCKET, "Connecting to proxy at %s:%d...\n", 
+              sess->proxy.hostname, sess->proxy.port );
+       sess->socket = sock_connect( sess->proxy.addr, sess->proxy.port );
+       if( sess->socket < 0 ) {
+           (void) set_sockerr( req, _("Could not connect to proxy server"), -1 );
+           return HTTP_CONNECT;
+       }
+    } else {
+       DEBUG( DEBUG_SOCKET, "Connecting to server at %s:%d...\n", 
+              sess->server.hostname, sess->server.port );
+       sess->socket = sock_connect( sess->server.addr, sess->server.port );
+       if( sess->socket < 0 ) {
+           (void) set_sockerr( req, _("Could not connect to server"), -1 );
+           return HTTP_CONNECT;
+       }
+    }
+    DEBUG( DEBUG_SOCKET, "Connected.\n" );
+    sess->connected = 1;
+    return HTTP_OK;
+}
+
+static int close_connection( http_session *sess ) {
+    DEBUG( DEBUG_SOCKET, "Closing socket.\n" );
+    sock_close( sess->socket );
+    sess->connected = 0;
+    DEBUG( DEBUG_SOCKET, "Socket closed.\n" );
+    return 0;
+}
+
diff --git a/neon/src/http_request.h b/neon/src/http_request.h
new file mode 100644 (file)
index 0000000..6059f8a
--- /dev/null
@@ -0,0 +1,261 @@
+/* 
+   HTTP Request Handling
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef HTTP_REQUEST_H
+#define HTTP_REQUEST_H
+
+#include <stdio.h> /* For FILE * */
+#include "http_utils.h" /* For http_status */
+#include "string_utils.h" /* For sbuffer */
+
+#define HTTP_OK (0)
+#define HTTP_ERROR (1) /* Generic error */
+#define HTTP_LOOKUP (3) /* Name lookup failed */
+#define HTTP_AUTH (4) /* User authentication failed on server */
+#define HTTP_AUTHPROXY (5) /* User authentication failed on proxy */
+#define HTTP_SERVERAUTH (6) /* Server authentication failed */
+#define HTTP_PROXYAUTH (7) /* Proxy authentication failed */
+#define HTTP_CONNECT (8) /* Could not connect to server */
+#define HTTP_TIMEOUT (9) /* Connection timed out */
+#define HTTP_FAILED (10) /* The precondition failed */
+
+/* Will be used by hooks: */
+#define HTTP_RETRY (101)
+
+#define EOL "\r\n"
+
+/****** Session Handling ******/
+
+typedef struct http_session_s http_session;
+typedef struct http_req_s http_req;
+
+/* 
+ * Session handling:
+ *  Call order:
+ *  1.   http_session_init    MANDATORY
+ *  2.   http_session_proxy   OPTIONAL
+ *  3.   http_session_server  MANDATORY
+ *  4.   http_set_*           OPTIONAL
+ *  ...  --- Any HTTP request method ---
+ *  n.   http_session_finish  MANDATORY
+ */
+/* Create a new HTTP session */
+http_session *http_session_init( void );
+
+
+/* Finish an HTTP session */
+int http_session_finish( http_session *sess );
+
+/* Set the server or proxy server to be used for the given session.
+ * Returns:
+ *   HTTP_LOOKUP if the DNS lookup for hostname failed.
+ *   HTTP_OK otherwise.
+ *
+ * Note that if a proxy is being used, http_session_proxy should be
+ * called BEFORE http_session_server, so the DNS lookup can be skipped
+ * on the server. */
+int http_session_server( http_session *sess, const char *hostname, int port );
+int http_session_proxy( http_session *sess, const char *hostname, int port );
+
+/* Set protocol options for session:
+ *   expect100: Defaults to OFF
+ *   persist:   Defaults to ON
+ */
+void http_set_expect100( http_session *sess, int use_expect100 );
+void http_set_persist( http_session *sess, int persist );
+/* Sets the user-agent string. neon/VERSION will be appended, to make
+ * the full header "User-Agent: token neon/VERSION" */
+void http_set_useragent( http_session *sess, const char *token );
+
+/* Determine if next-hop server claims HTTP/1.1 compliance. Returns:
+ *   0 if next-hop server does NOT claim HTTP/1.1 compliance
+ *   non-zero if next-hop serer DOES claim HTTP/1.1 compliance
+ */
+int http_version_pre_http11( http_session *sess );
+
+/* Returns the 'hostport' URI segment for the end-server, e.g.
+ *    "my.server.com:8080"    or    "www.server.com" 
+ *  (port segment is ommitted if == 80) 
+ */
+const char *http_get_server_hostport( http_session *sess );
+
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be placed in malloc()-allocated
+ * memory.
+ * Must return:
+ *   0 on success: *username and *password must be non-NULL, and will
+ *                 be free'd by the HTTP layer when necessary
+ *  -1 to cancel (*username and *password are ignored.)
+ */
+typedef int (*http_request_auth)( 
+    void *userdata, const char *realm, const char *hostname,
+    char **username, char **password );
+
+/* Set callbacks to handle server and proxy authentication.
+ * userdata is passed as the first argument to the callback. */
+void http_set_server_auth( http_session *sess, http_request_auth callback, 
+                          void *userdata );
+void http_set_proxy_auth( http_session *sess, http_request_auth callback, 
+                         void *userdata );
+
+/* Set the error string for the session */
+void http_set_error( http_session *sess, const char *errstring );
+/* Retrieve the error string for the session */
+const char *http_get_error( http_session *sess );
+
+/**** Request hooks handling *****/
+
+/* TODO: Unimplemented */
+
+/* The aim of hooks is to support HTTP authentication, SSL/TLS, and possibly
+ * DAV locking (though this is harder), in a modular fashion - i.e., without
+ * requiring any code in http_request.c.
+ *
+ * DAV locking is harder, as it is not transparent to the caller: they
+ * specify what locks are needed (i.e. MKCOL modifies the *parent* collection
+ * of the resource at Request-URI as well as the resource itself.).
+ * This will probably require some way of retrieving the private data of
+ * the hook externally, hence the handler_id hack.
+ */
+typedef struct {
+    /* A slight hack? Allows access to the hook private information,
+     * externally... */
+    const char *id; /* id could be a URI to be globally unique */
+    /* e.g. "http://webdav.org/neon/hooks/davlock" */
+    
+    /* Register a new request */
+    void (*create)( void **private, 
+                   http_req *req, const char *method, const char *uri );
+    /* Tell them what request body we are using: either buffer or stream
+     * will be non-NULL. */
+    void (*use_body)( void *private, const char *buffer, FILE *stream );
+    /* Just before sending the request: let them add headers if they want */
+    void (*pre_send)( void *private, sbuffer request );
+    /* After sending the request. May return:
+     *  HTTP_OK     everything is okay
+     *  HTTP_RETRY  try sending the request again */
+    int (*post_send)( void *private, http_status *stat );
+    /* Clean up after yourself */
+    void (*destroy)( void *private );
+} http_request_hooks;
+
+/* Add in hooks */
+void http_add_hooks( http_session *sess, http_request_hooks hooks );
+
+/* Return private data for a new request */ 
+void *http_get_hook_private( http_req *req, const char *id );
+
+/***** Request Handling *****/
+
+/* Create a new request, with given method and URI. 
+ * Returns request pointer. */
+http_req *
+http_request_create( http_session *sess, const char *method, const char *uri );
+
+/* 'buffer' will be sent as the request body with given request. */
+void http_set_request_body_buffer( http_req *req, const char *buffer );
+/* Contents of stream will be sent as the request body with the given
+ * request */
+void http_set_request_body_stream( http_req *req, FILE *stream );
+
+/* Handling response bodies... you provide TWO callbacks:
+ *
+ * 1) 'acceptance' callback: determines whether you want to handle the
+ * response body given the response-status information, e.g., if you
+ * only want 2xx responses, say so here.
+ *
+ * 2) 'reader' callback: passed blocks of the response-body as they
+ * arrive, if the acceptance callback returned non-zero.  */
+
+/* 'acceptance' callback type. */
+typedef int (*http_accept_response)( 
+    void *userdata, http_req *req, http_status *st );
+
+/* An 'acceptance' callback which only accepts 2xx-style responses.
+ * Ignores userdata. */
+int http_accept_2xx( void *userdata, http_req *req, http_status *st );
+
+/* The 'reader' callback type */
+typedef void (*http_block_reader)( 
+    void *userdata, const char *buf, size_t len );
+
+/* Add a response reader for the given request, with the given
+ * acceptance function. userdata is passed as the first argument to
+ * the acceptance + reader callbacks. */
+void http_add_response_body_reader( http_req *req, http_accept_response accpt,
+                                   http_block_reader rdr, void *userdata );
+
+/* Handle response headers. Each handler is associated with a specific
+ * header field (indicated by name). The handler is then passed the
+ * value of this header field. */
+
+/* The header handler callback type */
+typedef void (*http_header_handler)( void *userdata, const char *value );
+
+/* Adds a response header handler for the given request. userdata is passed
+ * as the first argument to the header handler. */
+void http_add_response_header_handler( 
+    http_req *req, const char *name, http_header_handler hdl, void *userdata );
+
+/* Stock header handlers:
+ *  'duplicate': *(char *)userdata = strdup(value)
+ *  'numeric':   *(int *)userdata = atoi(value)
+ * e.g.
+ *   int mynum;
+ *   http_add_response_header_handler( myreq, "Content-Length",
+ *                                     http_handle_numeric_handler, &mynum );
+ * ... arranges mynum to be set to the value of the Content-Length header.
+ */
+void http_duplicate_header( void *userdata, const char *value );
+void http_handle_numeric_header( void *userdata, const char *value );
+
+/* Returns the sbuffer which is the request header string, allowing
+ * extra headers to be added to the request. */
+/* TODO: This is a bit noddy. */
+sbuffer http_get_request_header( http_req *req );
+
+/* http_request_dispatch: Sends the given request, and reads the
+ * response.  Response-Status information is written into 'status'.
+ *
+ * Returns:
+ *  HTTP_OK         if request sent + response read okay.
+ *  HTTP_AUTH       if user authentication failed on origin server
+ *  HTTP_AUTHPROXY  if user authentication failed on proxy server
+ *  HTTP_SERVERAUTH server authentication failed
+ *  HTTP_PROXYAUTH  proxy authentication failed
+ *  HTTP_CONNECT    could not connect to server/proxy server
+ *  HTTP_TIMEOUT    connection timed out mid-request
+ *  HTTP_ERROR      for other errors, and http_get_error() should
+ *                  return a meaningful error string
+ *
+ * NB: HTTP_AUTH and HTTP_AUTHPROXY mean that the USER supplied the
+ * wrong username/password.  SERVER/PROXYAUTH mean that, after the
+ * server has accepted a valid username/password, the server/proxy did
+ * not authenticate the response message correctly.
+ * */
+int http_request_dispatch( http_req *req, http_status *status );
+
+/* Destroy memory associated with request pointer */
+void http_request_destroy( http_req *req );
+
+#endif /* HTTP_REQUEST_H */
+
diff --git a/neon/src/http_utils.c b/neon/src/http_utils.c
new file mode 100644 (file)
index 0000000..f4548ac
--- /dev/null
@@ -0,0 +1,116 @@
+/* 
+   HTTP utility functions
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: http_utils.c,v 1.4 2000/05/10 13:26:07 joe Exp 
+*/
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <ctype.h> /* isdigit() for http_parse_statusline */
+
+#include "dates.h"
+
+#include "http_utils.h"
+
+int http_debug_mask;
+FILE *http_debug_stream;
+
+void http_debug( int ch, char *template, ...) 
+{
+#ifdef DEBUGGING
+    va_list params;
+    if( (ch&http_debug_mask) != ch ) return;
+    fflush( stdout );
+    va_start( params, template );
+    vfprintf( http_debug_stream, template, params );
+    va_end( params );
+    if( (ch&DEBUG_FLUSH) == DEBUG_FLUSH ) {
+       fflush( http_debug_stream );
+    }
+#else
+    /* No debugging here */
+#endif
+}
+
+/* HTTP-date parser */
+time_t http_dateparse( const char *date ) {
+    time_t tmp;
+    tmp = rfc1123_parse( date );
+    if( tmp == -1 ) {
+        tmp = rfc1036_parse( date );
+       if( tmp == -1 )
+           tmp = asctime_parse( date );
+    }
+    return tmp;
+}
+
+int http_parse_statusline( const char *status_line, http_status *st ) {
+    const char *part;
+    int major, minor, status_code, class;
+    /* Check they're speaking the right language */
+    if( strncmp( status_line, "HTTP/", 5 ) != 0 ) {
+       return -1;
+    }
+    /* And find out which dialect of this peculiar language
+     * they can talk... */
+    major = 0;
+    minor = 0; 
+    /* Note, we're good children, and accept leading zero's on the
+     * version numbers */
+    for( part = status_line + 5; *part != '\0' && isdigit(*part); part++ ) {
+       major = major*10 + (*part-'0');
+    }
+    if( *part != '.' ) { 
+       return -1;
+    }
+    for( part++ ; *part != '\0' && isdigit(*part); part++ ) {
+       minor = minor*10 + (*part-'0');
+    }
+    if( *part != ' ' ) {
+       return -1;
+    }
+    /* Skip any spaces */
+    for( ; *part == ' ' ; part++ ) /* noop */;
+    /* Now for the Status-Code. part now points at the first Y in
+     * "HTTP/x.x YYY". We want value of YYY... could use atoi, but
+     * probably quicker this way. */
+    if( !isdigit(part[0]) || !isdigit(part[1]) || !isdigit(part[2]) ) {
+       return -1;
+    }
+    status_code = 100*(part[0]-'0') + 10*(part[1]-'0') + (part[2]-'0');
+    class = part[0]-'0';
+    /* Skip whitespace between status-code and reason-phrase */
+    for( part+=3; *part == ' ' || *part == '\t'; part++ ) /* noop */;
+    if( *part == '\0' ) {
+       return -1;
+    }
+    /* Fill in the results */
+    st->major_version = major;
+    st->minor_version = minor;
+    st->reason_phrase = part;
+    st->code = status_code;
+    st->class = class;
+    return 0;
+}
diff --git a/neon/src/http_utils.h b/neon/src/http_utils.h
new file mode 100644 (file)
index 0000000..d18ac6f
--- /dev/null
@@ -0,0 +1,105 @@
+/* 
+   HTTP utility functions
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef HTTP_UTILS_H
+#define HTTP_UTILS_H
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_DEBUG_FUNC
+#include "common.h"
+#endif
+
+#define HTTP_QUOTES "\"'"
+#define HTTP_WHITESPACE " \r\n\t"
+
+/* Handy macro to free things. */
+#define HTTP_FREE(x) \
+do { if( (x)!=NULL ) free( (x) ); (x) = NULL; } while(0)
+
+#define HTTP_PORT 80
+
+time_t http_dateparse( const char *date );
+
+#undef min
+#define min(a,b) ((a)<(b)?(a):(b))
+
+#ifndef HAVE_DEBUG_FUNC
+/* CONSIDER: mutt has a nicer way of way of doing debugging output... maybe
+ * switch to like that. */
+#ifdef DEBUGGING
+#define DEBUG if(0) http_debug
+#else /* !DEBUGGING */
+#define DEBUG http_debug
+#endif /* DEBUGGING */
+
+#define DEBUG_SOCKET (1<<0)
+#define DEBUG_HTTP (1<<3)
+#define DEBUG_XML (1<<5)
+#define DEBUG_HTTPAUTH (1<<7)
+#define DEBUG_HTTPPLAIN (1<<8)
+#define DEBUG_LOCKS (1<<10)
+#define DEBUG_XMLPARSE (1<<11)
+#define DEBUG_HTTPBODY (1<<12)
+#define DEBUG_HTTPBASIC (1<<13)
+#define DEBUG_FLUSH (1<<22)
+
+extern FILE *http_debug_stream;
+extern int http_debug_mask;
+
+void http_debug( int ch, char *, ... )
+#ifdef __GNUC__
+                __attribute__ ((format (printf, 2, 3)))
+#endif /* __GNUC__ */
+;
+
+#endif /* HAVE_DEBUG_FUNC */
+
+/* Storing an HTTP status result */
+typedef struct {
+    int major_version;
+    int minor_version;
+    int code; /* Status-Code value */
+    int class; /* Class of Status-Code (1-5) */
+    const char *reason_phrase;
+} http_status;
+
+/* Parser for strings which follow the Status-Line grammar from 
+ * RFC2616.
+ *  Returns:
+ *    0 on success, *s will be filled in.
+ *   -1 on parse error.
+ */
+int http_parse_statusline( const char *status_line, http_status *s );
+
+#endif /* HTTP_UTILS_H */
diff --git a/neon/src/md5.c b/neon/src/md5.c
new file mode 100644 (file)
index 0000000..00aab15
--- /dev/null
@@ -0,0 +1,447 @@
+/* md5.c - Functions to compute MD5 message digest of files or memory blocks
+   according to the definition of MD5 in RFC 1321 from April 1992.
+   Copyright (C) 1995, 1996, 1997 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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+# include <string.h>
+#else
+# ifndef HAVE_MEMCPY
+#  define memcpy(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include "md5.h"
+
+#ifdef _LIBC
+# include <endian.h>
+# if __BYTE_ORDER == __BIG_ENDIAN
+#  define WORDS_BIGENDIAN 1
+# endif
+/* We need to keep the namespace clean so define the MD5 function
+   protected using leading __ and use weak aliases.  */
+# define md5_init_ctx __md5_init_ctx
+# define md5_process_block __md5_process_block
+# define md5_process_bytes __md5_process_bytes
+# define md5_finish_ctx __md5_finish_ctx
+# define md5_read_ctx __md5_read_ctx
+# define md5_stream __md5_stream
+# define md5_buffer __md5_buffer
+#endif
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n)                                                       \
+    (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#else
+# define SWAP(n) (n)
+#endif
+
+
+/* This array contains the bytes used to pad the buffer to the next
+   64-byte boundary.  (RFC 1321, 3.1: Step 1)  */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */ };
+
+
+/* Initialize structure containing state of computation.
+   (RFC 1321, 3.3: Step 3)  */
+void
+md5_init_ctx (ctx)
+     struct md5_ctx *ctx;
+{
+  ctx->A = 0x67452301;
+  ctx->B = 0xefcdab89;
+  ctx->C = 0x98badcfe;
+  ctx->D = 0x10325476;
+
+  ctx->total[0] = ctx->total[1] = 0;
+  ctx->buflen = 0;
+}
+
+/* Put result from CTX in first 16 bytes following RESBUF.  The result
+   must be in little endian byte order.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+void *
+md5_read_ctx (ctx, resbuf)
+     const struct md5_ctx *ctx;
+     void *resbuf;
+{
+  ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
+  ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
+  ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
+  ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
+
+  return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+   prolog according to the standard and write the result to RESBUF.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+void *
+md5_finish_ctx (ctx, resbuf)
+     struct md5_ctx *ctx;
+     void *resbuf;
+{
+  /* Take yet unprocessed bytes into account.  */
+  md5_uint32 bytes = ctx->buflen;
+  size_t pad;
+
+  /* Now count remaining bytes.  */
+  ctx->total[0] += bytes;
+  if (ctx->total[0] < bytes)
+    ++ctx->total[1];
+
+  pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+  memcpy (&ctx->buffer[bytes], fillbuf, pad);
+
+  /* Put the 64-bit file length in *bits* at the end of the buffer.  */
+  *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
+  *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
+                                                       (ctx->total[0] >> 29));
+
+  /* Process last bytes.  */
+  md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
+
+  return md5_read_ctx (ctx, resbuf);
+}
+
+/* Compute MD5 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 16 bytes
+   beginning at RESBLOCK.  */
+int
+md5_stream (stream, resblock)
+     FILE *stream;
+     void *resblock;
+{
+  /* Important: BLOCKSIZE must be a multiple of 64.  */
+#define BLOCKSIZE 4096
+  struct md5_ctx ctx;
+  char buffer[BLOCKSIZE + 72];
+  size_t sum;
+
+  /* Initialize the computation context.  */
+  md5_init_ctx (&ctx);
+
+  /* Iterate over full file contents.  */
+  while (1)
+    {
+      /* We read the file in blocks of BLOCKSIZE bytes.  One call of the
+        computation function processes the whole buffer so that with the
+        next round of the loop another block can be read.  */
+      size_t n;
+      sum = 0;
+
+      /* Read block.  Take care for partial reads.  */
+      do
+       {
+         n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+         sum += n;
+       }
+      while (sum < BLOCKSIZE && n != 0);
+      if (n == 0 && ferror (stream))
+        return 1;
+
+      /* If end of file is reached, end the loop.  */
+      if (n == 0)
+       break;
+
+      /* Process buffer with BLOCKSIZE bytes.  Note that
+                       BLOCKSIZE % 64 == 0
+       */
+      md5_process_block (buffer, BLOCKSIZE, &ctx);
+    }
+
+  /* Add the last bytes if necessary.  */
+  if (sum > 0)
+    md5_process_bytes (buffer, sum, &ctx);
+
+  /* Construct result in desired memory.  */
+  md5_finish_ctx (&ctx, resblock);
+  return 0;
+}
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+void *
+md5_buffer (buffer, len, resblock)
+     const char *buffer;
+     size_t len;
+     void *resblock;
+{
+  struct md5_ctx ctx;
+
+  /* Initialize the computation context.  */
+  md5_init_ctx (&ctx);
+
+  /* Process whole buffer but last len % 64 bytes.  */
+  md5_process_bytes (buffer, len, &ctx);
+
+  /* Put result in desired memory area.  */
+  return md5_finish_ctx (&ctx, resblock);
+}
+
+
+void
+md5_process_bytes (buffer, len, ctx)
+     const void *buffer;
+     size_t len;
+     struct md5_ctx *ctx;
+{
+  /* When we already have some bits in our internal buffer concatenate
+     both inputs first.  */
+  if (ctx->buflen != 0)
+    {
+      size_t left_over = ctx->buflen;
+      size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+      memcpy (&ctx->buffer[left_over], buffer, add);
+      ctx->buflen += add;
+
+      if (left_over + add > 64)
+       {
+         md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx);
+         /* The regions in the following copy operation cannot overlap.  */
+         memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+                 (left_over + add) & 63);
+         ctx->buflen = (left_over + add) & 63;
+       }
+
+      buffer = (const char *) buffer + add;
+      len -= add;
+    }
+
+  /* Process available complete blocks.  */
+  if (len > 64)
+    {
+      md5_process_block (buffer, len & ~63, ctx);
+      buffer = (const char *) buffer + (len & ~63);
+      len &= 63;
+    }
+
+  /* Move remaining bytes in internal buffer.  */
+  if (len > 0)
+    {
+      memcpy (ctx->buffer, buffer, len);
+      ctx->buflen = len;
+    }
+}
+
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+   and defined in the RFC 1321.  The first function is a little bit optimized
+   (as found in Colin Plumbs public domain implementation).  */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+   It is assumed that LEN % 64 == 0.  */
+
+void
+md5_process_block (buffer, len, ctx)
+     const void *buffer;
+     size_t len;
+     struct md5_ctx *ctx;
+{
+  md5_uint32 correct_words[16];
+  const md5_uint32 *words = buffer;
+  size_t nwords = len / sizeof (md5_uint32);
+  const md5_uint32 *endp = words + nwords;
+  md5_uint32 A = ctx->A;
+  md5_uint32 B = ctx->B;
+  md5_uint32 C = ctx->C;
+  md5_uint32 D = ctx->D;
+
+  /* First increment the byte count.  RFC 1321 specifies the possible
+     length of the file up to 2^64 bits.  Here we only compute the
+     number of bytes.  Do a double word increment.  */
+  ctx->total[0] += len;
+  if (ctx->total[0] < len)
+    ++ctx->total[1];
+
+  /* Process all bytes in the buffer with 64 bytes in each round of
+     the loop.  */
+  while (words < endp)
+    {
+      md5_uint32 *cwp = correct_words;
+      md5_uint32 A_save = A;
+      md5_uint32 B_save = B;
+      md5_uint32 C_save = C;
+      md5_uint32 D_save = D;
+
+      /* First round: using the given function, the context and a constant
+        the next context is computed.  Because the algorithms processing
+        unit is a 32-bit word and it is determined to work on words in
+        little endian byte order we perhaps have to change the byte order
+        before the computation.  To reduce the work for the next steps
+        we store the swapped words in the array CORRECT_WORDS.  */
+
+#define OP(a, b, c, d, s, T)                                           \
+      do                                                               \
+        {                                                              \
+         a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T;             \
+         ++words;                                                      \
+         CYCLIC (a, s);                                                \
+         a += b;                                                       \
+        }                                                              \
+      while (0)
+
+      /* It is unfortunate that C does not provide an operator for
+        cyclic rotation.  Hope the C compiler is smart enough.  */
+#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+      /* Before we start, one word to the strange constants.
+        They are defined in RFC 1321 as
+
+        T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+       */
+
+      /* Round 1.  */
+      OP (A, B, C, D,  7, 0xd76aa478);
+      OP (D, A, B, C, 12, 0xe8c7b756);
+      OP (C, D, A, B, 17, 0x242070db);
+      OP (B, C, D, A, 22, 0xc1bdceee);
+      OP (A, B, C, D,  7, 0xf57c0faf);
+      OP (D, A, B, C, 12, 0x4787c62a);
+      OP (C, D, A, B, 17, 0xa8304613);
+      OP (B, C, D, A, 22, 0xfd469501);
+      OP (A, B, C, D,  7, 0x698098d8);
+      OP (D, A, B, C, 12, 0x8b44f7af);
+      OP (C, D, A, B, 17, 0xffff5bb1);
+      OP (B, C, D, A, 22, 0x895cd7be);
+      OP (A, B, C, D,  7, 0x6b901122);
+      OP (D, A, B, C, 12, 0xfd987193);
+      OP (C, D, A, B, 17, 0xa679438e);
+      OP (B, C, D, A, 22, 0x49b40821);
+
+      /* For the second to fourth round we have the possibly swapped words
+        in CORRECT_WORDS.  Redefine the macro to take an additional first
+        argument specifying the function to use.  */
+#undef OP
+#define OP(f, a, b, c, d, k, s, T)                                     \
+      do                                                               \
+       {                                                               \
+         a += f (b, c, d) + correct_words[k] + T;                      \
+         CYCLIC (a, s);                                                \
+         a += b;                                                       \
+       }                                                               \
+      while (0)
+
+      /* Round 2.  */
+      OP (FG, A, B, C, D,  1,  5, 0xf61e2562);
+      OP (FG, D, A, B, C,  6,  9, 0xc040b340);
+      OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+      OP (FG, B, C, D, A,  0, 20, 0xe9b6c7aa);
+      OP (FG, A, B, C, D,  5,  5, 0xd62f105d);
+      OP (FG, D, A, B, C, 10,  9, 0x02441453);
+      OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+      OP (FG, B, C, D, A,  4, 20, 0xe7d3fbc8);
+      OP (FG, A, B, C, D,  9,  5, 0x21e1cde6);
+      OP (FG, D, A, B, C, 14,  9, 0xc33707d6);
+      OP (FG, C, D, A, B,  3, 14, 0xf4d50d87);
+      OP (FG, B, C, D, A,  8, 20, 0x455a14ed);
+      OP (FG, A, B, C, D, 13,  5, 0xa9e3e905);
+      OP (FG, D, A, B, C,  2,  9, 0xfcefa3f8);
+      OP (FG, C, D, A, B,  7, 14, 0x676f02d9);
+      OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+      /* Round 3.  */
+      OP (FH, A, B, C, D,  5,  4, 0xfffa3942);
+      OP (FH, D, A, B, C,  8, 11, 0x8771f681);
+      OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+      OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+      OP (FH, A, B, C, D,  1,  4, 0xa4beea44);
+      OP (FH, D, A, B, C,  4, 11, 0x4bdecfa9);
+      OP (FH, C, D, A, B,  7, 16, 0xf6bb4b60);
+      OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+      OP (FH, A, B, C, D, 13,  4, 0x289b7ec6);
+      OP (FH, D, A, B, C,  0, 11, 0xeaa127fa);
+      OP (FH, C, D, A, B,  3, 16, 0xd4ef3085);
+      OP (FH, B, C, D, A,  6, 23, 0x04881d05);
+      OP (FH, A, B, C, D,  9,  4, 0xd9d4d039);
+      OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+      OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+      OP (FH, B, C, D, A,  2, 23, 0xc4ac5665);
+
+      /* Round 4.  */
+      OP (FI, A, B, C, D,  0,  6, 0xf4292244);
+      OP (FI, D, A, B, C,  7, 10, 0x432aff97);
+      OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+      OP (FI, B, C, D, A,  5, 21, 0xfc93a039);
+      OP (FI, A, B, C, D, 12,  6, 0x655b59c3);
+      OP (FI, D, A, B, C,  3, 10, 0x8f0ccc92);
+      OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+      OP (FI, B, C, D, A,  1, 21, 0x85845dd1);
+      OP (FI, A, B, C, D,  8,  6, 0x6fa87e4f);
+      OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+      OP (FI, C, D, A, B,  6, 15, 0xa3014314);
+      OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+      OP (FI, A, B, C, D,  4,  6, 0xf7537e82);
+      OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+      OP (FI, C, D, A, B,  2, 15, 0x2ad7d2bb);
+      OP (FI, B, C, D, A,  9, 21, 0xeb86d391);
+
+      /* Add the starting values of the context.  */
+      A += A_save;
+      B += B_save;
+      C += C_save;
+      D += D_save;
+    }
+
+  /* Put checksum in context given as argument.  */
+  ctx->A = A;
+  ctx->B = B;
+  ctx->C = C;
+  ctx->D = D;
+}
+
+
+#ifdef _LIBC
+/* Define weak aliases.  */
+# undef md5_init_ctx
+weak_alias (__md5_init_ctx, md5_init_ctx)
+# undef md5_process_block
+weak_alias (__md5_process_block, md5_process_block)
+# undef md5_process_bytes
+weak_alias (__md5_process_bytes, md5_process_bytes)
+# undef md5_finish_ctx
+weak_alias (__md5_finish_ctx, md5_finish_ctx)
+# undef md5_read_ctx
+weak_alias (__md5_read_ctx, md5_read_ctx)
+# undef md5_stream
+weak_alias (__md5_stream, md5_stream)
+# undef md5_buffer
+weak_alias (__md5_buffer, md5_buffer)
+#endif
diff --git a/neon/src/md5.h b/neon/src/md5.h
new file mode 100644 (file)
index 0000000..ce4091d
--- /dev/null
@@ -0,0 +1,157 @@
+/* Declaration of functions and data types used for MD5 sum computing
+   library functions.
+   Copyright (C) 1995, 1996, 1997 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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _MD5_H
+#define _MD5_H 1
+
+#include <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/* The following contortions are an attempt to use the C preprocessor
+   to determine an unsigned integral type that is 32 bits wide.  An
+   alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+   doing that would require that the configure script compile and *run*
+   the resulting executable.  Locally running cross-compiled executables
+   is usually not possible.  */
+
+#ifdef _LIBC
+# include <sys/types.h>
+typedef u_int32_t md5_uint32;
+#else
+# if defined __STDC__ && __STDC__
+#  define UINT_MAX_32_BITS 4294967295U
+# else
+#  define UINT_MAX_32_BITS 0xFFFFFFFF
+# endif
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+   This should be valid for all systems GNU cares about because
+   that doesn't include 16-bit systems, and only modern systems
+   (that certainly have <limits.h>) have 64+-bit integral types.  */
+
+# ifndef UINT_MAX
+#  define UINT_MAX UINT_MAX_32_BITS
+# endif
+
+# if UINT_MAX == UINT_MAX_32_BITS
+   typedef unsigned int md5_uint32;
+# else
+#  if USHRT_MAX == UINT_MAX_32_BITS
+    typedef unsigned short md5_uint32;
+#  else
+#   if ULONG_MAX == UINT_MAX_32_BITS
+     typedef unsigned long md5_uint32;
+#   else
+     /* The following line is intended to evoke an error.
+        Using #error is not portable enough.  */
+     "Cannot determine unsigned 32-bit data type."
+#   endif
+#  endif
+# endif
+#endif
+
+#undef __P
+#if defined (__STDC__) && __STDC__
+# define __P(x) x
+#else
+# define __P(x) ()
+#endif
+
+/* Structure to save state of computation between the single steps.  */
+struct md5_ctx
+{
+  md5_uint32 A;
+  md5_uint32 B;
+  md5_uint32 C;
+  md5_uint32 D;
+
+  md5_uint32 total[2];
+  md5_uint32 buflen;
+  char buffer[128];
+};
+
+/*
+ * The following three functions are build up the low level used in
+ * the functions `md5_stream' and `md5_buffer'.
+ */
+
+/* Initialize structure containing state of computation.
+   (RFC 1321, 3.3: Step 3)  */
+extern void __md5_init_ctx __P ((struct md5_ctx *ctx));
+extern void md5_init_ctx __P ((struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is necessary that LEN is a multiple of 64!!! */
+extern void __md5_process_block __P ((const void *buffer, size_t len,
+                                     struct md5_ctx *ctx));
+extern void md5_process_block __P ((const void *buffer, size_t len,
+                                   struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is NOT required that LEN is a multiple of 64.  */
+extern void __md5_process_bytes __P ((const void *buffer, size_t len,
+                                     struct md5_ctx *ctx));
+extern void md5_process_bytes __P ((const void *buffer, size_t len,
+                                   struct md5_ctx *ctx));
+
+/* Process the remaining bytes in the buffer and put result from CTX
+   in first 16 bytes following RESBUF.  The result is always in little
+   endian byte order, so that a byte-wise output yields to the wanted
+   ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+extern void *__md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
+extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
+
+
+/* Put result from CTX in first 16 bytes following RESBUF.  The result is
+   always in little endian byte order, so that a byte-wise output yields
+   to the wanted ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+extern void *__md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
+extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
+
+
+/* Compute MD5 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 16 bytes
+   beginning at RESBLOCK.  */
+extern int __md5_stream __P ((FILE *stream, void *resblock));
+extern int md5_stream __P ((FILE *stream, void *resblock));
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+extern void *__md5_buffer __P ((const char *buffer, size_t len,
+                               void *resblock));
+extern void *md5_buffer __P ((const char *buffer, size_t len,
+                             void *resblock));
+
+#endif /* md5.h */
diff --git a/neon/src/ne_207.c b/neon/src/ne_207.c
new file mode 100644 (file)
index 0000000..ac6065b
--- /dev/null
@@ -0,0 +1,378 @@
+/* 
+   WebDAV 207 multi-status response handling
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+/* Generic handling for WebDAV 207 Multi-Status responses. */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_alloc.h"
+#include "ne_utils.h"
+#include "ne_xml.h"
+#include "ne_207.h"
+#include "ne_uri.h"
+#include "ne_basic.h"
+
+#include "ne_i18n.h"
+
+struct ne_207_parser_s {
+    ne_207_start_response start_response;
+    ne_207_end_response end_response;
+    ne_207_start_propstat start_propstat;
+    ne_207_end_propstat end_propstat;
+    ne_xml_parser *parser;
+    void *userdata;
+    /* current position */
+    void *response, *propstat;
+    /* caching */
+    ne_status status;
+    char *description, *href, *status_line;
+};
+
+static const struct ne_xml_elm elements[] = {
+    { "DAV:", "multistatus", NE_ELM_multistatus, 0 },
+    { "DAV:", "response", NE_ELM_response, 0 },
+    { "DAV:", "responsedescription", NE_ELM_responsedescription, 
+      NE_XML_CDATA },
+    { "DAV:", "href", NE_ELM_href, NE_XML_CDATA },
+    { "DAV:", "propstat", NE_ELM_propstat, 0 },
+    { "DAV:", "prop", NE_ELM_prop, 0 },
+    { "DAV:", "status", NE_ELM_status, NE_XML_CDATA },
+    { NULL }
+};
+
+/* Set the callbacks for the parser */
+void ne_207_set_response_handlers(ne_207_parser *p,
+                                  ne_207_start_response start,
+                                  ne_207_end_response end)
+{
+    p->start_response = start;
+    p->end_response = end;
+}
+
+void ne_207_set_propstat_handlers(ne_207_parser *p,
+                                  ne_207_start_propstat start,
+                                  ne_207_end_propstat end)
+{
+    p->start_propstat = start;
+    p->end_propstat = end;
+}
+
+void *ne_207_get_current_response(ne_207_parser *p)
+{
+    return p->response;
+}
+
+void *ne_207_get_current_propstat(ne_207_parser *p)
+{
+    return p->propstat;
+}
+
+static int 
+start_element(void *userdata, const struct ne_xml_elm *elm, 
+             const char **atts) 
+{
+    ne_207_parser *p = userdata;
+    
+    switch (elm->id) {
+    case NE_ELM_response:
+       /* Create new response delayed until we get HREF */
+       break;
+    case NE_ELM_propstat:
+       if (p->start_propstat) {
+           p->propstat = (*p->start_propstat)(p->userdata, p->response);
+       }
+       break;
+    }
+    return 0;
+}
+
+static int 
+end_element(void *userdata, const struct ne_xml_elm *elm, const char *cdata)
+{
+    ne_207_parser *p = userdata;
+
+    switch (elm->id) {
+    case NE_ELM_responsedescription:
+       if (cdata != NULL) {
+           NE_FREE(p->description);
+           p->description = ne_strdup(cdata);
+       }
+       break;
+    case NE_ELM_href:
+       /* Now we have the href, begin the response */
+       if (p->start_response && cdata != NULL) {
+           p->response = (*p->start_response)(p->userdata, cdata);
+       }
+       break;
+    case NE_ELM_status:
+       if (cdata) {
+           NE_FREE(p->status_line);
+           p->status_line = ne_strdup(cdata);
+           if (ne_parse_statusline(p->status_line, &p->status)) {
+               char buf[500];
+               NE_DEBUG(NE_DBG_HTTP, "Status line: %s\n", cdata);
+               snprintf(buf, 500, 
+                        _("Invalid HTTP status line in status element at line %d of response:\nStatus line was: %s"),
+                        ne_xml_currentline(p->parser), p->status_line);
+               ne_xml_set_error(p->parser, buf);
+               NE_FREE(p->status_line);
+               return -1;
+           } else {
+               NE_DEBUG(NE_DBG_XML, "Decoded status line: %s\n", p->status_line);
+           }
+       }
+       break;
+    case NE_ELM_propstat:
+       if (p->end_propstat) {
+           (*p->end_propstat)(p->userdata, p->propstat, p->status_line,
+                              p->status_line?&p->status:NULL, p->description);
+       }
+       p->propstat = NULL;
+       NE_FREE(p->description);
+       NE_FREE(p->status_line);
+       break;
+    case NE_ELM_response:
+       if (p->end_response) {
+           (*p->end_response)(p->userdata, p->response, p->status_line,
+                              p->status_line?&p->status:NULL, p->description);
+       }
+       p->response = NULL;
+       NE_FREE(p->status_line);
+       NE_FREE(p->description);
+       break;
+    }
+    return 0;
+}
+
+/* This should map directly from the DTD... with the addition of
+ * ignoring anything we don't understand, being liberal in what we
+ * accept. */
+static int check_context(ne_xml_elmid parent, ne_xml_elmid child) 
+{
+    NE_DEBUG(NE_DBG_XML, "207cc: %d in %d\n", child, parent);
+    switch (parent) {
+    case NE_ELM_root:
+       switch (child) {
+       case NE_ELM_multistatus:
+       case NE_ELM_response: /* not sure why this is here... */
+           return NE_XML_VALID;
+       default:
+           break;
+       }
+       break;
+    case NE_ELM_multistatus:
+       /* <!ELEMENT multistatus (response+, responsedescription?) > */
+       switch (child) {
+       case NE_ELM_response:
+       case NE_ELM_responsedescription:
+           return NE_XML_VALID;
+       default:
+           break;
+       }
+       break;
+    case NE_ELM_response:
+       /* <!ELEMENT response (href, ((href*, status)|(propstat+)),
+          responsedescription?) > */
+       switch (child) {
+       case NE_ELM_href:
+       case NE_ELM_status:
+       case NE_ELM_propstat:
+       case NE_ELM_responsedescription:
+           return NE_XML_VALID;
+       default:
+           break;
+       }
+       break;
+    case NE_ELM_propstat:
+       /* <!ELEMENT propstat (prop, status, responsedescription?) > */
+       switch (child) {
+       case NE_ELM_prop: 
+       case NE_ELM_status:
+       case NE_ELM_responsedescription:
+           return NE_XML_VALID;
+       default:
+           break;
+       }
+       break;
+    default:
+       break;
+    }
+
+    return NE_XML_DECLINE;
+}
+
+static int ignore_cc(ne_xml_elmid parent, ne_xml_elmid child) 
+{
+    if (child == NE_ELM_unknown || parent == NE_ELM_unknown) {
+       NE_DEBUG(NE_DBG_XML, "207 catch-all caught %d in %d\n", child, parent);
+       return NE_XML_VALID;
+    }
+
+    return NE_XML_DECLINE;
+}
+
+void ne_207_ignore_unknown(ne_207_parser *p)
+{
+    static const struct ne_xml_elm any_elms[] = {
+       { "", "", NE_ELM_unknown, NE_XML_COLLECT },
+       { NULL }
+    };
+    
+    ne_xml_push_handler(p->parser, any_elms,
+                        ignore_cc, NULL, NULL, NULL);
+    
+}
+
+ne_207_parser *ne_207_create(ne_xml_parser *parser, void *userdata)
+{
+    ne_207_parser *p = ne_calloc(sizeof *p);
+
+    p->parser = parser;
+    p->userdata = userdata;
+    
+    /* Add handler for the standard 207 elements */
+    ne_xml_push_handler(parser, elements, check_context, 
+                        start_element, end_element, p);
+    
+    return p;
+}
+
+void ne_207_destroy(ne_207_parser *p) 
+{
+    free(p);
+}
+
+int ne_accept_207(void *userdata, ne_request *req, ne_status *status)
+{
+    return (status->code == 207);
+}
+
+/* Handling of 207 errors: we keep a string buffer, and append
+ * messages to it as they come down.
+ *
+ * Note, 424 means it would have worked but something else went wrong.
+ * We will have had the error for "something else", so we display
+ * that, and skip 424 errors. */
+
+/* This is passed as userdata to the 207 code. */
+struct context {
+    char *href;
+    ne_buffer *buf;
+    unsigned int is_error;
+};
+
+static void *start_response(void *userdata, const char *href)
+{
+    struct context *ctx = userdata;
+    NE_FREE(ctx->href);
+    ctx->href = ne_strdup(href);
+    return NULL;
+}
+
+static void handle_error(struct context *ctx,
+                        const char *status_line, const ne_status *status,
+                        const char *description)
+{
+    if (status && status->klass != 2 && status->code != 424) {
+       ctx->is_error = 1;
+       ne_buffer_concat(ctx->buf, ctx->href, ": ", status_line, "\n", NULL);
+       if (description != NULL) {
+           /* TODO: these can be multi-line. Would be good to
+            * word-wrap this at col 80. */
+           ne_buffer_concat(ctx->buf, " -> ", description, "\n", NULL);
+       }
+    }
+
+}
+
+static void end_response(void *userdata, void *response, const char *status_line,
+                        const ne_status *status, const char *description)
+{
+    struct context *ctx = userdata;
+    handle_error(ctx, status_line, status, description);
+}
+
+static void 
+end_propstat(void *userdata, void *propstat, const char *status_line,
+            const ne_status *status, const char *description)
+{
+    struct context *ctx = userdata;
+    handle_error(ctx, status_line, status, description);
+}
+
+/* Dispatch a DAV request and handle a 207 error response appropriately */
+int ne_simple_request(ne_session *sess, ne_request *req)
+{
+    int ret;
+    ne_content_type ctype = {0};
+    struct context ctx = {0};
+    ne_207_parser *p207;
+    ne_xml_parser *p;
+    
+    p = ne_xml_create();
+    p207 = ne_207_create(p, &ctx);
+    /* The error string is progressively written into the
+     * ne_buffer by the element callbacks */
+    ctx.buf = ne_buffer_create();
+
+    ne_207_set_response_handlers(p207, start_response, end_response);
+    ne_207_set_propstat_handlers(p207, NULL, end_propstat);
+    
+    ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v, p);
+    ne_add_response_header_handler(req, "Content-Type", 
+                                     ne_content_type_handler, &ctype);
+
+    ne_207_ignore_unknown(p207);
+
+    ret = ne_request_dispatch(req);
+
+    if (ret == NE_OK) {
+       if (ne_get_status(req)->code == 207) {
+           if (!ne_xml_valid(p)) { 
+               /* The parse was invalid */
+               ne_set_error(sess, ne_xml_get_error(p));
+               ret = NE_ERROR;
+           } else if (ctx.is_error) {
+               /* If we've actually got any error information
+                * from the 207, then set that as the error */
+               ne_set_error(sess, ctx.buf->data);
+               ret = NE_ERROR;
+           }
+       } else if (ne_get_status(req)->klass != 2) {
+           ret = NE_ERROR;
+       }
+    }
+
+    NE_FREE(ctype.value);
+    ne_207_destroy(p207);
+    ne_xml_destroy(p);
+    ne_buffer_destroy(ctx.buf);
+    NE_FREE(ctx.href);
+
+    ne_request_destroy(req);
+
+    return ret;
+}
+    
diff --git a/neon/src/ne_207.h b/neon/src/ne_207.h
new file mode 100644 (file)
index 0000000..4b74880
--- /dev/null
@@ -0,0 +1,100 @@
+/* 
+   WebDAV 207 multi-status response handling
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef DAV207_H
+#define DAV207_H
+
+#include "ne_xml.h"
+#include "ne_request.h" /* for http_req */
+
+BEGIN_NEON_DECLS
+
+#define NE_ELM_207_first (NE_ELM_UNUSED)
+
+#define NE_ELM_multistatus (NE_ELM_207_first)
+#define NE_ELM_response (NE_ELM_207_first + 1)
+#define NE_ELM_responsedescription (NE_ELM_207_first + 2)
+#define NE_ELM_href (NE_ELM_207_first + 3)
+#define NE_ELM_propstat (NE_ELM_207_first + 4)
+#define NE_ELM_prop (NE_ELM_207_first + 5)
+#define NE_ELM_status (NE_ELM_207_first + 6)
+
+#define NE_ELM_207_UNUSED (NE_ELM_UNUSED + 100)
+
+struct ne_207_parser_s;
+typedef struct ne_207_parser_s ne_207_parser;
+
+/* The name of a WebDAV property. */
+typedef struct {
+    const char *nspace, *name;
+} ne_propname;
+
+/* The handler structure: you provide a set of callbacks.
+ * They are called in the order they are listed... start/end_prop
+ * multiple times before end_prop, start/end_propstat multiple times
+ * before an end_response, start/end_response multiple times.
+ */
+
+/* TODO: do we need to pass userdata to ALL of these? We could get away with
+ * only passing the userdata to the start_'s and relying on the caller
+ * to send it through as the _start return value if they need it. */
+
+typedef void *(*ne_207_start_response)(void *userdata, const char *href);
+typedef void (*ne_207_end_response)(
+    void *userdata, void *response, const char *status_line,
+    const ne_status *status, const char *description);
+
+typedef void *(*ne_207_start_propstat)(void *userdata, void *response);
+typedef void (*ne_207_end_propstat)(
+    void *userdata, void *propstat, const char *status_line, 
+    const ne_status *status, const char *description);
+
+/* Create a 207 parser */
+
+ne_207_parser *ne_207_create(ne_xml_parser *parser, void *userdata);
+
+/* Set the callbacks for the parser */
+
+void ne_207_set_response_handlers(
+    ne_207_parser *p, ne_207_start_response start, ne_207_end_response end);
+
+void ne_207_set_propstat_handlers(
+    ne_207_parser *p, ne_207_start_propstat start, ne_207_end_propstat end);
+
+void ne_207_destroy(ne_207_parser *p);
+
+/* An acceptance function which only accepts 207 responses */
+int ne_accept_207(void *userdata, ne_request *req, ne_status *status);
+
+void *ne_207_get_current_propstat(ne_207_parser *p);
+void *ne_207_get_current_response(ne_207_parser *p);
+
+/* Call this as the LAST thing before beginning parsing, to install a
+ * catch-all handler which means all unknown XML returned in the 207
+ * response is ignored gracefully.  */
+void ne_207_ignore_unknown(ne_207_parser *p);
+
+/* Dispatch a DAV request and handle a 207 error response appropriately */
+int ne_simple_request(ne_session *sess, ne_request *req);
+
+END_NEON_DECLS
+
+#endif /* DAV207_H */
diff --git a/neon/src/ne_acl.c b/neon/src/ne_acl.c
new file mode 100644 (file)
index 0000000..888cb75
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+   Access control
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+/* Contributed by Arun Garg <arung@pspl.co.in> */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_locks.h"
+#include "ne_alloc.h"
+#include "ne_string.h"
+#include "ne_acl.h"
+#include "ne_uri.h"
+
+static ne_buffer *acl_body(ne_acl_entry *right, int count)
+{
+    ne_buffer *body = ne_buffer_create();
+    int m;
+
+    ne_buffer_zappend(body,
+                     "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
+                     "<acl xmlns='DAV:'>" EOL);
+
+    for (m = 0; m < count; m++) {
+       const char *type;
+
+       type = (right[m].type == ne_acl_grant ? "grant" : "deny");
+
+       ne_buffer_concat(body, "<ace>" EOL "<principal>", NULL);
+
+       switch (right[m].apply) {
+       case ne_acl_all:
+           ne_buffer_zappend(body, "<all/>" EOL);
+           break;
+       case ne_acl_property:
+           ne_buffer_concat(body, "<property><", right[m].principal,
+                            "/></property>" EOL, NULL);
+           break;
+       case ne_acl_href:
+           ne_buffer_concat(body, "<href>", right[m].principal,
+                            "</href>" EOL, NULL);
+           break;
+       }
+
+       ne_buffer_concat(body, "</principal>" EOL "<", type, ">" EOL, NULL);
+       
+       if (right[m].read == 0)
+           ne_buffer_concat(body,
+                            "<privilege>" "<read/>" "</privilege>" EOL,
+                            NULL);
+       if (right[m].read_acl == 0)
+           ne_buffer_concat(body,
+                            "<privilege>" "<read-acl/>" "</privilege>" EOL,
+                            NULL);
+       if (right[m].write == 0)
+           ne_buffer_concat(body,
+                            "<privilege>" "<write/>" "</privilege>" EOL,
+                            NULL);
+       if (right[m].write_acl == 0)
+           ne_buffer_concat(body,
+                            "<privilege>" "<write-acl/>" "</privilege>" EOL,
+                            NULL);
+       if (right[m].read_cuprivset == 0)
+           ne_buffer_concat(body,
+                            "<privilege>"
+                            "<read-current-user-privilege-set/>"
+                            "</privilege>" EOL, NULL);
+       ne_buffer_concat(body, "</", type, ">" EOL, NULL);
+       ne_buffer_zappend(body, "</ace>" EOL);
+    }
+    ne_buffer_zappend(body, "</acl>" EOL);
+
+    return body;
+}
+
+int ne_acl_set(ne_session *sess, const char *uri,
+              ne_acl_entry *entries, int numentries)
+{
+    int ret;
+    ne_request *req = ne_request_create(sess, "ACL", uri);
+    ne_buffer *body = acl_body(entries, numentries);
+
+#ifdef USE_DAV_LOCKS
+    ne_lock_using_resource(req, uri, 0);
+#endif
+
+    ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
+    ne_add_request_header(req, "Content-Type", "text/xml");
+    ret = ne_request_dispatch(req);
+
+    ne_buffer_destroy(body);
+
+    if (ret == NE_OK && ne_get_status(req)->code == 207) {
+       ret = NE_ERROR;
+    }
+
+    ne_request_destroy(req);
+    return ret;
+}
diff --git a/neon/src/ne_acl.h b/neon/src/ne_acl.h
new file mode 100644 (file)
index 0000000..bf1a1ec
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+   Access control
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_ACL_H
+#define NE_ACL_H
+
+#include "ne_session.h"
+
+BEGIN_NEON_DECLS
+
+typedef struct
+{
+    enum {
+       ne_acl_href,
+       ne_acl_property,
+       ne_acl_all
+    } apply;
+    
+    enum {
+       ne_acl_grant,
+       ne_acl_deny
+    } type;
+
+    char *principal;
+    int read;
+    int read_acl;
+    int write;
+    int write_acl;
+    int read_cuprivset;
+} ne_acl_entry;
+
+/* Set the ACL for the given resource to the list of ACL entries. */
+int ne_acl_set(ne_session *sess, const char *uri,
+              ne_acl_entry entries[], int numentries);
+
+END_NEON_DECLS
+
+#endif /* NE_ACL_H */
diff --git a/neon/src/ne_alloc.c b/neon/src/ne_alloc.c
new file mode 100644 (file)
index 0000000..f376621
--- /dev/null
@@ -0,0 +1,61 @@
+/* 
+   Replacement memory allocation handling etc.
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: ne_alloc.c,v 1.2 2000/08/02 10:23:03 joe Exp 
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_alloc.h"
+
+void *ne_malloc(size_t len) 
+{
+    void *ptr = malloc(len);
+    if (!ptr) {
+       /* uh-oh */
+       abort();
+    }
+    return ptr;
+}
+
+void *ne_calloc(size_t len) 
+{
+    return memset(ne_malloc(len), 0, len);
+}
+
+char *ne_strdup(const char *s) 
+{
+    return strcpy(ne_malloc(strlen(s) + 1), s);
+}
+
+char *ne_strndup(const char *s, size_t n)
+{
+    char *new = ne_malloc(n + 1);
+    new[n] = '\0';
+    memcpy(new, s, n);
+    return new;
+}
diff --git a/neon/src/ne_alloc.h b/neon/src/ne_alloc.h
new file mode 100644 (file)
index 0000000..e120c6b
--- /dev/null
@@ -0,0 +1,34 @@
+/* 
+   Replacement memory allocation handling etc.
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_ALLOC_H
+#define NE_ALLOC_H
+
+#include <sys/types.h>
+
+void *ne_malloc(size_t len);
+void *ne_calloc(size_t len);
+
+char *ne_strdup(const char *s);
+
+char *ne_strndup(const char *s, size_t n);
+
+#endif /* NE_ALLOC_H */
diff --git a/neon/src/ne_auth.c b/neon/src/ne_auth.c
new file mode 100644 (file)
index 0000000..e613726
--- /dev/null
@@ -0,0 +1,1095 @@
+/* 
+   HTTP Authentication routines
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+
+/* HTTP Authentication, as per RFC2617.
+ * 
+ * TODO:
+ *  - Improve cnonce? Make it really random using /dev/random or whatever?
+ *  - Test auth-int support
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <time.h>
+
+#ifdef HAVE_SNPRINTF_H
+#include "snprintf.h"
+#endif
+
+#include "base64.h"
+
+#include "ne_md5.h"
+#include "ne_dates.h"
+#include "ne_request.h"
+#include "ne_auth.h"
+#include "ne_string.h"
+#include "ne_utils.h"
+#include "ne_alloc.h"
+#include "ne_uri.h"
+#include "ne_i18n.h"
+
+/* TODO: should remove this eventually. Need it for
+ * ne_pull_request_body. */
+#include "ne_private.h"
+
+/* The MD5 digest of a zero-length entity-body */
+#define DIGEST_MD5_EMPTY "d41d8cd98f00b204e9800998ecf8427e"
+
+#define HOOK_SERVER_ID "http://webdav.org/neon/hooks/server-auth"
+#define HOOK_PROXY_ID "http://webdav.org/neon/hooks/proxy-auth"
+
+/* The authentication scheme we are using */
+typedef enum {
+    auth_scheme_basic,
+    auth_scheme_digest
+} auth_scheme;
+
+typedef enum { 
+    auth_alg_md5,
+    auth_alg_md5_sess,
+    auth_alg_unknown
+} auth_algorithm;
+
+/* Selected method of qop which the client is using */
+typedef enum {
+    auth_qop_none,
+    auth_qop_auth,
+    auth_qop_auth_int
+} auth_qop;
+
+/* A challenge */
+struct auth_challenge {
+    auth_scheme scheme;
+    char *realm;
+    char *domain;
+    char *nonce;
+    char *opaque;
+    unsigned int stale:1; /* if stale=true */
+    unsigned int got_qop:1; /* we were given a qop directive */
+    unsigned int qop_auth:1; /* "auth" token in qop attrib */
+    unsigned int qop_auth_int:1; /* "auth-int" token in qop attrib */
+    auth_algorithm alg;
+    struct auth_challenge *next;
+};
+
+static const char *qop_values[] = {
+    NULL,
+    "auth",
+    "auth-int"
+};
+static const char *algorithm_names[] = {
+    "MD5",
+    "MD5-sess",
+    NULL
+};
+
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be placed in malloc()-allocate
+ * memory.
+ * Must return:
+ *   0 on success, 
+ *  -1 to cancel.
+ */
+
+/* Authentication session state. */
+typedef struct {
+    ne_session *sess;
+
+    /* for making this proxy/server generic. */
+    const char *req_hdr, *resp_hdr, *resp_info_hdr, *fail_msg;
+    int status_code;
+    int fail_code;
+
+    /* The scheme used for this authentication session */
+    auth_scheme scheme;
+    /* The callback used to request new username+password */
+    ne_request_auth reqcreds;
+    void *reqcreds_udata;
+
+    /*** Session details ***/
+
+    /* The username and password we are using to authenticate with */
+    char *username;
+    /* Whether we CAN supply authentication at the moment */
+    unsigned int can_handle:1;
+    /* This used for Basic auth */
+    char *basic; 
+    /* These all used for Digest auth */
+    char *unq_realm;
+    char *unq_nonce;
+    char *unq_cnonce;
+    char *opaque;
+    /* A list of domain strings */
+    unsigned int domain_count;
+    char **domain;
+    auth_qop qop;
+    auth_algorithm alg;
+    int nonce_count;
+    /* The ASCII representation of the session's H(A1) value */
+    char h_a1[33];
+
+    /* Temporary store for half of the Request-Digest
+     * (an optimisation - used in the response-digest calculation) */
+    struct ne_md5_ctx stored_rdig;
+
+    /* Details of server... needed to reconstruct absoluteURI's when
+     * necessary */
+    const char *host;
+    const char *uri_scheme;
+    unsigned int port;
+
+} auth_session;
+
+struct auth_request {
+    auth_session *session;
+
+    /*** Per-request details. ***/
+    ne_request *request; /* the request object. */
+
+    /* The method and URI we are using for the current request */
+    const char *uri;
+    const char *method;
+    /* Whether we WILL supply authentication for this request or not */
+    unsigned int will_handle:1;
+
+    /* Used for calculation of H(entity-body) of the response */
+    struct ne_md5_ctx response_body;
+
+    int tries;
+    /* Results of response-header callbacks */
+    char *auth_hdr, *auth_info_hdr;
+};
+
+/* Private prototypes which used to be public. */
+
+static auth_session *auth_create(ne_session *sess,
+                                ne_request_auth callback, 
+                                void *userdata);
+
+/* Pass this the value of the "(Proxy,WWW)-Authenticate: " header field.
+ * Returns:
+ *   0 if we can now authenticate ourselves with the server.
+ *   non-zero if we can't
+ */
+static int auth_challenge(auth_session *sess, const char *value);
+
+/* If you receive a "(Proxy-)Authentication-Info:" header, pass its value to
+ * this function. Returns zero if this successfully authenticates
+ * the response as coming from the server, and false if it hasn't. */
+static int verify_response(struct auth_request *req, 
+                          auth_session *sess, const char *value);
+
+/* Private prototypes */
+static char *get_cnonce(void);
+static void clean_session(auth_session *sess);
+static int digest_challenge(auth_session *, struct auth_challenge *);
+static int basic_challenge(auth_session *, struct auth_challenge *);
+static char *request_digest(auth_session *sess, struct auth_request *req);
+static char *request_basic(auth_session *);
+
+/* Domain handling */
+static int is_in_domain(auth_session *sess, const char *uri);
+static int parse_domain(auth_session *sess, const char *domain);
+
+/* Get the credentials, passing a temporary store for the password value */
+static int get_credentials(auth_session *sess, char **password);
+
+auth_session *auth_create(ne_session *sess,
+                         ne_request_auth callback, 
+                         void *userdata) 
+{
+    auth_session *ret = ne_calloc(sizeof(auth_session));
+
+    ret->reqcreds = callback;
+    ret->reqcreds_udata = userdata;
+    ret->sess = sess;
+   
+    return ret;
+}
+
+#if 0
+void ne_auth_set_server(auth_session *sess, 
+                          const char *host, unsigned int port, const char *scheme)
+{
+    sess->host = host;
+    sess->port = port;
+    sess->req_scheme = scheme;
+}
+#endif
+
+static void clean_session(auth_session *sess) 
+{
+    sess->can_handle = 0;
+    NE_FREE(sess->basic);
+    NE_FREE(sess->unq_realm);
+    NE_FREE(sess->unq_nonce);
+    NE_FREE(sess->unq_cnonce);
+    NE_FREE(sess->opaque);
+    NE_FREE(sess->username);
+    if (sess->domain_count > 0) {
+       split_string_free(sess->domain);
+       sess->domain_count = 0;
+    }
+}
+
+/* Returns cnonce-value. We just use base64(time).
+ * TODO: Could improve this? */
+static char *get_cnonce(void) 
+{
+    char *ret, *tmp;
+    tmp = ne_rfc1123_date(time(NULL));
+    ret = ne_base64(tmp, strlen(tmp));
+    free(tmp);
+    return ret;
+}
+
+static int 
+get_credentials(auth_session *sess, char **password) 
+{
+    return (*sess->reqcreds)(sess->reqcreds_udata, sess->unq_realm,
+                            &sess->username, password);
+}
+
+static int parse_domain(auth_session *sess, const char *domain) {
+    char *unq, **doms;
+    int count, ret;
+
+    unq = shave_string(domain, '"');
+    doms = split_string_c(unq, ' ', NULL, " \r\n\t", &count);
+    if (doms != NULL) {
+       if (count > 0) {
+           sess->domain = doms;
+           sess->domain_count = count;
+           ret = 0;
+       } else {
+           free(doms);
+           ret = -1;
+       }
+    } else {
+       ret = -1;
+    }
+    free(unq);
+    return ret;
+}
+
+/* Returns:
+ *    0: if uri is in NOT in domain of session
+ * else: uri IS in domain of session (or no domain known)
+ */
+static int is_in_domain(auth_session *sess, const char *uri)
+{
+#if 1
+    return 1;
+#else
+    /* TODO: Need proper URI comparison for this to work. */
+    int ret, dom;
+    const char *abs_path;
+    if (sess->domain_count == 0) {
+       NE_DEBUG(NE_DBG_HTTPAUTH, "No domain, presuming okay.\n");
+       return 1;
+    }
+    ret = 0;
+    abs_path = uri_abspath(uri);
+    for (dom = 0; dom < sess->domain_count; dom++) {
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Checking domain: %s\n", sess->domain[dom]);
+       if (uri_childof(sess->domain[dom], abs_path)) {
+           ret = 1;
+           break;
+       }
+    }
+    return ret;
+#endif
+}
+
+/* Examine a Basic auth challenge.
+ * Returns 0 if an valid challenge, else non-zero. */
+static int 
+basic_challenge(auth_session *sess, struct auth_challenge *parms) 
+{
+    char *tmp, *password;
+
+    /* Verify challenge... must have a realm */
+    if (parms->realm == NULL) {
+       return -1;
+    }
+
+    NE_DEBUG(NE_DBG_HTTPAUTH, "Got Basic challenge with realm [%s]\n", 
+          parms->realm);
+
+    clean_session(sess);
+
+    sess->unq_realm = shave_string(parms->realm, '"');
+
+    if (get_credentials(sess, &password)) {
+       /* Failed to get credentials */
+       NE_FREE(sess->unq_realm);
+       return -1;
+    }
+
+    sess->scheme = auth_scheme_basic;
+
+    CONCAT3(tmp, sess->username, ":", password?password:"");
+    sess->basic = ne_base64(tmp, strlen(tmp));
+    free(tmp);
+
+    NE_FREE(password);
+
+    return 0;
+}
+
+/* Add Basic authentication credentials to a request */
+static char *request_basic(auth_session *sess) 
+{
+    char *buf;
+    CONCAT3(buf, "Basic ", sess->basic, "\r\n");
+    return buf;
+}
+
+/* Examine a digest challenge: return 0 if it is a valid Digest challenge,
+ * else non-zero. */
+static int digest_challenge(auth_session *sess,
+                           struct auth_challenge *parms) 
+{
+    struct ne_md5_ctx tmp;
+    unsigned char tmp_md5[16];
+    char *password;
+
+    /* Verify they've given us the right bits. */
+    if (parms->alg == auth_alg_unknown ||
+       ((parms->alg == auth_alg_md5_sess) &&
+        !(parms->qop_auth || parms->qop_auth_int)) ||
+       parms->realm==NULL || parms->nonce==NULL) {
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Invalid challenge.");
+       return -1;
+    }
+
+    if (parms->stale) {
+       /* Just a stale response, don't need to get a new username/password */
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Stale digest challenge.\n");
+    } else {
+       /* Forget the old session details */
+       NE_DEBUG(NE_DBG_HTTPAUTH, "In digest challenge.\n");
+
+       clean_session(sess);
+       sess->unq_realm = shave_string(parms->realm, '"');
+
+       /* Not a stale response: really need user authentication */
+       if (get_credentials(sess, &password)) {
+           /* Failed to get credentials */
+           NE_FREE(sess->unq_realm);
+           return -1;
+       }
+    }
+    sess->alg = parms->alg;
+    sess->scheme = auth_scheme_digest;
+    sess->unq_nonce = shave_string(parms->nonce, '"');
+    sess->unq_cnonce = get_cnonce();
+    if (parms->domain) {
+       if (parse_domain(sess, parms->domain)) {
+           /* TODO: Handle the error? */
+       }
+    } else {
+       sess->domain = NULL;
+       sess->domain_count = 0;
+    }
+    if (parms->opaque != NULL) {
+       sess->opaque = ne_strdup(parms->opaque); /* don't strip the quotes */
+    }
+    
+    if (parms->got_qop) {
+       /* What type of qop are we to apply to the message? */
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Got qop directive.\n");
+       sess->nonce_count = 0;
+       if (parms->qop_auth_int) {
+           sess->qop = auth_qop_auth_int;
+       } else {
+           sess->qop = auth_qop_auth;
+       }
+    } else {
+       /* No qop at all/ */
+       sess->qop = auth_qop_none;
+    }
+    
+    if (!parms->stale) {
+       /* Calculate H(A1).
+        * tmp = H(unq(username-value) ":" unq(realm-value) ":" passwd)
+        */
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Calculating H(A1).\n");
+       ne_md5_init_ctx(&tmp);
+       ne_md5_process_bytes(sess->username, strlen(sess->username), &tmp);
+       ne_md5_process_bytes(":", 1, &tmp);
+       ne_md5_process_bytes(sess->unq_realm, strlen(sess->unq_realm), &tmp);
+       ne_md5_process_bytes(":", 1, &tmp);
+       if (password != NULL)
+           ne_md5_process_bytes(password, strlen(password), &tmp);
+       ne_md5_finish_ctx(&tmp, tmp_md5);
+       if (sess->alg == auth_alg_md5_sess) {
+           unsigned char a1_md5[16];
+           struct ne_md5_ctx a1;
+           char tmp_md5_ascii[33];
+           /* Now we calculate the SESSION H(A1)
+            *    A1 = H(...above...) ":" unq(nonce-value) ":" unq(cnonce-value) 
+            */
+           ne_md5_to_ascii(tmp_md5, tmp_md5_ascii);
+           ne_md5_init_ctx(&a1);
+           ne_md5_process_bytes(tmp_md5_ascii, 32, &a1);
+           ne_md5_process_bytes(":", 1, &a1);
+           ne_md5_process_bytes(sess->unq_nonce, strlen(sess->unq_nonce), &a1);
+           ne_md5_process_bytes(":", 1, &a1);
+           ne_md5_process_bytes(sess->unq_cnonce, strlen(sess->unq_cnonce), &a1);
+           ne_md5_finish_ctx(&a1, a1_md5);
+           ne_md5_to_ascii(a1_md5, sess->h_a1);
+           NE_DEBUG(NE_DBG_HTTPAUTH, "Session H(A1) is [%s]\n", sess->h_a1);
+       } else {
+           ne_md5_to_ascii(tmp_md5, sess->h_a1);
+           NE_DEBUG(NE_DBG_HTTPAUTH, "H(A1) is [%s]\n", sess->h_a1);
+       }
+       
+       NE_FREE(password);
+    }
+    
+    NE_DEBUG(NE_DBG_HTTPAUTH, "I like this Digest challenge.\n");
+
+    return 0;
+}
+
+/* callback for ne_pull_request_body. */
+static int digest_body(void *userdata, const char *buf, size_t count)
+{
+    struct ne_md5_ctx *ctx = userdata;
+    ne_md5_process_bytes(buf, count, ctx);
+    return 0;
+}
+
+/* Return Digest authentication credentials header value for the given
+ * session. */
+static char *request_digest(auth_session *sess, struct auth_request *req) 
+{
+    struct ne_md5_ctx a2, rdig;
+    unsigned char a2_md5[16], rdig_md5[16];
+    char a2_md5_ascii[33], rdig_md5_ascii[33];
+    char nc_value[9] = {0};
+    const char *qop_value; /* qop-value */
+    ne_buffer *ret;
+
+    /* Increase the nonce-count */
+    if (sess->qop != auth_qop_none) {
+       sess->nonce_count++;
+       snprintf(nc_value, 9, "%08x", sess->nonce_count);
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Nonce count is %d, nc is [%s]\n", 
+              sess->nonce_count, nc_value);
+    }
+    qop_value = qop_values[sess->qop];
+
+    /* Calculate H(A2). */
+    ne_md5_init_ctx(&a2);
+    ne_md5_process_bytes(req->method, strlen(req->method), &a2);
+    ne_md5_process_bytes(":", 1, &a2);
+    ne_md5_process_bytes(req->uri, strlen(req->uri), &a2);
+    
+    if (sess->qop == auth_qop_auth_int) {
+       struct ne_md5_ctx body;
+       char tmp_md5_ascii[33];
+       unsigned char tmp_md5[16];
+       
+       ne_md5_init_ctx(&body);
+
+       /* Calculate H(entity-body): pull in the request body from
+        * where-ever it is coming from, and calculate the digest. */
+       
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting request body...\n");
+       ne_pull_request_body(req->request, digest_body, &body);
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting request body done.\n");
+               
+       ne_md5_finish_ctx(&body, tmp_md5);
+       ne_md5_to_ascii(tmp_md5, tmp_md5_ascii);
+
+       NE_DEBUG(NE_DBG_HTTPAUTH, "H(entity-body) is [%s]\n", tmp_md5_ascii);
+       
+       /* Append to A2 */
+       ne_md5_process_bytes(":", 1, &a2);
+       ne_md5_process_bytes(tmp_md5_ascii, 32, &a2);
+    }
+    ne_md5_finish_ctx(&a2, a2_md5);
+    ne_md5_to_ascii(a2_md5, a2_md5_ascii);
+    NE_DEBUG(NE_DBG_HTTPAUTH, "H(A2): %s\n", a2_md5_ascii);
+
+    NE_DEBUG(NE_DBG_HTTPAUTH, "Calculating Request-Digest.\n");
+    /* Now, calculation of the Request-Digest.
+     * The first section is the regardless of qop value
+     *     H(A1) ":" unq(nonce-value) ":" */
+    ne_md5_init_ctx(&rdig);
+
+    /* Use the calculated H(A1) */
+    ne_md5_process_bytes(sess->h_a1, 32, &rdig);
+
+    ne_md5_process_bytes(":", 1, &rdig);
+    ne_md5_process_bytes(sess->unq_nonce, strlen(sess->unq_nonce), &rdig);
+    ne_md5_process_bytes(":", 1, &rdig);
+    if (sess->qop != auth_qop_none) {
+       /* Add on:
+        *    nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":"
+        */
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Have qop directive, digesting: [%s:%s:%s]\n",
+              nc_value, sess->unq_cnonce, qop_value);
+       ne_md5_process_bytes(nc_value, 8, &rdig);
+       ne_md5_process_bytes(":", 1, &rdig);
+       ne_md5_process_bytes(sess->unq_cnonce, strlen(sess->unq_cnonce), &rdig);
+       ne_md5_process_bytes(":", 1, &rdig);
+       /* Store a copy of this structure (see note below) */
+       sess->stored_rdig = rdig;
+       ne_md5_process_bytes(qop_value, strlen(qop_value), &rdig);
+       ne_md5_process_bytes(":", 1, &rdig);
+    } else {
+       /* Store a copy of this structure... we do this because the
+        * calculation of the rspauth= field in the Auth-Info header 
+        * is the same as this digest, up to this point. */
+       sess->stored_rdig = rdig;
+    }
+    /* And finally, H(A2) */
+    ne_md5_process_bytes(a2_md5_ascii, 32, &rdig);
+    ne_md5_finish_ctx(&rdig, rdig_md5);
+    ne_md5_to_ascii(rdig_md5, rdig_md5_ascii);
+    
+    ret = ne_buffer_create();
+
+    ne_buffer_concat(ret, 
+                  "Digest username=\"", sess->username, "\", "
+                  "realm=\"", sess->unq_realm, "\", "
+                  "nonce=\"", sess->unq_nonce, "\", "
+                  "uri=\"", req->uri, "\", "
+                  "response=\"", rdig_md5_ascii, "\", "
+                  "algorithm=\"", algorithm_names[sess->alg], "\"", NULL);
+    
+    if (sess->opaque != NULL) {
+       /* We never unquote it, so it's still quoted here */
+       ne_buffer_concat(ret, ", opaque=", sess->opaque, NULL);
+    }
+
+    if (sess->qop != auth_qop_none) {
+       /* Add in cnonce and nc-value fields */
+       ne_buffer_concat(ret, 
+                      ", cnonce=\"", sess->unq_cnonce, "\", "
+                      "nc=", nc_value, ", "
+                      "qop=\"", qop_values[sess->qop], "\"", NULL);
+    }
+
+    ne_buffer_zappend(ret, "\r\n");
+    
+    NE_DEBUG(NE_DBG_HTTPAUTH, "Digest request header is %s\n", ret->data);
+
+    return ne_buffer_finish(ret);
+}
+
+/* Pass this the value of the 'Authentication-Info:' header field, if
+ * one is received.
+ * Returns:
+ *    0 if it gives a valid authentication for the server 
+ *    non-zero otherwise (don't believe the response in this case!).
+ */
+int verify_response(struct auth_request *req, auth_session *sess, const char *value) 
+{
+    char **pairs;
+    auth_qop qop = auth_qop_none;
+    char *nextnonce = NULL, /* for the nextnonce= value */
+       *rspauth = NULL, /* for the rspauth= value */
+       *cnonce = NULL, /* for the cnonce= value */
+       *nc = NULL, /* for the nc= value */
+       *unquoted, *qop_value = NULL;
+    int n, nonce_count, okay;
+    
+    if (!req->will_handle) {
+       /* Ignore it */
+       return 0;
+    }
+    
+    if (sess->scheme != auth_scheme_digest) {
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Found Auth-Info header not in response to Digest credentials - dodgy.\n");
+       return -1;
+    }
+    
+    NE_DEBUG(NE_DBG_HTTPAUTH, "Auth-Info header: %s\n", value);
+
+    pairs = pair_string(value, ',', '=', "\"'", " \r\n\t");
+    
+    for (n = 0; pairs[n]!=NULL; n+=2) {
+       unquoted = shave_string(pairs[n+1], '"');
+       if (strcasecmp(pairs[n], "qop") == 0) {
+           qop_value = ne_strdup(pairs[n+1]);
+           if (strcasecmp(pairs[n+1], "auth-int") == 0) {
+               qop = auth_qop_auth_int;
+           } else if (strcasecmp(pairs[n+1], "auth") == 0) {
+               qop = auth_qop_auth;
+           } else {
+               qop = auth_qop_none;
+           }
+       } else if (strcasecmp(pairs[n], "nextnonce") == 0) {
+           nextnonce = ne_strdup(unquoted);
+       } else if (strcasecmp(pairs[n], "rspauth") == 0) {
+           rspauth = ne_strdup(unquoted);
+       } else if (strcasecmp(pairs[n], "cnonce") == 0) {
+           cnonce = ne_strdup(unquoted);
+       } else if (strcasecmp(pairs[n], "nc") == 0) { 
+           nc = ne_strdup(pairs[n]);
+           if (sscanf(pairs[n+1], "%x", &nonce_count) != 1) {
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Couldn't scan [%s] for nonce count.\n",
+                      pairs[n+1]);
+           } else {
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Got nonce_count: %d\n", nonce_count);
+           }
+       }
+       free(unquoted);
+    }
+    pair_string_free(pairs);
+
+    /* Presume the worst */
+    okay = -1;
+
+    if ((qop != auth_qop_none) && (qop_value != NULL)) {
+       if ((rspauth == NULL) || (cnonce == NULL) || (nc == NULL)) {
+           NE_DEBUG(NE_DBG_HTTPAUTH, "Missing rspauth, cnonce or nc with qop.\n");
+       } else { /* Have got rspauth, cnonce and nc */
+           if (strcmp(cnonce, sess->unq_cnonce) != 0) {
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Response cnonce doesn't match.\n");
+           } else if (nonce_count != sess->nonce_count) { 
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Response nonce count doesn't match.\n");
+           } else {
+               /* Calculate and check the response-digest value.
+                * joe: IMO the spec is slightly ambiguous as to whether
+                * we use the qop which WE sent, or the qop which THEY
+                * sent...  */
+               struct ne_md5_ctx a2;
+               unsigned char a2_md5[16], rdig_md5[16];
+               char a2_md5_ascii[33], rdig_md5_ascii[33];
+
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Calculating response-digest.\n");
+
+               /* First off, H(A2) again. */
+               ne_md5_init_ctx(&a2);
+               ne_md5_process_bytes(":", 1, &a2);
+               ne_md5_process_bytes(req->uri, strlen(req->uri), &a2);
+               if (qop == auth_qop_auth_int) {
+                   unsigned char heb_md5[16];
+                   char heb_md5_ascii[33];
+                   /* Add on ":" H(entity-body) */
+                   ne_md5_finish_ctx(&req->response_body, heb_md5);
+                   ne_md5_to_ascii(heb_md5, heb_md5_ascii);
+                   ne_md5_process_bytes(":", 1, &a2);
+                   ne_md5_process_bytes(heb_md5_ascii, 32, &a2);
+                   NE_DEBUG(NE_DBG_HTTPAUTH, "Digested [:%s]\n", heb_md5_ascii);
+               }
+               ne_md5_finish_ctx(&a2, a2_md5);
+               ne_md5_to_ascii(a2_md5, a2_md5_ascii);
+               
+               /* We have the stored digest-so-far of 
+                *   H(A1) ":" unq(nonce-value) 
+                *        [ ":" nc-value ":" unq(cnonce-value) ] for qop
+                * in sess->stored_rdig, to save digesting them again.
+                *
+                */
+               if (qop != auth_qop_none) {
+                   /* Add in qop-value */
+                   NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting qop-value [%s:].\n", 
+                          qop_value);
+                   ne_md5_process_bytes(qop_value, strlen(qop_value), 
+                                      &sess->stored_rdig);
+                   ne_md5_process_bytes(":", 1, &sess->stored_rdig);
+               }
+               /* Digest ":" H(A2) */
+               ne_md5_process_bytes(a2_md5_ascii, 32, &sess->stored_rdig);
+               /* All done */
+               ne_md5_finish_ctx(&sess->stored_rdig, rdig_md5);
+               ne_md5_to_ascii(rdig_md5, rdig_md5_ascii);
+
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Calculated response-digest of: [%s]\n",
+                      rdig_md5_ascii);
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Given response-digest of:      [%s]\n",
+                      rspauth);
+
+               /* And... do they match? */
+               okay = (strcasecmp(rdig_md5_ascii, rspauth) == 0)?0:-1;
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Matched: %s\n", okay?"nope":"YES!");
+           }
+           free(rspauth);
+           free(cnonce);
+           free(nc);
+       }
+       free(qop_value);
+    } else {
+       NE_DEBUG(NE_DBG_HTTPAUTH, "No qop directive, auth okay.\n");
+       okay = 0;
+    }
+
+    /* Check for a nextnonce */
+    if (nextnonce != NULL) {
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Found nextnonce of [%s].\n", nextnonce);
+       if (sess->unq_nonce != NULL)
+           free(sess->unq_nonce);
+       sess->unq_nonce = nextnonce;
+    }
+
+    return okay;
+}
+
+/* A new challenge presented by the server */
+int auth_challenge(auth_session *sess, const char *value) 
+{
+    char **pairs, *pnt, *unquoted, *key;
+    struct auth_challenge *chall = NULL, *challenges = NULL;
+    int n, success;
+
+    NE_DEBUG(NE_DBG_HTTPAUTH, "Got new auth challenge: %s\n", value);
+
+    /* The header value may be made up of one or more challenges.
+     * We split it down into attribute-value pairs, then search for
+     * schemes in the pair keys.
+     */
+    pairs = pair_string(value, ',', '=', "\"'", " \r\n\t");
+
+    for (n = 0; pairs[n]!=NULL; n+=2) {
+       /* Look for an auth-scheme in the key */
+       pnt = strchr(pairs[n], ' ');
+       if (pnt != NULL) {
+           /* We have a new challenge */
+           NE_DEBUG(NE_DBG_HTTPAUTH, "New challenge.\n");
+           chall = ne_calloc(sizeof *chall);
+
+           chall->next = challenges;
+           challenges = chall;
+           /* Initialize the challenge parameters */
+           /* Which auth-scheme is it (case-insensitive matching) */
+           if (strncasecmp(pairs[n], "basic ", 6) == 0) {
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Basic scheme.\n");
+               chall->scheme = auth_scheme_basic;
+           } else if (strncasecmp(pairs[n], "digest ", 7) == 0) {
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Digest scheme.\n");
+               chall->scheme = auth_scheme_digest;
+           } else {
+               NE_DEBUG(NE_DBG_HTTPAUTH, "Unknown scheme.\n");
+               free(chall);
+               challenges = NULL;
+               break;
+           }
+           /* Now, the real key for this pair starts after the 
+            * auth-scheme... skipping whitespace */
+           while (strchr(" \r\n\t", *(++pnt)) != NULL)
+               /* nullop */;
+           key = pnt;
+       } else if (chall == NULL) {
+           /* If we haven't got an auth-scheme, and we're
+            * haven't yet found a challenge, skip this pair.
+            */
+           continue;
+       } else {
+           key = pairs[n];
+       }
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Got pair: [%s] = [%s]\n", key, pairs[n+1]);
+       /* Most values are quoted, so unquote them here */
+       unquoted = shave_string(pairs[n+1], '"');
+       /* Now parse the attribute */
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Unquoted pair is: [%s]\n", unquoted);
+       if (strcasecmp(key, "realm") == 0) {
+           chall->realm = pairs[n+1];
+       } else if (strcasecmp(key, "nonce") == 0) {
+           chall->nonce = pairs[n+1];
+       } else if (strcasecmp(key, "opaque") == 0) {
+           chall->opaque = pairs[n+1];
+       } else if (strcasecmp(key, "domain") == 0) {
+           chall->domain = pairs[n+1];
+       } else if (strcasecmp(key, "stale") == 0) {
+           /* Truth value */
+           chall->stale = 
+               (strcasecmp(unquoted, "true") == 0);
+       } else if (strcasecmp(key, "algorithm") == 0) {
+           if (strcasecmp(unquoted, "md5") == 0) {
+               chall->alg = auth_alg_md5;
+           } else if (strcasecmp(unquoted, "md5-sess") == 0) {
+               chall->alg = auth_alg_md5_sess;
+           } else {
+               chall->alg = auth_alg_unknown;
+           }
+       } else if (strcasecmp(key, "qop") == 0) {
+           char **qops;
+           int qop;
+           qops = split_string(unquoted, ',', NULL, " \r\n\t");
+           chall->got_qop = 1;
+           for (qop = 0; qops[qop] != NULL; qop++) {
+               if (strcasecmp(qops[qop], "auth") == 0) {
+                   chall->qop_auth = 1;
+               } else if (strcasecmp(qops[qop], "auth-int") == 0) {
+                   chall->qop_auth_int = 1;
+               }
+           }
+           split_string_free(qops);
+       }
+       free(unquoted);
+    }
+
+    NE_DEBUG(NE_DBG_HTTPAUTH, "Finished parsing parameters.\n");
+
+    /* Did we find any challenges */
+    if (challenges == NULL) {
+       pair_string_free(pairs);
+       return -1;
+    }
+    
+    success = 0;
+
+    NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for Digest challenges.\n");
+
+    /* Try a digest challenge */
+    for (chall = challenges; chall != NULL; chall = chall->next) {
+       if (chall->scheme == auth_scheme_digest) {
+           if (!digest_challenge(sess, chall)) {
+               success = 1;
+               break;
+           }
+       }
+    }
+
+    if (!success) {
+       NE_DEBUG(NE_DBG_HTTPAUTH, "No good Digest challenges, looking for Basic.\n");
+       for (chall = challenges; chall != NULL; chall = chall->next) {
+           if (chall->scheme == auth_scheme_basic) {
+               if (!basic_challenge(sess, chall)) {
+                   success = 1;
+                   break;
+               }
+           }
+       }
+
+       if (!success) {
+           /* No good challenges - record this in the session state */
+           NE_DEBUG(NE_DBG_HTTPAUTH, "Did not understand any challenges.\n");
+       }
+
+    }
+    
+    /* Remember whether we can now supply the auth details */
+    sess->can_handle = success;
+
+    while (challenges != NULL) {
+       chall = challenges->next;
+       free(challenges);
+       challenges = chall;
+    }
+
+    /* Free up the parsed header values */
+    pair_string_free(pairs);
+
+    return !success;
+}
+
+/* The body reader callback. */
+static void auth_body_reader(void *cookie, const char *block, size_t length)
+{
+    struct ne_md5_ctx *ctx = cookie;
+    NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting %d bytes of response body.\n", length);
+    ne_md5_process_bytes(block, length, ctx);
+}
+
+static void *ah_create(void *session, ne_request *request, const char *method,
+                      const char *uri)
+{
+    auth_session *sess = session;
+    struct auth_request *areq = ne_calloc(sizeof *areq);
+
+    NE_DEBUG(NE_DBG_HTTPAUTH, "ah_create, for %s\n", sess->resp_hdr);
+
+    areq->session = sess;
+    areq->method = method;
+    areq->uri = uri;
+    areq->request = request;
+
+    ne_add_response_header_handler(request, sess->resp_hdr,
+                                  ne_duplicate_header, &areq->auth_hdr);
+
+       
+    ne_add_response_header_handler(request, sess->resp_info_hdr,
+                                  ne_duplicate_header, 
+                                  &areq->auth_info_hdr);
+    
+    return areq;
+}
+
+
+static void ah_pre_send(void *cookie, ne_buffer *request)
+{
+    struct auth_request *req = cookie;
+    auth_session *sess = req->session;
+
+
+    if (!sess->can_handle) {
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Not handling session.\n");
+    } else if (!is_in_domain(sess, req->uri)) {
+       /* We have moved out of the authentication domain */
+       NE_DEBUG(NE_DBG_HTTPAUTH, 
+             "URI %s outside session domain, not handling.\n", req->uri);
+       req->will_handle = 0;
+    } else {
+       char *value;
+
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Handling.");
+       req->will_handle = 1;
+
+       if (sess->qop == auth_qop_auth_int) {
+           /* Digest mode / qop=auth-int: take an MD5 digest of the
+            * response body. */
+           ne_md5_init_ctx(&req->response_body);
+           ne_add_response_body_reader(req->request, ne_accept_always, 
+                                       auth_body_reader, &req->response_body);
+       }
+       
+       switch(sess->scheme) {
+       case auth_scheme_basic:
+           value = request_basic(sess);
+           break;
+       case auth_scheme_digest:
+           value = request_digest(sess, req);
+           break;
+       default:
+           value = NULL;
+           break;
+       }
+
+       if (value != NULL) {
+           ne_buffer_concat(request, sess->req_hdr, ": ", value, NULL);
+           free(value);
+       }
+
+       /* increase counter so we don't retry this >1. */
+       req->tries++;
+
+    }
+
+}
+
+#define SAFELY(x) ((x) != NULL?(x):"null")
+
+static int ah_post_send(void *cookie, const ne_status *status)
+{
+    struct auth_request *areq = cookie;
+    auth_session *sess = areq->session;
+    int ret = NE_OK;
+
+    NE_DEBUG(NE_DBG_HTTPAUTH, 
+         "ah_post_send (#%d), code is %d (want %d), %s is %s\n",
+         areq->tries, status->code, sess->status_code, 
+         SAFELY(sess->resp_hdr), SAFELY(areq->auth_hdr));
+    if (areq->auth_info_hdr != NULL && 
+       verify_response(areq, sess, areq->auth_info_hdr)) {
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Response authentication invalid.\n");
+       ne_set_error(sess->sess, sess->fail_msg);
+       ret = NE_ERROR;
+    } else if (status->code == sess->status_code && 
+              areq->auth_hdr != NULL && areq->tries == 0) {
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Got challenge with code %d.\n", status->code);
+       if (!auth_challenge(sess, areq->auth_hdr)) {
+           ret = NE_RETRY;
+       }
+    } else if (areq->tries > 0 && sess->status_code == status->code) {
+       NE_DEBUG(NE_DBG_HTTPAUTH, "Authentication failed - bad password?\n");
+       clean_session(sess);
+       ret = sess->fail_code;
+    }
+
+    NE_FREE(areq->auth_info_hdr);
+    NE_FREE(areq->auth_hdr);
+    
+    return ret;
+}
+
+static void ah_destroy(void *cookie)
+{
+    free(cookie);
+}
+
+static const ne_request_hooks ah_server_hooks = {
+    HOOK_SERVER_ID,
+    ah_create,
+    ah_pre_send,
+    ah_post_send,
+    ah_destroy
+};
+
+static const ne_request_hooks ah_proxy_hooks = {
+    HOOK_PROXY_ID,
+    ah_create,
+    ah_pre_send,
+    ah_post_send,
+    ah_destroy
+};
+
+static void free_auth(void *cookie)
+{
+    auth_session *sess = cookie;
+
+    clean_session(sess);
+    free(sess);
+}
+
+void ne_set_server_auth(ne_session *sess, ne_request_auth callback, 
+                         void *userdata)
+{
+    auth_session *auth_sess = auth_create(sess, callback, userdata);
+
+    /* Server auth details */
+    auth_sess->status_code = 401;
+    auth_sess->fail_code = NE_AUTH;
+    auth_sess->resp_hdr = "WWW-Authenticate";
+    auth_sess->resp_info_hdr = "Authentication-Info";
+    auth_sess->req_hdr = "Authorization";
+    auth_sess->fail_msg = _("Server was not authenticated correctly.");
+
+    ne_add_hooks(sess, &ah_server_hooks, auth_sess, free_auth);
+}
+
+void ne_set_proxy_auth(ne_session *sess, ne_request_auth callback, 
+                        void *userdata)
+{
+    auth_session *auth_sess = auth_create(sess, callback, userdata);
+
+    /* Proxy auth details */
+    auth_sess->status_code = 407;
+    auth_sess->fail_code = NE_PROXYAUTH;
+    auth_sess->resp_hdr = "Proxy-Authenticate";
+    auth_sess->resp_info_hdr = "Proxy-Authentication-Info";
+    auth_sess->req_hdr = "Proxy-Authorization";
+    auth_sess->fail_msg = _("Proxy server was not authenticated correctly.");
+
+    ne_add_hooks(sess, &ah_proxy_hooks, auth_sess, free_auth);
+}
+
+void ne_forget_auth(ne_session *sess)
+{
+    clean_session(ne_session_hook_private(sess, HOOK_SERVER_ID));
+    clean_session(ne_session_hook_private(sess, HOOK_PROXY_ID));
+}
+
diff --git a/neon/src/ne_auth.h b/neon/src/ne_auth.h
new file mode 100644 (file)
index 0000000..12be1ef
--- /dev/null
@@ -0,0 +1,53 @@
+/* 
+   HTTP authentication routines
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_AUTH_H
+#define NE_AUTH_H
+
+#include "ne_session.h" /* for http_session */
+
+BEGIN_NEON_DECLS
+
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be placed in malloc()-allocated
+ * memory.
+ * Must return:
+ *   0 on success: *username and *password must be non-NULL, and will
+ *                 be free'd by the HTTP layer when necessary
+ *  -1 to cancel (*username and *password are ignored.)
+ */
+typedef int (*ne_request_auth)(
+    void *userdata, const char *realm,
+    char **username, char **password);
+
+/* Set callbacks to handle server and proxy authentication.
+ * userdata is passed as the first argument to the callback. */
+void ne_set_server_auth(ne_session *sess, ne_request_auth callback, 
+                       void *userdata);
+void ne_set_proxy_auth(ne_session *sess, ne_request_auth callback, 
+                      void *userdata);
+
+/* Clear any stored authentication details for the given session. */
+void ne_forget_auth(ne_session *sess);
+
+END_NEON_DECLS
+
+#endif /* NE_AUTH_H */
diff --git a/neon/src/ne_basic.c b/neon/src/ne_basic.c
new file mode 100644 (file)
index 0000000..22ff2ee
--- /dev/null
@@ -0,0 +1,563 @@
+/* 
+   Basic HTTP and WebDAV methods
+   Copyright (C) 1999-2001, Joe Orton <joe@manyfish.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <errno.h>
+
+#include "ne_request.h"
+#include "ne_alloc.h"
+#include "ne_utils.h"
+#include "ne_basic.h"
+#include "ne_207.h"
+
+#ifndef NEON_NODAV
+#include "ne_uri.h"
+#endif
+
+#ifdef USE_DAV_LOCKS
+#include "ne_locks.h"
+#endif
+#include "ne_dates.h"
+#include "ne_i18n.h"
+
+/* Header parser to retrieve Last-Modified date */
+static void get_lastmodified(void *userdata, const char *value) {
+    time_t *modtime = userdata;
+    *modtime = ne_httpdate_parse(value);
+}
+
+int ne_getmodtime(ne_session *sess, const char *uri, time_t *modtime) 
+{
+    ne_request *req = ne_request_create(sess, "HEAD", uri);
+    int ret;
+
+    ne_add_response_header_handler(req, "Last-Modified", get_lastmodified,
+                                  modtime);
+
+    *modtime = -1;
+
+    ret = ne_request_dispatch(req);
+
+    if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+       *modtime = -1;
+       ret = NE_ERROR;
+    }
+
+    ne_request_destroy(req);
+
+    return ret;
+}
+
+/* PUT's from fd to URI */
+int ne_put(ne_session *sess, const char *uri, int fd) 
+{
+    ne_request *req = ne_request_create(sess, "PUT", uri);
+    int ret;
+    
+#ifdef USE_DAV_LOCKS
+    ne_lock_using_resource(req, uri, 0);
+    ne_lock_using_parent(req, uri);
+#endif
+
+    ne_set_request_body_fd(req, fd);
+       
+    ret = ne_request_dispatch(req);
+    
+    if (ret == NE_OK && ne_get_status(req)->klass != 2)
+       ret = NE_ERROR;
+
+    ne_request_destroy(req);
+
+    return ret;
+}
+
+/* Conditional HTTP put. 
+ * PUTs from fd to uri, returning NE_FAILED if resource as URI has
+ * been modified more recently than 'since'.
+ */
+int 
+ne_put_if_unmodified(ne_session *sess, const char *uri, int fd, 
+                    time_t since)
+{
+    ne_request *req;
+    char *date;
+    int ret;
+    
+    if (ne_version_pre_http11(sess)) {
+       time_t modtime;
+       /* Server is not minimally HTTP/1.1 compliant.  Do a HEAD to
+        * check the remote mod time. Of course, this makes the
+        * operation very non-atomic, but better than nothing. */
+       ret = ne_getmodtime(sess, uri, &modtime);
+       if (ret != NE_OK) return ret;
+       if (modtime != since)
+           return NE_FAILED;
+    }
+
+    req = ne_request_create(sess, "PUT", uri);
+
+    date = ne_rfc1123_date(since);
+    /* Add in the conditionals */
+    ne_add_request_header(req, "If-Unmodified-Since", date);
+    free(date);
+    
+#ifdef USE_DAV_LOCKS
+    ne_lock_using_resource(req, uri, 0);
+    /* FIXME: this will give 412 if the resource doesn't exist, since
+     * PUT may modify the parent... does that matter?  */
+#endif
+
+    ne_set_request_body_fd(req, fd);
+
+    ret = ne_request_dispatch(req);
+    
+    if (ret == NE_OK) {
+       if (ne_get_status(req)->code == 412) {
+           ret = NE_FAILED;
+       } else if (ne_get_status(req)->klass != 2) {
+           ret = NE_ERROR;
+       }
+    }
+
+    ne_request_destroy(req);
+
+    return ret;
+}
+
+struct get_context {
+    int error;
+    size_t total, progress;
+    int fd; /* used in get_to_fd */
+    ne_content_range *range;
+};
+
+int ne_read_file(ne_session *sess, const char *uri, 
+                  ne_block_reader reader, void *userdata) {
+    struct get_context ctx;
+    ne_request *req = ne_request_create(sess, "GET", uri);
+    int ret;
+
+    /* Read the value of the Content-Length header into ctx.total */
+    ne_add_response_header_handler(req, "Content-Length",
+                                  ne_handle_numeric_header, &ctx.total);
+    
+    ne_add_response_body_reader(req, ne_accept_2xx, reader, userdata);
+
+    ret = ne_request_dispatch(req);
+
+    if (ret == NE_OK && ne_get_status(req)->klass != 2)
+       ret = NE_ERROR;
+
+    ne_request_destroy(req);
+
+    return ret;
+}
+
+static void get_to_fd(void *userdata, const char *block, size_t length)
+{
+    struct get_context *ctx = userdata;
+    ssize_t ret;
+    
+    if (!ctx->error) {
+       while (length > 0) {
+           ret = write(ctx->fd, block, length);
+           if (ret < 0) {
+               ctx->error = errno;
+               break;
+           } else {
+               length -= ret;
+           }
+       }
+    }
+}
+
+static int accept_206(void *ud, ne_request *req, ne_status *st)
+{
+    return (st->code == 206);
+}
+
+static void clength_hdr_handler(void *ud, const char *value)
+{
+    struct get_context *ctx = ud;
+    off_t len = strtol(value, NULL, 10);
+    
+    if (ctx->range->end == -1) {
+       ctx->range->end = ctx->range->start + len - 1;
+       ctx->range->total = len;
+    }
+    else if (len != ctx->range->total) {
+       NE_DEBUG(NE_DBG_HTTP, 
+                "Expecting %ld bytes, got entity of length %ld\n", 
+                (long int) ctx->range->total, (long int) len);
+       ctx->error = 1;
+    }
+}
+
+static void content_range_hdr_handler(void *ud, const char *value)
+{
+    struct get_context *ctx = ud;
+
+    if (strncmp(value, "bytes ", 6) != 0) {
+       ctx->error = 1;
+    }
+
+    /* TODO: verify against requested range. */
+}
+
+int ne_get_range(ne_session *sess, const char *uri, 
+                ne_content_range *range, int fd)
+{
+    ne_request *req = ne_request_create(sess, "GET", uri);
+    struct get_context ctx;
+    int ret;
+
+    if (range->end == -1) {
+       ctx.total = -1;
+    } 
+    else {
+       ctx.total = (range->end - range->start) + 1;
+    }
+
+    ctx.fd = fd;
+    ctx.error = 0;
+    ctx.range = range;
+
+    ne_add_response_header_handler(req, "Content-Length",
+                                    clength_hdr_handler, &ctx);
+    ne_add_response_header_handler(req, "Content-Range",
+                                    content_range_hdr_handler,
+                                    &ctx);
+
+    ne_add_response_body_reader(req, accept_206, get_to_fd, &ctx);
+
+    /* icky casts to long int, which should be at least as large as the
+     * off_t's */
+    if (range->end == -1) {
+       ne_print_request_header(req, "Range", "bytes=%ld-", 
+                                 (long int) range->start);
+    }
+    else {
+       ne_print_request_header(req, "Range", "bytes=%ld-%ld",
+                                 (long int) range->start, 
+                                 (long int)range->end);
+    }
+    ne_add_request_header(req, "Accept-Ranges", "bytes");
+
+    ret = ne_request_dispatch(req);
+    
+    if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+       ret = NE_ERROR;
+    }
+    else if (ne_get_status(req)->code != 206) {
+       ne_set_error(sess, _("Server does not allow partial GETs."));
+       ret = NE_ERROR;
+    }
+    
+    ne_request_destroy(req);
+
+    return ret;
+}
+
+
+/* Get to given fd */
+int ne_get(ne_session *sess, const char *uri, int fd)
+{
+    ne_request *req = ne_request_create(sess, "GET", uri);
+    struct get_context ctx;
+    int ret;
+
+    ctx.total = -1;
+    ctx.progress = 0;
+    ctx.fd = fd;
+    ctx.error = 0;
+
+    /* Read the value of the Content-Length header into ctx.total */
+    ne_add_response_header_handler(req, "Content-Length",
+                                    ne_handle_numeric_header,
+                                    &ctx.total);
+    
+    ne_add_response_body_reader(req, ne_accept_2xx, get_to_fd, &ctx);
+
+    ret = ne_request_dispatch(req);
+    
+    if (ctx.error) {
+       char buf[BUFSIZ];
+       snprintf(buf, BUFSIZ, 
+                 _("Could not write to file: %s"), strerror(ctx.error));
+       ne_set_error(sess, buf);
+       ret = NE_ERROR;
+    }
+
+    if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+       ret = NE_ERROR;
+    }
+
+    ne_request_destroy(req);
+
+    return ret;
+}
+
+
+/* Get to given fd */
+int ne_post(ne_session *sess, const char *uri, int fd, const char *buffer)
+{
+    ne_request *req = ne_request_create(sess, "POST", uri);
+    struct get_context ctx;
+    int ret;
+
+    ctx.total = -1;
+    ctx.fd = fd;
+    ctx.error = 0;
+
+    /* Read the value of the Content-Length header into ctx.total */
+    ne_add_response_header_handler(req, "Content-Length",
+                                    ne_handle_numeric_header, &ctx.total);
+
+    ne_add_response_body_reader(req, ne_accept_2xx, get_to_fd, &ctx);
+
+    ne_set_request_body_buffer(req, buffer, strlen(buffer));
+
+    ret = ne_request_dispatch(req);
+    
+    if (ctx.error) {
+       char buf[BUFSIZ];
+       snprintf(buf, BUFSIZ, 
+                _("Could not write to file: %s"), strerror(ctx.error));
+       ne_set_error(sess, buf);
+       ret = NE_ERROR;
+    }
+
+    if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+       ret = NE_ERROR;
+    }
+
+    ne_request_destroy(req);
+
+    return ret;
+}
+
+static void server_hdr_handler(void *userdata, const char *value)
+{
+    char **tokens = split_string(value, ' ', "\"'", NULL);
+    ne_server_capabilities *caps = userdata;
+    int n;
+
+    for (n = 0; tokens[n] != NULL; n++) {
+       if (strncasecmp(tokens[n], "Apache/", 7) == 0 && 
+           strlen(tokens[n]) > 11) { /* 12 == "Apache/1.3.0" */
+           const char *ver = tokens[n] + 7;
+           int count;
+           char **vers;
+           vers = split_string_c(ver, '.', NULL, NULL, &count);
+           /* Apache/1.3.6 and before have broken Expect: 100 support */
+           if (count > 1 && atoi(vers[0]) < 2 && 
+               atoi(vers[1]) < 4 && atoi(vers[2]) < 7) {
+               caps->broken_expect100 = 1;
+           }
+           split_string_free(vers);
+       }
+    }    
+    
+    split_string_free(tokens);
+}
+
+void ne_content_type_handler(void *userdata, const char *value)
+{
+    ne_content_type *ct = userdata;
+    char *sep, *parms;
+
+    ct->value = ne_strdup(value);
+    
+    sep = strchr(ct->value, '/');
+    if (!sep) {
+       NE_FREE(ct->value);
+       return;
+    }
+
+    *++sep = '\0';
+    ct->type = ct->value;
+    ct->subtype = sep;
+    
+    parms = strchr(ct->value, ';');
+
+    if (parms) {
+       *parms = '\0';
+       /* TODO: handle charset. */
+    }
+}
+
+static void dav_hdr_handler(void *userdata, const char *value)
+{
+    char **classes, **class;
+    ne_server_capabilities *caps = userdata;
+    
+    classes = split_string(value, ',', "\"'", " \r\t\n");
+    for (class = classes; *class!=NULL; class++) {
+
+       if (strcmp(*class, "1") == 0) {
+           caps->dav_class1 = 1;
+       } else if (strcmp(*class, "2") == 0) {
+           caps->dav_class2 = 1;
+       } else if (strcmp(*class, "<http://apache.org/dav/propset/fs/1>") == 0) {
+           caps->dav_executable = 1;
+       }
+    }
+    
+    split_string_free(classes);
+
+}
+
+int ne_options(ne_session *sess, const char *uri,
+                 ne_server_capabilities *caps)
+{
+    ne_request *req = ne_request_create(sess, "OPTIONS", uri);
+    
+    int ret;
+
+    ne_add_response_header_handler(req, "Server", server_hdr_handler, caps);
+    ne_add_response_header_handler(req, "DAV", dav_hdr_handler, caps);
+
+    ret = ne_request_dispatch(req);
+    if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+       ret = NE_ERROR;
+    }
+    
+    ne_request_destroy(req);
+
+    return ret;
+}
+
+#ifndef NEON_NODAV
+
+void ne_add_depth_header(ne_request *req, int depth)
+{
+    const char *value;
+    switch(depth) {
+    case NE_DEPTH_ZERO:
+       value = "0";
+       break;
+    case NE_DEPTH_ONE:
+       value = "1";
+       break;
+    default:
+       value = "infinity";
+       break;
+    }
+    ne_add_request_header(req, "Depth", value);
+}
+
+static int copy_or_move(ne_session *sess, int is_move, int overwrite,
+                       const char *src, const char *dest ) 
+{
+    ne_request *req = ne_request_create( sess, is_move?"MOVE":"COPY", src );
+
+#ifdef USE_DAV_LOCKS
+    if (is_move) {
+       ne_lock_using_resource(req, src, NE_DEPTH_INFINITE);
+    }
+    ne_lock_using_resource(req, dest, NE_DEPTH_INFINITE);
+    /* And we need to be able to add members to the destination's parent */
+    ne_lock_using_parent(req, dest);
+#endif
+
+    ne_print_request_header(req, "Destination", "%s://%s%s", 
+                             ne_get_scheme(sess), 
+                             ne_get_server_hostport(sess), dest);
+    
+    ne_add_request_header(req, "Overwrite", overwrite?"T":"F");
+
+    return ne_simple_request(sess, req);
+}
+
+int ne_copy(ne_session *sess, int overwrite, 
+            const char *src, const char *dest) 
+{
+    return copy_or_move(sess, 0, overwrite, src, dest);
+}
+
+int ne_move(ne_session *sess, int overwrite,
+            const char *src, const char *dest) 
+{
+    return copy_or_move(sess, 1, overwrite, src, dest);
+}
+
+/* Deletes the specified resource. (and in only two lines of code!) */
+int ne_delete(ne_session *sess, const char *uri) 
+{
+    ne_request *req = ne_request_create(sess, "DELETE", uri);
+
+#ifdef USE_DAV_LOCKS
+    ne_lock_using_resource(req, uri, NE_DEPTH_INFINITE);
+    ne_lock_using_parent(req, uri);
+#endif
+    
+    /* joe: I asked on the DAV WG list about whether we might get a
+     * 207 error back from a DELETE... conclusion, you shouldn't if
+     * you don't send the Depth header, since we might be an HTTP/1.1
+     * client and a 2xx response indicates success to them.  But
+     * it's all a bit unclear. In any case, DAV servers today do
+     * return 207 to DELETE even if we don't send the Depth header.
+     * So we handle 207 errors appropriately. */
+
+    return ne_simple_request(sess, req);
+}
+
+int ne_mkcol(ne_session *sess, const char *uri) 
+{
+    ne_request *req;
+    char *real_uri;
+    int ret;
+
+    if (uri_has_trailing_slash(uri)) {
+       real_uri = ne_strdup(uri);
+    } else {
+       CONCAT2(real_uri, uri, "/");
+    }
+
+    req = ne_request_create(sess, "MKCOL", real_uri);
+
+#ifdef USE_DAV_LOCKS
+    ne_lock_using_resource(req, real_uri, 0);
+    ne_lock_using_parent(req, real_uri);
+#endif
+    
+    ret = ne_simple_request(sess, req);
+
+    free(real_uri);
+
+    return ret;
+}
+
+#endif /* NEON_NODAV */
diff --git a/neon/src/ne_basic.h b/neon/src/ne_basic.h
new file mode 100644 (file)
index 0000000..2daf919
--- /dev/null
@@ -0,0 +1,136 @@
+/* 
+   HTTP/1.1 methods
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_BASIC_H
+#define NE_BASIC_H
+
+#include <sys/types.h> /* for time_t */
+
+#include "ne_request.h"
+
+BEGIN_NEON_DECLS
+
+/* PUT resource at uri, reading request body from f */
+int ne_put(ne_session *sess, const char *uri, int fd);
+
+/* GET resource at uri, writing response body into f */
+int ne_get(ne_session *sess, const char *uri, int fd);
+
+#ifndef NEON_NODAV
+
+/* Basic WebDAV methods:
+ *   ne_copy:  copy resoure from src to dest
+ *   ne_move:  move resource from src to dest
+ *     -> if overwrite is non-zero, the destination resource
+ *     will be overwritten if it exists.
+ *   ne_delete: delete resource at uri
+ *   ne_mkcol: create a collection at uri (uri MUST have a trailing slash).
+ */
+int ne_copy(ne_session *sess, int overwrite, const char *src, const char *dest);
+int ne_move(ne_session *sess, int overwrite, const char *src, const char *dest);
+int ne_delete(ne_session *sess, const char *uri);
+int ne_mkcol(ne_session *sess, const char *uri);
+
+#define NE_DEPTH_ZERO (0)
+#define NE_DEPTH_ONE (1)
+#define NE_DEPTH_INFINITE (2)
+
+/* Adds a Depth: header to a request */
+void ne_add_depth_header(ne_request *req, int depth);
+
+#endif /* NEON_NODAV */
+
+/* PUT resource at uri as above, only if it has not been modified
+ * since given modtime. If server is HTTP/1.1, uses If-Unmodified-Since
+ * header; guaranteed failure if resource is modified after 'modtime'.
+ * If server is HTTP/1.0, HEAD's the resource first to fetch current
+ * modtime; race condition if resource is modified between HEAD and PUT.
+ */
+int ne_put_if_unmodified(ne_session *sess,
+                        const char *uri, int fd, time_t modtime);
+
+/* GET resource at uri, passing response body blocks to 'reader' */
+int ne_read_file(ne_session *sess, const char *uri, 
+                ne_block_reader reader, void *userdata);
+
+/* Retrieve modification time of resource at uri, place in *modtime.
+ * (uses HEAD) */
+int ne_getmodtime(ne_session *sess, const char *uri, time_t *modtime);
+
+typedef struct {
+    const char *type, *subtype;
+    const char *charset;
+    char *value;
+} ne_content_type;   
+
+/* Sets (*ne_content_type)userdata appropriately. 
+ * Caller must free ->value after use */
+void ne_content_type_handler(void *userdata, const char *value);
+
+/* Server capabilities: */
+typedef struct {
+    unsigned int broken_expect100:1; /* True if the server is known to
+                                     * have broken Expect:
+                                     * 100-continue support; Apache
+                                     * 1.3.6 and earlier. */
+
+    unsigned int dav_class1; /* True if Class 1 WebDAV server */
+    unsigned int dav_class2; /* True if Class 2 WebDAV server */
+    unsigned int dav_executable; /* True if supports the 'executable'
+                                 * property a. la. mod_dav */
+} ne_server_capabilities;
+
+/* Determines server capabilities (using OPTIONS). 
+ * Pass uri="*" to determine proxy server capabilities if using
+ * a proxy server. */
+int ne_options(ne_session *sess, const char *uri, 
+              ne_server_capabilities *caps);
+
+/* Defines a range of bytes, starting at 'start' and ending
+ * at 'end'.  'total' is the number of bytes in the range.
+ */
+typedef struct {
+    off_t start, end, total;
+} ne_content_range;
+
+/* Partial GET. range->start must be >= 0. range->total is ignored.
+ *
+ * If range->end is -1, then the rest of the resource from start is
+ * requested, and range->total and end are filled in on success.
+ *
+ * Otherwise, bytes from range->start to range->end are requested.
+ *
+ * This will write to the CURRENT position of f; so if you want
+ * to do a resume download, use:
+ *      struct ne_content_range range;
+ *      range.start = resume_from; 
+ *      range.end = range.start + 999;  (= 1000 bytes)
+ *      fseek(myfile, resume_from, SEEK_SET);
+ *      ne_get_range(sess, uri, &range, myfile); */
+int ne_get_range(ne_session *sess, const char *uri, 
+                ne_content_range *range, int fd);
+
+/* Post using buffer as request-body: stream response into f */
+int ne_post(ne_session *sess, const char *uri, int fd, const char *buffer);
+
+END_NEON_DECLS
+
+#endif /* NE_BASIC_H */
diff --git a/neon/src/ne_compress.c b/neon/src/ne_compress.c
new file mode 100644 (file)
index 0000000..6478fb9
--- /dev/null
@@ -0,0 +1,332 @@
+/* 
+   Handling of compressed HTTP responses
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <assert.h>
+
+#include "ne_request.h"
+#include "ne_compress.h"
+#include "ne_utils.h"
+
+#include <zlib.h>
+
+/* Adds support for the 'gzip' Content-Encoding in HTTP.  gzip is a
+ * file format which wraps the DEFLATE compression algorithm.  zlib
+ * implements DEFLATE: we have to unwrap the gzip format as it comes
+ * off the wire, and hand off chunks of data to be inflated. */
+
+struct ne_decompress_s {
+    ne_session *session; /* associated session. */
+    /* temporary buffer for holding inflated data. */
+    char outbuf[BUFSIZ];
+    z_stream zstr;
+    char *enchdr; /* value of Content-Enconding response header. */
+
+    /* pass blocks back to this. */
+    ne_block_reader reader;
+    void *userdata;
+
+    /* buffer for gzip header bytes. */
+    union {
+       unsigned char buf[10];
+       struct header {
+           unsigned char id1;
+           unsigned char id2;
+           unsigned char cmeth; /* compression method. */
+           unsigned char flags;
+           unsigned int mtime;
+           unsigned char xflags;
+           unsigned char os;
+       } hdr;
+    } in;
+    size_t incount;    /* bytes in in.buf */
+
+    /* current state. */
+    enum state {
+       BEFORE_DATA, /* not received any response blocks yet. */
+       PASSTHROUGH, /* response not compressed: passing through. */
+       IN_HEADER, /* received a few bytes of response data, but not
+                   * got past the gzip header yet. */
+       POST_HEADER, /* waiting for the end of the NUL-terminated bits. */
+       INFLATING, /* inflating response bytes. */
+       FINISHED, /* inflate has returned Z_STREAM_END: not expecting
+                  * any more response data. */
+       ERROR /* inflate bombed. */
+    } state;
+};
+
+#define ID1 0x1f
+#define ID2 0x8b
+
+#define HDR_DONE 0
+#define HDR_EXTENDED 1
+#define HDR_ERROR 2
+
+/* parse_header parses the gzip header, sets the next state and returns
+ *   HDR_DONE: all done, bytes following are raw DEFLATE data.
+ *   HDR_EXTENDED: all done, expect a NUL-termianted string
+ *                 before the DEFLATE data
+ *   HDR_ERROR: invalid header, give up.
+ */
+static int parse_header(ne_decompress *ctx)
+{
+    struct header *h = &ctx->in.hdr;
+
+    NE_DEBUG(NE_DBG_HTTP, "ID1: %d  ID2: %d, cmeth %d, flags %d\n", 
+           h->id1, h->id2, h->cmeth, h->flags);
+    
+    if (h->id1 != ID1 || h->id2 != ID2 || h->cmeth != 8) {
+       ctx->state = ERROR;
+       ne_set_error(ctx->session, "Compressed stream invalid");
+       return HDR_ERROR;
+    }
+
+    NE_DEBUG(NE_DBG_HTTP, "mtime: %d, xflags: %d, os: %d\n",
+            h->mtime, h->xflags, h->os);
+    
+    /* TODO: we can only handle one NUL-terminated extensions field
+     * currently.  Really, we should count the number of bits set, and
+     * skip as many fields as bits set (bailing if any reserved bits
+     * are set. */
+    if (h->flags == 8) {
+       ctx->state = POST_HEADER;
+       return HDR_EXTENDED;
+    } else if (h->flags != 0) {
+       ctx->state = ERROR;
+       ne_set_error(ctx->session, "Compressed stream not supported");
+       return HDR_ERROR;
+    }
+
+    NE_DEBUG(NE_DBG_HTTP, "compress: Good stream.\n");
+    
+    ctx->state = INFLATING;
+    return HDR_DONE;
+}
+
+static int find_token(const char *value, const char *wanted)
+{
+    char *buf = ne_strdup(value), *pnt = buf, *tok;
+    int ret = 0;
+
+    while ((tok = ne_token(&pnt, ',', NULL)) != NULL)
+    {
+       if (strcasecmp(tok, wanted) == 0) {
+           ret = 1;
+           break;
+       }
+    }
+
+    free(buf);
+    
+    return ret;
+}
+
+/* inflate it baby. */
+static void do_inflate(ne_decompress *ctx, const char *buf, size_t len)
+{
+    int ret;
+
+    ctx->zstr.avail_in = len;
+    ctx->zstr.next_in = (char *)buf;
+    ctx->zstr.total_in = 0;
+    
+    do {
+       ctx->zstr.avail_out = BUFSIZ;
+       ctx->zstr.next_out = ctx->outbuf;
+       ctx->zstr.total_out = 0;
+       
+       ret = inflate(&ctx->zstr, Z_NO_FLUSH);
+       
+       NE_DEBUG(NE_DBG_HTTP, 
+                "compress: inflate %d, %ld bytes out, %d remaining\n",
+                ret, ctx->zstr.total_out, ctx->zstr.avail_in);
+#if 0
+       NE_DEBUG(NE_DBG_HTTPBODY,
+                "Inflated body block (%ld):\n[%.*s]\n", 
+                ctx->zstr.total_out, (int)ctx->zstr.total_out, 
+                ctx->outbuf);
+#endif
+       
+       /* pass on the inflated data */
+       ctx->reader(ctx->userdata, ctx->outbuf, ctx->zstr.total_out);
+       
+    } while (ret == Z_OK && ctx->zstr.avail_out == 0);
+    
+    if (ret == Z_STREAM_END) {
+       NE_DEBUG(NE_DBG_HTTP, "compress: end of stream.\n");
+       ctx->state = FINISHED;
+    } else if (ret != Z_OK) {
+       ctx->state = ERROR;
+       ne_set_error(ctx->session, "Error reading compressed data.");
+       NE_DEBUG(NE_DBG_HTTP, "compress: inflate failed (%d): %s\n", 
+                ret, ctx->zstr.msg);
+    }
+}
+
+static void gz_reader(void *ud, const char *buf, size_t len)
+{
+    ne_decompress *ctx = ud;
+    const char *zbuf;
+    size_t count;
+
+    switch (ctx->state) {
+    case PASSTHROUGH:
+       /* move along there. */
+       ctx->reader(ctx->userdata, buf, len);
+       return;
+
+    case ERROR:
+       /* beyond hope. */
+       return;
+
+    case FINISHED:
+       NE_DEBUG(NE_DBG_HTTP, 
+                "compress: %d bytes in after end of stream.\n", len);
+       return;
+
+    case BEFORE_DATA:
+       /* work out whether this is a compressed response or not. */
+       if (ctx->enchdr != NULL && find_token(ctx->enchdr, "gzip")) {
+           NE_DEBUG(NE_DBG_HTTP, "compress: got gzipped stream.\n");
+
+           /* This is the magic bit: using plain inflateInit()
+            * doesn't work, and this does, but I have no idea why..
+            * Google showed me the way. */
+           if (inflateInit2(&ctx->zstr, -MAX_WBITS) != Z_OK) {
+               /* bugger. can't get to the session. */
+               ne_set_error(ctx->session, ctx->zstr.msg);
+               ctx->state = ERROR;
+               return;
+           }
+
+       } else {
+           /* No Content-Encoding header: pass it on.  TODO: we could
+            * hack it and register the real callback now. But that
+            * would require add_resp_body_rdr to have defined
+            * ordering semantics etc etc */
+           ctx->state = PASSTHROUGH;
+           ctx->reader(ctx->userdata, buf, len);
+           return;
+       }
+
+       ctx->state = IN_HEADER;
+       /* FALLTHROUGH */
+
+    case IN_HEADER:
+       /* copy as many bytes as possible into the buffer. */
+       if (len + ctx->incount > 10) {
+           count = 10 - ctx->incount;
+       } else {
+           count = len;
+       }
+       memcpy(ctx->in.buf + ctx->incount, buf, count);
+       ctx->incount += count;
+       /* have we got the full header yet? */
+       if (ctx->incount != 10) {
+           return;
+       }
+
+       buf += count;
+       len -= count;
+
+       switch (parse_header(ctx)) {
+       case HDR_ERROR:
+           return;
+       case HDR_EXTENDED:
+           if (len == 0)
+               return;
+           break;
+       case HDR_DONE:
+           if (len > 0) {
+               do_inflate(ctx, buf, len);
+           }
+           return;
+       }
+
+       /* FALLTHROUGH */
+
+    case POST_HEADER:
+       /* eating the filename string. */
+       zbuf = memchr(buf, '\0', len);
+       if (zbuf == NULL) {
+           /* not found it yet. */
+           return;
+       }
+
+       NE_DEBUG(NE_DBG_HTTP, "compresss: skipped %d header bytes.\n", 
+                zbuf - buf);
+       /* found end of string. */
+       len -= (1 + zbuf - buf);
+       buf = zbuf + 1;
+       ctx->state = INFLATING;
+       if (len == 0) {
+           /* end of string was at end of buffer. */
+           return;
+       }
+
+       /* FALLTHROUGH */
+
+    case INFLATING:
+       do_inflate(ctx, buf, len);
+       break;
+    }    
+    
+}
+
+int ne_decompress_destroy(ne_decompress *ctx)
+{
+    if (ctx->state != PASSTHROUGH && ctx->state != BEFORE_DATA) {
+       /* stream must have been initialized */
+       inflateEnd(&ctx->zstr);
+    }
+    if (ctx->enchdr)
+       free(ctx->enchdr);
+    free(ctx);
+    return (ctx->state == ERROR);
+}
+
+ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response acpt,
+                                   ne_block_reader rdr, void *userdata)
+{
+    ne_decompress *ctx = ne_calloc(sizeof *ctx);
+
+    ne_add_request_header(req, "Accept-Encoding", "gzip");
+
+    ne_add_response_header_handler(req, "Content-Encoding", 
+                                  ne_duplicate_header, &ctx->enchdr);
+
+    ne_add_response_body_reader(req, acpt, gz_reader, ctx);
+
+    ctx->state = BEFORE_DATA;
+    ctx->reader = rdr;
+    ctx->userdata = userdata;
+    ctx->session = ne_get_session(req);
+
+    return ctx;    
+}
diff --git a/neon/src/ne_compress.h b/neon/src/ne_compress.h
new file mode 100644 (file)
index 0000000..9fa6b73
--- /dev/null
@@ -0,0 +1,44 @@
+/* 
+   Compressed HTPT request/response Handling
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_COMPRESS_H
+#define NE_COMPRESS_H
+
+#include "ne_request.h"
+
+typedef struct ne_decompress_s ne_decompress;
+
+/* Call this to register a 'reader' callback which will be passed
+ * blocks of response body (if the 'acceptance' callback is
+ * successful).  If the response body is returned compressed by the
+ * server, this reader will receive UNCOMPRESSED blocks.
+ *
+ * Returns pointer to context object which must be passed to
+ * ne_decompress_destroy after the request has been dispatched, to
+ * free any internal state.  */
+ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response accpt,
+                                   ne_block_reader rdr, void *userdata);
+
+/* Free's up internal state. Returns non-zero if errors occured during
+ * decompression: the session error string will have the error. */
+int ne_decompress_destroy(ne_decompress *ctx);
+
+#endif /* NE_COMPRESS_H */
diff --git a/neon/src/ne_cookies.c b/neon/src/ne_cookies.c
new file mode 100644 (file)
index 0000000..4d99ef6
--- /dev/null
@@ -0,0 +1,142 @@
+/* 
+   Basic cookie support for neon
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+/* A nice demo of hooks, since it doesn't need any external
+ * interface to muck with the stored states.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <time.h>
+
+#include "ne_cookies.h"
+
+#include "ne_request.h"
+#include "ne_string.h"
+#include "ne_alloc.h"
+
+static void set_cookie_hdl(void *userdata, const char *value)
+{
+    char **pairs = pair_string(value, ';', '=', HTTP_QUOTES, HTTP_WHITESPACE);
+    ne_cookie *cook;
+    ne_cookie_cache *cache = userdata;
+    int n;
+
+    /* Check sanity */
+    if (pairs[0] == NULL || pairs[1] == NULL) {
+       /* yagaboo */
+       return;
+    }
+
+    NE_DEBUG(NE_DBG_HTTP, "Got cookie name=%s\n", pairs[0]);
+
+    /* Search for a cookie of this name */
+    NE_DEBUG(NE_DBG_HTTP, "Searching for existing cookie...\n");
+    for (cook = cache->cookies; cook != NULL; cook = cook->next) {
+       if (strcasecmp(cook->name, pairs[0]) == 0) {
+           break;
+       }
+    }
+    
+    if (cook == NULL) {
+       NE_DEBUG(NE_DBG_HTTP, "New cookie.\n");
+       cook = ne_malloc(sizeof(ne_cookie));
+       memset(cook, 0, sizeof(ne_cookie));
+       cook->name = pairs[0];
+       cook->next = cache->cookies;
+       cache->cookies = cook;
+    } else {
+       /* Free the old value */
+       free(cook->value);
+    }
+
+    cook->value = pairs[1];
+
+    for (n = 2; pairs[n] != NULL; n+=2) {
+       NE_DEBUG(NE_DBG_HTTP, "Cookie parm %s=%s\n", pairs[n], pairs[n+1]);
+       if (strcasecmp(pairs[n], "path") == 0) {
+           cook->path = pairs[n+1];
+           pairs[n+1] = NULL;
+       } else if (strcasecmp(pairs[n], "max-age") == 0) {
+           int t = atoi(pairs[n+1]);
+           cook->expiry = time(NULL) + (time_t)t;
+       } else if (strcasecmp(pairs[n], "domain") == 0) {
+           cook->domain = pairs[n+1];
+           pairs[n+1] = NULL;
+       }
+    }
+
+    NE_DEBUG(NE_DBG_HTTP, "End of parms.\n");
+
+    pair_string_free(pairs);
+}
+
+static void *create(void *session, ne_request *req, const char *method, const char *uri)
+{
+    ne_cookie_cache *cache = session;
+    ne_add_response_header_handler(req, "Set-Cookie", set_cookie_hdl, cache);
+    return cache;
+}
+
+/* Just before sending the request: let them add headers if they want */
+static void pre_send(void *private, ne_buffer *request)
+{
+    ne_cookie_cache *cache = private;
+    ne_cookie *cook;
+    
+    if (cache->cookies == NULL) {
+       return;
+    }
+    
+    ne_buffer_zappend(request, "Cookie: ");
+
+    for (cook = cache->cookies; cook != NULL; cook=cook->next) {
+       ne_buffer_concat(request, cook->name, "=", cook->value, NULL);
+       if (cook->next != NULL) {
+           ne_buffer_zappend(request, "; ");
+       }
+    }
+    
+    ne_buffer_zappend(request, EOL);
+    
+}
+
+static void destroy(void *private)
+{
+    /* FIXME */
+    return;
+}
+
+ne_request_hooks ne_cookie_hooks = {
+    "http://www.webdav.org/neon/hooks/cookies",
+    create,
+    pre_send,
+    NULL,
+    destroy
+};
diff --git a/neon/src/ne_cookies.h b/neon/src/ne_cookies.h
new file mode 100644 (file)
index 0000000..0025ac3
--- /dev/null
@@ -0,0 +1,50 @@
+/* 
+   HTTP Request Handling
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_COOKIES_H
+#define NE_COOKIES_H
+
+#include "ne_request.h"
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+struct ne_cookie_s;
+typedef struct ne_cookie_s ne_cookie;
+
+struct ne_cookie_s {
+    char *name, *value;
+    unsigned int secure:1;
+    unsigned int discard:1;
+    char *domain, *path;
+    time_t expiry; /* time at which the cookie expires */
+    ne_cookie *next;
+};
+
+typedef struct {
+    ne_cookie *cookies;
+} ne_cookie_cache;
+
+extern ne_request_hooks ne_cookie_hooks;
+
+END_NEON_DECLS
+
+#endif /* NE_COOKIES_H */
diff --git a/neon/src/ne_dates.c b/neon/src/ne_dates.c
new file mode 100644 (file)
index 0000000..34f3539
--- /dev/null
@@ -0,0 +1,191 @@
+/* 
+   Date manipulation routines
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#include <time.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SNPRINTF_H
+#include "snprintf.h"
+#endif
+
+#include "ne_alloc.h"
+#include "ne_dates.h"
+
+/* Generic date manipulation routines. */
+
+/* RFC1123: Sun, 06 Nov 1994 08:49:37 GMT */
+#define RFC1123_FORMAT "%3s, %02d %3s %4d %02d:%02d:%02d GMT"
+/* RFC850:  Sunday, 06-Nov-94 08:49:37 GMT */
+#define RFC1036_FORMAT "%s, %2d-%3s-%2d %2d:%2d:%2d GMT"
+/* asctime: Wed Jun 30 21:49:08 1993 */
+#define ASCTIME_FORMAT "%3s %3s %2d %2d:%2d:%2d %4d"
+
+static const char *rfc1123_weekdays[7] = { 
+    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 
+};
+static const char *short_months[12] = { 
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* Returns the time/date GMT, in RFC1123-type format: eg
+ *  Sun, 06 Nov 1994 08:49:37 GMT. */
+char *ne_rfc1123_date(time_t anytime) {
+    struct tm *gmt;
+    char *ret;
+    gmt = gmtime(&anytime);
+    if (gmt == NULL)
+       return NULL;
+    ret = ne_malloc(29 + 1); /* dates are 29 chars long */
+/*  it goes: Sun, 06 Nov 1994 08:49:37 GMT */
+    snprintf(ret, 30, RFC1123_FORMAT,
+             rfc1123_weekdays[gmt->tm_wday], gmt->tm_mday, 
+             short_months[gmt->tm_mon], 1900 + gmt->tm_year, 
+             gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
+    
+    return ret;
+}
+
+/* Takes an RFC1123-formatted date string and returns the time_t.
+ * Returns (time_t)-1 if the parse fails. */
+time_t ne_rfc1123_parse(const char *date) 
+{
+    struct tm gmt = {0};
+    static char wkday[4], mon[4];
+    int n;
+/*  it goes: Sun, 06 Nov 1994 08:49:37 GMT */
+    n = sscanf(date, RFC1123_FORMAT,
+           wkday, &gmt.tm_mday, mon, &gmt.tm_year, &gmt.tm_hour,
+           &gmt.tm_min, &gmt.tm_sec);
+    /* Is it portable to check n==7 here? */
+    gmt.tm_year -= 1900;
+    for (n=0; n<12; n++)
+       if (strcmp(mon, short_months[n]) == 0)
+           break;
+    /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+     * since the mktime will then fail */
+    gmt.tm_mon = n;
+    gmt.tm_isdst = -1;
+    return mktime(&gmt);
+}
+
+/* Takes a string containing a RFC1036-style date and returns the time_t */
+time_t ne_rfc1036_parse(const char *date) 
+{
+    struct tm gmt = {0};
+    int n;
+    static char wkday[10], mon[4];
+    /* RFC850/1036 style dates: Sunday, 06-Nov-94 08:49:37 GMT */
+    n = sscanf(date, RFC1036_FORMAT,
+               wkday, &gmt.tm_mday, mon, &gmt.tm_year,
+               &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec);
+    /* portable to check n here? */
+    for (n=0; n<12; n++)
+       if (strcmp(mon, short_months[n]) == 0)
+           break;
+    /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+     * since the mktime will then fail */
+    gmt.tm_mon = n;
+    gmt.tm_isdst = -1;
+    return mktime(&gmt);
+}
+
+
+/* (as)ctime dates are like:
+ *    Wed Jun 30 21:49:08 1993
+ */
+time_t ne_asctime_parse(const char *date) 
+{
+    struct tm gmt = {0};
+    int n;
+    static char wkday[4], mon[4];
+    n = sscanf(date, ASCTIME_FORMAT,
+               wkday, mon, &gmt.tm_mday, 
+               &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec,
+               &gmt.tm_year);
+    /* portable to check n here? */
+    for (n=0; n<12; n++)
+       if (strcmp(mon, short_months[n]) == 0)
+           break;
+    /* tm_mon comes out as 12 if the month is corrupt, which is desired,
+     * since the mktime will then fail */
+    gmt.tm_mon = n;
+    gmt.tm_isdst = -1;
+    return mktime(&gmt);
+}
+
+/* HTTP-date parser */
+time_t ne_httpdate_parse(const char *date)
+{
+    time_t tmp;
+    tmp = ne_rfc1123_parse(date);
+    if (tmp == -1) {
+        tmp = ne_rfc1036_parse(date);
+       if (tmp == -1)
+           tmp = ne_asctime_parse(date);
+    }
+    return tmp;
+}
+
+#undef RFC1036_FORMAT
+#undef ASCTIME_FORMAT
+#undef RFC1123_FORMAT
+
+#ifdef RFC1123_TEST
+
+int main(int argc, char **argv) {
+    time_t now, in;
+    char *out;
+    if (argc > 1) {
+       printf("Got: %s\n", argv[1]);
+       in = ne_rfc1123_parse(argv[1]);
+       printf("Parsed: %d\n", in);
+       out = ne_rfc1123_date(in);
+       printf("Back again: %s\n", out);
+    } else {
+       now = time(NULL);
+       out = ne_rfc1123_date(now);
+       in = ne_rfc1123_parse(out);
+       printf("time(NULL) = %d\n", now);
+       printf("RFC1123 Time: [%s]\n", out);
+       printf("Parsed = %d\n", in);
+       out = ne_rfc1123_date(in);
+       printf("Back again: [%s]\n", out);
+    }
+    return 0;
+}
+
+#endif
+
+
diff --git a/neon/src/ne_dates.h b/neon/src/ne_dates.h
new file mode 100644 (file)
index 0000000..7016300
--- /dev/null
@@ -0,0 +1,49 @@
+/* 
+   Date manipulation routines
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef DATES_H
+#define DATES_H
+
+#include <sys/types.h>
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+/* Date manipulation routines as per RFC1123 and RFC1036 */
+
+/* Return current date/time in RFC1123 format */
+char *ne_rfc1123_date(time_t anytime);
+
+/* Returns time from date/time in RFC1123 format */
+time_t ne_rfc1123_parse(const char *date);
+
+time_t ne_rfc1036_parse(const char *date);
+
+/* Parses asctime date string */
+time_t ne_asctime_parse(const char *date);
+
+/* Parse an HTTP-date as per RFC2616 */
+time_t ne_httpdate_parse(const char *date);
+
+END_NEON_DECLS
+
+#endif /* DATES_H */
diff --git a/neon/src/ne_defs.h b/neon/src/ne_defs.h
new file mode 100644 (file)
index 0000000..f029edf
--- /dev/null
@@ -0,0 +1,10 @@
+
+#undef BEGIN_NEON_DECLS
+#undef END_NEON_DECLS
+#ifdef __cplusplus
+# define BEGIN_NEON_DECLS extern "C" {
+# define END_NEON_DECLS }
+#else
+# define BEGIN_NEON_DECLS /* empty */
+# define END_NEON_DECLS /* empty */
+#endif
diff --git a/neon/src/ne_i18n.c b/neon/src/ne_i18n.c
new file mode 100644 (file)
index 0000000..a0df0ac
--- /dev/null
@@ -0,0 +1,32 @@
+/* 
+   Internationalization of neon
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+void neon_i18n_init(void)
+{
+#if defined(ENABLE_NLS) && defined(NEON_IS_LIBRARY)
+    /* if neon is build bundled in (i.e., not as a standalone
+     * library), then there is probably no point in this, since the
+     * messages won't be pointing in the right direction.
+     * there's not really any point in doing this if neon is
+     * a library since the messages aren't i18n'ized, but... */
+    bindtextdomain("neon", LOCALEDIR);
+#endif
+}
diff --git a/neon/src/ne_i18n.h b/neon/src/ne_i18n.h
new file mode 100644 (file)
index 0000000..c1770e9
--- /dev/null
@@ -0,0 +1,37 @@
+/* 
+   Internationalization of neon
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NEON_I18N_H
+#define NEON_I18N_H
+
+#undef _
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(str) gettext(str)
+#else
+#define _(str) (str)
+#endif /* ENABLE_NLS */
+#define N_(str) (str)
+
+/* Initialize i18n in neon */
+void neon_i18n_init(void);
+
+#endif /* NEON_I18N_H */
diff --git a/neon/src/ne_locks.c b/neon/src/ne_locks.c
new file mode 100644 (file)
index 0000000..b6f4686
--- /dev/null
@@ -0,0 +1,618 @@
+/* 
+   WebDAV Class 2 locking operations
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ctype.h> /* for isdigit() */
+
+#include "ne_alloc.h"
+
+#include "ne_request.h"
+#include "ne_xml.h"
+#include "ne_locks.h"
+#include "ne_uri.h"
+#include "ne_basic.h"
+#include "ne_props.h"
+#include "ne_207.h"
+#include "ne_i18n.h"
+
+#define HOOK_ID "http://webdav.org/neon/hooks/webdav-locking"
+
+/* The list of locks to submit in an If header 
+ * for the current requests */
+struct submit_locks {
+    const struct ne_lock *lock;
+    const char *uri;
+    struct submit_locks *next;
+};
+
+struct ne_lock_session_s {
+    struct ne_lock *locks;
+};
+
+/* Per-request lock structure */
+struct request_locks {
+    struct submit_locks *locks; /* for the If header */
+    ne_lock_session *session;
+};
+
+/* Context for PROPFIND/lockdiscovery callbacks */
+struct discover_ctx {
+    ne_lock_result results;
+    void *userdata;
+};
+
+/* Hook callbacks */
+static void *create(void *session, ne_request *req, 
+                   const char *method, const char *uri);
+static void pre_send(void *private, ne_buffer *req);
+static void destroy(void *private);
+
+/* The hooks are needed for construction of the If header */
+static ne_request_hooks lock_hooks = {
+    HOOK_ID, /* unique id for the locking hooks */
+    create,
+    pre_send,
+    NULL, /* post_send not needed */
+    destroy
+};
+
+/* Element ID's start at HIP_ELM_UNUSED and work upwards */
+
+#define NE_ELM_LOCK_FIRST (NE_ELM_207_UNUSED)
+
+#define NE_ELM_lockdiscovery (NE_ELM_LOCK_FIRST)
+#define NE_ELM_activelock (NE_ELM_LOCK_FIRST + 1)
+#define NE_ELM_lockscope (NE_ELM_LOCK_FIRST + 2)
+#define NE_ELM_locktype (NE_ELM_LOCK_FIRST + 3)
+#define NE_ELM_depth (NE_ELM_LOCK_FIRST + 4)
+#define NE_ELM_owner (NE_ELM_LOCK_FIRST + 5)
+#define NE_ELM_timeout (NE_ELM_LOCK_FIRST + 6)
+#define NE_ELM_locktoken (NE_ELM_LOCK_FIRST + 7)
+#define NE_ELM_lockinfo (NE_ELM_LOCK_FIRST + 8)
+#define NE_ELM_write (NE_ELM_LOCK_FIRST + 9)
+#define NE_ELM_exclusive (NE_ELM_LOCK_FIRST + 10)
+#define NE_ELM_shared (NE_ELM_LOCK_FIRST + 11)
+
+static const struct ne_xml_elm lock_elms[] = {
+#define A(x) { "DAV:", #x, NE_ELM_ ## x, NE_XML_COLLECT } /* ANY */
+#define D(x) { "DAV:", #x, NE_ELM_ ## x, 0 }               /* normal */
+#define C(x) { "DAV:", #x, NE_ELM_ ## x, NE_XML_CDATA }   /* (#PCDATA) */
+#define E(x) { "DAV:", #x, NE_ELM_ ## x, 0 /* LEAF */ }    /* EMPTY */
+    D(lockdiscovery), D(activelock),
+    D(prop),
+    D(lockscope), D(locktype), C(depth), A(owner), C(timeout), D(locktoken),
+    /* no lockentry */
+    D(lockinfo), D(lockscope), D(locktype),
+    E(write), E(exclusive), E(shared),
+    C(href),
+#undef A
+#undef D
+#undef C
+#undef E
+    { NULL, 0, 0 }
+};
+
+static const ne_propname lock_props[] = {
+    { "DAV:", "lockdiscovery" },
+    { NULL }
+};
+
+static void *create(void *session, ne_request *req, 
+                   const char *method, const char *uri)
+{
+    struct request_locks *rl = ne_calloc(sizeof *rl);
+    rl->session = session;
+    return rl;
+}
+
+static void pre_send(void *private, ne_buffer *req)
+{
+    struct request_locks *rl = private;
+    
+    if (rl->locks != NULL) {
+       struct submit_locks *item;
+
+       /* Add in the If header */
+       ne_buffer_zappend(req, "If:");
+       for (item = rl->locks; item != NULL; item = item->next) {
+           ne_buffer_concat(req, " <", item->lock->uri, "> (<",
+                          item->lock->token, ">)", NULL);
+       }
+       ne_buffer_zappend(req, EOL);
+    }
+}
+
+static void destroy(void *priv)
+{
+    struct request_locks *rl = priv;
+    struct submit_locks *lock, *next;
+    
+    for (lock = rl->locks; lock != NULL; lock = next) {
+       next = lock->next;
+       free(lock);
+    }
+    
+    free(rl);
+}
+
+static void free_locks(void *session)
+{
+    ne_lock_session *sess = session;
+    struct ne_lock *lk = sess->locks;
+
+    while (lk) {
+       struct ne_lock *nextlk = lk->next;
+       ne_lock_free(lk);
+       lk = nextlk;
+    }  
+
+    free(sess);
+}
+
+ne_lock_session *ne_lock_register(ne_session *sess)
+{
+    ne_lock_session *locksess = ne_calloc(sizeof *locksess);
+
+    /* Register the hooks */
+    ne_add_hooks(sess, &lock_hooks, locksess, free_locks);
+    
+    return locksess;
+}
+
+/* Submit the given lock for the given URI */
+static void submit_lock(struct request_locks *rl, struct ne_lock *lock, 
+                       const char *uri)
+{
+    struct submit_locks *slock;
+
+    /* Check for dups */
+    for (slock = rl->locks; slock != NULL; slock = slock->next) {
+       if (strcasecmp(slock->lock->token, lock->token) == 0)
+           return;
+    }
+
+    slock = ne_calloc(sizeof *slock);
+    slock->lock = lock;
+    slock->uri = uri;
+    slock->next = rl->locks;
+    rl->locks = slock;
+}
+
+struct ne_lock *ne_lock_find(ne_lock_session *sess, const char *uri)
+{
+    struct ne_lock *cur;
+    for (cur = sess->locks; cur != NULL; cur = cur->next) {
+       if (uri_compare(uri, cur->uri) == 0) 
+           return cur;
+    }
+    return NULL;
+}
+
+void ne_lock_using_parent(ne_request *req, const char *uri)
+{
+    struct request_locks *rl = ne_request_hook_private(req, HOOK_ID);
+    char *parent;
+
+    if (rl == NULL)
+       return; 
+
+    parent = uri_parent(uri);
+
+    if (parent != NULL) {
+       struct ne_lock *lock;
+       /* Find any locks on the parent resource.
+        * FIXME: should check for depth-infinity locks too. */
+       lock = ne_lock_find(rl->session, parent);
+       if (lock) {
+           NE_DEBUG(NE_DBG_LOCKS, "Locked parent, %s on %s\n", lock->token, 
+                 lock->uri);
+           submit_lock(rl, lock, uri);
+       }
+       free(parent);
+    }
+}
+
+int ne_lock_iterate(ne_lock_session *sess, 
+                    ne_lock_walkfunc func, void *userdata)
+{
+    struct ne_lock *lock;
+    int count = 0;
+
+    for (lock = sess->locks; lock != NULL; lock = lock->next) {
+       if (func != NULL) {
+           (*func)(lock, userdata);
+       }
+       count++;
+    }
+    
+    return count;
+}
+
+void ne_lock_using_resource(ne_request *req, const char *uri, int depth)
+{
+    /* Grab the private cookie for this request */
+    struct request_locks *rl = ne_request_hook_private(req, HOOK_ID);
+    struct ne_lock *lock; /* all the known locks */
+    int match;
+
+    if (rl == NULL)
+       return; 
+
+    /* Iterate over the list of session locks to see if any of
+     * them apply to this resource */
+    for (lock = rl->session->locks; lock != NULL; lock = lock->next) {
+       
+       match = 0;
+       
+       if (depth == NE_DEPTH_INFINITE && uri_childof(uri, lock->uri)) {
+           /* Case 1: this is a depth-infinity request which will 
+            * modify a lock somewhere inside the collection. */
+           NE_DEBUG(NE_DBG_LOCKS, "Has child: %s\n", lock->token);
+           match = 1;
+       } 
+       else if (uri_compare(uri, lock->uri) == 0) {
+           /* Case 2: this request is directly on a locked resource */
+           NE_DEBUG(NE_DBG_LOCKS, "Has direct lock: %s\n", lock->token);
+           match = 1;
+       }
+       else if (lock->depth == NE_DEPTH_INFINITE && 
+                uri_childof(lock->uri, uri)) {
+           /* Case 3: there is a higher-up infinite-depth lock which
+            * covers the resource that this request will modify. */
+           NE_DEBUG(NE_DBG_LOCKS, "Is child of: %s\n", lock->token);
+           match = 1;
+       }
+       
+       if (match) {
+           submit_lock(rl, lock, uri);
+       }
+    }
+
+}
+
+void ne_lock_add(ne_lock_session *sess, struct ne_lock *lock)
+{
+    if (sess->locks != NULL) {
+       sess->locks->prev = lock;
+    }
+    lock->prev = NULL;
+    lock->next = sess->locks;
+    sess->locks = lock;
+}
+
+void ne_lock_remove(ne_lock_session *sess, struct ne_lock *lock)
+{
+    if (lock->prev != NULL) {
+       lock->prev->next = lock->next;
+    } else {
+       sess->locks = lock->next;
+    }
+    if (lock->next != NULL) {
+       lock->next->prev = lock->prev;
+    }
+}
+
+struct ne_lock *ne_lock_copy(const struct ne_lock *lock)
+{
+    struct ne_lock *ret = ne_calloc(sizeof *ret);
+
+    /* TODO: check whether uri or token are NULL? Or, maybe, might as
+     * well crash now if they are, since something else will later
+     * on. */
+    ret->uri = ne_strdup(lock->uri);
+    ret->depth = lock->depth;
+    ret->type = lock->type;
+    ret->scope = lock->scope;
+    ret->token = ne_strdup(lock->token);
+    if (lock->owner) ret->owner = ne_strdup(lock->owner);
+    ret->timeout = lock->timeout;
+
+    return ret;
+}
+
+void ne_lock_free(struct ne_lock *lock)
+{
+    NE_FREE(lock->uri);
+    NE_FREE(lock->owner);
+    NE_FREE(lock->token);
+    free(lock);
+}
+
+int ne_unlock(ne_session *sess, struct ne_lock *lock)
+{
+    ne_request *req = ne_request_create(sess, "UNLOCK", lock->uri);
+    int ret;
+    
+    ne_print_request_header(req, "Lock-Token", "<%s>", lock->token);
+    
+    /* TODO: need this or not?
+     * it definitely goes away if lock-null resources go away */
+    ne_lock_using_parent(req, lock->uri);
+
+    ret = ne_request_dispatch(req);
+    
+    if (ret == NE_OK && ne_get_status(req)->klass == 2) {
+       ret = NE_OK;    
+    } else {
+       ret = NE_ERROR;
+    }
+
+    ne_request_destroy(req);
+    
+    return ret;
+}
+
+static int check_context(ne_xml_elmid parent, ne_xml_elmid child) {
+    NE_DEBUG(NE_DBG_XML, "ne_locks: check_context %d in %d\n", child, parent);
+    switch (parent) {
+    case NE_ELM_root:
+       /* TODO: for LOCK requests only...
+        * shouldn't allow this for PROPFIND really */
+       if (child == NE_ELM_prop)
+           return NE_XML_VALID;
+       break;      
+    case NE_ELM_prop:
+       if (child == NE_ELM_lockdiscovery)
+           return NE_XML_VALID;
+       break;
+    case NE_ELM_lockdiscovery:
+       if (child == NE_ELM_activelock)
+           return NE_XML_VALID;
+       break;
+    case NE_ELM_activelock:
+       switch (child) {
+       case NE_ELM_lockscope:
+       case NE_ELM_locktype:
+       case NE_ELM_depth:
+       case NE_ELM_owner:
+       case NE_ELM_timeout:
+       case NE_ELM_locktoken:
+           return NE_XML_VALID;
+       default:
+           break;
+       }
+       break;
+    case NE_ELM_lockscope:
+       switch (child) {
+       case NE_ELM_exclusive:
+       case NE_ELM_shared:
+           return NE_XML_VALID;
+       default:
+           break;
+       }
+    case NE_ELM_locktype:
+       if (child == NE_ELM_write)
+           return NE_XML_VALID;
+       break;
+       /* ... depth is PCDATA, owner is COLLECT, timeout is PCDATA */
+    case NE_ELM_locktoken:
+       if (child == NE_ELM_href)
+           return NE_XML_VALID;
+       break;
+    }
+    return NE_XML_DECLINE;
+}
+
+static int parse_depth(const char *depth) {
+    if (strcasecmp(depth, "infinity") == 0) {
+       return NE_DEPTH_INFINITE;
+    } else if (isdigit(depth[0])) {
+       return atoi(depth);
+    } else {
+       return -1;
+    }
+}
+
+static long parse_timeout(const char *timeout) {
+    if (strcasecmp(timeout, "infinite") == 0) {
+       return NE_TIMEOUT_INFINITE;
+    } else if (strncasecmp(timeout, "Second-", 7) == 0) {
+       long to = strtol(timeout, NULL, 10);
+       if (to == LONG_MIN || to == LONG_MAX)
+           return NE_TIMEOUT_INVALID;
+       return to;
+    } else {
+       return NE_TIMEOUT_INVALID;
+    }
+}
+
+static void discover_results(void *userdata, const char *href,
+                            const ne_prop_result_set *set)
+{
+    struct discover_ctx *ctx = userdata;
+    struct ne_lock *lock = ne_propset_private(set);
+
+    if (lock == NULL)
+       return;
+
+    lock->uri = ne_strdup(href);
+
+    ctx->results(ctx->userdata, lock, 
+                href, ne_propset_status(set, &lock_props[0]));
+
+    
+    ne_lock_free(lock);
+
+    NE_DEBUG(NE_DBG_LOCKS, "End of response for %s\n", href);
+}
+
+static int 
+end_element_common(struct ne_lock *l, const struct ne_xml_elm *elm,
+                  const char *cdata)
+{
+    switch (elm->id){ 
+    case NE_ELM_write:
+       l->type = ne_locktype_write;
+       break;
+    case NE_ELM_exclusive:
+       l->scope = ne_lockscope_exclusive;
+       break;
+    case NE_ELM_shared:
+       l->scope = ne_lockscope_shared;
+       break;
+    case NE_ELM_depth:
+       NE_DEBUG(NE_DBG_LOCKS, "Got depth: %s\n", cdata);
+       l->depth = parse_depth(cdata);
+       if (l->depth == -1) {
+           return -1;
+       }
+       break;
+    case NE_ELM_timeout:
+       NE_DEBUG(NE_DBG_LOCKS, "Got timeout: %s\n", cdata);
+       l->timeout = parse_timeout(cdata);
+       if (l->timeout == NE_TIMEOUT_INVALID) {
+           return -1;
+       }
+       break;
+    case NE_ELM_owner:
+       l->owner = strdup(cdata);
+       break;
+    case NE_ELM_href:
+       l->token = strdup(cdata);
+       break;
+    }
+    return 0;
+}
+
+/* End-element handler for lock discovery PROPFIND response */
+static int 
+end_element_ldisc(void *userdata, const struct ne_xml_elm *elm, 
+                 const char *cdata) 
+{
+    struct ne_lock *lock = ne_propfind_current_private(userdata);
+
+    return end_element_common(lock, elm, cdata);
+}
+
+/* End-element handler for LOCK response */
+static int
+end_element_lock(void *userdata, const struct ne_xml_elm *elm, 
+                const char *cdata)
+{
+    struct ne_lock *lock = userdata;
+    return end_element_common(lock, elm, cdata);
+}
+
+static void *create_private(void *userdata, const char *uri)
+{
+    return ne_calloc(sizeof(struct ne_lock));
+}
+
+/* Discover all locks on URI */
+int ne_lock_discover(ne_session *sess, const char *uri, 
+                     ne_lock_result callback, void *userdata)
+{
+    ne_propfind_handler *handler;
+    struct discover_ctx ctx = {0};
+    int ret;
+    
+    ctx.results = callback;
+    ctx.userdata = userdata;
+
+    handler = ne_propfind_create(sess, uri, NE_DEPTH_ZERO);
+
+    ne_propfind_set_private(handler, create_private, NULL);
+    
+    ne_xml_push_handler(ne_propfind_get_parser(handler), lock_elms, 
+                        check_context, NULL, end_element_ldisc, handler);
+    
+    ret = ne_propfind_named(handler, lock_props, discover_results, &ctx);
+    
+    ne_propfind_destroy(handler);
+
+    return ret;
+}
+
+int ne_lock(ne_session *sess, struct ne_lock *lock) 
+{
+    ne_request *req = ne_request_create(sess, "LOCK", lock->uri);
+    ne_buffer *body = ne_buffer_create();
+    ne_xml_parser *parser = ne_xml_create();
+    int ret, parse_failed;
+
+    ne_xml_push_handler(parser, lock_elms, check_context, 
+                        NULL, end_element_lock, lock);
+    
+    /* Create the body */
+    ne_buffer_concat(body, "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL
+                   "<lockinfo xmlns='DAV:'>" EOL " <lockscope>",
+                   lock->scope==ne_lockscope_exclusive?
+                   "<exclusive/>":"<shared/>",
+                   "</lockscope>" EOL
+                   "<locktype><write/></locktype>", NULL);
+
+    if (lock->owner) {
+       ne_buffer_concat(body, "<owner>", lock->owner, "</owner>" EOL, NULL);
+    }
+    ne_buffer_zappend(body, "</lockinfo>" EOL);
+
+    ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
+    ne_add_response_body_reader(req, ne_accept_2xx, 
+                                 ne_xml_parse_v, parser);
+    ne_add_request_header(req, "Content-Type", "text/xml");
+    ne_add_depth_header(req, lock->depth);
+
+    /* TODO: 
+     * By 2518, we need this only if we are creating a lock-null resource.
+     * Since we don't KNOW whether the lock we're given is a lock-null
+     * or not, we cover our bases.
+     */
+    ne_lock_using_parent(req, lock->uri);
+    /* This one is clearer from 2518 sec 8.10.4. */
+    ne_lock_using_resource(req, lock->uri, lock->depth);
+
+    ret = ne_request_dispatch(req);
+
+    ne_buffer_destroy(body);
+    parse_failed = !ne_xml_valid(parser);
+    
+    if (ret == NE_OK && ne_get_status(req)->klass == 2) {
+       if (parse_failed) {
+           ret = NE_ERROR;
+           ne_set_error(sess, ne_xml_get_error(parser));
+       }
+       else if (ne_get_status(req)->code == 207) {
+           ret = NE_ERROR;
+           /* TODO: set the error string appropriately */
+       }
+    } else {
+       ret = NE_ERROR;
+    }
+
+    ne_request_destroy(req);
+    ne_xml_destroy(parser);
+
+    /* TODO: free the list */
+    return ret;
+}
diff --git a/neon/src/ne_locks.h b/neon/src/ne_locks.h
new file mode 100644 (file)
index 0000000..12199cd
--- /dev/null
@@ -0,0 +1,126 @@
+/* 
+   WebDAV Class 2 locking operations
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_LOCKS_H
+#define NE_LOCKS_H
+
+#include "ne_request.h" /* for ne_session + http_req */
+
+BEGIN_NEON_DECLS
+
+/* The scope of a lock */
+enum ne_lock_scope {
+    ne_lockscope_exclusive,
+    ne_lockscope_shared
+};
+
+/* Lock type. Only write locks are defined in RFC2518. */
+enum ne_lock_type {
+    ne_locktype_write
+};
+
+/* A lock object. Lock objects are kept on doubly-linked lists. */
+struct ne_lock {
+    char *uri; /* the URI which this lock covers. */
+    int depth; /* the depth of the lock (NE_DEPTH_*). */
+    enum ne_lock_type type;
+    enum ne_lock_scope scope;
+    char *token; /* the lock token: uniquely identifies this lock. */
+    char *owner; /* string describing the owner of the lock. */
+    long timeout; /* timeout in seconds. (or NE_TIMEOUT_*) */
+    struct ne_lock *next, *prev;
+};
+/* NB: struct ne_lock Would be typedef'ed to ne_lock except lock is
+ * a verb and a noun, so we already have ne_lock the function. Damn
+ * the English language. */
+
+#define NE_TIMEOUT_INFINITE -1
+#define NE_TIMEOUT_INVALID -2
+
+typedef struct ne_lock_session_s ne_lock_session;
+
+/* TODO: 
+ * "session" is a bad word, and it's already used for ne_session,
+ * maybe think up a better name. lock_store is quite good.
+ */
+
+/* Register the locking hooks with an ne_session.  Owned locks
+ * persist for the duration of this session. The lock session lasts
+ * exactly as long as the corresponding ne_session. Once you call
+ * ne_session_destroy(sess), any use of the lock session has
+ * undefined results.  */
+ne_lock_session *ne_lock_register(ne_session *sess);
+
+/* Add a lock to the given session. The lock will subsequently be
+ * submitted as required in an If: header with any requests created
+ * using the ne_session which the lock session is tied to.  Requests
+ * indicate to the locking layer which locks they might need using
+ * ne_lock_using_*, as described below. */
+void ne_lock_add(ne_lock_session *sess, struct ne_lock *lock);
+
+/* Remove lock, which must have been previously added to the
+ * session using 'ne_lock_add' above. */
+void ne_lock_remove(ne_lock_session *sess, struct ne_lock *lock);
+
+typedef void (*ne_lock_walkfunc)(struct ne_lock *lock, void *userdata);
+
+/* For each lock added to the session, call func, passing the lock
+ * and the given userdata. Returns the number of locks. func may be
+ * pass as NULL, in which case, can be used to simply return number
+ * of locks in the session. */
+int ne_lock_iterate(ne_lock_session *sess, 
+                   ne_lock_walkfunc func, void *userdata);
+
+/* Issue a LOCK request for the given lock. */
+int ne_lock(ne_session *sess, struct ne_lock *lock);
+/* Issue an UNLOCK request for the given lock */
+int ne_unlock(ne_session *sess, struct ne_lock *lock);
+
+/* Find a lock in the session with given URI */
+struct ne_lock *ne_lock_find(ne_lock_session *sess, const char *uri);
+
+/* Deep-copy a lock structure. */
+struct ne_lock *ne_lock_copy(const struct ne_lock *lock);
+
+/* Free a lock structure */
+void ne_lock_free(struct ne_lock *lock);
+
+/* Callback for lock discovery.  If 'lock' is NULL, 
+ * something went wrong retrieving lockdiscover for the resource,
+ * look at 'status' for the details. */
+typedef void (*ne_lock_result)(void *userdata, const struct ne_lock *lock, 
+                              const char *uri, const ne_status *status);
+
+/* Perform lock discovery on the given URI.  'result' is called
+ * with the results (possibly >1 times).  */
+int ne_lock_discover(ne_session *sess, const char *uri, 
+                    ne_lock_result result, void *userdata);
+
+/*** For use by method functions */
+
+/* Indicate that this request is of depth n on given uri */
+void ne_lock_using_resource(ne_request *req, const char *uri, int depth);
+/* Indicate that this request will modify parent collection of given URI */
+void ne_lock_using_parent(ne_request *req, const char *uri);
+
+END_NEON_DECLS
+
+#endif /* NE_LOCKS_H */
diff --git a/neon/src/ne_md5.c b/neon/src/ne_md5.c
new file mode 100644 (file)
index 0000000..208d831
--- /dev/null
@@ -0,0 +1,460 @@
+/* md5.c - Functions to compute MD5 message digest of files or memory blocks
+   according to the definition of MD5 in RFC 1321 from April 1992.
+   Copyright (C) 1995, 1996, 1997 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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.  */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+# include <string.h>
+#else
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+# ifndef HAVE_MEMCPY
+#  define memcpy(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include <ctype.h> /* for tolower */
+
+#include "ne_md5.h"
+
+#ifdef _LIBC
+# include <endian.h>
+# if __BYTE_ORDER == __BIG_ENDIAN
+#  define WORDS_BIGENDIAN 1
+# endif
+#endif
+
+#define md5_init_ctx ne_md5_init_ctx
+#define md5_process_block ne_md5_process_block
+#define md5_process_bytes ne_md5_process_bytes
+#define md5_finish_ctx ne_md5_finish_ctx
+#define md5_read_ctx ne_md5_read_ctx
+#define md5_stream ne_md5_stream
+#define md5_buffer ne_md5_buffer
+#define md5_ctx ne_md5_ctx
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n)                                                       \
+    (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#else
+# define SWAP(n) (n)
+#endif
+
+
+/* This array contains the bytes used to pad the buffer to the next
+   64-byte boundary.  (RFC 1321, 3.1: Step 1)  */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */ };
+
+
+/* Initialize structure containing state of computation.
+   (RFC 1321, 3.3: Step 3)  */
+void
+md5_init_ctx (ctx)
+     struct md5_ctx *ctx;
+{
+  ctx->A = 0x67452301;
+  ctx->B = 0xefcdab89;
+  ctx->C = 0x98badcfe;
+  ctx->D = 0x10325476;
+
+  ctx->total[0] = ctx->total[1] = 0;
+  ctx->buflen = 0;
+}
+
+/* Put result from CTX in first 16 bytes following RESBUF.  The result
+   must be in little endian byte order.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+void *
+md5_read_ctx (ctx, resbuf)
+     const struct md5_ctx *ctx;
+     void *resbuf;
+{
+  ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
+  ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
+  ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
+  ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
+
+  return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+   prolog according to the standard and write the result to RESBUF.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+void *
+md5_finish_ctx (ctx, resbuf)
+     struct md5_ctx *ctx;
+     void *resbuf;
+{
+  /* Take yet unprocessed bytes into account.  */
+  md5_uint32 bytes = ctx->buflen;
+  size_t pad;
+
+  /* Now count remaining bytes.  */
+  ctx->total[0] += bytes;
+  if (ctx->total[0] < bytes)
+    ++ctx->total[1];
+
+  pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+  memcpy (&ctx->buffer[bytes], fillbuf, pad);
+
+  /* Put the 64-bit file length in *bits* at the end of the buffer.  */
+  *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
+  *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
+                                                       (ctx->total[0] >> 29));
+
+  /* Process last bytes.  */
+  md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
+
+  return md5_read_ctx (ctx, resbuf);
+}
+
+/* Compute MD5 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 16 bytes
+   beginning at RESBLOCK.  */
+int
+md5_stream (stream, resblock)
+     FILE *stream;
+     void *resblock;
+{
+  /* Important: BLOCKSIZE must be a multiple of 64.  */
+#define BLOCKSIZE 4096
+  struct md5_ctx ctx;
+  char buffer[BLOCKSIZE + 72];
+  size_t sum;
+
+  /* Initialize the computation context.  */
+  md5_init_ctx (&ctx);
+
+  /* Iterate over full file contents.  */
+  while (1)
+    {
+      /* We read the file in blocks of BLOCKSIZE bytes.  One call of the
+        computation function processes the whole buffer so that with the
+        next round of the loop another block can be read.  */
+      size_t n;
+      sum = 0;
+
+      /* Read block.  Take care for partial reads.  */
+      do
+       {
+         n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+         sum += n;
+       }
+      while (sum < BLOCKSIZE && n != 0);
+      if (n == 0 && ferror (stream))
+        return 1;
+
+      /* If end of file is reached, end the loop.  */
+      if (n == 0)
+       break;
+
+      /* Process buffer with BLOCKSIZE bytes.  Note that
+                       BLOCKSIZE % 64 == 0
+       */
+      md5_process_block (buffer, BLOCKSIZE, &ctx);
+    }
+
+  /* Add the last bytes if necessary.  */
+  if (sum > 0)
+    md5_process_bytes (buffer, sum, &ctx);
+
+  /* Construct result in desired memory.  */
+  md5_finish_ctx (&ctx, resblock);
+  return 0;
+}
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+void *
+md5_buffer (buffer, len, resblock)
+     const char *buffer;
+     size_t len;
+     void *resblock;
+{
+  struct md5_ctx ctx;
+
+  /* Initialize the computation context.  */
+  md5_init_ctx (&ctx);
+
+  /* Process whole buffer but last len % 64 bytes.  */
+  md5_process_bytes (buffer, len, &ctx);
+
+  /* Put result in desired memory area.  */
+  return md5_finish_ctx (&ctx, resblock);
+}
+
+
+void
+md5_process_bytes (buffer, len, ctx)
+     const void *buffer;
+     size_t len;
+     struct md5_ctx *ctx;
+{
+  /* When we already have some bits in our internal buffer concatenate
+     both inputs first.  */
+  if (ctx->buflen != 0)
+    {
+      size_t left_over = ctx->buflen;
+      size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+      memcpy (&ctx->buffer[left_over], buffer, add);
+      ctx->buflen += add;
+
+      if (left_over + add > 64)
+       {
+         md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx);
+         /* The regions in the following copy operation cannot overlap.  */
+         memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+                 (left_over + add) & 63);
+         ctx->buflen = (left_over + add) & 63;
+       }
+
+      buffer = (const char *) buffer + add;
+      len -= add;
+    }
+
+  /* Process available complete blocks.  */
+  if (len > 64)
+    {
+      md5_process_block (buffer, len & ~63, ctx);
+      buffer = (const char *) buffer + (len & ~63);
+      len &= 63;
+    }
+
+  /* Move remaining bytes in internal buffer.  */
+  if (len > 0)
+    {
+      memcpy (ctx->buffer, buffer, len);
+      ctx->buflen = len;
+    }
+}
+
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+   and defined in the RFC 1321.  The first function is a little bit optimized
+   (as found in Colin Plumbs public domain implementation).  */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+   It is assumed that LEN % 64 == 0.  */
+
+void
+md5_process_block (buffer, len, ctx)
+     const void *buffer;
+     size_t len;
+     struct md5_ctx *ctx;
+{
+  md5_uint32 correct_words[16];
+  const unsigned char *words = buffer;
+  const unsigned char *endp = words + len;
+  md5_uint32 A = ctx->A;
+  md5_uint32 B = ctx->B;
+  md5_uint32 C = ctx->C;
+  md5_uint32 D = ctx->D;
+
+  /* First increment the byte count.  RFC 1321 specifies the possible
+     length of the file up to 2^64 bits.  Here we only compute the
+     number of bytes.  Do a double word increment.  */
+  ctx->total[0] += len;
+  if (ctx->total[0] < len)
+    ++ctx->total[1];
+
+  /* Process all bytes in the buffer with 64 bytes in each round of
+     the loop.  */
+  while (words < endp)
+    {
+      md5_uint32 *cwp = correct_words;
+      md5_uint32 A_save = A;
+      md5_uint32 B_save = B;
+      md5_uint32 C_save = C;
+      md5_uint32 D_save = D;
+
+      /* First round: using the given function, the context and a constant
+        the next context is computed.  Because the algorithms processing
+        unit is a 32-bit word and it is determined to work on words in
+        little endian byte order we perhaps have to change the byte order
+        before the computation.  To reduce the work for the next steps
+        we store the swapped words in the array CORRECT_WORDS.  */
+
+#define OP(a, b, c, d, s, T)                                           \
+      do                                                               \
+        {                                                              \
+         md5_uint32 WORD_ = (md5_uint32)words[0] | ((md5_uint32)words[1] << 8) \
+              | ((md5_uint32)words[2] << 16) | ((md5_uint32)words[3] << 24); \
+         a += FF (b, c, d) + (*cwp++ = WORD_) + T;             \
+         words += 4;                                                   \
+         CYCLIC (a, s);                                                \
+         a += b;                                                       \
+        }                                                              \
+      while (0)
+
+      /* It is unfortunate that C does not provide an operator for
+        cyclic rotation.  Hope the C compiler is smart enough.  */
+#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+      /* Before we start, one word to the strange constants.
+        They are defined in RFC 1321 as
+
+        T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+       */
+
+      /* Round 1.  */
+      OP (A, B, C, D,  7, 0xd76aa478);
+      OP (D, A, B, C, 12, 0xe8c7b756);
+      OP (C, D, A, B, 17, 0x242070db);
+      OP (B, C, D, A, 22, 0xc1bdceee);
+      OP (A, B, C, D,  7, 0xf57c0faf);
+      OP (D, A, B, C, 12, 0x4787c62a);
+      OP (C, D, A, B, 17, 0xa8304613);
+      OP (B, C, D, A, 22, 0xfd469501);
+      OP (A, B, C, D,  7, 0x698098d8);
+      OP (D, A, B, C, 12, 0x8b44f7af);
+      OP (C, D, A, B, 17, 0xffff5bb1);
+      OP (B, C, D, A, 22, 0x895cd7be);
+      OP (A, B, C, D,  7, 0x6b901122);
+      OP (D, A, B, C, 12, 0xfd987193);
+      OP (C, D, A, B, 17, 0xa679438e);
+      OP (B, C, D, A, 22, 0x49b40821);
+
+      /* For the second to fourth round we have the possibly swapped words
+        in CORRECT_WORDS.  Redefine the macro to take an additional first
+        argument specifying the function to use.  */
+#undef OP
+#define OP(f, a, b, c, d, k, s, T)                                     \
+      do                                                               \
+       {                                                               \
+         a += f (b, c, d) + correct_words[k] + T;                      \
+         CYCLIC (a, s);                                                \
+         a += b;                                                       \
+       }                                                               \
+      while (0)
+
+      /* Round 2.  */
+      OP (FG, A, B, C, D,  1,  5, 0xf61e2562);
+      OP (FG, D, A, B, C,  6,  9, 0xc040b340);
+      OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+      OP (FG, B, C, D, A,  0, 20, 0xe9b6c7aa);
+      OP (FG, A, B, C, D,  5,  5, 0xd62f105d);
+      OP (FG, D, A, B, C, 10,  9, 0x02441453);
+      OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+      OP (FG, B, C, D, A,  4, 20, 0xe7d3fbc8);
+      OP (FG, A, B, C, D,  9,  5, 0x21e1cde6);
+      OP (FG, D, A, B, C, 14,  9, 0xc33707d6);
+      OP (FG, C, D, A, B,  3, 14, 0xf4d50d87);
+      OP (FG, B, C, D, A,  8, 20, 0x455a14ed);
+      OP (FG, A, B, C, D, 13,  5, 0xa9e3e905);
+      OP (FG, D, A, B, C,  2,  9, 0xfcefa3f8);
+      OP (FG, C, D, A, B,  7, 14, 0x676f02d9);
+      OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+      /* Round 3.  */
+      OP (FH, A, B, C, D,  5,  4, 0xfffa3942);
+      OP (FH, D, A, B, C,  8, 11, 0x8771f681);
+      OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+      OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+      OP (FH, A, B, C, D,  1,  4, 0xa4beea44);
+      OP (FH, D, A, B, C,  4, 11, 0x4bdecfa9);
+      OP (FH, C, D, A, B,  7, 16, 0xf6bb4b60);
+      OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+      OP (FH, A, B, C, D, 13,  4, 0x289b7ec6);
+      OP (FH, D, A, B, C,  0, 11, 0xeaa127fa);
+      OP (FH, C, D, A, B,  3, 16, 0xd4ef3085);
+      OP (FH, B, C, D, A,  6, 23, 0x04881d05);
+      OP (FH, A, B, C, D,  9,  4, 0xd9d4d039);
+      OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+      OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+      OP (FH, B, C, D, A,  2, 23, 0xc4ac5665);
+
+      /* Round 4.  */
+      OP (FI, A, B, C, D,  0,  6, 0xf4292244);
+      OP (FI, D, A, B, C,  7, 10, 0x432aff97);
+      OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+      OP (FI, B, C, D, A,  5, 21, 0xfc93a039);
+      OP (FI, A, B, C, D, 12,  6, 0x655b59c3);
+      OP (FI, D, A, B, C,  3, 10, 0x8f0ccc92);
+      OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+      OP (FI, B, C, D, A,  1, 21, 0x85845dd1);
+      OP (FI, A, B, C, D,  8,  6, 0x6fa87e4f);
+      OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+      OP (FI, C, D, A, B,  6, 15, 0xa3014314);
+      OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+      OP (FI, A, B, C, D,  4,  6, 0xf7537e82);
+      OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+      OP (FI, C, D, A, B,  2, 15, 0x2ad7d2bb);
+      OP (FI, B, C, D, A,  9, 21, 0xeb86d391);
+
+      /* Add the starting values of the context.  */
+      A += A_save;
+      B += B_save;
+      C += C_save;
+      D += D_save;
+    }
+
+  /* Put checksum in context given as argument.  */
+  ctx->A = A;
+  ctx->B = B;
+  ctx->C = C;
+  ctx->D = D;
+}
+
+#define ASC2HEX(x) (((x) <= '9') ? ((x) - '0') : (tolower((x)) + 10 - 'a'))
+#define HEX2ASC(x) ((x) > 9 ? ((x) - 10 + 'a') : ((x) + '0'))
+
+/* Writes the ASCII representation of the MD5 digest into the
+ * given buffer, which must be at least 33 characters long. */
+void ne_md5_to_ascii(const unsigned char md5_buf[16], char *buffer) 
+{
+    int count;
+    for (count = 0; count<16; count++) {
+       buffer[count*2] = HEX2ASC(md5_buf[count] >> 4);
+       buffer[count*2+1] = HEX2ASC(md5_buf[count] & 0x0f);
+    }
+    buffer[32] = '\0';
+}
+
+/* Reads the ASCII representation of an MD5 digest. The buffer must
+ * be at least 32 characters long. */
+void ne_ascii_to_md5(const char *buffer, unsigned char md5_buf[16]) 
+{
+    int count;
+    for (count = 0; count<16; count++) {
+       md5_buf[count] = ((ASC2HEX(buffer[count*2])) << 4) |
+           ASC2HEX(buffer[count*2+1]);
+    }
+}
diff --git a/neon/src/ne_md5.h b/neon/src/ne_md5.h
new file mode 100644 (file)
index 0000000..7033b8b
--- /dev/null
@@ -0,0 +1,151 @@
+/* Declaration of functions and data types used for MD5 sum computing
+   library functions.
+   Copyright (C) 1995, 1996, 1997 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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef NEON_MD5_H
+#define NEON_MD5_H 1
+
+#include <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/* The following contortions are an attempt to use the C preprocessor
+   to determine an unsigned integral type that is 32 bits wide.  An
+   alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+   doing that would require that the configure script compile and *run*
+   the resulting executable.  Locally running cross-compiled executables
+   is usually not possible.  */
+
+#ifdef _LIBC
+# include <sys/types.h>
+typedef u_int32_t md5_uint32;
+#else
+# if defined __STDC__ && __STDC__
+#  define UINT_MAX_32_BITS 4294967295U
+# else
+#  define UINT_MAX_32_BITS 0xFFFFFFFF
+# endif
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+   This should be valid for all systems GNU cares about because
+   that doesn't include 16-bit systems, and only modern systems
+   (that certainly have <limits.h>) have 64+-bit integral types.  */
+
+# ifndef UINT_MAX
+#  define UINT_MAX UINT_MAX_32_BITS
+# endif
+
+# if UINT_MAX == UINT_MAX_32_BITS
+   typedef unsigned int md5_uint32;
+# else
+#  if USHRT_MAX == UINT_MAX_32_BITS
+    typedef unsigned short md5_uint32;
+#  else
+#   if ULONG_MAX == UINT_MAX_32_BITS
+     typedef unsigned long md5_uint32;
+#   else
+     /* The following line is intended to evoke an error.
+        Using #error is not portable enough.  */
+     "Cannot determine unsigned 32-bit data type."
+#   endif
+#  endif
+# endif
+#endif
+
+#undef __P
+#if defined (__STDC__) && __STDC__
+# define __P(x) x
+#else
+# define __P(x) ()
+#endif
+
+/* Structure to save state of computation between the single steps.  */
+struct ne_md5_ctx
+{
+  md5_uint32 A;
+  md5_uint32 B;
+  md5_uint32 C;
+  md5_uint32 D;
+
+  md5_uint32 total[2];
+  md5_uint32 buflen;
+  char buffer[128];
+};
+
+/*
+ * The following three functions are build up the low level used in
+ * the functions `md5_stream' and `md5_buffer'.
+ */
+
+/* Initialize structure containing state of computation.
+   (RFC 1321, 3.3: Step 3)  */
+extern void ne_md5_init_ctx __P ((struct ne_md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is necessary that LEN is a multiple of 64!!! */
+extern void ne_md5_process_block __P ((const void *buffer, size_t len,
+                                   struct ne_md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is NOT required that LEN is a multiple of 64.  */
+extern void ne_md5_process_bytes __P ((const void *buffer, size_t len,
+                                      struct ne_md5_ctx *ctx));
+
+/* Process the remaining bytes in the buffer and put result from CTX
+   in first 16 bytes following RESBUF.  The result is always in little
+   endian byte order, so that a byte-wise output yields to the wanted
+   ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+extern void *ne_md5_finish_ctx __P ((struct ne_md5_ctx *ctx, void *resbuf));
+
+
+/* Put result from CTX in first 16 bytes following RESBUF.  The result is
+   always in little endian byte order, so that a byte-wise output yields
+   to the wanted ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+extern void *ne_md5_read_ctx __P ((const struct ne_md5_ctx *ctx, void *resbuf));
+
+
+/* Compute MD5 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 16 bytes
+   beginning at RESBLOCK.  */
+extern int ne_md5_stream __P ((FILE *stream, void *resblock));
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+extern void *ne_md5_buffer __P ((const char *buffer, size_t len,
+                                void *resblock));
+
+/* MD5 ascii->binary conversion */
+void ne_md5_to_ascii(const unsigned char md5_buf[16], char *buffer);
+void ne_ascii_to_md5(const char *buffer, unsigned char md5_buf[16]);
+
+#endif /* NEON_MD5_H */
diff --git a/neon/src/ne_private.h b/neon/src/ne_private.h
new file mode 100644 (file)
index 0000000..38f2e3a
--- /dev/null
@@ -0,0 +1,204 @@
+/* 
+   HTTP Request Handling
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+/* THIS IS NOT A PUBLIC INTERFACE. You CANNOT include this header file
+ * from an application.  */
+#ifndef NE_PRIVATE_H
+#define NE_PRIVATE_H
+
+#include "ne_request.h"
+
+struct host_info {
+    /* hostname is not const since it changes on redirects. */
+    char *hostname;
+    int port;
+    struct in_addr addr;
+    char *hostport; /* URI hostport segment */
+};
+
+/* This is called with each of the headers in the response */
+struct header_handler {
+    char *name;
+    ne_header_handler handler;
+    void *userdata;
+    struct header_handler *next;
+};
+
+/* TODO: could unify these all into a generic callback list */
+
+struct body_reader {
+    ne_block_reader handler;
+    ne_accept_response accept_response;
+    unsigned int use:1;
+    void *userdata;
+    struct body_reader *next;
+};
+
+/* Store hook information */
+struct hook {
+    const ne_request_hooks *hooks;
+    void *private;
+    ne_free_hooks free;
+    struct hook *next;
+};
+
+/* Per-request store for hooks.
+ * This is a bit noddy really. */
+struct hook_request {
+    struct hook *hook;
+    void *cookie;
+    struct hook_request *next;
+};
+
+#define HAVE_HOOK(st,func) (st->hook->hooks->func != NULL)
+#define HOOK_FUNC(st, func) (*st->hook->hooks->func)
+
+/* Session support. */
+struct ne_session_s {
+    /* Connection information */
+    nsocket *socket;
+
+    struct host_info server, proxy;
+
+    /* Connection states:
+     *   0:  Not connected at all.
+     *   1:  We have a TCP connection to the next-hop server.
+     *   2:  We have a negotiated an SSL connection over the proxy's 
+     *       TCP tunnel.
+     *
+     * Note, 1 is all we need if we don't have a proxy server, or
+     * if we do have a proxy server and we're not using SSL.
+     */
+    unsigned int connected:2;
+
+    /* Settings */
+    unsigned int have_proxy:1; /* do we have a proxy server? */
+    unsigned int no_persist:1; /* set to disable persistent connections */
+    unsigned int use_secure:1; /* whether a secure connection is required */
+    int expect100_works:2; /* known state of 100-continue support */
+    unsigned int in_connect:1; /* doing a proxy CONNECT */
+    unsigned int request_secure_upgrade:1; 
+    unsigned int accept_secure_upgrade:1;
+
+    ne_use_proxy proxy_decider;
+    void *proxy_decider_udata;
+
+    nssl_context *ssl_context;
+
+    sock_progress progress_cb;
+    void *progress_ud;
+
+    ne_notify_status notify_cb;
+    void *notify_ud;
+
+    struct hook *hooks;
+
+    char *user_agent; /* full User-Agent string */
+
+    /* The last HTTP-Version returned by the server */
+    int version_major;
+    int version_minor;
+
+    /* Error string */
+    char error[BUFSIZ];
+};
+
+struct ne_request_s {
+    const char *method;
+    char *uri, *abs_path;
+    
+    /*** Request ***/
+
+    ne_buffer *headers;
+    ne_provide_body body_cb;
+    void *body_ud;
+
+    int body_fd;
+    const char *body_buffer, *body_pnt;
+    size_t body_size, body_left;
+
+    /* temporary store for request. */
+    ne_buffer *reqbuf;
+
+    /* temporary store for response lines. */
+    ne_buffer *respbuf;
+
+    /**** Response ***/
+
+    /* The transfer encoding types */
+    struct ne_response {
+       int length;            /* Response entity-body content-length */
+       size_t left;              /* Bytes left to read */
+       size_t chunk_left;        /* Bytes of chunk left to read */
+       size_t total;             /* total bytes read so far. */
+       unsigned int is_chunked; /* Are we using chunked TE? */
+    } resp;
+
+    /* List of callbacks which are passed response headers */
+    struct header_handler *header_catchers;
+    
+    /* We store response header handlers in a hash table.  The hash is
+     * derived from the header name in lower case. */
+
+    /* 53 is magic, of course.  For a standard http_get (with
+     * redirects), 9 header handlers are defined.  Two of these are
+     * for Content-Length (which is a bug, and should be fixed
+     * really).  Ignoring that hash clash, the 8 *different* handlers
+     * all hash uniquely into the hash table of size 53.  */
+#define HH_HASHSIZE 53
+    
+    struct header_handler *header_handlers[HH_HASHSIZE];
+    /* List of callbacks which are passed response body blocks */
+    struct body_reader *body_readers;
+
+    /*** Miscellaneous ***/
+    unsigned int method_is_head:1;
+    unsigned int use_proxy:1;
+    unsigned int use_expect100:1;
+    unsigned int can_persist:1;
+    unsigned int forced_close:1;
+    unsigned int upgrade_to_tls:1;
+
+    ne_session *session;
+    ne_status status;
+
+    /* stores request-private hook info */
+    struct hook_request *hook_store;
+
+#ifdef USE_DAV_LOCKS
+    /* TODO: move this to hooks... list of locks to submit */
+    struct dav_submit_locks *if_locks;
+#endif
+
+};
+
+/* Set a new URI for the given request. */
+void ne_set_request_uri(ne_request *req, const char *uri);
+
+typedef int (*ne_push_fn)(void *userdata, const char *buf, size_t count);
+
+/* Pulls the request body for the given request, passing blocks to the
+ * given callback.
+ */
+int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud);
+
+#endif /* HTTP_PRIVATE_H */
diff --git a/neon/src/ne_props.c b/neon/src/ne_props.c
new file mode 100644 (file)
index 0000000..ff27d99
--- /dev/null
@@ -0,0 +1,589 @@
+/* 
+   WebDAV Properties manipulation
+   Copyright (C) 2000-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_alloc.h"
+#include "ne_xml.h"
+#include "ne_props.h"
+#include "ne_basic.h"
+
+struct ne_propfind_handler_s {
+    ne_session *sess;
+    ne_request *request;
+
+    int has_props; /* whether we've already written some
+                   * props to the body. */
+    ne_buffer *body;
+    
+    ne_207_parser *parser207;
+    ne_xml_parser *parser;
+
+    /* Callback to create the private structure. */
+    ne_props_create_complex private_creator;
+    void *private_userdata;
+    
+    /* Current propset. */
+    ne_prop_result_set *current;
+
+    ne_props_result callback;
+    void *userdata;
+};
+
+#define ELM_namedprop (NE_ELM_207_UNUSED)
+
+static const struct ne_xml_elm flat_elms[] = {
+    { "", "", NE_ELM_unknown, NE_XML_COLLECT },
+    { NULL }
+};
+
+/* We build up the results of one 'response' element in memory. */
+struct prop {
+    char *name, *nspace, *value, *lang;
+    /* Store a ne_propname here too, for convienience.  pname.name =
+     * name, pname.nspace = nspace, but they are const'ed in pname. */
+    ne_propname pname;
+};
+
+struct propstat {
+    struct prop *props;
+    int numprops;
+    ne_status status;
+} propstat;
+
+/* Results set. */
+struct ne_prop_result_set_s {
+    struct propstat *pstats;
+    int numpstats;
+    void *private;
+    char *href;
+};
+
+
+static int 
+startelm(void *userdata, const struct ne_xml_elm *elm, 
+        const char **atts);
+static int 
+endelm(void *userdata, const struct ne_xml_elm *elm, const char *cdata);
+static int check_context(ne_xml_elmid parent, ne_xml_elmid child);
+
+ne_xml_parser *ne_propfind_get_parser(ne_propfind_handler *handler)
+{
+    return handler->parser;
+}
+
+ne_request *ne_propfind_get_request(ne_propfind_handler *handler)
+{
+    return handler->request;
+}
+
+static int propfind(ne_propfind_handler *handler, 
+                   ne_props_result results, void *userdata)
+{
+    int ret;
+    ne_request *req = handler->request;
+
+    /* Register the flat property handler to catch any properties 
+     * which the user isn't handling as 'complex'. */
+    ne_xml_push_handler(handler->parser, flat_elms, 
+                        check_context, startelm, endelm, handler);
+
+    /* Register the catch-all handler to ignore any cruft the
+     * server returns. */
+    ne_207_ignore_unknown(handler->parser207);
+    
+    handler->callback = results;
+    handler->userdata = userdata;
+
+    ne_set_request_body_buffer(req, handler->body->data,
+                              ne_buffer_size(handler->body));
+
+    ne_add_request_header(req, "Content-Type", "text/xml"); /* TODO: UTF-8? */
+    
+    ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v, 
+                                 handler->parser);
+
+    ret = ne_request_dispatch(req);
+
+    if (ret == NE_OK && ne_get_status(req)->klass != 2) {
+       ret = NE_ERROR;
+    } else if (!ne_xml_valid(handler->parser)) {
+       ne_set_error(handler->sess, ne_xml_get_error(handler->parser));
+       ret = NE_ERROR;
+    }
+
+    return ret;
+}
+
+static void set_body(ne_propfind_handler *hdl, const ne_propname *names)
+{
+    ne_buffer *body = hdl->body;
+    int n;
+    
+    if (!hdl->has_props) {
+       ne_buffer_zappend(body, "<prop>" EOL);
+       hdl->has_props = 1;
+    }
+
+    for (n = 0; names[n].name != NULL; n++) {
+       ne_buffer_concat(body, "<", names[n].name, " xmlns=\"", 
+                      names[n].nspace, "\"/>" EOL, NULL);
+    }
+
+}
+
+int ne_propfind_allprop(ne_propfind_handler *handler, 
+                        ne_props_result results, void *userdata)
+{
+    ne_buffer_zappend(handler->body, "<allprop/></propfind>" EOL);
+    return propfind(handler, results, userdata);
+}
+
+int ne_propfind_named(ne_propfind_handler *handler, const ne_propname *props,
+                      ne_props_result results, void *userdata)
+{
+    set_body(handler, props);
+    ne_buffer_zappend(handler->body, "</prop></propfind>" EOL);
+    return propfind(handler, results, userdata);
+}
+
+
+/* The easy one... PROPPATCH */
+int ne_proppatch(ne_session *sess, const char *uri, 
+                 const ne_proppatch_operation *items)
+{
+    ne_request *req = ne_request_create(sess, "PROPPATCH", uri);
+    ne_buffer *body = ne_buffer_create();
+    int n, ret;
+    
+    /* Create the request body */
+    ne_buffer_zappend(body, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" EOL
+                    "<propertyupdate xmlns=\"DAV:\">");
+
+    for (n = 0; items[n].name != NULL; n++) {
+       switch (items[n].type) {
+       case ne_propset:
+           /* <set><prop><prop-name>value</prop-name></prop></set> */
+           ne_buffer_concat(body, "<set><prop>"
+                          "<", items[n].name->name, " xmlns=\"",
+                          items[n].name->nspace, "\">", items[n].value,
+                          "</", items[n].name->name, "></prop></set>" EOL, 
+                          NULL);
+           break;
+
+       case ne_propremove:
+           /* <remove><prop><prop-name/></prop></remove> */
+           ne_buffer_concat(body, 
+                          "<remove><prop><", items[n].name->name, " xmlns=\"",
+                          items[n].name->nspace, "\"/></prop></remove>" EOL, 
+                          NULL);
+           break;
+       }
+    }  
+
+    ne_buffer_zappend(body, "</propertyupdate>" EOL);
+
+    ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
+    ne_add_request_header(req, "Content-Type", "text/xml"); /* TODO: UTF-8? */
+    
+    ret = ne_simple_request(sess, req);
+    
+    ne_buffer_destroy(body);
+
+    return ret;
+}
+
+/* Compare two property names. */
+static int pnamecmp(const ne_propname *pn1, const ne_propname *pn2)
+{
+    return (strcasecmp(pn1->nspace, pn2->nspace) ||
+           strcasecmp(pn1->name, pn2->name));
+}
+
+/* Find property in 'set' with name 'pname'.  If found, set pstat_ret
+ * to the containing propstat, likewise prop_ret, and returns zero.
+ * If not found, returns non-zero.  */
+static int findprop(const ne_prop_result_set *set, const ne_propname *pname,
+                   struct propstat **pstat_ret, struct prop **prop_ret)
+{
+    
+    int ps, p;
+
+    for (ps = 0; ps < set->numpstats; ps++) {
+       for (p = 0; p < set->pstats[ps].numprops; p++) {
+           struct prop *prop = &set->pstats[ps].props[p];
+
+           if (pnamecmp(&prop->pname, pname) == 0) {
+               if (pstat_ret != NULL)
+                   *pstat_ret = &set->pstats[ps];
+               if (prop_ret != NULL)
+                   *prop_ret = prop;
+               return 0;
+           }
+       }
+    }
+
+    return -1;
+}
+
+const char *ne_propset_value(const ne_prop_result_set *set,
+                             const ne_propname *pname)
+{
+    struct prop *prop;
+    
+    if (findprop(set, pname, NULL, &prop)) {
+       return NULL;
+    } else {
+       return prop->value;
+    }
+}
+
+const char *ne_propset_lang(const ne_prop_result_set *set,
+                            const ne_propname *pname)
+{
+    struct prop *prop;
+
+    if (findprop(set, pname, NULL, &prop)) {
+       return NULL;
+    } else {
+       return prop->lang;
+    }
+}
+
+void *ne_propfind_current_private(ne_propfind_handler *handler)
+{
+    return handler->current->private;
+}
+
+void *ne_propset_private(const ne_prop_result_set *set)
+{
+    return set->private;
+}
+
+int ne_propset_iterate(const ne_prop_result_set *set,
+                       ne_propset_iterator iterator, void *userdata)
+{
+    int ps, p;
+
+    for (ps = 0; ps < set->numpstats; ps++) {
+       for (p = 0; p < set->pstats[ps].numprops; p++) {
+           struct prop *prop = &set->pstats[ps].props[p];
+           int ret = iterator(userdata, &prop->pname, prop->value, 
+                              &set->pstats[ps].status);
+           if (ret)
+               return ret;
+
+       }
+    }
+
+    return 0;
+}
+
+const ne_status *ne_propset_status(const ne_prop_result_set *set,
+                                     const ne_propname *pname)
+{
+    struct propstat *pstat;
+    
+    if (findprop(set, pname, &pstat, NULL)) {
+       /* TODO: it is tempting to return a dummy status object here
+        * rather than NULL, which says "Property result was not given
+        * by server."  but I'm not sure if this is best left to the
+        * client.  */
+       return NULL;
+    } else {
+       return &pstat->status;
+    }
+}
+
+
+/* Pick up all properties as flat properties. */
+static int check_context(ne_xml_elmid parent, ne_xml_elmid child)
+{
+    if (child == NE_ELM_unknown && parent == NE_ELM_prop)
+       return NE_XML_VALID;
+
+    return NE_XML_DECLINE;
+}
+
+static void *start_response(void *userdata, const char *href)
+{
+    ne_prop_result_set *set = ne_calloc(sizeof(*set));
+    ne_propfind_handler *hdl = userdata;
+
+    set->href = ne_strdup(href);
+
+    if (hdl->private_creator != NULL) {
+       set->private = hdl->private_creator(hdl->private_userdata, href);
+    }
+
+    hdl->current = set;
+
+    return set;
+}
+
+static void *start_propstat(void *userdata, void *response)
+{
+    ne_prop_result_set *set = response;
+    int n;
+    struct propstat *pstat;
+
+    n = set->numpstats;
+    set->pstats = realloc(set->pstats, sizeof(struct propstat) * (n+1));
+    set->numpstats = n+1;
+
+    pstat = &set->pstats[n];
+    memset(pstat, 0, sizeof(*pstat));
+    
+    /* And return this as the new pstat. */
+    return &set->pstats[n];
+}
+
+static int 
+startelm(void *userdata, const struct ne_xml_elm *elm, 
+        const char **atts)
+{
+    ne_propfind_handler *hdl = userdata;
+    struct propstat *pstat = ne_207_get_current_propstat(hdl->parser207);
+    struct prop *prop;
+    int n;
+    const char *lang;
+
+    /* Paranoia */
+    if (pstat == NULL) {
+       NE_DEBUG(NE_DBG_XML, "gp_startelm: No propstat found, or not my element.");
+       return -1;
+    }
+
+    /* Add a property to this propstat */
+    n = pstat->numprops;
+
+    pstat->props = realloc(pstat->props, sizeof(struct prop) * (n + 1));
+    pstat->numprops = n+1;
+
+    /* Fill in the new property. */
+    prop = &pstat->props[n];
+
+    prop->pname.name = prop->name = ne_strdup(elm->name);
+    prop->pname.nspace = prop->nspace = ne_strdup(elm->nspace);
+    prop->value = NULL;
+
+    NE_DEBUG(NE_DBG_XML, "Got property #%d: %s@@%s.\n", n, 
+         prop->nspace, prop->name);
+
+    /* This is under discussion at time of writing (April '01), and it
+     * looks like we need to retrieve the xml:lang property from any
+     * element here or above.
+     *
+     * Also, I think we might need attribute namespace handling here.  */
+    lang = ne_xml_get_attr(atts, "xml:lang");
+    if (lang != NULL) {
+       prop->lang = ne_strdup(lang);
+       NE_DEBUG(NE_DBG_XML, "Property language is %s\n", prop->lang);
+    } else {
+       prop->lang = NULL;
+    }
+
+    return 0;
+}
+
+static int 
+endelm(void *userdata, const struct ne_xml_elm *elm, const char *cdata)
+{
+    ne_propfind_handler *hdl = userdata;
+    struct propstat *pstat = ne_207_get_current_propstat(hdl->parser207);
+    int n;
+
+    if (pstat == NULL) {
+       NE_DEBUG(NE_DBG_XML, "gp_endelm: No propstat found, or not my element.");
+       return -1;
+    }
+
+    n = pstat->numprops - 1;
+
+    NE_DEBUG(NE_DBG_XML, "Value of property #%d is %s\n", n, cdata);
+    
+    pstat->props[n].value = ne_strdup(cdata);
+
+    return 0;
+}
+
+static void end_propstat(void *userdata, void *pstat_v, 
+                        const char *status_line, const ne_status *status,
+                        const char *description)
+{
+    struct propstat *pstat = pstat_v;
+
+    /* If we get a non-2xx response back here, we wipe the value for
+     * each of the properties in this propstat, so the caller knows to
+     * look at the status instead. It's annoying, since for each prop
+     * we will have done an unnecessary strdup("") above, but there is
+     * no easy way round that given the fact that we don't know
+     * whether we've got an error or not till after we get the
+     * property element.
+     *
+     * Interestingly IIS breaks the 2518 DTD and puts the status
+     * element first in the propstat. This is useful since then we
+     * *do* know whether each subsequent empty prop element means, but
+     * we can't rely on that here. */
+    if (status->klass != 2) {
+       int n;
+       
+       for (n = 0; n < pstat->numprops; n++) {
+           free(pstat->props[n].value);
+           pstat->props[n].value = NULL;
+       }
+    }
+
+    pstat->status = *status;
+}
+
+/* Frees up a results set */
+static void free_propset(ne_prop_result_set *set)
+{
+    int n;
+    
+    for (n = 0; n < set->numpstats; n++) {
+       int m;
+       struct propstat *p = &set->pstats[n];
+
+       for (m = 0; m < p->numprops; m++) {
+           free(p->props[m].nspace);
+           free(p->props[m].name);
+           NE_FREE(p->props[m].lang);
+           NE_FREE(p->props[m].value);
+       }
+
+       free(set->pstats[n].props);
+    }
+
+    free(set->pstats);
+    free(set);  
+}
+
+static void end_response(void *userdata, void *resource,
+                        const char *status_line,
+                        const ne_status *status,
+                        const char *description)
+{
+    ne_propfind_handler *handler = userdata;
+    ne_prop_result_set *set = resource;
+    
+    /* TODO: Handle status here too? The status element is mandatory
+     * inside each propstat, so, not much point probably. */
+
+    /* Pass back the results for this resource. */
+    if (handler->callback != NULL) {
+       handler->callback(handler->userdata, set->href, set);
+    }
+
+    free(set->href);
+
+    /* Clean up the propset tree we've just built. */
+    free_propset(set);
+}
+
+ne_propfind_handler *
+ne_propfind_create(ne_session *sess, const char *uri, int depth)
+{
+    ne_propfind_handler *ret = ne_calloc(sizeof(ne_propfind_handler));
+
+    ret->parser = ne_xml_create();
+    ret->parser207 = ne_207_create(ret->parser, ret);
+    ret->sess = sess;
+    ret->body = ne_buffer_create();
+    ret->request = ne_request_create(sess, "PROPFIND", uri);
+
+    ne_add_depth_header(ret->request, depth);
+
+    ne_207_set_response_handlers(ret->parser207, 
+                                 start_response, end_response);
+
+    ne_207_set_propstat_handlers(ret->parser207, start_propstat,
+                                 end_propstat);
+
+    /* The start of the request body is fixed: */
+    ne_buffer_concat(ret->body, 
+                   "<?xml version=\"1.0\" encoding=\"utf-8\"?>" EOL 
+                   "<propfind xmlns=\"DAV:\">", NULL);
+
+    return ret;
+}
+
+/* Destroy a propfind handler */
+void ne_propfind_destroy(ne_propfind_handler *handler)
+{
+    ne_207_destroy(handler->parser207);
+    ne_xml_destroy(handler->parser);
+    ne_buffer_destroy(handler->body);
+    ne_request_destroy(handler->request);
+    free(handler);    
+}
+
+int ne_simple_propfind(ne_session *sess, const char *href, int depth,
+                       const ne_propname *props,
+                       ne_props_result results, void *userdata)
+{
+    ne_propfind_handler *hdl;
+    int ret;
+
+    hdl = ne_propfind_create(sess, href, depth);
+    if (props != NULL) {
+       ret = ne_propfind_named(hdl, props, results, userdata);
+    } else {
+       ret = ne_propfind_allprop(hdl, results, userdata);
+    }
+       
+    ne_propfind_destroy(hdl);
+    
+    return ret;
+}
+
+int ne_propnames(ne_session *sess, const char *href, int depth,
+                 ne_props_result results, void *userdata)
+{
+    ne_propfind_handler *hdl;
+    int ret;
+
+    hdl = ne_propfind_create(sess, href, depth);
+
+    ne_buffer_zappend(hdl->body, "<propname/></propfind>");
+
+    ret = propfind(hdl, results, userdata);
+
+    ne_propfind_destroy(hdl);
+
+    return ret;
+}
+
+void ne_propfind_set_private(ne_propfind_handler *hdl,
+                             ne_props_create_complex creator,
+                             void *userdata)
+{
+    hdl->private_creator = creator;
+    hdl->private_userdata = userdata;
+}
diff --git a/neon/src/ne_props.h b/neon/src/ne_props.h
new file mode 100644 (file)
index 0000000..0e8e2b2
--- /dev/null
@@ -0,0 +1,224 @@
+/* 
+   WebDAV Properties manipulation
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_PROPS_H
+#define NE_PROPS_H
+
+#include "ne_request.h"
+#include "ne_207.h"
+
+BEGIN_NEON_DECLS
+
+/* There are two interfaces for fetching properties. The first is
+ * 'ne_simple_propfind', which is relatively simple, and easy to use,
+ * but only lets you fetch FLAT properties, i.e. properties which are
+ * just a string of bytes.  The complex interface is 'ne_propfind_*',
+ * which is complicated, and hard to use, but lets you parse
+ * structured properties, i.e.  properties which have XML content.  */
+
+/* The 'ne_simple_propfind' interface. ***
+ *
+ * ne_simple_propfind allows you to fetch a set of properties for a
+ * single resource, or a tree of resources.  You set the operation
+ * going by passing these arguments:
+ *
+ *  - the session which should be used.
+ *  - the URI and the depth of the operation (0, 1, infinite)
+ *  - the names of the properties which you want to fetch
+ *  - a results callback, and the userdata for the callback.
+ *
+ * For each resource found, the results callback is called, passing
+ * you two things along with the userdata you passed in originally:
+ *
+ *   - the URI of the resource (const char *href)
+ *   - the properties results set (const ne_prop_result_set *results)
+ * */
+
+
+typedef struct ne_prop_result_set_s ne_prop_result_set;
+
+/* Get the value of a given property. Will return NULL if there was an
+ * error fetching this property on this resource.  Call
+ * ne_propset_result to get the response-status if so.  */
+const char *ne_propset_value(const ne_prop_result_set *set,
+                             const ne_propname *propname);
+
+/* Returns the status structure for fetching the given property on
+ * this resource. This function will return NULL if the server did not
+ * return the property (which is a server error). */
+const ne_status *ne_propset_status(const ne_prop_result_set *set,
+                                     const ne_propname *propname);
+
+/* Returns the private pointer for the given propset. */
+void *ne_propset_private(const ne_prop_result_set *set);
+
+/* Return language string of property (may be NULL). */
+const char *ne_propset_lang(const ne_prop_result_set *set,
+                            const ne_propname *pname);
+
+/* ne_propset_iterate iterates over a properties result set,
+ * calling the callback for each property in the set. userdata is
+ * passed as the first argument to the callback. value may be NULL,
+ * indicating an error occurred fetching this property: look at 
+ * status for the error in that case.
+ *
+ * If the iterator returns non-zero, ne_propset_iterate will return
+ * immediately with that value.
+ */
+typedef int (*ne_propset_iterator)(void *userdata,
+                                   const ne_propname *pname,
+                                   const char *value,
+                                   const ne_status *status);
+
+/* Iterate over all the properties in 'set', calling 'iterator'
+ * for each, passing 'userdata' as the first argument to callback.
+ * 
+ * Returns:
+ *   whatever value iterator returns.
+ */
+int ne_propset_iterate(const ne_prop_result_set *set,
+                       ne_propset_iterator iterator, void *userdata);
+
+/* Callback for handling the results of fetching properties for a
+ * single resource (named by 'href').  The results are stored in the
+ * result set 'results': use ne_propset_* to examine this object.  */
+typedef void (*ne_props_result)(void *userdata, const char *href,
+                                const ne_prop_result_set *results);
+
+/* Fetch properties for a resource (if depth == NE_DEPTH_ZERO),
+ * or a tree of resources (if depth == NE_DEPTH_ONE or _INFINITE).
+ *
+ * Names of the properties required must be given in 'props',
+ * or if props is NULL, *all* properties are fetched.
+ *
+ * 'results' is called for each resource in the response, userdata is
+ * passed as the first argument to the callback. It is important to
+ * note that the callback is called as the response is read off the
+ * socket, so don't do anything silly in it (e.g. sleep(100), or call
+ * any functions which use this session).
+ *
+ * Note that if 'depth' is NE_DEPTH_INFINITY, some servers may refuse
+ * the request.
+ *
+ * Returns NE_*.  */
+int ne_simple_propfind(ne_session *sess, const char *uri, int depth,
+                       const ne_propname *props,
+                       ne_props_result results, void *userdata);
+
+/* A PROPPATCH request may include any number of operations. Pass an
+ * array of these operations to ne_proppatch, with the last item
+ * having the name element being NULL.  If the type is propset, the
+ * property of the given name is set to the new value.  If the type is
+ * propremove, the property of the given name is deleted, and the
+ * value is ignored.  */
+typedef struct {
+    const ne_propname *name;
+    enum {
+       ne_propset,
+       ne_propremove
+    } type;
+    const char *value;
+} ne_proppatch_operation;
+
+int ne_proppatch(ne_session *sess, const char *uri,
+                 const ne_proppatch_operation *items);
+
+/* Retrieve property names for the resources at 'href'.  'results'
+ * callback is called for each resource.  Use 'ne_propset_iterate' on
+ * the passed results object to retrieve the list of property names.  */
+int ne_propnames(ne_session *sess, const char *href, int depth,
+                 ne_props_result results, void *userdata);
+
+/* The complex, you-do-all-the-work, property fetch interface:
+ */
+
+struct ne_propfind_handler_s;
+typedef struct ne_propfind_handler_s ne_propfind_handler;
+
+/* Retrieve the 'private' pointer for the current propset for the
+ * given handler, as returned by the ne_props_create_complex callback
+ * installed using 'ne_propfind_set_private'.  If this callback was
+ * not registered, this function will return NULL.  */
+void *ne_propfind_current_private(ne_propfind_handler *handler);
+
+/* Create a PROPFIND handler, for the given resource or set of 
+ * resources.
+ *
+ * Depth must be one of NE_DEPTH_*. */
+ne_propfind_handler *
+ne_propfind_create(ne_session *sess, const char *uri, int depth);
+
+/* Return the XML parser for the given handler (only need if you want
+ * to handle complex properties). */
+ne_xml_parser *ne_propfind_get_parser(ne_propfind_handler *handler);
+
+/* Return the request object for the given handler.  You MUST NOT use
+ * ne_set_request_body_* on this request object.  (this call is only
+ * needed if for instance, you want to add extra headers to the
+ * PROPFIND request).  */
+ne_request *ne_propfind_get_request(ne_propfind_handler *handler);
+
+/* A "complex property" has a value which is structured XML. To handle
+ * complex properties, you must set up and register an XML handler
+ * which will understand the elements which make up such properties.
+ * The handler must be registered with the parser returned by
+ * 'ne_propfind_get_parser'.
+ *
+ * To store the parsed value of the property, a 'private' structure is
+ * allocated in each propset (i.e. one per resource). When parsing the
+ * property value elements, for each new resource encountered in the
+ * response, the 'creator' callback is called to retrieve a 'private'
+ * structure for this resource.
+ *
+ * Whilst in XML element callbacks you will have registered to handle
+ * complex properties, you can use the 'ne_propfind_current_private'
+ * call to retrieve the pointer to this private structure.
+ *
+ * To retrieve this 'private' structure from the propset in the
+ * results callback, simply call 'ne_propset_private'.
+ * */
+typedef void *(*ne_props_create_complex)(void *userdata,
+                                         const char *uri);
+
+void ne_propfind_set_private(ne_propfind_handler *handler,
+                             ne_props_create_complex creator,
+                             void *userdata);
+
+/* Find all properties.
+ *
+ * Returns NE_*. */
+int ne_propfind_allprop(ne_propfind_handler *handler, 
+                        ne_props_result result, void *userdata);
+
+/* Find properties named in a call to ne_propfind_set_flat and/or
+ * ne_propfind_set_complex.
+ *
+ * Returns NE_*. */
+int ne_propfind_named(ne_propfind_handler *handler, 
+                      const ne_propname *prop,
+                      ne_props_result result, void *userdata);
+
+/* Destroy a propfind handler after use. */
+void ne_propfind_destroy(ne_propfind_handler *handler);
+
+END_NEON_DECLS
+
+#endif /* NE_PROPS_H */
diff --git a/neon/src/ne_redirect.c b/neon/src/ne_redirect.c
new file mode 100644 (file)
index 0000000..bb07a48
--- /dev/null
@@ -0,0 +1,194 @@
+/* 
+   HTTP-redirect support
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_session.h"
+#include "ne_request.h"
+#include "ne_alloc.h"
+#include "ne_uri.h"
+#include "ne_redirect.h"
+#include "ne_i18n.h"
+
+/* TODO: should remove this. */
+#include "ne_private.h"
+
+#define REDIRECT_ID "http://www.webdav.org/neon/hooks/http-redirect"
+
+struct redirect {
+    /* per-request stuff. */
+    char *location;
+    ne_request *req;
+    const char *method, *request_uri;
+
+    /* per-session stuff. */
+    ne_session *session;
+    ne_redirect_confirm confirm;
+    ne_redirect_notify notify;
+    void *userdata;
+};
+
+static void *create(void *session, ne_request *req, 
+                   const char *method, const char *uri);
+static int post_send(void *private, const ne_status *status);
+
+static const ne_request_hooks redirect_hooks = {
+    REDIRECT_ID,
+    create,
+    NULL,
+    post_send,
+    NULL
+};
+
+static void *
+create(void *session, ne_request *req, const char *method, const char *uri)
+{
+    struct redirect *red = session;
+    
+    /* Free up the location. */
+    NE_FREE(red->location);
+
+    /* for handling 3xx redirects */
+    ne_add_response_header_handler(req, "Location",
+                                    ne_duplicate_header, &red->location);
+
+    red->req = req;
+    red->method = method;
+    red->request_uri = uri;
+
+    return red;
+}
+
+/* 2616 says we can't auto-redirect if the method is not GET or HEAD.
+ * We extend this to PROPFIND and OPTIONS, which violates a 2616 MUST,
+ * but is following the spirit of the spec, I think.
+ *
+ * In fact Roy Fielding said as much in a new-httpd posting
+ * <20010224232203.G799@waka.ebuilt.net>: the spec should allow
+ * auto-redirects of any read-only method.  
+ *
+ * We have no interface for saying "this method is read-only",
+ * although this might be useful.
+ */
+static int auto_redirect(struct redirect *red)
+{
+    return (strcasecmp(red->method, "HEAD") == 0 ||
+           strcasecmp(red->method, "GET") == 0 || 
+           strcasecmp(red->method, "PROPFIND") == 0 ||
+           strcasecmp(red->method, "OPTIONS") == 0);
+}
+
+static int post_send(void *private, const ne_status *status)
+{
+    struct redirect *red = private;
+    struct uri uri;
+    int ret = NE_OK;
+
+    if ((status->code != 302 && status->code != 301) ||
+       red->location == NULL) {
+       /* Nothing to do. */
+       return NE_OK;
+    }
+    
+    if (uri_parse(red->location, &uri, NULL)) {
+       /* Couldn't parse the URI */
+       ne_set_error(red->session, _("Could not parse redirect location."));
+       return NE_ERROR;
+    }
+    
+    if ((uri.host != NULL && 
+        strcasecmp(uri.host, red->session->server.hostname) != 0) ||
+       (uri.port != -1 &&
+        uri.port != red->session->server.port) ||
+       (uri.scheme != NULL &&
+        strcasecmp(uri.scheme, ne_get_scheme(red->session)) != 0)) {
+       /* Cannot redirect to another server. Throw this back to the caller
+        * to have them start a new session. */
+       NE_DEBUG(NE_DBG_HTTP, 
+             "Redirected to different host/port/scheme:\n"
+             "From %s://%s:%d to %s//%s:%d\n",
+             ne_get_scheme(red->session),
+             red->session->server.hostname,
+             red->session->server.port,
+             uri.scheme, uri.host, uri.port);
+       ret = NE_REDIRECT;
+       ne_set_error(red->session, _("Redirected to a different server.\n"));
+    } else {
+
+       /* Same-server redirect. Can we auto-redirect? */
+
+       if (!auto_redirect(red) && 
+           (red->confirm == NULL ||
+            !(*red->confirm)(red->userdata, red->request_uri, uri.path))) {
+           /* No auto-redirect or confirm failed. */
+           ret = NE_ERROR;
+       } else {
+           /* Okay, follow it: set the new URI and retry the request. */
+           ne_set_request_uri(red->req, uri.path);
+           ret = NE_RETRY;
+           /* Notify them that we're following the redirect. */
+           if (red->notify != NULL) {
+               red->notify(red->userdata, red->request_uri, uri.path);
+           }
+       }
+    }
+
+    /* Free up the URI. */
+    uri_free(&uri);
+
+    return ret;
+}
+
+static void free_redirect(void *cookie)
+{
+    struct redirect *red = cookie;
+    NE_FREE(red->location);
+    free(red);
+}
+
+void ne_redirect_register(ne_session *sess, 
+                         ne_redirect_confirm confirm,
+                         ne_redirect_notify notify,
+                         void *userdata)
+{
+    struct redirect *red = ne_calloc(sizeof *red);
+    
+    red->confirm = confirm;
+    red->notify = notify;
+    red->userdata = userdata;
+    red->session = sess;
+
+    ne_add_hooks(sess, &redirect_hooks, red, free_redirect);
+}
+
+const char *ne_redirect_location(ne_session *sess)
+{
+    struct redirect *red = ne_session_hook_private(sess, REDIRECT_ID);
+    return red->location;
+}
+
diff --git a/neon/src/ne_redirect.h b/neon/src/ne_redirect.h
new file mode 100644 (file)
index 0000000..d18baf5
--- /dev/null
@@ -0,0 +1,74 @@
+/* 
+   HTTP-redirect support
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_REDIRECT_H
+#define NE_REDIRECT_H
+
+#include "ne_request.h"
+
+BEGIN_NEON_DECLS
+
+/* The redirect code does not handle redirects to a different server.
+ * For a redirect to a different server, the request fails and returns
+ * HTTP_REDIRECT.  You can then retrieve the redirect location using the
+ * call http_redirect_location.
+ *
+ * (you must have called http_redirect_register for this to happen).
+ * */
+
+/* Get confirmation from the user that a redirect from
+ * URI 'src' to URI 'dest' is acceptable. Should return:
+ *   Non-Zero to FOLLOW the redirect
+ *   Zero to NOT follow the redirect
+ */
+typedef int (*ne_redirect_confirm)(void *userdata,
+                                  const char *src, const char *dest);
+
+/* Notify the user that a redirect has been automatically 
+ * followed from URI 'src' to URI 'dest' */
+typedef void (*ne_redirect_notify)(void *userdata,
+                                  const char *src, const char *dest);
+
+/* Register redirect handling for the given session.
+ * Some redirect responses will be automatically followed.
+ * If the redirect is automatically followed, the 'notify' callback
+ * is called.
+ * For redirects which are NOT automatically followed, the
+ * 'confirm' callback is called: if this returns zero, the redirect
+ * is ignored.
+ * 
+ * 'confirm' may be passed as NULL: in this case, only automatic
+ * redirects are followed.  'notify' may also be passed as NULL,
+ * automatic redirects are still followed.
+ *
+ * 'userdata' is passed as the first argument to the confirm and
+ * notify callbacks.  */
+void ne_redirect_register(ne_session *sess,
+                         ne_redirect_confirm confirm,
+                         ne_redirect_notify notify,
+                         void *userdata);
+
+/* Return location of last redirect. */
+const char *ne_redirect_location(ne_session *sess);
+
+END_NEON_DECLS
+
+#endif /* NE_REDIRECT_H */
diff --git a/neon/src/ne_request.c b/neon/src/ne_request.c
new file mode 100644 (file)
index 0000000..af52449
--- /dev/null
@@ -0,0 +1,1340 @@
+/* 
+   HTTP request/response handling
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+/* This is the HTTP client request/response implementation.
+ * The goal of this code is to be modular and simple.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __EMX__
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h> /* just for Win32? */
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif 
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifdef HAVE_SNPRINTF_H
+#include "snprintf.h"
+#endif
+
+#include "ne_i18n.h"
+
+#include "ne_alloc.h"
+#include "ne_request.h"
+#include "ne_string.h" /* for ne_buffer */
+#include "ne_utils.h"
+#include "ne_socket.h"
+#include "ne_uri.h"
+
+#include "ne_private.h"
+
+#define HTTP_EXPECT_TIMEOUT 15
+/* 100-continue only used if size > HTTP_EXPECT_MINSIZ */
+#define HTTP_EXPECT_MINSIZE 1024
+
+/* with thanks to Jim Blandy; this macro simplified loads of code. */
+#define HTTP_ERR(x) do { int _ret = (x); if (_ret != NE_OK) return _ret; } while (0)
+
+static int open_connection(ne_request *req);
+
+/* The iterative step used to produce the hash value.  This is DJB's
+ * magic "*33" hash function.  Ralf Engelschall has done some amazing
+ * statistical analysis to show that *33 really is a good hash
+ * function: check the new-httpd list archives, or his 'str' library
+ * source code, for the details.
+ *
+ * TODO: due to limited range of characters used in header names,
+ * could maybe get a better hash function to use? */
+#define HH_ITERATE(hash, char) (((hash)*33 + char) % HH_HASHSIZE);
+
+/* Produce the hash value for a header name, which MUST be in lower
+ * case.  */
+static unsigned int hdr_hash(const char *name)
+{
+    const char *pnt;
+    unsigned int hash = 0;
+
+    for (pnt = name; *pnt != '\0'; pnt++) {
+       hash = HH_ITERATE(hash,*pnt);
+    }
+
+    return hash;
+}
+
+static int set_sockerr(ne_request *req, const char *doing, int code)
+{
+    switch(code) {
+    case 0: /* FIXME: still needed? */
+    case SOCK_CLOSED:
+       if (req->use_proxy) {
+           snprintf(req->session->error, BUFSIZ,
+                     _("%s: connection was closed by proxy server."), doing);
+       } else {
+           snprintf(req->session->error, BUFSIZ,
+                     _("%s: connection was closed by server."), doing);
+       }
+       return NE_ERROR;
+    case SOCK_TIMEOUT:
+       snprintf(req->session->error, BUFSIZ, 
+                 _("%s: connection timed out."), doing);
+       return NE_TIMEOUT;
+    default:
+       if (req->session->socket != NULL) {
+           const char *err = sock_get_error(req->session->socket);
+           if (err != NULL) {
+               snprintf(req->session->error, BUFSIZ, "%s: %s", doing, err);
+           } else {
+               snprintf(req->session->error, BUFSIZ, _("%s: socket error."),
+                        doing);
+           }
+       } else {
+           snprintf(req->session->error, BUFSIZ,
+                    "%s: %s", doing, strerror(errno));
+       }           
+       return NE_ERROR;
+    }
+}
+
+static char *lower_string(const char *str)
+{
+    char *ret = ne_malloc(strlen(str) + 1), *pnt;
+    
+    for (pnt = ret; *str != '\0'; str++) {
+       *pnt++ = tolower(*str);
+    }
+    
+    *pnt = '\0';
+
+    return ret;
+}
+
+static void notify_status(ne_session *sess, ne_conn_status status,
+                         const char *info)
+{
+    if (sess->notify_cb) {
+       sess->notify_cb(sess->notify_ud, status, info);
+    }
+}
+
+void ne_duplicate_header(void *userdata, const char *value)
+{
+    char **location = userdata;
+    *location = ne_strdup(value);
+}
+
+void ne_handle_numeric_header(void *userdata, const char *value)
+{
+    int *location = userdata;
+    *location = atoi(value);
+}
+
+void ne_add_hooks(ne_session *sess, 
+                   const ne_request_hooks *hooks, void *private,
+                   ne_free_hooks free_hooks)
+{
+    struct hook *hk = ne_malloc(sizeof(struct hook));
+    hk->hooks = hooks;
+    hk->private = private;
+    hk->next = sess->hooks;
+    hk->free = free_hooks;
+    sess->hooks = hk;
+}
+
+void *ne_request_hook_private(ne_request *req, const char *id)
+{
+    struct hook_request *hk;
+
+    for (hk = req->hook_store; hk != NULL; hk = hk->next) {
+       if (strcasecmp(hk->hook->hooks->id, id) == 0) {
+           return hk->cookie;
+       }
+    }
+
+    return NULL;
+}
+
+void *ne_session_hook_private(ne_session *sess, const char *id)
+{
+    struct hook *hk;
+
+    for (hk = sess->hooks; hk != NULL; hk = hk->next) {
+       if (strcasecmp(hk->hooks->id, id) == 0) {
+           return hk->private;
+       }
+    }
+
+    return NULL;
+}
+
+static ssize_t body_string_send(void *userdata, char *buffer, size_t count)
+{
+    ne_request *req = userdata;
+    
+    if (count == 0) {
+       req->body_left = req->body_size;
+       req->body_pnt = req->body_buffer;
+    } else {
+       /* if body_left == 0 we fall through and return 0. */
+       if (req->body_left < count)
+           count = req->body_left;
+
+       memcpy(buffer, req->body_pnt, count);
+       req->body_pnt += count;
+       req->body_left -= count;
+    }
+
+    return count;
+}    
+
+static ssize_t body_fd_send(void *userdata, char *buffer, size_t count)
+{
+    ne_request *req = userdata;
+
+    if (count) {
+       return read(req->body_fd, buffer, count);
+    } else {
+       /* rewind since we may have to send it again */
+       return lseek(req->body_fd, SEEK_SET, 0);
+    }
+}
+
+/* Pulls the request body from the source and pushes it to the given
+ * callback.  Returns 0 on success, or NE_* code */
+int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud)
+{
+    int ret = 0;
+    char buffer[BUFSIZ];
+    size_t bytes;
+    
+    /* tell the source to start again from the beginning. */
+    (void) req->body_cb(req->body_ud, NULL, 0);
+    
+    while ((bytes = req->body_cb(req->body_ud, buffer, BUFSIZ)) > 0) {
+       ret = fn(ud, buffer, bytes);
+       if (ret < 0)
+           break;
+    }
+
+    if (bytes < 0) {
+       ne_set_error(req->session, _("Error reading request body."));
+       ret = NE_ERROR;
+    }
+
+    return ret;
+}
+
+/* Sends the request body down the socket.
+ * Returns 0 on success, or NE_* code */
+static int send_request_body(ne_request *req)
+{
+    int ret = ne_pull_request_body(req, (ne_push_fn)sock_fullwrite, 
+                                  req->session->socket);
+    
+    if (ret < 0) {
+       /* transfer failed */
+       req->forced_close = 1;
+       ret = set_sockerr(req, _("Could not send request body"), ret);
+    }
+
+    return ret;
+}
+
+/* Lob the User-Agent, connection and host headers in to the request
+ * headers */
+static void add_fixed_headers(ne_request *req) 
+{
+    if (req->session->user_agent) {
+       ne_buffer_concat(req->headers, 
+                       "User-Agent: ", req->session->user_agent, EOL, NULL);
+    }
+    /* Send Connection: Keep-Alive for pre-1.1 origin servers, so we
+     * might get a persistent connection. 2068 sec 19.7.1 says we MUST
+     * NOT do this for proxies, though. So we don't.  Note that on the
+     * first request on any session, we don't know whether the server
+     * is 1.1 compliant, so we presume that it is not. */
+    if (ne_version_pre_http11(req->session) && !req->use_proxy) {
+       ne_buffer_zappend(req->headers, "Keep-Alive: " EOL);
+       ne_buffer_zappend(req->headers, "Connection: TE, Keep-Alive");
+    } else {
+       ne_buffer_zappend(req->headers, "Connection: TE");
+    }
+    if (req->upgrade_to_tls) {
+       ne_buffer_zappend(req->headers, ", Upgrade");
+    }
+    ne_buffer_zappend(req->headers, EOL);
+    if (req->upgrade_to_tls) {
+       ne_buffer_zappend(req->headers, "Upgrade: TLS/1.0" EOL);
+    }
+    /* We send TE: trailers since we understand trailers in the chunked
+     * response. */
+    ne_buffer_zappend(req->headers, "TE: trailers" EOL);
+
+}
+
+int ne_accept_always(void *userdata, ne_request *req, ne_status *st)
+{
+    return 1;
+}                                 
+
+int ne_accept_2xx(void *userdata, ne_request *req, ne_status *st)
+{
+    return (st->klass == 2);
+}
+
+/* Exposed since redirect hooks need this.
+ *
+ * DESIGN FLAW: mutating the URI does not get passed down to hooks.
+ * auth hooks need the URI.  */
+void ne_set_request_uri(ne_request *req, const char *uri)
+{
+    ne_buffer *real_uri = ne_buffer_create();
+    req->abs_path = ne_strdup(uri);
+    if (req->use_proxy && strcmp(uri, "*") != 0)
+       ne_buffer_concat(real_uri, ne_get_scheme(req->session), "://", 
+                      req->session->server.hostport, NULL);
+    ne_buffer_zappend(real_uri, uri);
+    req->uri = ne_buffer_finish(real_uri);
+}
+
+/* Handler for the "Transfer-Encoding" response header */
+static void te_hdr_handler(void *userdata, const char *value) 
+{
+    struct ne_response *resp = userdata;
+    if (strcasecmp(value, "chunked") == 0) {
+       resp->is_chunked = 1;
+    } else {
+       resp->is_chunked = 0;
+    }
+}
+
+/* Handler for the "Connection" response header */
+static void connection_hdr_handler(void *userdata, const char *value)
+{
+    ne_request *req = userdata;
+    if (strcasecmp(value, "close") == 0) {
+       req->forced_close = 1;
+    } else if (strcasecmp(value, "Keep-Alive") == 0) {
+       req->can_persist = 1;
+    }
+}
+
+/* Initializes the request with given method and URI.
+ * URI must be abs_path - i.e., NO scheme+hostname. It will BREAK 
+ * otherwise. */
+ne_request *ne_request_create(ne_session *sess,
+                             const char *method, const char *uri) 
+{
+    ne_request *req = ne_calloc(sizeof(ne_request));
+
+    NE_DEBUG(NE_DBG_HTTP, "Creating request...\n");
+
+    req->session = sess;
+    req->headers = ne_buffer_create();
+    req->reqbuf = ne_buffer_create();
+    req->respbuf = ne_buffer_create_sized(BUFSIZ);
+
+    /* Add in the fixed headers */
+    add_fixed_headers(req);
+
+    /* Set the standard stuff */
+    req->method = method;
+    req->method_is_head = (strcmp(req->method, "HEAD") == 0);
+    
+    /* FIXME: the proxy_decider is broken if they called
+     * ne_session_proxy before ne_session_server, since in that
+     * case we have not done a name lookup on the session server.  */
+    if (sess->have_proxy && sess->proxy_decider != NULL) {
+       req->use_proxy = 
+           (*sess->proxy_decider)(sess->proxy_decider_udata,
+                                  ne_get_scheme(sess), sess->server.hostname);
+    }
+    else {
+       req->use_proxy = sess->have_proxy;
+    }
+
+    if (sess->request_secure_upgrade == 1) {
+       req->upgrade_to_tls = 1;
+    }
+
+    /* Add in handlers for all the standard HTTP headers. */
+
+    ne_add_response_header_handler(req, "Content-Length", 
+                                     ne_handle_numeric_header, &req->resp.length);
+    ne_add_response_header_handler(req, "Transfer-Encoding", 
+                                     te_hdr_handler, &req->resp);
+    ne_add_response_header_handler(req, "Connection", 
+                                     connection_hdr_handler, req);
+
+    if (uri) {
+       ne_set_request_uri(req, uri);
+    }
+
+    {
+       struct hook *hk;
+       struct hook_request *store;
+       void *cookie;
+
+       NE_DEBUG(NE_DBG_HTTP, "Running request create hooks.\n");
+
+       for (hk = sess->hooks; hk != NULL; hk = hk->next) {
+           cookie = (*hk->hooks->create)(hk->private, req, method, uri);
+           if (cookie != NULL) {
+               store = ne_malloc(sizeof(struct hook_request));
+               store->hook = hk;
+               store->cookie = cookie;
+               store->next = req->hook_store;
+               req->hook_store = store;
+           }
+       }
+    }
+
+    NE_DEBUG(NE_DBG_HTTP, "Request created.\n");
+
+    return req;
+}
+
+static void set_body_size(ne_request *req, size_t size)
+{
+    req->body_size = size;
+    ne_print_request_header(req, "Content-Length", "%d", size);
+}
+
+void ne_set_request_body_buffer(ne_request *req, const char *buffer,
+                               size_t size)
+{
+    req->body_buffer = buffer;
+    req->body_cb = body_string_send;
+    req->body_ud = req;
+    set_body_size(req, size);
+}
+
+void ne_set_request_body_provider(ne_request *req, size_t bodysize,
+                                 ne_provide_body provider, void *ud)
+{
+    req->body_cb = provider;
+    req->body_ud = ud;
+    set_body_size(req, bodysize);
+}
+
+int ne_set_request_body_fd(ne_request *req, int fd)
+{
+    struct stat bodyst;
+
+    /* Get file length */
+    if (fstat(fd, &bodyst) < 0) {
+       const char *err = strerror(errno);
+       /* Stat failed */
+       snprintf(req->session->error, BUFSIZ,
+                _("Could not find file length: %s"), err);
+       NE_DEBUG(NE_DBG_HTTP, "Stat failed: %s\n", err);
+       return -1;
+    }
+    req->body_fd = fd;
+    req->body_cb = body_fd_send;
+    req->body_ud = req;
+    set_body_size(req, bodyst.st_size);
+    return 0;
+}
+
+void ne_add_request_header(ne_request *req, const char *name, 
+                            const char *value)
+{
+    ne_buffer_concat(req->headers, name, ": ", value, EOL, NULL);
+}
+
+void ne_print_request_header(ne_request *req, const char *name,
+                              const char *format, ...)
+{
+    va_list params;
+    char buf[BUFSIZ];
+    
+    va_start(params, format);
+    vsnprintf(buf, BUFSIZ, format, params);
+    va_end(params);
+    
+    ne_buffer_concat(req->headers, name, ": ", buf, EOL, NULL);
+}
+
+void
+ne_add_response_header_handler(ne_request *req, const char *name, 
+                                ne_header_handler hdl, void *userdata)
+{
+    struct header_handler *new = ne_calloc(sizeof *new);
+    int hash;
+    new->name = lower_string(name);
+    new->handler = hdl;
+    new->userdata = userdata;
+    hash = hdr_hash(new->name);
+    new->next = req->header_handlers[hash];
+    req->header_handlers[hash] = new;
+}
+
+void ne_add_response_header_catcher(ne_request *req, 
+                                     ne_header_handler hdl, void *userdata)
+{
+    struct header_handler *new = ne_calloc(sizeof  *new);
+    new->handler = hdl;
+    new->userdata = userdata;
+    new->next = req->header_catchers;
+    req->header_catchers = new;
+}
+
+void
+ne_add_response_body_reader(ne_request *req, ne_accept_response acpt,
+                              ne_block_reader rdr, void *userdata)
+{
+    struct body_reader *new = ne_malloc(sizeof(struct body_reader));
+    new->accept_response = acpt;
+    new->handler = rdr;
+    new->userdata = userdata;
+    new->next = req->body_readers;
+    req->body_readers = new;
+}
+
+void ne_request_destroy(ne_request *req) 
+{
+    struct body_reader *rdr, *next_rdr;
+    struct header_handler *hdlr, *next_hdlr;
+    struct hook_request *st, *next_st;
+    int n;
+
+    NE_FREE(req->uri);
+    NE_FREE(req->abs_path);
+
+    for (rdr = req->body_readers; rdr != NULL; rdr = next_rdr) {
+       next_rdr = rdr->next;
+       free(rdr);
+    }
+
+    for (hdlr = req->header_catchers; hdlr != NULL; hdlr = next_hdlr) {
+       next_hdlr = hdlr->next;
+       free(hdlr);
+    }
+
+    for (n = 0; n < HH_HASHSIZE; n++) {
+       for (hdlr = req->header_handlers[n]; hdlr != NULL; 
+            hdlr = next_hdlr) {
+           next_hdlr = hdlr->next;
+           free(hdlr->name);
+           free(hdlr);
+       }
+    }
+
+    ne_buffer_destroy(req->headers);
+    ne_buffer_destroy(req->reqbuf);
+    ne_buffer_destroy(req->respbuf);
+
+    NE_DEBUG(NE_DBG_HTTP, "Running destroy hooks.\n");
+    for (st = req->hook_store; st!=NULL; st = next_st) {
+       next_st = st->next;
+       if (HAVE_HOOK(st,destroy)) {
+           HOOK_FUNC(st,destroy)(st->cookie);
+       }
+       free(st);
+    }
+
+    NE_DEBUG(NE_DBG_HTTP, "Request ends.\n");
+    free(req);
+}
+
+
+/* Reads a block of the response into buffer, which is of size buflen.
+ * Returns number of bytes read, 0 on end-of-response, or NE_* on error.
+ * TODO?: only make one actual read() call in here... 
+ */
+static int read_response_block(ne_request *req, struct ne_response *resp, 
+                              char *buffer, size_t *buflen) 
+{
+    int willread, readlen;
+    nsocket *sock = req->session->socket;
+    if (resp->is_chunked) {
+       /* We are doing a chunked transfer-encoding.
+        * It goes:  `SIZE CRLF CHUNK CRLF SIZE CRLF CHUNK CRLF ...'
+        * ended by a `CHUNK CRLF 0 CRLF', a 0-sized chunk.
+        * The slight complication is that we have to cope with
+        * partial reads of chunks.
+        * For this reason, resp.chunk_left contains the number of
+        * bytes left to read in the current chunk.
+        */
+       if (resp->chunk_left == 0) {
+           long int chunk_len;
+           /* We are at the start of a new chunk. */
+           NE_DEBUG(NE_DBG_HTTP, "New chunk.\n");
+           readlen = sock_readline(sock, buffer, *buflen);
+           if (readlen <= 0) {
+               return set_sockerr(req, _("Could not read chunk size"), readlen);
+           }
+           NE_DEBUG(NE_DBG_HTTP, "[Chunk Size] < %s", buffer);
+           chunk_len = strtol(buffer, NULL, 16);
+           if (chunk_len == LONG_MIN || chunk_len == LONG_MAX) {
+               NE_DEBUG(NE_DBG_HTTP, "Couldn't read chunk size.\n");
+               ne_set_error(req->session, _("Could not parse chunk size"));
+               return -1;
+           }
+           NE_DEBUG(NE_DBG_HTTP, "Got chunk size: %ld\n", chunk_len);
+           if (chunk_len == 0) {
+               /* Zero-size chunk == end of response. */
+               NE_DEBUG(NE_DBG_HTTP, "Zero-size chunk.\n");
+               *buflen = 0;
+               return NE_OK;
+           }
+           resp->chunk_left = chunk_len;
+       }
+       willread = min(*buflen - 1, resp->chunk_left);
+    } else if (resp->length > 0) {
+       /* Have we finished reading the body? */
+       if (resp->left == 0) {
+           *buflen = 0;
+           return NE_OK;
+       }
+       willread = min(*buflen - 1, resp->left);
+    } else {
+       /* Read until socket-close */
+       willread = *buflen - 1;
+    }
+    NE_DEBUG(NE_DBG_HTTP, "Reading %d bytes of response body.\n", willread);
+    readlen = sock_read(sock, buffer, willread);
+
+    /* EOF is valid if we don't know the response body length, or
+     * we've read all of the response body, and we're not using
+     * chunked. */
+    if (readlen == SOCK_CLOSED && resp->length <= 0 && !resp->is_chunked) {
+       NE_DEBUG(NE_DBG_HTTP, "Got EOF.\n");
+       readlen = 0;
+    } else if (readlen < 0) {
+       return set_sockerr(req, _("Could not read response body"), readlen);
+    } else {
+       NE_DEBUG(NE_DBG_HTTP, "Got %d bytes.\n", readlen);
+    }
+    buffer[readlen] = '\0';
+    *buflen = readlen;
+    NE_DEBUG(NE_DBG_HTTPBODY, "Read block:\n%s\n", buffer);
+    if (resp->is_chunked) {
+       resp->chunk_left -= readlen;
+       if (resp->chunk_left == 0) {
+           char crlfbuf[2];
+           /* If we've read a whole chunk, read a CRLF */
+           readlen = sock_fullread(sock, crlfbuf, 2);
+           if (readlen < 0 || strncmp(crlfbuf, EOL, 2) != 0) {
+               return set_sockerr(req, 
+                                  _("Error reading chunked response body"),
+                                  readlen);
+           }
+       }
+    } else if (resp->length > 0) {
+       resp->left -= readlen;
+    }
+    return NE_OK;
+}
+
+ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen)
+{
+    struct body_reader *rdr;
+    size_t readlen = buflen;
+
+    if (req->resp.length == 0) {
+       return 0;
+    }
+
+    if (read_response_block(req, &req->resp, buffer, &readlen)) {
+       /* it failed. */
+       req->forced_close = 1;  
+       return -1;
+    }
+
+    if (req->session->progress_cb) {
+       req->resp.total += readlen;
+       req->session->progress_cb(req->session->progress_ud, req->resp.total, 
+                                 req->resp.is_chunked?-1:req->resp.length);
+    }
+
+    /* TODO: call the readers when this fails too. */
+    for (rdr = req->body_readers; rdr!=NULL; rdr=rdr->next) {
+       if (rdr->use)
+           (*rdr->handler)(rdr->userdata, buffer, readlen);
+    }
+    
+    return readlen;
+}
+
+/* Build the request. */
+static const char *build_request(ne_request *req) 
+{
+    const char *uri;
+    struct hook_request *st;
+    ne_buffer *buf = req->reqbuf;
+
+    /* If we are talking to a proxy, we send them the absoluteURI
+     * as the Request-URI. If we are talking to a server, we just 
+     * send abs_path. */
+    if (req->use_proxy)
+       uri = req->uri;
+    else
+       uri = req->abs_path;
+    
+    ne_buffer_clear(buf);
+
+    /* Add in the request and the user-supplied headers */
+    ne_buffer_concat(buf, req->method, " ", uri, " HTTP/1.1" EOL,
+                  req->headers->data, NULL);
+    
+    /* And the all-important Host header.  This is done here since it
+     * might change for a new server. */
+    ne_buffer_concat(buf, "Host: ", req->session->server.hostport, 
+                  EOL, NULL);
+
+    
+    /* Now handle the body. */
+    req->use_expect100 = 0;
+    if ((req->session->expect100_works > -1) &&
+       (req->body_size > HTTP_EXPECT_MINSIZE) && 
+       !ne_version_pre_http11(req->session)) {
+       /* Add Expect: 100-continue. */
+       ne_buffer_zappend(buf, "Expect: 100-continue" EOL);
+       req->use_expect100 = 1;
+    }
+
+    NE_DEBUG(NE_DBG_HTTP, "Running pre_send hooks\n");
+    for (st = req->hook_store; st!=NULL; st = st->next) {
+       if (HAVE_HOOK(st,pre_send)) {
+           HOOK_FUNC(st,pre_send)(st->cookie, buf);
+       }
+    }                                              
+    
+    /* Final CRLF */
+    ne_buffer_zappend(buf, EOL);
+
+    return buf->data;
+}
+
+/* FIXME: this function need re-writing.
+ *
+ * Returns HTTP_*, and sets session error appropriately.
+ */
+static int send_request(ne_request *req)
+{
+    ne_session *sess = req->session;
+    int ret, try_again, send_attempt;
+    const char *request = build_request(req);
+    char *buffer = req->respbuf->data;
+
+    try_again = 1;
+    
+    do {
+
+       /* FIXME: this is broken */
+       try_again--;
+
+#ifdef DEBUGGING
+       { 
+           if ((NE_DBG_HTTPPLAIN&ne_debug_mask) == NE_DBG_HTTPPLAIN) { 
+               /* Display everything mode */
+               NE_DEBUG(NE_DBG_HTTP, "Sending request headers:\n%s", request);
+           } else {
+               /* Blank out the Authorization paramaters */
+               char *reqdebug = ne_strdup(request), *pnt = reqdebug;
+               while ((pnt = strstr(pnt, "Authorization: ")) != NULL) {
+                   for (pnt += 15; *pnt != '\r' && *pnt != '\0'; pnt++) {
+                       *pnt = 'x';
+                   }
+               }
+               NE_DEBUG(NE_DBG_HTTP, "Sending request headers:\n%s", reqdebug);
+               free(reqdebug);
+           }
+       }
+#endif /* DEBUGGING */
+       
+       /* Send the Request-Line and headers */
+       for (send_attempt = 0; send_attempt < 2; send_attempt++) {
+           NE_DEBUG(NE_DBG_HTTP, "Sending headers: attempt %d\n", send_attempt);
+           /* Open the connection if necessary */
+           ret = open_connection(req);
+           if (ret != NE_OK) {
+               return ret;
+           }
+           ret = sock_send_string(req->session->socket, request);
+           if (ret == SOCK_CLOSED) {
+               /* Could happen due to a persistent connection timeout.
+                * Or the server being restarted. */
+               NE_DEBUG(NE_DBG_HTTP, "Connection was closed by server.\n");
+               ne_close_connection(req->session);
+           } else {
+               break;
+           }
+       }
+       
+       if (ret < 0) {
+           return set_sockerr(req, _("Could not send request"), ret);
+       }
+
+       NE_DEBUG(NE_DBG_HTTP, "Request sent\n");
+       
+       /* Now, if we are doing a Expect: 100, hang around for a short
+        * amount of time, to see if the server actually cares about the 
+        * Expect and sends us a 100 Continue response if the request
+        * is valid, else an error code if it's not. This saves sending
+        * big files to the server when they will be rejected.
+        */
+       
+       if (req->use_expect100) {
+           NE_DEBUG(NE_DBG_HTTP, "Waiting for response...\n");
+           ret = sock_block(sess->socket, HTTP_EXPECT_TIMEOUT);
+           switch(ret) {
+           case SOCK_TIMEOUT: 
+               /* Timed out - i.e. Expect: ignored. There is a danger
+                * here that the server DOES respect the Expect: header,
+                * but was going SO slowly that it didn't get time to
+                * respond within HTTP_EXPECT_TIMEOUT.
+                * TODO: while sending the body, check to see if the
+                * server has sent anything back - if it HAS, then
+                * stop sending - this is a spec compliance SHOULD */
+               NE_DEBUG(NE_DBG_HTTP, "Wait timed out.\n");
+               sess->expect100_works = -1; /* don't try that again */
+               /* Try sending the request again without using 100-continue */
+               try_again++;
+               continue;
+               break;
+           case SOCK_CLOSED:
+           case SOCK_ERROR: /* error */
+               return set_sockerr(req, _("Error waiting for response"), ret);
+           default:
+               NE_DEBUG(NE_DBG_HTTP, "Wait got data.\n");
+               sess->expect100_works = 1; /* it works - use it again */
+               break;
+           }
+       } else if (req->body_size) {
+           /* Just chuck the file down the socket */
+           NE_DEBUG(NE_DBG_HTTP, "Sending body...\n");
+           ret = send_request_body(req);
+           if (ret == SOCK_CLOSED) {
+               /* This happens if the persistent connection times out:
+                * the first write() of the headers gets a delayed write
+                * seemingly, so the write doesn't fail till this one.
+                */
+               NE_DEBUG(NE_DBG_HTTP, "Connection closed before request sent, retrying\n");
+               try_again++;
+               ne_close_connection(req->session);
+               continue;
+           } else if (ret < 0) {
+               NE_DEBUG(NE_DBG_HTTP, "Body send failed.\n");
+               return ret;
+           }
+           NE_DEBUG(NE_DBG_HTTP, "Body sent.\n");
+           
+       }
+       
+       /* Now, we have either:
+        *   - Sent the header and body, or
+        *   - Sent the header incl. Expect: line, and got some response.
+        * In any case, we get the status line of the response.
+        */
+       
+       /* HTTP/1.1 says that the server MAY emit any number of
+        * interim 100 (Continue) responses prior to the normal
+        * response.  So loop while we get them.  */
+       
+       do {
+           if (sock_readline(sess->socket, buffer, BUFSIZ) <= 0) {
+               if (try_again) {
+                   return set_sockerr(req, _("Could not read status line"), ret);
+               }
+               NE_DEBUG(NE_DBG_HTTP, "Failed to read status line.\n");
+               try_again++;
+               break;
+           }
+
+           NE_DEBUG(NE_DBG_HTTP, "[Status Line] < %s", buffer);
+           
+           /* Got the status line - parse it */
+           if (ne_parse_statusline(buffer, &req->status)) {
+               ne_set_error(sess, _("Could not parse response status line."));
+               return -1;
+           }
+
+           sess->version_major = req->status.major_version;
+           sess->version_minor = req->status.minor_version;
+           snprintf(sess->error, BUFSIZ, "%d %s", 
+                    req->status.code, req->status.reason_phrase);
+           STRIP_EOL(sess->error);
+
+           if (req->status.klass == 1) {
+               NE_DEBUG(NE_DBG_HTTP, "Got 1xx-class.\n");
+               /* Skip any headers, we don't need them */
+               do {
+                   ret = sock_readline(sess->socket, buffer, BUFSIZ);
+                   if (ret <= 0) {
+                       return set_sockerr(
+                           req, _("Error reading response headers"), ret);
+                   }
+                   NE_DEBUG(NE_DBG_HTTP, "[Ignored header] < %s", buffer);
+               } while (strcmp(buffer, EOL) != 0);
+       
+               if (req->use_expect100 && (req->status.code == 100)) {
+                   /* We are using Expect: 100, and we got a 100-continue 
+                    * return code... send the request body */
+                   NE_DEBUG(NE_DBG_HTTP, "Got continue... sending body now.\n");
+                   HTTP_ERR(send_request_body(req));
+                   NE_DEBUG(NE_DBG_HTTP, "Body sent.\n");
+               } else if (req->upgrade_to_tls && (req->status.code == 101)) {
+                   /* Switch to TLS on the fly */
+                   if (sock_make_secure(sess->socket, sess->ssl_context)) {
+                       set_sockerr(req, _("Could not negotiate SSL session"), 
+                                   SOCK_ERROR);
+                       ne_close_connection(sess);
+                       return NE_ERROR;
+                   }
+               }
+           }
+       } while (req->status.klass == 1);
+
+       if (try_again == 1) {
+           /* If we're trying again, close the conn first */
+           NE_DEBUG(NE_DBG_HTTP, "Retrying request, closing connection first.\n");
+           ne_close_connection(sess);
+       }
+
+    } while (try_again == 1);
+
+    return NE_OK;
+}
+
+/* Read a message header from sock into buf, which has size 'buflen'.
+ *
+ * Returns:
+ *   NE_RETRY: Success, read a header into buf.
+ *   NE_OK: End of headers reached.
+ *   NE_ERROR: Error (session error is set).
+ */
+static int read_message_header(ne_request *req, char *buf, size_t buflen)
+{
+    int ret, n;
+    size_t len; /* holds strlen(buf) */
+    nsocket *sock = req->session->socket;
+
+    n = sock_readline(sock, buf, buflen);
+    if (n <= 0)
+       return set_sockerr(req, _("Error reading response headers"), n);
+    NE_DEBUG(NE_DBG_HTTP, "[hdr] %s", buf);
+
+    STRIP_EOL(buf);
+
+    len = strlen(buf);
+
+    if (len == 0) {
+       NE_DEBUG(NE_DBG_HTTP, "End of headers.\n");
+       return NE_OK;
+    }
+
+    while (buflen > 0) {
+       char ch;
+
+       /* Collect any extra lines into buffer */
+       ret = sock_peek(sock, &ch, 1);
+       if (ret <= 0) {
+           /* FIXME: can sock_peek return 0? */
+           return set_sockerr(req, _("Error reading response headers"), ret);
+       }
+       if (ch != ' ' && ch != '\t') {
+           /* No continuation of this header. NUL-terminate to be paranoid. */
+           return NE_RETRY;
+       }
+
+       /* Otherwise, read the next line onto the end of 'buf'. */
+       buf += len;
+       buflen -= len;
+
+       /* Read BUFSIZ-1 bytes to guarantee that we have a \0 */
+       ret = sock_readline(sock, buf, buflen);
+       if (ret <= 0) {
+           return set_sockerr(req, _("Error reading response headers"), ret);
+       }
+
+       NE_DEBUG(NE_DBG_HTTP, "[cont] %s", buf);
+
+       STRIP_EOL(buf);
+       len = strlen(buf);
+       
+       /* assert(buf[0] == ch), which implies len(buf) > 0.
+        * Otherwise the TCP stack is lying, but we'll be paranoid.
+        * This might be a \t, so replace it with a space to be
+        * friendly to applications (2616 says we MAY do this). */
+       if (len) buf[0] = ' ';
+    }
+
+    ne_set_error(req->session, _("Response header too long"));
+    return NE_ERROR;
+}
+
+static void normalize_response_length(ne_request *req)
+{
+    /* Response entity-body length calculation, bit icky.
+     * Here, we set:
+     * length==-1 if we DO NOT know the exact body length
+     * length>=0 if we DO know the body length.
+     *
+     * RFC2616, section 4.4: 
+     * NO body is returned if the method is HEAD, or the resp status
+     * is 204 or 304
+     */
+    if (req->method_is_head || req->status.code==204 || 
+       req->status.code==304) {
+       req->resp.length = 0;
+    } else {
+       /* RFC2616, section 4.4: if we have a transfer encoding
+        * and a content-length, then ignore the content-length. */
+       if ((req->resp.length>-1) && 
+           (req->resp.is_chunked)) {
+           req->resp.length = -1;
+       }
+    }
+    /* Noddy noddy noddy. Testing from Apache/mod_proxy, CONNECT does
+     * not return a Content-Length... */
+    if (req->resp.length == -1 && req->session->in_connect &&
+       req->status.klass == 2) {
+       req->resp.length = 0;
+    }
+       
+}
+
+/* Apache's default is 100, seems reasonable. */
+#define MAX_HEADER_FIELDS 100
+
+#define MAX_HEADER_LENGTH 8192
+
+/* Read response headers, using buffer buffer.
+ * Returns NE_* code, sets session error. */
+static int read_response_headers(ne_request *req) 
+{
+    char hdr[MAX_HEADER_LENGTH] = {0};
+    int ret, count = 0;
+    
+    /* Read response headers.  This loop was once optimized so that
+     * GCC will put all the local vars in registers, but that was a
+     * long time ago. */
+    while ((ret = read_message_header(req, hdr, MAX_HEADER_LENGTH)) == NE_RETRY 
+          && ++count < MAX_HEADER_FIELDS) {
+       struct header_handler *hdl;
+       /* hint to the compiler that we'd like these in registers */
+       register char *pnt;
+       register int hash = 0;
+       
+       for (hdl = req->header_catchers; hdl != NULL; hdl = hdl->next) {
+           (*hdl->handler)(hdl->userdata, hdr);
+       }
+       
+       /* Iterate over the header name, converting it to lower case and 
+        * calculating the hash value as we go. */
+       for (pnt = hdr; *pnt != '\0' && *pnt != ':'; pnt++) {
+           *pnt = tolower(*pnt);
+           hash = HH_ITERATE(hash,*pnt);
+       }
+
+       if (*pnt != '\0') {
+           /* Null-term name at the : */
+           *pnt = '\0';
+           
+           /* Value starts after any whitespace... */
+           do {
+               pnt++;
+           } while (*pnt == ' ' || *pnt == '\t');
+           
+           NE_DEBUG(NE_DBG_HTTP, "Header Name: [%s], Value: [%s]\n", hdr, pnt);
+           
+           /* Iterate through the header handlers */
+           for (hdl = req->header_handlers[hash]; hdl != NULL; 
+                hdl = hdl->next) {
+               if (strcmp(hdr, hdl->name) == 0) {
+                   (*hdl->handler)(hdl->userdata, pnt);
+               }
+           }
+       } else {
+           ne_set_error(req->session, _("Malformed header line."));
+           return NE_ERROR;
+       }
+    }
+
+    if (count == MAX_HEADER_FIELDS) {
+       ne_set_error(req->session, 
+                      _("Response exceeded maximum number of header fields."));
+       ret = NE_ERROR;
+    }
+
+    return ret;
+}
+
+int ne_begin_request(ne_request *req)
+{
+    struct body_reader *rdr;
+
+    NE_DEBUG(NE_DBG_HTTPBASIC, "Request: %s %s\n", req->method, req->uri);
+
+    req->can_persist = 0;
+    req->forced_close = 0;
+    req->resp.length = -1;
+    req->resp.is_chunked = 0;
+    
+    /* Now send the request, and read the Status-Line */
+    HTTP_ERR(send_request(req));
+    
+    /* Read the headers */
+    HTTP_ERR(read_response_headers(req));
+
+    /* response message logic */
+    normalize_response_length(req);
+
+    /* Prepare for reading the response entity-body.  call each of the
+     * body readers and ask them whether they want to accept this
+     * response or not. */
+    for (rdr = req->body_readers; rdr != NULL; rdr=rdr->next) {
+       rdr->use = (*rdr->accept_response)(rdr->userdata, req, &req->status);
+    }
+    
+    req->resp.left = req->resp.length;
+    req->resp.chunk_left = 0;
+
+    return NE_OK;
+}
+
+int ne_end_request(ne_request *req)
+{
+    struct hook_request *st;
+    int ret = NE_OK;
+
+    NE_DEBUG(NE_DBG_HTTPBASIC, "Response: %s\n", req->session->error);
+
+    /* Read headers in chunked trailers */
+    if (req->resp.is_chunked) {
+       HTTP_ERR(read_response_headers(req));
+    }
+    
+    NE_DEBUG(NE_DBG_HTTP, "Running post_send hooks\n");
+    for (st = req->hook_store; ret == NE_OK && st != NULL; st = st->next) {
+       if (HAVE_HOOK(st,post_send)) {
+           ret = HOOK_FUNC(st,post_send)(st->cookie, &req->status);
+       }
+    }
+    
+    NE_DEBUG(NE_DBG_HTTP, "Connection status: %s, %s, %s\n",
+         req->forced_close?"forced close":"no forced close",
+         req->session->no_persist?"no persistent connection":"persistent connection",
+         ne_version_pre_http11(req->session)?"pre-HTTP/1.1":"HTTP/1.1 or later");
+    
+    /* Close the connection if any of the following are true:
+     *  - We have a forced close (e.g. "Connection: close" header)
+     *  - We are not using persistent connections for this session
+     *  - All of the following are true:
+     *    * this is HTTP/1.0
+     *    * and they haven't said they can do persistent connections 
+     *    * we've not just done a successful CONNECT
+     */
+    if (req->forced_close || req->session->no_persist ||
+       (ne_version_pre_http11(req->session) && 
+        !req->can_persist && 
+        (!req->session->in_connect || req->status.klass != 2))) {
+       ne_close_connection(req->session);
+    }
+    
+    /* Retry if any hook asked us too (i.e. authentication hooks) */
+
+    return ret;
+}
+
+/* HTTP/1.x request/response mechanism 
+ *
+ * Returns an NE_* return code. 
+ *   
+ * The status information is placed in status. The error string is
+ * placed in req->session->error
+ */
+int ne_request_dispatch(ne_request *req) 
+{
+    int ret;
+    ssize_t len;
+
+    /* Loop sending the request:
+     * Retry whilst authentication fails and we supply it. */
+    
+    do {
+       char buffer[BUFSIZ];
+       
+       HTTP_ERR(ne_begin_request(req));
+       
+       do {
+           len = ne_read_response_block(req, buffer, BUFSIZ);
+       } while (len > 0);
+
+       if (len < 0) {
+           return NE_ERROR;
+       }
+
+       ret = ne_end_request(req);
+
+    } while (ret == NE_RETRY);
+
+    NE_DEBUG(NE_DBG_HTTP | NE_DBG_FLUSH, 
+          "Request ends, status %d class %dxx, error line:\n%s\n", 
+          req->status.code, req->status.klass, req->session->error);
+
+    return ret;
+}
+
+const ne_status *ne_get_status(ne_request *req)
+{
+    return &(req->status);
+}
+
+/* Create a CONNECT tunnel through the proxy server.
+ * Returns HTTP_* */
+static int proxy_tunnel(ne_session *sess)
+{
+    /* Hack up an HTTP CONNECT request... */
+    ne_request *req = ne_request_create(sess, "CONNECT", NULL);
+    int ret = NE_OK;
+
+    /* Fudge the URI to be how we want it */
+    req->uri = ne_strdup(sess->server.hostport);
+
+    sess->connected = 1;
+    sess->in_connect = 1;
+
+    ret = ne_request_dispatch(req);
+
+    sess->in_connect = 0;
+
+    if (ret != NE_OK || !sess->connected || 
+       req->status.klass != 2) {
+       /* It failed */
+       ne_set_error(sess, 
+                      _("Could not create SSL connection through proxy server"));
+       ret = NE_ERROR;
+    }
+
+    ne_request_destroy(req);
+    
+    return ret;
+}
+
+static int open_connection(ne_request *req) 
+{
+    ne_session *sess = req->session;
+
+    if (req->use_proxy) {
+       switch(sess->connected) {
+       case 0:
+           /* Make the TCP connection to the proxy */
+           NE_DEBUG(NE_DBG_SOCKET, "Connecting to proxy at %s:%d...\n", 
+                  sess->proxy.hostname, sess->proxy.port);
+           notify_status(sess, ne_conn_connecting, sess->proxy.hostport);
+           sess->socket = sock_connect(sess->proxy.addr, sess->proxy.port);
+           if (sess->socket == NULL) {
+               (void) set_sockerr(req, _("Could not connect to proxy server"), SOCK_ERROR);
+               return NE_CONNECT;
+           }
+           
+           notify_status(sess, ne_conn_connected, sess->proxy.hostport);
+
+           if (sess->progress_cb) {
+               sock_register_progress(sess->socket, sess->progress_cb,
+                                      sess->progress_ud);
+           }
+           sess->connected = 1;
+           /* FALL-THROUGH */
+       case 1:
+           if (sess->use_secure && !sess->in_connect) {
+               int ret;
+               ret = proxy_tunnel(sess);
+               if (ret != NE_OK) {
+                   ne_close_connection(sess);
+                   return ret;
+               }
+               if (sock_make_secure(sess->socket, sess->ssl_context)) {
+                   (void) set_sockerr(req, _("Could not negotiate SSL session"), SOCK_ERROR);
+                   ne_close_connection(sess);
+                   return NE_CONNECT;
+               }
+               sess->connected = 2;
+               notify_status(sess, ne_conn_secure, 
+                          sock_get_version(sess->socket));
+           } else {
+               break;
+           }
+           break;
+       default:
+           /* We've got everything we need */
+           break;          
+       }
+    } else if (sess->connected == 0) {
+
+       NE_DEBUG(NE_DBG_SOCKET, "Connecting to server at %s:%d...\n", 
+              sess->server.hostname, sess->server.port);
+
+       notify_status(sess, ne_conn_connecting, sess->server.hostport);
+       sess->socket = sock_connect(sess->server.addr, sess->server.port);
+           
+       if (sess->socket == NULL) {
+           (void) set_sockerr(req, _("Could not connect to server"), -1);
+           return NE_CONNECT;
+       }
+
+       notify_status(sess, ne_conn_connected, sess->server.hostport);
+
+       if (sess->progress_cb) {
+           sock_register_progress(sess->socket, sess->progress_cb,
+                                  sess->progress_ud);
+       }
+
+       if (sess->use_secure) {
+           NE_DEBUG(NE_DBG_SOCKET, "Starting SSL...\n");
+           if (sock_make_secure(sess->socket, sess->ssl_context)) {
+               (void) set_sockerr(req, _("Could not negotiate SSL session"), SOCK_ERROR);
+               ne_close_connection(sess);
+               return NE_CONNECT;
+           }
+
+           notify_status(sess, ne_conn_secure, sock_get_version(sess->socket));
+
+       }
+
+       sess->connected = 1;
+    }
+    return NE_OK;
+}
diff --git a/neon/src/ne_request.h b/neon/src/ne_request.h
new file mode 100644 (file)
index 0000000..336355f
--- /dev/null
@@ -0,0 +1,271 @@
+/* 
+   HTTP Request Handling
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_REQUEST_H
+#define NE_REQUEST_H
+
+#include "ne_utils.h" /* For ne_status */
+#include "ne_string.h" /* For sbuffer */
+#include "ne_session.h"
+
+BEGIN_NEON_DECLS
+
+#define NE_OK (0)
+#define NE_ERROR (1) /* Generic error; use ne_get_error(session) for message */
+#define NE_LOOKUP (3) /* Name lookup failed */
+#define NE_AUTH (4) /* User authentication failed on server */
+#define NE_AUTHPROXY (5) /* User authentication failed on proxy */
+#define NE_SERVERAUTH (6) /* Server authentication failed */
+#define NE_PROXYAUTH (7) /* Proxy authentication failed */
+#define NE_CONNECT (8) /* Could not connect to server */
+#define NE_TIMEOUT (9) /* Connection timed out */
+#define NE_FAILED (10) /* The precondition failed */
+#define NE_RETRY (11) /* Retry request (ne_end_request ONLY) */
+#define NE_REDIRECT (12) /* See ne_redirect.h */
+
+#define EOL "\r\n"
+
+typedef struct ne_request_s ne_request;
+
+/***** Request Handling *****/
+
+/* Create a new request, with given method and URI. 
+ * Returns request pointer. */
+ne_request *
+ne_request_create(ne_session *sess, const char *method, const char *uri);
+
+/* 'buffer' will be sent as the request body with given request. */
+void ne_set_request_body_buffer(ne_request *req, const char *buffer,
+                               size_t size);
+
+/* Contents of stream will be sent as the request body with the given
+ * request. Returns:
+ *  0 on okay.
+ *  non-zero if could not determine length of file.
+ */
+int ne_set_request_body_fd(ne_request *req, int fd);
+
+/* Callback for providing request body blocks.
+ *
+ * Before each time the body is provided, the callback will be called
+ * once with buflen == 0.  The body may have to be provided >1 time
+ * per request (for authentication retries etc.).
+ *
+ * The callback must return:
+ *        <0           : error, abort.
+ *         0           : ignore 'buffer' contents, end of body.
+ *     0 < x <= buflen : buffer contains x bytes of body data.
+ */
+typedef ssize_t (*ne_provide_body)(void *userdata, 
+                                  char *buffer, size_t buflen);
+
+/* Callback is called to provide blocks of request body. */
+void ne_set_request_body_provider(ne_request *req, size_t size,
+                                 ne_provide_body provider, void *userdata);
+
+/* Handling response bodies... you provide TWO callbacks:
+ *
+ * 1) 'acceptance' callback: determines whether you want to handle the
+ * response body given the response-status information, e.g., if you
+ * only want 2xx responses, say so here.
+ *
+ * 2) 'reader' callback: passed blocks of the response-body as they
+ * arrive, if the acceptance callback returned non-zero.  */
+
+/* 'acceptance' callback type. Return non-zero to accept the response,
+ * else zero to ignore it. */
+typedef int (*ne_accept_response)(
+    void *userdata, ne_request *req, ne_status *st);
+
+/* An 'acceptance' callback which only accepts 2xx-class responses.
+ * Ignores userdata. */
+int ne_accept_2xx(void *userdata, ne_request *req, ne_status *st);
+
+/* An acceptance callback which accepts all responses.  Ignores
+ * userdata. */
+int ne_accept_always(void *userdata, ne_request *req, ne_status *st);
+
+/* The 'reader' callback type */
+typedef void (*ne_block_reader)(
+    void *userdata, const char *buf, size_t len);
+
+/* Add a response reader for the given request, with the given
+ * acceptance function. userdata is passed as the first argument to
+ * the acceptance + reader callbacks. 
+ *
+ * The acceptance callback is called once each time the request is
+ * sent: it may be sent >1 time because of authentication retries etc.
+ * For each time the acceptance callback is called, if it returns
+ * non-zero, blocks of the response body will be passed to the reader
+ * callback as the response is read.  After all the response body has
+ * been read, the callback will be called with a 'len' argument of
+ * zero.
+ */
+void ne_add_response_body_reader(ne_request *req, ne_accept_response accpt,
+                                ne_block_reader rdr, void *userdata);
+
+/* Handle response headers. Each handler is associated with a specific
+ * header field (indicated by name). The handler is then passed the
+ * value of this header field. */
+
+/* The header handler callback type */
+typedef void (*ne_header_handler)(void *userdata, const char *value);
+
+/* Adds a response header handler for the given request. userdata is passed
+ * as the first argument to the header handler, and the 'value' is the
+ * header field value (i.e. doesn't include the "Header-Name: " part").
+ */
+void ne_add_response_header_handler(ne_request *req, const char *name, 
+                                   ne_header_handler hdl, void *userdata);
+
+/* Add handler which is passed ALL header values regardless of name */
+void ne_add_response_header_catcher(ne_request *req, 
+                                   ne_header_handler hdl, void *userdata);
+
+/* Stock header handlers:
+ *  'duplicate': *(char **)userdata = strdup(value)
+ *  'numeric':   *(int *)userdata = atoi(value)
+ * e.g.
+ *   int mynum;
+ *   ne_add_response_header_handler(myreq, "Content-Length",
+ *                                    ne_handle_numeric_handler, &mynum);
+ * ... arranges mynum to be set to the value of the Content-Length header.
+ */
+void ne_duplicate_header(void *userdata, const char *value);
+void ne_handle_numeric_header(void *userdata, const char *value);
+
+/* Adds a header to the request with given name and value. */
+void ne_add_request_header(ne_request *req, const char *name, 
+                          const char *value);
+/* Adds a header to the request with given name, using printf-like
+ * format arguments for the value. */
+void ne_print_request_header(ne_request *req, const char *name,
+                            const char *format, ...)
+#ifdef __GNUC__
+                __attribute__ ((format (printf, 3, 4)))
+#endif /* __GNUC__ */
+;
+
+/* ne_request_dispatch: Sends the given request, and reads the
+ * response. Response-Status information can be retrieve with
+ * ne_get_status(req).
+ *
+ * Returns:
+ *  NE_OK         if request sent + response read okay.
+ *  NE_AUTH       if user authentication failed on origin server
+ *  NE_AUTHPROXY  if user authentication failed on proxy server
+ *  NE_SERVERAUTH server authentication failed
+ *  NE_PROXYAUTH  proxy authentication failed
+ *  NE_CONNECT    could not connect to server/proxy server
+ *  NE_TIMEOUT    connection timed out mid-request
+ *  NE_ERROR      for other errors, and ne_get_error() should
+ *                  return a meaningful error string
+ *
+ * NB: NE_AUTH and NE_AUTHPROXY mean that the USER supplied the
+ * wrong username/password.  SERVER/PROXYAUTH mean that, after the
+ * server has accepted a valid username/password, the server/proxy did
+ * not authenticate the response message correctly.
+ * */
+int ne_request_dispatch(ne_request *req);
+
+/* Returns a pointer to the response status information for the
+ * given request. */
+const ne_status *ne_get_status(ne_request *req)
+
+/* Declare this with attribute const, since we often call it >1 times
+ * with the same argument, and it will return the same thing each
+ * time. This lets the compiler optimize away any subsequent calls
+ * (theoretically).  */
+#ifdef __GNUC__
+                __attribute__ ((const))
+#endif /* __GNUC__ */
+    ;
+
+/* Destroy memory associated with request pointer */
+void ne_request_destroy(ne_request *req);
+
+/* "Caller-pulls" request interface.  This is an ALTERNATIVE interface
+ * to ne_request_dispatch: either use that, or do all this yourself:
+ *
+ * caller must call:
+ *  1. ne_begin_request (fail if returns non-NE_OK)
+ *  2. while(ne_read_response_block(...) > 0) ... loop ...;
+ *  3. ne_end_request
+ * If ne_end_request returns NE_RETRY, MUST go back to 1.
+ */
+int ne_begin_request(ne_request *req);
+int ne_end_request(ne_request *req);
+
+/* Read a block of the response.  buffer must be at least 128 bytes.
+ * 'buflen' must be length of buffer.
+ *
+ * Returns:
+ *  <0 - error, stop reading.
+ *   0 - end of response
+ *  >0 - number of bytes read into buffer.
+ */
+ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen);
+
+/**** Request hooks handling *****/
+
+typedef struct {
+    /* A slight hack? Allows access to the hook private information,
+     * externally... */
+    const char *id; /* id could be a URI to be globally unique */
+    /* e.g. "http://webdav.org/neon/hooks/davlock" */
+    
+    /* Register a new request: return value passed to subsequent
+     * handlers as '_private'. Session cookie passed as 'session'. */
+    void *(*create)(void *cookie, ne_request *req, 
+                   const char *method, const char *uri);
+    /* Just before sending the request: let them add headers if they want */
+    void (*pre_send)(void *_private, ne_buffer *request);
+    /* After sending the request. May return:
+     *  NE_OK     everything is okay
+     *  NE_RETRY  try sending the request again.
+     *  otherwise: request fails.  Returned to _dispatch caller.
+     */
+    int (*post_send)(void *_private, const ne_status *status);
+    /* Clean up after yourself. */
+    void (*destroy)(void *_private);
+} ne_request_hooks;
+
+typedef void (*ne_free_hooks)(void *cookie);
+
+/* Add in hooks.
+ *  'cookie' will be passed to each call to the 'create' handler of the hooks.
+ * If 'free_hooks' is non-NULL, it will called when the session is destroyed,
+ * to free any data associated with 'cookie'.
+ */
+void ne_add_hooks(ne_session *sess, 
+                 const ne_request_hooks *hooks, void *cookie,
+                 ne_free_hooks free_cookie);
+
+/* Return private data for a new request */ 
+void *ne_request_hook_private(ne_request *req, const char *id);
+
+/* Return private data for the session */
+void *ne_session_hook_private(ne_session *sess, const char *id);
+
+END_NEON_DECLS
+
+#endif /* NE_REQUEST_H */
+
diff --git a/neon/src/ne_session.c b/neon/src/ne_session.c
new file mode 100644 (file)
index 0000000..9f730d5
--- /dev/null
@@ -0,0 +1,264 @@
+/* 
+   HTTP session handling
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "ne_session.h"
+#include "ne_alloc.h"
+#include "ne_utils.h"
+
+#include "ne_private.h"
+
+#define NEON_USERAGENT "neon/" NEON_VERSION;
+
+/* Initializes an HTTP session */
+ne_session *ne_session_create(void) 
+{
+    ne_session *sess = ne_calloc(sizeof *sess);
+
+    NE_DEBUG(NE_DBG_HTTP, "HTTP session begins.\n");
+    strcpy(sess->error, "Unknown error.");
+    sess->version_major = -1;
+    sess->version_minor = -1;
+    /* Default expect-100 to OFF. */
+    sess->expect100_works = -1;
+    return sess;
+}
+
+int ne_session_destroy(ne_session *sess) 
+{
+    struct hook *hk;
+
+    NE_DEBUG(NE_DBG_HTTP, "ne_session_destroy called.\n");
+
+    /* Clear the hooks. */
+    hk = sess->hooks;
+    while (hk) {
+       struct hook *nexthk = hk->next;
+       if (hk->free) {
+           hk->free(hk->private);
+       }
+       free(hk);
+       hk = nexthk;
+    }
+
+    NE_FREE(sess->server.hostname);
+    NE_FREE(sess->server.hostport);
+    NE_FREE(sess->proxy.hostport);
+    NE_FREE(sess->user_agent);
+
+    if (sess->connected) {
+       ne_close_connection(sess);
+    }
+
+    free(sess);
+    return NE_OK;
+}
+
+int ne_version_pre_http11(ne_session *s)
+{
+    return (s->version_major<1 || (s->version_major==1 && s->version_minor<1));
+}
+
+static char *get_hostport(struct host_info *host) 
+{
+    size_t len = strlen(host->hostname);
+    char *ret = ne_malloc(len + 10);
+    strcpy(ret, host->hostname);
+    if (host->port != 80) {
+       snprintf(ret + len, 9, ":%d", host->port);
+    }
+    return ret;
+}
+
+static void
+set_hostinfo(struct host_info *info, const char *hostname, int port)
+{
+    NE_FREE(info->hostport);
+    NE_FREE(info->hostname);
+    info->hostname= ne_strdup(hostname);
+    info->port = port;
+    info->hostport = get_hostport(info);
+}
+
+static int lookup_host(ne_session *sess, struct host_info *info)
+{
+    if (sess->notify_cb) {
+       sess->notify_cb(sess->notify_ud, ne_conn_namelookup, info->hostname);
+    }
+    if (sock_name_lookup(info->hostname, &info->addr)) {
+       return NE_LOOKUP;
+    } else {
+       return NE_OK;
+    }
+}
+
+int ne_session_server(ne_session *sess, const char *hostname, int port)
+{
+    if (sess->connected && !sess->have_proxy) {
+       /* Force reconnect */
+       ne_close_connection(sess);
+    }
+    set_hostinfo(&sess->server, hostname, port);
+    /* We do a name lookup on the origin server if either:
+     *  1) we do not have a proxy server
+     *  2) we *might not* have a proxy server (since we have a 'proxy decider' function).
+     */
+    if (!sess->have_proxy || sess->proxy_decider) {
+       return lookup_host(sess, &sess->server);
+    } else {
+       return NE_OK;
+    }
+}
+
+void ne_set_secure_context(ne_session *sess, nssl_context *ctx)
+{
+    sess->ssl_context = ctx;
+}
+
+int ne_set_request_secure_upgrade(ne_session *sess, int req_upgrade)
+{
+#ifdef ENABLE_SSL
+    sess->request_secure_upgrade = req_upgrade;
+    return 0;
+#else
+    return -1;
+#endif
+}
+
+int ne_set_accept_secure_upgrade(ne_session *sess, int acc_upgrade)
+{
+#ifdef ENABLE_SSL
+    sess->accept_secure_upgrade = acc_upgrade;
+    return 0;
+#else
+    return -1;
+#endif
+}
+
+int ne_set_secure(ne_session *sess, int use_secure)
+{
+#ifdef ENABLE_SSL
+    sess->use_secure = use_secure;
+    return 0;
+#else
+    return -1;
+#endif
+}
+
+void ne_session_decide_proxy(ne_session *sess, ne_use_proxy use_proxy,
+                              void *userdata)
+{
+    sess->proxy_decider = use_proxy;
+    sess->proxy_decider_udata = userdata;
+}
+
+int ne_session_proxy(ne_session *sess, const char *hostname, int port)
+{
+    if (sess->connected) {
+       /* Force reconnect */
+       ne_close_connection(sess);
+    }
+    sess->have_proxy = 1;
+    set_hostinfo(&sess->proxy, hostname, port);
+    return lookup_host(sess, &sess->proxy);
+}
+
+void ne_set_error(ne_session *sess, const char *errstring)
+{
+    strncpy(sess->error, errstring, BUFSIZ);
+    sess->error[BUFSIZ-1] = '\0';
+    STRIP_EOL(sess->error);
+}
+
+
+void ne_set_progress(ne_session *sess, 
+                      sock_progress progress, void *userdata)
+{
+    sess->progress_cb = progress;
+    sess->progress_ud = userdata;
+}
+
+void ne_set_status(ne_session *sess,
+                    ne_notify_status status, void *userdata)
+{
+    sess->notify_cb = status;
+    sess->notify_ud = userdata;
+}
+
+void ne_set_expect100(ne_session *sess, int use_expect100)
+{
+    if (use_expect100) {
+       sess->expect100_works = 1;
+    } else {
+       sess->expect100_works = -1;
+    }
+}
+
+void ne_set_persist(ne_session *sess, int persist)
+{
+    sess->no_persist = !persist;
+}
+
+void ne_set_useragent(ne_session *sess, const char *token)
+{
+    static const char *fixed = " " NEON_USERAGENT;
+    NE_FREE(sess->user_agent);
+    CONCAT2(sess->user_agent, token, fixed);
+}
+
+const char *ne_get_server_hostport(ne_session *sess) {
+    return sess->server.hostport;
+}
+
+const char *ne_get_scheme(ne_session *sess)
+{
+    if (sess->use_secure) {
+       return "https";
+    } else {
+       return "http";
+    }
+}
+
+
+const char *ne_get_error(ne_session *sess) {
+    return sess->error;
+}
+
+int ne_close_connection(ne_session *sess)
+{
+    NE_DEBUG(NE_DBG_SOCKET, "Closing connection.\n");
+    if (sess->connected > 0) {
+       sock_close(sess->socket);
+       sess->socket = NULL;
+    }
+    sess->connected = 0;
+    NE_DEBUG(NE_DBG_SOCKET, "Connection closed.\n");
+    return 0;
+}
+
diff --git a/neon/src/ne_session.h b/neon/src/ne_session.h
new file mode 100644 (file)
index 0000000..7397349
--- /dev/null
@@ -0,0 +1,194 @@
+/* 
+   HTTP session handling
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_SESSION_H
+#define NE_SESSION_H 1
+
+#include "ne_socket.h" /* for nssl_context */
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+typedef struct ne_session_s ne_session;
+
+/* 
+ * Session handling:
+ *  Call order:
+ *  0.   sock_init             MANDATORY
+ *  1.   ne_session_create   MANDATORY
+ *  2.   ne_session_proxy    OPTIONAL
+ *  3.   ne_session_server   MANDATORY
+ *  4.   ne_set_*            OPTIONAL
+ *  ...  --- Any HTTP request method ---
+ *  n.   ne_session_destroy  MANDATORY
+ */
+/* Create a new HTTP session */
+ne_session *ne_session_create(void);
+
+/* Finish an HTTP session */
+int ne_session_destroy(ne_session *sess);
+
+/* Prematurely force the connection to be closed for the given
+ * session.  */
+int ne_close_connection(ne_session *sess);
+
+/* Set the server or proxy server to be used for the session.
+ * Returns:
+ *   NE_LOOKUP if the DNS lookup for hostname failed.
+ *   NE_OK otherwise.
+ *
+ * Note that if a proxy is being used, ne_session_proxy should be
+ * called BEFORE ne_session_server, so the DNS lookup can be skipped
+ * on the server. */
+int ne_session_server(ne_session *sess, const char *hostname, int port);
+int ne_session_proxy(ne_session *sess, const char *hostname, int port);
+
+/* Callback to determine whether the proxy server should be used or
+ * not for a request to the given hostname using the given scheme.
+ * Scheme will be "http" or "https" etc.
+ * Must return:
+ *   Zero to indicate the proxy should NOT be used
+ *   Non-zero to indicate the proxy SHOULD be used.
+ */
+typedef int (*ne_use_proxy)(void *userdata,
+                           const char *scheme, const char *hostname);
+
+/* Register the callback for determining whether the proxy server
+ * should be used or not here.  'userdata' will be passed as the first
+ * argument to the callback. The callback is only called if a proxy
+ * server has been set up using ne_session_proxy. 
+ *
+ * This function MUST be called before calling ne_session_server.
+ */
+void ne_session_decide_proxy(ne_session *sess, ne_use_proxy use_proxy,
+                            void *userdata);
+
+/* Set protocol options for session:
+ *   expect100: Defaults to OFF
+ *   persist:   Defaults to ON
+ *
+ * expect100: When set, send the "Expect: 100-continue" request header
+ * with requests with bodies.
+ *
+ * persist: When set, use a persistent connection. (Generally,
+ * you don't want to turn this off.)
+ * */
+void ne_set_expect100(ne_session *sess, int use_expect100);
+void ne_set_persist(ne_session *sess, int persist);
+
+/* Set a progress callback for the session. */
+void ne_set_progress(ne_session *sess, 
+                    sock_progress progress, void *userdata);
+
+typedef enum {
+    ne_conn_namelookup, /* lookup up hostname (info = hostname) */
+    ne_conn_connecting, /* connecting to host (info = hostname) */
+    ne_conn_connected, /* connected to host (info = hostname) */
+    ne_conn_secure /* connection now secure (info = crypto level) */
+} ne_conn_status;
+
+typedef void (*ne_notify_status)(void *userdata, 
+                                ne_conn_status status,
+                                const char *info);
+
+/* Set a status notification callback for the session, to report
+ * connection status. */
+void ne_set_status(ne_session *sess,
+                  ne_notify_status status, void *userdata);
+
+/* Using SSL/TLS connections:
+ *
+ * Session settings:
+ *   secure:                 Defaults to OFF
+ *   secure_context:         No callbacks, any protocol allowed.
+ *   request_secure_upgrade: Defaults to OFF
+ *   accept_secure_ugprade:  Defaults to OFF
+ *
+ * secure_context: When set, specifies the settings to use for secure
+ * connections, and any callbacks (see nsocket.h).  The lifetime of
+ * the context object must be >= to the lifetime of the session
+ * object.
+ *
+ * secure: When set, use a secure (SSL/TLS) connection to the origin
+ * server. This will tunnel (using CONNECT) through the proxy server
+ * if one is being used.
+ *
+ * NB: ne_set_scure will return -1 if SSL is not supported by the
+ * library (i.e., it was not built against OpenSSL), or 0 if it is.
+ * */
+void ne_set_secure_context(ne_session *sess, nssl_context *ctx);
+int ne_set_secure(ne_session *sess, int secure);
+
+/*
+ * NOTE: don't bother using the two _secure_update functions yet.
+ * "secure upgrades" (RFC2817) are not currently supported by any HTTP
+ * servers.
+ * 
+ * request_secure_upgrade: When set, notify the server with each
+ * request made that an upgrade to a secure connection is desired, if
+ * available.
+ *
+ * accept_secure_upgrade: When set, allow a server-requested upgrade
+ * to a secure connection. 
+ *
+ * These return as per ne_set_secure.  */
+int ne_set_request_secure_upgrade(ne_session *sess, int req_upgrade);
+int ne_set_accept_secure_upgrade(ne_session *sess, int acc_upgrade);
+
+/* Sets the user-agent string. neon/VERSION will be appended, to make
+ * the full header "User-Agent: product neon/VERSION".
+ * If this function is not called, the User-Agent header is not sent.
+ * The product string must follow the RFC2616 format, i.e.
+ *       product         = token ["/" product-version]
+ *       product-version = token
+ * where token is any alpha-numeric-y string [a-zA-Z0-9]*
+ */
+void ne_set_useragent(ne_session *sess, const char *product);
+
+/* Determine if next-hop server claims HTTP/1.1 compliance. Returns:
+ *   0 if next-hop server does NOT claim HTTP/1.1 compliance
+ *   non-zero if next-hop server DOES claim HTTP/1.1 compliance
+ * Not that the "next-hop" server is the proxy server if one is being
+ * used, otherwise, the origin server.
+ */
+int ne_version_pre_http11(ne_session *sess);
+
+/* Returns the 'hostport' URI segment for the end-server, e.g.
+ *    "my.server.com:8080"    or    "www.server.com" 
+ *  (port segment is ommitted if == 80) 
+ */
+const char *ne_get_server_hostport(ne_session *sess);
+
+/* Returns the URL scheme being used for the current session.
+ * Does NOT include a trailing ':'. 
+ * Example returns: "http" or "https".
+ */
+const char *ne_get_scheme(ne_session *sess);
+
+/* Set the error string for the session */
+void ne_set_error(ne_session *sess, const char *errstring);
+/* Retrieve the error string for the session */
+const char *ne_get_error(ne_session *sess);
+
+END_NEON_DECLS
+
+#endif /* NE_SESSION_H */
diff --git a/neon/src/ne_socket.c b/neon/src/ne_socket.c
new file mode 100644 (file)
index 0000000..1ab4c1e
--- /dev/null
@@ -0,0 +1,924 @@
+/* 
+   socket handling routines
+   Copyright (C) 1998-2001, Joe Orton <joe@light.plus.com>, 
+   except where otherwise indicated.
+
+   Portions are:
+   Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+   Originally under GPL in Mutt, http://www.mutt.org/
+   Relicensed under LGPL for neon, http://www.webdav.org/neon/
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   The sock_readline() function is:
+
+   Copyright (c) 1999 Eric S. Raymond
+
+   Permission is hereby granted, free of charge, to any person
+   obtaining a copy of this software and associated documentation
+   files (the "Software"), to deal in the Software without
+   restriction, including without limitation the rights to use, copy,
+   modify, merge, publish, distribute, sublicense, and/or sell copies
+   of the Software, and to permit persons to whom the Software is
+   furnished to do so, subject to the following conditions:
+
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+   DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef WIN32
+#include <WinSock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#endif /* WIN32 */
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <errno.h>
+
+#include <fcntl.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif 
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+/* SOCKS support. */
+#ifdef HAVE_SOCKS_H
+#include <socks.h>
+#endif
+
+#include "ne_i18n.h"
+#include "ne_utils.h"
+#include "ne_string.h"
+#include "ne_socket.h"
+#include "ne_alloc.h"
+
+#if defined(BEOS_PRE_BONE)
+#define NEON_WRITE(a,b,c) send(a,b,c,0)
+#define NEON_READ(a,b,c) recv(a,b,c,0)
+#define NEON_CLOSE(s) closesocket(s)
+#elif defined(WIN32)
+#define NEON_WRITE(a,b,c) send(a,b,c,0)
+#define NEON_READ(a,b,c) recv(a,b,c,0)
+#define NEON_CLOSE(s) closesocket(s)
+#else /* really Unix! */
+#define NEON_WRITE(a,b,c) write(a,b,c)
+#define NEON_READ(a,b,c) read(a,b,c)
+#define NEON_CLOSE(s) close(s)
+#endif
+
+#ifdef ENABLE_SSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+/* Whilst the OpenSSL interface *looks* like it is not thread-safe, it
+ * appears to do horrible gymnastics to maintain per-thread global
+ * variables for error reporting. UGH! */
+#define ERROR_SSL_STRING (ERR_reason_error_string(ERR_get_error()))
+
+#endif
+
+/* BeOS doesn't have fd==sockets on anything pre-bone, so see if
+ * we need to drop back to our old ways... 
+ */
+#ifdef __BEOS__
+  #ifndef BONE_VERSION /* if we have BONE this isn't an issue... */
+    #define BEOS_PRE_BONE
+  #endif
+#endif
+
+struct nsocket_s {
+    int fd;
+    const char *error; /* Store error string here */
+    sock_progress progress_cb;
+    void *progress_ud;
+#ifdef ENABLE_SSL
+    SSL *ssl;
+    SSL_CTX *default_ctx;
+#endif
+#ifdef BEOS_PRE_BONE
+#define MAX_PEEK_BUFFER 1024
+    char peeked_bytes[MAX_PEEK_BUFFER];
+    char *peeked_bytes_curpos;
+    int peeked_bytes_avail;
+#endif
+};
+
+struct nssl_context_s {
+#ifdef ENABLE_SSL
+    SSL_CTX *ctx;
+#endif
+    nssl_accept cert_accept;
+    void *accept_ud; /* userdata for callback */
+
+    /* private key prompt callback */
+    nssl_key_prompt key_prompt;
+    void *key_userdata;
+    const char *key_file;
+};
+    
+void sock_register_progress(nsocket *sock, sock_progress cb, void *userdata)
+{
+    sock->progress_cb = cb;
+    sock->progress_ud = userdata;
+}
+
+void sock_call_progress(nsocket *sock, off_t progress, off_t total)
+{
+    if (sock->progress_cb) {
+       sock->progress_cb(sock->progress_ud, progress, total);
+    }
+}
+
+int sock_init(void)
+{
+#ifdef WIN32
+    WORD wVersionRequested;
+    WSADATA wsaData;
+    int err;
+    
+    wVersionRequested = MAKEWORD(2, 2);
+    
+    err = WSAStartup(wVersionRequested, &wsaData);
+    if (err != 0)
+       return -1;
+
+#endif
+
+#ifdef ENABLE_SSL
+    SSL_load_error_strings();
+    SSL_library_init();
+
+    NE_DEBUG(NE_DBG_SOCKET, "Initialized SSL.\n");
+#endif
+
+    return 0;
+}
+
+void sock_exit(void)
+{
+#ifdef WIN32
+    WSACleanup();
+#endif 
+}
+
+/* sock_read is read() with a timeout of SOCKET_READ_TIMEOUT. */
+int sock_read(nsocket *sock, char *buffer, size_t count) 
+{
+    int ret;
+
+    if (count == 0) {
+       NE_DEBUG(NE_DBG_SOCKET, "Passing 0 to sock_read is probably bad.\n");
+       /* But follow normal read() semantics anyway... */
+       return 0;
+    }
+
+    ret = sock_block(sock, SOCKET_READ_TIMEOUT);
+    if (ret == 0) {
+       /* Got data */
+       do {
+#ifdef ENABLE_SSL
+           if (sock->ssl) {
+               ret = SSL_read(sock->ssl, buffer, count);
+           } else {
+#endif
+#ifndef BEOS_PRE_BONE
+               ret = NEON_READ(sock->fd, buffer, count);
+#else
+               if (sock->peeked_bytes_avail > 0) {
+                   /* simply return the peeked bytes.... */
+                   if (count >= sock->peeked_bytes_avail){
+                       /* we have room */
+                       strncpy(buffer, sock->peeked_bytes_curpos, 
+                               sock->peeked_bytes_avail);
+                       ret = sock->peeked_bytes_avail;
+                       sock->peeked_bytes_avail = 0;
+                   } else {
+                       strncpy(buffer, sock->peeked_bytes_curpos, count);
+                       sock->peeked_bytes_curpos += count;
+                       sock->peeked_bytes_avail -= count;
+                       ret = count;
+                   }
+               } else {
+                   ret = recv(sock->fd, buffer, count, 0);
+               }
+#endif
+#ifdef ENABLE_SSL
+           }
+#endif
+       } while (ret == -1 && errno == EINTR);
+       if (ret < 0) {
+           sock->error = strerror(errno);
+           ret = SOCK_ERROR;
+       } else if (ret == 0) {
+           /* This might not or might not be an error depending on
+               the context. */
+           sock->error = _("Connection was closed by server");
+           NE_DEBUG(NE_DBG_SOCKET, "read returned zero.\n");
+           ret = SOCK_CLOSED;
+       }
+    }
+    return ret;
+}
+
+/* sock_peek is recv(...,MSG_PEEK) with a timeout of SOCKET_TIMEOUT.
+ * Returns length of data read or SOCK_* on error */
+int sock_peek(nsocket *sock, char *buffer, size_t count) 
+{
+    int ret;
+    ret = sock_block(sock, SOCKET_READ_TIMEOUT);
+    if (ret < 0) {
+       return ret;
+    }
+    /* Got data */
+#ifdef ENABLE_SSL
+    if (sock->ssl) {
+       ret = SSL_peek(sock->ssl, buffer, count);
+       /* TODO: This is the fetchmail fix as in sock_readline.
+        * Do we really need it? */
+       if (ret == 0) {
+           if (sock->ssl->shutdown) {
+               return SOCK_CLOSED;
+           }
+           if (0 != ERR_get_error()) {
+               sock->error = ERROR_SSL_STRING;
+               return SOCK_ERROR;
+           }
+       }
+    } else {
+#endif
+    do {
+#ifndef BEOS_PRE_BONE
+       ret = recv(sock->fd, buffer, count, MSG_PEEK);
+#else /* we're on BeOS pre-BONE so we need to use the buffer... */
+       if (sock->peeked_bytes_avail > 0) {
+           /* we've got some from last time!!! */
+           if (count >= sock->peeked_bytes_avail) {
+               strncpy(buffer, sock->peeked_bytes_curpos, sock->peeked_bytes_avail);
+               ret = sock->peeked_bytes_avail;
+           } else {
+               strncpy(buffer, sock->peeked_bytes_curpos, count);
+               ret = count;
+           }
+       } else {
+           if (count > MAX_PEEK_BUFFER)
+               count = MAX_PEEK_BUFFER;
+           ret = recv(sock->fd, buffer, count, 0);
+           sock->peeked_bytes_avail = ret;
+           strncpy(sock->peeked_bytes, buffer, ret);
+           sock->peeked_bytes_curpos = sock->peeked_bytes;
+       }
+#endif
+    } while (ret == -1 && errno == EINTR);
+#ifdef ENABLE_SSL
+    }
+#endif
+    /* According to the Single Unix Spec, recv() will return
+     * zero if the socket has been closed the other end. */
+    if (ret == 0) {
+       ret = SOCK_CLOSED;
+    } else if (ret < 0) {
+       sock->error = strerror(errno);
+       ret = SOCK_ERROR;
+    } 
+    return ret;
+}
+
+/* Blocks waiting for read input on the given socket for the given time.
+ * Returns:
+ *    0 if data arrived
+ *    SOCK_TIMEOUT if data did not arrive before timeout
+ *    SOCK_ERROR on error
+ */
+int sock_block(nsocket *sock, int timeout) 
+{
+    struct timeval tv;
+    fd_set fds;
+    int ret;
+
+#ifdef ENABLE_SSL
+    if (sock->ssl) {
+       /* There may be data already available in the 
+        * SSL buffers */
+       if (SSL_pending(sock->ssl)) {
+           return 0;
+       }
+       /* Otherwise, we should be able to select on
+        * the socket as per normal. Probably? */
+    }
+#endif
+#ifdef BEOS_PRE_BONE
+    if (sock->peeked_bytes_avail > 0) {
+        return 0;
+    }
+#endif
+
+    /* Init the fd set */
+    FD_ZERO(&fds);
+    FD_SET(sock->fd, &fds);
+    /* Set the timeout */
+    tv.tv_sec = timeout;
+    tv.tv_usec = 0;
+    do {
+       ret = select(sock->fd+1, &fds, NULL, NULL, &tv);
+    } while (ret == -1 && errno == EINTR);
+
+    switch(ret) {
+    case 0:
+       return SOCK_TIMEOUT;
+    case -1:
+       sock->error = strerror(errno);
+       return SOCK_ERROR;
+    default:
+       return 0;
+    }
+}
+
+/* Send the given line down the socket with CRLF appended. 
+ * Returns 0 on success or SOCK_* on failure. */
+int sock_sendline(nsocket *sock, const char *line) 
+{
+    char *buffer;
+    int ret;
+    CONCAT2(buffer, line, "\r\n");
+    ret = sock_send_string(sock, buffer);
+    free(buffer);
+    return ret;
+}
+
+int sock_readfile_blocked(nsocket *sock, off_t length,
+                         sock_block_reader reader, void *userdata) 
+{
+    char buffer[BUFSIZ];
+    int ret;
+    off_t done = 0;
+    do {
+       ret = sock_read(sock, buffer, BUFSIZ);
+       if (ret < 0) {
+           if (length == -1 && ret == SOCK_CLOSED) {
+               /* Not an error condition. */
+               return 0;
+           }
+           return ret;
+       } 
+       done += ret;
+       sock_call_progress(sock, done, length);
+       (*reader)(userdata, buffer, ret);
+    } while ((length == -1) || (done < length));
+    return 0;
+}
+
+
+/* Send a block of data down the given fd.
+ * Returns 0 on success or SOCK_* on failure */
+int sock_fullwrite(nsocket *sock, const char *data, size_t length) 
+{
+    ssize_t wrote;
+
+#ifdef ENABLE_SSL
+    if (sock->ssl) {
+       /* joe: ssl.h says SSL_MODE_ENABLE_PARTIAL_WRITE must 
+        * be enabled to have SSL_write return < length... 
+        * so, SSL_write should never return < length. */
+       wrote = SSL_write(sock->ssl, data, length);
+       if (wrote >= 0 && (size_t)wrote < length) {
+           NE_DEBUG(NE_DBG_SOCKET, "SSL_write returned less than length!\n");
+           sock->error = ERROR_SSL_STRING;
+           return SOCK_ERROR;
+       }
+    } else {
+#endif
+       const char *pnt = data;
+       size_t sent = 0;
+
+       while (sent < length) {
+           wrote = NEON_WRITE(sock->fd, pnt, length-sent);
+           if (wrote < 0) {
+               if (errno == EINTR) {
+                   continue;
+               } else if (errno == EPIPE) {
+                   return SOCK_CLOSED;
+               } else {
+                   sock->error = strerror(errno);
+                   return SOCK_ERROR;
+               }
+           }
+           sent += wrote;
+           pnt += wrote;
+#ifdef ENABLE_SSL
+       }
+#endif
+    }
+    return 0;
+}
+
+/* Sends the given string down the given socket.
+ * Returns 0 on success or -1 on failure. */
+int sock_send_string(nsocket *sock, const char *data) 
+{
+    return sock_fullwrite(sock, data, strlen(data));
+}
+
+/* This is from from Eric Raymond's fetchmail (SockRead() in socket.c)
+ * since I wouldn't have a clue how to do it properly.
+ * This function is Copyright 1999 (C) Eric Raymond.
+ * Modifications Copyright 2000 (C) Joe Orton
+ */
+int sock_readline(nsocket *sock, char *buf, int len)
+{
+    char *newline, *bp = buf;
+    int n;
+
+    do {
+       /* 
+        * The reason for these gymnastics is that we want two things:
+        * (1) to read \n-terminated lines,
+        * (2) to return the true length of data read, even if the
+        *     data coming in has embedded NULS.
+        */
+#ifdef ENABLE_SSL
+
+       if (sock->ssl) {
+           /* Hack alert! */
+           /* OK...  SSL_peek works a little different from MSG_PEEK
+              Problem is that SSL_peek can return 0 if there is no
+              data currently available.  If, on the other hand, we
+              loose the socket, we also get a zero, but the SSL_read
+              then SEGFAULTS!  To deal with this, we'll check the
+              error code any time we get a return of zero from
+              SSL_peek.  If we have an error, we bail.  If we don't,
+              we read one character in SSL_read and loop.  This
+              should continue to work even if they later change the
+              behavior of SSL_peek to "fix" this problem...  :-(*/
+           NE_DEBUG(NE_DBG_SOCKET, "SSL readline... \n");
+           if ((n = SSL_peek(sock->ssl, bp, len)) < 0) {
+               sock->error = ERROR_SSL_STRING;
+               return(-1);
+           }
+           if (0 == n) {
+               /* SSL_peek says no data...  Does he mean no data
+                  or did the connection blow up?  If we got an error
+                  then bail! */
+               NE_DEBUG(NE_DBG_SOCKET, "SSL_Peek says no data!\n");
+               /* Check properly to see if the connection has closed */
+               if (sock->ssl->shutdown) {
+                   NE_DEBUG(NE_DBG_SOCKET, "SSL says shutdown.");
+                   return SOCK_CLOSED;
+               } else if (0 != (n = ERR_get_error())) {
+                   NE_DEBUG(NE_DBG_SOCKET, "SSL error occured.\n");
+                   sock->error = ERROR_SSL_STRING;
+                   return -1;
+               }
+                   
+               /* We didn't get an error so read at least one
+                  character at this point and loop */
+               n = 1;
+               /* Make sure newline start out NULL!  We don't have a
+                * string to pass through the strchr at this point yet
+                * */
+               newline = NULL;
+           } else if ((newline = memchr(bp, '\n', n)) != NULL)
+               n = newline - bp + 1;
+           n = SSL_read(sock->ssl, bp, n);
+           NE_DEBUG(NE_DBG_SOCKET, "SSL_read returned %d\n", n);
+           if (n == -1) {
+               sock->error = ERROR_SSL_STRING;
+               return(-1);
+           }
+           /* Check for case where our single character turned out to
+            * be a newline...  (It wasn't going to get caught by
+            * the strchr above if it came from the hack...). */
+           if (NULL == newline && 1 == n && '\n' == *bp) {
+               /* Got our newline - this will break
+                               out of the loop now */
+               newline = bp;
+           }
+       } else {
+#endif
+           if ((n = sock_peek(sock, bp, len)) <= 0)
+               return n;
+           if ((newline = memchr(bp, '\n', n)) != NULL)
+               n = newline - bp + 1;
+           if ((n = sock_read(sock, bp, n)) < 0)
+               return n;
+#ifdef ENABLE_SSL
+       }
+#endif
+       bp += n;
+       len -= n;
+       if (len < 1) {
+           sock->error = _("Line too long");
+           return SOCK_FULL;
+       }
+    } while (!newline && len);
+    *bp = '\0';
+    return bp - buf;
+}
+
+/*** End of ESR-copyrighted section ***/
+
+/* Reads readlen bytes from fd and write to sock.
+ * If readlen == -1, then it reads from srcfd until EOF.
+ * Returns number of bytes written to destfd, or -1 on error.
+ */
+int sock_transfer(int fd, nsocket *sock, off_t readlen) 
+{
+    char buffer[BUFSIZ];
+    size_t curlen; /* total bytes yet to read from srcfd */
+    off_t sumwrlen; /* total bytes written to destfd */
+
+    if (readlen == -1) {
+       curlen = BUFSIZ; /* so the buffer size test works */
+    } else {
+       curlen = readlen; /* everything to do */
+    }
+    sumwrlen = 0; /* nowt done yet */
+
+    while (curlen > 0) {
+       int rdlen, wrlen;
+
+       /* Get a chunk... if the number of bytes that are left to read
+        * is less than the buffer size, only read that many bytes. */
+       rdlen = read(fd, buffer, (readlen==-1)?BUFSIZ:(min(BUFSIZ, curlen)));
+       sock_call_progress(sock, sumwrlen, readlen);
+       if (rdlen < 0) { 
+           if (errno == EPIPE) {
+               return SOCK_CLOSED;
+           } else {
+               sock->error = strerror(errno);
+               return SOCK_ERROR;
+           }
+       } else if (rdlen == 0) { 
+           /* End of file... get out of here */
+           break;
+       }
+       if (readlen != -1)
+           curlen -= rdlen;
+
+       /* Otherwise, we have bytes!  Write them to destfd */
+       
+       wrlen = sock_fullwrite(sock, buffer, rdlen);
+       if (wrlen < 0) { 
+           return wrlen;
+       }
+
+       sumwrlen += rdlen;
+    }
+    sock_call_progress(sock, sumwrlen, readlen);
+    return sumwrlen;
+}
+
+/* Reads buflen bytes into buffer until it's full.
+ * Returns 0 on success, -1 on error */
+int sock_fullread(nsocket *sock, char *buffer, int buflen) 
+{
+    char *pnt; /* current position within buffer */
+    int len;
+    pnt = buffer;
+    while (buflen > 0) {
+       len = sock_read(sock, pnt, buflen);
+       if (len < 0) return len;
+       buflen -= len;
+       pnt += len;
+    }
+    return 0;
+}
+
+/* Do a name lookup on given hostname, writes the address into
+ * given address buffer. Return -1 on failure.
+ */
+int sock_name_lookup(const char *hostname, struct in_addr *addr) 
+{
+    struct hostent *hp;
+    unsigned long laddr;
+    
+    /* TODO?: a possible problem here, is that if we are passed an
+     * invalid IP address e.g. "500.500.500.500", then this gets
+     * passed to gethostbyname and returned as "Host not found".
+     * Arguably wrong, but maybe difficult to detect correctly what is
+     * an invalid IP address and what is a hostname... can hostnames
+     * begin with a numeric character? */
+    laddr = (unsigned long)inet_addr(hostname);
+    if ((int)laddr == -1) {
+       /* inet_addr failed. */
+       hp = gethostbyname(hostname);
+       if (hp == NULL) {
+#if 0
+           /* Need to get this back somehow, but we don't have 
+            * an nsocket * yet... */
+           switch(h_errno) {
+           case HOST_NOT_FOUND:
+               sock->error = _("Host not found");
+               break;
+           case TRY_AGAIN:
+               sock->error = _("Host not found (try again later?)");
+               break;
+           case NO_ADDRESS:
+               sock->error = _("Host exists but has no address.");
+               break;
+           case NO_RECOVERY:
+           default:
+               sock->error = _("Non-recoverable error in resolver library.");
+               break;
+           }
+#endif
+           return SOCK_ERROR;
+       }
+       memcpy(addr, hp->h_addr, hp->h_length);
+    } else {
+       addr->s_addr = laddr;
+    }
+    return 0;
+}
+
+static nsocket *create_sock(int fd)
+{
+    nsocket *sock = ne_calloc(sizeof *sock);
+#ifdef ENABLE_SSL
+    sock->default_ctx = SSL_CTX_new(SSLv23_client_method());
+#endif
+    sock->fd = fd;
+    return sock;
+}
+
+/* Opens a socket to the given port at the given address.
+ * Returns -1 on failure, or the socket on success. 
+ * portnum must be in HOST byte order */
+nsocket *sock_connect(const struct in_addr addr, 
+                     unsigned short int portnum)
+{
+    struct sockaddr_in sa;
+    int fd;
+
+    /* Create the socket */
+    fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (fd < 0)
+       return NULL;
+    /* Connect the nsocket */
+    sa.sin_family = AF_INET;
+    sa.sin_port = htons(portnum); /* host -> net byte orders */
+    sa.sin_addr = addr;
+    if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
+       (void) NEON_CLOSE(fd);
+       return NULL;
+    }
+    /* Success - return the nsocket */
+    return create_sock(fd);
+}
+
+nsocket *sock_accept(int listener) 
+{
+    int fd = accept(listener, NULL, NULL);
+    if (fd > 0) {
+       return create_sock(fd);
+    } else {
+       return NULL;
+    }
+}
+
+int sock_get_fd(nsocket *sock)
+{
+    return sock->fd;
+}
+
+nssl_context *sock_create_ssl_context(void)
+{
+    nssl_context *ctx = ne_calloc(sizeof *ctx);
+#ifdef ENABLE_SSL
+    ctx->ctx = SSL_CTX_new(SSLv23_client_method());
+#endif
+    return ctx;
+}
+
+void sock_destroy_ssl_context(nssl_context *ctx)
+{
+#ifdef ENABLE_SSL
+    SSL_CTX_free(ctx->ctx);
+#endif
+    free(ctx);
+}
+
+#ifdef ENABLE_SSL
+void sock_disable_tlsv1(nssl_context *c)
+{
+    SSL_CTX_set_options(c->ctx, SSL_OP_NO_TLSv1);
+}
+void sock_disable_sslv2(nssl_context *c)
+{
+    SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv2);
+
+}
+void sock_disable_sslv3(nssl_context *c)
+{
+    SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv3);
+}
+
+/* The callback neon installs with OpenSSL for giving the private key
+ * prompt.  FIXME: WTH is 'rwflag'? */
+static int key_prompt_cb(char *buf, int len, int rwflag, void *userdata)
+{
+    nssl_context *ctx = userdata;
+    int ret;
+    ret = ctx->key_prompt(ctx->key_userdata, ctx->key_file, buf, len);
+    if (ret)
+       return -1;
+    /* Obscurely OpenSSL requires the callback to return the length of
+     * the password, this seems a bit weird so we don't expose this in
+     * the neon API. */
+    return strlen(buf);
+}
+
+void sock_set_key_prompt(nssl_context *ctx, 
+                        nssl_key_prompt prompt, void *userdata)
+{
+    SSL_CTX_set_default_passwd_cb(ctx->ctx, key_prompt_cb);
+    SSL_CTX_set_default_passwd_cb_userdata(ctx->ctx, ctx);
+    ctx->key_prompt = prompt;
+    ctx->key_userdata = userdata;
+}
+
+#else
+void sock_disable_tlsv1(nssl_context *c) {}
+void sock_disable_sslv2(nssl_context *c) {}
+void sock_disable_sslv3(nssl_context *c) {}
+void sock_set_key_prompt(nssl_context *c, nssl_key_prompt p, void *u) {}
+#endif
+
+int sock_make_secure(nsocket *sock, nssl_context *ctx)
+{
+#ifdef ENABLE_SSL
+    int ret;
+    SSL_CTX *ssl_ctx;
+
+    if (ctx) {
+       ssl_ctx = ctx->ctx;
+    } else {
+       ssl_ctx = sock->default_ctx;
+    }
+
+    sock->ssl = SSL_new(ssl_ctx);
+    if (!sock->ssl) {
+       sock->error = ERROR_SSL_STRING;
+       /* Usually goes wrong because: */
+       fprintf(stderr, "Have you called sock_init()!?\n");
+       return SOCK_ERROR;
+    }
+    
+#ifdef SSL_MODE_AUTO_RETRY
+    /* OpenSSL 0.9.6 and later... */
+    (void) SSL_set_mode(sock->ssl, SSL_MODE_AUTO_RETRY);
+#endif
+
+    SSL_set_fd(sock->ssl, sock->fd);
+    
+    ret = SSL_connect(sock->ssl);
+    if (ret == -1) {
+       sock->error = ERROR_SSL_STRING;
+       SSL_free(sock->ssl);
+       sock->ssl = NULL;
+       return SOCK_ERROR;
+    }
+
+#if 0
+    /* Tommi Komulainen <Tommi.Komulainen@iki.fi> has donated his SSL
+     * cert verification from the mutt IMAP/SSL code under the
+     * LGPL... it will plug in here */
+    ret = sock_check_certicate(sock);
+    if (ret) {
+       SSL_shutdown(sock->ssl);
+       SSL_free(sock->ssl);
+       sock->ssl = NULL;
+       return ret;
+    }
+#endif
+
+    NE_DEBUG(NE_DBG_SOCKET, "SSL connected: version %s\n", 
+          SSL_get_version(sock->ssl));
+    return 0;
+#else
+    sock->error = _("This application does not have SSL support.");
+    return SOCK_ERROR;
+#endif
+}
+
+const char *sock_get_version(nsocket *sock)
+{
+#ifdef ENABLE_SSL
+    return SSL_get_version(sock->ssl);
+#else
+    return NULL;
+#endif
+}
+
+const char *sock_get_error(nsocket *sock)
+{
+    return sock->error;
+}
+
+/* Closes given nsocket */
+int sock_close(nsocket *sock) {
+    int ret;
+#ifdef ENABLE_SSL
+    if (sock->ssl) {
+       SSL_shutdown(sock->ssl);
+       SSL_free(sock->ssl);
+    }
+    SSL_CTX_free(sock->default_ctx);
+#endif
+    ret = NEON_CLOSE(sock->fd);
+    free(sock);
+    return ret;
+}
+
+/* FIXME: get error messages back to the caller. */   
+int sock_set_client_cert(nssl_context *ctx, const char *cert, const char *key)
+{
+#ifdef ENABLE_SSL
+    if (SSL_CTX_use_certificate_file(ctx->ctx, cert, SSL_FILETYPE_PEM) <= 0) {
+       NE_DEBUG(NE_DBG_SOCKET, "Could not load cert file.\n");
+       return -1;
+    }
+    
+    /* The cert file can contain the private key too, apparently. Not
+     * sure under what circumstances this is sensible, but hey. */
+    if (key == NULL)
+       key = cert;
+    
+    /* Set this so the callback can tell the user what's going on. */
+    ctx->key_file = key;
+    
+    if (SSL_CTX_use_PrivateKey_file(ctx->ctx, key, SSL_FILETYPE_PEM) <= 0) {
+       NE_DEBUG(NE_DBG_SOCKET, "Could not load private key file.\n");
+       return -1;
+    }
+
+    /* Sanity check. */
+    if (!SSL_CTX_check_private_key(ctx->ctx)) {
+       NE_DEBUG(NE_DBG_SOCKET, "Private key does not match certificate.\n");
+       return -1;
+    }
+
+    return 0;
+#else
+    return -1;
+#endif
+}
+
+/* Returns HOST byte order port of given name */
+int sock_service_lookup(const char *name) {
+    struct servent *ent;
+    ent = getservbyname(name, "tcp");
+    if (ent == NULL) {
+       return 0;
+    } else {
+       return ntohs(ent->s_port);
+    }
+}
diff --git a/neon/src/ne_socket.h b/neon/src/ne_socket.h
new file mode 100644 (file)
index 0000000..d0e0cf9
--- /dev/null
@@ -0,0 +1,228 @@
+/* 
+   socket handling interface
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_SOCKET_H
+#define NE_SOCKET_H
+
+#ifdef WIN32
+#include <WinSock2.h>
+#include <stddef.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+#define SOCK_ERROR -1
+/* Read/Write timed out */
+#define SOCK_TIMEOUT -2
+/* Passed buffer was full */
+#define SOCK_FULL -3
+/* Socket was closed */
+#define SOCK_CLOSED -4
+
+/* Socket read timeout */
+#define SOCKET_READ_TIMEOUT 120
+
+struct nsocket_s;
+typedef struct nsocket_s nsocket;
+
+typedef void (*sock_block_reader) (
+    void *userdata, const char *buf, size_t len);
+
+typedef void (*sock_progress)(void *userdata, off_t progress, off_t total);
+
+void sock_register_progress(nsocket *sock, sock_progress cb, void *userdata);
+
+void sock_call_progress(nsocket *sock, off_t progress, off_t total);
+
+/* Initialize the socket library. If you don't do this, SSL WILL NOT WORK.
+ * Returns 0 on success, or non-zero on screwed up SSL library. */
+int sock_init(void);
+
+/* Shutdown the socket library. */
+void sock_exit(void);
+
+/* sock_read is read() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ *   SOCK_* on error,
+ *    0 on no data to read (due to EOF),
+ *   >0 length of data read into buffer.
+ */
+int sock_read(nsocket *sock, char *buffer, size_t count);
+
+/* sock_peek is recv() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ *   SOCK_* on error,
+ *    0 on no data to read (due to EOF),
+ *   >0 length of data read into buffer.
+ */
+int sock_peek(nsocket *sock, char *buffer, size_t count);
+
+/* Blocks waiting for data on the given socket for the given time.
+ * Returns:
+ *  SOCK_* on error,
+ *  SOCK_TIMEOUT on no data within timeout,
+ *  0 if data arrived on the socket.
+ */
+int sock_block(nsocket *sock, int timeout);
+
+/* Reads readlen bytes from fd and writes to socket.
+ * (Not all in one go, obviously).
+ * If readlen == -1, then it reads from srcfd until EOF.
+ * Returns number of bytes written to destfd, or SOCK_* on error.
+ */
+int sock_transfer(int fd, nsocket *sock, off_t readlen);
+
+/* Sends the given line to given socket, CRLF appended */
+int sock_sendline(nsocket *sock, const char *line); 
+/* Sends the given block of data down the nsocket */
+int sock_fullwrite(nsocket *sock, const char *data, size_t length); 
+/* Sends the null-terminated string down the given nsocket */
+int sock_send_string(nsocket *sock, const char *string); 
+
+/* Reads a line from given nsocket */
+int sock_readline(nsocket *sock, char *line, int len); 
+/* Reads a chunk of data. */
+int sock_fullread(nsocket *sock, char *buffer, int buflen);
+
+/* Creates and connects a nsocket */
+nsocket *sock_connect(const struct in_addr host, 
+                     unsigned short int portnum);
+
+/* Not as good as accept(2), missing parms 2+3.
+ * Addings parms 2+3 would probably mean passing socklen_t as an
+ * int then casting internally, since we don't really want to
+ * autogenerate the header file to be correct for the build platform.
+ */
+nsocket *sock_accept(int listener);
+
+/* Returns the file descriptor used for the socket */
+int sock_get_fd(nsocket *sock);
+
+/* Closes the socket and frees the nsocket object. */
+int sock_close(nsocket *sock);
+
+const char *sock_get_version(nsocket *sock);
+
+const char *sock_get_error(nsocket *sock);
+
+/* Do a name lookup on given hostname, writes the address into
+ * given address buffer. Return -1 on failure. */
+int sock_name_lookup(const char *hostname, struct in_addr *addr);
+
+/* Returns the standard TCP port for the given service */
+int sock_service_lookup(const char *name);
+
+/* Read from socket, passing each block read to reader callback.
+ * Pass userdata as first argument to reader callback.
+ *
+ * If length is -1, keep going till EOF is returned. SOCK_CLOSED
+ * is never returned in this case.
+ *
+ * Otherwise, read exactly 'length' bytes. If EOF is encountered
+ * before length bytes have been read, and SOCK_CLOSED will be
+ * returned.
+ *
+ * Returns:
+ *   0 on success,
+ *   SOCK_* on error (SOCK_CLOSED is a special case, as above)
+ */
+int sock_readfile_blocked(nsocket *sock, off_t length,
+                         sock_block_reader reader, void *userdata);
+
+/* Auxiliary for use with SSL. */
+struct nssl_context_s;
+typedef struct nssl_context_s nssl_context;
+
+/* Netscape's prompts on getting a certificate which it doesn't
+ * recognize the CA for:
+ *   1. Hey, I don't recognize the CA for this cert.
+ *   2. Here is the certificate: for foo signed by BLAH,
+ *      using encryption level BLEE
+ *   3. Allow: accept for this session only, 
+ *             don't accept
+ *             accept forever
+ */
+nssl_context *sock_create_ssl_context(void);
+
+void sock_destroy_ssl_context(nssl_context *ctx);
+
+/* Callback to decide whether the user will accept the
+ * given certificate or not */
+typedef struct {
+    char *owner; /* Multi-line string describing owner of
+                 * certificate */
+    char *issuer; /* As above for issuer of certificate */
+    /* Strings the certificate is valid between */
+    char *valid_from, *valid_till;
+    /* Certificate fingerprint */
+    char *fingerprint;
+} nssl_certificate;
+
+/* Returns:
+ *   0 -> User accepts the certificate
+ *   non-zero -> user does NOT accept the certificate.
+ */
+typedef int (*nssl_accept)(void *userdata, const nssl_certificate *info);
+
+void sock_set_cert_accept(nssl_context *c, 
+                         nssl_accept accepter, void *userdata);
+
+/* Callback for retrieving the private key password.
+ * Filename will be the filename of the private key file.
+ * Must return:
+ *    0 on success.  buf must be filled in with the password.
+ *  non-zero if the user cancelled the prompt.
+ *
+ * FIXME: this is inconsistent with the HTTP authentication callbacks.  */
+typedef int (*nssl_key_prompt)(void *userdata, const char *filename,
+                              char *buf, int buflen);
+
+void sock_set_key_prompt(nssl_context *c, 
+                        nssl_key_prompt prompt, void *userdata);
+
+/* For PEM-encoded client certificates: use the given client
+ * certificate and private key file. 
+ * Returns: 0 if certificate is read okay,
+ * non-zero otherwise.
+
+ * For decoding the private key file, the callback above will be used
+ * to prompt for the password.  If no callback has been set, then the
+ * OpenSSL default will be used: the prompt appears on the terminal.
+ * */
+int sock_set_client_cert(nssl_context *ctx, const char *certfile,
+                        const char *keyfile);
+
+void sock_disable_tlsv1(nssl_context *c);
+void sock_disable_sslv2(nssl_context *c);
+void sock_disable_sslv3(nssl_context *c);
+
+/* Ctx is OPTIONAL. If it is NULL, defaults are used. */
+int sock_make_secure(nsocket *sock, nssl_context *ctx);
+
+END_NEON_DECLS
+
+#endif /* NE_SOCKET_H */
diff --git a/neon/src/ne_string.c b/neon/src/ne_string.c
new file mode 100644 (file)
index 0000000..b205eb3
--- /dev/null
@@ -0,0 +1,469 @@
+/* 
+   String utility functions
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_alloc.h"
+#include "ne_string.h"
+
+char *ne_token(char **str, char separator, const char *quotes)
+{
+    char *pnt, *ret = NULL;
+
+    if (quotes) {
+
+       for (pnt = *str; *pnt != '\0'; pnt++) {
+           char *quot = strchr(quotes, *pnt);
+           
+           if (quot) {
+               char *qclose = strchr(pnt+1, *quot);
+               
+               if (!qclose) {
+                   /* no closing quote: invalid string. */
+                   return NULL;
+               }
+               
+               pnt = qclose;
+           } else if (*pnt == separator) {
+               /* found end of token. */
+               *pnt = '\0';
+               ret = *str;
+               *str = pnt + 1;
+               break;
+           }
+       }
+
+    } else {
+       pnt = strchr(*str, separator);
+       if (pnt != NULL) {
+           *pnt = '\0';
+           ret = *str;
+           *str = pnt + 1;
+       }
+    }
+
+    if (ret == NULL) {
+       /* no separator found: return end of string. */
+       ret = *str;
+       *str = NULL;
+    }
+
+    return ret;
+}
+
+char *ne_shave(char *str, const char *whitespace)
+{
+    char *pnt, *ret = str;
+
+    while (strchr(whitespace, *ret) != NULL) {
+       ret++;
+    }
+
+    /* pnt points at the NUL terminator. */
+    pnt = &ret[strlen(ret)];
+    
+    while (pnt > ret && strchr(whitespace, *(pnt-1)) != NULL) {
+       pnt--;
+    }
+
+    *pnt = '\0';
+    return ret;
+}
+
+/* TODO: deprecate all these and use ne_token() instead. */
+
+char **split_string(const char *str, const char separator,
+                    const char *quotes, const char *whitespace) 
+{
+    return split_string_c(str, separator, quotes, whitespace, NULL);
+}
+
+char **split_string_c(const char *str, const char separator,
+                     const char *quotes, const char *whitespace,
+                     int *give_count) 
+{
+    char **comps;
+    const char *pnt, *quot = NULL,
+       *start, *end; /* The start of the current component */
+    int count, /* The number of components */
+       iswhite, /* is it whitespace */
+       issep, /* is it the separator */
+       curr, /* current component index */
+       length, /* length of component */
+       leading_wspace; /* in leading whitespace still? */
+
+    /* Inefficient, but easier - first off, count the number of 
+     * components we have. */
+    count = 1;
+    for (pnt = str; *pnt!='\0'; pnt++) {
+       if (quotes != NULL) {
+           quot = strchr(quotes, *pnt);
+       }
+       if (quot != NULL) {
+           /* We found a quote, so skip till the next quote */
+           for (pnt++; (*pnt!=*quot) && (*pnt!='\0'); pnt++)
+               /* nullop */;
+       } else if (*pnt == separator) {
+           count++;
+       }
+    }
+
+    if (give_count) {
+       /* Write the count */
+       *give_count = count;
+    }
+
+    /* Now, have got the number of components.
+     * Allocate the comps array. +1 for the NULL */
+    comps = ne_malloc(sizeof(char *) * (count + 1));
+
+    comps[count] = NULL;
+    
+    quot = end = start = NULL;
+    curr = 0;
+    leading_wspace = 1;
+
+    /* Now fill in the array */
+    for (pnt = str; *pnt != '\0'; pnt++) {
+       /* What is the current character - quote, whitespace, separator? */
+       if (quotes != NULL) {
+           quot = strchr(quotes, *pnt);
+       }
+       iswhite = (whitespace!=NULL) && 
+           (strchr(whitespace, *pnt) != NULL);
+       issep = (*pnt == separator);
+       /* What to do? */
+       if (leading_wspace) {
+           if (quot!=NULL) {
+               /* Quoted bit */
+               start = pnt;
+               length = 1;
+               leading_wspace = 0;
+           } else if (issep) {
+               /* Zero-length component */
+               comps[curr++] = ne_strdup("");
+           } else if (!iswhite) {
+               start = end = pnt;
+               length = 1;
+               leading_wspace = 0;
+           }
+       } else {
+           if (quot!=NULL) {
+               /* Quoted bit */
+               length++;
+           } else if (issep) {
+               /* End of component - enter it into the array */
+               length = (end - start) + 1;
+               comps[curr] = ne_malloc(length+1);
+               memcpy(comps[curr], start, length);
+               comps[curr][length] = '\0';
+               curr++;
+               leading_wspace = 1;
+           } else if (!iswhite) {
+               /* Not whitespace - update end marker */
+               end = pnt;
+           }
+       }
+       if (quot != NULL) {
+           /* Skip to closing quote */
+           for (pnt++; *pnt!=*quot && *pnt != '\0'; ++pnt)
+               /* nullop */;
+           /* Last non-wspace char is closing quote */
+           end = pnt;
+       }
+    }
+    /* Handle final component */
+    if (leading_wspace) {
+       comps[curr] = ne_strdup("");
+    } else {
+       /* End of component - enter it into the array */
+       length = (end - start) + 1;
+       comps[curr] = ne_malloc(length+1);
+       memcpy(comps[curr], start, length);
+       comps[curr][length] = '\0';
+    }
+    return comps;
+}
+
+char **pair_string(const char *str, const char compsep, const char kvsep, 
+                const char *quotes, const char *whitespace) 
+{
+    char **comps, **pairs, *split;
+    int count = 0, n, length;
+    comps = split_string_c(str, compsep, quotes, whitespace, &count);
+    /* Allocate space for 2* as many components as split_string returned,
+     * +2 for the NULLS. */
+    pairs = ne_malloc((2*count+2) * sizeof(char *));
+    if (pairs == NULL) {
+       return NULL;
+    }
+    for (n = 0; n < count; n++) {
+       /* Find the split */
+       split = strchr(comps[n], kvsep);
+       if (split == NULL) {
+           /* No seperator found */
+           length = strlen(comps[n]);
+       } else {
+           length = split-comps[n];
+       }
+       /* Enter the key into the array */
+       pairs[2*n] = comps[n];
+       /* Null-terminate the key */
+       pairs[2*n][length] = '\0';
+       pairs[2*n+1] = split?(split + 1):NULL;
+    }
+    free(comps);
+    pairs[2*count] = pairs[2*count+1] = NULL;    
+    return pairs;
+}
+
+void split_string_free(char **components) 
+{
+    char **pnt = components;
+    while (*pnt != NULL) {
+       free(*pnt);
+       pnt++;
+    }
+    free(components);
+}
+
+void pair_string_free(char **pairs) 
+{
+    int n;
+    for (n = 0; pairs[n] != NULL; n+=2) {
+       free(pairs[n]);
+    }
+    free(pairs);
+}
+
+char *shave_string(const char *str, const char ch) 
+{
+    size_t len = strlen(str);
+    char *ret;
+    if (str[len-1] == ch) {
+       len--;
+    }
+    if (str[0] == ch) {
+       len--;
+       str++;
+    }
+    ret = ne_malloc(len + 1);
+    memcpy(ret, str, len);
+    ret[len] = '\0';
+    return ret;
+}
+
+char *ne_concat(const char *str, ...)
+{
+    va_list ap;
+    ne_buffer *tmp = ne_buffer_create();
+
+    ne_buffer_zappend(tmp, str);
+
+    va_start(ap, str);
+    ne_buffer_concat(tmp, ap);
+    va_end(ap);
+    
+    return ne_buffer_finish(tmp);
+}
+
+void ne_buffer_clear(ne_buffer *buf) 
+{
+    memset(buf->data, 0, buf->length);
+    buf->used = 1;
+}  
+
+/* Grows for given size, returns 0 on success, -1 on error. */
+int ne_buffer_grow(ne_buffer *buf, size_t newsize) 
+{
+    size_t newlen, oldbuflen;
+
+#define NE_BUFFER_GROWTH 512
+
+    if (newsize <= buf->length) return 0; /* big enough already */
+    /* FIXME: ah, can't remember my maths... better way to do this? */
+    newlen = ((newsize / NE_BUFFER_GROWTH) + 1) * NE_BUFFER_GROWTH;
+    
+    oldbuflen = buf->length;
+    /* Reallocate bigger buffer */
+    buf->data = realloc(buf->data, newlen);
+    if (buf->data == NULL) return -1;
+    buf->length = newlen;
+    /* Zero-out the new bit of buffer */
+    memset(buf->data+oldbuflen, 0, newlen-oldbuflen);
+
+    return 0;
+}
+
+int ne_buffer_concat(ne_buffer *buf, ...) 
+{
+    va_list ap;
+    char *next;
+    size_t totallen = buf->used; 
+
+    /* Find out how much space we need for all the args */
+    va_start(ap, buf);
+    do {
+       next = va_arg(ap, char *);
+       if (next != NULL) {
+           totallen += strlen(next);
+       }
+    } while (next != NULL);
+    va_end(ap);
+    
+    /* Grow the buffer */
+    if (ne_buffer_grow(buf, totallen))
+       return -1;
+    
+    /* Now append the arguments to the buffer */
+    va_start(ap, buf);
+    do {
+       next = va_arg(ap, char *);
+       if (next != NULL) {
+           /* TODO: use stpcpy */
+           strcat(buf->data, next);
+       }
+    } while (next != NULL);
+    va_end(ap);
+    
+    buf->used = totallen;
+    return 0;
+}
+
+/* Append zero-terminated string... returns 0 on success or -1 on
+ * realloc failure. */
+int ne_buffer_zappend(ne_buffer *buf, const char *str) 
+{
+    size_t len = strlen(str);
+
+    if (ne_buffer_grow(buf, buf->used + len)) {
+       return -1;
+    }
+    strcat(buf->data, str);
+    buf->used += len;
+    return 0;
+}
+
+int ne_buffer_append(ne_buffer *buf, const char *data, size_t len) 
+{
+    if (ne_buffer_grow(buf, buf->used + len)) {
+       return -1;
+    }
+    memcpy(buf->data + buf->used - 1, data, len);
+    buf->used += len;
+    buf->data[buf->used - 1] = '\0';
+    return 0;
+}
+
+ne_buffer *ne_buffer_create(void) 
+{
+    return ne_buffer_create_sized(512);
+}
+
+ne_buffer *ne_buffer_create_sized(size_t s) 
+{
+    ne_buffer *buf = ne_malloc(sizeof(struct ne_buffer_s));
+    buf->data = ne_calloc(s);
+    buf->length = s;
+    buf->used = 1;
+    return buf;
+}
+
+void ne_buffer_destroy(ne_buffer *buf) 
+{
+    if (buf->data) {
+       free(buf->data);
+    }
+    free(buf);
+}
+
+char *ne_buffer_finish(ne_buffer *buf)
+{
+    char *ret = buf->data;
+    free(buf);
+    return ret;
+}
+
+void ne_buffer_altered(ne_buffer *buf)
+{
+    buf->used = strlen(buf->data) + 1;
+}
+
+char *ne_utf8_encode(const char *str)
+{
+    char *buffer = ne_malloc(strlen(str) * 2 + 1);
+    int in, len = strlen(str);
+    char *out;
+
+    for (in = 0, out = buffer; in < len; in++, out++) {
+       if ((unsigned char)str[in] <= 0x7D) {
+           *out = str[in];
+       } else {
+           *out++ = 0xC0 | ((str[in] & 0xFC) >> 6);
+           *out = str[in] & 0xBF;
+       }
+    }
+
+    /* nul-terminate */
+    *out = '\0';
+    return buffer;
+}
+
+/* Single byte range 0x00 -> 0x7F */
+#define SINGLEBYTE_UTF8(ch) (((unsigned char) (ch)) < 0x80)
+
+char *ne_utf8_decode(const char *str)
+{
+    int n, m, len = strlen(str);
+    char *dest = ne_malloc(len + 1);
+    
+    for (n = 0, m = 0; n < len; n++, m++) {
+       if (SINGLEBYTE_UTF8(str[n])) {
+           dest[m] = str[n];
+       } else {
+           /* We only deal with 8-bit data, which will be encoded as
+            * two bytes of UTF-8 */
+           if ((len - n < 2) || (str[n] & 0xFC) != 0xC0) {
+               free(dest);
+               return NULL;
+           } else {
+               /* see hip_xml.c for comments. */
+               dest[m] = ((str[n] & 0x03) << 6) | (str[n+1] & 0x3F);
+               n++;
+           }
+       }
+    }
+    
+    dest[m] = '\0';
+    return dest;
+}
diff --git a/neon/src/ne_string.h b/neon/src/ne_string.h
new file mode 100644 (file)
index 0000000..517bcfe
--- /dev/null
@@ -0,0 +1,212 @@
+/* 
+   String utility functions
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_STRING_H
+#define NE_STRING_H
+
+#include "ne_defs.h"
+#include "ne_alloc.h"
+
+#include <stdarg.h>
+
+BEGIN_NEON_DECLS
+
+/* Returns an ne_malloc-allocated UTF-8 encoded copy of 'str'. */
+char *ne_utf8_encode(const char *str);
+
+/* Returns an ne_malloc-allocated UTF-8 decode copy of 'str'.
+ * Returns NULL if any of the characters in 'str' are non-8-bit.
+ */
+char *ne_utf8_decode(const char *str);
+
+/* Simple string tokenizer.
+ *
+ * Returns the next token in *str between *str and 'separator'
+ * or NUL terminator, skipping over any parts quoted using
+ * a pair of any character found in 'quotes'.  After returning,
+ * *str will point to the next character after the separator,
+ * or NULL if no more separator characters were found. 
+ *
+ * quotes may be NULL.
+ *
+ * Returns NULL if unbalanced quotes are found. (this will not
+ * happen if quotes == NULL).
+ */
+char *ne_token(char **str, char separator, const char *quotes);
+
+/* Return portion of 'str' with any characters in 'whitespace' shaved
+ * off the beginning and end.  Modifies str. */
+char *ne_shave(char *str, const char *whitespace);
+
+/* Splits str into component parts using given seperator,
+ * skipping given whitespace around separators and at the 
+ * beginning and end of str. Separators are ignored within
+ * any pair of characters specified as being quotes.
+ * Returns array of components followed by a NULL pointer. The
+ * components are taken from dynamically allocated memory, and
+ * the entire array can be easily freed using split_string_free.
+ *
+ * aka: What strtok should have been.
+ */
+char **split_string(const char *str, const char seperator,
+                   const char *quotes, const char *whitespace);
+
+/* As above, but returns count of items as well */
+char **split_string_c(const char *str, const char seperator,
+                     const char *quotes, const char *whitespace, int *count);
+
+/* A bit like split_string, except each component is split into a pair.
+ * Each pair is returned consecutively in the return array.
+ *  e.g.:
+ *     strpairs("aa=bb,cc=dd", ',', '=', NULL, NULL)
+ *  =>   "aa", "bb", "cc", "dd", NULL, NULL
+ * Note, that if a component is *missing* it's key/value separator,
+ * then the 'value' in the array will be a NULL pointer. But, if the
+ * value is zero-length (i.e., the component separator follows directly
+ * after the key/value separator, or with only whitespace inbetween),
+ * then the value in the array will be a zero-length string.
+ *  e.g.:
+ *     pair_string("aaaa,bb=cc,dd=,ee=ff", ',', '=', NULL, NULL)
+ *  =>   "aaaa", NULL, "bb", "cc", "dd", "", "ee", "ff"
+ * A NULL key indicates the end of the array (the value will also 
+ * be NULL, for convenience).
+ */
+char **pair_string(const char *str, const char compsep, const char kvsep, 
+                  const char *quotes, const char *whitespace);
+
+/* Frees the array returned by split_string */
+void split_string_free(char **components);
+
+/* Frees the array returned by pair_string */
+void pair_string_free(char **pairs);
+
+/* Returns a string which is str with ch stripped from
+ * beggining and end, if present in either. The string returned
+ * is dynamically allocated using malloc().
+ *
+ * e.g.  shave_string("abbba", 'a')  => "bbb". */
+char *shave_string(const char *str, const char ch);
+
+#define EOL "\r\n"
+
+#define STRIP_EOL(str)                         \
+do {                                                   \
+   char *p;                                            \
+   if ((p = strrchr(str, '\r')) != NULL) *p = '\0';    \
+   if ((p = strrchr(str, '\n')) != NULL) *p = '\0';    \
+} while (0)
+
+/* Return concatenation of all given strings. */
+char *ne_concat(const char *str, ...);
+
+/* String buffer handling. (Strings are zero-terminated still).
+ * A string buffer sbuffer * which grows dynamically with the string. */
+
+struct ne_buffer_s {
+    char *data; /* contents: null-terminated string. */
+    size_t used; /* used bytes in buffer */
+    size_t length; /* length of buffer */
+};
+
+typedef struct ne_buffer_s ne_buffer;
+
+/* Returns size of data in buffer, equiv to strlen(ne_buffer_data(buf)) */
+#define ne_buffer_size(buf) ((buf)->used - 1)
+
+/* Concatenate all given strings onto the end of the buffer.
+ * The strings must be null-terminated, and MUST be followed by a
+ * NULL argument marking the end of the list.
+ * Returns:
+ *   0 on success
+ *   non-zero on error
+ */
+int ne_buffer_concat(ne_buffer *buf, ...);
+
+/* Create a new ne_buffer. Returns NULL on error */
+ne_buffer *ne_buffer_create(void);
+
+/* Create a new ne_buffer of given minimum size. Returns NULL on error */
+ne_buffer *ne_buffer_create_sized(size_t size);
+
+/* Destroys (deallocates) a buffer */
+void ne_buffer_destroy(ne_buffer *buf);
+
+/* Append a zero-terminated string 'str' to buf.
+ * Returns 0 on success, non-zero on error. */
+int ne_buffer_zappend(ne_buffer *buf, const char *str);
+
+/* Append 'len' bytes of 'data' to buf.  'data' does not need to be
+ * zero-terminated. The resultant string will have a zero-terminator,
+ * either way. Returns 0 on success, non-zero on error.  */
+int ne_buffer_append(ne_buffer *buf, const char *data, size_t len);
+
+/* Empties the contents of buf; makes the buffer zero-length. */
+void ne_buffer_clear(ne_buffer *buf);
+
+/* Grows the ne_buffer to a minimum size.
+ * Returns 0 on success, non-zero on error */
+int ne_buffer_grow(ne_buffer *buf, size_t size);
+
+void ne_buffer_altered(ne_buffer *buf);
+
+/* Destroys a buffer, WITHOUT freeing the data, and returns the
+ * data. */
+char *ne_buffer_finish(ne_buffer *buf);
+
+/* TODO: do these with stpcpy instead... more efficient, but means 
+ * bloat on non-GNU platforms. */
+
+/* TODO: could replace with glib equiv's where available, too */
+
+/* NOTES:
+ *  - These abort() on malloc() returning NULL
+ *  - You will need to #include <string.h> / <strings.h> YOURSELF to
+ *    prototype strlen and strcat.
+ */
+
+#define CONCAT2(out, str1, str2)                       \
+do {                                                   \
+    out = ne_malloc(strlen(str1) + strlen(str2) + 1);  \
+    strcpy(out, str1);                                 \
+    strcat(out, str2);                                 \
+} while (0)
+
+#define CONCAT3(out, str1, str2, str3)                                 \
+do {                                                                   \
+    out = ne_malloc(strlen(str1) + strlen(str2) + strlen(str3) + 1);   \
+    strcpy(out, str1);                                                 \
+    strcat(out, str2);                                                 \
+    strcat(out, str3);                                                 \
+} while (0)
+
+#define CONCAT4(out, str1, str2, str3, str4)           \
+do {                                                   \
+    out = ne_malloc(strlen(str1) + strlen(str2)                \
+                   + strlen(str3) + strlen(str4) + 1); \
+    strcpy(out, str1);                                 \
+    strcat(out, str2);                                 \
+    strcat(out, str3);                                 \
+    strcat(out, str4);                                 \
+} while (0)
+
+END_NEON_DECLS
+
+#endif /* NE_STRING_H */
diff --git a/neon/src/ne_uri.c b/neon/src/ne_uri.c
new file mode 100644 (file)
index 0000000..fbc2a8d
--- /dev/null
@@ -0,0 +1,313 @@
+/* 
+   HTTP URI handling
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <ctype.h>
+
+#include "ne_utils.h" /* for 'min' */
+#include "ne_string.h" /* for CONCAT3 */
+#include "ne_alloc.h"
+#include "ne_uri.h"
+
+char *uri_parent(const char *uri) 
+{
+    const char *pnt;
+    char *ret;
+    pnt = uri+strlen(uri)-1;
+    while (*(--pnt) != '/' && pnt >= uri) /* noop */;
+    if (pnt < uri) {
+       /* not a valid absPath */
+       return NULL;
+    }
+    /*  uri    
+     *   V
+     *   |---|
+     *   /foo/bar/
+     */
+    ret = ne_malloc((pnt - uri) + 2);
+    memcpy(ret, uri, (pnt - uri) + 1);
+    ret[1+(pnt-uri)] = '\0';
+    pnt++;
+    return ret;
+}
+
+int uri_has_trailing_slash(const char *uri) 
+{
+    size_t len = strlen(uri);
+    return ((len > 0) &&
+           (uri[len-1] == '/'));
+}
+
+const char *uri_abspath(const char *uri) 
+{
+    const char *ret;
+    /* Look for the scheme: */
+    ret = strstr(uri, "://");
+    if (ret == NULL) {
+       /* No scheme */
+       ret = uri;
+    } else {
+       /* Look for the abs_path */
+       ret = strchr(ret+3, '/');
+       if (ret == NULL) {
+           /* Uh-oh */
+           ret = uri;
+       }
+    }
+    return ret;
+}
+
+/* TODO: not a proper URI parser */
+int uri_parse(const char *uri, struct uri *parsed, 
+             const struct uri *defaults)
+{
+    const char *pnt, *slash, *colon, *atsign;
+
+    parsed->port = -1;
+    parsed->host = NULL;
+    parsed->path = NULL;
+    parsed->scheme = NULL;
+    parsed->authinfo = NULL;
+
+    if (strlen(uri) == 0) {
+       return -1;
+    }
+
+    pnt = strstr(uri, "://");
+    if (pnt) {
+       parsed->scheme = ne_strndup(uri, pnt - uri);
+       pnt += 3; /* start of hostport segment */
+    } else {
+       pnt = uri;
+       if (defaults && defaults->scheme != NULL) {
+           parsed->scheme = ne_strdup(defaults->scheme);
+       }
+    }
+    
+    atsign = strchr(pnt, '@');
+    slash = strchr(pnt, '/');
+
+    /* Check for an authinfo segment in the hostport segment. */
+    if (atsign != NULL && (slash == NULL || atsign < slash)) {
+       parsed->authinfo = ne_strndup(pnt, atsign - pnt);
+       pnt = atsign + 1;
+    }
+
+    colon = strchr(pnt, ':');
+
+    if (slash == NULL) {
+       parsed->path = ne_strdup("/");
+       if (colon == NULL) {
+           if (defaults) parsed->port = defaults->port;
+           parsed->host = ne_strdup(pnt);
+       } else {
+           parsed->port = atoi(colon+1);
+           parsed->host = ne_strndup(pnt, colon - pnt);
+       }
+    } else {
+       if (colon == NULL || colon > slash) {
+           /* No port segment */
+           if (defaults) parsed->port = defaults->port;
+           if (slash != uri) {
+               parsed->host = ne_strndup(pnt, slash - pnt);
+           } else {
+               /* No hostname segment. */
+           }
+       } else {
+           /* Port segment */
+           parsed->port = atoi(colon + 1);
+           parsed->host = ne_strndup(pnt, colon - pnt);
+       }
+       parsed->path = ne_strdup(slash);
+    }
+
+    return 0;
+}
+
+void uri_free(struct uri *uri)
+{
+    NE_FREE(uri->host);
+    NE_FREE(uri->path);
+    NE_FREE(uri->scheme);
+    NE_FREE(uri->authinfo);
+}
+
+/* Returns an absoluteURI */
+char *uri_absolute(const char *uri, const char *scheme, 
+                  const char *hostport) 
+{
+    char *ret;
+    /* Is it absolute already? */
+    if (strncmp(uri, scheme, strlen(scheme)) == 0)  {
+       /* Yes it is */
+       ret = ne_strdup(uri);
+    } else {
+       /* Oh no it isn't */
+       CONCAT3(ret, scheme, hostport, uri);
+    }
+    return ret;
+}
+
+/* Un-escapes a URI. Returns ne_malloc-allocated URI */
+char *uri_unescape(const char *uri) 
+{
+    const char *pnt;
+    char *ret, *retpos, buf[5] = { "0x00\0" };
+    retpos = ret = ne_malloc(strlen(uri) + 1);
+    for (pnt = uri; *pnt != '\0'; pnt++) {
+       if (*pnt == '%') {
+           if (!isxdigit((unsigned char) pnt[1]) || 
+               !isxdigit((unsigned char) pnt[2])) {
+               /* Invalid URI */
+               return NULL;
+           }
+           buf[2] = *++pnt; buf[3] = *++pnt; /* bit faster than memcpy */
+           *retpos++ = (char)strtol(buf, NULL, 16);
+       } else {
+           *retpos++ = *pnt;
+       }
+    }
+    *retpos = '\0';
+    return ret;
+}
+
+/* RFC2396 spake:
+ * "Data must be escaped if it does not have a representation 
+ * using an unreserved character".
+ */
+
+/* Lookup table: character classes from 2396. (This is overkill) */
+
+#define SP 0   /* space    = <US-ASCII coded character 20 hexadecimal>                 */
+#define CO 0   /* control  = <US-ASCII coded characters 00-1F and 7F hexadecimal>      */
+#define DE 0   /* delims   = "<" | ">" | "#" | "%" | <">                               */
+#define UW 0   /* unwise   = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"             */
+#define MA 1   /* mark     = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"       */
+#define AN 2   /* alphanum = alpha | digit                                             */
+#define RE 2   /* reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," */
+
+static const char uri_chars[128] = {
+/*                +2      +4      +6      +8     +10     +12     +14     */
+/*   0 */ CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO,
+/*  16 */ CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO,
+/*  32 */ SP, MA, DE, DE, RE, DE, RE, MA, MA, MA, MA, RE, RE, MA, MA, RE,
+/*  48 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, RE, RE, DE, RE, DE, RE,
+/*  64 */ RE, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN,
+/*  80 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, UW, UW, UW, UW, MA,
+/*  96 */ UW, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN,
+/* 112 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, UW, UW, UW, MA, CO 
+};
+
+#define ESCAPE(ch) (((const signed char)(ch) < 0 || \
+               uri_chars[(unsigned int)(ch)] == 0))
+
+#undef SP
+#undef CO
+#undef DE
+#undef UW
+#undef MA
+#undef AN
+#undef RE
+
+/* Escapes the abspath segment of a URI.
+ * Returns ne_malloc-allocated string.
+ */
+char *uri_abspath_escape(const char *abs_path) 
+{
+    const char *pnt;
+    char *ret, *retpos;
+    int count = 0;
+    for (pnt = abs_path; *pnt != '\0'; pnt++) {
+       if (ESCAPE(*pnt)) {
+           count++;
+       }
+    }
+    if (count == 0) {
+       return ne_strdup(abs_path);
+    }
+    /* An escaped character is "%xx", i.e., two MORE
+     * characters than the original string */
+    retpos = ret = ne_malloc(strlen(abs_path) + 2*count + 1);
+    for (pnt = abs_path; *pnt != '\0'; pnt++) {
+       if (ESCAPE(*pnt)) {
+           /* Escape it - %<hex><hex> */
+           sprintf(retpos, "%%%02x", (unsigned char) *pnt);
+           retpos += 3;
+       } else {
+           /* It's cool */
+           *retpos++ = *pnt;
+       }
+    }
+    *retpos = '\0';
+    return ret;
+}
+
+#undef ESCAPE
+
+/* TODO: implement properly */
+int uri_compare(const char *a, const char *b) 
+{
+    int ret = strcasecmp(a, b);
+    if (ret) {
+       /* This logic says: "If the lengths of the two URIs differ by
+        * exactly one, and the LONGER of the two URIs has a trailing
+        * slash and the SHORTER one DOESN'T, then..." */
+       int traila = uri_has_trailing_slash(a),
+           trailb = uri_has_trailing_slash(b),
+           lena = strlen(a), lenb = strlen(b);
+       if (traila != trailb && abs(lena - lenb) == 1 &&
+           ((traila && lena > lenb) || (trailb && lenb > lena))) {
+           /* Compare them, ignoring the trailing slash on the longer
+            * URI */
+           if (strncasecmp(a, b, min(lena, lenb)) == 0)
+               ret = 0;
+       }
+    }
+    return ret;
+}
+
+/* Give it a path segment, it returns non-zero if child is 
+ * a child of parent. */
+int uri_childof(const char *parent, const char *child) 
+{
+    char *root = ne_strdup(child);
+    int ret;
+    if (strlen(parent) >= strlen(child)) {
+       ret = 0;
+    } else {
+       /* root is the first of child, equal to length of parent */
+       root[strlen(parent)] = '\0';
+       ret = (uri_compare(parent, root) == 0);
+    }
+    free(root);
+    return ret;
+}
diff --git a/neon/src/ne_uri.h b/neon/src/ne_uri.h
new file mode 100644 (file)
index 0000000..12cb497
--- /dev/null
@@ -0,0 +1,75 @@
+/* 
+   HTTP URI handling
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_URI_H
+#define NE_URI_H
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+/* Un-escapes a URI. Returns malloc-allocated URI on success,
+ * or NULL on failure (malloc failure or invalid %<HEX><HEX> sequence). */
+char *uri_unescape(const char *uri);
+
+/* Escapes the abspath segment of a URI.
+ * Returns malloc-allocated string on success, or NULL on malloc failure.
+ */
+char *uri_abspath_escape(const char *abs_path);
+
+/* Returns abspath segment in (absolute) uri */
+const char *uri_abspath(const char *uri);
+
+/* Returns parent of path */
+char *uri_parent(const char *path);
+
+/* Returns strcmp-like value giving comparison between a and b,
+ * ignoring trailing-slashes. */
+int uri_compare(const char *a, const char *b);
+
+/* Returns an absolute URI from a possibly-relative 'uri', using
+ * given scheme + hostport segment.
+ * Returns malloc-allocated string on success, or NULL on malloc failure. */
+char *uri_absolute(const char *uri, const char *scheme, const char *hostport);
+
+/* Returns non-zero if child is a child of parent */
+int uri_childof(const char *parent, const char *child);
+
+/* Returns non-zero if uri has a trailing slash character */
+int uri_has_trailing_slash(const char *uri);
+
+struct uri {
+    char *scheme;
+    char *host;
+    int port;
+    char *path;
+    char *authinfo;
+};
+
+/* Parse 'uri' and place parsed segments in *parsed. */
+int uri_parse(const char *uri, struct uri *parsed, const struct uri *defaults);
+
+void uri_free(struct uri *parsed);
+
+END_NEON_DECLS
+
+#endif /* NE_URI_H */
+
diff --git a/neon/src/ne_utils.c b/neon/src/ne_utils.c
new file mode 100644 (file)
index 0000000..fe6afa8
--- /dev/null
@@ -0,0 +1,160 @@
+/* 
+   HTTP utility functions
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <ctype.h> /* isdigit() for ne_parse_statusline */
+
+#include "ne_utils.h"
+
+#include "ne_dates.h"
+
+#ifdef ENABLE_SSL
+#include <openssl/opensslv.h>
+#endif
+
+#ifdef HAVE_XMLVERSION_H
+/* libxml2: pick up the version string. */
+#include <xmlversion.h>
+#endif
+
+int ne_debug_mask = 0;
+FILE *ne_debug_stream = NULL;
+
+void ne_debug_init(FILE *stream, int mask)
+{
+    ne_debug_stream = stream;
+    ne_debug_mask = mask;
+}
+
+void ne_debug(int ch, const char *template, ...) 
+{
+    va_list params;
+    if ((ch&ne_debug_mask) != ch) return;
+    fflush(stdout);
+    va_start(params, template);
+    vfprintf(ne_debug_stream, template, params);
+    va_end(params);
+    if ((ch&NE_DBG_FLUSH) == NE_DBG_FLUSH) {
+       fflush(ne_debug_stream);
+    }
+}
+
+static const char *version_string = "neon " NEON_VERSION ": " 
+#ifdef NEON_IS_LIBRARY
+  "Library build"
+#else
+  "Bundled build"
+#endif
+#ifdef HAVE_EXPAT
+  ", Expat"
+#else
+#ifdef HAVE_LIBXML
+  ", libxml"
+#ifdef LIBXML_DOTTED_VERSION
+  " " LIBXML_DOTTED_VERSION
+#endif
+#endif
+#endif
+#ifdef ENABLE_SSL
+   ", SSL support using "
+#ifdef OPENSSL_VERSION_TEXT
+    OPENSSL_VERSION_TEXT
+#else
+   "unknown SSL library"
+#endif /* OPENSSL_VERSION_TEXT */
+#else /* !ENABLE_SSL */
+   ", no OpenSSL support"
+#endif /* ENABLE_SSL */
+   "."
+;
+
+const char *ne_version_string(void)
+{
+    return version_string;
+}
+
+int ne_version_minimum(int major, int minor)
+{
+    return 
+       (NEON_VERSION_MAJOR < major) || 
+       (NEON_VERSION_MINOR < minor);
+}
+
+int ne_parse_statusline(const char *status_line, ne_status *st)
+{
+    const char *part;
+    int major, minor, status_code, klass;
+
+    /* Check they're speaking the right language */
+    status_line = strstr(status_line, "HTTP/");
+    if (status_line == NULL) {
+       return -1;
+    } 
+
+    /* And find out which dialect of this peculiar language
+     * they can talk... */
+    major = 0;
+    minor = 0; 
+    /* Note, we're good children, and accept leading zero's on the
+     * version numbers */
+    for (part = status_line + 5; *part != '\0' && isdigit(*part); part++) {
+       major = major*10 + (*part-'0');
+    }
+    if (*part != '.') { 
+       return -1;
+    }
+    for (part++ ; *part != '\0' && isdigit(*part); part++) {
+       minor = minor*10 + (*part-'0');
+    }
+    if (*part != ' ') {
+       return -1;
+    }
+    /* Skip any spaces */
+    for (; *part == ' ' ; part++) /* noop */;
+    /* Now for the Status-Code. part now points at the first Y in
+     * "HTTP/x.x YYY". We want value of YYY... could use atoi, but
+     * probably quicker this way. */
+    if (!isdigit(part[0]) || !isdigit(part[1]) || !isdigit(part[2]) ||
+       part[3] != ' ') {
+       return -1;
+    }
+    status_code = 100*(part[0]-'0') + 10*(part[1]-'0') + (part[2]-'0');
+    klass = part[0]-'0';
+    /* Skip whitespace between status-code and reason-phrase */
+    for (part+=3; *part == ' ' || *part == '\t'; part++) /* noop */;
+    if (*part == '\0') {
+       return -1;
+    }
+    /* Fill in the results */
+    st->major_version = major;
+    st->minor_version = minor;
+    st->reason_phrase = part;
+    st->code = status_code;
+    st->klass = klass;
+    return 0;
+}
diff --git a/neon/src/ne_utils.h b/neon/src/ne_utils.h
new file mode 100644 (file)
index 0000000..f269a68
--- /dev/null
@@ -0,0 +1,133 @@
+/* 
+   HTTP utility functions
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_UTILS_H
+#define NE_UTILS_H
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ne_defs.h"
+
+BEGIN_NEON_DECLS
+
+/* Returns a human-readable version string like:
+ * "neon 0.2.0: Library build, OpenSSL support"
+ */
+const char *ne_version_string(void);
+
+/* Returns non-zero if the neon API compiled in is less than
+ * major.minor. i.e.
+ *   I am: 1.2 -  neon_version_check(1, 3) => -1
+ *   I am: 0.10 -  neon_version_check(0, 9) => 0
+ */
+int ne_version_minimum(int major, int minor);
+
+#define HTTP_QUOTES "\"'"
+#define HTTP_WHITESPACE " \r\n\t"
+
+#ifndef WIN32
+#undef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifdef WIN32
+/* some of the API uses ssize_t so we need to define it. */
+#define ssize_t int
+#endif
+
+/* CONSIDER: mutt has a nicer way of way of doing debugging output... maybe
+ * switch to like that. */
+
+#ifdef __GNUC__
+/* really, we want to do this if we have any C99-capable compiler, so
+ * what's a better test? */
+
+#ifndef NE_DEBUGGING
+#define NE_DEBUG(x, fmt, args...)
+#else
+#define NE_DEBUG(x, fmt, args...) do {         \
+ if (((x)&ne_debug_mask) == (x)) {                     \
+  fflush(stdout);                              \
+  fprintf(ne_debug_stream, fmt, ##args);       \
+  if (((x) & NE_DBG_FLUSH) == NE_DBG_FLUSH)    \
+   fflush(ne_debug_stream);                    \
+ }                                             \
+} while (0)
+#endif   
+
+#else /* !__GNUC__ */
+
+#ifndef NE_DEBUGGING
+#define NE_DEBUG if (0) ne_debug
+#else /* DEBUGGING */
+#define NE_DEBUG ne_debug
+#endif /* DEBUGGING */
+
+#endif
+
+#define NE_DBG_SOCKET (1<<0)
+#define NE_DBG_HTTP (1<<1)
+#define NE_DBG_XML (1<<2)
+#define NE_DBG_HTTPAUTH (1<<3)
+#define NE_DBG_HTTPPLAIN (1<<4)
+#define NE_DBG_LOCKS (1<<5)
+#define NE_DBG_XMLPARSE (1<<6)
+#define NE_DBG_HTTPBODY (1<<7)
+#define NE_DBG_HTTPBASIC (1<<8)
+#define NE_DBG_FLUSH (1<<30)
+
+void ne_debug_init(FILE *stream, int mask);
+extern int ne_debug_mask;
+extern FILE *ne_debug_stream;
+
+void ne_debug(int ch, const char *, ...)
+#ifdef __GNUC__
+                __attribute__ ((format (printf, 2, 3)))
+#endif /* __GNUC__ */
+;
+
+/* Storing an HTTP status result */
+typedef struct {
+    int major_version;
+    int minor_version;
+    int code; /* Status-Code value */
+    /* We can't use 'class' as the member name since this crashes
+     * with the C++ reserved keyword 'class', annoyingly.
+     * This was '_class' previously, but that was even MORE annoying.
+     * So know it is klass. */
+    int klass; /* Class of Status-Code (1-5) */
+    const char *reason_phrase;
+} ne_status;
+
+/* Parser for strings which follow the Status-Line grammar from 
+ * RFC2616.
+ *  Returns:
+ *    0 on success, *s will be filled in.
+ *   -1 on parse error.
+ */
+int ne_parse_statusline(const char *status_line, ne_status *s);
+
+END_NEON_DECLS
+
+#endif /* NE_UTILS_H */
diff --git a/neon/src/ne_xml.c b/neon/src/ne_xml.c
new file mode 100644 (file)
index 0000000..0920c1f
--- /dev/null
@@ -0,0 +1,862 @@
+/* 
+   Higher Level Interface to XML Parsers.
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_i18n.h"
+
+#include "ne_alloc.h"
+#include "ne_xml.h"
+#include "ne_utils.h"
+#include "ne_string.h"
+
+#ifdef HAVE_EXPAT
+/******** Expat ***********/
+#ifdef HAVE_OLD_EXPAT
+/* jclark expat */
+#include "xmlparse.h"
+#else /* not HAVE_OLD_EXPAT */
+/* new-style expat */
+#include "expat.h"
+#endif
+
+typedef XML_Char ne_xml_char;
+
+#else /* not HAVE_EXPAT */
+# ifdef HAVE_LIBXML
+
+#ifdef HAVE_XMLVERSION_H
+/* libxml2: pick up the version string. */
+#include <xmlversion.h>
+#endif
+
+/******** libxml **********/
+#  include <parser.h>
+typedef xmlChar ne_xml_char;
+
+# else /* not HAVE_LIBXML */
+#  error need an XML parser
+# endif /* not HAVE_LIBXML */
+#endif /* not HAVE_EXPAT */
+
+/* Approx. one screen of text: */
+#define ERR_SIZE (2048)
+
+/* A list of elements */
+struct ne_xml_handler {
+    const struct ne_xml_elm *elements; /* put it in static memory */
+    ne_xml_validate_cb validate_cb; /* validation function */
+    ne_xml_startelm_cb startelm_cb; /* on-complete element function */
+    ne_xml_endelm_cb endelm_cb; /* on-complete element function */
+    ne_xml_cdata_cb cdata_cb; /* cdata callback for mixed mode */
+    void *userdata;
+    struct ne_xml_handler *next;
+};
+
+#ifdef HAVE_LIBXML
+static void sax_error(void *ctx, const char *msg, ...);
+#endif
+
+struct ne_xml_state {
+    /* The element details */
+    const struct ne_xml_elm *elm;
+
+    /* Storage for an unknown element */
+    struct ne_xml_elm elm_real;
+    char *real_name;
+    
+    /* Namespaces declared in this element */
+    ne_xml_char *default_ns; /* A default namespace */
+    struct ne_xml_nspace *nspaces; /* List of other namespace scopes */
+
+    unsigned int mixed:1; /* are we in MIXED mode? */
+
+    /* Extras */
+    struct ne_xml_handler *handler; /* Where the element was declared */
+    struct ne_xml_state *parent; /* The parent in the tree */
+};
+
+  
+/* TODO: 
+ * could move 'valid' into state, maybe allow optional
+ * continuation past an invalid branch.
+ */
+
+/* We pass around a ne_xml_parser as the userdata in the parsing
+ * library.  This maintains the current state of the parse and various
+ * other bits and bobs. Within the parse, we store the current branch
+ * of the tree, i.e., the current element and all its parents, up to
+ * the root, but nothing other than that.  */
+struct ne_xml_parser_s {
+    struct ne_xml_state *root; /* the root of the document */
+    struct ne_xml_state *current; /* current element in the branch */
+    ne_buffer *buffer; /* the CDATA/collect buffer */
+    unsigned int valid:1; /* currently valid? */
+    unsigned int want_cdata:1; /* currently collecting CDATA? */
+    unsigned int collect; /* current collect depth */
+    struct ne_xml_handler *top_handlers; /* always points at the 
+                                          * handler on top of the stack. */
+#ifdef HAVE_EXPAT
+    XML_Parser parser;
+#else
+    xmlParserCtxtPtr parser;
+#endif
+    char error[ERR_SIZE];
+};
+
+static void destroy_state(struct ne_xml_state *s);
+
+static const char *friendly_name(const struct ne_xml_elm *elm)
+{
+    switch(elm->id) {
+    case NE_ELM_root:
+       return _("document root");
+    case NE_ELM_unknown:
+       return _("unknown element");
+    default:
+       if (elm->name) {
+           return elm->name;
+       } else {
+           return _("unspecified");
+       }
+    }
+}
+
+const static struct ne_xml_elm root_element = 
+{ "@<root>@", NE_ELM_root, 0 };
+
+/* The callback handlers */
+static void start_element(void *userdata, const ne_xml_char *name, const ne_xml_char **atts);
+static void end_element(void *userdata, const ne_xml_char *name);
+static void char_data(void *userdata, const ne_xml_char *cdata, int len);
+
+#define NE_XML_DECODE_UTF8
+
+#ifdef NE_XML_DECODE_UTF8
+
+/* UTF-8 decoding */
+
+/* Single byte range 0x00 -> 0x7F */
+#define SINGLEBYTE_UTF8(ch) (((unsigned char) (ch)) < 0x80)
+
+/* Decode a double byte UTF8 string.
+ * Returns 0 on success or non-zero on error. */
+static inline int decode_utf8_double(char *dest, const char *src);
+
+#endif
+
+/* Linked list of namespace scopes */
+struct ne_xml_nspace {
+    ne_xml_char *name;
+    ne_xml_char *uri;
+    struct ne_xml_nspace *next;
+};
+
+/* And an auxiliary */
+static int parse_element(ne_xml_parser *p, struct ne_xml_state *state,
+                        const ne_xml_char *name, const ne_xml_char **atts);
+
+#ifdef HAVE_LIBXML
+
+/* Could be const as far as we care, but libxml doesn't want that */
+static xmlSAXHandler sax_handler = {
+    NULL, /* internalSubset */
+    NULL, /* isStandalone */
+    NULL, /* hasInternalSubset */
+    NULL, /* hasExternalSubset */
+    NULL, /* resolveEntity */
+    NULL, /* getEntity */
+    NULL, /* entityDecl */
+    NULL, /* notationDecl */
+    NULL, /* attributeDecl */
+    NULL, /* elementDecl */
+    NULL, /* unparsedEntityDecl */
+    NULL, /* setDocumentLocator */
+    NULL, /* startDocument */
+    NULL, /* endDocument */
+    start_element, /* startElement */
+    end_element, /* endElement */
+    NULL, /* reference */
+    char_data, /* characters */
+    NULL, /* ignorableWhitespace */
+    NULL, /* processingInstruction */
+    NULL, /* comment */
+    NULL, /* xmlParserWarning */
+    sax_error, /* xmlParserError */
+    NULL, /* xmlParserError */
+    NULL, /* getParameterEntity */
+    char_data /* cdataBlock */
+};
+
+#endif /* HAVE_LIBXML */
+
+#ifdef NE_XML_DECODE_UTF8
+
+static inline int 
+decode_utf8_double(char *dest, const char *src) 
+{
+    /* From utf-8 man page; two-byte encoding is:
+     *    0x00000080 - 0x000007FF:
+     *       110xxxxx 10xxxxxx
+     * If more than 8-bits of those x's are set, we fail.
+     * So, we check that the first 6 bits of the first byte are:
+     *       110000.
+     * Then decode like:
+     *       110000xx 10yyyyyy  -> xxyyyyyy
+     * Do this with a mask and a compare:
+     *       zzzzzzzz
+     *     & 11111100  <=> 0xFC
+     *    == 11000000  <=> 0xC0    
+     * 
+     * joe: A real C hacker would probably do some funky bit
+     * inversion, and turn this into an is-not-zero test, 
+     * but I'm a fake, so...
+     */
+    if ((src[0] & 0xFC) == 0xC0) {
+       dest[0] = ((src[0] & 0x03) << 6) | (src[1] & 0x3F);
+       /* nb.
+        *    00000011  = 0x03
+        *    00111111  = 0x3F
+        */
+       return 0;
+    } else {
+       return -1;
+    }
+}
+
+#endif
+
+int ne_xml_currentline(ne_xml_parser *p) 
+{
+#ifdef HAVE_EXPAT
+    return XML_GetCurrentLineNumber(p->parser);
+#else
+    return p->parser->input->line;
+#endif
+}
+
+static int find_handler(ne_xml_parser *p, struct ne_xml_state *state) 
+{
+    struct ne_xml_handler *cur, *unk_handler = NULL;
+    const char *name = state->elm_real.name, *nspace = state->elm_real.nspace;
+    int n, got_unknown = 0;
+
+    for (cur = state->parent->handler; cur != NULL; cur = cur->next) {
+       for (n = 0; (cur->elements[n].nspace != NULL || (
+                    cur->elements[n].nspace == NULL && 
+                    cur->elements[n].id == NE_ELM_unknown)); n++) {
+           if (cur->elements[n].nspace != NULL && 
+               (strcasecmp(cur->elements[n].name, name) == 0 && 
+                strcasecmp(cur->elements[n].nspace, nspace) == 0)) {
+
+               switch ((*cur->validate_cb)(state->parent->elm->id, cur->elements[n].id)) {
+               case NE_XML_VALID:
+                   NE_DEBUG(NE_DBG_XML, "Validated by handler.\n");
+                   state->handler = cur;
+                   state->elm = &cur->elements[n];
+                   return 0;
+               case NE_XML_INVALID:
+                   NE_DEBUG(NE_DBG_XML, "Invalid context.\n");
+                   snprintf(p->error, ERR_SIZE, 
+                            _("XML is not valid (%s found in parent %s)"),
+                            friendly_name(&cur->elements[n]), 
+                            friendly_name(state->parent->elm));
+                   return -1;
+               default:
+                   /* ignore it */
+                   NE_DEBUG(NE_DBG_XML, "Declined by handler.\n");
+                   break;
+               }
+           }
+           if (!got_unknown && cur->elements[n].id == NE_ELM_unknown) {
+               switch ((*cur->validate_cb)(state->parent->elm->id, NE_ELM_unknown)) {
+               case NE_XML_VALID:
+                   unk_handler = cur;
+                   got_unknown = 1;
+                   state->elm_real.id = NE_ELM_unknown;
+                   state->elm_real.flags = cur->elements[n].flags;
+                   break;
+               case NE_XML_INVALID:
+                   NE_DEBUG(NE_DBG_XML, "Invalid context.\n");
+                   snprintf(p->error, ERR_SIZE, 
+                            _("XML is not valid (%s found in parent %s)"),
+                            friendly_name(&cur->elements[n]), 
+                            friendly_name(state->parent->elm));
+                   return -1;
+               default:
+                   NE_DEBUG(NE_DBG_XML, "Declined by handler.\n");
+                   break;
+               }
+           }
+       }
+    }
+    if (!cur && got_unknown) {
+       /* Give them the unknown handler */
+       NE_DEBUG(NE_DBG_XMLPARSE, "Using unknown element handler\n");
+       state->handler = unk_handler;
+       state->elm = &state->elm_real;
+       return 0;
+    } else {
+       NE_DEBUG(NE_DBG_XMLPARSE, "Unexpected element\n");
+       snprintf(p->error, ERR_SIZE, 
+                _("Unknown XML element `%s (in %s)'"), name, nspace);
+       return -1;
+    }
+}
+
+/* Called with the start of a new element. */
+static void 
+start_element(void *userdata, const ne_xml_char *name, const ne_xml_char **atts) 
+{
+    ne_xml_parser *p = userdata;
+    struct ne_xml_state *s;
+
+    if (!p->valid) {
+       /* We've stopped parsing */
+       NE_DEBUG(NE_DBG_XML, "Parse died. Ignoring start of element: %s\n", name);
+       return;
+    }
+
+    /* If we are in collect mode, print the element to the buffer */
+    if (p->collect) {
+       /* In Collect Mode. */
+       const ne_xml_char *pnt = strchr(name, ':');
+       if (pnt == NULL) {
+           pnt = name;
+       } else {
+           pnt++;
+       }
+       ne_buffer_concat(p->buffer, "<", pnt, NULL);
+       if (atts != NULL) {
+           int n;
+           for (n = 0; atts[n] != NULL; n+=2) {
+               ne_buffer_concat(p->buffer, " ", atts[n], "=\"", atts[n+1], "\"",
+                              NULL);
+           }
+       }
+       ne_buffer_zappend(p->buffer, ">");
+       /* One deeper */
+       p->collect++;
+       return;
+    }
+
+    /* Set the new state */
+    s = ne_calloc(sizeof(struct ne_xml_state));
+    s->parent = p->current;
+    p->current = s;
+
+    /* We need to handle namespaces ourselves */
+    if (parse_element(p, s, name, atts)) {
+       /* it bombed. */
+       p->valid = 0;
+       return;
+    }
+
+    /* Map the element name to an id */
+    NE_DEBUG(NE_DBG_XML, "Mapping element name %s@@%s... ", 
+         s->elm_real.nspace, s->elm_real.name);
+
+    if (find_handler(p, s)) {
+       p->valid = 0;
+       return;
+    }
+
+    NE_DEBUG(NE_DBG_XMLPARSE, "mapped to id %d\n", s->elm->id);
+
+    /* Do we want cdata? */
+    p->want_cdata = ((s->elm->flags & NE_XML_CDATA) == NE_XML_CDATA);
+    p->collect = ((s->elm->flags & NE_XML_COLLECT) == NE_XML_COLLECT);
+    
+    /* Is this element using mixed-mode? */
+    s->mixed = ((s->elm->flags & NE_XML_MIXED) == NE_XML_MIXED);
+
+    if (s->handler->startelm_cb) {
+       if ((*s->handler->startelm_cb)(s->handler->userdata, s->elm, 
+                                      (const char **) atts)) {
+           NE_DEBUG(NE_DBG_XML, "Startelm callback failed.\n");
+           p->valid = 0;
+       }
+    } else {
+       NE_DEBUG(NE_DBG_XML, "No startelm handler.\n");
+    }
+
+}
+
+/* Destroys given state */
+static void destroy_state(struct ne_xml_state *s) 
+{
+    struct ne_xml_nspace *this_ns, *next_ns;
+    NE_DEBUG(NE_DBG_XMLPARSE, "Freeing namespaces...\n");
+    NE_FREE(s->default_ns);
+    NE_FREE(s->real_name);
+    /* Free the namespaces */
+    this_ns = s->nspaces;
+    while (this_ns != NULL) {
+       next_ns = this_ns->next;
+       free(this_ns->name);
+       free(this_ns->uri);
+       free(this_ns);
+       this_ns = next_ns;
+    };
+    NE_DEBUG(NE_DBG_XMLPARSE, "Finished freeing namespaces.\n");
+    free(s);
+}
+
+static void char_data(void *userdata, const ne_xml_char *data, int len) 
+{
+    ne_xml_parser *p = userdata;
+    
+    if (p->current->mixed) {
+       (*p->current->handler->cdata_cb)( 
+           p->current->handler->userdata, p->current->elm, data, len);
+       return;
+    }
+
+    if (!p->want_cdata || !p->valid) return;
+    /* First, if this is the beginning of the CDATA, skip all
+     * leading whitespace, we don't want it. */
+    NE_DEBUG(NE_DBG_XMLPARSE, "Given %d bytes of cdata.\n", len);
+    if (ne_buffer_size(p->buffer) == 0) {
+       int wslen = 0;
+       /* Ignore any leading whitespace */
+       while (wslen < len && 
+              (data[wslen] == ' ' || data[wslen] == '\r' ||
+               data[wslen] == '\n' || data[wslen] == '\t')) {
+           wslen++;
+       }
+       data += wslen;
+       len -= wslen;
+       NE_DEBUG(NE_DBG_XMLPARSE, "Skipped %d bytes of leading whitespace.\n", 
+              wslen);
+       if (len == 0) {
+           NE_DEBUG(NE_DBG_XMLPARSE, "Zero bytes of content.\n");
+           return;
+       }
+    }
+
+#ifdef NE_XML_DECODE_UTF8
+
+    if ((p->current->elm->flags & NE_XML_UTF8DECODE) == NE_XML_UTF8DECODE) {
+       int n, m, clen;
+       char *dest;
+
+       clen = ne_buffer_size(p->buffer);
+       ne_buffer_grow(p->buffer, clen + len + 1);
+       dest = p->buffer->data + clen;
+
+/* #define TOO_MUCH_DEBUG 1 */
+       for (n = 0, m = 0; n < len; n++, m++) {
+#ifdef TOO_MUCH_DEBUG
+           NE_DEBUG(NE_DBG_XML, "decoding 0x%02x", 0xFF & data[n]);
+#endif
+           if (SINGLEBYTE_UTF8(data[n])) {
+               dest[m] = data[n];
+           } else {
+               /* An optimisation here: we only deal with 8-bit 
+                * data, which will be encoded as two bytes of UTF-8 */
+               if ((len - n < 2) ||
+                   decode_utf8_double(&dest[m], &data[n])) {
+                   /* Failed to decode! */
+                   NE_DEBUG(NE_DBG_XML, "Could not decode UTF-8 data.\n");
+                   strcpy(p->error, "XML parser received non-8-bit data");
+                   p->valid = 0;
+                   return;
+               } else {
+#ifdef TOO_MUCH_DEBUG
+                   NE_DEBUG(NE_DBG_XML, "UTF-8 two-bytes decode: "
+                          "0x%02hx 0x%02hx -> 0x%02hx!\n",
+                          data[n] & 0xFF, data[n+1] & 0xFF, dest[m] & 0xFF);
+#endif
+                   /* Skip the second byte */
+                   n++;
+               }
+           }
+       }
+       ne_buffer_altered(p->buffer);
+    } else {
+       ne_buffer_append(p->buffer, data, len);
+    }
+
+#else /* !NE_XML_DECODE_UTF8 */
+
+    ne_buffer_append(p->buffer, data, len);
+
+#endif
+
+}
+
+/* Called with the end of an element */
+static void end_element(void *userdata, const ne_xml_char *name) 
+{
+    ne_xml_parser *p = userdata;
+    struct ne_xml_state *s = p->current;
+    if (!p->valid) {
+       /* We've stopped parsing */
+       NE_DEBUG(NE_DBG_XML, "Parse died. Ignoring end of element: %s\n", name);
+       return;
+    }
+    if (p->collect > 0) {
+       if (--p->collect) {
+           const ne_xml_char *pnt = strchr(name, ':');
+           if (pnt == NULL) {
+               pnt = name;
+           } else {
+               pnt++;
+           }
+           ne_buffer_concat(p->buffer, "</", pnt, ">", NULL);
+           return;
+       }
+    }
+       
+    /* process it */
+    if (s->handler->endelm_cb) {
+       NE_DEBUG(NE_DBG_XMLPARSE, "Calling endelm callback for %s.\n", s->elm->name);
+       if ((*s->handler->endelm_cb)(s->handler->userdata, s->elm,
+                                    p->want_cdata?p->buffer->data:NULL)) {
+           NE_DEBUG(NE_DBG_XML, "Endelm callback failed.\n");
+           p->valid = 0;
+       }
+    }
+    p->current = s->parent;
+    /* Move the current pointer up the branch */
+    NE_DEBUG(NE_DBG_XML, "Back in element: %s\n", friendly_name(p->current->elm));
+    if (p->want_cdata) {
+       ne_buffer_clear(p->buffer);
+    } 
+    destroy_state(s);
+}
+
+/* Parses the attributes, and handles XML namespaces. 
+ * With a little bit of luck.
+ * Returns:
+ *   the element name on success
+ *   or NULL on error.
+ */
+static int parse_element(ne_xml_parser *p, struct ne_xml_state *state,
+                        const ne_xml_char *name, const ne_xml_char **atts)
+{
+    struct ne_xml_nspace *ns;
+    const ne_xml_char *pnt;
+    struct ne_xml_state *xmlt;
+
+    NE_DEBUG(NE_DBG_XMLPARSE, "Parsing elm of name: [%s]\n", name);
+    /* Parse the atts for namespace declarations... if we have any atts.
+     * expat will never pass us atts == NULL, but libxml will. */
+    if (atts != NULL) {
+       int attn;
+       for (attn = 0; atts[attn]!=NULL; attn+=2) {
+           NE_DEBUG(NE_DBG_XMLPARSE, "Got attribute: [%s] = [%s]\n", atts[attn], atts[attn+1]);
+           if (strcasecmp(atts[attn], "xmlns") == 0) {
+               /* New default namespace */
+               state->default_ns = ne_strdup(atts[attn+1]);
+               NE_DEBUG(NE_DBG_XMLPARSE, "New default namespace: %s\n", 
+                      state->default_ns);
+           } else if (strncasecmp(atts[attn], "xmlns:", 6) == 0) {
+               /* New namespace scope */
+               ns = ne_calloc(sizeof(struct ne_xml_nspace));
+               ns->next = state->nspaces;
+               state->nspaces = ns;
+               ns->name = ne_strdup(atts[attn]+6); /* skip the xmlns= */
+               ns->uri = ne_strdup(atts[attn+1]);
+               NE_DEBUG(NE_DBG_XMLPARSE, "New namespace scope: %s -> %s\n",
+                      ns->name, ns->uri);
+           }
+       }
+    }
+    /* Now check the elm name for a namespace scope */
+    pnt = strchr(name, ':');
+    if (pnt == NULL) {
+       /* No namespace prefix - have we got a default? */
+       state->real_name = ne_strdup(name);
+       NE_DEBUG(NE_DBG_XMLPARSE, "No prefix found, searching for default.\n");
+       for (xmlt = state; xmlt!=NULL; xmlt=xmlt->parent) {
+           if (xmlt->default_ns != NULL) {
+               state->elm_real.nspace = xmlt->default_ns;
+               break;
+           }
+       }
+       if (state->elm_real.nspace == NULL) {
+           NE_DEBUG(NE_DBG_XMLPARSE, "No default namespace, using empty.\n");
+           state->elm_real.nspace = "";
+       }
+    } else {
+       NE_DEBUG(NE_DBG_XMLPARSE, "Got namespace scope. Trying to resolve...");
+       /* Have a scope - resolve it */
+       for (xmlt = state; state->elm_real.nspace==NULL && xmlt!=NULL; xmlt=xmlt->parent) {
+           for (ns = xmlt->nspaces; ns!=NULL; ns=ns->next) {
+               /* Just compare against the bit before the :
+                * pnt points to the colon. */
+               if (strncasecmp(ns->name, name, pnt-name) == 0) {
+                   /* Scope matched! Hoorah */
+                   state->elm_real.nspace = ns->uri;
+                   /* end the search */
+                   break;
+               }
+           }
+       }
+       if (state->elm_real.nspace != NULL) {
+           NE_DEBUG(NE_DBG_XMLPARSE, "Resolved prefix to [%s]\n", state->elm_real.nspace);
+           /* The name is everything after the ':' */
+           if (pnt[1] == '\0') {
+               snprintf(p->error, ERR_SIZE, 
+                         "Element name missing in '%s' at line %d.",
+                         name, ne_xml_currentline(p));
+               NE_DEBUG(NE_DBG_XMLPARSE, "No element name after ':'. Failed.\n");
+               return -1;
+           }
+           state->real_name = ne_strdup(pnt+1);
+       } else {
+           NE_DEBUG(NE_DBG_XMLPARSE, "Undeclared namespace.\n");
+           snprintf(p->error, ERR_SIZE, 
+                     "Undeclared namespace in '%s' at line %d.",
+                     name, ne_xml_currentline(p));
+           return -1;
+       }
+    }
+    state->elm_real.name = state->real_name;
+    return 0;
+}
+
+ne_xml_parser *ne_xml_create(void) 
+{
+    ne_xml_parser *p = ne_calloc(sizeof *p);
+    /* Initialize other stuff */
+    p->valid = 1;
+    /* Placeholder for the root element */
+    p->current = p->root = ne_calloc(sizeof(struct ne_xml_state));
+    p->root->elm = &root_element;
+    /* Initialize the cdata buffer */
+    p->buffer = ne_buffer_create();
+#ifdef HAVE_EXPAT
+    p->parser = XML_ParserCreate(NULL);
+    if (p->parser == NULL) {
+       abort();
+    }
+    XML_SetElementHandler(p->parser, start_element, end_element);
+    XML_SetCharacterDataHandler(p->parser, char_data);
+    XML_SetUserData(p->parser, (void *) p);
+#else
+    p->parser = xmlCreatePushParserCtxt(&sax_handler, 
+                                       (void *)p, NULL, 0, NULL);
+    if (p->parser == NULL) {
+       abort();
+    }
+#endif
+    return p;
+}
+
+static void push_handler(ne_xml_parser *p,
+                        struct ne_xml_handler *handler)
+{
+
+    /* If this is the first handler registered, update the
+     * base pointer too. */
+    if (p->top_handlers == NULL) {
+       p->root->handler = handler;
+       p->top_handlers = handler;
+    } else {
+       p->top_handlers->next = handler;
+       p->top_handlers = handler;
+    }
+}
+
+void ne_xml_push_handler(ne_xml_parser *p,
+                         const struct ne_xml_elm *elements, 
+                         ne_xml_validate_cb validate_cb, 
+                         ne_xml_startelm_cb startelm_cb, 
+                         ne_xml_endelm_cb endelm_cb,
+                         void *userdata)
+{
+    struct ne_xml_handler *hand = ne_calloc(sizeof(struct ne_xml_handler));
+
+    hand->elements = elements;
+    hand->validate_cb = validate_cb;
+    hand->startelm_cb = startelm_cb;
+    hand->endelm_cb = endelm_cb;
+    hand->userdata = userdata;
+
+    push_handler(p, hand);
+}
+
+void ne_xml_push_mixed_handler(ne_xml_parser *p,
+                              const struct ne_xml_elm *elements,
+                              ne_xml_validate_cb validate_cb,
+                              ne_xml_startelm_cb startelm_cb,
+                              ne_xml_cdata_cb cdata_cb,
+                              ne_xml_endelm_cb endelm_cb,
+                              void *userdata)
+{
+    struct ne_xml_handler *hand = ne_calloc(sizeof *hand);
+    
+    hand->elements = elements;
+    hand->validate_cb = validate_cb;
+    hand->startelm_cb = startelm_cb;
+    hand->cdata_cb = cdata_cb;
+    hand->endelm_cb = endelm_cb;
+    hand->userdata = userdata;
+    
+    push_handler(p, hand);
+}
+
+void ne_xml_parse_v(void *userdata, const char *block, size_t len) 
+{
+    ne_xml_parser *p = userdata;
+    /* FIXME: The two XML parsers break all our nice abstraction by
+     * choosing different char *'s. The swine. This cast will come
+     * back and bite us someday, no doubt. */
+    ne_xml_parse(p, block, len);
+}
+
+/* Parse the given block of input of length len */
+void ne_xml_parse(ne_xml_parser *p, const char *block, size_t len) 
+{
+    int ret, flag;
+    /* duck out if it's broken */
+    if (!p->valid) {
+       NE_DEBUG(NE_DBG_XML, "Not parsing %d bytes.\n", len);
+       return;
+    }
+    if (len == 0) {
+       flag = -1;
+       block = "";
+       NE_DEBUG(NE_DBG_XML, "Got 0-length buffer, end of document.\n");
+    } else {   
+       NE_DEBUG(NE_DBG_XML, "Parsing %d length buffer.\n", len);
+       flag = 0;
+    }
+    /* Note, don't write a parser error if !p->valid, since an error
+     * will already have been written in that case. */
+#ifdef HAVE_EXPAT
+    ret = XML_Parse(p->parser, block, len, flag);
+    NE_DEBUG(NE_DBG_XMLPARSE, "XML_Parse returned %d\n", ret);
+    if (ret == 0 && p->valid) {
+       snprintf(p->error, ERR_SIZE,
+                 "XML parse error at line %d: %s", 
+                 XML_GetCurrentLineNumber(p->parser),
+                 XML_ErrorString(XML_GetErrorCode(p->parser)));
+       p->valid = 0;
+    }
+#else
+    ret = xmlParseChunk(p->parser, block, len, flag);
+    NE_DEBUG(NE_DBG_XMLPARSE, "xmlParseChunk returned %d\n", ret);
+    if (p->parser->errNo && p->valid) {
+       /* FIXME: error handling */
+       snprintf(p->error, ERR_SIZE, "XML parse error at line %d.", 
+                ne_xml_currentline(p));
+       p->valid = 0;
+    }
+#endif
+}
+
+int ne_xml_valid(ne_xml_parser *p)
+{
+    return p->valid;
+}
+
+void ne_xml_destroy(ne_xml_parser *p) 
+{
+    struct ne_xml_state *s, *parent;
+    struct ne_xml_handler *hand, *next;
+
+    ne_buffer_destroy(p->buffer);
+
+    /* Free up the handlers on the stack: the root element has the
+     * pointer to the base of the handler stack. */
+    for (hand = p->root->handler; hand!=NULL; hand=next) {
+       next = hand->next;
+       free(hand);
+    }
+
+    /* Clean up any states which may remain.
+     * If p.valid, then this should be only the root element. */
+    for (s = p->current; s!=NULL; s=parent) {
+       parent = s->parent;
+       destroy_state(s);
+    }
+        
+#ifdef HAVE_EXPAT
+    XML_ParserFree(p->parser);
+#else
+    xmlFreeParserCtxt(p->parser);
+#endif
+
+    free(p);
+}
+
+void ne_xml_set_error(ne_xml_parser *p, const char *msg)
+{
+    snprintf(p->error, ERR_SIZE, msg);
+}
+
+#ifdef HAVE_LIBXML
+static void sax_error(void *ctx, const char *msg, ...)
+{
+    ne_xml_parser *p = ctx;
+    va_list ap;
+    char buf[1024];
+
+    va_start(ap, msg);
+    vsnprintf(buf, 1024, msg, ap);
+    va_end(ap);
+
+    snprintf(p->error, ERR_SIZE, 
+            _("XML parse error at line %d: %s."),
+            p->parser->input->line, buf);
+
+    p->valid = 0;
+}
+#endif
+
+const char *ne_xml_get_error(ne_xml_parser *p)
+{
+    return p->error;
+}
+
+
+const char *ne_xml_get_attr(const char **attrs, const char *name)
+{
+    int n;
+    
+#ifdef HAVE_LIBXML
+    /* libxml passes a NULL attribs array to the startelm callback if
+     * there are no attribs on the element.  expat passes a
+     * zero-length array. */
+    if (attrs == NULL) {
+       return NULL;
+    }
+#endif
+
+    for (n = 0; attrs[n] != NULL; n += 2) {
+       if (strcmp(attrs[n], name) == 0) {
+           return attrs[n+1];
+       }
+    }
+
+    return NULL;
+}
diff --git a/neon/src/ne_xml.h b/neon/src/ne_xml.h
new file mode 100644 (file)
index 0000000..2d2c365
--- /dev/null
@@ -0,0 +1,184 @@
+/* 
+   Higher Level Interface to XML Parsers.
+   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NE_XML_H
+#define NE_XML_H
+
+BEGIN_NEON_DECLS
+
+/* Generic XML parsing interface...
+   
+   Definitions:
+   
+   * A handler knows how to parse a certain set of XML elements.
+   * The handler stack
+
+   * 
+   
+
+   Basic principle is that you provide these things:
+     1) a list of elements which you wish to handle 
+     2) a validation callback which tells the parser whether you
+     want to handle this element.
+     3) a callback function which is called when a complete element
+     has been parsed (provided you are handling the element).
+    
+   This code then deals with the boring stuff like:
+     - Dealing with XML namespaces   
+     - Collecting CDATA
+
+   The element list is stored in a 'ne_xml_elm' array.  For each
+   element, you specify the element name, an element id, and some
+   flags for that element.
+
+const static struct ne_xml_elm[] = {
+   { "DAV:", "resourcetype", ELM_resourcetype, 0 },
+   { "DAV:", "collection", ELM_collection, NE_ELM_CDATA },
+   { NULL }
+};
+
+   This list contains two elements, resourcetype and collection, both in 
+   the "DAV:" namespace.  The collection element can contain cdata.
+   
+*/
+
+/* Reserved element id's */
+#define NE_ELM_unknown -1
+#define NE_ELM_root 0
+
+#define NE_ELM_UNUSED (100)
+
+typedef int ne_xml_elmid;
+
+struct ne_xml_elm;
+
+/* An element */
+struct ne_xml_elm {
+    const char *nspace, *name;
+    ne_xml_elmid id;
+    unsigned int flags;
+};
+
+/* Function to check element context... 
+   This callback must return:
+     NE_XML_VALID ->
+       Yes, this is valid XML, and I want to handle this element.
+     NE_XML_INVALID ->
+       No, this is NOT valid XML, and parsing should stop.
+     NE_XML_DECLINE ->
+       I don't know anything about this element, someone else
+       can handle it.
+*/
+
+
+#define NE_XML_VALID (0)
+#define NE_XML_INVALID (-1)
+#define NE_XML_DECLINE (-2)
+
+/* Validate a new child element. */
+typedef int (*ne_xml_validate_cb)
+    (ne_xml_elmid parent, ne_xml_elmid child);
+
+typedef int (*ne_xml_startelm_cb)
+    (void *userdata, const struct ne_xml_elm *elm, const char **atts);
+
+/* Called when a complete element is parsed */
+typedef int (*ne_xml_endelm_cb)
+    (void *userdata, const struct ne_xml_elm *s, const char *cdata);
+
+typedef void (*ne_xml_cdata_cb)
+    (void *userdata, const struct ne_xml_elm *s, 
+     const char *cdata, int len);
+
+struct ne_xml_parser_s;
+typedef struct ne_xml_parser_s ne_xml_parser;
+
+/* Flags */
+/* This element has no children */
+#define NE_XML_CDATA (1<<1)
+/* Collect complete contents of this node as cdata */
+#define NE_XML_COLLECT ((1<<2) | NE_XML_CDATA)
+/* Decode UTF-8 data in cdata. */
+#define NE_XML_UTF8DECODE (1<<3)
+/* This element uses MIXED mode */
+#define NE_XML_MIXED (1<<4)
+
+/* Initialise the parser */
+ne_xml_parser *ne_xml_create(void);
+
+/* Push a handler onto the handler stack for the given list of elements.
+ * elements must be an array, with the last element .nspace being NULL.
+ * Callbacks are called in order:
+ *   1. validate_cb
+ *   2. startelm_cb
+ *   3. endelm_cb
+ * (then back to the beginning again).
+ * If any of the callbacks ever return non-zero, the parse will STOP.
+ * userdata is passed as the first argument to startelm_cb and endelm_cb.
+ */
+void ne_xml_push_handler(ne_xml_parser *p,
+                         const struct ne_xml_elm *elements, 
+                         ne_xml_validate_cb validate_cb, 
+                         ne_xml_startelm_cb startelm_cb, 
+                         ne_xml_endelm_cb endelm_cb,
+                         void *userdata);
+
+/* Add a handler which uses a mixed-mode cdata callback */
+void ne_xml_push_mixed_handler(ne_xml_parser *p,
+                               const struct ne_xml_elm *elements,
+                               ne_xml_validate_cb validate_cb,
+                               ne_xml_startelm_cb startelm_cb,
+                               ne_xml_cdata_cb cdata_cb,
+                               ne_xml_endelm_cb endelm_cb,
+                               void *userdata);
+
+/* Returns non-zero if the parse was valid, zero if it failed (e.g.,
+ * any of the callbacks return non-zero, the XML was not well-formed,
+ * etc).  Use ne_xml_get_error to retrieve the error message if it
+ * failed. */
+int ne_xml_valid(ne_xml_parser *p);
+
+/* Destroys the parser. Any operations on it then have 
+ * undefined results. */
+void ne_xml_destroy(ne_xml_parser *p);
+
+/* Parse the given block of input of length len. Block does 
+ * not need to be NULL-terminated. */
+void ne_xml_parse(ne_xml_parser *p, const char *block, size_t len);
+
+/* As above, casting (ne_xml_parser *)userdata internally.
+ * (This function can be passed to http_add_response_body_reader) */
+void ne_xml_parse_v(void *userdata, const char *block, size_t len);
+
+/* Return current parse line for errors */
+int ne_xml_currentline(ne_xml_parser *p);
+
+/* Set error message for parser */
+void ne_xml_set_error(ne_xml_parser *p, const char *msg);
+
+const char *ne_xml_get_error(ne_xml_parser *p);
+
+/* Get attribute of given name. TODO: doesn't consider namespaces. */
+const char *ne_xml_get_attr(const char **attrs, const char *name);
+
+END_NEON_DECLS
+
+#endif /* NE_XML_H */
diff --git a/neon/src/neon.h b/neon/src/neon.h
new file mode 100644 (file)
index 0000000..4ebf947
--- /dev/null
@@ -0,0 +1,3 @@
+
+#undef NEON_VERSION
+#define NEON_VERSION "0.1.0"
diff --git a/neon/src/neon_config.h b/neon/src/neon_config.h
new file mode 100644 (file)
index 0000000..d691bd5
--- /dev/null
@@ -0,0 +1,8 @@
+
+#ifndef NEON_CONFIG_H
+#define NEON_CONFIG_H
+
+#undef NEON_VERSION
+#define NEON_VERSION "0.2.0"
+
+#endif
diff --git a/neon/src/neon_defs.h b/neon/src/neon_defs.h
new file mode 100644 (file)
index 0000000..f029edf
--- /dev/null
@@ -0,0 +1,10 @@
+
+#undef BEGIN_NEON_DECLS
+#undef END_NEON_DECLS
+#ifdef __cplusplus
+# define BEGIN_NEON_DECLS extern "C" {
+# define END_NEON_DECLS }
+#else
+# define BEGIN_NEON_DECLS /* empty */
+# define END_NEON_DECLS /* empty */
+#endif
diff --git a/neon/src/neon_i18n.c b/neon/src/neon_i18n.c
new file mode 100644 (file)
index 0000000..509ca9d
--- /dev/null
@@ -0,0 +1,30 @@
+/* 
+   Internationalization of neon
+   Copyright (C) 2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: neon_i18n.c,v 1.2 2000/07/16 15:45:04 joe Exp 
+*/
+
+void neon_i18n_init(void)
+{
+#if defined(ENABLE_NLS) && defined(NEON_IS_LIBRARY)
+    bindtextdomain("neon", LOCALEDIR);
+    /* if neon is build bundled in, then there is probably
+     * no point in this... probably. */
+#endif
+}
diff --git a/neon/src/neon_i18n.h b/neon/src/neon_i18n.h
new file mode 100644 (file)
index 0000000..5d82eac
--- /dev/null
@@ -0,0 +1,11 @@
+
+/* Include this to use I18N inside neon */
+
+#undef _
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(str) gettext(str)
+#else
+#define _(str) (str)
+#endif /* ENABLE_NLS */
+#define N_(str) (str)
diff --git a/neon/src/neon_md5.h b/neon/src/neon_md5.h
new file mode 100644 (file)
index 0000000..6ff784c
--- /dev/null
@@ -0,0 +1,157 @@
+/* Declaration of functions and data types used for MD5 sum computing
+   library functions.
+   Copyright (C) 1995, 1996, 1997 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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef NEON_MD5_H
+#define NEON_MD5_H 1
+
+#include <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/* The following contortions are an attempt to use the C preprocessor
+   to determine an unsigned integral type that is 32 bits wide.  An
+   alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+   doing that would require that the configure script compile and *run*
+   the resulting executable.  Locally running cross-compiled executables
+   is usually not possible.  */
+
+#ifdef _LIBC
+# include <sys/types.h>
+typedef u_int32_t md5_uint32;
+#else
+# if defined __STDC__ && __STDC__
+#  define UINT_MAX_32_BITS 4294967295U
+# else
+#  define UINT_MAX_32_BITS 0xFFFFFFFF
+# endif
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+   This should be valid for all systems GNU cares about because
+   that doesn't include 16-bit systems, and only modern systems
+   (that certainly have <limits.h>) have 64+-bit integral types.  */
+
+# ifndef UINT_MAX
+#  define UINT_MAX UINT_MAX_32_BITS
+# endif
+
+# if UINT_MAX == UINT_MAX_32_BITS
+   typedef unsigned int md5_uint32;
+# else
+#  if USHRT_MAX == UINT_MAX_32_BITS
+    typedef unsigned short md5_uint32;
+#  else
+#   if ULONG_MAX == UINT_MAX_32_BITS
+     typedef unsigned long md5_uint32;
+#   else
+     /* The following line is intended to evoke an error.
+        Using #error is not portable enough.  */
+     "Cannot determine unsigned 32-bit data type."
+#   endif
+#  endif
+# endif
+#endif
+
+#undef __P
+#if defined (__STDC__) && __STDC__
+# define __P(x) x
+#else
+# define __P(x) ()
+#endif
+
+/* Structure to save state of computation between the single steps.  */
+struct md5_ctx
+{
+  md5_uint32 A;
+  md5_uint32 B;
+  md5_uint32 C;
+  md5_uint32 D;
+
+  md5_uint32 total[2];
+  md5_uint32 buflen;
+  char buffer[128];
+};
+
+/*
+ * The following three functions are build up the low level used in
+ * the functions `md5_stream' and `md5_buffer'.
+ */
+
+/* Initialize structure containing state of computation.
+   (RFC 1321, 3.3: Step 3)  */
+extern void __md5_init_ctx __P ((struct md5_ctx *ctx));
+extern void md5_init_ctx __P ((struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is necessary that LEN is a multiple of 64!!! */
+extern void __md5_process_block __P ((const void *buffer, size_t len,
+                                     struct md5_ctx *ctx));
+extern void md5_process_block __P ((const void *buffer, size_t len,
+                                   struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is NOT required that LEN is a multiple of 64.  */
+extern void __md5_process_bytes __P ((const void *buffer, size_t len,
+                                     struct md5_ctx *ctx));
+extern void md5_process_bytes __P ((const void *buffer, size_t len,
+                                   struct md5_ctx *ctx));
+
+/* Process the remaining bytes in the buffer and put result from CTX
+   in first 16 bytes following RESBUF.  The result is always in little
+   endian byte order, so that a byte-wise output yields to the wanted
+   ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+extern void *__md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
+extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
+
+
+/* Put result from CTX in first 16 bytes following RESBUF.  The result is
+   always in little endian byte order, so that a byte-wise output yields
+   to the wanted ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+extern void *__md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
+extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
+
+
+/* Compute MD5 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 16 bytes
+   beginning at RESBLOCK.  */
+extern int __md5_stream __P ((FILE *stream, void *resblock));
+extern int md5_stream __P ((FILE *stream, void *resblock));
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+extern void *__md5_buffer __P ((const char *buffer, size_t len,
+                               void *resblock));
+extern void *md5_buffer __P ((const char *buffer, size_t len,
+                             void *resblock));
+
+#endif /* NEON_MD5_H */
diff --git a/neon/src/nsocket.h b/neon/src/nsocket.h
new file mode 100644 (file)
index 0000000..dd5c30a
--- /dev/null
@@ -0,0 +1,192 @@
+/* 
+   socket handling interface
+   Copyright (C) 1998-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef NSOCKET_H
+#define NSOCKET_H
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+
+#include <neon_defs.h>
+
+BEGIN_NEON_DECLS
+
+#define SOCK_ERROR -1
+/* Read/Write timed out */
+#define SOCK_TIMEOUT -2
+/* Passed buffer was full */
+#define SOCK_FULL -3
+/* Socket was closed */
+#define SOCK_CLOSED -4
+
+/* Socket read timeout */
+#define SOCKET_READ_TIMEOUT 60
+
+typedef enum {
+    sock_namelookup, /* Looking up hostname given by info */
+    sock_connecting, /* Connecting to server */
+    sock_connected, /* Connection established */
+    sock_secure_details /* Secure connection details */
+} sock_status;
+
+struct nsocket_s;
+typedef struct nsocket_s nsocket;
+
+typedef void (*sock_block_reader) (
+    void *userdata, const char *buf, size_t len);
+
+typedef void (*sock_progress)(void *userdata, size_t progress, size_t total);
+typedef void (*sock_notify)(void *userdata, 
+                           sock_status status, const char *info);
+
+void sock_register_progress(sock_progress cb, void *userdata);
+void sock_register_notify(sock_notify cb, void *userdata);
+
+void sock_call_progress(size_t progress, size_t total);
+
+/* Initialize the socket library. If you don't do this, SSL WILL NOT WORK.
+ * Returns 0 on success, or non-zero on screwed up SSL library. */
+int sock_init(void);
+
+/* sock_read is read() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ *   SOCK_* on error,
+ *    0 on no data to read (due to EOF),
+ *   >0 length of data read into buffer.
+ */
+int sock_read(nsocket *sock, char *buffer, size_t count);
+
+/* sock_peek is recv() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ *   SOCK_* on error,
+ *    0 on no data to read (due to EOF),
+ *   >0 length of data read into buffer.
+ */
+int sock_peek(nsocket *sock, char *buffer, size_t count);
+
+/* Blocks waiting for data on the given socket for the given time.
+ * Returns:
+ *  SOCK_* on error,
+ *  SOCK_TIMEOUT on no data within timeout,
+ *  0 if data arrived on the socket.
+ */
+int sock_block(nsocket *sock, int timeout);
+
+/* Reads readlen bytes from fd and writes to socket.
+ * (Not all in one go, obviously).
+ * If readlen == -1, then it reads from srcfd until EOF.
+ * Returns number of bytes written to destfd, or SOCK_* on error.
+ */
+int sock_transfer(int fd, nsocket *sock, ssize_t readlen);
+
+/* Sends the given line to given socket, CRLF appended */
+int sock_sendline(nsocket *sock, const char *line); 
+/* Sends the given block of data down the nsocket */
+int sock_fullwrite(nsocket *sock, const char *data, size_t length); 
+/* Sends the null-terminated string down the given nsocket */
+int sock_send_string(nsocket *sock, const char *string); 
+
+/* Reads a line from given nsocket */
+int sock_readline(nsocket *sock, char *line, int len); 
+/* Reads a chunk of data. */
+int sock_fullread(nsocket *sock, char *buffer, int buflen);
+
+/* Creates and connects a nsocket */
+nsocket *sock_connect(const struct in_addr host, int portnum);
+
+nsocket *sock_connect_u(const struct in_addr addr, int portnum, int call_fe);
+
+/* Not as good as accept(2), missing parms 2+3.
+ * Addings parms 2+3 would probably mean passing socklen_t as an
+ * int then casting internally, since we don't really want to
+ * autogenerate the header file to be correct for the build platform.
+ */
+nsocket *sock_accept(int listener);
+
+/* Returns the file descriptor used for the socket */
+int sock_get_fd(nsocket *sock);
+
+/* Closes the socket and frees the nsocket object. */
+int sock_close(nsocket *socket);
+
+const char *sock_get_error(nsocket *sock);
+
+/* Do a name lookup on given hostname, writes the address into
+ * given address buffer. Return -1 on failure. */
+int sock_name_lookup(const char *hostname, struct in_addr *addr);
+
+/* Returns the standard TCP port for the given service */
+int sock_service_lookup(const char *name);
+
+int sock_readfile_blocked(nsocket *socket, size_t length,
+                         sock_block_reader reader, void *userdata);
+
+/* Auxiliary for use with SSL. */
+struct nssl_context_s;
+typedef struct nssl_context_s nssl_context;
+
+/* Netscape's prompts on getting a certificate which it doesn't
+ * recognize the CA for:
+ *   1. Hey, I don't recognize the CA for this cert.
+ *   2. Here is the certificate: for foo signed by BLAH,
+ *      using encryption level BLEE
+ *   3. Allow: accept for this session only, 
+ *             don't accept
+ *             accept forever
+ */
+nssl_context *sock_create_ssl_context(void);
+
+void sock_destroy_ssl_context(nssl_context *ctx);
+
+/* Callback to decide whether the user will accept the
+ * given certificate or not */
+typedef struct {
+    char *owner; /* Multi-line string describing owner of
+                 * certificate */
+    char *issuer; /* As above for issuer of certificate */
+    /* Strings the certificate is valid between */
+    char *valid_from, *valid_till;
+    /* Certificate fingerprint */
+    char *fingerprint;
+} nssl_certificate;
+
+/* Returns:
+ *   0 -> User accepts the certificate
+ *   non-zero -> user does NOT accept the certificate.
+ */
+typedef int (*nssl_accept)(void *userdata, const nssl_certificate *info);
+
+void sock_set_cert_accept(nssl_context *c, 
+                         nssl_accept accepter, void *userdata);
+void sock_set_certificate_file(nssl_context *c, const char *fname);
+
+void sock_disable_tlsv1(nssl_context *c);
+void sock_disable_sslv2(nssl_context *c);
+void sock_disable_sslv3(nssl_context *c);
+
+/* Ctx is OPTIONAL. If it is NULL, defaults are used. */
+int sock_make_secure(nsocket *sock, nssl_context *ctx);
+
+END_NEON_DECLS
+
+#endif /* NSOCKET_H */
diff --git a/neon/src/socket.c b/neon/src/socket.c
new file mode 100644 (file)
index 0000000..a4899ac
--- /dev/null
@@ -0,0 +1,592 @@
+/* 
+   socket handling routines
+   Copyright (C) 1998, 1999, 2000, Joe Orton <joe@orton.demon.co.uk>, 
+   except where otherwise indicated.
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   The sock_readline() function is:
+
+   Copyright (c) 1999 Eric S. Raymond
+
+   Permission is hereby granted, free of charge, to any person
+   obtaining a copy of this software and associated documentation
+   files (the "Software"), to deal in the Software without
+   restriction, including without limitation the rights to use, copy,
+   modify, merge, publish, distribute, sublicense, and/or sell copies
+   of the Software, and to permit persons to whom the Software is
+   furnished to do so, subject to the following conditions:
+
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+   DEALINGS IN THE SOFTWARE.
+
+   Id: socket.c,v 1.7 2000/05/10 13:24:42 joe Exp  
+*/
+
+#include <config.h>
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <errno.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif 
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include "string_utils.h"
+#include "http_utils.h"
+#include "socket.h"
+
+static sock_progress progress_cb = NULL;
+static sock_notify notify_cb = NULL;
+static void *progress_ud, *notify_ud;
+
+void sock_register_progress( sock_progress cb, void *userdata )
+{
+    progress_cb = cb;
+    progress_ud = userdata;
+}
+
+void sock_register_notify( sock_notify cb, void *userdata )
+{
+    notify_cb = cb;
+    notify_ud = userdata;
+}
+
+void sock_call_progress( size_t progress, size_t total )
+{
+    if( progress_cb ) {
+       (*progress_cb)( progress_ud, progress, total );
+    }
+}
+
+/* sock_read is read() with a timeout of SOCKET_READ_TIMEOUT. */
+int sock_read( int sock, void *buffer, size_t count ) {
+    int ret;
+    ret = sock_block( sock, SOCKET_READ_TIMEOUT );
+    if( ret == 0 ) {
+       /* Got data */
+       do {
+           ret = read( sock, buffer, count );
+       } while( ret == -1 && errno == EINTR );
+       if( ret < 0 ) {
+           ret = SOCK_ERROR;
+       }
+    }
+    return ret;
+}
+
+/* sock_recv is recv() with a timeout of SOCKET_TIMEOUT.
+ * Returns length of data read or SOCK_* on error */
+int sock_recv( int sock, void *buffer, size_t count, unsigned int flags ) {
+    int ret;
+    ret = sock_block( sock, SOCKET_READ_TIMEOUT );
+    if( ret < 0 ) {
+       return ret;
+    }
+    /* Got data */
+    do {
+       ret = recv( sock, buffer, count, flags );
+    } while( ret == -1 && errno == EINTR );
+    if( ret < 0 ) {
+       ret = SOCK_ERROR;
+    }
+    return ret;
+}
+
+/* Blocks waiting for read input on the given socket for the given time.
+ * Returns:
+ *    0 if data arrived
+ *    SOCK_TIMEOUT if data did not arrive before timeout
+ *    SOCK_ERROR on error
+ */
+int sock_block( int sock, int timeout ) {
+    struct timeval tv;
+    fd_set fds;
+    int ret;
+    
+    /* Init the fd set */
+    FD_ZERO( &fds );
+    FD_SET( sock, &fds );
+    /* Set the timeout */
+    tv.tv_sec = timeout;
+    tv.tv_usec = 0;
+    do {
+       ret = select( sock+1, &fds, NULL, NULL, &tv );
+    } while( ret == -1 && errno == EINTR );
+
+    switch( ret ) {
+    case 0:
+       return SOCK_TIMEOUT;
+    case -1:
+       return SOCK_ERROR;
+    default:
+       return 0;
+    }
+}
+
+/* Send the given line down the socket with CRLF appended. 
+ * Returns 0 on success or SOCK_* on failure. */
+int sock_sendline( int sock, const char *line ) {
+    char *buffer;
+    int ret;
+    CONCAT2( buffer, line, "\r\n" );
+    ret = sock_send_string( sock, buffer );
+    free( buffer );
+    return ret;
+}
+
+/* Reads from fd, passing blocks to reader, also calling
+ * fe_t_p. 
+ * Returns 0 on success or SOCK_* on error. */
+int sock_readfile_blocked( int fd, size_t length,
+                          sock_block_reader reader, void *userdata ) {
+    char buffer[BUFSIZ];
+    int ret;
+    size_t done = 0;
+    do {
+       ret = sock_read( fd, buffer, BUFSIZ );
+       if( ret < 0 ) {
+           return ret;
+       } 
+       done += ret;
+       sock_call_progress( done, length );
+       (*reader)( userdata, buffer, ret );
+    } while( (done < length) && ret );
+    return 0;
+}
+
+
+/* Send a block of data down the given fd.
+ * Returns 0 on success or SOCK_* on failure */
+int sock_fullwrite( int fd, const char *data, size_t length ) {
+    ssize_t sent, wrote;
+    const char *pnt;
+
+    sent = 0;
+    pnt = data;
+    while( sent < length ) {
+       wrote = write( fd, pnt, length-sent );
+       if( wrote < 0 ) {
+           if( errno == EINTR ) {
+               continue;
+           } else if( errno == EPIPE ) {
+               return SOCK_CLOSED;
+           } else {
+               return SOCK_ERROR;
+           }
+       }
+       sent += wrote;
+    }
+    return 0;
+}
+
+/* Sends the given string down the given socket.
+ * Returns 0 on success or -1 on failure. */
+int sock_send_string( int sock, const char *data ) {
+    return sock_fullwrite( sock, data, strlen( data ) );
+}
+
+/* This is from from Eric Raymond's fetchmail (SockRead() in socket.c)
+ * since I wouldn't have a clue how to do it properly.
+ * This function is Copyright 1999 (C) Eric Raymond.
+ * Modifications Copyright 2000 (C) Joe Orton
+ */
+int sock_readline(int sock, char *buf, int len)
+{
+    char *newline, *bp = buf;
+    int n;
+
+    do {
+       /* 
+        * The reason for these gymnastics is that we want two things:
+        * (1) to read \n-terminated lines,
+        * (2) to return the true length of data read, even if the
+        *     data coming in has embedded NULS.
+        */
+#ifdef SSL_ENABLE
+
+       /* joe: of course, we don't actually have SSL support here yet,
+        * but this will be handy when that day comes. */
+
+       SSL *ssl;
+
+       if( NULL != ( ssl = SSLGetContext( sock ) ) ) {
+               /* Hack alert! */
+               /* OK...  SSL_peek works a little different from MSG_PEEK
+                       Problem is that SSL_peek can return 0 if there
+                       is no data currently available.  If, on the other
+                       hand, we loose the socket, we also get a zero, but
+                       the SSL_read then SEGFAULTS!  To deal with this,
+                       we'll check the error code any time we get a return
+                       of zero from SSL_peek.  If we have an error, we bail.
+                       If we don't, we read one character in SSL_read and
+                       loop.  This should continue to work even if they
+                       later change the behavior of SSL_peek
+                       to "fix" this problem...  :-(   */
+               if ((n = SSL_peek(ssl, bp, len)) < 0) {
+                       return(-1);
+               }
+               if( 0 == n ) {
+                       /* SSL_peek says no data...  Does he mean no data
+                       or did the connection blow up?  If we got an error
+                       then bail! */
+                       if( 0 != ( n = ERR_get_error() ) ) {
+                               return -1;
+                       }
+                       /* We didn't get an error so read at least one
+                               character at this point and loop */
+                       n = 1;
+                       /* Make sure newline start out NULL!
+                        * We don't have a string to pass through
+                        * the strchr at this point yet */
+                       newline = NULL;
+               } else if ((newline = memchr(bp, '\n', n)) != NULL)
+                       n = newline - bp + 1;
+               if ((n = SSL_read(ssl, bp, n)) == -1) {
+                       return(-1);
+               }
+               /* Check for case where our single character turned out to
+                * be a newline...  (It wasn't going to get caught by
+                * the strchr above if it came from the hack...  ). */
+               if( NULL == newline && 1 == n && '\n' == *bp ) {
+                       /* Got our newline - this will break
+                               out of the loop now */
+                       newline = bp;
+               }
+       } else {
+               if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0)
+                       return(-1);
+               if ((newline = memchr(bp, '\n', n)) != NULL)
+                       n = newline - bp + 1;
+               if ((n = read(sock, bp, n)) == -1)
+                       return(-1);
+       }
+#else
+       if ((n = sock_recv(sock, bp, len, MSG_PEEK)) <= 0)
+           return n;
+       if ((newline = memchr(bp, '\n', n)) != NULL)
+           n = newline - bp + 1;
+       if ((n = sock_read(sock, bp, n)) < 0)
+           return n;
+#endif
+       bp += n;
+       len -= n;
+       if( len < 1 ) return SOCK_FULL;
+    } while (!newline && len);
+    *bp = '\0';
+    return bp - buf;
+}
+
+/*** End of ESR-copyrighted section ***/
+
+/* Reads readlen bytes from srcfd and writes to destfd.
+ * (Not all in one go, obviously).
+ * If readlen == -1, then it reads from srcfd until EOF.
+ * Returns number of bytes written to destfd, or -1 on error.
+ */
+int sock_transfer( int srcfd, int destfd, ssize_t readlen ) {
+    char buffer[BUFSIZ], *pnt;
+    size_t curlen; /* total bytes yet to read from srcfd */
+    size_t sumwrlen; /* total bytes written to destfd */
+
+    if( readlen == -1 ) {
+       curlen = BUFSIZ; /* so the buffer size test works */
+    } else {
+       curlen = readlen; /* everything to do */
+    }
+    sumwrlen = 0; /* nowt done yet */
+
+    while( curlen > 0 ) {
+       int rdlen, len2;
+
+       /* Get a chunk... if the number of bytes that are left to read
+        * is less than the buffer size, only read that many bytes. */
+       rdlen = sock_read( srcfd, buffer, 
+                          (readlen==-1)?BUFSIZ:(min( BUFSIZ, curlen )) );
+       sock_call_progress( sumwrlen, readlen );
+       if( rdlen < 0 ) { 
+           return -1;
+       } else if( rdlen == 0 ) { 
+           /* End of file... get out of here */
+           break;
+       }
+       if( readlen != -1 )
+           curlen -= rdlen;
+       /* Otherwise, we have bytes!  Write them to destfd... might
+        * only manage to write a few of them at a time, so we have
+        * to deal with that too. */
+       /* Replace this with a call to send_data? */
+       
+       pnt = buffer;
+       len2 = rdlen;
+       while( len2 > 0 ) {
+           ssize_t wrlen;
+           wrlen = write( destfd, pnt, len2 );
+           if( wrlen < 0 ) { 
+               return SOCK_ERROR;
+           }
+           len2 -= wrlen;
+           pnt += wrlen;
+           sumwrlen += wrlen;
+       }
+    }
+    sock_call_progress( sumwrlen, readlen );
+    return sumwrlen;
+}
+
+/* Reads buflen bytes into buffer until it's full.
+ * Returns 0 on success, -1 on error */
+int sock_fullread( int sock, char *buffer, int buflen ) {
+    char *pnt; /* current position within buffer */
+    int len;
+    pnt = buffer;
+    while( buflen > 0 ) {
+       len = sock_read( sock, pnt, buflen );
+       if( len < 0 ) return len;
+       buflen -= len;
+       pnt += len;
+    }
+    return 0;
+}
+
+/* Dump the given filename down the given socket.
+ * Returns 0 on success or non-zero on error. */
+int send_file_binary( int sock, const char *filename ) {
+    int fd, wrote;
+    struct stat fs;
+
+#if defined (__EMX__) || defined(__CYGWIN__)
+    if( (fd = open( filename, O_RDONLY | O_BINARY )) < 0 ) {
+#else
+    if( (fd = open( filename, O_RDONLY )) < 0 ) {
+#endif     
+       return -1;
+    }
+    if( fstat( fd, &fs ) < 0 ) {
+       close( fd );
+       return -2;
+    }
+    /* What's the Right Thing to do? Choices:
+     *  a) Let transfer send everything from fd until EOF
+     *    + If the EOF pos changes, we'll know and can signal an error
+     *    - Unsafe - the transfer might not end if someone does 
+     *        yes > file
+     *  b) Tell transfer to send only the number of bytes from the stat()
+     *    + Safe - the transfer WILL end.
+     *    - If the EOF pos changes, we'll have a partial (corrupt) file.
+     * I'm not sure. I think (a) gets my vote but it doesn't allow
+     * nice transfer progress bars in the FE under the current API
+     * so we go with (b).
+     */
+    wrote = sock_transfer( fd, sock, fs.st_size );
+    close( fd ); /* any point in checking that one? */
+    if( wrote == fs.st_size ) {
+       return 0;
+    } else {
+       return -1;
+    }
+}
+
+/* Dump the given filename down the given socket, in ASCII translation
+ * mode.  Performs LF -> CRLF conversion.
+ * Returns zero on success or non-zero on error. */
+int send_file_ascii( int sock, const char *filename ) {
+    int ret;
+    char buffer[BUFSIZ], *pnt;
+    FILE *f;
+    ssize_t total = 0;
+    
+    f = fopen( filename, "r" );
+    if( f == NULL ) {
+       return -1;
+    }
+
+    /* Init to success */
+    ret = 0;
+
+    while(1) {
+       if( fgets( buffer, BUFSIZ - 1, f ) == NULL ) {
+           if( ferror( f ) ) {
+               ret = -1;
+               break;
+           }
+           /* Finished upload */
+           ret = 0;
+           break;
+       }
+       /* To send in ASCII mode, we need to send CRLF as the EOL.
+        * We might or might not already have CRLF-delimited lines.
+        * So we mess about a bit to ensure that we do.
+        */
+       pnt = strchr( buffer, '\r' );
+       if( pnt == NULL ) {
+           /* We need to add the CR in */
+           pnt = strchr( buffer, '\n' );
+           if( pnt == NULL ) {
+               /* No CRLF found at all */
+               pnt = strchr( buffer, '\0' );
+               if( pnt == NULL ) /* crud in buffer */
+                   pnt = buffer;
+           }
+           /* Now, pnt points to the first character after the 
+            * end of the line, i.e., where we want to put the CR.
+            */
+           *pnt++ = '\r';
+           /* And lob in an LF afterwards */
+           *pnt-- = '\n';
+       }
+       /* At this point, pnt points to the CR.
+        * We send everything between pnt and the beginning of the buffer,
+        * +2 for the CRLF
+        */
+       if( sock_fullwrite( sock, buffer, (pnt - buffer) +2 ) != 0 ) {
+           ret = -1;
+           break;
+       }
+       total += (pnt - buffer) + 2;
+       sock_call_progress( total, -1 );
+    }
+    fclose( f ); /* any point in checking that one? */
+    /* Return true */
+    return ret;
+}
+
+/* Dump from given socket into given file. Reads only filesize bytes,
+ * or until EOF if filesize == -1.
+ * Returns number of bytes written on success, or -1 on error */
+int recv_file( int sock, const char *filename, ssize_t filesize ) {
+    int fd, wrote;
+#if defined (__EMX__) || defined(__CYGWIN__)
+    /* We have to set O_BINARY, thus need open(). Otherwise it should be
+       equivalent to creat(). */
+    if( (fd = open( filename, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, 0644 )) < 0 ) {
+       return -1;
+    }
+#else
+    if( (fd = creat( filename, 0644 )) < 0 ) {
+       return -1;
+    }
+#endif
+    wrote = sock_transfer( sock, fd, filesize );
+    if( close( fd ) == -1 ) {
+       /* Close failed - file was not written correctly */
+       return -1;
+    }
+    if( filesize == -1 ) {
+       return wrote;
+    } else {
+       return (wrote==filesize);
+    }
+}
+
+/* Do a name lookup on given hostname, writes the address into
+ * given address buffer. Return -1 on failure.
+ */
+int host_lookup( const char *hostname, struct in_addr *addr ) {
+    struct hostent *hp;
+    unsigned long laddr;
+    
+    if( notify_cb )
+       (*notify_cb)( notify_ud, sock_namelookup, hostname );
+    
+    laddr = (unsigned long)inet_addr(hostname);
+    if ((int)laddr == -1) {
+       /* inet_addr failed. */
+       hp = gethostbyname(hostname);
+       if( hp == NULL ) {
+           return -1;
+       }
+       memcpy( addr, hp->h_addr, hp->h_length );
+    } else {
+       addr->s_addr = laddr;
+    }
+    return 0;
+}
+
+/* Opens a socket to the given port at the given address.
+ * Returns -1 on failure, or the socket on success. 
+ * portnum must be in HOST byte order */
+int sock_connect_u( const struct in_addr addr, int portnum, int call_fe ) {
+    struct sockaddr_in sa;
+    int sock;
+    /* Create the socket */
+    if( ( sock = socket(AF_INET, SOCK_STREAM, 0) ) < 0)
+       return -1;
+    /* Connect the socket */
+    sa.sin_family = AF_INET;
+    sa.sin_port = htons(portnum); /* host -> net byte orders */
+    sa.sin_addr = addr;
+    if( call_fe && notify_cb ) (*notify_cb)( notify_ud, sock_connecting, NULL );
+    if( connect(sock, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0 )
+       return -1;
+    if( call_fe && notify_cb ) (*notify_cb)( notify_ud, sock_connected, NULL );
+    /* Success - return the socket */
+    return sock;
+}
+
+int sock_connect( const struct in_addr addr, int portnum ) {
+    return sock_connect_u( addr, portnum, 1 );
+}
+
+/* Closes given socket */
+int sock_close( int sock ) {
+    return close( sock );
+}
+
+/* Returns HOST byte order port of given name */
+int get_tcp_port( const char *name ) {
+    struct servent *ent;
+    ent = getservbyname( name, "tcp" );
+    if( ent == NULL ) {
+       return 0;
+    } else {
+       return ntohs( ent->s_port );
+    }
+}
diff --git a/neon/src/socket.h b/neon/src/socket.h
new file mode 100644 (file)
index 0000000..26db328
--- /dev/null
@@ -0,0 +1,137 @@
+/* 
+   socket handling routines
+   Copyright (C) 1998, Joe Orton <joe@orton.demon.co.uk>, except where
+   otherwise indicated.
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef SOCKET_H
+#define SOCKET_H
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+
+#define SOCK_ERROR -1
+/* Read/Write timed out */
+#define SOCK_TIMEOUT -2
+/* Passed buffer was full */
+#define SOCK_FULL -3
+/* Socket was closed */
+#define SOCK_CLOSED -4
+
+/* Socket read timeout */
+#define SOCKET_READ_TIMEOUT 30
+
+typedef enum {
+    sock_namelookup, /* Looking up hostname given by info */
+    sock_connecting, /* Connecting to server */
+    sock_connected /* Connection established */
+} sock_status;
+
+typedef void (*sock_block_reader) ( 
+    void *userdata, const char *buf, size_t len );
+
+typedef void (*sock_progress)( void *userdata, size_t progress, size_t total );
+typedef void (*sock_notify)( void *userdata, sock_status status, const char *info );
+
+void sock_register_progress( sock_progress cb, void *userdata );
+void sock_register_notify( sock_notify cb, void *userdata );
+
+void sock_call_progress( size_t progress, size_t total );
+
+/* sock_read is read() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ *   SOCK_ERROR on error,
+ *   SOCK_TIMEOUT on timeout,
+ *    0 on no data to read (due to timeout or EOF),
+ *   >0 length of data read into buffer.
+ */
+int sock_read( int sock, void *buffer, size_t count );
+
+/* sock_recv is recv() with a timeout of SOCKET_TIMEOUT.
+ * Returns:
+ *   -1 on error,
+ *    0 on no data to read (due to timeout or EOF),
+ *   >0 length of data read into buffer.
+ */
+int sock_recv( int sock, void *buffer, size_t count, unsigned int flags);
+
+/* Blocks waiting for data on the given socket for the given time.
+ * Returns:
+ *  -1 on error,
+ *   0 on no data within timeout,
+ *   1 if data arrived on the socket.
+ */
+int sock_block( int sock, int timeout );
+
+/* Dump the given filename down the given socket.
+ * Returns 0 on success, non-zero on error */
+int send_file_binary( int sock, const char *filename ); 
+
+/* Dump the given filename down the given socket, performing
+ * CR -> CRLF conversion.
+ * Returns 0 on success, non-zero on error */
+int send_file_ascii( int sock, const char *filename ); 
+
+/* Dump from given socket into given file. Reads only filesize bytes,
+ * or until EOF if filesize == -1.
+ * Returns number of bytes written on success, or -1 on error */
+int recv_file( int sock, const char *filename, ssize_t filesize ); 
+
+/* Reads readlen bytes from srcfd and writes to destfd.
+ * (Not all in one go, obviously).
+ * If readlen == -1, then it reads from srcfd until EOF.
+ * Returns number of bytes written to destfd, or -1 on error.
+ * Calls fe_transfer_progress( a, b ) during transfers, where
+ *  a = bytes transferred so far, and b = readlen
+ */
+int sock_transfer( int srcfd, int destfd, ssize_t readlen );
+
+/* Sends the given line to given socket, CRLF appended */
+int sock_sendline( int sock, const char *line ); 
+/* Sends the given block of data down the socket */
+int sock_fullwrite( int sock, const char *data, size_t length ); 
+/* Sends the null-terminated string down the given socket */
+int sock_send_string( int sock, const char *string ); 
+
+/* Reads a line from given socket */
+int sock_readline( int sock, char *line, int len ); 
+/* Reads a chunk of data. */
+int sock_fullread( int sock, char *buffer, int buflen );
+
+/* Creates and connects a socket */
+int sock_connect( const struct in_addr host, int portnum );
+
+int sock_connect_u( const struct in_addr addr, int portnum, int call_fe );
+
+int sock_close( int socket );
+
+/* Do a name lookup on given hostname, writes the address into
+ * given address buffer. Return -1 on failure.
+ */
+int host_lookup( const char *hostname, struct in_addr *addr );
+
+/* Returns the standard TCP port for the given service */
+int get_tcp_port( const char *name );
+
+int sock_readfile_blocked( int fd, size_t length,
+                          sock_block_reader reader, void *userdata );
+
+#endif /* SOCKET_H */
diff --git a/neon/src/sslcerts.c b/neon/src/sslcerts.c
new file mode 100644 (file)
index 0000000..b0c972b
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+ * Originally under GPL in Mutt, http://www.mutt.org/
+ * Relicensed under LGPL for neon, http://www.webdav.org/neon/
+ * 
+ *     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., 59 Temple Place - Suite 330, Boston,
+ *     MA 02111-1307, USA
+ */
+
+/* for SSL NO_* defines */
+#include "config.h"
+
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#undef _
+
+#include <string.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x00904000L
+#define READ_X509_KEY(fp, key) PEM_read_X509(fp, key, NULL, NULL)
+#else
+#define READ_X509_KEY(fp, key) PEM_read_X509(fp, key, NULL)
+#endif
+
+/* This is ugly, but as RAND_status came in on OpenSSL version 0.9.5
+ * and the code has to support older versions too, this is seemed to
+ * be cleaner way compared to having even uglier #ifdefs all around.
+ */
+#ifdef HAVE_RAND_STATUS
+#define HAVE_ENTROPY() (RAND_status() == 1)
+#define GOT_ENTROPY()  return 0;
+#else
+static int needentropy = 1;
+/* OpenSSL fills the entropy pool from /dev/urandom if it exists */
+#define HAVE_ENTROPY() (!access("/dev/urandom", R_OK) || !needentropy)
+#define GOT_ENTROPY()  do { needentropy = 1; return 0; } while (0)
+#endif
+
+char *SslCertFile = NULL;
+char *SslEntropyFile = NULL;
+
+typedef struct _sslsockdata
+{
+  SSL_CTX *ctx;
+  SSL *ssl;
+  X509 *cert;
+}
+sslsockdata;
+
+/* 
+ * OpenSSL library needs to be fed with sufficient entropy. On systems
+ * with /dev/urandom, this is done transparently by the library itself,
+ * on other systems we need to fill the entropy pool ourselves.
+ *
+ * Even though only OpenSSL 0.9.5 and later will complain about the
+ * lack of entropy, we try to our best and fill the pool with older
+ * versions also. (That's the reason for the ugly #ifdefs and macros,
+ * otherwise I could have simply #ifdef'd the whole ssl_init funcion)
+ */
+int sock_ssl_init (nsocket_ssl *ctx)
+{
+  char path[_POSIX_PATH_MAX], *file;
+
+  if (HAVE_ENTROPY()) return 0;
+  
+  mutt_message (_("Filling entropy pool"));
+  
+  /* try egd */
+#ifdef HAVE_RAND_EGD
+  file = SslEntropyFile;
+  if (file && RAND_egd(file) != -1)
+    GOT_ENTROPY();
+  file = getenv("EGDSOCKET");
+  if (file && RAND_egd(file) != -1)
+    GOT_ENTROPY();
+  snprintf (path, sizeof(path), "%s/.entropy", NONULL(Homedir));
+  if (RAND_egd(path) != -1)
+    GOT_ENTROPY();
+  if (RAND_egd("/tmp/entropy") != -1)
+    GOT_ENTROPY();
+#endif
+
+  /* try some files */
+  file = SslEntropyFile;
+  if (!file || access(file, R_OK) == -1)
+    file = getenv("RANDFILE");
+  if (!file || access(file, R_OK) == -1) {
+    snprintf (path, sizeof(path), "%s/.rnd", NONULL(Homedir));
+    file = path;
+  }
+  if (access(file, R_OK) == 0) {
+    if (RAND_load_file(file, 10240) >= 16)
+      GOT_ENTROPY();
+  }
+
+  if (HAVE_ENTROPY()) return 0;
+
+  mutt_error (_("Failed to find enough entropy on your system"));
+  sleep (2);
+  return -1;
+}
+
+static int ssl_socket_open_err (CONNECTION *conn)
+{
+  mutt_error (_("SSL disabled due the lack of entropy"));
+  sleep (2);
+  return -1;
+}
+
+static int ssl_check_certificate (sslsockdata * data);
+
+int ssl_socket_open (CONNECTION * conn)
+{
+  sslsockdata *data;
+  int err;
+
+  if (raw_socket_open (conn) < 0)
+    return -1;
+
+  data = (sslsockdata *) safe_calloc (1, sizeof (sslsockdata));
+  conn->sockdata = data;
+
+  SSL_library_init();
+  data->ctx = SSL_CTX_new (SSLv23_client_method ());
+
+  /* disable SSL protocols as needed */
+  if (!option(OPTTLSV1)) 
+  {
+    SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1);
+  }
+  if (!option(OPTSSLV2)) 
+  {
+    SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv2);
+  }
+  if (!option(OPTSSLV3)) 
+  {
+    SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv3);
+  }
+
+  data->ssl = SSL_new (data->ctx);
+  SSL_set_fd (data->ssl, conn->fd);
+
+  if ((err = SSL_connect (data->ssl)) < 0)
+  {
+    ssl_socket_close (conn);
+    return -1;
+  }
+
+  data->cert = SSL_get_peer_certificate (data->ssl);
+  if (!data->cert)
+  {
+    mutt_error (_("Unable to get certificate from peer"));
+    sleep (1);
+    return -1;
+  }
+
+  if (!ssl_check_certificate (data))
+  {
+    ssl_socket_close (conn);
+    return -1;
+  }
+
+  mutt_message (_("SSL connection using %s"), SSL_get_cipher (data->ssl));
+  sleep (1);
+
+  return 0;
+}
+
+int ssl_socket_close (CONNECTION * conn)
+{
+  sslsockdata *data = conn->sockdata;
+  SSL_shutdown (data->ssl);
+
+  X509_free (data->cert);
+  SSL_free (data->ssl);
+  SSL_CTX_free (data->ctx);
+
+  return raw_socket_close (conn);
+}
+
+
+
+static char *x509_get_part (char *line, const char *ndx)
+{
+  static char ret[SHORT_STRING];
+  char *c, *c2;
+
+  strncpy (ret, _("Unknown"), sizeof (ret));
+
+  c = strstr (line, ndx);
+  if (c)
+  {
+    c += strlen (ndx);
+    c2 = strchr (c, '/');
+    if (c2)
+      *c2 = '\0';
+    strncpy (ret, c, sizeof (ret));
+    if (c2)
+      *c2 = '/';
+  }
+
+  return ret;
+}
+
+static void x509_fingerprint (char *s, int l, X509 * cert)
+{
+  unsigned char md[EVP_MAX_MD_SIZE];
+  unsigned int n;
+  int j;
+
+  if (!X509_digest (cert, EVP_md5 (), md, &n))
+  {
+    snprintf (s, l, _("[unable to calculate]"));
+  }
+  else
+  {
+    for (j = 0; j < (int) n; j++)
+    {
+      char ch[8];
+      snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
+      strncat (s, ch, l);
+    }
+  }
+}
+
+static char *asn1time_to_string (ASN1_UTCTIME *tm)
+{
+  static char buf[64];
+  BIO *bio;
+
+  strncpy (buf, _("[invalid date]"), sizeof (buf));
+  
+  bio = BIO_new (BIO_s_mem());
+  if (bio)
+  {
+    if (ASN1_TIME_print (bio, tm))
+      (void) BIO_read (bio, buf, sizeof (buf));
+    BIO_free (bio);
+  }
+
+  return buf;
+}
+
+static int check_certificate_by_signer (X509 *peercert)
+{
+  X509_STORE_CTX xsc;
+  X509_STORE *ctx;
+  int pass;
+
+  ctx = X509_STORE_new ();
+  if (ctx == NULL) return 0;
+
+  if (option (OPTSSLSYSTEMCERTS) && !X509_STORE_set_default_paths (ctx))
+  {
+    DEBUG (DEBUG_SOCKET, "X509_STORE_set_default_paths failed\n");
+    X509_STORE_free (ctx);
+    return 0;
+  }
+
+  if (!X509_STORE_load_locations (ctx, SslCertFile, NULL))
+  {
+    DEBUG (DEBUG_SOCKET, "X509_STORE_load_locations failed\n");
+    X509_STORE_free (ctx);
+    return 0;
+  }
+
+  X509_STORE_CTX_init (&xsc, ctx, peercert, NULL);
+
+  pass = (X509_verify_cert (&xsc) > 0);
+#ifdef DEBUG
+  if (! pass)
+  {
+    char buf[SHORT_STRING];
+    int err;
+
+    err = X509_STORE_CTX_get_error (&xsc);
+    snprintf (buf, sizeof (buf), "%s (%d)", 
+       X509_verify_cert_error_string(err), err);
+    DEBUG (DEBUG_SOCKET, "X509_verify_cert: %s\n", buf);
+  }
+#endif
+  X509_STORE_CTX_cleanup (&xsc);
+  X509_STORE_free (ctx);
+
+  return pass;
+}
+
+static int check_certificate_by_digest (X509 *peercert)
+{
+  unsigned char peermd[EVP_MAX_MD_SIZE];
+  unsigned int peermdlen;
+  X509 *cert = NULL;
+  int pass = 0;
+  FILE *fp;
+
+  /* expiration check */
+  if (X509_cmp_current_time (X509_get_notBefore (peercert)) >= 0)
+  {
+    DEBUG (DEBUG_SCOKET, "Server certificate is not yet valid\n");
+    mutt_error (_("Server certificate is not yet valid"));
+    sleep (2);
+    return 0;
+  }
+  if (X509_cmp_current_time (X509_get_notAfter (peercert)) <= 0)
+  {
+    DEBUG (DEBUG_SOCKET, "Server certificate has expired");
+    mutt_error (_("Server certificate has expired"));
+    sleep (2);
+    return 0;
+  }
+
+  if ((fp = fopen (SslCertFile, "rt")) == NULL)
+    return 0;
+
+  if (!X509_digest (peercert, EVP_sha1(), peermd, &peermdlen))
+  {
+    fclose (fp);
+    return 0;
+  }
+  
+  while ((cert = READ_X509_KEY (fp, &cert)) != NULL)
+  {
+    unsigned char md[EVP_MAX_MD_SIZE];
+    unsigned int mdlen;
+
+    /* Avoid CPU-intensive digest calculation if the certificates are
+     * not even remotely equal.
+     */
+    if (X509_subject_name_cmp (cert, peercert) != 0 ||
+       X509_issuer_name_cmp (cert, peercert) != 0)
+      continue;
+
+    if (!X509_digest (cert, EVP_sha1(), md, &mdlen) || peermdlen != mdlen)
+      continue;
+    
+    if (memcmp(peermd, md, mdlen) != 0)
+      continue;
+
+    pass = 1;
+    break;
+  }
+  X509_free (cert);
+  fclose (fp);
+
+  return pass;
+}
+
+static int ssl_check_certificate (sslsockdata * data)
+{
+  char *part[] =
+  {"/CN=", "/Email=", "/O=", "/OU=", "/L=", "/ST=", "/C="};
+  char helpstr[SHORT_STRING];
+  char buf[SHORT_STRING];
+  MUTTMENU *menu;
+  int done, row, i;
+  FILE *fp;
+  char *name = NULL, *c;
+
+  if (check_certificate_by_signer (data->cert))
+  {
+    dprint (1, (debugfile, "ssl_check_certificate: signer check passed\n"));
+    return 1;
+  }
+
+  /* automatic check from user's database */
+  if (SslCertFile && check_certificate_by_digest (data->cert))
+  {
+    dprint (1, (debugfile, "ssl_check_certificate: digest check passed\n"));
+    return 1;
+  }
+
+  /* interactive check from user */
+  menu = mutt_new_menu ();
+  menu->max = 19;
+  menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *));
+  for (i = 0; i < menu->max; i++)
+    menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char));
+
+  row = 0;
+  strncpy (menu->dialog[row++], _("This certificate belongs to:"), SHORT_STRING);
+  name = X509_NAME_oneline (X509_get_subject_name (data->cert),
+                           buf, sizeof (buf));
+  for (i = 0; i < 5; i++)
+  {
+    c = x509_get_part (name, part[i]);
+    snprintf (menu->dialog[row++], SHORT_STRING, "   %s", c);
+  }
+
+  row++;
+  strncpy (menu->dialog[row++], _("This certificate was issued by:"), SHORT_STRING);
+  name = X509_NAME_oneline (X509_get_issuer_name (data->cert),
+                           buf, sizeof (buf));
+  for (i = 0; i < 5; i++)
+  {
+    c = x509_get_part (name, part[i]);
+    snprintf (menu->dialog[row++], SHORT_STRING, "   %s", c);
+  }
+
+  row++;
+  snprintf (menu->dialog[row++], SHORT_STRING, _("This certificate is valid"));
+  snprintf (menu->dialog[row++], SHORT_STRING, _("   from %s"), 
+      asn1time_to_string (X509_get_notBefore (data->cert)));
+  snprintf (menu->dialog[row++], SHORT_STRING, _("     to %s"), 
+      asn1time_to_string (X509_get_notAfter (data->cert)));
+
+  row++;
+  buf[0] = '\0';
+  x509_fingerprint (buf, sizeof (buf), data->cert);
+  snprintf (menu->dialog[row++], SHORT_STRING, _("Fingerprint: %s"), buf);
+
+  menu->title = _("SSL Certificate check");
+  if (SslCertFile)
+  {
+    menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
+    menu->keys = _("roa");
+  }
+  else
+  {
+    menu->prompt = _("(r)eject, accept (o)nce");
+    menu->keys = _("ro");
+  }
+  
+  helpstr[0] = '\0';
+  mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
+  strncat (helpstr, buf, sizeof (helpstr));
+  mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
+  strncat (helpstr, buf, sizeof (helpstr));
+  menu->help = helpstr;
+
+  done = 0;
+  while (!done)
+  {
+    switch (mutt_menuLoop (menu))
+    {
+      case -1:                 /* abort */
+      case OP_MAX + 1:         /* reject */
+      case OP_EXIT:
+        done = 1;
+        break;
+      case OP_MAX + 3:         /* accept always */
+        done = 0;
+        if ((fp = fopen (SslCertFile, "a")))
+       {
+         if (PEM_write_X509 (fp, data->cert))
+           done = 1;
+         fclose (fp);
+       }
+       if (!done)
+         mutt_error (_("Warning: Couldn't save certificate"));
+       else
+         mutt_message (_("Certificate saved"));
+       sleep (1);
+        /* fall through */
+      case OP_MAX + 2:         /* accept once */
+        done = 2;
+        break;
+    }
+  }
+  mutt_menuDestroy (&menu);
+  return (done == 2);
+}
diff --git a/neon/src/string_utils.c b/neon/src/string_utils.c
new file mode 100644 (file)
index 0000000..6736a56
--- /dev/null
@@ -0,0 +1,447 @@
+/* 
+   String utility functions
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: string_utils.c,v 1.8 2000/05/09 18:32:07 joe Exp 
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "xalloc.h"
+
+#include "string_utils.h"
+
+struct sbuffer_s {
+    char *data;
+    size_t used; /* used bytes in buffer */
+    size_t length; /* length of buffer */
+};
+
+/* TODO: These are both crap. Rewrite to be like strsep(). */
+
+char **split_string( const char *str, const char separator,
+                    const char *quotes, const char *whitespace ) {
+    return split_string_c( str, separator, quotes, whitespace, NULL );
+}
+
+char **split_string_c( const char *str, const char separator,
+                      const char *quotes, const char *whitespace,
+                      int *give_count ) {
+    char **comps;
+    const char *pnt, *quot = NULL,
+       *start, *end; /* The start of the current component */
+    int count, /* The number of components */
+       iswhite, /* is it whitespace */
+       issep, /* is it the separator */
+       curr, /* current component index */
+       length, /* length of component */
+       leading_wspace; /* in leading whitespace still? */
+
+    /* Inefficient, but easier - first off, count the number of 
+     * components we have. */
+    count = 1;
+    for( pnt = str; *pnt!='\0'; pnt++ ) {
+       if( quotes != NULL ) {
+           quot = strchr( quotes, *pnt );
+       }
+       if( quot != NULL ) {
+           /* We found a quote, so skip till the next quote */
+           for( pnt++; (*pnt!=*quot) && (*pnt!='\0'); pnt++ )
+               /* nullop */;
+       } else if( *pnt == separator ) {
+           count++;
+       }
+    }
+
+    if( give_count ) {
+       /* Write the count */
+       *give_count = count;
+    }
+
+    /* Now, have got the number of components.
+     * Allocate the comps array. +1 for the NULL */
+    comps = xmalloc( sizeof(char *) * (count + 1) );
+
+    comps[count] = NULL;
+    
+    quot = end = start = NULL;
+    curr = 0;
+    leading_wspace = 1;
+
+    /* Now fill in the array */
+    for( pnt = str; *pnt != '\0'; pnt++ ) {
+       /* What is the current character - quote, whitespace, separator? */
+       if( quotes != NULL ) {
+           quot = strchr( quotes, *pnt );
+       }
+       iswhite = (whitespace!=NULL) && 
+           (strchr( whitespace, *pnt ) != NULL );
+       issep = (*pnt == separator);
+       /* What to do? */
+       if( leading_wspace ) {
+           if( quot!=NULL ) {
+               /* Quoted bit */
+               start = pnt;
+               length = 1;
+               leading_wspace = 0;
+           } else if( issep ) {
+               /* Zero-length component */
+               comps[curr++] = xstrdup("");
+           } else if( !iswhite ) {
+               start = end = pnt;
+               length = 1;
+               leading_wspace = 0;
+           }
+       } else {
+           if( quot!=NULL ) {
+               /* Quoted bit */
+               length++;
+           } else if( issep ) {
+               /* End of component - enter it into the array */
+               length = (end - start) + 1;
+               comps[curr] = xmalloc( length+1 );
+               memcpy( comps[curr], start, length );
+               comps[curr][length] = '\0';
+               curr++;
+               leading_wspace = 1;
+           } else if( !iswhite ) {
+               /* Not whitespace - update end marker */
+               end = pnt;
+           }
+       }
+       if( quot != NULL ) {
+           /* Skip to closing quote */
+           for( pnt++; *pnt!=*quot && *pnt != '\0'; ++pnt )
+               /* nullop */;
+           /* Last non-wspace char is closing quote */
+           end = pnt;
+       }
+    }
+    /* Handle final component */
+    if( leading_wspace ) {
+       comps[curr] = xstrdup( "" );
+    } else {
+       /* End of component - enter it into the array */
+       length = (end - start) + 1;
+       comps[curr] = xmalloc( length+1 );
+       memcpy( comps[curr], start, length );
+       comps[curr][length] = '\0';
+    }
+    return comps;
+}
+
+char **pair_string( const char *str, const char compsep, const char kvsep, 
+                const char *quotes, const char *whitespace ) 
+{
+    char **comps, **pairs, *split;
+    int count = 0, n, length;
+    comps = split_string_c( str, compsep, quotes, whitespace, &count );
+    /* Allocate space for 2* as many components as split_string returned,
+     * +2 for the NULLS. */
+    pairs = xmalloc( (2*count+2) * sizeof(char *) );
+    if( pairs == NULL ) {
+       return NULL;
+    }
+    for( n = 0; n < count; n++ ) {
+       /* Find the split */
+       split = strchr( comps[n], kvsep );
+       if( split == NULL ) {
+           /* No seperator found */
+           length = strlen(comps[n]);
+       } else {
+           length = split-comps[n];
+       }
+       /* Enter the key into the array */
+       pairs[2*n] = comps[n];
+       /* Null-terminate the key */
+       pairs[2*n][length] = '\0';
+       pairs[2*n+1] = split?(split + 1):NULL;
+    }
+    free( comps );
+    pairs[2*count] = pairs[2*count+1] = NULL;    
+    return pairs;
+}
+
+void split_string_free( char **components ) 
+{
+    char **pnt = components;
+    while( *pnt != NULL ) {
+       free( *pnt );
+       pnt++;
+    }
+    free( components );
+}
+
+void pair_string_free( char **pairs ) 
+{
+    int n;
+    for( n = 0; pairs[n] != NULL; n+=2 ) {
+       free( pairs[n] );
+    }
+    free( pairs );
+}
+
+char *shave_string( const char *str, const char ch ) {
+    size_t len = strlen( str );
+    char *ret;
+    if( str[len-1] == ch ) {
+       len--;
+    }
+    if( str[0] == ch ) {
+       len--;
+       str++;
+    }
+    ret = xmalloc( len + 1 );
+    memcpy( ret, str, len );
+    ret[len] = '\0';
+    return ret;
+}
+
+char *sbuffer_data( sbuffer buf ) {
+    return buf->data;
+}
+
+int sbuffer_size( sbuffer buf ) {
+    return buf->used;
+}
+
+void sbuffer_clear( sbuffer buf ) {
+    memset( buf->data, 0, buf->length );
+    buf->used = 0;
+}  
+
+/* Grows for given size, 0 on success, -1 on error.
+ * Could make this externally accessible, but, there hasn't been
+ * a need currently. */
+int sbuffer_grow( sbuffer buf, size_t newsize ) {
+    size_t newlen, oldbuflen;
+
+#define SBUFFER_GROWTH 512
+
+    if( newsize <= buf->length ) return 0; /* big enough already */
+    /* FIXME: ah, can't remember my maths... better way to do this? */
+    newlen = ( (newsize / SBUFFER_GROWTH) + 1) * SBUFFER_GROWTH;
+    
+    oldbuflen = buf->length;
+    /* Reallocate bigger buffer */
+    buf->data = realloc( buf->data, newlen );
+    if( buf->data == NULL ) return -1;
+    buf->length = newlen;
+    /* Zero-out the new bit of buffer */
+    memset( buf->data+oldbuflen, 0, newlen-oldbuflen );
+
+    return 0;
+}
+
+int sbuffer_concat( sbuffer buf, ... ) {
+    va_list ap;
+    char *next;
+    size_t totallen = 1; /* initialized to 1 for the terminating \0 */    
+
+    /* Find out how much space we need for all the args */
+    va_start( ap, buf );
+    do {
+       next = va_arg( ap, char * );
+       if( next != NULL ) {
+           totallen += strlen( next );
+       }
+    } while( next != NULL );
+    va_end( ap );
+    
+    /* Grow the buffer */
+    if( sbuffer_grow( buf, buf->used + totallen ) )
+       return -1;
+    
+    /* Now append the arguments to the buffer */
+    va_start( ap, buf );
+    do {
+       next = va_arg( ap, char * );
+       if( next != NULL ) {
+           strcat( buf->data, next );
+       }
+    } while( next != NULL );
+    va_end( ap );
+    
+    buf->used += totallen;
+    return 0;
+}
+
+/* Append zero-terminated string... returns 0 on success or -1 on
+ * realloc failure. */
+int sbuffer_zappend( sbuffer buf, const char *str ) {
+    size_t len = strlen( str );
+    if( sbuffer_grow( buf, buf->used + len + 1 ) ) {
+       return -1;
+    }
+    strcat( buf->data, str );
+    buf->used += len;
+    return 0;
+}
+
+int sbuffer_append( sbuffer buf, const char *data, size_t len ) {
+    if( sbuffer_grow( buf, buf->used + len ) ) {
+       return -1;
+    }
+    memcpy( buf->data+buf->used, data, len );
+    buf->used += len;
+    return 0;
+}
+
+sbuffer sbuffer_create( void ) {
+    return sbuffer_create_sized( 512 );
+}
+
+sbuffer sbuffer_create_sized( size_t s ) {
+    sbuffer buf = xmalloc( sizeof(struct sbuffer_s) );
+    buf->data = xmalloc( s );
+    memset( buf->data, 0, s );
+    buf->length = s;
+    buf->used = 0;
+    return buf;
+}
+
+void sbuffer_destroy( sbuffer buf ) {
+    if( buf->data ) {
+       free( buf->data );
+    }
+    free( buf );
+}
+
+char *sbuffer_finish( sbuffer buf )
+{
+    char *ret = buf->data;
+    free( buf );
+    return ret;
+}
+
+void sbuffer_altered( sbuffer buf )
+{
+    buf->used = strlen( buf->data );
+}
+
+/* Writes the ASCII representation of the MD5 digest into the
+ * given buffer, which must be at least 33 characters long. */
+void md5_to_ascii( const unsigned char md5_buf[16], char *buffer ) 
+{
+    int count;
+    for( count = 0; count<16; count++ ) {
+       buffer[count*2] = HEX2ASC( md5_buf[count] >> 4 );
+       buffer[count*2+1] = HEX2ASC( md5_buf[count] & 0x0f );
+    }
+    buffer[32] = '\0';
+}
+
+/* Reads the ASCII representation of an MD5 digest. The buffer must
+ * be at least 32 characters long. */
+void ascii_to_md5( const char *buffer, unsigned char md5_buf[16] ) 
+{
+    int count;
+    for( count = 0; count<16; count++ ) {
+       md5_buf[count] = ((ASC2HEX(buffer[count*2])) << 4) |
+           ASC2HEX(buffer[count*2+1]);
+    }
+}
+
+#ifdef SPLIT_STRING_TEST
+
+#include <stdio.h>
+
+int main( int argc, char *argv[] ) {
+    char *str, sep, **comps, *wspace, *quotes;
+    int count;
+    if( argc < 3 ) {
+       printf( "Usage: split_string <sep> <string> [whitespace] [quotes]\n" );
+       return -1;
+    }
+    sep = *argv[1];
+    str = argv[2];
+    if( argc > 3 ) {
+        wspace = argv[3];
+    } else {
+       wspace = " ";
+    }
+    if( argc > 4 ) {
+       quotes = argv[4];
+    } else {
+       quotes = "\"";
+    }
+    printf( "String: [%s]  Separator: `%c'  Whitespace: [%s]  Quotes: [%s]\n", str, sep, wspace, quotes );
+    comps = split_string( str, sep, quotes, wspace );
+    count = 0;
+    do {
+       printf( "Component #%d: [%s]\n", count, comps[count] );
+    } while( comps[++count] != NULL );
+    return 0;
+}
+
+#endif
+
+#ifdef PAIR_STRING_TEST
+
+#include <stdio.h>
+
+int main( int argc, char *argv[] ) {
+    char *str, compsep, kvsep, **comps, *wspace, *quotes;
+    int count;
+    if( argc < 4 ) {
+       printf( "Usage: pair_string <compsep> <kvsep> <string> [whitespace] [quotes]\n" );
+       return -1;
+    }
+    compsep = *argv[1];
+    kvsep = *argv[2];
+    str = argv[3];
+    if( argc > 4 ) {
+        wspace = argv[4];
+    } else {
+       wspace = " ";
+    }
+    if( argc > 5 ) {
+       quotes = argv[5];
+    } else {
+       quotes = "\"";
+    }
+    printf( "String: [%s]  CompSep: `%c' K/VSep: `%c'\nWhitespace: [%s]  Quotes: [%s]\n", str, compsep, kvsep, wspace, quotes );
+    comps = pair_string( str, compsep, kvsep, quotes, wspace );
+    count = 0;
+    do {
+       printf( "Component #%d: Key [%s] Value [%s]\n", count, 
+               comps[count], comps[count+1] );
+    } while( comps[(count+=2)] != NULL );
+    return 0;
+}
+
+#endif
+
+/* variables:
+ *
+ * Local variables:
+ *  compile-command: "gcc -g -O2 -Wall -I.. -ansi -DHAVE_CONFIG_H -DSPLIT_STRING_TEST -o split_string string_utils.c"
+ * End:
+ */
diff --git a/neon/src/string_utils.h b/neon/src/string_utils.h
new file mode 100644 (file)
index 0000000..c7302e6
--- /dev/null
@@ -0,0 +1,198 @@
+/* 
+   String utility functions
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef STRING_UTILS_H
+#define STRING_UTILS_H
+
+#include <config.h>
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h> /* for strcat */
+#endif
+
+#include "xalloc.h"
+
+#include <ctype.h> /* for tolower */
+
+#define ASC2HEX(x) (((x) <= '9') ? ((x) - '0') : (tolower((x)) + 10 - 'a'))
+#define HEX2ASC(x) ((x) > 9 ? ((x) - 10 + 'a') : ((x) + '0'))
+
+/* Splits str into component parts using given seperator,
+ * skipping given whitespace around separators and at the 
+ * beginning and end of str. Separators are ignored within
+ * any pair of characters specified as being quotes.
+ * Returns array of components followed by a NULL pointer. The
+ * components are taken from dynamically allocated memory, and
+ * the entire array can be easily freed using split_string_free.
+ *
+ * aka: What strtok should have been.
+ */
+char **split_string( const char *str, const char seperator,
+                const char *quotes, const char *whitespace );
+
+/* As above, but returns count of items as well */
+char **split_string_c( const char *str, const char seperator,
+                const char *quotes, const char *whitespace, int *count );
+
+
+/* A bit like split_string, except each component is split into a pair.
+ * Each pair is returned consecutively in the return array.
+ *  e.g.:
+ *     strpairs( "aa=bb,cc=dd", ',', '=', NULL, NULL )
+ *  =>   "aa", "bb", "cc", "dd", NULL, NULL
+ * Note, that if a component is *missing* it's key/value separator,
+ * then the 'value' in the array will be a NULL pointer. But, if the
+ * value is zero-length (i.e., the component separator follows directly
+ * after the key/value separator, or with only whitespace inbetween),
+ * then the value in the array will be a zero-length string.
+ *  e.g.:
+ *     pair_string( "aaaa,bb=cc,dd=,ee=ff", ',', '=', NULL, NULL )
+ *  =>   "aaaa", NULL, "bb", "cc", "dd", "", "ee", "ff"
+ * A NULL key indicates the end of the array (the value will also 
+ * be NULL, for convenience).
+ */
+char **pair_string( const char *str, const char compsep, const char kvsep, 
+                const char *quotes, const char *whitespace );
+
+/* Frees the array returned by split_string */
+void split_string_free( char **components );
+
+/* Frees the array returned by pair_string */
+void pair_string_free( char **pairs );
+
+/* Returns a string which is str with ch stripped from
+ * beggining and end, if present in either. The string returned
+ * is dynamically allocated using xmalloc().
+ *
+ * e.g.  shave_string( "abbba", 'a' )  => "bbb". */
+char *shave_string( const char *str, const char ch );
+
+#define EOL "\r\n"
+
+#define STRIP_EOL( str )                               \
+do {                                                   \
+   char *p;                                            \
+   if( (p = strrchr( str, '\r' )) != NULL ) *p = '\0'; \
+   if( (p = strrchr( str, '\n' )) != NULL ) *p = '\0'; \
+} while(0)
+
+/* String buffer handling.
+ * A string buffer sbuffer * which grows dynamically with the string. */
+
+struct sbuffer_s;
+typedef struct sbuffer_s *sbuffer;
+
+/* Returns contents of buffer at current point in time.
+ * NOTE: if the buffer is modified with _concat, _append etc,
+ * this value may no longer be correct. */
+char *sbuffer_data( sbuffer buf );
+
+/* Returns size of data in buffer, equiv to strlen(sbuffer_data(buf)) */
+int sbuffer_size( sbuffer buf );
+
+/* Concatenate all given strings onto the end of the buffer.
+ * The strings must be null-terminated, and MUST be followed by a
+ * NULL argument marking the end of the list.
+ * Returns:
+ *   0 on success
+ *   non-zero on error
+ */
+int sbuffer_concat( sbuffer buf, ... );
+
+/* Create a new sbuffer. Returns NULL on error */
+sbuffer sbuffer_create( void );
+
+/* Create a new sbuffer of given minimum size. Returns NULL on error */
+sbuffer sbuffer_create_sized( size_t size );
+
+/* Destroys (deallocates) a buffer */
+void sbuffer_destroy( sbuffer buf );
+
+/* Append a zero-terminated string 'str' to buf.
+ * Returns 0 on success, non-zero on error. */
+int sbuffer_zappend( sbuffer buf, const char *str );
+
+/* Append 'len' bytes of 'data' to buf.
+ * Returns 0 on success, non-zero on error. */
+int sbuffer_append( sbuffer buf, const char *data, size_t len );
+
+/* Empties the contents of buf; makes the buffer zero-length. */
+void sbuffer_clear( sbuffer buf );
+
+/* Grows the sbuffer to a minimum size.
+ * Returns 0 on success, non-zero on error */
+int sbuffer_grow( sbuffer buf, size_t size );
+
+void sbuffer_altered( sbuffer buf );
+
+/* MD5 ascii->binary conversion */
+void md5_to_ascii( const unsigned char md5_buf[16], char *buffer );
+void ascii_to_md5( const char *buffer, unsigned char md5_buf[16] );
+
+/* Destroys a buffer, returning the buffer contents. */
+char *sbuffer_finish( sbuffer buf );
+
+/* Handy macro to free things. */
+#define SAFE_FREE(x) \
+do { if( (x)!=NULL ) free( (x) ); (x) = NULL; } while(0)
+
+/* TODO: do these with stpcpy instead... more efficient, but means 
+ * bloat on non-GNU platforms. */
+
+/* TODO: could replace with glib equiv's where available, too */
+
+#define CONCAT2( out, str1, str2 )                     \
+do {                                                   \
+    out = xmalloc( strlen(str1) + strlen(str2) + 1 );  \
+    if( out != NULL ) {                                        \
+       strcpy( out, str1 );                            \
+       strcat( out, str2 );                            \
+    }                                                  \
+} while(0)
+
+#define CONCAT3( out, str1, str2, str3 )                                \
+do {                                                                    \
+    out = xmalloc( strlen(str1) + strlen(str2) + strlen(str3) + 1 );    \
+    if( out != NULL ) {                                                         \
+       strcpy( out, str1 );                                             \
+       strcat( out, str2 );                                             \
+       strcat( out, str3 );                                             \
+    }                                                                   \
+} while(0)
+
+#define CONCAT4( out, str1, str2, str3, str4 )         \
+do {                                                   \
+    out = xmalloc( strlen(str1) + strlen(str2)         \
+                 + strlen(str3) + strlen(str4) + 1 );  \
+    if( out != NULL ) {                                        \
+       strcpy( out, str1 );                            \
+       strcat( out, str2 );                            \
+       strcat( out, str3 );                            \
+       strcat( out, str4 );                            \
+    }                                                  \
+} while(0)
+
+#endif STRING_UTILS_H
+
diff --git a/neon/src/uri.c b/neon/src/uri.c
new file mode 100644 (file)
index 0000000..622044f
--- /dev/null
@@ -0,0 +1,303 @@
+/* 
+   HTTP URI handling
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: uri.c,v 1.7 2000/05/10 16:47:06 joe Exp 
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include "http_utils.h" /* for 'min' */
+#include "string_utils.h" /* for CONCAT3 */
+#include "uri.h"
+
+char *uri_parent( const char *uri ) {
+    const char *pnt;
+    char *ret;
+    pnt = uri+strlen(uri)-1;
+    while( *(--pnt) != '/' && pnt >= uri ) /* noop */;
+    if( pnt < uri ) {
+       /* not a valid absPath */
+       return NULL;
+    }
+    /*  uri    
+     *   V
+     *   |---|
+     *   /foo/bar/
+     */
+    ret = xmalloc( (pnt - uri) + 2 );
+    memcpy( ret, uri, (pnt - uri) + 1 );
+    ret[1+(pnt-uri)] = '\0';
+    pnt++;
+    return ret;
+}
+
+int uri_has_trailing_slash( const char *uri ) 
+{
+     return (uri[strlen(uri)-1] == '/');
+}
+
+const char *uri_abspath( const char *uri ) {
+    const char *ret;
+    /* Look for the scheme: */
+    ret = strstr( uri, "://" );
+    if( ret == NULL ) {
+       /* No scheme */
+       ret = uri;
+    } else {
+       /* Look for the abs_path */
+       ret = strchr( ret+3, '/' );
+       if( ret == NULL ) {
+           /* Uh-oh */
+           ret = uri;
+       }
+    }
+    return ret;
+}
+
+/* TODO: not a proper URI parser */
+int uri_parse( const char *uri, struct uri *parsed, 
+              const struct uri *defaults )
+{
+    const char *pnt, *slash, *colon;
+
+    parsed->port = -1;
+    parsed->host = NULL;
+    parsed->path = NULL;
+    parsed->scheme = NULL;
+
+    pnt = strstr( uri, "://" );
+    if( pnt ) {
+       parsed->scheme = xstrndup( uri, pnt - uri );
+       pnt += 3; /* start of hostport segment */
+       slash = strchr( pnt, '/' );
+       colon = strchr( pnt, ':' );
+       if( slash == NULL ) {
+           parsed->path = xstrdup( "/" );
+           if( colon == NULL ) {
+               if( defaults ) parsed->port = defaults->port;
+               parsed->host = xstrdup( pnt );
+           } else {
+               parsed->port = atoi(colon+1);
+               parsed->host = xstrndup( pnt, colon - pnt );
+           }
+       } else {
+           if( colon == NULL || colon > slash ) {
+               /* No port segment */
+               if( defaults ) parsed->port = defaults->port;
+               parsed->host = xstrndup( pnt, slash - pnt );
+           } else {
+               /* Port segment */
+               parsed->port = atoi( colon + 1 );
+               parsed->host = xstrndup( pnt, colon - pnt );
+           }
+           parsed->path = xstrdup( slash );
+       }
+    } else {
+       if( defaults && defaults->scheme != NULL ) {
+           parsed->scheme = xstrdup( defaults->scheme );
+       }
+       if( defaults && defaults->host != NULL ) {
+           parsed->host = xstrdup( defaults->host );
+       }
+       if( defaults ) parsed->port = defaults->port;
+       parsed->path = xstrdup(uri);
+    }
+    return 0;
+}
+
+void uri_free( struct uri *uri )
+{
+    HTTP_FREE( uri->host );
+    HTTP_FREE( uri->path );
+    HTTP_FREE( uri->scheme );
+}
+
+/* Returns an absoluteURI */
+char *uri_absolute( const char *uri, const char *scheme, 
+                   const char *hostport ) {
+    char *ret;
+    /* Is it absolute already? */
+    if( strncmp( uri, scheme, strlen( scheme ) ) == 0 )  {
+       /* Yes it is */
+       ret = xstrdup( uri );
+    } else {
+       /* Oh no it isn't */
+       CONCAT3( ret, scheme, hostport, uri );
+    }
+    return ret;
+}
+
+/* Un-escapes a URI. Returns xmalloc-allocated URI */
+char *uri_unescape( const char *uri ) {
+    const char *pnt;
+    char *ret, *retpos, buf[5] = { "0x00\0" };
+    retpos = ret = xmalloc( strlen( uri ) + 1 );
+    for( pnt = uri; *pnt != '\0'; pnt++ ) {
+       if( *pnt == '%' ) {
+           if( !isxdigit((unsigned char) pnt[1]) || 
+               !isxdigit((unsigned char) pnt[2]) ) {
+               /* Invalid URI */
+               return NULL;
+           }
+           buf[2] = *++pnt; buf[3] = *++pnt; /* bit faster than memcpy */
+           *retpos++ = strtol( buf, NULL, 16 );
+       } else {
+           *retpos++ = *pnt;
+       }
+    }
+    *retpos = '\0';
+    return ret;
+}
+
+/* RFC2396 spake:
+ * "Data must be escaped if it does not have a representation 
+ * using an unreserved character".
+ * ...where...
+ *  unreserved  = alphanum | mark
+ *  mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+ * 
+ * We need also to skip reserved characters
+ * reserved    = ";" | "/" | "?" | ":" | "@" | "&" |
+ *               "=" | "+" | "$" | ","
+ */
+
+/* Lookup table:
+ * 1 marks an RESERVED character. 2 marks a UNRESERVED character.
+ * 0 marks everything else. 
+ */
+
+#define RE 1
+#define UN 2 
+static const short uri_chars[128] = {
+/* 0 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 16 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 32 */  0, UN, 0, 0, RE, 0, RE, UN, UN, UN, UN, RE, RE, UN, UN, RE,
+/* 48 */ UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, RE, RE, 0, RE, 0, RE,
+/* 64 */ RE, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN,
+/* 80 */ UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, 0, 0, 0, 0, UN,
+/* 96 */ 0, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN,
+/* 112 */ UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, 0, 0, 0, UN, 0 
+};
+#undef RE
+#undef UN
+
+/* Escapes the abspath segment of a URI.
+ * Returns xmalloc-allocated string.
+ */
+char *uri_abspath_escape( const char *abs_path ) {
+    const char *pnt;
+    char *ret, *retpos;
+    /* Rather than mess about growing the buffer, allocate as much as
+     * the URI could possibly need, i.e. every character gets %XX
+     * escaped. Hence 3 times input size. 
+     */
+    retpos = ret = xmalloc( strlen( abs_path ) * 3 + 1 );
+    for( pnt = abs_path; *pnt != '\0'; pnt++ ) {
+       /* Escape it:
+        *  - if it isn't 7-bit
+        *  - if it is a reserved character (but ignore '/')
+        *  - otherwise, if it is not an unreserved character
+        * (note, there are many characters that are neither reserved
+        * nor unreserved)
+        */
+       if( *pnt<0 || (uri_chars[(int) *pnt] < 2 && *pnt!='/' )) {
+           /* Escape it - %<hex><hex> */
+           /* FIXME: Could overflow buffer with uri_abspath_escape("%") */
+           sprintf( retpos, "%%%02x", (unsigned char) *pnt );
+           retpos += 3;
+       } else {
+           /* It's cool */
+           *retpos++ = *pnt;
+       }
+    }
+    *retpos = '\0';
+    return ret;
+}
+
+/* TODO: implement properly */
+int uri_compare( const char *a, const char *b ) {
+    int ret = strcasecmp( a, b );
+    if( ret ) {
+       /* TODO: joe: I'm not 100% sure this is logically sound.
+        * It feels right, though */
+       int traila = uri_has_trailing_slash( a ),
+           trailb = uri_has_trailing_slash( b ),
+           lena = strlen(a), lenb = strlen(b);
+       if( traila != trailb && abs(lena - lenb) == 1) {
+           /* They are the same length, apart from one has a trailing
+            * slash and the other doesn't. */
+           if( strncasecmp( a, b, min(lena, lenb) ) == 0 )
+               ret = 0;
+       }
+    }
+    return ret;
+}
+
+/* Hrm, well, this is kind of not very generic over URI schemes, but wth */
+int uri_childof( const char *parent, const char *child ) 
+{
+    char *root = xstrdup(child);
+    int ret;
+    if( strlen(parent) >= strlen(child) ) {
+       ret = 0;
+    } else {
+       /* root is the first of child, equal to length of parent */
+       root[strlen(parent)] = '\0';
+       ret = (uri_compare( parent, root ) == 0 );
+    }
+    free( root );
+    return ret;
+}
+
+#ifdef URITEST
+
+int main( int argc, char *argv[] ) {
+    char *tmp;
+    if( argc<2 || argc>3 ) {
+       printf( "doh. usage:\nuritest uria [urib]\n"
+               "e.g. uritest \"/this/is/a silly<filename>/but/hey\"\n" );
+       exit(-1);
+    }
+    if( argv[2] ) {
+       printf( "uri_compare: %s with %s: %s\n",
+               argv[1], argv[2],
+               uri_compare(argv[1], argv[2])==0?"true":"false" );
+    } else {
+       printf( "Input URI: %s\n", argv[1] );
+       tmp = uri_abspath_escape( argv[1] );
+       printf( "Encoded: %s\n", tmp );
+       printf( "Decoded: %s\n", uri_unescape( tmp ) );
+    }
+    return 0;
+}
+
+#endif /* URITEST */
diff --git a/neon/src/uri.h b/neon/src/uri.h
new file mode 100644 (file)
index 0000000..01d7dd9
--- /dev/null
@@ -0,0 +1,63 @@
+/* 
+   HTTP URI handling
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef URI_H
+#define URI_H
+
+/* Un-escapes a URI. Returns malloc-allocated URI on success,
+ * or NULL on failure (malloc failure or invalid %<HEX><HEX> sequence). */
+char *uri_unescape( const char *uri );
+
+/* Escapes the abspath segment of a URI.
+ * Returns malloc-allocated string on success, or NULL on malloc failure.
+ */
+char *uri_abspath_escape( const char *abs_path );
+
+const char *uri_abspath( const char *uri );
+
+char *uri_parent( const char *uri );
+
+int uri_compare( const char *a, const char *b );
+
+/* Returns an absolute URI from a possibly-relative 'uri', using
+ * given scheme + hostport segment.
+ * Returns malloc-allocated string on success, or NULL on malloc failure. */
+char *uri_absolute( const char *uri, const char *scheme, 
+                   const char *hostport );
+
+/* Returns non-zero if child is a child of parent */
+int uri_childof( const char *parent, const char *child );
+
+int uri_has_trailing_slash( const char *uri );
+
+struct uri {
+    char *scheme;
+    char *host;
+    int port;
+    char *path;
+};
+
+int uri_parse( const char *uri, struct uri *parsed, 
+              const struct uri *defaults );
+
+void uri_free( struct uri *parsed );
+
+#endif URI_H
diff --git a/neon/src/xalloc.c b/neon/src/xalloc.c
new file mode 100644 (file)
index 0000000..95e3081
--- /dev/null
@@ -0,0 +1,56 @@
+/* 
+   Replacement memory allocation handling etc.
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+   Id: xalloc.c,v 1.1 2000/05/09 22:29:56 joe Exp 
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "xalloc.h"
+
+void *xmalloc( size_t len ) 
+{
+    void *ptr = malloc(len);
+    if( !ptr ) {
+       /* uh-oh */
+       abort();
+    }
+    return ptr;
+}
+
+char *xstrdup( const char *s ) 
+{
+    return strcpy( xmalloc( strlen(s) + 1 ), s );
+}
+
+char *xstrndup( const char *s, size_t n )
+{
+    char *new = xmalloc( n + 1 );
+    new[n] = '\0';
+    memcpy( new, s, n );
+    return new;
+}
diff --git a/neon/src/xalloc.h b/neon/src/xalloc.h
new file mode 100644 (file)
index 0000000..51b21cb
--- /dev/null
@@ -0,0 +1,33 @@
+/* 
+   Replacement memory allocation handling etc.
+   Copyright (C) 1999-2000, Joe Orton <joe@orton.demon.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef XALLOC_H
+#define XALLOC_H
+
+#include <sys/types.h>
+
+void *xmalloc( size_t len );
+
+char *xstrdup( const char *s );
+
+char *xstrndup( const char *s, size_t n );
+
+#endif /* XALLOC_H */
diff --git a/neon/test/.cvsignore b/neon/test/.cvsignore
new file mode 100644 (file)
index 0000000..9cb7ba7
--- /dev/null
@@ -0,0 +1,3 @@
+tests
+*-tests
+Makefile
diff --git a/neon/test/COPYING b/neon/test/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 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.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, 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 software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, 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 redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) 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; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, 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 executable.  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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+  5. 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 Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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.
+
+  7. 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 Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  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 program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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 version 2 of the License, 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 this program; 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.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/neon/test/ChangeLog b/neon/test/ChangeLog
new file mode 100644 (file)
index 0000000..0ab69b8
--- /dev/null
@@ -0,0 +1,51 @@
+Sun Apr 29 14:57:59 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri-tests.c (slash): Check behaviour of passing zero-length URI.
+
+Sun Apr 29 13:43:59 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * Makefile.in (clean): New target.  (libtest.a): Depend on libneon
+       to force rebuilds when necessary.  (all): Build but don't test.
+
+Sun Apr 29 13:41:13 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c: Add status line with leading garbage.
+
+Sun Apr 29 13:39:53 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (status_lines): Add some tests for invalid status
+       lines too.
+
+Sun Apr 29 13:38:31 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (main): Use basename(argv[0]) as suite name.  Fail if no
+       tests are in the functions vector.
+
+Sun Apr 29 11:06:45 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (segv): New function.  (main): Add SIGSEGV handler.
+
+Fri Apr 27 00:00:12 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (base64): New test.
+
+Thu Apr 26 22:39:44 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * uri-tests.c (just_hostname, just_path, null_uri): New tests.
+
+Thu Apr 26 22:03:58 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * util-tests.c (md5): Test of MD5 functions.
+
+Mon Apr 23 23:08:02 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http-tests.c (simple_head): Add HEAD test.
+
+Mon Apr 23 22:49:52 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * http-tests.c (simple_get): Check for EOF after reading response
+       body of HTTP/1.0 GET request.
+
+       (null_resource): New function, test for 404 on null resource.
+
+
diff --git a/neon/test/Makefile.in b/neon/test/Makefile.in
new file mode 100644 (file)
index 0000000..7a51457
--- /dev/null
@@ -0,0 +1,55 @@
+
+SHELL = @SHELL@
+CFLAGS = @CFLAGS@ -I. -I../src
+LDFLAGS = -L. @LDFLAGS@ -L../src -L../src/.libs
+DEFS = @DEFS@
+top_builddir = ..
+top_srcdir = @top_srcdir@
+
+VPATH = @srcdir@
+
+AR = ar
+
+RANLIB = @RANLIB@
+LIBS = @LIBS@ @NEON_LIBS@
+CC = @CC@
+
+TESTS = uri-tests util-tests string-tests sock-tests http-tests
+
+LIBTEST = libtest.a
+
+all: $(TESTS)
+
+clean:
+       rm -f $(TESTS) *.o
+
+test: $(TESTS)
+       $(SHELL) run.sh $(TESTS)
+
+Makefile: Makefile.in
+       (cd .. && CONFIG_FILES=test/Makefile ./config.status)
+
+libtest.a: tests.o tests.h ../src/@NEON_TARGET@
+       $(AR) cru $@ tests.o
+       $(RANLIB) $@
+
+uri-tests: uri-tests.o $(LIBTEST)
+       $(CC) $(LDFLAGS) -o $@ uri-tests.o -ltest $(LIBS)
+
+util-tests: util-tests.o $(LIBTEST)
+       $(CC) $(LDFLAGS) -o $@ util-tests.o -ltest $(LIBS)
+
+string-tests: string-tests.o $(LIBTEST)
+       $(CC) $(LDFLAGS) -o $@ string-tests.o -ltest $(LIBS)
+
+sock-tests: sock-tests.o $(LIBTEST)
+       $(CC) $(LDFLAGS) -o $@ sock-tests.o -ltest $(LIBS)
+
+http-tests: http-tests.o $(LIBTEST)
+       $(CC) $(LDFLAGS) -o $@ http-tests.o -ltest $(LIBS)
+
+uri-tests.o: uri-tests.c tests.h
+util-tests.o: util-tests.c tests.h
+string-tests: string-tests.c tests.h
+sock-tests.o: sock-tests.c tests.h
+http-tests.o: http-tests.c tests.h
diff --git a/neon/test/README b/neon/test/README
new file mode 100644 (file)
index 0000000..02b632c
--- /dev/null
@@ -0,0 +1,14 @@
+
+Stupidly Simple Test Suite for neon
+-----------------------------------
+
+http-tests requires that you have a running HTTP server on localhost
+port 80, and you have copied htdocs/* to server-htdocs-root/test/*
+
+The test code is licensed under the GPL.
+
+Credits
+-------
+
+This test suite is inspired by the Subversion project and discussion
+on the subversion mailing list.
diff --git a/neon/test/STATUS b/neon/test/STATUS
new file mode 100644 (file)
index 0000000..efb5c34
--- /dev/null
@@ -0,0 +1,79 @@
+                                                        -*- text -*-
+
+This document attempts to list RFC requirements and determine whether
+neon meets them, or where they do not apply, etc.
+
+ Yes: test written, succeeds
+ No:  test written, but currently fails
+ ???: no test written
+ ---: feature not supported
+ App: this is an application issue not a neon issue
+
+  RFC2616
+  =======
+
+3.1: MUST treat major/minor as separate digits                 Yes
+3.1: MUST ignore leading zeros                                 Yes
+3.1: MUST only send HTTP/1.1 when appropriate                  ???
+
+3.2.2: MUST use abs_path of "/" in Request-URI                 App
+3.2.3: comparisons of host names MUST be case-insensitive      --- [1]
+       comparisons of scheme names MUST be ...                 ---
+       comparison of empty abs_path equivalent to "/"          No/---
+
+3.3.1: MUST accept three date formats                          App/Yes [2]
+       MUST only generate RFC1123-style dates                  App
+
+3.3.1: MUST use GMT for http-dates                             ???
+       MUST assume GMT when parsing asctime dates              ???
+
+3.4*: character set handling                                   App/??? [3]
+
+3.5*: content codings                                          App
+
+3.6: MUST requirements for multiple transfer-codings           --- [4]
+
+3.6.1: parsing of chunked transfer coding                      ???
+ TODO: translate that section into requirements
+
+3.6.1: MUST be able to handle "chunked" transfer-coding                Yes
+       MUST ignore unknown chunk-extension extensions          ???
+
+3.7: parsing of Content-Type headers                           ???
+ TODO: translate section into requirements
+3.7: MUST NOT have LWS between type/subtype in C-T hdr         App
+     SHOULD only send parameters to "new HTTP apps" (>1.0?)     App
+
+3.7.1: MUST represent HTTP message in canonical form           App
+       MUST accept CRLF/CR/LF as line-breaks in text/* media   App
+       MUST NOT use only CR or LF in HTTP control structures   ???
+       MUST specify charset if not ISO-8859-1                  App
+
+3.7.2: multipart types                                         ---
+
+3.8: SHOULD have short product token                           Yes/App [5]
+     SHOULD use product-version for version identifier         Yes/App
+     only product-version differs between versions             Yes/App
+
+3.9: Content Negotiation                                       ---/App
+
+3.10: Language Tags                                            ---/App
+
+3.11: Entity Tags                                              ---/App
+
+
+
+
+
+[1]: these are only applicable if neon had a full URI comparison 
+suite.
+
+[2]: date parser is provided which handles all three formats, but no 
+handling of the Date header is present within neon.
+
+[3]: not sure if neon should be handling of this internally.
+
+[4]: neon only supports using just chunked Transfer-Coding or none.
+
+[5]: these reflect that applications may add their own product tokens
+     alongside neon's.
diff --git a/neon/test/acl.c b/neon/test/acl.c
new file mode 100644 (file)
index 0000000..1b1efa7
--- /dev/null
@@ -0,0 +1,101 @@
+/* 
+   Dummy ACL tests
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "ne_acl.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+/**** DUMMY TESTS: just makes sure the stuff doesn't dump core. */
+
+static int test_acl(const char *uri, ne_acl_entry *es, int nume)
+{
+    ne_session *sess = ne_session_create();
+
+    ne_session_server(sess, "localhost", 7777);
+    ON(spawn_server(7777, single_serve_string, 
+                   "HTTP/1.1 200 OK\r\n"
+                   "Connection: close\r\n\r\n"));
+    
+    ON(ne_acl_set(sess, uri, es, nume));
+    
+    CALL(await_server());
+    
+    return OK;
+}
+
+static int grant_all(void)
+{
+    ne_acl_entry e;
+
+    e.apply = ne_acl_all;
+    e.type = ne_acl_grant;
+
+    CALL(test_acl("/foo", &e, 1));
+
+    return OK;
+}
+
+static int deny_all(void)
+{
+    ne_acl_entry e;
+
+    e.apply = ne_acl_all;
+    e.type = ne_acl_deny;
+    
+    CALL(test_acl("/foo", &e, 1));
+
+    return OK;
+}
+
+static int deny_one(void)
+{
+    ne_acl_entry e;
+
+    e.apply = ne_acl_href;
+    e.type = ne_acl_deny;
+    e.principal = "http://webdav.org/users/joe";
+
+    CALL(test_acl("/foo", &e, 1));
+
+    return OK;
+}       
+
+static int deny_byprop(void)
+{
+    ne_acl_entry e;
+
+    e.apply = ne_acl_property;
+    e.type = ne_acl_deny;
+    e.principal = "owner";
+
+    CALL(test_acl("/foo", &e, 1));
+
+    return OK;
+}
+
+ne_test tests[] = {
+    T(grant_all),
+    T(deny_all),
+    T(deny_one),
+    T(deny_byprop),
+    T(NULL)
+};
diff --git a/neon/test/auth.c b/neon/test/auth.c
new file mode 100644 (file)
index 0000000..729f55b
--- /dev/null
@@ -0,0 +1,258 @@
+/* 
+   Authentication tests
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_auth.h"
+#include "ne_basic.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+char username[BUFSIZ], password[BUFSIZ];
+static int auth_failed;
+
+static const char www_wally[] = "WWW-Authenticate: Basic realm=WallyWorld";
+
+static int auth_cb(void *userdata, const char *realm, int tries, 
+                  char *un, char *pw)
+{
+    strcpy(un, username);
+    strcpy(pw, password);
+    return tries;
+}                 
+
+static void auth_hdr(char *value)
+{
+#define B "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
+    NE_DEBUG(NE_DBG_HTTP, "Got auth header: [%s]\n", value);
+    NE_DEBUG(NE_DBG_HTTP, "Wanted header:   [%s]\n", B);
+    auth_failed = strcmp(value, B);
+#undef B
+}
+
+/* Sends a response with given response-code. If hdr is not NULL,
+ * sends that header string too (appending an EOL).  If eoc is
+ * non-zero, request must be last sent down a connection; otherwise,
+ * clength 0 is sent to maintain a persistent connection. */
+static int send_response(nsocket *sock, const char *hdr, int code, int eoc)
+{
+    char buffer[BUFSIZ];
+    
+    sprintf(buffer, "HTTP/1.1 %d Blah Blah" EOL, code);
+    
+    if (hdr) {
+       strcat(buffer, hdr);
+       strcat(buffer, EOL);
+    }
+
+    if (eoc) {
+       strcat(buffer, "Connection: close" EOL EOL);
+    } else {
+       strcat(buffer, "Content-Length: 0" EOL EOL);
+    }
+       
+    return sock_send_string(sock, buffer);
+}
+
+static int auth_serve(nsocket *sock, void *userdata)
+{
+    auth_failed = 0;
+
+    /* Register globals for discard_request. */
+    got_header = auth_hdr;
+    want_header = "Authorization";
+
+    discard_request(sock);
+    send_response(sock, www_wally, 401, 0);
+
+    discard_request(sock);
+    send_response(sock, NULL, auth_failed?500:200, 1);
+
+    return 0;
+}
+
+static int basic(void)
+{
+    int ret;
+    ne_session *sess = ne_session_create();
+
+    ne_session_server(sess, "localhost", 7777);
+
+    ne_set_server_auth(sess, auth_cb, NULL);
+
+    strcpy(username, "Aladdin");
+    strcpy(password, "open sesame");
+
+    CALL(spawn_server(7777, auth_serve, NULL));
+    
+    ret = any_request(sess, "/norman");
+
+    ne_session_destroy(sess);
+
+    CALL(await_server());
+
+    ONREQ(ret);
+    return OK;
+}
+
+static int retry_serve(nsocket *sock, void *ud)
+{
+    discard_request(sock);
+    send_response(sock, www_wally, 401, 0);
+
+    discard_request(sock);
+    send_response(sock, www_wally, 401, 0);
+
+    discard_request(sock);
+    send_response(sock, NULL, 200, 0);
+    
+    discard_request(sock);
+    send_response(sock, www_wally, 401, 0);
+
+    discard_request(sock);
+    send_response(sock, NULL, 200, 0);
+
+    discard_request(sock);
+    send_response(sock, NULL, 200, 0);
+
+    discard_request(sock);
+    send_response(sock, NULL, 200, 0);
+
+    discard_request(sock);
+    send_response(sock, www_wally, 401, 0);
+
+    discard_request(sock);
+    send_response(sock, NULL, 200, 0);
+    
+    return OK;
+}
+
+static int retry_cb(void *userdata, const char *realm, int tries, 
+                   char *un, char *pw)
+{
+    int *count = userdata;
+
+    /* dummy creds; server ignores them anyway. */
+    strcpy(un, "a");
+    strcpy(pw, "b");
+
+    switch (*count) {
+    case 0:
+    case 1:
+       if (tries == *count) {
+           *count += 1;
+           return 0;
+       } else {
+           t_context("On request #%d, got attempt #%d", *count, tries);
+           *count = -1;
+           return 1;
+       }
+       break;
+    case 2:
+    case 3:
+       /* server fails a subsequent request, check that tries has
+        * reset to zero. */
+       if (tries == 0) {
+           *count += 1;
+           return 0;
+       } else {
+           t_context("On retry after failure #%d, tries was %d", 
+                     *count, tries);
+           *count = -1;
+           return 1;
+       }
+       break;
+    default:
+       t_context("Count reached %d!?", *count);
+       *count = -1;
+    }
+    return 1;
+}
+
+/* Test that auth retries are working correctly. */
+static int retries(void)
+{
+    ne_session *sess = ne_session_create();
+    int count = 0;
+    
+    ne_session_server(sess, "localhost", 7777);
+    ne_set_server_auth(sess, retry_cb, &count);
+
+    CALL(spawn_server(7777, retry_serve, NULL));
+
+    /* This request will be 401'ed twice, then succeed. */
+    ONREQ(any_request(sess, "/foo"));
+
+    /* auth_cb will have set up context. */
+    CALL(count != 2);
+
+    /* this request will be 401'ed once, then succeed. */
+    ONREQ(any_request(sess, "/foo"));
+
+    /* auth_cb will have set up context. */
+    CALL(count != 3);
+
+    /* some 20x requests. */
+    ONREQ(any_request(sess, "/foo"));
+    ONREQ(any_request(sess, "/foo"));
+
+    /* this request will be 401'ed once, then succeed. */
+    ONREQ(any_request(sess, "/foo"));
+
+    /* auth_cb will have set up context. */
+    CALL(count != 4);
+
+    ne_session_destroy(sess);
+
+    CALL(await_server());
+
+    return OK;
+}
+
+/* test digest auth 2068-style. */
+
+/* test digest auth 2617-style. */
+
+/* negotiation: within a single header, multiple headers.
+ * check digest has precedence */
+
+/* test auth-int, auth-int FAILURE. chunk trailers/non-trailer */
+
+/* test logout */
+
+/* proxy auth, proxy AND origin */
+
+ne_test tests[] = {
+    T(basic),
+    T(retries),
+    T(NULL)
+};
diff --git a/neon/test/basic.c b/neon/test/basic.c
new file mode 100644 (file)
index 0000000..84be1ae
--- /dev/null
@@ -0,0 +1,101 @@
+/* 
+   neon test suite
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_basic.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static int content_type(void)
+{
+    int n;
+    struct {
+       const char *value, *type, *subtype, *charset;
+    } ctypes[] = {
+       { "text/plain", "text", "plain", NULL },
+       { "text/plain  ", "text", "plain", NULL },
+       { "application/xml", "application", "xml", NULL },
+#undef TXU
+#define TXU "text", "xml", "utf-8"
+       /* 2616 doesn't *say* that charset can be quoted, but bets are
+        * that some servers do it anyway. */
+       { "text/xml; charset=utf-8", TXU },
+       { "text/xml; charset=utf-8; foo=bar", TXU },
+       { "text/xml;charset=utf-8", TXU },
+       { "text/xml ;charset=utf-8", TXU },
+       { "text/xml;charset=utf-8;foo=bar", TXU },
+       { "text/xml; foo=bar; charset=utf-8", TXU },
+       { "text/xml; foo=bar; charset=utf-8; bar=foo", TXU },
+       { "text/xml; charset=\"utf-8\"", TXU },
+       { "text/xml; charset='utf-8'", TXU },
+       { "text/xml; foo=bar; charset=\"utf-8\"; bar=foo", TXU },
+#undef TXU
+       /* badly quoted charset should come out as NULL */
+       { "text/xml; charset=\"utf-8", "text", "xml", NULL },
+       { NULL }
+    };
+
+    for (n = 0; ctypes[n].value != NULL; n++) {
+       ne_content_type ct = {0};
+
+       ne_content_type_handler(&ct, ctypes[n].value);
+       
+       ONV(strcmp(ct.type, ctypes[n].type),
+           ("for `%s': type was `%s'", ctypes[n].value, ct.type));
+
+       ONV(strcmp(ct.subtype, ctypes[n].subtype),
+           ("for `%s': subtype was `%s'", ctypes[n].value, ct.subtype));
+
+       ONV(ctypes[n].charset && ct.charset == NULL,
+           ("for `%s': charset unset", ctypes[n].value));
+       
+       ONV(ctypes[n].charset == NULL && ct.charset != NULL,
+           ("for `%s': unexpected charset `%s'", ctypes[n].value, 
+            ct.charset));
+
+       ONV(ctypes[n].charset && ct.charset &&
+           strcmp(ctypes[n].charset, ct.charset),
+           ("for `%s': charset was `%s'", ctypes[n].value, ct.charset));
+
+       NE_FREE(ct.value);
+    }
+
+    return OK;
+}
+
+ne_test tests[] = {
+    T(content_type), /* test functions here */
+
+    /* end of test functions. */
+    T(NULL) 
+};
+
diff --git a/neon/test/child.c b/neon/test/child.c
new file mode 100644 (file)
index 0000000..1014ef0
--- /dev/null
@@ -0,0 +1,160 @@
+/* 
+   Framework for testing with a server process
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <signal.h>
+
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+
+static pid_t child = 0;
+
+static struct in_addr lh_addr;
+
+int lookup_localhost(void)
+{
+    if (sock_name_lookup("localhost", &lh_addr))
+       return FAILHARD;
+    
+    return OK;
+}
+
+static nsocket *server_socket(int port)
+{
+    int ls = socket(PF_INET, SOCK_STREAM, 0);
+    struct sockaddr_in addr;
+    nsocket *s;
+    int val = 1;
+
+    setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(int));
+    
+    addr.sin_addr = lh_addr;
+    addr.sin_port = htons(port);
+
+    if (bind(ls, (struct sockaddr *)&addr, sizeof(addr))) {
+       printf("bind failed: %s\n", strerror(errno));
+       return NULL;
+    }
+    if (listen(ls, 5)) {
+       printf("listen failed: %s\n", strerror(errno));
+       return NULL;
+    }
+
+    s = sock_accept(ls);
+    close(ls);
+    
+    return s;
+}
+
+static void minisleep(void)
+{
+    sleep(1);
+}
+
+/* This runs as the child process. */
+static int server_child(int port, server_fn callback, void *userdata)
+{
+    nsocket *s;
+    int ret;
+
+    in_child();
+
+    s = server_socket(port);
+
+    if (s == NULL)
+       return FAIL;
+
+    ret = callback(s, userdata);
+
+    sock_close(s);
+
+    return ret;
+}
+
+
+int spawn_server(int port, server_fn fn, void *ud)
+{
+    child = fork();
+    
+    ONN("fork server", child == -1);
+
+    if (child == 0) {
+       /* this is the child. */
+       int ret = server_child(port, fn, ud);
+
+       /* print the error out otherwise it gets lost. */
+       if (ret) {
+           printf("server child failed: %s\n", name);
+       }
+
+       /* and quit the child. */
+       exit(ret);
+    } else {
+       /* this is the parent... sleep for a bit to let the parent
+        * get itself into gear. */
+       minisleep();
+       return OK;
+    }
+}
+
+int await_server(void)
+{
+    int status;
+
+    (void) wait(&status);
+    
+    i_am("error from server process");
+
+    /* so that we aren't reaped by mistake. */
+    child = 0;
+
+    return WEXITSTATUS(status);
+}
+
+int reap_server(void)
+{
+    int status;
+    
+    if (child != 0) {
+       (void) kill(child, SIGTERM);
+       minisleep();
+       (void) wait(&status);
+    }
+
+    return OK;
+}
diff --git a/neon/test/child.h b/neon/test/child.h
new file mode 100644 (file)
index 0000000..24121f1
--- /dev/null
@@ -0,0 +1,47 @@
+/* 
+   Framework for testing with a server process
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef CHILD_H
+#define CHILD_H 1
+
+/* Test which does DNS lookup on "localhost": this must be the first
+ * named test. */
+int lookup_localhost(void);
+
+/* Callback for spawn_server. */
+typedef int (*server_fn)(nsocket *sock, void *userdata);
+
+/* Spawns server child process:
+ * - forks child process.
+ * - child process listens on localhost at given port.
+ * - when you connect to it, 'fn' is run...
+ * fn is passed the client/server socket as first argument,
+ * and userdata as second.
+ * - the socket is closed when 'fn' returns, so don't close in in 'fn'.
+ */
+int spawn_server(int port, server_fn fn, void *userdata);
+
+/* Blocks until child process exits, and gives return code of 'fn'. */
+int await_server(void);
+
+/* Kills child process. */
+int reap_server(void);
+
+#endif /* CHILD_H */
diff --git a/neon/test/common/ChangeLog b/neon/test/common/ChangeLog
new file mode 100644 (file)
index 0000000..a7ca54d
--- /dev/null
@@ -0,0 +1,31 @@
+Mon Aug 27 00:31:09 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (test_num): Expose test counter.  (segv): Handle
+       segfault nicely.
+
+Mon Aug 27 00:30:20 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * child.c (discard_request): New function, from request.c in
+       neon/test.
+
+Wed Aug  8 22:09:21 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c, test.h: Only define test_argc/argv once.
+
+Mon Jul 16 16:30:28 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (warning): Take printf-style arguments list.
+
+Mon Jul 16 16:16:08 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (main): Cope with skipped tests properly.
+
+Mon Jul 16 16:00:59 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (warning): New function.  (main): Cope with warnings.
+
+Sun Jul  8 16:09:33 2001  Joe Orton  <joe@manyfish.co.uk>
+
+       * tests.c (main): Export argc/argv as test_argc, test_argv.
+
+
diff --git a/neon/test/common/README b/neon/test/common/README
new file mode 100644 (file)
index 0000000..24c6614
--- /dev/null
@@ -0,0 +1,3 @@
+
+Common test code directory for neon.
+
diff --git a/neon/test/common/child.c b/neon/test/common/child.c
new file mode 100644 (file)
index 0000000..5331548
--- /dev/null
@@ -0,0 +1,202 @@
+/* 
+   Framework for testing with a server process
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <signal.h>
+
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+
+static pid_t child = 0;
+
+static struct in_addr lh_addr;
+
+/* If we have pipe(), then use a pipe between the parent and child to
+ * know when the child is ready to accept incoming connections.
+ * Otherwise use boring sleep()s trying to avoid the race condition
+ * between listen() and connect() in the two processes. */
+#ifdef HAVE_PIPE
+#define USE_PIPE 1
+#endif
+
+int lookup_localhost(void)
+{
+    if (sock_name_lookup("localhost", &lh_addr))
+       return FAILHARD;
+    
+    return OK;
+}
+
+static nsocket *server_socket(int readyfd, int port)
+{
+    int ls = socket(AF_INET, SOCK_STREAM, 0);
+    struct sockaddr_in addr;
+    nsocket *s;
+    int val = 1;
+
+    setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(int));
+    
+    addr.sin_addr = lh_addr;
+    addr.sin_port = htons(port);
+    addr.sin_family = AF_INET;
+
+    if (bind(ls, (struct sockaddr *)&addr, sizeof(addr))) {
+       printf("bind failed: %s\n", strerror(errno));
+       return NULL;
+    }
+    if (listen(ls, 5)) {
+       printf("listen failed: %s\n", strerror(errno));
+       return NULL;
+    }
+
+#ifdef USE_PIPE
+    /* wakey wakey. */
+    write(readyfd, "a", 1);
+#endif
+
+    s = sock_accept(ls);
+    close(ls);
+    
+    return s;
+}
+
+static void minisleep(void)
+{
+    sleep(1);
+}
+
+/* This runs as the child process. */
+static int server_child(int readyfd, int port,
+                       server_fn callback, void *userdata)
+{
+    nsocket *s;
+    int ret;
+
+    in_child();
+
+    s = server_socket(readyfd, port);
+
+    if (s == NULL)
+       return FAIL;
+
+    ret = callback(s, userdata);
+
+    sock_close(s);
+
+    return ret;
+}
+
+int spawn_server(int port, server_fn fn, void *ud)
+{
+    int fds[2];
+
+#ifdef USE_PIPE
+    if (pipe(fds)) {
+       perror("spawn_server: pipe");
+       return FAIL;
+    }
+#else
+    /* avoid using uninitialized variable. */
+    fds[0] = fds[1] = 0;
+#endif
+    
+    child = fork();
+
+    ONN("fork server", child == -1);
+
+    if (child == 0) {
+       /* this is the child. */
+       int ret;
+
+       ret = server_child(fds[1], port, fn, ud);
+
+#ifdef USE_PIPE
+       close(fds[0]);
+       close(fds[1]);
+#endif
+
+       /* print the error out otherwise it gets lost. */
+       if (ret) {
+           printf("server child failed: %s\n", name);
+       }
+       
+       /* and quit the child. */
+       exit(ret);
+    } else {
+       char ch;
+       
+#ifdef USE_PIPE
+       if (read(fds[0], &ch, 1) < 0)
+           perror("parent read");
+
+       close(fds[0]);
+       close(fds[1]);
+#else
+       minisleep();
+#endif
+
+       return OK;
+    }
+}
+
+int await_server(void)
+{
+    int status;
+
+    (void) wait(&status);
+    
+    i_am("error from server process");
+
+    /* so that we aren't reaped by mistake. */
+    child = 0;
+
+    return WEXITSTATUS(status);
+}
+
+int reap_server(void)
+{
+    int status;
+    
+    if (child != 0) {
+       (void) kill(child, SIGTERM);
+       minisleep();
+       (void) wait(&status);
+    }
+
+    return OK;
+}
diff --git a/neon/test/common/child.h b/neon/test/common/child.h
new file mode 100644 (file)
index 0000000..24121f1
--- /dev/null
@@ -0,0 +1,47 @@
+/* 
+   Framework for testing with a server process
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef CHILD_H
+#define CHILD_H 1
+
+/* Test which does DNS lookup on "localhost": this must be the first
+ * named test. */
+int lookup_localhost(void);
+
+/* Callback for spawn_server. */
+typedef int (*server_fn)(nsocket *sock, void *userdata);
+
+/* Spawns server child process:
+ * - forks child process.
+ * - child process listens on localhost at given port.
+ * - when you connect to it, 'fn' is run...
+ * fn is passed the client/server socket as first argument,
+ * and userdata as second.
+ * - the socket is closed when 'fn' returns, so don't close in in 'fn'.
+ */
+int spawn_server(int port, server_fn fn, void *userdata);
+
+/* Blocks until child process exits, and gives return code of 'fn'. */
+int await_server(void);
+
+/* Kills child process. */
+int reap_server(void);
+
+#endif /* CHILD_H */
diff --git a/neon/test/common/run.sh b/neon/test/common/run.sh
new file mode 100755 (executable)
index 0000000..01ed5a9
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+rm -f debug.log
+rm -f child.log
+
+# for shared builds.
+LD_LIBRARY_PATH=../src/.libs:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH
+
+for f in $*; do
+    if ./$f; then
+       :
+    else
+       echo FAILURE
+       exit 1
+    fi
+done
+
+exit 0
diff --git a/neon/test/common/tests.c b/neon/test/common/tests.c
new file mode 100644 (file)
index 0000000..b24e6d3
--- /dev/null
@@ -0,0 +1,152 @@
+/* 
+   Stupidly simple test framework
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/signal.h>
+
+#include <stdio.h>
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ne_utils.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+
+const char *name = NULL;
+static FILE *child_debug;
+
+static int passes = 0, fails = 0, aborted = 0;
+static const char *suite;
+
+void i_am(const char *testname)
+{
+    name = testname;
+}
+
+#define TEST_DEBUG (NE_DBG_HTTP | NE_DBG_SOCKET)
+
+#define W(m) write(0, m, strlen(m))
+
+static void child_segv(int signo)
+{
+    signal(SIGSEGV, SIG_DFL);
+    W("(possible segfault in child, not dumping core)\n");
+    exit(-1);
+}
+
+void in_child(void)
+{
+    ne_debug_init(child_debug, TEST_DEBUG);    
+    NE_DEBUG(TEST_DEBUG, "**** Child forked ****\n");
+    signal(SIGSEGV, child_segv);
+}
+
+int main(int argc, char *argv[])
+{
+    int n;
+    FILE *debug;
+    
+    /* get basename(argv[0]) */
+    suite = strrchr(argv[0], '/');
+    if (suite == NULL) {
+       suite = argv[0];
+    } else {
+       suite++;
+    }
+    
+    sock_init();
+
+    debug = fopen("debug.log", "a");
+    if (debug == NULL) {
+       fprintf(stderr, "%s: Could not open debug.log: %s\n", suite,
+               strerror(errno));
+       return -1;
+    }
+    child_debug = fopen("child.log", "a");
+    if (child_debug == NULL) {
+       fprintf(stderr, "%s: Could not open child.log: %s\n", suite,
+               strerror(errno));
+       fclose(debug);
+       return -1;
+    }
+
+    ne_debug_init(debug, TEST_DEBUG);
+
+    if (tests[0] == NULL) {
+       printf("-> no tests found in %s\n", suite);
+       return -1;
+    }
+
+    printf("-> running %s:\n", suite);
+    
+    for (n = 0; !aborted && tests[n] != NULL; n++) {
+       printf("%d: ", n);
+       name = NULL;
+       fflush(stdout);
+       NE_DEBUG(TEST_DEBUG, "******* Running test %d ********\n", n);
+       switch (tests[n]()) {
+       case OK:
+           printf("pass.\n");
+           passes++;
+           break;
+       case FAILHARD:
+           aborted = 1;
+           /* fall-through */
+       case FAIL:
+           if (name != NULL) {
+               printf("%s - ", name);
+           }
+           printf("FAIL.\n");
+           fails++;
+           break;
+       }
+    }
+
+    printf("-> summary for %s: of %d tests: %d passed, %d failed. %.1f%%\n", 
+          suite, n, passes, fails, 100*(float)passes/n);
+
+    if (fclose(debug)) {
+       fprintf(stderr, "Error closing debug.log: %s\n", strerror(errno));
+       fails = 1;
+    }
+       
+    if (fclose(child_debug)) {
+       fprintf(stderr, "Error closing child.log: %s\n", strerror(errno));
+       fails = 1;
+    }
+    
+    return fails;
+}
+
diff --git a/neon/test/common/tests.h b/neon/test/common/tests.h
new file mode 100644 (file)
index 0000000..a299148
--- /dev/null
@@ -0,0 +1,60 @@
+/* 
+   Stupidly simple test framework
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef TESTS_H
+#define TESTS_H 1
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <stdio.h>
+
+/* prototype of test function. */
+typedef int (*test_func)(void);
+
+/* array of test functions to call. */
+extern test_func tests[];
+
+void i_am(const char *testname);
+
+#define OK 0
+#define FAIL 1
+#define FAILHARD 2 /* fail and skip succeeding tests. */
+
+extern const char *name;
+
+/* are __FUNCTION__ and __LINE__ gcc-isms? Probably. */
+
+static char on_err_buf[BUFSIZ];
+
+/* child process should call this. */
+void in_child(void);
+
+#define ON(x) do { sprintf(on_err_buf, "%s line %d", __FUNCTION__, __LINE__ ); i_am(on_err_buf); if ((x)) return FAIL; } while(0)
+
+#define TESTFUNC do { i_am(__FUNCTION__); } while(0)
+
+#define ONN(n,x) do { i_am(n); if ((x)) return FAIL; } while(0)
+
+#define CALL(x) do { int ret = (x); if (ret != OK) return ret; } while (0)
+
+#endif /* TESTS_H */
diff --git a/neon/test/compress.c b/neon/test/compress.c
new file mode 100644 (file)
index 0000000..1e33d3f
--- /dev/null
@@ -0,0 +1,183 @@
+/* 
+   tests for compressed response handling.
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <fcntl.h>
+
+#include "tests.h"
+
+#ifndef NEON_ZLIB
+
+static int skip_compress(void)
+{
+    return SKIP;
+}
+
+test_func tests[] = {
+    skip_compress,
+    NULL
+};
+
+#else
+
+#include "ne_compress.h"
+
+#include "child.h"
+#include "utils.h"
+
+static int failed = 0;
+
+static void reader(void *ud, const char *block, size_t len)
+{
+    const char **pnt = ud;
+    
+    if (memcmp(*pnt, block, len) != 0)
+       failed = 1;
+
+    *pnt += len;
+}
+
+static int file2buf(int fd, ne_buffer *buf)
+{
+    char buffer[BUFSIZ];
+    ssize_t n;
+    
+    while ((n = read(fd, buffer, fd)) > 0) {
+       ne_buffer_append(buf, buffer, n);
+    }
+    
+    return 0;
+}
+
+static int fetch(const char *realfn, const char *gzipfn, int chunked)
+{
+    ne_session *sess = ne_session_create();
+    ne_request *req;
+    int fd;
+    ne_buffer *buf = ne_buffer_create();
+    const char *pnt;
+    struct serve_file_args sfargs;
+    ne_decompress *dc;
+    
+    fd = open(realfn, O_RDONLY);
+    ONN("failed to open file", fd < 0);
+    file2buf(fd, buf);
+    (void) close(fd);
+    pnt = buf->data;
+    
+    ne_session_server(sess, "localhost", 7777);
+    
+    if (gzipfn) {
+       sfargs.fname = gzipfn;
+       sfargs.headers = "Content-Encoding: gzip\r\n";
+    } else {
+       sfargs.fname = realfn;
+       sfargs.headers = NULL;
+    }
+    sfargs.chunks = chunked;
+    
+    CALL(spawn_server(7777, serve_file, &sfargs));
+    
+    req = ne_request_create(sess, "GET", "/");
+    dc = ne_decompress_reader(req, ne_accept_2xx, reader, &pnt);
+
+    ONREQ(ne_request_dispatch(req));
+
+    ONN("error during decompress", ne_decompress_destroy(dc));
+
+    ne_request_destroy(req);
+    ne_session_destroy(sess);
+    ne_buffer_destroy(buf);
+    
+    CALL(await_server());
+
+    ONN("inflated response compare", failed);
+
+    return OK;
+}
+
+/* Test the no-compression case. */
+static int compress_0(void)
+{
+    return fetch("../NEWS", NULL, 0);
+}
+
+static int compress_1(void)
+{
+    return fetch("../NEWS", "file1.gz", 0);
+}
+
+/* file1.gz has an embedded filename. */
+static int compress_2(void)
+{
+    return fetch("../NEWS", "file2.gz", 0);
+}
+
+/* deliver various different sizes of chunks: tests the various
+ * decoding cases. */
+static int compress_3(void)
+{
+    return fetch("../NEWS", "file2.gz", 1);
+}
+
+static int compress_4(void)
+{
+    return fetch("../NEWS", "file1.gz", 1);
+}
+
+static int compress_5(void)
+{
+    return fetch("../NEWS", "file2.gz", 12);
+}
+
+static int compress_6(void)
+{
+    return fetch("../NEWS", "file2.gz", 20);
+}
+
+static int compress_7(void)
+{
+    return fetch("../NEWS", "file1.gz", 10);
+}
+
+static int compress_8(void)
+{
+    return fetch("../NEWS", "file2.gz", 10);
+}
+
+test_func tests[] = {
+    compress_0,
+    compress_1,
+    compress_2,
+    compress_3,
+    compress_4, 
+    compress_5, 
+    compress_6,
+    compress_7,
+    compress_8,
+    NULL,
+};
+
+#endif /* NEON_ZLIB */
diff --git a/neon/test/cookies.c b/neon/test/cookies.c
new file mode 100644 (file)
index 0000000..a0ce3ed
--- /dev/null
@@ -0,0 +1,140 @@
+/* 
+   Test for cookies interface (ne_cookies.h)
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+#include "ne_cookies.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static int serve_cookie(ne_socket *sock, void *ud)
+{
+    const char *hdr = ud;
+    char buf[BUFSIZ];
+
+    CALL(discard_request(sock));
+    
+    ne_snprintf(buf, BUFSIZ, "HTTP/1.0 200 Okey Dokey\r\n"
+               "Connection: close\r\n" "%s\r\n\r\n", hdr);
+    
+    SEND_STRING(sock, buf);
+
+    return OK;    
+}
+
+static int fetch_cookie(const char *hdr, const char *path,
+                       ne_cookie_cache *jar)
+{
+    ne_session *sess;
+    
+    CALL(make_session(&sess, serve_cookie, (void *)hdr));
+
+    ne_cookie_register(sess, jar);
+    
+    CALL(any_request(sess, path));
+
+    ne_session_destroy(sess);
+    CALL(await_server());
+
+    return OK;
+}
+
+static int parsing(void)
+{
+    static const struct {
+       const char *hdr, *name, *value;
+    } cookies[] = {
+       { "Set-Cookie: alpha=bar", "alpha", "bar" },
+       { "Set-Cookie2: alpha=bar", "alpha", "bar" },
+       { "Set-Cookie: beta = bar", "beta", "bar" },
+       { "Set-Cookie: delta = bar; norman=fish", "delta", "bar" },
+       { NULL, NULL, NULL }
+    };
+    int n;
+
+    for (n = 0; cookies[n].hdr != NULL; n++) {
+       ne_cookie_cache jar = {0};
+       ne_cookie *ck;
+
+       CALL(fetch_cookie(cookies[n].hdr, "/foo", &jar));
+
+       ck = jar.cookies;
+       ONV(ck == NULL, ("%d: cookie jar was empty!", n));
+       
+       ONV(strcmp(ck->name, cookies[n].name) || 
+           strcmp(ck->value, cookies[n].value),
+           ("%d: was [%s]=[%s]!", n, ck->name, ck->value));
+    }
+
+    return OK;
+}
+
+static int rejects(void)
+{
+    static const struct {
+       const char *hdr, *path;
+    } resps[] = {
+       /* names prefixed with $ are illegal */
+       { "Set-Cookie2: $foo=bar, Version=1", "/foo" },
+#define FOOBAR "Set-Cookie2: foo=bar, Version=1"
+       /* Path is not prefix of Request-URI */
+       { FOOBAR ", Path=/bar", "/foo" },
+       /* Domain must have embedded dots. */
+       { FOOBAR ", Domain=fish", "/foo" },
+       /* Domain must domain-match request-host */ 
+       { FOOBAR ", Domain=other.host.com", "/foo" },
+       /* Port not named in Port list */
+       { FOOBAR ", Port=\"12\"", "/foo" },
+       { NULL, NULL }
+    };
+    int n;
+    
+    for (n = 0; resps[n].hdr != NULL; n++) {
+       ne_cookie_cache jar = {0};
+
+       CALL(fetch_cookie(resps[n].hdr, resps[n].path, &jar));
+       
+       ONV(jar.cookies != NULL, 
+           ("cookie was returned for `%s'", resps[n].hdr));
+    }
+
+    return OK;
+}
+
+ne_test tests[] = {
+    T(lookup_localhost),
+    T(parsing),
+    T(rejects),
+    T(NULL) 
+};
+
diff --git a/neon/test/expired.pem b/neon/test/expired.pem
new file mode 100644 (file)
index 0000000..0062cc8
--- /dev/null
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDODCCAuKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBoTELMAkGA1UEBhMCR0Ix
+FzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGjAY
+BgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFBIERlcHQx
+EjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu
+b3JnMB4XDTAyMDEyMTIwMzkwNFoXDTAyMDEzMTIwMzkwNFowgaExCzAJBgNVBAYT
+AkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGlyZTESMBAGA1UEBxMJQ2FtYnJpZGdl
+MRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRkLjEVMBMGA1UECxMMTmVvbiBRQSBE
+ZXB0MRIwEAYDVQQDEwlsb2NhbGhvc3QxHjAcBgkqhkiG9w0BCQEWD25lb25Ad2Vi
+ZGF2Lm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9Kl
+oEgT2FqzZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRht
+AgMBAAGjggEBMIH+MB0GA1UdDgQWBBRFA3ktzHSuD9uB6mJOWoElmOtknzCBzgYD
+VR0jBIHGMIHDgBRFA3ktzHSuD9uB6mJOWoElmOtkn6GBp6SBpDCBoTELMAkGA1UE
+BhMCR0IxFzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlk
+Z2UxGjAYBgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFB
+IERlcHQxEjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3
+ZWJkYXYub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQBDSFbe
+9EjP+IyZ4vhJSk66gLSN8CnafoGm5JHpNOHy5gWLh7j0a/dxWRd4gpoBYBB6Y9rO
+YV6Eq3njdj0gu+NN
+-----END CERTIFICATE-----
diff --git a/neon/test/htdocs/plain b/neon/test/htdocs/plain
new file mode 100644 (file)
index 0000000..bce1946
--- /dev/null
@@ -0,0 +1 @@
+Test file.
diff --git a/neon/test/http-tests.c b/neon/test/http-tests.c
new file mode 100644 (file)
index 0000000..6ae4a43
--- /dev/null
@@ -0,0 +1,195 @@
+/* 
+   HTTP server tests
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* These don't really test neon, they test an HTTP server.  */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "nsocket.h"
+#include "http_utils.h"
+
+#include "tests.h"
+
+struct in_addr addr;
+nsocket *sock;
+
+char buffer[BUFSIZ];
+int clength;
+
+#define HOSTNAME "localhost"
+
+static int hostname_lookup(void) {
+    if (sock_name_lookup(HOSTNAME, &addr))
+       return FAILHARD;
+    return OK;
+}
+
+static int connect_to_server(void) {
+    sock = sock_connect(addr, 80);
+    if (sock == NULL)
+       return FAILHARD;
+    return OK;
+}
+
+static int close_conn(void) {
+    ON(sock_close(sock));
+    return OK;
+}
+
+#define EOL "\r\n"
+
+#define TRACE_REQ "TRACE / HTTP/1.0\r\n" "Host: " HOSTNAME "\r\n\r\n"
+
+#define ROOT_URL "/test/"
+
+#define GET_REQ(url) do { ON(sock_send_string(sock, "GET " url " HTTP/1.0" EOL "Host: " HOSTNAME EOL EOL)); } while (0)
+
+static int trace(void) {
+    CALL(connect_to_server());
+
+    ON(sock_send_string(sock, TRACE_REQ));
+    ON(sock_readline(sock, buffer, 1024) < 0);
+    /* this is true for any Apache server. */
+    ON(strcasecmp(buffer, "HTTP/1.1 200 OK\r\n"));
+    /* read hdrs */
+    do {
+       ON(sock_readline(sock, buffer, 1024) < 0);
+    } while (strcmp(buffer, "\r\n") != 0);
+    /* read body */
+    ON(sock_read(sock, buffer, 1024) < 0);
+    /* this will fail if you have a transparent proxy. */
+    ONN("pristine TRACE loopback", 
+       strncmp(buffer, TRACE_REQ, strlen(TRACE_REQ)) != 0);
+
+    CALL(close_conn());
+
+    return OK;
+}
+
+static int basic_syntax(void) {
+    char *value;
+    
+    CALL(connect_to_server());
+    GET_REQ(ROOT_URL);
+    ON(sock_readline(sock, buffer, 1024) < 0);
+    ONN("CRLF on status-line", strstr(buffer, EOL) == NULL);
+    
+    ONN("HTTP-Version is 1.0 or 1.1",
+       strncasecmp(buffer, "HTTP/1.1", 8) != 0 &&
+       strncasecmp(buffer, "HTTP/1.0", 8));
+
+    ONN("Status-Line syntax", buffer[8] != ' ' || buffer[12] != ' ');  
+
+    /* should be a warning if this fails. */
+    ONN("Status-Code is 200", 
+       buffer[9] != '2' || buffer[10] != '0' || buffer[11] != '0');
+
+    ONN("Reason-Phrase is present", strlen(buffer) < strlen("HTTP/x.y nnn X" EOL));
+
+    do {
+       ON(sock_readline(sock, buffer, 1024) < 0);
+       ONN("CRLF on request-header", strstr(buffer, EOL) == NULL);
+       if (strcmp(buffer, EOL) != 0) {
+           value = strchr(buffer, ':');
+           ONN("colon in request-header", value == NULL);
+           ONN("field-name in request-header", value == buffer);
+       }
+    } while (strcmp(buffer, EOL) != 0);
+
+    CALL(close_conn());
+
+    return OK;
+}
+
+static int skip_header(void)
+{
+    do {
+       ON(sock_readline(sock, buffer, 1024) < 0);
+       if (strncasecmp(buffer, "content-length:", 15) == 0) {
+           clength = atoi(buffer + 16);
+       }
+    } while (strcmp(buffer, EOL) != 0);
+    return OK;
+}
+
+static int simple_get(void)
+{
+    CALL(connect_to_server());
+    GET_REQ(ROOT_URL "plain");
+    clength = -1;
+    CALL(skip_header());
+    ONN("Content-Length header present", clength == -1);
+    ONN("Content-Length of 'plain'", clength != 11);
+    ONN("read response body", sock_read(sock, buffer, BUFSIZ) != 11);
+    ONN("content of 'plain'", strncmp(buffer, "Test file.\n", 11) != 0);
+
+    /* FIXME: I'm not sure if this is right, actually. */
+    ONN("connection close after GET",
+       sock_peek(sock, buffer, BUFSIZ) != SOCK_CLOSED);
+
+    CALL(close_conn());
+    return OK;
+}
+
+static int simple_head(void)
+{
+    CALL(connect_to_server());
+    ON(sock_send_string(sock, "HEAD " ROOT_URL "plain HTTP/1.0" EOL
+                       "Host: " HOSTNAME EOL EOL));
+    clength = -1;
+    CALL(skip_header());
+    ONN("Content-Length header present", clength == -1);
+    ONN("Content-Length of 'plain'", clength != 11);
+
+    ONN("connection close after HEAD",
+       sock_peek(sock, buffer, BUFSIZ) != SOCK_CLOSED);
+    
+    CALL(close_conn());
+    return OK;    
+}
+
+static int null_resource(void)
+{
+    http_status s = {0};
+    CALL(connect_to_server());
+    GET_REQ(ROOT_URL "nothing-here");
+    ON(sock_readline(sock, buffer, BUFSIZ) < 0);
+    ON(http_parse_statusline(buffer, &s));
+    ONN("null resource gives 404", s.code != 404);
+    CALL(close_conn());
+    return OK;
+}
+
+test_func tests[] = {
+    hostname_lookup,
+    basic_syntax,
+    trace,
+    simple_get,
+    simple_head,
+    null_resource,
+    NULL
+};
diff --git a/neon/test/lock.c b/neon/test/lock.c
new file mode 100644 (file)
index 0000000..94e75b5
--- /dev/null
@@ -0,0 +1,101 @@
+/* 
+   lock tests
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_locks.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+/* return body of LOCK response for given lock. */
+static char *lock_response(enum ne_lock_scope scope,
+                          const char *depth,
+                          const char *owner,
+                          const char *timeout,
+                          const char *token_href)
+{
+    static char buf[BUFSIZ];
+    sprintf(buf, 
+           "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+           "<D:prop xmlns:D=\"DAV:\"><D:lockdiscovery><D:activelock>\n"
+           "<D:locktype><D:write/></D:locktype>\n"
+           "<D:lockscope><D:%s/></D:lockscope>\n"
+           "<D:depth>%s</D:depth>\n"
+           "<D:owner>%s</D:owner>\n"
+           "<D:timeout>%s</D:timeout>\n"
+           "<D:locktoken><D:href>%s</D:href></D:locktoken>\n"
+           "</D:activelock></D:lockdiscovery></D:prop>\n",
+           scope==ne_lockscope_exclusive?"exclusive":"shared",
+           depth, owner, timeout, token_href);
+    return buf;
+}          
+
+/* regression test for <= 0.18.2, where timeout field was not parsed correctly. */
+static int lock_timeout(void)
+{
+    ne_session *sess = ne_session_create();
+    char *resp, *rbody = lock_response(ne_lockscope_exclusive, "0", "me",
+                                      "Second-6500", "opaquelocktoken:foo");
+    struct ne_lock lock = {0};
+
+    ON(ne_session_server(sess, "localhost", 7777));
+
+    CONCAT2(resp, 
+           "HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
+           "Connection: close\r\n\r\n", rbody);
+       
+    CALL(spawn_server(7777, single_serve_string, resp));
+
+    lock.uri = "/foo";
+    lock.depth = 0;
+    lock.scope = ne_lockscope_exclusive;
+    lock.type = ne_locktype_write;
+    lock.timeout = 5;
+
+    ONREQ(ne_lock(sess, &lock));
+
+    ONN("lock timeout ignored in response",
+       lock.timeout != 6500);
+
+    ne_session_destroy(sess);
+    
+    CALL(await_server());
+
+    return OK;
+}
+
+ne_test tests[] = {
+    T(lock_timeout),
+    T(NULL)
+};
+
diff --git a/neon/test/makekeys.sh b/neon/test/makekeys.sh
new file mode 100755 (executable)
index 0000000..e66dabf
--- /dev/null
@@ -0,0 +1,92 @@
+#!/bin/sh
+# Helper script to create CA and server certificates.
+
+srcdir=${1-.}
+
+set -ex
+
+mkdir ca
+
+openssl genrsa -rand ${srcdir}/../configure > ca/key.pem
+openssl genrsa -rand ${srcdir}/../configure > client.key
+
+openssl req -x509 -new -key ca/key.pem -out ca/cert.pem <<EOF
+US
+California
+Oakland
+Neosign
+Random Dept
+nowhere.example.com
+neon@webdav.org
+EOF
+
+openssl req -new -key ${srcdir}/server.key -out server.csr <<EOF
+GB
+Cambridgeshire
+Cambridge
+Neon Hackers Ltd
+Neon QA Dept
+localhost
+neon@webdav.org
+.
+.
+EOF
+
+openssl req -new -x509 -key ${srcdir}/server.key -out ssigned.pem <<EOF
+GB
+Cambridgeshire
+Cambridge
+Neon Hackers Ltd
+Neon Self-Signed Dept
+localhost
+neon@webdav.org
+.
+.
+EOF
+
+# Only works with a Linuxy hostname command
+hostname=`hostname -s 2>/dev/null`
+domain=`hostname -d 2>/dev/null`
+fqdn=`hostname -f 2>/dev/null`
+if [ "x${hostname}.${domain}" = "x${fqdn}" ]; then
+  openssl req -new -key ${srcdir}/server.key -out wildcard.csr <<EOF
+GB
+Cambridgeshire
+Cambridge
+Neon Hackers Ltd
+Neon Wildcard Cert
+*.${domain}
+neon@webdav.org
+.
+.
+EOF
+fi
+
+openssl req -new -key client.key -out client.csr <<EOF
+GB
+Cambridgeshire
+Cambridge
+Neon Hackers Ltd
+Neon Client Cert
+ignored.example.com
+neon@webdav.org
+.
+.
+EOF
+
+touch ca/index.txt
+echo 01 > ca/serial
+
+openssl ca -config ${srcdir}/openssl.conf -batch -days 900 -in server.csr \
+    -out server.cert
+
+openssl ca -config ${srcdir}/openssl.conf -batch -days 900 -in wildcard.csr \
+    -out wildcard.cert
+
+openssl ca -config ${srcdir}/openssl.conf -batch -days 900 -in client.csr \
+    -out client.cert
+
+# generate a PKCS12 cert from the client cert: -passOUT because it's the
+# passphrase on the OUTPUT cert, confusing...
+echo foobar | openssl pkcs12 -export -passout stdin -name "Neon Client Cert" \
+   -in client.cert -inkey client.key -out client.p12
diff --git a/neon/test/notvalid.pem b/neon/test/notvalid.pem
new file mode 100644 (file)
index 0000000..42008b3
--- /dev/null
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDODCCAuKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBoTELMAkGA1UEBhMCR0Ix
+FzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGjAY
+BgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFBIERlcHQx
+EjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu
+b3JnMB4XDTIzMTIyNzIwNDAyOVoXDTIzMTIyODIwNDAyOVowgaExCzAJBgNVBAYT
+AkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGlyZTESMBAGA1UEBxMJQ2FtYnJpZGdl
+MRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRkLjEVMBMGA1UECxMMTmVvbiBRQSBE
+ZXB0MRIwEAYDVQQDEwlsb2NhbGhvc3QxHjAcBgkqhkiG9w0BCQEWD25lb25Ad2Vi
+ZGF2Lm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9Kl
+oEgT2FqzZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRht
+AgMBAAGjggEBMIH+MB0GA1UdDgQWBBRFA3ktzHSuD9uB6mJOWoElmOtknzCBzgYD
+VR0jBIHGMIHDgBRFA3ktzHSuD9uB6mJOWoElmOtkn6GBp6SBpDCBoTELMAkGA1UE
+BhMCR0IxFzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlk
+Z2UxGjAYBgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFB
+IERlcHQxEjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3
+ZWJkYXYub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQA80TYV
+2F4QLveuldmxGoIOq5hHGxCR6aVsdtm4PGY49R5/ObCAgdWw/JV/Tc448JAz5QvU
+ahr1x9kA4Vo5NZ4q
+-----END CERTIFICATE-----
diff --git a/neon/test/openssl.conf b/neon/test/openssl.conf
new file mode 100644 (file)
index 0000000..32dfa61
--- /dev/null
@@ -0,0 +1,21 @@
+[ca]
+default_ca = neonca
+
+[neonca]
+dir = ./ca
+database = $dir/index.txt
+new_certs_dir = $dir
+certificate = $dir/cert.pem
+serial = $dir/serial
+private_key = $dir/key.pem
+policy = policy_any
+default_md = md5
+
+[policy_any]
+countryName = supplied
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
diff --git a/neon/test/props.c b/neon/test/props.c
new file mode 100644 (file)
index 0000000..7c2a635
--- /dev/null
@@ -0,0 +1,64 @@
+/* 
+   Tests for property handling
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_props.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static const ne_propname p_alpha = {"DAV:", "alpha"},
+    p_beta = {"http://webdav.org/random/namespace", "beta"},
+    p_delta = {NULL, "delta"};
+
+/* Tests little except that ne_proppatch() doesn't segfault. */
+static int patch_simple(void)
+{
+    ne_session *sess;
+    ne_proppatch_operation ops[] = {
+       { &p_alpha, ne_propset, "fish" },
+       { &p_beta, ne_propremove, NULL },
+       { NULL, ne_propset, NULL }
+    };
+    
+    CALL(make_session(&sess, single_serve_string, 
+                     "HTTP/1.1 200 Goferit\r\n"
+                     "Connection: close\r\n\r\n"));
+    ONREQ(ne_proppatch(sess, "/fish", ops));
+    ne_session_destroy(sess);
+    return await_server();
+}
+
+ne_test tests[] = {
+    T(patch_simple),
+    T(NULL) 
+};
+
diff --git a/neon/test/redirect.c b/neon/test/redirect.c
new file mode 100644 (file)
index 0000000..582462e
--- /dev/null
@@ -0,0 +1,135 @@
+/* 
+   Tests for 3xx redirect interface (ne_redirect.h)
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_redirect.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+struct redir_args {
+    int code;
+    const char *dest;
+};
+
+static int serve_redir(ne_socket *sock, void *ud)
+{
+    struct redir_args *args = ud;
+    char buf[BUFSIZ];
+
+    CALL(discard_request(sock));
+
+    ne_snprintf(buf, BUFSIZ, 
+               "HTTP/1.0 %d Get Ye Away\r\n"
+               "Content-Length: 0\r\n"
+               "Location: %s\r\n\n",
+               args->code, args->dest);
+
+    SEND_STRING(sock, buf);
+
+    return OK;
+}
+
+static int check_redir(struct redir_args *args, const char *expect)
+{
+    ne_session *sess;
+    int ret;
+    const ne_uri *loc;
+    
+    CALL(make_session(&sess, serve_redir, args));
+
+    ne_redirect_register(sess);
+    
+    ret = any_request(sess, "/redir/me");
+
+    ONN("did not get NE_REDIRECT", ret != NE_REDIRECT);
+
+    loc = ne_redirect_location(sess);
+    
+    ONN("redirect location was NULL", loc == NULL);
+
+    ONV(strcmp(ne_uri_unparse(loc), expect),
+       ("redirected to `%s' not `%s'", ne_uri_unparse(loc), expect));
+
+    ne_session_destroy(sess);
+
+    return OK;
+}
+
+#define DEST "http://foo.com/blah/blah/bar"
+
+static int simple(void)
+{
+    struct redir_args args = {302, DEST};
+    return check_redir(&args, DEST);
+}
+
+static int redir_303(void)
+{
+    struct redir_args args = {303, DEST};
+    return check_redir(&args, DEST);
+}
+
+/* check that a non-absoluteURI is qualified properly */
+static int non_absolute(void)
+{
+    struct redir_args args = {302, "/foo/bar/blah"};
+    return check_redir(&args, "http://localhost:7777/foo/bar/blah");
+}
+
+#if 0
+/* could implement failure on self-referential redirects, but
+ * realistically, the application must implement a max-redirs count
+ * check, so it's kind of redundant.  Mozilla takes this approach. */
+static int fail_loop(void)
+{
+    ne_session *sess;
+    
+    CALL(make_session(&sess, serve_redir, "http://localhost:7777/foo/bar"));
+
+    ne_redirect_register(sess);
+
+    ONN("followed looping redirect", 
+       any_request(sess, "/foo/bar") != NE_ERROR);
+
+    ne_session_destroy(sess);
+    return OK;
+}
+#endif
+
+ne_test tests[] = {
+    T(lookup_localhost),
+    T(simple),
+    T(redir_303),
+    T(non_absolute),
+    T(NULL) 
+};
+
diff --git a/neon/test/regress.c b/neon/test/regress.c
new file mode 100644 (file)
index 0000000..a2d70e5
--- /dev/null
@@ -0,0 +1,87 @@
+/* 
+   Some miscellenaneous regression tests
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_props.h"
+
+#include "tests.h"
+#include "child.h"
+
+/* This caused a segfault in 0.15.3 and earlier. */
+static int serve_dodgy_xml(nsocket *sock, void *ud)
+{
+    char buffer[BUFSIZ];
+
+    CALL(discard_request(sock));
+    
+    sock_read(sock, buffer, clength);
+
+    sock_send_string(sock,
+                    "HTTP/1.0 207 OK\r\n"
+                    "Server: foo\r\n"
+                    "Connection: close\r\n"
+                    "\r\n"
+                    "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                    "<multistatus xmlns=\"DAV:\">"
+                    "<response><propstat><prop><href>"
+                    "</href></prop></propstat></response>"
+                    "</multistatus>");
+
+    return 0;
+}
+
+static void dummy_results(void *ud, const char *href,
+                         const ne_prop_result_set *rset)
+{
+
+}
+
+static int propfind_segv(void)
+{
+    ne_session *sess = ne_session_create();
+
+    ne_session_server(sess, "localhost", 7777);
+
+    CALL(spawn_server(7777, serve_dodgy_xml, NULL));
+    
+    ne_simple_propfind(sess, "/", 0, NULL, dummy_results, NULL);
+
+    ne_session_destroy(sess);
+
+    await_server();
+
+    return OK;
+}
+
+test_func tests[] = {
+    propfind_segv,
+    NULL
+};
diff --git a/neon/test/request.c b/neon/test/request.c
new file mode 100644 (file)
index 0000000..7d4180c
--- /dev/null
@@ -0,0 +1,423 @@
+/* 
+   HTTP request handling tests
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+
+static char buffer[BUFSIZ];
+static int clength;
+
+static int discard_request(nsocket *sock)
+{
+    NE_DEBUG(NE_DBG_HTTP, "Discarding request...\n");
+    do {
+       ON(sock_readline(sock, buffer, 1024) < 0);
+       if (strncasecmp(buffer, "content-length:", 15) == 0) {
+           clength = atoi(buffer + 16);
+       }
+       NE_DEBUG(NE_DBG_HTTP, "[req] %s", buffer);
+    } while (strcmp(buffer, EOL) != 0);
+    
+    return OK;
+}
+
+#define RESP200 "HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
+#define TE_CHUNKED "Transfer-Encoding: chunked\r\n"
+
+static int single_serve_string(nsocket *s, void *userdata)
+{
+    const char *str = userdata;
+
+    ON(discard_request(s));
+    ON(sock_send_string(s, str));
+
+    return OK;
+}
+
+/* takes response body chunks and appends them to a buffer. */
+static void collector(void *ud, const char *data, size_t len)
+{
+    ne_buffer *buf = ud;
+    ne_buffer_append(buf, data, len);
+}
+
+#define ONREQ(x) do { int _ret = (x); if (_ret) { snprintf(on_err_buf, 500, "%s line %d: HTTP error:\n%s", __FUNCTION__, __LINE__, ne_get_error(sess)); i_am(on_err_buf); return FAIL; } } while (0);
+
+
+typedef ne_request *(*construct_request)(ne_session *sess, void *userdata);
+
+static ne_request *construct_get(ne_session *sess, void *userdata)
+{
+    ne_request *r = ne_request_create(sess, "GET", "/");
+    ne_buffer *buf = userdata;
+
+    ne_add_response_body_reader(r, ne_accept_2xx, collector, buf);
+
+    return r;
+}
+
+static int run_request(ne_session *sess, int status,
+                      construct_request cb, void *userdata)
+{
+    ne_request *req = cb(sess, userdata);
+
+    ON(req == NULL);
+    
+    ONREQ(ne_request_dispatch(req));
+    ONN("response status", ne_get_status(req)->code != status);
+
+    ne_request_destroy(req);
+
+    return OK;
+}
+
+static int expect_response(const char *expect, server_fn fn, void *userdata)
+{
+    ne_session *sess = ne_session_create();
+    ne_buffer *buf = ne_buffer_create();
+    int ret;
+
+    ON(sess == NULL || buf == NULL);
+    ON(ne_session_server(sess, "localhost", 7777));
+    ON(spawn_server(7777, fn, userdata));
+
+    ret = run_request(sess, 200, construct_get, buf);
+    if (ret) {
+       reap_server();
+       return ret;
+    }
+
+    ON(await_server());
+
+    ONN("response body match", strcmp(buf->data, expect));
+
+    ne_session_destroy(sess);
+    
+    return OK;
+}
+
+static int single_get_eof(void)
+{
+    return expect_response("a", single_serve_string, 
+                          RESP200
+                          "Connection: close\r\n"
+                          "\r\n"
+                          "a");
+}
+
+static int single_get_clength(void)
+{
+    return expect_response("a", single_serve_string,
+                          RESP200
+                          "Content-Length: 1\r\n"
+                          "\r\n"
+                          "a"
+                          "bbbbbbbbasdasd");
+}
+
+static int single_get_chunked(void) 
+{
+    return expect_response("a", single_serve_string,
+                          RESP200 TE_CHUNKED
+                          "\r\n"
+                          "1\r\n"
+                          "a\r\n"
+                          "0\r\n" "\r\n"
+                          "g;lkjalskdjalksjd");
+}
+
+#define CHUNK(len, data) #len "\r\n" data "\r\n"
+
+#define ABCDE_CHUNKS CHUNK(1, "a") CHUNK(1, "b") \
+ CHUNK(1, "c") CHUNK(1, "d") \
+ CHUNK(1, "e") CHUNK(0, "")
+
+static int chunk_syntax_1(void)
+{
+    /* lots of little chunks. */
+    ON(expect_response("abcde", single_serve_string,
+                      RESP200 TE_CHUNKED
+                      "\r\n"
+                      ABCDE_CHUNKS));
+    return OK;
+}
+
+static int chunk_syntax_2(void)
+{    
+    /* leading zero's */
+    ON(expect_response("0123456789abcdef", single_serve_string,
+                      RESP200 TE_CHUNKED
+                      "\r\n"
+                      "000000010\r\n" "0123456789abcdef\r\n"
+                      "000000000\r\n" "\r\n"));
+    return OK;
+}
+
+static int chunk_syntax_3(void)
+{
+    /* chunk-extensions. */
+    ON(expect_response("0123456789abcdef", single_serve_string,
+                      RESP200 TE_CHUNKED
+                      "\r\n"
+                      "000000010; foo=bar; norm=fish\r\n" 
+                      "0123456789abcdef\r\n"
+                      "000000000\r\n" "\r\n"));
+    return OK;
+}
+
+static int chunk_syntax_4(void)
+{
+    /* trailers. */
+    ON(expect_response("abcde", single_serve_string,
+                      RESP200 TE_CHUNKED
+                      "\r\n"
+                      "00000005; foo=bar; norm=fish\r\n" 
+                      "abcde\r\n"
+                      "000000000\r\n" 
+                      "X-Hello: world\r\n"
+                      "X-Another: header\r\n"
+                      "\r\n"));
+    return OK;
+}
+
+static int chunk_syntax_5(void)
+{   
+    /* T-E dominates over C-L. */
+    ON(expect_response("abcde", single_serve_string,
+                      RESP200 TE_CHUNKED
+                      "Content-Length: 300\r\n" 
+                      "\r\n"
+                      ABCDE_CHUNKS));
+    return OK;
+}
+
+static int fold_headers(void)
+{
+    ON(expect_response("abcde", single_serve_string,
+                      RESP200 "Content-Length: \r\n   5\r\n"
+                      "\r\n"
+                      "abcde"));
+    return OK;
+}
+
+static int fold_many_headers(void)
+{
+    ON(expect_response("abcde", single_serve_string,
+                      RESP200 "Content-Length: \r\n \r\n \r\n \r\n  5\r\n"
+                      "\r\n"
+                      "abcde"));
+    return 0;    
+}
+
+static void mh_header(void *ctx, const char *value)
+{
+    int *state = ctx;
+    static const char *hdrs[] = { "jim", "jab", "jar" };
+
+    if (*state < 0 || *state > 2) {
+       /* already failed. */
+       return;
+    }
+
+    if (strcmp(value, hdrs[*state]))
+       *state = -*state;
+    else
+       (*state)++;
+}
+
+/* check headers callbacks are working correctly. */
+static int multi_header(void)
+{
+    ne_session *sess = ne_session_create();
+    ne_request *req;
+    int ret, state = 0;
+
+    ON(sess == NULL);
+    ON(ne_session_server(sess, "localhost", 7777));
+    ON(spawn_server(7777, single_serve_string, 
+                   RESP200 
+                   "X-Header: jim\r\n" 
+                   "x-header: jab\r\n"
+                   "x-Header: jar\r\n"
+                   "Content-Length: 0\r\n\r\n"));
+
+    req = ne_request_create(sess, "GET", "/");
+    ON(req == NULL);
+
+    ne_add_response_header_handler(req, "x-header", mh_header, &state);
+
+    ret = ne_request_dispatch(req);
+    if (ret) {
+       reap_server();
+       ONREQ(ret);
+       ONN("shouldn't get here.", 1);
+    }
+
+    ON(await_server());
+
+    ON(state != 3);
+
+    ne_request_destroy(req);
+    ne_session_destroy(sess);
+
+    return OK;
+}
+
+struct body {
+    char *body;
+    size_t size;
+};
+
+static int want_body(nsocket *sock, void *userdata)
+{
+    struct body *b = userdata;
+    char *buf = ne_malloc(b->size);
+
+    clength = 0;
+    CALL(discard_request(sock));
+    ONN("request has c-l header", clength == 0);
+    
+    ONN("request length", clength != (int)b->size);
+    
+    NE_DEBUG(NE_DBG_HTTP, "reading body of %d bytes...\n", b->size);
+    
+    ON(sock_fullread(sock, buf, b->size));
+    
+    ON(sock_send_string(sock, RESP200 "Content-Length: 0\r\n\r\n"));
+
+    ON(memcmp(buf, b->body, b->size));
+
+    return OK;
+}
+
+static ssize_t provide_body(void *userdata, char *buf, size_t buflen)
+{
+    static const char *pnt;
+    static size_t left;
+    struct body *b = userdata;
+
+    if (buflen == 0) {
+       pnt = b->body;
+       left = b->size;
+    } else {
+       if (left < buflen) buflen = left;
+       memcpy(buf, pnt, buflen);
+       left -= buflen;
+    }
+    
+    return buflen;
+}
+
+static int send_bodies(void)
+{
+    int n, m;
+
+#define B(x) { x, strlen(x) }
+    struct body bodies[] = { 
+       B("abcde"), 
+       { "\0\0\0\0\0\0", 6 },
+       { NULL, 50000 },
+       { NULL }
+    };
+
+#define BIG 2
+    /* make the body with some cruft. */
+    bodies[BIG].body = ne_malloc(bodies[BIG].size);
+    for (n = 0; n < bodies[BIG].size; n++) {
+       bodies[BIG].body[n] = (char)n%80;
+    }
+
+    for (m = 0; m < 2; m++) {
+       for (n = 0; bodies[n].body != NULL; n++) {
+           ne_session *sess = ne_session_create();
+           ne_request *req;
+           
+           ON(sess == NULL);
+           ON(ne_session_server(sess, "localhost", 7777));
+           ON(spawn_server(7777, want_body, &(bodies[n])));
+
+           req = ne_request_create(sess, "PUT", "/");
+           ON(req == NULL);
+
+           if (m == 0) {
+               ne_set_request_body_buffer(req, bodies[n].body, bodies[n].size);
+           } else {
+               ne_set_request_body_provider(req, bodies[n].size, 
+                                            provide_body, &bodies[n]);
+           }
+
+           ONREQ(ne_request_dispatch(req));
+           
+           CALL(await_server());
+           
+           ne_request_destroy(req);
+           ne_session_destroy(sess);
+       }
+    }
+
+    return OK;
+}
+
+/* TODO: */
+
+/* test that header folding is bounded, e.g. have the server process do:
+ *   send("Foo: norman");
+ *   while(1) send("  Jim\r\n");
+ */
+
+/* test sending request body. */
+static int send_body(void)
+{
+    return FAIL;
+}
+
+test_func tests[] = {
+    lookup_localhost,
+    single_get_clength,
+    single_get_eof,
+    single_get_chunked,
+    chunk_syntax_1,
+    chunk_syntax_2,
+    chunk_syntax_3,
+    chunk_syntax_4,
+    chunk_syntax_5,
+    fold_headers,
+    fold_many_headers,
+    multi_header,
+    send_bodies,
+    NULL,
+    send_body,
+    NULL
+};
diff --git a/neon/test/resolve.c b/neon/test/resolve.c
new file mode 100644 (file)
index 0000000..be3cd68
--- /dev/null
@@ -0,0 +1,57 @@
+/* 
+   Test program for the neon resolver interface
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include "ne_socket.h"
+
+int main(int argc, char **argv)
+{
+    ne_sock_addr *addr;
+    char buf[256];
+    int ret = 0;
+
+    if (argc < 2) {
+       printf("Usage: %s hostname\n", argv[0]);
+       return -1;
+    }
+    
+    if (ne_sock_init())
+       printf("%s: Failed to initialize socket library.\n", argv[0]);
+
+    addr = ne_addr_resolve(argv[1], 0);
+    if (ne_addr_result(addr)) {
+       printf("Could not resolve `%s': %s\n", argv[1],
+              ne_addr_error(addr, buf, sizeof buf));
+       ret = 1;
+    } else {
+       ne_inet_addr *ia;
+       printf("Resolved `%s' OK:", argv[1]);
+       for (ia = ne_addr_first(addr); ia; ia = ne_addr_next(addr)) {
+           printf(" <%s>", ne_addr_print(ia, buf, sizeof buf));
+       }
+       putchar('\n');
+    }
+    ne_addr_destroy(addr);
+
+    return 0;
+}
diff --git a/neon/test/run.sh b/neon/test/run.sh
new file mode 100755 (executable)
index 0000000..2e0b7f1
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+for f in $*; do
+    if ./$f; then
+       :
+    else
+       echo FAILURE
+       exit -1
+    fi
+done
+
+exit 0
diff --git a/neon/test/server.c b/neon/test/server.c
new file mode 100644 (file)
index 0000000..1466638
--- /dev/null
@@ -0,0 +1,195 @@
+/* 
+   HTTP server tests
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* These don't really test neon, they test an HTTP server.  */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "nsocket.h"
+#include "ne_utils.h"
+
+#include "tests.h"
+
+struct in_addr addr;
+nsocket *sock;
+
+char buffer[BUFSIZ];
+int clength;
+
+#define HOSTNAME "localhost"
+
+static int hostname_lookup(void) {
+    if (sock_name_lookup(HOSTNAME, &addr))
+       return FAILHARD;
+    return OK;
+}
+
+static int connect_to_server(void) {
+    sock = sock_connect(addr, 80);
+    if (sock == NULL)
+       return FAILHARD;
+    return OK;
+}
+
+static int close_conn(void) {
+    ON(sock_close(sock));
+    return OK;
+}
+
+#define EOL "\r\n"
+
+#define TRACE_REQ "TRACE / HTTP/1.0\r\n" "Host: " HOSTNAME "\r\n\r\n"
+
+#define ROOT_URL "/test/"
+
+#define GET_REQ(url) do { ON(sock_send_string(sock, "GET " url " HTTP/1.0" EOL "Host: " HOSTNAME EOL EOL)); } while (0)
+
+static int trace(void) {
+    CALL(connect_to_server());
+
+    ON(sock_send_string(sock, TRACE_REQ));
+    ON(sock_readline(sock, buffer, 1024) < 0);
+    /* this is true for any Apache server. */
+    ON(strcasecmp(buffer, "HTTP/1.1 200 OK\r\n"));
+    /* read hdrs */
+    do {
+       ON(sock_readline(sock, buffer, 1024) < 0);
+    } while (strcmp(buffer, "\r\n") != 0);
+    /* read body */
+    ON(sock_read(sock, buffer, 1024) < 0);
+    /* this will fail if you have a transparent proxy. */
+    ONN("pristine TRACE loopback", 
+       strncmp(buffer, TRACE_REQ, strlen(TRACE_REQ)) != 0);
+
+    CALL(close_conn());
+
+    return OK;
+}
+
+static int basic_syntax(void) {
+    char *value;
+    
+    CALL(connect_to_server());
+    GET_REQ(ROOT_URL);
+    ON(sock_readline(sock, buffer, 1024) < 0);
+    ONN("CRLF on status-line", strstr(buffer, EOL) == NULL);
+    
+    ONN("HTTP-Version is 1.0 or 1.1",
+       strncasecmp(buffer, "HTTP/1.1", 8) != 0 &&
+       strncasecmp(buffer, "HTTP/1.0", 8));
+
+    ONN("Status-Line syntax", buffer[8] != ' ' || buffer[12] != ' ');  
+
+    /* should be a warning if this fails. */
+    ONN("Status-Code is 200", 
+       buffer[9] != '2' || buffer[10] != '0' || buffer[11] != '0');
+
+    ONN("Reason-Phrase is present", strlen(buffer) < strlen("HTTP/x.y nnn X" EOL));
+
+    do {
+       ON(sock_readline(sock, buffer, 1024) < 0);
+       ONN("CRLF on request-header", strstr(buffer, EOL) == NULL);
+       if (strcmp(buffer, EOL) != 0) {
+           value = strchr(buffer, ':');
+           ONN("colon in request-header", value == NULL);
+           ONN("field-name in request-header", value == buffer);
+       }
+    } while (strcmp(buffer, EOL) != 0);
+
+    CALL(close_conn());
+
+    return OK;
+}
+
+static int skip_header(void)
+{
+    do {
+       ON(sock_readline(sock, buffer, 1024) < 0);
+       if (strncasecmp(buffer, "content-length:", 15) == 0) {
+           clength = atoi(buffer + 16);
+       }
+    } while (strcmp(buffer, EOL) != 0);
+    return OK;
+}
+
+static int simple_get(void)
+{
+    CALL(connect_to_server());
+    GET_REQ(ROOT_URL "plain");
+    clength = -1;
+    CALL(skip_header());
+    ONN("Content-Length header present", clength == -1);
+    ONN("Content-Length of 'plain'", clength != 11);
+    ONN("read response body", sock_read(sock, buffer, BUFSIZ) != 11);
+    ONN("content of 'plain'", strncmp(buffer, "Test file.\n", 11) != 0);
+
+    /* FIXME: I'm not sure if this is right, actually. */
+    ONN("connection close after GET",
+       sock_peek(sock, buffer, BUFSIZ) != SOCK_CLOSED);
+
+    CALL(close_conn());
+    return OK;
+}
+
+static int simple_head(void)
+{
+    CALL(connect_to_server());
+    ON(sock_send_string(sock, "HEAD " ROOT_URL "plain HTTP/1.0" EOL
+                       "Host: " HOSTNAME EOL EOL));
+    clength = -1;
+    CALL(skip_header());
+    ONN("Content-Length header present", clength == -1);
+    ONN("Content-Length of 'plain'", clength != 11);
+
+    ONN("connection close after HEAD",
+       sock_peek(sock, buffer, BUFSIZ) != SOCK_CLOSED);
+    
+    CALL(close_conn());
+    return OK;    
+}
+
+static int null_resource(void)
+{
+    ne_status s = {0};
+    CALL(connect_to_server());
+    GET_REQ(ROOT_URL "nothing-here");
+    ON(sock_readline(sock, buffer, BUFSIZ) < 0);
+    ON(ne_parse_statusline(buffer, &s));
+    ONN("null resource gives 404", s.code != 404);
+    CALL(close_conn());
+    return OK;
+}
+
+test_func tests[] = {
+    hostname_lookup,
+    basic_syntax,
+    trace,
+    simple_get,
+    simple_head,
+    null_resource,
+    NULL
+};
diff --git a/neon/test/server.key b/neon/test/server.key
new file mode 100644 (file)
index 0000000..cdfb91b
--- /dev/null
@@ -0,0 +1,9 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIBOwIBAAJBAPNFTmxnz4JZA+8+SonD0qWgSBPYWrNlH1FP+psm5EGZGmGJGvSD
+sk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEAAQJAJBhYdoVAqNqEVu8rKB3C
+F4kcqLUlYBDVAL+ZM4QlwgWncAKk2C53BwH4PVWIIfyysleyt3bTAtqg/tgMNM06
+AQIhAP1HKbuppa+UY4rNP4Xcyj5BrCU4wVz77sg/ygW+mWIhAiEA9eKcUnnaIpig
+hlWtx9qz++85/JtahA85j6T48v0hBM0CIQCa8ByUg2wq45CdSX+xiOZjfVMslfKb
+yjZBY9xW9UjpYQIgdy9j5JqKANEIpnTran95VLot2mMXagHTPeySe331PlUCIQD0
+rL1AXeIR3Vd4D8dgab/FVbg4i94qBiY0731nyPJRoQ==
+-----END RSA PRIVATE KEY-----
diff --git a/neon/test/session.c b/neon/test/session.c
new file mode 100644 (file)
index 0000000..d183ebf
--- /dev/null
@@ -0,0 +1,115 @@
+/* 
+   Tests for session handling
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_session.h"
+
+#include "tests.h"
+
+/* should be in a 'session.c'? */
+static int fill_uri(void)
+{
+    ne_uri uri = {0};
+    ne_session *sess = ne_session_create("http", "localhost", 7777);
+    
+    ne_fill_server_uri(sess, &uri);
+
+    ONN("hostname mis-match", strcmp(uri.host, "localhost"));
+    ONN("port mis-match", uri.port != 7777);
+    ONN("scheme mis-match", strcmp(uri.scheme, "http"));
+
+    ne_session_destroy(sess);
+    ne_uri_free(&uri);
+
+    return OK;
+}
+
+static int match_hostport(const char *scheme, const char *hostname, int port,
+                         const char *hostport)
+{
+    ne_session *sess = ne_session_create(scheme, hostname, port);
+    const char *hp = ne_get_server_hostport(sess);
+    ONV(strcmp(hp, hostport),
+       ("hostport incorrect for %s: `%s' not `%s'", scheme, hp, hostport));
+    ne_session_destroy(sess);
+    return OK;
+}
+
+static int hostports(void)
+{
+    const static struct {
+       const char *scheme, *hostname;
+       int port;
+       const char *hostport;
+    } hps[] = {
+       { "http", "host.name", 80, "host.name" },
+       { "http", "host.name", 555, "host.name:555" },
+       { "http", "host.name", 443, "host.name:443" },
+       { "https", "host.name", 80, "host.name:80" },
+       { "https", "host.name", 443, "host.name" },
+       { "https", "host.name", 700, "host.name:700" },
+       { NULL }
+    };
+    int n;
+
+    for (n = 0; hps[n].scheme; n++) {
+       CALL(match_hostport(hps[n].scheme, hps[n].hostname,
+                           hps[n].port, hps[n].hostport));
+    }
+
+    return OK;
+}
+
+
+/* Check that ne_set_error is passing through to printf correctly. */
+static int errors(void)
+{
+    ne_session *sess = ne_session_create("http", "foo.com", 80);
+    
+#define EXPECT "foo, hello world, 100, bar!"
+
+    ne_set_error(sess, "foo, %s, %d, bar!", "hello world", 100);
+
+    ONV(strcmp(ne_get_error(sess), EXPECT),
+       ("session error was `%s' not `%s'",
+        ne_get_error(sess), EXPECT));
+#undef EXPECT
+
+    ne_session_destroy(sess);
+    return OK;    
+}
+
+ne_test tests[] = {
+    T(fill_uri),
+    T(hostports),
+    T(errors),
+    T(NULL) 
+};
+
diff --git a/neon/test/skeleton.c b/neon/test/skeleton.c
new file mode 100644 (file)
index 0000000..9260c85
--- /dev/null
@@ -0,0 +1,51 @@
+/* 
+   neon test suite
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+static int foo(void)
+{
+    /* This is a skeleton test suite file. */
+    return OK;
+}
+
+ne_test tests[] = {
+    T(foo), /* test functions here */
+
+    /* end of test functions. */
+    T(NULL) 
+};
+
diff --git a/neon/test/sock-tests.c b/neon/test/sock-tests.c
new file mode 100644 (file)
index 0000000..05a3f1e
--- /dev/null
@@ -0,0 +1,70 @@
+/* 
+   Socket handling tests
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "nsocket.h"
+
+#include "tests.h"
+
+static int namelookup(void) {
+    struct in_addr a;
+    ON(sock_name_lookup("www.apache.org", &a));
+    ON(sock_name_lookup("asdkajhd.webdav.org", &a) == 0);
+    return OK;
+}
+
+static int svclookup(void) {
+    ON(sock_service_lookup("http") != 80);
+    ON(sock_service_lookup("https") != 443);
+    return OK;
+}
+
+static int http(void) {
+    struct in_addr a;
+    nsocket *s;
+    char buffer[1024];
+
+    ON(sock_name_lookup("www.apache.org", &a));
+    s = sock_connect(a, 80);
+    ON(s == NULL);
+    ON(sock_send_string(s, 
+                       "GET / HTTP/1.0\r\n"
+                       "Host: www.apache.org\r\n\r\n"));
+    ON(sock_readline(s, buffer, 1024) < 0);
+    ON(strcasecmp(buffer, "HTTP/1.1 200 OK\r\n"));
+    ON(sock_close(s));
+    return OK;
+}
+
+test_func tests[] = {
+    namelookup,
+    svclookup,
+    http,
+    NULL
+};
+    
diff --git a/neon/test/socket.c b/neon/test/socket.c
new file mode 100644 (file)
index 0000000..5d85f78
--- /dev/null
@@ -0,0 +1,257 @@
+/* 
+   Socket handling tests
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ne_socket.h"
+
+#include "child.h"
+#include "tests.h"
+
+static struct in_addr localhost;
+static char buffer[BUFSIZ];
+
+static int resolve(void)
+{
+    ONN("could not resolve `localhost'",
+       ne_name_lookup("localhost", &localhost));
+    /* and again for child.c */
+    CALL(lookup_localhost());
+    return OK;
+}
+
+static int serve_close(ne_socket *sock, void *ud)
+{
+    return 0;
+}
+
+static int begin(ne_socket **sock, server_fn fn, void *ud)
+{
+    CALL(spawn_server(7777, fn, ud));
+    *sock = ne_sock_connect(localhost, 7777);
+    ONN("could not connect to localhost:7777", sock == NULL);
+    return OK;
+}
+
+static int just_connect(void)
+{
+    ne_socket *sock;
+    
+    CALL(begin(&sock, serve_close, NULL));
+    ne_sock_close(sock);
+    CALL(await_server());
+
+    return OK;
+}
+
+/* Exect a read() to return EOF */
+static int expect_close(ne_socket *sock)
+{
+    ssize_t n = ne_sock_read(sock, buffer, 1);
+    ONV(n != NE_SOCK_CLOSED, ("read gave %d not closure", n));
+    return OK;
+}
+
+/* Exect a ne_sock_peek() to return EOF */
+static int expect_peek_close(ne_socket *sock)
+{
+    ssize_t n = ne_sock_read(sock, buffer, 1);
+    ONV(n != NE_SOCK_CLOSED, ("peek gave %d not closure", n));
+    return OK;
+}
+
+/* Test that just does a connect then a close. */
+static int read_close(void)
+{
+    ne_socket *sock;
+
+    CALL(begin(&sock, serve_close, NULL));
+    CALL(expect_close(sock));    
+    ONN("close failed", ne_sock_close(sock));
+    CALL(await_server());
+    return OK;
+}
+
+/* Test that just does a connect then a close (but gets the close via
+ * ne_sock_peek). */
+static int peek_close(void)
+{
+    ne_socket *sock;
+
+    CALL(begin(&sock, serve_close, NULL));
+    CALL(expect_peek_close(sock));    
+    ONN("close failed", ne_sock_close(sock));
+    CALL(await_server());
+    return OK;
+}
+
+
+struct string {
+    char *data;
+    size_t len;
+};
+
+static int serve_string(ne_socket *sock, void *ud)
+{
+    struct string *str = ud;
+
+    ONN("write failed", ne_sock_fullwrite(sock, str->data, str->len));
+    
+    return 0;
+}
+
+/* Don't change this string. */
+#define STR "Hello, World."
+
+/* do a sock_peek() on sock for 'len' bytes, and expect 'str'. */
+static int peek_expect(ne_socket *sock, const char *str, size_t len)
+{
+    ssize_t ret = ne_sock_peek(sock, buffer, len);
+    ONV((ssize_t)len != ret, ("peek got %d bytes not %d", ret, len));
+    ONV(memcmp(str, buffer, len),
+       ("peek mismatch: `%.*s' not `%.*s'", len, buffer, len, str));    
+    return OK;
+}
+
+/* do a sock_read() on sock for 'len' bytes, and expect 'str'. */
+static int read_expect(ne_socket *sock, const char *str, size_t len)
+{
+    ssize_t ret = ne_sock_read(sock, buffer, len);
+    ONV((ssize_t)len != ret, ("peek got %d bytes not %d", ret, len));
+    ONV(memcmp(str, buffer, len),
+       ("read mismatch: `%.*s' not `%.*s'", len, buffer, len, str));    
+    return OK;
+}
+
+/* Test a simple read. */
+static int single_read(void)
+{
+    ne_socket *sock;
+    struct string hello = { STR, strlen(STR) };
+
+    CALL(begin(&sock, serve_string, &hello));
+    CALL(read_expect(sock, STR, strlen(STR)));
+    CALL(expect_close(sock));
+
+    CALL(await_server());
+    return OK;
+}
+
+/* Test a simple peek. */
+static int single_peek(void)
+{
+    ne_socket *sock;
+    struct string hello = { STR, strlen(STR) };
+
+    CALL(begin(&sock, serve_string, &hello));
+    CALL(peek_expect(sock, STR, strlen(STR)));
+    ONN("close failed", ne_sock_close(sock));
+
+    CALL(await_server());
+    return OK;
+}
+
+/* Test lots of 1-byte reads. */
+static int small_reads(void)
+{
+    ne_socket *sock;
+    struct string hello = { STR, strlen(STR) };
+    char *pnt;
+
+    CALL(begin(&sock, serve_string, &hello));
+
+    /* read the string byte-by-byte. */
+    for (pnt = hello.data; *pnt; pnt++) {
+       CALL(read_expect(sock, pnt, 1));
+    }
+
+    ONN("close failed", ne_sock_close(sock));
+    CALL(await_server());
+    return OK;
+}
+
+/* Stress out the read buffer handling a little. */
+static int read_and_peek(void)
+{
+    ne_socket *sock;
+    struct string hello = { STR, strlen(STR) };
+
+#define READ(str) CALL(read_expect(sock, str, strlen(str)))
+#define PEEK(str) CALL(peek_expect(sock, str, strlen(str)))
+
+    CALL(begin(&sock, serve_string, &hello));
+
+    PEEK("Hello");
+    PEEK("Hell");
+    PEEK(STR);
+    READ("He");
+    PEEK("llo, ");
+    READ("l");
+    PEEK("lo, World.");
+    READ("lo, Worl");
+    PEEK("d."); PEEK("d");
+    READ("d.");
+
+    CALL(expect_close(sock));
+
+    ONN("close failed", ne_sock_close(sock));
+    CALL(await_server());
+    return OK;
+}
+
+/* Read more bytes than were written. */
+static int larger_read(void)
+{
+    ne_socket *sock;
+    struct string hello = { STR, strlen(STR) };
+    ssize_t nb;
+
+    CALL(begin(&sock, serve_string, &hello));
+    
+    nb = ne_sock_read(sock, buffer, hello.len + 10);
+    ONV(nb != (ssize_t)hello.len, ("read gave too many bytes (%d)", nb));
+    ONN("read gave wrong data", memcmp(buffer, hello.data, hello.len));
+    
+    CALL(expect_close(sock));
+
+    ONN("close failed", ne_sock_close(sock));
+    CALL(await_server());
+    return OK;
+}
+
+ne_test tests[] = {
+    T(resolve),
+    T(just_connect),
+    T(read_close),
+    T(peek_close),
+    T(single_read),
+    T(single_peek),
+    T(small_reads),
+    T(read_and_peek),
+    T(larger_read),
+    T(NULL)
+};
diff --git a/neon/test/ssl.c b/neon/test/ssl.c
new file mode 100644 (file)
index 0000000..7d1b222
--- /dev/null
@@ -0,0 +1,131 @@
+/* 
+   neon test suite
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+#ifndef NEON_SSL
+/* this file shouldn't be built if SSL is not enabled. */
+#error SSL not supported
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#define ERROR_SSL_STRING (ERR_reason_error_string(ERR_get_error()))
+
+SSL_CTX *server_ctx;
+
+static int s_strwrite(SSL *s, const char *buf)
+{
+    size_t len = strlen(buf);
+    
+    ONV(SSL_write(s, buf, len) != (int)len,
+       ("SSL_write failed: %s", ERROR_SSL_STRING));
+
+    return OK;
+}
+
+static int serve_ssl(nsocket *sock, void *ud)
+{
+    int fd = sock_get_fd(sock);
+    /* we don't want OpenSSL to close this socket for us. */
+    BIO *bio = BIO_new_socket(fd, BIO_NOCLOSE);
+    SSL *ssl = SSL_new(server_ctx);
+    char buf[BUFSIZ];
+
+    ONN("SSL_new failed", ssl == NULL);
+
+    SSL_set_bio(ssl, bio, bio);
+
+    ONV(SSL_accept(ssl) != 1,
+       ("SSL_accept failed: %s", ERROR_SSL_STRING));
+
+    SSL_read(ssl, buf, BUFSIZ);
+
+    CALL(s_strwrite(ssl, "HTTP/1.0 200 OK\r\n"
+                   "Content-Length: 0\r\n"
+                   "Connection: close\r\n\r\n"));
+
+    /* Erk, shutdown is messy! See Eric Rescorla's article:
+     * http://www.linuxjournal.com/article.php?sid=4822 ; we'll just
+     * hide our heads in the sand here. */
+    SSL_shutdown(ssl);
+    SSL_free(ssl);
+
+    return OK;
+}
+
+static int init(void)
+{
+    ONN("sock_init failed.\n", sock_init());
+    server_ctx = SSL_CTX_new(SSLv23_server_method());
+    ONN("SSL_CTX_new failed", server_ctx == NULL);
+    ONN("failed to load private key",
+       !SSL_CTX_use_PrivateKey_file(server_ctx, 
+                                   "server.key", SSL_FILETYPE_PEM));
+    ONN("failed to load certificate",
+       !SSL_CTX_use_certificate_file(server_ctx, 
+                                     "server.pem", SSL_FILETYPE_PEM));
+    return OK;
+}
+
+static int simple(void)
+{
+    ne_session *sess;
+    int ret;
+
+    CALL(make_session(&sess, serve_ssl, NULL));
+
+    ne_set_secure(sess, 1);
+
+    ret = any_request(sess, "/foo");
+
+    CALL(await_server());
+
+    ONREQ(ret);
+
+    ne_session_destroy(sess);
+    return OK;
+}
+
+ne_test tests[] = {
+    T(init),
+
+    T(simple),
+
+    T(NULL) 
+};
diff --git a/neon/test/string-tests.c b/neon/test/string-tests.c
new file mode 100644 (file)
index 0000000..c288d1e
--- /dev/null
@@ -0,0 +1,89 @@
+/* 
+   String handling tests
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "string_utils.h"
+
+#include "tests.h"
+
+static int simple(void) {
+    sbuffer s = sbuffer_create();
+    ON(s == NULL);
+    ON(sbuffer_zappend(s, "abcde"));
+    ON(strcmp(sbuffer_data(s), "abcde"));
+    ON(sbuffer_size(s) != 5);
+    sbuffer_destroy(s);
+    return OK;
+}
+
+static int concat(void) {
+    sbuffer s = sbuffer_create();
+    ON(s == NULL);
+    ON(sbuffer_concat(s, "a", "b", "c", "d", "e", "f", "g", NULL));
+    ON(strcmp(sbuffer_data(s), "abcdefg"));
+    ON(sbuffer_size(s) != 7);
+    sbuffer_destroy(s);
+    return OK;
+}
+
+static int append(void) {
+    sbuffer s = sbuffer_create();
+    ON(s == NULL);
+    ON(sbuffer_append(s, "a", 1));
+    ON(sbuffer_append(s, "b", 1));
+    ON(sbuffer_append(s, "c", 1));
+    ON(strcmp(sbuffer_data(s), "abc"));
+    ON(sbuffer_size(s) != 3);
+    sbuffer_destroy(s);
+    return OK;
+}    
+
+static int alter(void) {
+    sbuffer s = sbuffer_create();
+    char *d;
+    ON(s == NULL);
+    ON(sbuffer_zappend(s, "abcdefg"));
+    d = sbuffer_data(s);
+    ON(d == NULL);
+    d[2] = '\0';
+    sbuffer_altered(s);
+    ON(strcmp(sbuffer_data(s), "ab"));
+    ON(sbuffer_size(s) != 2);
+    ON(sbuffer_zappend(s, "hijkl"));
+    ON(strcmp(sbuffer_data(s), "abhijkl"));
+    return OK;
+}
+
+test_func tests[] = {
+    simple,
+    concat,
+    append,
+    alter,
+    NULL
+};
+
diff --git a/neon/test/stubs.c b/neon/test/stubs.c
new file mode 100644 (file)
index 0000000..5c61513
--- /dev/null
@@ -0,0 +1,126 @@
+/* 
+   neon test suite
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** These tests show that the stub functions produce appropriate
+ * results to provide ABI-compatibility when a particular feature is
+ * not supported by the library.
+ **/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_request.h"
+#include "ne_socket.h"
+#include "ne_compress.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+#if defined(NEON_ZLIB) && defined(NEON_SSL)
+#define NO_TESTS 1
+#endif
+
+#ifndef NEON_ZLIB
+static int sd_result = OK;
+
+static void sd_reader(void *ud, const char *block, size_t len)
+{
+    const char *expect = ud;
+    if (strncmp(expect, block, len) != 0) {
+       sd_result = FAIL;
+       t_context("decompress reader got bad data");
+    }    
+}
+
+static int stub_decompress(void)
+{
+    ne_session *sess;
+    ne_decompress *dc;
+    ne_request *req;
+    int ret;
+
+    CALL(make_session(&sess, single_serve_string, 
+                     "HTTP/1.1 200 OK" EOL
+                     "Connection: close" EOL EOL
+                     "abcde"));
+    
+    req = ne_request_create(sess, "GET", "/foo");
+
+    dc = ne_decompress_reader(req, ne_accept_2xx, sd_reader, "abcde");
+    
+    ret = ne_request_dispatch(req);
+    
+    CALL(await_server());
+    
+    ONREQ(ret);
+
+    ONN("decompress_destroy failed", ne_decompress_destroy(dc));
+    
+    ne_request_destroy(req);
+    ne_session_destroy(sess);
+
+    /* This is a skeleton test suite file. */
+    return sd_result;
+}
+#endif
+
+#ifndef NEON_SSL
+static int stub_ssl(void)
+{
+    ne_session *sess = ne_session_create("https", "localhost", 7777);
+    
+    /* these should all fail when SSL is not supported. */
+    ONN("load default CA succeeded", ne_ssl_load_default_ca(sess) == 0);
+    ONN("load CA succeeded", ne_ssl_load_ca(sess, "Makefile") == 0);
+    ONN("load PKCS12 succeeded", ne_ssl_load_pkcs12(sess, "Makefile") == 0);
+    ONN("load PEM succeeded", ne_ssl_load_pem(sess, "Makefile", NULL) == 0);
+    
+    ne_session_destroy(sess);
+    return OK;
+}
+#endif
+
+#ifdef NO_TESTS
+static int null_test(void) { return OK; }
+#endif
+
+ne_test tests[] = {
+#ifndef NEON_ZLIB
+    T(stub_decompress),
+#endif
+#ifndef NEON_SSL
+    T(stub_ssl),
+#endif
+/* to prevent failure when SSL and zlib are supported. */
+#ifdef NO_TESTS
+    T(null_test),
+#endif
+    T(NULL) 
+};
+
diff --git a/neon/test/tests.c b/neon/test/tests.c
new file mode 100644 (file)
index 0000000..7d25a12
--- /dev/null
@@ -0,0 +1,103 @@
+/* 
+   Stupidly simple test framework
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/signal.h>
+
+#include <stdio.h>
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "tests.h"
+
+static const char *name = NULL;
+static int passes = 0, fails = 0, aborted = 0;
+static const char *suite;
+
+void i_am(const char *testname)
+{
+    name = testname;
+}
+
+static void segv(int signo)
+{
+    write(0, "SEGFAULT.\n", 10);
+    exit(-1);
+}
+
+int main(int argc, char *argv[]) {
+    int n;
+    
+    /* get basename(argv[0]) */
+    suite = strrchr(argv[0], '/');
+    if (suite == NULL) {
+       suite = argv[0];
+    } else {
+       suite++;
+    }
+
+    (void) signal(SIGSEGV, segv);
+
+    if (tests[0] == NULL) {
+       printf("-> no tests found in %s\n", suite);
+       return -1;
+    }
+
+    printf("-> running %s:\n", suite);
+    
+    for (n = 0; !aborted && tests[n] != NULL; n++) {
+       printf("%d: ", n);
+       name = NULL;
+       fflush(stdout);
+       switch (tests[n]()) {
+       case OK:
+           printf("pass.\n");
+           passes++;
+           break;
+       case FAILHARD:
+           aborted = 1;
+           /* fall-through */
+       case FAIL:
+           if (name != NULL) {
+               printf("%s - ", name);
+           }
+           printf("fail.\n");
+           fails++;
+           break;
+       }
+    }
+
+    printf("-> summary for %s: of %d tests: %d passed, %d failed. %.1f%%\n", 
+          suite, n, passes, fails, 100*(float)passes/n);
+    
+    return fails;
+}
+
diff --git a/neon/test/tests.h b/neon/test/tests.h
new file mode 100644 (file)
index 0000000..c68c627
--- /dev/null
@@ -0,0 +1,49 @@
+/* 
+   Stupidly simple test framework
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA
+
+*/
+
+#ifndef TESTS_H
+#define TESTS_H 1
+
+#include <stdio.h>
+
+/* prototype of test function. */
+typedef int (*test_func)(void);
+
+/* array of test functions to call. */
+extern test_func tests[];
+
+void i_am(const char *testname);
+
+#define OK 0
+#define FAIL 1
+#define FAILHARD 2 /* fail and skip succeeding tests. */
+
+/* are __FUNCTION__ and __LINE__ gcc-isms? Probably. */
+
+#define ON(x) do { char _buf[50]; sprintf(_buf, "%s line %d", __FUNCTION__, __LINE__ ); i_am(_buf); if ((x)) return FAIL; } while(0)
+
+#define TESTFUNC do { i_am(__FUNCTION__); } while(0)
+
+#define ONN(n,x) do { i_am(n); if ((x)) return FAIL; } while(0)
+
+#define CALL(x) do { int ret = (x); if (ret != OK) return ret; } while (0)
+
+#endif /* TESTS_H */
diff --git a/neon/test/uri-tests.c b/neon/test/uri-tests.c
new file mode 100644 (file)
index 0000000..761c477
--- /dev/null
@@ -0,0 +1,188 @@
+/* 
+   URI tests
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <uri.h>
+
+#include "tests.h"
+
+struct test_uris {
+    const char *uri;
+    const char *scheme, *hostname;
+    int port;
+    const char *path;
+} uritests[] = {
+    { "http://webdav.org/norman", "http:", "webdav.org", 80, "/norman" },
+    { "https://webdav.org/foo", "https:", "webdav.org", 443, "/foo" },
+    { "http://a/b", "a:", "a", 80, "/b" },
+    { NULL }
+};
+
+static int simple(void)
+{
+    struct uri p = {0};
+    ON(uri_parse("http://www.webdav.org/foo", &p, NULL));
+    ON(strcmp(p.host, "www.webdav.org"));
+    ON(strcmp(p.path, "/foo"));
+    ON(strcmp(p.scheme, "http"));
+    ON(p.port != -1);
+    ON(p.authinfo != NULL);
+    return 0;
+}
+
+static int simple_ssl(void)
+{
+    struct uri p = {0};
+    ON(uri_parse("https://webdav.org/", &p, NULL));
+    ON(strcmp(p.scheme, "https"));
+    ON(p.port != -1);
+    return OK;
+}
+
+static int no_path(void)
+{
+    struct uri p = {0};
+    ON(uri_parse("https://webdav.org", &p, NULL));
+    ON(strcmp(p.path, "/"));
+    return OK;
+}
+
+static int authinfo(void)
+{
+    struct uri p = {0};
+    ON(uri_parse("ftp://jim:bob@jim.com", &p, NULL));
+    ON(strcmp(p.host, "jim.com"));
+    ON(strcmp(p.authinfo, "jim:bob"));
+    return OK;
+}
+
+#define STR "/a¹²³¼½/"
+static int escapes(void)
+{
+    char *un, *esc;
+    esc = uri_abspath_escape(STR);
+    ON(esc == NULL);
+    un = uri_unescape(esc);
+    ON(un == NULL);
+    ON(strcmp(un, STR));
+    free(un);
+    free(esc);
+    return OK;
+}    
+
+static int parents(void)
+{
+    char *p = uri_parent("/a/b/c");
+    ON(p == NULL);
+    ON(strcmp(p, "/a/b/"));
+    free(p);
+    p = uri_parent("/a/b/c/");
+    ON(p == NULL);
+    ON(strcmp(p, "/a/b/"));
+    free(p);
+    return OK;
+}
+
+static int abspath(void)
+{
+    const char *p = uri_abspath("http://norman:1234/a/b");
+    ON(p == NULL);
+    ON(strcmp(p, "/a/b"));
+    return OK;
+}
+
+static int compares(void)
+{
+    ON(uri_compare("/a", "/a/") != 0);
+    ON(uri_compare("/a/", "/a") != 0);
+    ON(uri_compare("/ab", "/a/") == 0);
+    ON(uri_compare("/a/", "/ab") == 0);
+    ON(uri_compare("/a/", "/a/") != 0);
+    ON(uri_compare("/a/b/c/d", "/a/b/c/") == 0);
+    return OK;
+}       
+
+static int children(void)
+{
+    ON(uri_childof("/a", "/a/b") == 0);
+    ON(uri_childof("/a/", "/a/b") == 0);
+    ON(uri_childof("/aa/b/c", "/a/b/c/d/e") != 0);
+    ON(uri_childof("////", "/a") != 0);
+    return OK;
+}
+
+static int slash(void)
+{
+    ON(uri_has_trailing_slash("/a/") == 0);
+    ON(uri_has_trailing_slash("/a") != 0);
+    {
+       /* check the uri == "" case. */
+       char *foo = "/";
+       ON(uri_has_trailing_slash(&foo[1]));
+    }
+    return OK;
+}
+
+static int just_hostname(void)
+{
+    struct uri p = {0};
+    ON(uri_parse("host.name.com", &p, NULL));
+    ON(strcmp(p.host, "host.name.com"));
+    return 0;
+}
+
+static int just_path(void)
+{
+    struct uri p = {0};
+    ON(uri_parse("/argh", &p, NULL));
+    ON(strcmp(p.path, "/argh"));
+    return 0;
+}
+
+static int null_uri(void) {
+    struct uri p = {0};
+    ON(uri_parse("", &p, NULL) == 0);
+    return 0;
+}
+
+test_func tests[] = {
+    simple,
+    simple_ssl,
+    authinfo,
+    no_path,
+    escapes,
+    parents,
+    abspath,
+    compares,
+    children,
+    slash,
+    just_hostname,
+    just_path,
+    null_uri,
+    NULL
+};
diff --git a/neon/test/util-tests.c b/neon/test/util-tests.c
new file mode 100644 (file)
index 0000000..284f46b
--- /dev/null
@@ -0,0 +1,139 @@
+/* 
+   utils tests
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "http_utils.h"
+#include "neon_md5.h"
+#include "base64.h"
+#include "tests.h"
+
+static const struct {
+    const char *status;
+    int major, minor, code;
+    const char *rp;
+} accept_sl[] = {
+    /* These are really valid. */
+    { "HTTP/1.1 200 OK", 1, 1, 200, "OK" },
+    { "HTTP/1.1000 200 OK", 1, 1000, 200, "OK" },
+    { "HTTP/1000.1000 200 OK", 1000, 1000, 200, "OK" },
+    { "HTTP/00001.1 200 OK", 1, 1, 200, "OK" },
+    { "HTTP/1.00001 200 OK", 1, 1, 200, "OK" },
+    { "HTTP/99.99 999 99999", 99, 99, 999, "99999" },
+    /* these aren't really valid but we should be able to parse them. */
+    { "HTTP/1.1   200   OK", 1, 1, 200, "OK" },
+    { "   HTTP/1.1 200 OK", 1, 1, 200, "OK" },
+    { "Norman is a dog HTTP/1.1 200 OK", 1, 1, 200, "OK" },
+    { NULL }
+};
+
+static const char *bad_sl[] = {
+    "",
+    "HTTP/1.1 1000 OK",
+    "HTTP/1.1 1000",
+    "HTTP/1.1 100",
+    "HTTP/ 200 OK",
+    NULL
+};  
+
+static int status_lines(void)
+{
+    http_status s;
+    int n;
+    char buf[50], *pnt;
+
+    for (n = 0; accept_sl[n].status != NULL; n++) {
+       sprintf(buf, "valid %d: ", n);
+       pnt = buf + strlen(buf);
+       strcpy(pnt, "parse");
+       ONN(buf, http_parse_statusline(accept_sl[n].status, &s));
+       strcpy(pnt, "major");
+       ONN(buf, accept_sl[n].major != s.major_version);
+       strcpy(pnt, "minor");
+       ONN(buf, accept_sl[n].minor != s.minor_version);
+       strcpy(pnt, "code");
+       ONN(buf, accept_sl[n].code != s.code);
+       strcpy(pnt, "reason-phrase");
+       ONN(buf, strcmp(accept_sl[n].rp, s.reason_phrase));
+    }
+    
+    for (n = 0; bad_sl[n] != NULL; n++) {
+       sprintf(buf, "invalid %d", n);
+       ONN(buf, http_parse_statusline(bad_sl[n], &s) == 0);
+    }
+
+    return OK;
+}
+
+static int md5(void)
+{
+    unsigned char buf[17] = {0}, buf2[17] = {0};
+    char ascii[33] = {0};
+
+    ne_md5_to_ascii(ne_md5_buffer("", 0, buf), ascii);
+    ONN("MD5(null)", strcmp(ascii, "d41d8cd98f00b204e9800998ecf8427e"));
+    
+    ne_md5_to_ascii(ne_md5_buffer("foobar", 7, buf), ascii);
+    ONN("MD5(foobar)", strcmp(ascii, "b4258860eea29e875e2ee4019763b2bb"));
+
+    ne_ascii_to_md5(ascii, buf2);
+
+    ON(memcmp(buf, buf2, 16));
+
+    return 0;
+}
+
+static int base64(void)
+{
+#define B64(x, y) \
+do { char *_b = ne_base64(x); ON(strcmp(_b, y)); free(_b); } while (0)
+
+    /* invent these with 
+     *  $ printf "string" | uuencode -m blah
+     */
+    B64("a", "YQ==");
+    B64("bb", "YmI=");
+    B64("ccc", "Y2Nj");
+    B64("Hello, world", "SGVsbG8sIHdvcmxk");
+    B64("I once saw a dog called norman.\n", 
+       "SSBvbmNlIHNhdyBhIGRvZyBjYWxsZWQgbm9ybWFuLgo=");
+#if 0
+    /* duh, base64() doesn't handle binary data. which moron wrote
+     * that then? add a length argument. */
+    B64("\0\0\0\0\0", "AAAAAAAK");
+#endif
+
+#undef B64
+    return OK;
+}
+
+test_func tests[] = {
+    status_lines,
+    md5,
+    base64,
+    NULL
+};
diff --git a/neon/test/utils.c b/neon/test/utils.c
new file mode 100644 (file)
index 0000000..50dbdbe
--- /dev/null
@@ -0,0 +1,40 @@
+/* 
+   Utility functions for HTTP client tests
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "child.h"
+#include "tests.h"
+#include "utils.h"
+
+int single_serve_string(nsocket *s, void *userdata)
+{
+    const char *str = userdata;
+    char buf[1024];
+
+    ON(discard_request(s));
+    
+    while (clength > 0) {
+       clength -= sock_read(s, buf, clength);
+    }
+
+    ON(sock_send_string(s, str));
+
+    return OK;
+}
+
diff --git a/neon/test/utils.h b/neon/test/utils.h
new file mode 100644 (file)
index 0000000..6484cf1
--- /dev/null
@@ -0,0 +1,29 @@
+/* 
+   neon-specific test utils
+   Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef UTILS_H
+#define UTILS_H 1
+
+#include "ne_request.h"
+
+#define ONREQ(x) do { int _ret = (x); if (_ret) { snprintf(on_err_buf, 500, "%s line %d: HTTP error:\n%s", __FUNCTION__, __LINE__, ne_get_error(sess)); i_am(on_err_buf); return FAIL; } } while (0);
+
+
+#endif /* UTILS_H */
diff --git a/neon/test/wrongcn.pem b/neon/test/wrongcn.pem
new file mode 100644 (file)
index 0000000..846ed7a
--- /dev/null
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAv6gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBqjELMAkGA1UEBhMCR0Ix
+FzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGjAY
+BgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFBIERlcHQx
+GzAZBgNVBAMTEmhvaG9zdC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVv
+bkB3ZWJkYXYub3JnMB4XDTAyMDEzMTIwNDExNFoXDTAyMDMwMjIwNDExNFowgaox
+CzAJBgNVBAYTAkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGlyZTESMBAGA1UEBxMJ
+Q2FtYnJpZGdlMRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRkLjEVMBMGA1UECxMM
+TmVvbiBRQSBEZXB0MRswGQYDVQQDExJob2hvc3QuZXhhbXBsZS5jb20xHjAcBgkq
+hkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgC
+QQDzRU5sZ8+CWQPvPkqJw9KloEgT2FqzZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXa
+KShedFLhGidutyKKwIZJnRhtAgMBAAGjggELMIIBBzAdBgNVHQ4EFgQURQN5Lcx0
+rg/bgepiTlqBJZjrZJ8wgdcGA1UdIwSBzzCBzIAURQN5Lcx0rg/bgepiTlqBJZjr
+ZJ+hgbCkga0wgaoxCzAJBgNVBAYTAkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGly
+ZTESMBAGA1UEBxMJQ2FtYnJpZGdlMRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRk
+LjEVMBMGA1UECxMMTmVvbiBRQSBEZXB0MRswGQYDVQQDExJob2hvc3QuZXhhbXBs
+ZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9yZ4IBADAMBgNVHRME
+BTADAQH/MA0GCSqGSIb3DQEBBAUAA0EAaFru+DGQ3JiV+jEoZF68PIolHwOAHBM6
+4wfO/lHI7Hb8Tn9SQh1jgIa5/7LRue4hCo9GvIrlA5DHXnZX64DEUQ==
+-----END CERTIFICATE-----
diff --git a/neon/test/xml.c b/neon/test/xml.c
new file mode 100644 (file)
index 0000000..10a2d88
--- /dev/null
@@ -0,0 +1,137 @@
+/* 
+   neon test suite
+   Copyright (C) 2002, Joe Orton <joe@manyfish.co.uk>
+
+   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 version 2 of the License, 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ne_xml.h"
+
+#include "tests.h"
+#include "child.h"
+#include "utils.h"
+
+/* validelm, startelm, endelm re-create an XML-like representation of
+ * the original document, to verify namespace handling etc works
+ * correctly. */
+static int validelm(void *userdata, ne_xml_elmid parent, ne_xml_elmid child)
+{
+    return NE_XML_VALID;
+}
+
+static int startelm(void *userdata, const struct ne_xml_elm *s,
+                   const char **atts)
+{
+    ne_buffer *buf = userdata;
+    int n;
+
+    ne_buffer_concat(buf, "<", "{", s->nspace, "}", s->name, NULL);
+    for (n = 0; atts && atts[n] != NULL; n+=2) {
+       ne_buffer_concat(buf, " ", atts[n], "='", atts[n+1], "'", NULL);
+    }
+    ne_buffer_zappend(buf, ">");
+    return 0;
+}
+
+static int endelm(void *userdata, const struct ne_xml_elm *s,
+                 const char *cdata)
+{
+    ne_buffer *buf = userdata;
+    ne_buffer_concat(buf, cdata?cdata:"NOCDATA", "</{", s->nspace, "}",
+                    s->name, ">", NULL);
+    return 0;
+}
+
+static int parse_match(struct ne_xml_elm *elms,
+                      const char *doc, const char *result)
+{
+    ne_xml_parser *p = ne_xml_create();
+    ne_buffer *buf = ne_buffer_create();
+
+    ne_xml_push_handler(p, elms, validelm, startelm, endelm, buf);
+
+    ne_xml_parse(p, doc, strlen(doc));
+    
+    ONN("parse failed", !ne_xml_valid(p));
+    
+    ONV(strcmp(result, buf->data),
+       ("result mismatch: %s not %s", buf->data, result));
+
+    ne_xml_destroy(p);
+    ne_buffer_destroy(buf);
+
+    return OK;
+}
+
+static int matches(void)
+{
+#define PFX "<?xml version='1.0'?>\r\n"
+#define E(ns, n) "<{" ns "}" n "></{" ns "}" n ">"
+    static const struct {
+       const char *in, *out;
+       int flags;
+    } ms[] = {
+       { PFX "<hello/>", "<{}hello></{}hello>", 0 },
+       { PFX "<hello foo='bar'/>",
+         "<{}hello foo='bar'></{}hello>", 0 },
+       /*** CDATA handling. ***/
+       { PFX "<hello> world</hello>", "<{}hello> world</{}hello>", 0 },
+       { PFX "<hello> world</hello>", "<{}hello>world</{}hello>",
+         NE_XML_STRIPWS },
+       /* test that the XML interface is truly borked. */
+       { PFX "<hello>\r\n<wide>  world</wide></hello>",
+         "<{}hello><{}wide>  world</{}wide></{}hello>", 0 },
+       /*** namespace handling. */
+#define NSA "xmlns:foo='bar'"
+       { PFX "<foo:widget " NSA "/>", 
+         "<{bar}widget " NSA ">"
+         "</{bar}widget>" },
+       /* inherited namespace expansion. */
+       { PFX "<widget " NSA "><foo:norman/></widget>",
+         "<{}widget " NSA ">" E("bar", "norman") "</{}widget>", 0 },
+       { NULL, NULL }
+    };
+    struct ne_xml_elm elms[] = {
+       { "", "", NE_ELM_unknown, 0 },
+       { NULL }
+    };
+    int n;
+
+    for (n = 0; ms[n].in != NULL; n++) {
+       elms[0].flags = NE_XML_CDATA | ms[n].flags;
+       CALL(parse_match(elms, ms[n].in, ms[n].out));
+    }
+
+    return OK;
+}
+       
+ne_test tests[] = {
+    T(matches),
+
+    T(NULL)
+};
+
diff --git a/neon/tools/ChangeLog b/neon/tools/ChangeLog
new file mode 100644 (file)
index 0000000..c85b59c
--- /dev/null
@@ -0,0 +1,26 @@
+Wed May 10 14:45:55 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * cvsdist: Run autoheader if necessary.
+
+Wed May 10 14:36:17 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * reconf: New file.
+
+Sun Apr 30 21:59:56 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * cvsdist: Handle version numbers with a '-' in like 0.10.0-beta.
+
+Sun Apr 30 19:53:06 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * cvsdist: Run aclocal if there is a configure.in but no
+       aclocal.m4.  Pass "-I macros" if there is a macros directory.
+
+Sun Mar 26 11:58:26 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * mk: Only display new cwd if it has changed.
+
+Tue Mar  7 20:30:13 2000  Joe Orton  <joe@orton.demon.co.uk>
+
+       * cvsdist: Updated for optional arguments. Fixed specfile
+       creation.
+
diff --git a/neon/tools/README b/neon/tools/README
new file mode 100644 (file)
index 0000000..725f5c3
--- /dev/null
@@ -0,0 +1,4 @@
+
+This directory contains scripts which may or may not be useful
+to the maintainer.
+
diff --git a/neon/tools/cvsdist b/neon/tools/cvsdist
new file mode 100755 (executable)
index 0000000..2ef58a3
--- /dev/null
@@ -0,0 +1,143 @@
+#!/bin/sh
+# Usage: cvsdist module [tag] [version]
+# Creates a tarball from the CVS archive, generating a 'configure'
+# script and .spec file as necessary.
+# HEAD is the default tag, and mmmyy is the default version string.
+#
+# Copyright (C) 1999 Joe Orton <joe@orton.demon.co.uk>
+#
+# Id: cvsdist,v 1.8 2000/05/10 13:51:27 joe Exp 
+
+# Usage, redistribution, modification under the terms of the GNU GPL,
+# see COPYING for full details.
+
+replace_version() {
+if [ -e $1.in ]; then
+    echo Creating $1 from $1.in for release $rel
+    if ! sed -e s/@VERSION@/$rel/ < $1.in > $1; then
+       echo sed failed
+       exit -5
+    fi
+fi
+}
+
+if [ $# -eq 0 -o $# -gt 3 -o "$1" = "--help" -o "$1" = "-h" ]; then
+    cat<<EOF
+Usage:
+ cvsdist foobar-x.y.z
+   => Distribute package 'foobar', at version 'x.y.z', at tag 'foobar_x-y-z'
+ cvsdist foobar
+   => Distribute package 'foobar', at version 'mmmyy', at tag 'HEAD'
+ cvsdist foobar random_tag
+   => Distribute package 'foobar', at version 'mmmyy', at tag 'random_tag'
+ cvsdist foobar random_tag x.y.z
+   => Distribute package 'foobar', at version 'x.y.z', at tag 'random_tag'
+EOF
+    exit -1
+fi
+
+# mod is the package name (e.g. cadaver)
+# rel is the version name (e.g. 0.10.0, mar12)
+# tag is the CVS tag
+
+if [ -z "$3" ]; then
+    # No version argument
+    if [ -z "$2" ]; then
+       # No tag argument either
+       # Look for a version in the package name, like cadaver-0.10.0
+       if echo $1 | grep '-' > /dev/null; then
+           # Found one, convert it from 0.10.0 to 0-10-0 for the tag
+           # and strip the package name from the beginning
+           mod=`echo $1 | sed "s/-.*$//g"`
+           rel=`echo $1 | sed "s/^[^-]*-//g"`
+           tag=${mod}_`echo $rel | sed "s/\./-/g"`
+       else
+           # No version given, use HEAD
+           mod=$1
+           rel=`date +%b%d | dd conv=lcase 2>/dev/null`
+           tag=HEAD
+       fi
+    else
+       # Got a tag, but no release name
+       mod=$1
+       tag=$2
+       rel=`date +%b%d | dd conv=lcase 2>/dev/null`
+    fi
+else
+    # Got all the info we need
+    mod=$1
+    tag=$2
+    rel=$3
+fi
+
+echo "Distributing \`$mod' for release \`$rel' at tag \`$tag'"
+mname=$mod-$rel
+tname=/tmp/$mname
+ball=/tmp/${mname}.tar.gz
+if [ -d $tname ]; then
+       echo $tname exists, cannot proceed
+       exit -1
+fi
+if [ -r $ball ]; then
+       echo $ball exists, cannot proceed
+       exit -2
+fi
+echo Exporting $mod from CVS at $tag...
+if ! cvs -Q export -d $tname -r $tag $mod; then
+       echo cvs export failed
+       exit -3
+fi
+cd $tname
+
+# Do we need to generate a configure script?
+if [ -e configure.in ]; then
+    if [ ! -e aclocal.m4 ]; then
+       # We need to run aclocal
+       ACLARGS=""
+       if [ -d macros ]; then
+           ACLARGS="-I macros"
+       fi
+       echo Running aclocal...
+       if ! aclocal $ACLARGS; then
+           echo aclocal failed
+           exit -4
+       fi
+    fi
+    echo Running `autoconf --version`...
+    if ! autoconf; then
+       echo autoconf failed
+       exit -4
+    fi
+    if [ ! -r config.h.in ]; then
+       AUHARGS=""
+       if [ -r macros/acconfig.h ]; then
+           AUHARGS="-l macros"
+       fi
+       echo Running autoheader...
+       if ! autoheader $AUHARGS; then
+           echo autoheader failed
+           exit -4
+       fi
+    fi
+fi
+
+# Replace @VERSION@ in the following files:
+replace_version $mod.spec
+replace_version Makefile.emx
+replace_version config.h.emx
+
+### It would be nice to be able to generate the po files here too,
+### but it's a bit complex, since normally you have to run configure
+### to have po/Makefile exist
+
+cd /tmp
+echo Creating tarball...
+tar czf $ball $mname
+ls -l $ball
+
+if [ -e $mname/$mod.lsm.in ]; then
+    echo Creating .lsm file for release $rel...
+    mklsm $mod $rel $ball < $mname/$mod.lsm.in > $mod.lsm
+fi
+
+rm -r $mname
diff --git a/neon/tools/mk b/neon/tools/mk
new file mode 100755 (executable)
index 0000000..e09f6ef
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+showpwd=
+until [ -f Makefile ]; do
+       pre=`basename \`pwd\``/$pre
+       cd ..
+       showpwd=1
+done
+if [ ! -z $showpwd ]; then
+       echo cd `pwd`
+fi
+if [ $# -gt 0 ]; then
+    until [ $# -eq 0 ]; do
+       tar="$tar $pre$1"
+       shift
+    done
+    exec make $tar
+else
+    exec make
+fi
diff --git a/neon/tools/mklsm b/neon/tools/mklsm
new file mode 100755 (executable)
index 0000000..a73f705
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+# Usage:
+#   cat lsmtemplate.in | mklsm target version [tarball] > mylsmfile
+# Copyright (C) 1999 Joe Orton
+#
+# Usage, redistribution, modification under the terms of the GNU GPL,
+# see COPYING for full details.
+#
+# Id: mklsm,v 1.1.1.1 2000/03/23 19:17:11 joe Exp 
+
+# You need a 'filesize' command, which does the obvious thing.
+
+#### need to configure this ####
+
+# Where the archive files are stored
+archroot=$HOME/store/archive
+
+#### that should be all ####
+
+archive=${3-$archroot/$1-$2.tar.gz}
+
+if [ ! -e $archive ]; then
+    echo Could not find archive file $archive
+    exit -1
+fi
+bytes=`filesize $archive`
+size=`expr $bytes \/ 1024`
+case `date -r $archive +%m` in
+01) month=JAN;;        02) month=FEB;; 03) month=MAR;; 04) month=APR;;
+05) month=MAY;;        06) month=JUN;; 07) month=JUL;; 08) month=AUG;;
+09) month=SEP;;        10) month=OCT;; 11) month=NOV;; 12) month=DEC;;
+esac
+date=`date -r $archive +%d`${month}`date -r $archive +%y`
+version=$2
+target=$1
+sedscr="s/@VERSION@/${version}/;s/@DATE@/${date}/;s/@SIZE@/${size}kb/"
+exec sed -e $sedscr -
diff --git a/neon/tools/reconf b/neon/tools/reconf
new file mode 100755 (executable)
index 0000000..9cd28e5
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+rm -f config.cache
+([ ! -d macros ] || aclocal -I macros) && \
+([ ! -r acconfig.h ] || autoheader) && \
+([ ! -r macros/acconfig.h ] || autoheader -l macros) && \
+autoconf && CONFIG_SITE= ./configure $*
diff --git a/neon/tools/update-pot.sh b/neon/tools/update-pot.sh
new file mode 100755 (executable)
index 0000000..b8ef816
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Nicked from gnome-vfs/po, modified to be package-independant
+#  -joe
+
+xgettext --default-domain=$1 --directory=.. \
+  --add-comments --keyword=_ --keyword=N_ \
+  --files-from=./POTFILES.in \
+&& test ! -f $1.po \
+   || ( rm -f ./$1.pot \
+    && mv $1.po ./$1.pot )